PyQt5实时视频流裁剪异常:QImage显示扭曲如何解决?
问题:PyQt5摄像头帧裁剪后显示扭曲/错位
我开发了一个PyQt5 GUI应用,功能是从摄像头读取帧,在帧上选择一块小区域,后续只显示该区域。我已经正确选中区域并存储为QRect,但裁剪帧用于显示和后续处理时,实时视频有时显示正常,但大多数时候会出现扭曲——画面倾斜,原本的右角变成左角,而且本该在视频中间的粉色圆圈位置也错了。
相关代码(Worker1类)
from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage import cv2 as cv class Worker1(QThread): ImageUpdate = pyqtSignal(QImage) def __init__(self, canvas, selection): super().__init__() self.ThreadActive = True self.canvas = canvas self.anim = None self.selection = selection def run(self): Capture = cv.VideoCapture(0) old_closest_contour = None d_tresh = 300 P_color = (0, 255, 0) global i i = 0 while self.ThreadActive: i += 1 ret, frame = Capture.read() if ret: if self.selection is None or self.selection.isEmpty(): cropped_frame = frame else: a = int(self.selection.x()) b = int(self.selection.y()) width = int(self.selection.width()) height = int(self.selection.height()) #print(str(a) + " " + str(b) + " " + str(width) + " " + str(height)) cropped_frame = frame[b: b + height, a: a + width].copy() #print(cropped_frame.shape[1]) #print(cropped_frame.shape[0]) ### other processing lines, those are not relevant in the displaying ### Image = cv.cvtColor(cropped_frame, cv.COLOR_BGR2RGB) img = cv.medianBlur(Image, 25) cv.circle(img, (x, y), r, P_color, 1) cv.circle(img, (x, y), 4, P_color, -1) cv.circle(img, point, 0, (255, 0, 255), 10) ConvertToQtFormat = QImage(img.data, img.shape[1], img.shape[0], QImage.Format_RGB888) self.ImageUpdate.emit(ConvertToQtFormat) Capture.release()
问题排查与解决方案
1. QImage内存对齐问题(最可能导致扭曲)
当前创建QImage时未指定bytesPerLine参数,当图像行字节数不是4的倍数时,QImage会自动按4字节对齐,直接导致画面错位、扭曲。
修正代码:
# 替换原QImage创建代码 ConvertToQtFormat = QImage(img.data, img.shape[1], img.shape[0], img.strides[0], QImage.Format_RGB888)
img.strides[0]是OpenCV图像每行的实际字节数,传递给QImage后能保证内存布局完全匹配,避免对齐错误。
2. 坐标映射不匹配问题
如果GUI显示控件对摄像头帧做了缩放(比如拉伸适配控件大小),选中的QRect是基于控件的坐标,和原始摄像头帧的坐标不对应,裁剪区域自然会出错。
解决方法:
- 获取摄像头原始帧尺寸:
frame_width = Capture.get(cv.CAP_PROP_FRAME_WIDTH) frame_height = Capture.get(cv.CAP_PROP_FRAME_HEIGHT) - 获取GUI控件尺寸:
canvas_width = self.canvas.width() canvas_height = self.canvas.height() - 计算缩放比例并转换坐标:
注意要确保转换后的坐标不超出原始帧范围(比如scale_x = frame_width / canvas_width scale_y = frame_height / canvas_height a = int(self.selection.x() * scale_x) b = int(self.selection.y() * scale_y) width = int(self.selection.width() * scale_x) height = int(self.selection.height() * scale_y)a + width不能大于frame_width)。
3. 线程安全问题
self.selection如果在主线程中修改,子线程直接读取可能拿到半更新的错误值,导致裁剪区域随机错乱。
解决方法:
- 用
pyqtSignal在主线程更新selection时发送给子线程,子线程维护私有变量接收更新; - 或者用
QMutex加锁,读取和修改self.selection时都加锁保护。
内容的提问来源于stack exchange,提问作者zoltnu.wxkejt6l0
相关产品推荐
相关产品推荐

