如何在PyQt5中裁剪实时视频流?QImage看起来倾斜。

huangapple go评论67阅读模式
英文:

How to crop a live video feed in PyQt5? QImage looks sheared

问题

我有一个PyQt5 GUI应用程序。我从摄像头读取一帧来选择其中的一个较小区域,稍后我想只显示该区域。我正确地选择了这个区域,并将其存储在一个QRect中。但当我尝试裁剪帧以显示和稍后处理时,显示的实时视频有时候能正确工作,但大多数时候看起来像图片上这样。它变得倾斜,右上角应该是左上角。粉色的圆应该在视频的中间,所以有点不对劲。

英文:

I have a PyQt5 GUI application. I read a frame from the camera to select a smaller area on it, and later I would like to display only that area. I correctly select the area, and store it in a QRect. But when I try to crop the frames to display and to process later, the displayed live video just sometimes works correctly. but most of the times it looks like on this picture. It becomes slanted, the right corner should be the left corner. And the pink circle should be in the middle of the video, so it is kinda wrong.

如何在PyQt5中裁剪实时视频流?QImage看起来倾斜。

I have this Worker1 class, where I process the displayed frames.

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

得分: 0

We have to set bytesPerLine argument.
Replace ConvertToQtFormat = QImage(img.data, img.shape[1], img.shape[0], QImage.Format_RGB888) with:

ConvertToQtFormat = QImage(img.data, img.shape[1], img.shape[0], img.strides[0], QImage.Format_RGB888)

img.strides[0] 应用于 img 中每行的字节数。
img.shape[1] = 100 时,通常情况下,img.strides[0] 等于 100*3 = 300 字节,当每个像素有3个字节时。

之所以有 strides 是因为存在内存中行不连续的情况,而步长不等于宽度 * 3。

QImage 对象中的等效参数是 bytesPerLine
默认情况下,QImage 对象假定 bytesPerLine 是4的倍数。
如果宽度*3不是4的倍数,则假定每行末尾存在填充字节。
例如,当宽度 = 101 时,bytesPerLine 为304,而不是303(假定有1个填充字节)。
(4的倍数假设来源于BMP图像格式)。

在我们的情况下:

  • img.shape[1] 是4的倍数时,图像将看起来正确。
  • img.shape[1] 不是4的倍数时,图像将会 "倾斜"。
    在我们的情况下,NumPy数组没有填充字节,Qt的bytesPerLine和NumPy的strides之间存在不匹配。

解决方法是设置bytesPerLine参数(使用重载的QImage构造函数)。
通常情况下,从NumPy转换为QImage时,我们应该将img.strides[0]设置为bytesPerLine参数。

英文:

We have to set bytesPerLine argument.
Replace ConvertToQtFormat = QImage(img.data, img.shape[1], img.shape[0], QImage.Format_RGB888) with:

ConvertToQtFormat = QImage(img.data, img.shape[1], img.shape[0], img.strides[0], QImage.Format_RGB888)

img.strides[0] applies the number of bytes in each line of img.
When img.shape[1] = 100, img.strides[0] is usually equal 100*3 = 300 bytes when there are 3 bytes per pixel.

The reason that we have the strides is that there are cases when lines are not continuous in memory, and the stride doesn't equal width * 3.

The equivalent parameter in QImage object is bytesPerLine.
By default, QImage object assumes that bytesPerLine is a multiple of 4.
In case width*3 is not a multiple of 4, padding bytes are assumed to be present at the end of each line.
For example, when width = 101, bytesPerLine is 304 instead of 303 (1 padding byte is assumed).
(The multiple of 4 assumption is originated from the BMP image format).

In our case:

  • When img.shape[1] is a multiple of 4, the image is going to look correct.
  • When img.shape[1] is a not multiple of 4, the image is going to be "slanted".
    In our case the NumPy array has no padding bytes, and there is a mismatch between Qt bytesPerLine and NumPy strides.

The solution is setting the bytesPerLine parameter (using an overloaded QImage constructor).
In general, when converting from NumPy to QImage, we should set img.strides[0] as bytesPerLine argument.

huangapple
  • 本文由 发表于 2023年5月25日 05:49:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76327618.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定