在 Qt 中显示 matplotlib imshow 输出

show matplotlib imshow output in Qt

我有一个 np.float64 类型的二维 numpy 数组,我想在 QLabel(或任何其他有效方式)中将其显示为图像:

self.img = np.rot90(get_my_data()) # this line returns a 2D numpy array of type np.float64
self.qimg = QtGui.QImage(self.img, self.img.shape[0], self.img.shape[1], QtGui.QImage.Format_Grayscale8)
self.myLabel.setPixmap(QtGui.QPixmap(self.qimg))

我上面的代码返回以下错误:

TypeError: arguments did not match any overloaded call:
QImage(): too many arguments
QImage(QSize, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
QImage(int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
QImage(bytes, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
QImage(sip.voidptr, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
QImage(bytes, int, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
QImage(sip.voidptr, int, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
QImage(List[str]): argument 1 has unexpected type 'numpy.ndarray'
QImage(str, format: str = None): argument 1 has unexpected type 'numpy.ndarray'
QImage(QImage): argument 1 has unexpected type 'numpy.ndarray'
QImage(Any): too many arguments

但是,如果我在第一行末尾添加 .copy(),那么就可以了!但它没有正确显示数据。

self.img = np.rot90(get_my_data()).copy()
self.qimg = QtGui.QImage(self.img, self.img.shape[0], self.img.shape[1], QtGui.QImage.Format_Grayscale8)
self.myLabel.setPixmap(QtGui.QPixmap(self.qimg))

pyplot.imshow()相比,标签显示的内容如下:

self.img = 20 * np.log10(np.rot90(get_my_data()).copy())
self.qimg = QtGui.QImage(self.img, self.img.shape[0], self.img.shape[1], QtGui.QImage.Format_Grayscale8)
self.myLabel.setPixmap(QtGui.QPixmap(self.qimg))
pyplot.imshow(self.img)
pyplot.show()

pyplot.imshow()的结果是:

myLabel 显示以下结果:

那么,我的代码有什么问题?

是否有更优雅的方式将我的 2D numpy 数组显示为图像?

据我所知,OP 有一个 XY problem,也就是说,它的 objective 是在 Qt window 中显示 imshow() 的输出,但询问尝试在 QImage 中显示数据。

imshow()方法不显示原始数据,而是根据文档指示的参数处理信息:

matplotlib.pyplot.imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, origin=None, extent=None, shape=, filternorm=1, filterrad=4.0, imlim=, resample=None, url=None, *, data=None, **kwargs)

所以如果你想用那个数据得到一个图像你必须实现那个算法(你可以查看matplotlib或类似软件的源代码来分析逻辑)

如果我们关注真正的objective那么最简单的解决方案是使用matplotlib的Qt后端来获取合适的canvas如下所示:

import numpy as np

from PyQt5 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.figure = Figure(figsize=(5, 3))
        self.canvas = FigureCanvas(self.figure)
        self.ax = self.figure.subplots()

        delta = 0.025
        x = y = np.arange(-3.0, 3.0, delta)
        X, Y = np.meshgrid(x, y)
        Z1 = np.exp(-(X ** 2) - Y ** 2)
        Z2 = np.exp(-((X - 1) ** 2) - (Y - 1) ** 2)
        Z = (Z1 - Z2) * 2

        self.ax.imshow(Z)
        self.ax.set_axis_off()

        self.setCentralWidget(self.canvas)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.resize(640, 480)
    w.show()

    sys.exit(app.exec_())

更新:

如果您想不时显示数据,那么您可以使用 QTimer 来更新信息,如下所示:

import random
import numpy as np

from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.figure = Figure(figsize=(5, 3))
        self.canvas = FigureCanvas(self.figure)
        self.ax = self.figure.subplots()
        self.ax.set_axis_off()

        self.setCentralWidget(self.canvas)

        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.on_timeout)
        timer.start(100)

    def on_timeout(self):
        x0, y0 = random.uniform(-2, 2), random.uniform(-2, 2)
        delta = 0.025
        x = y = np.arange(-3.0, 3.0, delta)
        X, Y = np.meshgrid(x, y)
        Z1 = np.exp(-(X ** 2) - Y ** 2)
        Z2 = np.exp(-((X - x0) ** 2) - (Y - y0) ** 2)
        Z = (Z1 - Z2) * 2
        self.ax.imshow(Z)
        self.canvas.draw()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.resize(640, 480)
    w.show()

    sys.exit(app.exec_())

另一方面,如果您想实时使用 SW,那么 GUI 会限制 objective。建议每 N 个样本显示一次数据,这样 GUI 就不会被阻塞,用户可以查看和分析信息。人眼非常慢,所以即使存在每微秒显示图像的技术,我们的视觉也不会欣赏它,我们的视觉需要 60 毫秒来处理图像,因此设备设计为工作在 30Hz,因为如果频率更高不会观察到改善。