我试图读取以16位数据类型编写的PNG图像文件。数据应转换为NumPy数组。但我不知道如何阅读16位'中的文件。我尝试使用PIL和SciPy,但是当它们加载时它们将16位数据转换为8位数据。谁能告诉我如何从16位PNG文件中提取数据并将其转换为NumPy数组而不更改数据类型?
以下是我使用的脚本。
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
答案 0 :(得分:5)
我在这里遇到同样的问题。我用我自己创建的16位图像测试了它。当我用png包加载它们时,所有这些都被正确打开了。也是'文件'的输出看起来没问题。
用PIL打开它们总是导致8位numpy-arrays。
在Linux btw上使用Python 2.7.6。
像这样对我有用:
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 )
也许有人可以提供更多信息,在哪种情况下前一种方法有效? (因为这个很慢)
提前致谢。
答案 1 :(得分:2)
我怀疑你的" 16位" PNG不是16位。 (如果您使用的是Linux或Mac,则可以运行file 16bit.png
并查看其内容)
当我使用PIL和numpy时,我得到一个带有16位值的32位数组:
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)
输出是:
PIL: int32
65535
答案 2 :(得分:2)
这是因为PIL不支持16位数据,如下所述:http://effbot.org/imagingbook/concepts.htm
我使用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)
这将返回
png_array.dtype
dtype('uint16')
我找到的一种更清洁的方法是使用skimage包。
from skimage import io
im = io.imread(jpg)
在哪里' im'将是一个numpy阵列。 注意:我还没有使用PNG对此进行测试,但它适用于TIFF文件
答案 3 :(得分:1)
我建议使用opencv:
pip install opencv-python
和
import cv2
image = cv2.imread('16bit.png', cv2.IMREAD_UNCHANGED)
uint16
。答案 4 :(得分:0)
我正在使用png模块: 首先通过以下方式安装png:
>pip install pypng
然后
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)
答案 5 :(得分:0)
我一直在使用PIL版本5.3.0来处理此图像:
它可以读取数据:
>>> image = Image.open('/home/jcomeau/Downloads/grayscale_example.png')
>>> image.mode
'I'
>>> image.getextrema()
(5140, 62708)
>>> image.save('/tmp/test.png')
并以正确的模式保存,但是内容不相同:
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
但是,image.show()
始终会转换为8位灰度级,固定在0和255之间。因此,对于在转换的任何阶段查看所得到的内容都没有用。尽管我可以编写一个例程来执行此操作,甚至可以编写Monkeypatch .show()
,但是我只是在另一个xterm中运行了display
命令。
>>> image.putdata([n - 32768 for n in image.getdata()])
>>> image.getextrema()
(-27628, 29940)
>>> image.save('/tmp/test2.png')
请注意,转换为模式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
答案 6 :(得分:0)
我建立的最简单的解决方案:
当我打开16位单色PNG枕头时,无法以I;16
模式正确打开它。
Image.mode
作为I
(32位)打开
因此,转换为numpy数组的最佳方法。它是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))
在Pillow 6.1.0的Python 3.6.8中进行了测试
答案 7 :(得分:0)
您还可以使用出色的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在VFX行业中得到广泛使用,因此大多数Linux发行版都附带了一个本机软件包。不幸的是,本来很好的文档都是PDF格式(我个人更喜欢HTML),请在/usr/share/doc/OpenImageIO
中查找。
答案 8 :(得分:0)
基于弗雷迪先生的answer,要考虑的另一种选择是使用pypng加载它,如下所示:
import png
pngdata = png.Reader("path/to/16bit.png").read_flat()
img = np.array(pngdata[2]).reshape((pngdata[1], pngdata[0], -1))
您可以使用pip安装pypng:
pip install pypng
png.Reader.read_flat()中的dtype正确为uint16,并且np.ndarray
的重塑将其设置为(高度,宽度,通道)格式。
答案 9 :(得分:0)
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
使用 imagemagick:
>> identify 16bit.png
16bit.png PNG 256x256 256x256+0+0 16-bit Grayscale Gray 502B 0.000u 0:00.000