Python和16位PGM [英] Python and 16-bit PGM
问题描述
我有16位PGM图片,我正在尝试用Python读取.似乎(?)像PIL不支持这种格式?
I have 16-bit PGM images that I am trying to read in Python. It seems (?) like PIL does not support this format?
import Image
im = Image.open('test.pgm')
im.show()
粗略显示图像,但是不正确.整个区域都有黑带,据报道img具有mode=L
.我认为这与我对 16位TIFF文件的早期疑问有关.是不是PIL不支持16位的情况如此罕见?关于如何使用PIL或其他标准库或本地代码在Python中读取16位PGM文件的任何建议?
Shows roughly the image, but it isn't right. There are dark bands throughout and img is reported to have mode=L
. I think this is related to an early question I had about 16-bit TIFF files. Is 16-bit that rare that PIL just does not support it? Any advice how I can read 16-bit PGM files in Python, using PIL or another standard library, or home-grown code?
推荐答案
以下内容仅取决于 numpy 加载图像,该图像可以是8位或16位原始PGM/PPM.我还展示了几种查看图像的不同方法.使用PIL(import Image
)的用户需要首先将数据转换为8位.
The following depends only on numpy to load the image, which can be 8-bit or 16-bit raw PGM/PPM. I also show a couple different ways to view the image. The one that uses PIL (import Image
) requires that the data first be converted to 8-bit.
#!/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的其他性能问题可能会使这种担忧变得微不足道(请参见下文).
Usage notes
I eventually figured out "how it decides about endianness" -- it is actually storing the image in memory as big-endian (rather than native). This scheme might slow down any non-trivial image processing -- although other performance issues with Python may relegate this concern to a triviality (see below).
我在此处询问了有关字节序的问题.我还遇到了一些与字节序有关的有趣混淆,因为我正在通过使用
pnmdepth 65535
预处理图像进行测试,这本身并不能很好地测试字节序,因为低字节和高字节最终可能是相同的(我没有不会立即注意到,因为print(array)
输出十进制).我也应该应用pnmgamma
来避免一些困惑.I asked a question related to the endianness concern here. I also ran into some interesting confusion related to endianness with this because I was testing by preprocessing the image with
pnmdepth 65535
which is not good (by itself) for testing endianness since the low and high bytes might end up being the same (I didn't notice right away becauseprint(array)
outputs decimal). I should have also appliedpnmgamma
to save myself some confusion.由于Python太慢,因此
numpy
试图对应用某些操作的方法偷偷摸摸" (请参阅 不要编写自己的for
循环 ).上面代码中的有趣之处在于,在执行示例图像处理"时,它仅部分遵循此规则,因此该行的性能与给reshape
的参数有极大的依赖性.Because Python is so slow,
numpy
tries to besneakyclever about how it applies certain operations (see broadcasting). The first rule of thumb for efficiency withnumpy
is let numpy handle iteration for you (or put another way don't write your ownfor
loops). The funny thing in the code above is that it only partially follows this rule when doing the "example image processing", and therefore the performance of that line has an extreme dependency on the parameters that were given toreshape
.下一个重要的
numpy
字节序大谜:为什么newbyteorder()
似乎已记录,以返回dtype
.如果要使用dst.pixels=dst.pixels.byteswap(True).newbyteorder()
转换为本机字节序,则这是相关的.The next big
numpy
endianness mystery: Why doesnewbyteorder()
seem to return an array, when it's documented to return adtype
. This is relevant if you want to convert to native endian withdst.pixels=dst.pixels.byteswap(True).newbyteorder()
.移植到Python 3的提示:具有ASCII文本标头的二进制输入,可从stdin中读取
Hints on porting to Python 3: binary input with an ASCII text header, read from stdin
这篇关于Python和16位PGM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!