【问题标题】:Convert 16-bit grayscale to QImage将 16 位灰度转换为 QImage
【发布时间】:2013-03-18 08:29:57
【问题描述】:

我正在开发基于 PyQt4 GUI 的基于传感器的 Python 应用程序。传感器正在生成 16 位测量值……每条“线”有 256 个 16 位“像素”。通过获得 256 行来获取一个正方形的“图像”,从而得到一个 (256,256) 个 16 位数字的 Numpy 数组。我只是想将其显示为灰度图像。传感器循环在 QThread 中运行并发出 QImage 信号。该信号连接到一个插槽,该插槽通过将数据打包成 32 位 RGB 图像来呈现主 GUI 中的数据。当然,要将 16 位灰度像素打包成 32 位 RGB 图像,我不得不将 16 位像素缩放为 8 位,并且会丢失大量动态范围。提供了一个 MWE,显示了我当前的策略(这显然不是我基于传感器的大型线程应用程序......它只是提取了显着部分)。请注意,我是一名 Python 初学者,我正在努力跟上...

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Grayscale to RGB32 QPixmap tests
"""

import sys
import numpy as np
from PyQt4 import QtGui, QtCore

class PixmapTest(QtGui.QWidget):

    def __init__(self):
        super(PixmapTest, self).__init__()
        self.initUI()

    def initUI(self):      
        imglayout = QtGui.QHBoxLayout(self)

        img_16bit = np.random.randint(0,65535,size=(256,256)).astype(np.uint32)
        img_16bit_to_8bit = (img_16bit / 65535.0 * 255).astype(np.uint32)
        packed_img_array = (255 << 24 | (img_16bit_to_8bit) << 16 | (img_16bit_to_8bit) << 8 | (img_16bit_to_8bit)).flatten()
        img = QtGui.QImage(packed_img_array, 256, 256, QtGui.QImage.Format_RGB32)
        pixmap = QtGui.QPixmap(img.scaledToWidth(img.width()*2))

        imglabel = QtGui.QLabel(self)
        imglabel.setPixmap(pixmap)

        imglayout.addWidget(imglabel)
        self.setLayout(imglayout)

        self.move(300, 200)
        self.setWindowTitle('QPixmap Test')
        self.show()        

def main():

    app = QtGui.QApplication(sys.argv)
    form = PixmapTest()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main() 

具体来说,我的问题是:

  1. 有没有更好的方法?解决方案必须保持“轻量级”(即 PyQt4 QImage/QPixmap)。我不能使用 Matplotlib 或任何重量级的东西,因为它太慢了。越接近原生 Python/Numpy 越好。我意识到这最终是 QImage 类的限制,但我希望有一个聪明的解决方案,我只是没有看到让我保持当前的信号/插槽“接线”。

  2. 通过实验,我发现我必须将所有最终在 QImage 中处理的数组声明为 np.uint32(尽管 np.int32 似乎也可以工作)。如果我只是将倒数第二个数组声明为 uint32/int32,则它不起作用。我不明白为什么。

  3. 我已经尝试过使用Y' = 0.2126 * R + 0.7152 * G + 0.0722 * B 和其他类似的转换来改变亮度。可能在这里“抛光粪便”,但我想我会包括这个,因为 SX 上的其他答案似乎表明这很重要。尽管动态范围有所损失,但似乎可以像在我的 MWE 中那样简单地为 R、G、B 分配相同的值。

根据下面评论中的要求,这里是来自传感器的一些样本数据的直方图,用于说明动态范围:

【问题讨论】:

  • 你能显示传感器数据的直方图吗?也许它没有使用全系列的 16 位。例如,您可以通过img_16bit / (img_16bit.max()/255.0) 将最大值设置为白色。您还可以找到传感器数据的最大值和最小值并将它们映射到 255 和 0。
  • @HYRY,添加了直方图,显示我正在使用全范围的 16 位...
  • 所以,大部分数据都在 25000 - 45000 范围内,也许将数据裁剪到这个范围并映射到 0 - 255 会得到更好的结果。
  • 换个角度看,我想知道是否对 16 位灰度进行编码是否重要。我在qtcentre 上发现了一篇帖子,似乎表明典型的显示器无论如何都不能显示超过 8 位灰度。 Others 建议典型的人类只能感知 32 到 64 种灰度。
  • @HYRY 裁剪数据不是一种选择......那些外围数据点与 25000-45000 范围内的数据点一样重要(如果不是更重要的话),因为它们可能是由于信号,而不是噪音。有了传感器数据,每个像素都很重要……

标签: python numpy pyqt4 qimage qpixmap


【解决方案1】:

这里我用一些函数数据做demo:

y, x = np.mgrid[-10:10:256j, -10:10:256j]
data = ((np.sin(y**2 + x**2) + 2) * 1000).astype(np.uint16)

img_8bit = (data / 256.0).astype(np.uint8) # use the high 8bit
img_8bit = ((data - data.min()) / (data.ptp() / 255.0)).astype(np.uint8) # map the data range to 0 - 255
img = QtGui.QImage(img_8bit.repeat(4), 256, 256, QtGui.QImage.Format_RGB32)

使用高8bit时,如下图:

将最小值和最大值映射到 (0, 255) 时,如下所示:

要将8bit图像转换为32bit,只需调用img_8bit.repeat(4),这将每个字节重复4次,因此可以将内存视为uint32缓冲区。由于您是通过Format_RGB32 而不是Format_ARGB32 创建QImage,因此不使用最高有效字节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-24
    • 1970-01-01
    相关资源
    最近更新 更多