[英]Read 16-bit PNG image file using Python
I'm trying to read a PNG image file written in 16-bit data type.我正在尝试读取以 16 位数据类型编写的 PNG 图像文件。 The data should be converted to a NumPy array.
数据应转换为 NumPy 数组。 But I have no idea how to read the file in '16-bit'.
但我不知道如何以“16 位”读取文件。 I tried with PIL and SciPy, but they converted the 16-bit data to 8-bit when they load it.
我尝试使用 PIL 和 SciPy,但它们在加载时将 16 位数据转换为 8 位。 Could anyone please let me know how to read data from a 16-bit PNG file and convert it to NumPy array without changing the datatype?
谁能让我知道如何从 16 位 PNG 文件中读取数据并将其转换为 NumPy 数组而不更改数据类型?
The following is the script that I used.以下是我使用的脚本。
from scipy import misc
import numpy as np
from PIL import Image
#make a png file
a = np.zeros((1304,960), dtype=np.uint16)
a[:] = np.arange(960)
misc.imsave('16bit.png',a)
#read the png file using scipy
b = misc.imread('16bit.png')
print "scipy:" ,b.dtype
#read the png file using PIL
c = Image.open('16bit.png')
d = np.array(c)
print "PIL:", d.dtype
I have the same problem here.我在这里也有同样的问题。 I tested it even with 16 bit images i created by my own.
即使使用我自己创建的 16 位图像,我也对其进行了测试。 All of them were opened correctly when i loaded them with the png package.
当我用 png 包加载它们时,所有这些都被正确打开。 Also the output of 'file ' looked okay.
'file' 的输出也看起来不错。
Opening them with PIL always led to 8-bit numpy-arrays.用 PIL 打开它们总是会导致 8 位 numpy 数组。
Working with Python 2.7.6 on Linux btw.顺便说一句,在 Linux 上使用 Python 2.7.6。
Like this it works for me:像这样它对我有用:
import png
import numpy as np
reader = png.Reader( path-to-16bit-png )
pngdata = reader.read()
px_array = np.array( map( np.uint16, pngdata[2] )
print( px_array.dtype )
Maybe someone can give more information under which circumstances the former approach worked?也许有人可以提供更多信息,前一种方法在哪种情况下有效? (as this one is pretty slow)
(因为这个很慢)
Thanks in advance.提前致谢。
The simplest solution I've founded:我建立的最简单的解决方案:
When I open a 16 bit monochrome PNG Pillow doesn't open it correctly as I;16
mode.当我打开 16 位单色 PNG Pillow 时,它无法像
I;16
模式那样正确打开。 Image.mode
is opened as I
(32 bits) Image.mode
打开为I
(32 位)
So, the best way to convert to numpy Array.因此,转换为 numpy 数组的最佳方法。 It is dtype="int32" so we will convert it to dtype="uint16".
它是 dtype="int32",因此我们将其转换为 dtype="uint16"。
import numpy as np
from PIL import Image
im = Image.fromarray(np.array(Image.open(name)).astype("uint16"))
print("Image mode: ",im.mode))
Tested in Python 3.6.8 with Pillow 6.1.0在 Python 3.6.8 和 Pillow 6.1.0 中测试
This happens because PIL does not support 16-bit data, explained here: http://effbot.org/imagingbook/concepts.htm发生这种情况是因为 PIL 不支持 16 位数据,解释如下: http : //effbot.org/imagingbook/concepts.htm
I use a work around using the osgeo gdal package (which can read PNG).我使用 osgeo gdal 包(可以读取 PNG)来解决这个问题。
#Import
import numpy as np
from osgeo import gdal
#Read in PNG file as 16-bit numpy array
lon_offset_px=0
lat_offset_px=0
fn = 'filepath'
gdo = gdal.Open(fn)
band = gdo.GetRasterBand(1)
xsize = band.XSize
ysize = band.YSize
png_array = gdo.ReadAsArray(lon_offset_px, lat_offset_px, xsize, ysize)
png_array = np.array(png_array)
This will return这将返回
png_array.dtype
dtype('uint16')
A cleaner way I found is using the skimage package.我发现一种更简洁的方法是使用 skimage 包。
from skimage import io
im = io.imread(jpg)
Where 'im' will be a numpy array.其中 'im' 将是一个 numpy 数组。 Note: I haven't tested this with PNG but it works with TIFF files
注意:我没有用 PNG 测试过这个,但它适用于 TIFF 文件
I'd recommend using opencv:我建议使用 opencv:
pip install opencv-python
and和
import cv2
image = cv2.imread('16bit.png', cv2.IMREAD_UNCHANGED)
uint16
.uint16
。I'm using png module: At first install png by:我正在使用 png 模块:首先通过以下方式安装 png:
>pip install pypng
Then然后
import png
import numpy as np
reader = png.Reader('16bit.png')
data = reader.asDirect()
pixels = data[2]
image = []
for row in pixels:
row = np.asarray(row)
row = np.reshape(row, [-1, 3])
image.append(row)
image = np.stack(image, 1)
print(image.dtype)
print(image.shape)
I've been playing with this image using PIL version 5.3.0:我一直在使用 PIL 版本 5.3.0 来处理这个图像:
it reads the data just fine:它读取数据就好了:
>>> image = Image.open('/home/jcomeau/Downloads/grayscale_example.png')
>>> image.mode
'I'
>>> image.getextrema()
(5140, 62708)
>>> image.save('/tmp/test.png')
and it saves in the right mode, however the contents are not identical:它以正确的模式保存,但内容不相同:
jcomeau@aspire:~$ diff /tmp/test.png ~/Downloads/grayscale_example.png
Binary files /tmp/test.png and /home/jcomeau/Downloads/grayscale_example.png differ
jcomeau@aspire:~$ identify /tmp/test.png ~/Downloads/grayscale_example.png
/tmp/test.png PNG 85x63 85x63+0+0 16-bit sRGB 6.12KB 0.010u 0:00.000
/home/jcomeau/Downloads/grayscale_example.png PNG 85x63 85x63+0+0 16-bit sRGB 6.14KB 0.000u 0:00.000
however, image.show()
always converts to 8-bit grayscale, clamped at 0 and 255. so it's useless for seeing what you've got at any stage of the transformation.然而,
image.show()
总是转换为 8 位灰度,固定在 0 和 255。所以在转换的任何阶段查看你所得到的都是无用的。 while I could write a routine to do so, and perhaps even monkeypatch .show()
, I just run the display
command in another xterm.虽然我可以编写一个例程来这样做,甚至可能是monkeypatch
.show()
,但我只是在另一个xterm 中运行display
命令。
>>> image.putdata([n - 32768 for n in image.getdata()])
>>> image.getextrema()
(-27628, 29940)
>>> image.save('/tmp/test2.png')
note that converting to mode I;16
doesn't help:请注意,转换为模式
I;16
没有帮助:
>>> image.convert('I;16').save('/tmp/test3.png')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/jcomeau/.local/lib/python2.7/site-packages/PIL/Image.py", line 1969, in save
save_handler(self, fp, filename)
File "/home/jcomeau/.local/lib/python2.7/site-packages/PIL/PngImagePlugin.py", line 729, in _save
raise IOError("cannot write mode %s as PNG" % mode)
IOError: cannot write mode I;16 as PNG
You can also use the excellent OpenImageIO library's Python API.您还可以使用优秀的OpenImageIO库的 Python API。
import OpenImageIO as oiio
img_input = oiio.ImageInput.open("test.png") # Only reads the image header
pix = img_input.read_image(format="uint16") # Reads the pixels into a Numpy array
OpneImageIO is used extensively in the VFX industry, so most Linux distros come with a native package for it. OpneImageIO 在 VFX 行业中被广泛使用,因此大多数 Linux 发行版都带有一个本地包。 Unfortunately the otherwise excellent documentation is in PDF format (I personally prefer HTML), look for it in
/usr/share/doc/OpenImageIO
.不幸的是,其他优秀的文档是 PDF 格式(我个人更喜欢 HTML),请在
/usr/share/doc/OpenImageIO
。
Another option to consider, based on Mr. Fridy's answer , is to load it using pypng like this:根据 Fridy 先生的回答,另一个要考虑的选择是使用 pypng 加载它,如下所示:
import png
pngdata = png.Reader("path/to/16bit.png").read_flat()
img = np.array(pngdata[2]).reshape((pngdata[1], pngdata[0], -1))
You can install pypng using pip:您可以使用 pip 安装 pypng:
pip install pypng
The dtype from png.Reader.read_flat() is correctly uint16 and the reshaping of the np.ndarray
puts it into (height, width, channels) format.来自 png.Reader.read_flat() 的 dtype 是正确的 uint16 并且
np.ndarray
的重塑将其放入(高度、宽度、通道)格式。
imageio library supports 16bit images: imageio 库支持 16 位图像:
from imageio import imread, imwrite
import numpy as np
from PIL import Image
#make a png file
a = np.arange(65536, dtype=np.uint16).reshape(256,256)
imwrite('16bit.png',a)
#read the png file using imageio
b = imread('16bit.png')
print("imageio:" ,b.dtype)
#imageio: uint16
#read the png file using PIL
c = Image.open('16bit.png')
d = np.array(c)
print("PIL:", d.dtype)
# PIL: int32
Using imagemagick:使用 imagemagick:
>> identify 16bit.png
16bit.png PNG 256x256 256x256+0+0 16-bit Grayscale Gray 502B 0.000u 0:00.000
I suspect your "16 bit" PNG is not 16-bit.我怀疑您的“16 位”PNG 不是 16 位。 (if you're on Linux or Mac you could run
file 16bit.png
and see what it says) (如果您使用的是 Linux 或 Mac,您可以运行
file 16bit.png
并查看其内容)
When I use PIL and numpy I get a 32-bit array with 16-bit values in it:当我使用 PIL 和 numpy 时,我得到一个 32 位数组,其中包含 16 位值:
import PIL.Image
import numpy
image = PIL.Image.open('16bit.png')
pixel = numpy.array(image)
print "PIL:", pixel.dtype
print max(max(row) for row in pixel)
the output is:输出是:
PIL: int32
65535
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.