python imread():在阅读png文件时保留原始灰度值

szqfcxe2  于 2023-11-15  发布在  Python
关注(0)|答案(2)|浏览(117)

我想处理一张以png格式保存的12位灰度图像。使用单色12位相机作为信号源。图像文件是用NI Vision IMAQ Write File2.vi创建的。最大像素值为419计数。当我用Python 3 imageio-2.32.0imageio.imread("testimagefile")加载图像时,图像似乎是自动缩放的,最大值现在是55019计数。我怎样才能保持原来的灰度值?

wlzqhblo

wlzqhblo1#

这是查找sBIT块并提取比例因子的实现(恕我直言,非常糟糕)的替代方案。
PNG块看起来像这样:
x1c 0d1x的数据
来源:Wikipedia
sBIT块在十六进制中看起来像这样:

00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 056c 0000 040e 1000 0000 003a b9f2  ...l.........:..
00000020: 5200 0000 0173 4249 5409 910d 6b0f 0000  R....sBIT...k...

字符串
在十六进制中,73 42 49 54(在第三行的中间)表示sBIT,因此长度是之前的4个字节,即00 00 00 01,这意味着块中有1个值-这是灰度图像中的预期值。根据图像类型,可能最多有4个值(grey,grey+alpha,RGB,RGBA)。这意味着在此图像中仅使用1个字节。该字节为09,意味着值为9。
因此,您可以更简单地像这样提取sBIT:

#!/usr/bin/env python3

from pathlib import Path

# Load PNG as bytes and find "sBIT" chunk
png = Path('120130.png').read_bytes()
sBIToffset = png.find(b'sBIT')
if sBIToffset:
    # 4 bytes before "sBIT" tell us how many values there are - could be 1-4 values
    nValues = int.from_bytes(png[sBIToffset-4:sBIToffset], byteorder="big")
    values  = list(png[sBIToffset+4:sBIToffset+4+nValues])
    print(f'Found sBIT chunk at offset: {sBIToffset} with value(s): {values}')


我将另一个实现描述为 “相当差”,因为它花费时间在内存中移动文件的整个内容,并且不处理多通道图像的多个有效位-它还读取文件两次,并且......

d5vmydt9

d5vmydt92#

我终于在NI论坛上得到了答案。非常感谢Andrew Dmitriev。
NI的16位PNG图像与sBIT存储在一起。这意味着像素的值被移到最高位(这是PNG标准的一部分,但大多数库都不知道这一点)。请参阅http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.sBIT
此外,有符号表示的偏移量存储在scAl块的前两个字节中。所以,所有你需要的-只需读取“RAW”png数据,然后根据sBIT值执行移位,然后减去从scAl块中获取的偏移量。这就是全部(如果你想在没有“额外”DLL的情况下完成任务)。附加I16和U16图像的示例。
顺便说一句-有一个错误,在PNG编码器从NI。而不是逻辑移位,他们执行旋转(以某种奇怪的形式),所以应该是零的较低有效位-不是零,因此我们有上面提到的这些奇怪的值(例如,当范围0.1000变成0.64062时,它应该是0.64000)。但这不是一个大问题,因为这些位在移回后就消失了。
同样在手册中,它错误地指出使用位深度输入仅适用于有符号的图像。在有符号的图像上,此控件将切断负值,但在无符号的图像上(当与IMAQ图像位深度一起使用时),此控件将允许保存“原生”16位值,因此图像可以在第三方库中打开而无需转换。
以下是dgagnon05使用openCV的代码:

import cv2
import struct
import binascii
import numpy as np
from matplotlib import pyplot as plt

def __getSBit(bytes):
    bytes = bytes[8:]
    sBit = 0
    while bytes:
        length = struct.unpack('>I', bytes[:4])[0]
        bytes = bytes[4:]
        chunk_type = bytes[:4]
        bytes = bytes[4:]
        chunk_data = bytes[:length]
        bytes = bytes[length:]
        if chunk_type == b'sBIT':
            sBit = int.from_bytes(chunk_data, "big")
            break
        bytes = bytes[4:]
    return sBit

def __getOffset(bytes):
    bytes = bytes[8:]
    Offset = 0
    sBit = 0
    while bytes:
        length = struct.unpack('>I', bytes[:4])[0]
        bytes = bytes[4:]
        chunk_type = bytes[:4]
        bytes = bytes[4:]
        chunk_data = bytes[:length]
        bytes = bytes[length:]
        if chunk_type == b'scAl':
            chunk_data_part = chunk_data[:2]
            Offset = 65536 - int.from_bytes(chunk_data_part, "big")
            break
        bytes = bytes[4:]
    return Offset

def getSigBits(filename):
    with open(filename, 'rb') as f:
        bytes = f.read()
    return __getSBit(bytes)

def getOffset(filename):
    with open(filename, 'rb') as f:
        bytes = f.read()
    return __getOffset(bytes)

def shift_offset(image_src, shift, offset):
    height, width = image_src.shape
    counter=0;
    if shift<16:
        temp=np.zeros(image_src.shape,dtype=np.int16)
        temp=(image_src>> shift).astype(np.int16)
        temp=temp-offset
        return temp

def load_lv_i16(file_path):
    offset=getOffset(file_path)
    sigbits=getSigBits(file_path)
    image_src=cv2.imread(file_path, cv2.IMREAD_UNCHANGED)
    shift=16-sigbits
    image=shift_offset(image_src,shift,offset)
    return image

if __name__ == '__main__':
    file_path=r"LabVIEW_crated_12bit_grayscale.png"
    image=load_lv_i16(file_path)

字符串

相关问题