switchPicture.py
from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtGui import * import numpy as np import cv2 import sys class MyWidget(QWidget): def __init__(self, parent = None): super().__init__(parent) self.setWindowTitle(self.tr('顯示圖片')) self.resize(500,400) self.label = QLabel(self) self.label.setFrameShape(QFrame.Box) self.label.setAlignment(Qt.AlignCenter) img = cv2.imread('girl_1.jpg') #cv2.imshow('111',img) #cv2.waitKey(0) width = img.shape[1] height = img.shape[0] print('cv2, width: '+str(width)+' height: '+str(height)) cv2.cvtColor(img, cv2.COLOR_BGR2RGB,img) qt_img = QImage(img.data,width,height,QImage.Format_RGB888) #print(type(qt_img)) #self.label.setPixmap(QPixmap.fromImage(qt_img)) self.label.setGeometry(0, 0, 400, 300) n_width = qt_img.width() n_height = qt_img.height() print('Qt, width: '+str(n_width)+' height: '+str(n_height)) if n_width / 400 >= n_height / 300: ratio = n_width / 400 else: ratio = n_height / 300 new_width = n_width / ratio new_height = n_height / ratio new_img = qt_img.scaled(new_width, new_height, Qt.KeepAspectRatio) self.label.setPixmap(QPixmap.fromImage(new_img)) if __name__ == '__main__': app = QApplication(sys.argv) widget = MyWidget() widget.show() #print(widget.children()) sys.exit(app.exec_())
img是<class 'numpy.ndarray'>類型的,img.shape是一個包含三個元素的元祖,img.data是<class 'memoryview'>類型的。
numpy中是這樣解釋ndarray.data的:
data
Python buffer object pointing to the start of the array's data.
所以我們可以將ndarray.data理解為一個指向存儲array數組數據的內存的指針。
再來看QImage的構造函數:
1 | QImage() 2 | QImage(QSize, QImage.Format) 3 | QImage(int, int, QImage.Format) 4 | QImage(bytes, int, int, QImage.Format) 5 | QImage(sip.voidptr, int, int, QImage.Format) 6 | QImage(bytes, int, int, int, QImage.Format) 7 | QImage(sip.voidptr, int, int, int, QImage.Format) 8 | QImage(List[str]) 9 | QImage(str, format: str = None) 10 | QImage(QImage) 11 | QImage(Any)
我們在上面的程序中用到的是第5個構造函數,下面解釋一下第5個構造函數的各個參數:
@sip.voidptr: 把它理解為一個地址,這個地址指向的內存,存儲的是圖片的所有像素點的值
@int:代表圖像的寬度
@int:代表圖像的高度
@QImage.Format:圖像是BGR格式還是RGB格式,等等
在上面的程序中,我們用cv2.imread()讀取的圖片,將結果存在img中(img是一個np.ndarray數組),然后我們再將img.data傳遞給QImage的構造函數。
在這,我不禁有一個疑惑,將img.data傳遞給QImage,QImage是如何解析它並生成圖片的呢?
以下是我的理解:
img.data指向的內存連續存儲着的許多整形數字,這些數字每三個或幾個為一組表示一個像素點,在內存中沒有所謂的圖片的行和列之分,那么QImage是如何區分出圖片有多少行,有多少列呢?
QImage的構造函數中有兩個int型變量,他們分別是圖片的寬度和高度,QImage就是通過這兩個數字來識別圖片有多少行和多少列的。在最后的參數QImage.Format,它的作用就是讓QImage知道
應該讓幾個數字一組表示一個像素點。
有時,我們使用第5個構造函數會出現圖像扭曲的問題,這是我們應該用第7個構造函數QImage(sip.voidptr, int, int, int, QImage.Format),第三個int型參數表示的是每行有多少個字節
將上面程序中對應項改為下面的程序就可以了,我這里又增加了一個channel變量,用於計算每行有多少字節。
channel = img.shape[2]
qt_img = QImage(img1.data,width,height,width*channel,QImage.Format_RGB888)
出現扭曲的原因是QImage構造函數不知道圖片一行有多少個像素點了。
下面是正常顯示和扭曲的圖片:
正常:
扭曲: