[英]Python and 16-bit PGM
我有16位PGM图片,正尝试用Python读取。 似乎(?)像PIL不支持此格式?
import Image
im = Image.open('test.pgm')
im.show()
粗略显示图像,但是不正确。 到处都是黑带,据报道img具有mode=L
我认为这与我有关16位TIFF文件的早期问题有关。 是不是PIL不支持16位的情况如此罕见? 关于如何使用PIL或其他标准库或本地代码在Python中读取16位PGM文件的任何建议?
您需要使用"L;16"
; 但是,在加载PGM时,PIL似乎具有"L"
模式硬编码到File.c中。 如果要读取16位PGM,则必须编写自己的解码器 。
但是,对16位图像的支持似乎仍然不稳定:
>>> im = Image.fromstring('I;16', (16, 16), '\xCA\xFE' * 256, 'raw', 'I;16')
>>> im.getcolors()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.6/dist-packages/PIL/Image.py", line 866, in getcolors
return self.im.getcolors(maxcolors)
ValueError: image has wrong mode
我认为PIL能够读取 16位图像,但实际上存储和处理它们仍处于实验阶段。
>>> im = Image.fromstring('L', (16, 16), '\xCA\xFE' * 256, 'raw', 'L;16')
>>> im
<Image.Image image mode=L size=16x16 at 0x27B4440>
>>> im.getcolors()
[(256, 254)]
瞧,它只是将0xCAFE
值解释为0xFE
,这并不完全正确。
这是一个基于NumPy的通用PNM / PAM读取器,以及PyPNG中未记录的功能。
def read_pnm( filename, endian='>' ):
fd = open(filename,'rb')
format, width, height, samples, maxval = png.read_pnm_header( fd )
pixels = numpy.fromfile( fd, dtype='u1' if maxval < 256 else endian+'u2' )
return pixels.reshape(height,width,samples)
当然, 编写这种图像格式通常不需要库的帮助。
以下内容仅取决于numpy来加载图像,该图像可以是8位或16位原始PGM / PPM。 我还展示了几种查看图像的不同方法。 使用PIL( import Image
)的用户需要首先将数据转换为8位。
#!/usr/bin/python2 -u
from __future__ import print_function
import sys, numpy
def read_pnm_from_stream( fd ):
pnm = type('pnm',(object,),{}) ## create an empty container
pnm.header = fd.readline()
pnm.magic = pnm.header.split()[0]
pnm.maxsample = 1 if ( pnm.magic == 'P4' ) else 0
while ( len(pnm.header.split()) < 3+(1,0)[pnm.maxsample] ): s = fd.readline() ; pnm.header += s if ( len(s) and s[0] != '#' ) else ''
pnm.width, pnm.height = [int(item) for item in pnm.header.split()[1:3]]
pnm.samples = 3 if ( pnm.magic == 'P6' ) else 1
if ( pnm.maxsample == 0 ): pnm.maxsample = int(pnm.header.split()[3])
pnm.pixels = numpy.fromfile( fd, count=pnm.width*pnm.height*pnm.samples, dtype='u1' if pnm.maxsample < 256 else '>u2' )
pnm.pixels = pnm.pixels.reshape(pnm.height,pnm.width) if pnm.samples==1 else pnm.pixels.reshape(pnm.height,pnm.width,pnm.samples)
return pnm
if __name__ == '__main__':
## read image
# src = read_pnm_from_stream( open(filename) )
src = read_pnm_from_stream( sys.stdin )
# print("src.header="+src.header.strip(), file=sys.stderr )
# print("src.pixels="+repr(src.pixels), file=sys.stderr )
## write image
dst=src
dst.pixels = numpy.array([ dst.maxsample-i for i in src.pixels ],dtype=dst.pixels.dtype) ## example image processing
# print("dst shape: "+str(dst.pixels.shape), file=sys.stderr )
sys.stdout.write(("P5" if dst.samples==1 else "P6")+"\n"+str(dst.width)+" "+str(dst.height)+"\n"+str(dst.maxsample)+"\n");
dst.pixels.tofile( sys.stdout ) ## seems to work, I'm not sure how it decides about endianness
## view using Image
import Image
viewable = dst.pixels if dst.pixels.dtype == numpy.dtype('u1') else numpy.array([ x>>8 for x in dst.pixels],dtype='u1')
Image.fromarray(viewable).show()
## view using scipy
import scipy.misc
scipy.misc.toimage(dst.pixels).show()
我最终弄清楚了“它如何决定字节序”-它实际上是将图像以大字节序(而不是本地字节序)存储在内存中。 这种方案可能会减慢任何非平凡的图像处理速度-尽管Python的其他性能问题可能会使这种担忧变得平淡无奇(请参阅下文)。
我在这里问了一个有关字节序的问题。 我还遇到了一些与字节序有关的有趣混淆,因为我正在通过使用pnmdepth 65535
预处理图像进行测试,这本身并不能很好地测试字节序,因为低字节和高字节最终可能是相同的(我没有这样做)。请立即注意,因为print(array)
输出十进制)。 我也应该应用pnmgamma
来避免一些混乱。
因为Python是如此缓慢, numpy
尝试偷偷摸摸巧妙之如何应用某些操作(见广播 )。 提高numpy
效率的第一个经验法则是让numpy为您处理迭代 (或者换句话说,不要编写自己的for
循环 )。 上面代码中的有趣之处在于,在执行“示例图像处理”时,它仅部分遵循此规则,因此该行的性能与为reshape
给出的参数具有极大的依赖性。
下一个大numpy
字节序谜:为什么newbyteorder()
似乎返回一个数组 ,当它记录返回一个dtype
。 如果要使用dst.pixels=dst.pixels.byteswap(True).newbyteorder()
转换为本机字节序,则此方法很重要。
移植到Python 3的提示: 具有ASCII文本标头的二进制输入,可从stdin中读取
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.