我能不能用“Normal”(恩思德)PYTHON将NumPy数组保存为16位图像?

w3nuxt5m  于 2022-11-10  发布在  Python
关注(0)|答案(5)|浏览(185)

有没有办法将NumPy数组保存为16位图像(tif、png),使用任何常见的python包?This是我过去唯一可以去工作的方式,但我需要安装FreeImage包,这有点烦人。
这似乎是一个非常基本的任务,所以我预计它应该由scipy来完成,但scipy.misc.imsave只支持8位。
有什么主意吗?

xn1cxnb4

xn1cxnb41#

一种替代方法是使用pypng。您仍然需要安装另一个包,但它是纯Python,所以这应该很容易。(实际上在pypng源代码中有一个Cython文件,但它的使用是可选的。)
下面是一个使用pypng将NumPy数组写入PNG的示例:

import png

import numpy as np

# The following import is just for creating an interesting array

# of data.  It is not necessary for writing a PNG file with PyPNG.

from scipy.ndimage import gaussian_filter

# Make an image in a numpy array for this demonstration.

nrows = 240
ncols = 320
np.random.seed(12345)
x = np.random.randn(nrows, ncols, 3)

# y is our floating point demonstration data.

y = gaussian_filter(x, (16, 16, 0))

# Convert y to 16 bit unsigned integers.

z = (65535*((y - y.min())/y.ptp())).astype(np.uint16)

# Use pypng to write z as a color PNG.

with open('foo_color.png', 'wb') as f:
    writer = png.Writer(width=z.shape[1], height=z.shape[0], bitdepth=16,
                        greyscale=False)
    # Convert z to the Python list of lists expected by
    # the png writer.
    z2list = z.reshape(-1, z.shape[1]*z.shape[2]).tolist()
    writer.write(f, z2list)

# Here's a grayscale example.

zgray = z[:, :, 0]

# Use pypng to write zgray as a grayscale PNG.

with open('foo_gray.png', 'wb') as f:
    writer = png.Writer(width=z.shape[1], height=z.shape[0], bitdepth=16,
                        greyscale=True)
    zgray2list = zgray.tolist()
    writer.write(f, zgray2list)

以下是颜色输出:

下面是灰度输出:

  • 更新*:我创建了一个名为numpngw的库(在PyPIgithub上可用),它提供了将NumPy数组写入PNG文件的函数。该存储库有一个setup.py文件,用于将其作为包安装,但基本代码位于一个文件numpngw.py中,该文件可以复制到任何方便的位置。numpngw的唯一依赖项是NumPy。

下面是一个脚本,它生成与上面所示相同的16位图像:

import numpy as np
import numpngw

# The following import is just for creating an interesting array

# of data.  It is not necessary for writing a PNG file.

from scipy.ndimage import gaussian_filter

# Make an image in a numpy array for this demonstration.

nrows = 240
ncols = 320
np.random.seed(12345)
x = np.random.randn(nrows, ncols, 3)

# y is our floating point demonstration data.

y = gaussian_filter(x, (16, 16, 0))

# Convert y to 16 bit unsigned integers.

z = (65535*((y - y.min())/y.ptp())).astype(np.uint16)

# Use numpngw to write z as a color PNG.

numpngw.write_png('foo_color.png', z)

# Here's a grayscale example.

zgray = z[:, :, 0]

# Use numpngw to write zgray as a grayscale PNG.

numpngw.write_png('foo_gray.png', zgray)
nue99wik

nue99wik2#

这个关于PNG和Numpngw的解释非常有帮助!但是,有一个小“错误”我想我应该提一提。在转换为16位无符号整数时,y.max()应该是y.min()。对于随机颜色的图片,这并不重要,但对于真实的图片,我们需要正确地处理它。以下是更正后的代码行。

z = (65535*((y - y.min())/y.ptp())).astype(np.uint16)
p1iqtdky

p1iqtdky3#

您可以将16位数组转换为双通道图像(甚至可以将24位数组转换为3通道图像)。像这样的东西运行得很好,只需要NumPy:

import numpy as np
arr = np.random.randint(0, 2**16, (128, 128), dtype=np.uint16)  # 16-bit array
print(arr.min(), arr.max(), arr.dtype)
img_bgr = np.zeros((*arr.shape, 3), np.int)
img_bgr[:, :, 0] = arr // 256
img_bgr[:, :, 1] = arr % 256
cv2.imwrite('arr.png', img_bgr)

# Read image and check if our array is restored without losing precision

img_bgr_read = cv2.imread('arr.png')
B, G, R = np.split(img_bgr_read, [1, 2], 2)
arr_read = (B * 256 + G).astype(np.uint16).squeeze()
print(np.allclose(arr, arr_read), np.max(np.abs(arr_read - arr)))

结果:

0 65523 uint16
True 0
ccgok5k5

ccgok5k54#

如前所述,PyPNG非常有用。对于EnThink用户,可以将其安装为:

conda install -c eaton-lab pypng

我会使用货架的from_array方法:

import png
import numpy as np

bit_depth = 16

my_array = np.ones((800, 800, 3)) 

png.from_array(my_array*2**bit_depth-1, 'RGB;%s'%bit_depth).save('foo.png')

模式使用PIL样式格式,例如“L”、“LA”、“RGB”或“RGBA”,后跟“;16”或“;8”太设置位深度。如果省略位深度,则使用数组的数据类型。
阅读更多here

bqf10yzr

bqf10yzr5#

创建了一个仅使用NumPy和OpenCV即可完成此任务的定制脚本:(尽管感觉仍有些过头了……)

import numpy as np
import cv2

def save_gray_deep_bits(filepath, float_array, bitdepth=16):
    assert bitdepth in [8,16,24]
    arr = np.squeeze(float_array)
    assert len(arr.shape) == 2
    assert '.png' in filepath

    bit_iterations = int(bitdepth/8)
    img_bgr = np.zeros((*arr.shape, 3), np.uint8)
    encoded = np.zeros(arr.shape, np.uint8)

    for i in range(bit_iterations):
        residual = float_array - encoded
        plane_i = (residual*(256**i)).astype(np.uint8)
        img_bgr[:,:,i] = plane_i
        encoded += plane_i

    cv2.imwrite(filepath, img_bgr)
    return img_bgr

def bgr_to_gray_deep_bits(bgr_array, bitdepth=16):
    gray = np.zeros((bgr_array.shape[0], bgr_array.shape[1]), dtype = np.float32)
    for i in range(int(bitdepth/8)):
        gray += bgr_array[:,:,i] / float(256**i)
    return gray

def load_gray_deep_bits(filepath, bitdepth=16):
    bgr_image = cv2.imread('test.png').astype(np.float64)
    gray_reconstructed = bgr_to_gray_deep_bits(bgr_image, bitdepth = bd)
    return gray_reconstructed

bd = 24
gray_image_full_precision = np.random.rand(1024, 1024)*255.
save_gray_deep_bits('test.png', gray_image_full_precision, bitdepth = bd)

# Read image and check if our array is restored without losing precision

bgr_image = cv2.imread('test.png').astype(np.float64)
gray_reconstructed = bgr_to_gray_deep_bits(bgr_image, bitdepth = bd)
avg_residual = np.mean(np.abs(gray_reconstructed - gray_image_full_precision))
print("avg pixel residual: %.3f" %avg_residual)

相关问题