OpenCV的BGR图像和其反转版本RGB图像[:,:,::-1]有什么区别?

4

我试图使用QLabel展示一个opencv图片。我得到了两个不同版本的图片,第一个是opencv的BGR图片,第二个是使用image[:,:,::-1]得到的RGB图片。BGR版本可以正常工作,但是RGB版本不行。

以下代码可以正常工作

src = cv.imread('image.jpg')
h,w,ch = src.shape
bytesPerLine = ch * w
qImg = QImage(src.data, w, h, bytesPerLine, QImage.Format_RGB888)
qImg = qImg.rgbSwapped()
self.ui.label.setPixmap(QPixmap.fromImage(qImg))


这些代码无法运行:

src = cv.imread('image.jpg')
src = src[:,:,::-1]
h,w,ch = src.shape
bytesPerLine = ch * w
qImg = QImage(src.data, w, h, bytesPerLine, QImage.Format_RGB888)
self.ui.label.setPixmap(QPixmap.fromImage(qImg))
1个回答

8
正如您所注意到的,OpenCV以BGR格式读取图像,而QImage以RGB格式读取。在第一种方法中,您在不进行转换的情况下将其转换为QImage,然后使用rgbSwapped()方法进行转换。
通过测试第一种方法,我得到:
1000 loops, best of 5: 291 usec per loop

在第二种方法中,您尝试在将其转换为QImage之前进行操作,但当我执行它时,我得到以下错误,假设您也会遇到相同的错误。
Traceback (most recent call last):
  File "xxxx.py", line 18, in <module>
    qImg = QtGui.QImage(src.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
TypeError: arguments did not match any overloaded call:
  QImage(): too many arguments
  QImage(QSize, QImage.Format): argument 1 has unexpected type 'memoryview'
  QImage(int, int, QImage.Format): argument 1 has unexpected type 'memoryview'
  QImage(bytes, int, int, QImage.Format): argument 1 has unexpected type 'memoryview'
  QImage(sip.voidptr, int, int, QImage.Format): argument 1 has unexpected type 'memoryview'
  QImage(bytes, int, int, int, QImage.Format): argument 1 has unexpected type 'memoryview'
  QImage(sip.voidptr, int, int, int, QImage.Format): argument 1 has unexpected type 'memoryview'
  QImage(List[str]): argument 1 has unexpected type 'memoryview'
  QImage(str, format: str = None): argument 1 has unexpected type 'memoryview'
  QImage(QImage): argument 1 has unexpected type 'memoryview'
  QImage(Any): too many arguments

这是因为NumPy使用memoryview来优化某些任务。在这种情况下,当执行src[:,:,::-1]时,一种优化方式是不修改数据,而是修改访问数据的方式,这是通过缓冲区协议来实现的。
而在这种情况下,QImage不支持这种类型的数据,所以解决方案是使用tobytes()bytes()来访问字节。
import cv2
from PyQt5 import QtGui, QtWidgets

if __name__ == '__main__':
    import sys
    src = cv2.imread('image.jpg')
    src = src[:,:,::-1]
    h, w, ch = src.shape
    bytesPerLine = ch * w
    qImg = QtGui.QImage(src.data.tobytes(), w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
    # Or
    # qImg = QtGui.QImage(bytes(src.data), w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QLabel()
    w.setPixmap(QtGui.QPixmap.fromImage(qImg))
    w.show()
    sys.exit(app.exec_())

时间:
500 loops, best of 5: 523 usec per loop

另一种解决方案是使用OpenCV的cvtColor()函数,如果您修改了数据:
import cv2
from PyQt5 import QtGui, QtWidgets

if __name__ == '__main__':
    import sys
    src = cv2.imread('image.jpg')
    src = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
    h, w, ch = src.shape
    bytesPerLine = ch * w
    qImg = QtGui.QImage(src.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QLabel()
    w.setPixmap(QtGui.QPixmap.fromImage(qImg))
    w.show()
    sys.exit(app.exec_())

时间:

1000 loops, best of 5: 263 usec per loop

2
干得好,先生。我怀疑 cvtColor() 将是最快的,因为毫无疑问 OpenCV 将使用 SIMD 来完成它。 - Mark Setchell
@MarkSetchell 看起来你是对的,使用timeit我已经做了测量,观察到opencv的cvtColor方法是最好的。 - eyllanesc
1
感谢您为已经很好的答案添加了时序检查。如果可以的话,我还会再次投赞成票的 :-) - Mark Setchell
1
非常有帮助,非常感谢您的耐心。 - Yifeng_Li
@eyllanesc QtGui.QImage.Format_RGB888可以工作,但为什么QtGui.QImage.Format_RGB444不能?您能提供原因吗? - Nikhil.Nixel
1
@Nixel 通常相机使用RGB格式,对于8位交错通道,您应该使用支持该格式的QImage,此时Format_RGB888是适当的格式,而Format_RGB444不是适当的格式,因为它明显等待每个通道4位。我建议您查看文档以了解图像在内存中的放置方式。 - eyllanesc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接