Python 如何将RGB ImageItem添加到pyqtgraph ViewBox?ValueError:无法将输入数组从形状(256256,4)广播到形状(256256)

Python 如何将RGB ImageItem添加到pyqtgraph ViewBox?ValueError:无法将输入数组从形状(256256,4)广播到形状(256256),python,arrays,numpy,pyqt4,pyqtgraph,Python,Arrays,Numpy,Pyqt4,Pyqtgraph,最新更新日期:2016年3月29日: 如果你复制粘贴下面的代码,你应该得到一个带有按钮的pyqt应用程序。如果单击它,则会将灰度添加到ViewBox中。如果您重写声明了ARR\u OFF的位置,以便ARR=plt.cm.jet(norm(ARR))那么您就得到了我正在遭受的错误。我想在ViewBox中将ARR显示为彩色贴图,因此我将其转换为RGBA数组 import os, sys, matplotlib, matplotlib.pyplot import numpy as np from py

最新更新日期:2016年3月29日:

如果你复制粘贴下面的代码,你应该得到一个带有按钮的pyqt应用程序。如果单击它,则会将灰度添加到ViewBox中。如果您重写声明了
ARR\u 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_())

为方便起见,请提供注释和参考资料
  • 我知道ImageView,它包含ImageItem的ViewBox
  • 阅读后,我发现要让setImage与RGBA一起工作,我只需要向它传递一个数组,数组的尺寸指定RGBA通道
(numpy数组)指定图像数据。可以是2D(宽度、高度)或 3D(宽度、高度、RGBa)。数组数据类型必须是整数或浮点型 任何位深度的点。对于三维阵列,第三维必须为 长度3(RGB)或4(RGBA)

  • 回溯不包含我的代码行。以下是我在完整课程中看到的内容
回溯(最近一次呼叫最后一次):

  • pyqt应用程序不会崩溃,并在错误回溯后对其他操作保持响应。但我的视图框仍然是黑色的

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

  • 最后,这里是我完整应用的截图,以防你看到我没有看到的东西。请注意,这当然适用于灰度。我非常感谢他让我为我的目的修改他的代码

在这条线的某个地方,您的
256x256x4
阵列正在获得第四维度。我可以通过添加额外的维度并运行与pyqtgraph相同的操作来重现错误

>>> import numpy as np
>>> a = np.empty((256,256,4,4), dtype=np.ubyte)
>>> b = np.empty((256,256,4), dtype=np.ubyte)
>>> b[..., 0] = a[..., 1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not broadcast input array from shape (256,256,4) into shape (256,256)
>>将numpy作为np导入
>>>a=np.empty((256256,4,4),dtype=np.ubyte)
>>>b=np.empty((256256,4),dtype=np.ubyte)
>>>b[…,0]=a[…,1]
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
ValueError:无法将输入数组从形状(256256,4)广播到形状(256256)

您可能需要在整个代码中一直打印
ndim
。您甚至可能需要编辑
pyqtgraph
代码来定位更改发生的时间。

如果您自己将图像转换为RGBA数组,则不应为图像分配LUT。否则,在
makeARGB
中调用的
applyLookupTable
函数将添加第五维。因此,解决方案的第一步是删除
multiroviewbox.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来避免进一步的意外。此外,它可能会使其他程序员感到困惑,因此,如果您这样做,请正确记录它

自我形象和自我形象之间有什么关系。你的程序流程和类型不是很清楚。请参见self.image是numpy数组。self.img是由self.image制成的ImageItem。因此,self.img=pg.ImageItem(self.image)。是的,我刚刚注意到我的代码中有一个重要的输入错误。经过一些预处理和平移/旋转后,self.arr就是self.image。但这对解决我的问题并不重要,所以我修改了问题代码thanx@BrendanAbel-在阅读了你给我的链接后,我做了进一步的主要编辑。如果我们能自己重现这个问题,这真的会有帮助。请确认我们可以直接复制粘贴执行。我想我认为答案是显而易见的,因为文档说支持RGBA。虽然你是对的,但我不应该做出这样的假设。你的愿望已经实现了。我想问题可能在updateView中。问题是,如果您仔细阅读我的代码,您将看到数组在showImage中变成了一个ImageItem,它没有ndim属性。我对我的问题进行了编辑,以表明ndim在调用最终函数updateView之前仍然是3,该函数完全依赖于pyqtgraph代码(即不是我编写的)。所以我的问题是:我是在错误地使用pyqtgraph,还是必须修改实际的库?后一种情况似乎是荒谬的,因为有很多彩色图像在pyqtgraph中工作的例子。添加了最小的、完整的和可验证的示例,让您知道。我希望它能使发现问题变得容易!我意识到255的问题,并确实认为公约将只是乘以255。否则,您将如何将使用matplotlib计算的本应为彩色贴图的numpy数组转换为pyqtgraph可以使用的RGB?只要我——正如你所说——更好地记录它,这种方式看起来相当干净。预计在5小时内重复+50次。十亿吨泰特斯詹。
>>> import numpy as np
>>> a = np.empty((256,256,4,4), dtype=np.ubyte)
>>> b = np.empty((256,256,4), dtype=np.ubyte)
>>> b[..., 0] = a[..., 1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not broadcast input array from shape (256,256,4) into shape (256,256)
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)
ARR = plt.cm.jet(norm(ARR)) * 255