python - 如何将 RGB ImageItem 添加到 pyqtgraph ViewBox? ValueError : could not broadcast input array from shape (256, 256,4) 变为形状 (256,256)

标签 python arrays numpy pyqt4 pyqtgraph

最新更新 2016 年 3 月 29 日:

如果您复制粘贴以下代码,您应该会得到一个带有按钮的 pyqt 应用程序。如果单击它,您会在 ViewBox 中添加灰度。如果您重写声明 ARR_OFF 的位置,以便 ARR = plt.cm.jet(norm(ARR)) 那么您会得到我遇到的错误。我想在 ViewBox 中将 ARR 显示为颜色图,因此我将其转换为 RGBA 数组。

import os, sys, matplotlib, matplotlib.pyplot
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.widgets.GraphicsLayoutWidget import GraphicsLayoutWidget
import pyqtgraph as pg
import pyqtgraph.functions as fn
import matplotlib.pyplot as plt

N = 256
ARR = np.random.random((N,N))*255
norm = plt.Normalize()
ARR_OFF = plt.cm.jet(norm(ARR))
# Change ARR_OFF to ARR to see my problem

class MainWindow(QtGui.QMainWindow):

    def __init__(self, parent=None):

        QtGui.QMainWindow.__init__(self, parent)
        self.setupUserInterface()
        self.setupSignals()

    def setupUserInterface(self):
        """ Initialise the User Interface """
        # Left frame
        leftFrame = QtGui.QFrame()
        leftFrameLayout = QtGui.QHBoxLayout()
        leftFrame.setLayout(leftFrameLayout)
        leftFrame.setLineWidth(0)
        leftFrame.setFrameStyle(QtGui.QFrame.Panel)
        leftFrameLayout.setContentsMargins(0,0,5,0)

        # Left frame contents
        self.viewMain = GraphicsLayoutWidget()  # A GraphicsLayout within a GraphicsView
        leftFrameLayout.addWidget(self.viewMain)
        self.viewMain.setMinimumSize(200,200)
        self.vb = MultiRoiViewBox(lockAspect=True,enableMenu=True)
        self.viewMain.addItem(self.vb)
        self.vb.enableAutoRange()

        # Right frame
        self.sidePanel = SidePanel(self)

        # UI window (containing left and right frames)
        UIwindow         = QtGui.QWidget(self)
        UIwindowLayout   = QtGui.QHBoxLayout()
        UIwindowSplitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
        UIwindowLayout.addWidget(UIwindowSplitter)
        UIwindow.setLayout(UIwindowLayout)
        self.setCentralWidget(UIwindow)
        UIwindowSplitter.addWidget(leftFrame)
        UIwindowSplitter.addWidget(self.sidePanel)

        self.setMinimumSize(600,500)
        self.resize(self.minimumSize())

    def setupSignals(self):
        """ Setup signals """
        self.sidePanel.buttImageAdd.clicked.connect(self.showImage)

    def showImage(self,imageFilename):
        """ Shows image in main view """
        self.vb.showImage(ARR)

class ViewMode():
    def __init__(self,id,cmap):
        self.id   = id
        self.cmap = cmap
        self.getLookupTable()
    def getLookupTable(self):
        lut = [ [ int(255*val) for val in self.cmap(i)[:3] ] for i in xrange(256) ]
        lut = np.array(lut,dtype=np.ubyte)
        self.lut = lut

class MultiRoiViewBox(pg.ViewBox):

    def __init__(self,parent=None,border=None,lockAspect=False,enableMouse=True,invertY=False,enableMenu=True,name=None):
        pg.ViewBox.__init__(self,parent,border,lockAspect,enableMouse,invertY,enableMenu,name)
        self.img      = None
        self.NORMAL   = ViewMode(0,matplotlib.cm.gray)
        self.DEXA     = ViewMode(1,matplotlib.cm.jet)
        self.viewMode = self.NORMAL

    def showImage(self,arr):
        if arr==None:
            self.img = None
            return
        if self.img==None:
            self.img = pg.ImageItem(arr,autoRange=False,autoLevels=False)
            self.addItem(self.img)
        self.img.setImage(arr,autoLevels=False)
        self.updateView()

    def updateView(self):
        self.background.setBrush(fn.mkBrush(self.viewMode.lut[0]))
        self.background.show()
        if    self.img==None: return
        else: self.img.setLookupTable(self.viewMode.lut)


from pyqtgraph.Qt import QtCore,QtGui

class SidePanel(QtGui.QWidget):

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self,parent)
        self.setMinimumWidth(250)
        self.buttMinimumSize = QtCore.QSize(36,36)
        self.setupImageToolbox()
        sidePanelLayout = QtGui.QVBoxLayout()
        sidePanelLayout.addWidget(self.imageToolbox)
        sidePanelLayout.setContentsMargins(0,0,0,0)
        self.setLayout(sidePanelLayout)

    def setupImageToolbox(self):
        # Image buttons
        self.buttImageAdd  = QtGui.QPushButton()
        imageButtons       = [self.buttImageAdd]
        for i in xrange(len(imageButtons)):
            image = imageButtons[i]
            image.setMinimumSize(self.buttMinimumSize)

        self.imageFileTools  = QtGui.QFrame()
        imageFileToolsLayout = QtGui.QHBoxLayout()
        self.imageFileTools.setLayout(imageFileToolsLayout)
        self.imageFileTools.setLineWidth(1)
        self.imageFileTools.setFrameStyle(QtGui.QFrame.StyledPanel)
        imageFileToolsLayout.addWidget(self.buttImageAdd)

        # Image Toolbox (containing imageFileList + imageFileList buttons)
        self.imageToolbox = QtGui.QFrame()
        self.imageToolbox.setLineWidth(2)
        self.imageToolbox.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Raised)
        imageToolboxLayout = QtGui.QVBoxLayout()
        self.imageToolbox.setLayout(imageToolboxLayout)
        imageToolboxLayout.addWidget(self.imageFileTools)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

为方便起见的注释和引用

(numpy array) Specifies the image data. May be 2D (width, height) or 3D (width, height, RGBa). The array dtype must be integer or floating point of any bit depth. For 3D arrays, the third dimension must be of length 3 (RGB) or 4 (RGBA).

  • 回溯不包含我的代码行。这是我在完整程序中看到的内容

回溯(最近一次调用最后一次):

  File "/usr/lib/python2.7/dist-packages/pyqtgraph/graphicsItems/ImageItem.py", line 309, in paint
    self.render()
  File "/usr/lib/python2.7/dist-packages/pyqtgraph/graphicsItems/ImageItem.py", line 301, in render
    argb, alpha = fn.makeARGB(image.transpose((1, 0, 2)[:image.ndim]), lut=lut, levels=self.levels)
  File "/usr/lib/python2.7/dist-packages/pyqtgraph/functions.py", line 976, in makeARGB
    imgData[..., i] = data[..., order[i]] 
ValueError: could not broadcast input array from shape (256,256,4) into shape (256,256)
  • 在此错误回溯之后,pyqt 应用程序不会崩溃并保持对其他操作的响应。但我的 ViewBox 仍然是黑的

  • 在调用 updateView 之前,在 showImage 的 dubug 控制台中,我看到以下内容,似乎表明一切正常。

>>> self.img
<pyqtgraph.graphicsItems.ImageItem.ImageItem object at 0x7fc7140dbd60>
>> arr.ndim
3
  • 这让我认为问题出在 updateView 中。但这里的所有行都调用内置的 pyqtgraph 函数。
  • 以下是我在调用 setLookupTable 之前在调试控制台中得到的内容
>>> fn
<module 'pyqtgraph.functions' from '/usr/lib/python2.7/dist-packages/pyqtgraph/functions.pyc'>
>>> self.viewMode.lut[0]
array([0, 0, 0], dtype=uint8)
  • 最后,这是我的完整应用程序的屏幕截图,以防您看到我看不到的内容。当然请注意,这适用于灰度。我非常感谢 Micheal Hogg让我根据自己的目的修改他的代码。

enter image description here

最佳答案

如果您自己将图像转换为 RGBA 数组,则不应为图像分配 LUT。否则,将通过在 makeARGB 中调用的 applyLookupTable 函数添加第五维。因此,解决方案的第一步是删除 MultiRoiViewBox.updateView 中的 self.img.setLookupTable(self.viewMode.lut)

此外,通过查看 makeARGB 中的以下几行,您可以看到 PyQtGraph 期望 RGBA 值在 0 到 255 的范围内

if lut is not None:
    data = applyLookupTable(data, lut)
else:
    if data.dtype is not np.ubyte:
        data = np.clip(data, 0, 255).astype(np.ubyte)

但是,您使用 Matplotlib norm 来标准化 0 和 1 之间的值。因此,快速修复方法是将数组乘以 255

ARR = plt.cm.jet(norm(ARR)) * 255

但是,我可能不会像这样混合 MatPlotLib 和 PyQtGraph 以避免进一步的意外。此外,它可能会让其他程序员感到困惑,因此如果您这样做,请正确记录。

关于python - 如何将 RGB ImageItem 添加到 pyqtgraph ViewBox? ValueError : could not broadcast input array from shape (256, 256,4) 变为形状 (256,256),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36229937/

相关文章:

javascript - 随机生成的数组中的最大数

python - Opencv Python显示原始图像

python - 加速 python/numpy 中的动态编程

python - 如何在列表内部的 python 中运行嵌套循环,以便外循环始终从列表的下一个元素开始,依此类推

python - 我如何在一种布局中排列多个 pyplot 图形?

php - 如何在保留键的同时合并多维数组?

python - 如何切片 numpy 数组,使每个切片成为一个新数组

python - 无法将包上传到 PyPI : 410 Gone

python - TensorFlow 中 LSTMCell 单元中的权重是如何初始化的

两个字符串数组的 Java 比较