【问题标题】:QImage skews some images but not othersQImage 歪斜一些图像但不是其他的
【发布时间】:2017-05-26 14:26:42
【问题描述】:

我正在使用 tif 堆栈,QImage 似乎将一些图像倾斜到 45 度角。 Matplotlib 能够在两个测试用例中毫无问题地显示图像(下面提供了两个 tif 堆栈的链接),所以我认为我没有在某个地方搞砸了我的数组。

这是一个工作示例:(注意:为简单起见,此示例仅显示 tif 堆栈中的第一个图像)

import matplotlib.pyplot as plt
import sys
from PIL import Image
from PyQt5.QtGui import QPixmap, QImage 
from PyQt5.QtWidgets import (QMainWindow, QApplication, QVBoxLayout, 
                             QWidget, QFileDialog, QGraphicsPixmapItem, QGraphicsView,
                             QGraphicsScene)

import numpy as np


class Example(QMainWindow):
    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):
        # set up a widget to hold a pixmap
        wid = QWidget(self)
        self.setCentralWidget(wid)
        self.local_grview = QGraphicsView()
        self.local_scene = QGraphicsScene()
        vbox = QVBoxLayout()                
        self.local_grview.setScene( self.local_scene )
        vbox.addWidget(self.local_grview)
        wid.setLayout(vbox)

        # load and display the image
        self.loadImage()

        # display the widget
        self.show()

        # also use matplotlib to display the data as it should appear
        plt.imshow(self.dataUint8[0], cmap='gray')
        plt.show()


    def loadImage(self):
        fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')[0]

        # use the tif reader to read in the tif stack
        self.data = self.readTif(fname)

        # convert to uint8 for display
        self.dataUint8 = self.uint8Convert(self.data)

        ###############################################################################################################################
        # I suspect this is where something goes wrong
        ###############################################################################################################################
        # create a QImage object
        self.im = QImage(self.dataUint8[0], self.dataUint8[0].shape[1], self.dataUint8[0].shape[0], QImage.Format_Grayscale8)
        # if we save using self.im.save() we also have a skewed image
        ###############################################################################################################################

        # send the QImage object to the pixmap generator
        self.pixmap = QPixmap(self.im)


        self.pixMapItem = QGraphicsPixmapItem(self.pixmap, None)
        self.local_scene.addItem(self.pixMapItem)

    def readTif(self, filename): # use this function to read in a tif stack and return a 3D numpy array
        # read in the file
        stack = Image.open(filename)    

        # extract each frame from the file and store in the frames variable
        frames = []
        i = 0
        while True:
            try:
                stack.seek(i) # move to the ith position in the stack
                frames.append(np.array(stack) )
                i += 1
            except EOFError:
                # end of stack
                break
        del stack # probably unnecessary but this presumably saves a bit of memory

        return frames 


    def uint8Convert(self, frames): # use this function to scale a 3D numpy array of floats to 0-255 so it plays well with Qt methods

        # convert float array to uint8 array
        if np.min(frames)<0:
            frames_uint8 = [np.uint8((np.array(frames[i]) - np.min(frames[i]))/np.max(frames[i])*255) for i in range(np.shape(frames)[0])]
        else:
            frames_uint8 = [np.uint8(np.array(frames[i])/np.max(frames[i])*255) for i in range(np.shape(frames)[0])]

        return frames_uint8


if __name__=='__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

这是输出的截图:

Qimage 与 matplotlib

这是一个正确显示的 tif 堆栈的链接:

https://drive.google.com/uc?export=download&id=0B9EG5AHWC9qzX3NrNTJRb2toV2c

这里有一个 tif 堆栈的链接,它在显示时会倾斜:

https://drive.google.com/uc?export=download&id=0B9EG5AHWC9qzbFB4TDU4c2x1OE0

任何帮助理解为什么QImage 歪曲这张图片将不胜感激。两个 tif 堆栈之间的唯一主要区别在于,倾斜显示的堆栈在图像周围有一个填充的黑色区域(零),这使得数组更大。

更新:我现在发现,如果我将有问题的图像裁剪为 1024x1024 或 512x512 或 1023x1024 QImage 会正确显示,但按 1024x1023 裁剪会显示倾斜。所以看起来 x(水平)长度必须是 2 的幂,以便QImage 按预期处理它。这是一个可笑的限制!一定有什么我不明白的。它肯定有办法处理任意形状的数组。

...我想,原则上,一个人可以先对图像应用倾斜,然后让QImage 将其倾斜回来...(

【问题讨论】:

  • @ekhumoro 我同意!那是我第一次使用该文件托管网站。我不会再使用它们了。我已将链接更新为直接下载。我也没有意识到这些文件有多大,这些新文件要轻得多。下载应该只需要一两秒钟。
  • 谢谢。 FWIW,我可以重现该问题,但恐怕我目前对原因没有任何见解。
  • 使用 QImage(fname)。 QImage 支持 Tiff。
  • @eyllanesc 使用self.im = QImage(fname) 确实可以正常显示!但是我如何访问堆栈中的其他图像? QImage 似乎只读取了第一张图片。
  • 另外,如果我每次都必须从 tif 文件中读取以使 QImage 正确显示,那么这意味着我必须将数据写入磁盘并在每次我想要的时候读回更新图像。

标签: python matplotlib python-3.5 pyqt5 qimage


【解决方案1】:

图像没有歪斜,底层数据被错误解释。

在您使用的构造函数中,数据缓冲区是平坦的,您还必须指定行和列的大小(以像素为单位)。您以某种方式将行指定为太长,以便下一行的开头被包裹到当前行的末尾。这就是为什么你会得到图像的“条带化”,以及为什么在你到达后面的行时会有越来越多的包装量。这也解释了为什么当您使用构造函数的QImage(fname) 版本时它会起作用。该构造函数使用Qt库代码读取图像数据,不存在您自己的代码存在的问题。

有几个地方可能会错误地读取数据。我不知道 PIL 包的详细信息,但 np.array(stack) 行看起来像是一个合理的候选者。我不知道堆栈对象如何公开缓冲区接口,但它的做法可能与您想象的不同,例如,数据是列优先的而不是行优先的。另请注意,您使用的 QImage 构造函数期望数据是 32 位对齐的,即使是 8 位数据也是如此。这可能是个问题。

另一个可能的候选方法是uint8Convert 方法,它可能会无意中转置数据或以其他方式向前/向后滚动数据。这可能就是为什么方形尺寸有效,而矩形无效。

【讨论】:

  • 明确地说,我可以得到矩形和正方形来工作或失败。所以不管是长方形还是正方形。此外,我正在使用matplotlib 验证数组本身不是“条带化”,所以我认为np.array(stack)uint8Convert 不会弄乱数组(matplotlib 绘图是在这两种方法都完成后完成的称为)。但是...... 32 位对齐的要求听起来像是一个可能的问题。我真的不明白这是什么意思。你能详细说明一下吗?
  • 酷,我同意你的观点,np.array() 调用和你的uint8Convert 方法可能没问题。对齐约束意味着每行数据必须从内存中的 32 位边界开始。这似乎是问题所在。我在 (deep) 中挖掘了 Qt C++ 源代码,这似乎是合理的,这会导致问题。 (请参阅此处开始,code.woboq.org/qt5/qtbase/src/gui/image/qimage.cpp.html#804,如果您需要,我可以发送更多详细信息。)如果您不使用 8 位数据,而是使用 32 位,会发生什么情况?
  • 感谢您的链接。这帮助我弄清楚了。可以选择指定数组中每行的字节数。提供此值可解决问题。我会发布代码...
【解决方案2】:

非常感谢 bnaecker 提供 32 位对齐的提示并提供源链接。这是解决方案。

QImage 需要知道数组每行有多少字节,否则它只会猜测(在某些情况下它会猜测错误)。因此,在loadImage() 函数中使用以下内容会产生正确的输出。

# get the shape of the array
nframes, height, width = np.shape(self.dataUint8)

# calculate the total number of bytes in the frame 
totalBytes = self.dataUint8[0].nbytes

# divide by the number of rows
bytesPerLine = int(totalBytes/height)

# create a QImage object 
self.im = QImage(self.dataUint8[0], width, height, bytesPerLine, QImage.Format_Grayscale8)

其余代码相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-30
    • 1970-01-01
    • 2018-10-19
    • 1970-01-01
    • 2013-06-27
    • 2013-07-19
    • 1970-01-01
    相关资源
    最近更新 更多