简体   繁体   English

Python和16位PGM

[英]Python and 16-bit PGM

I have 16-bit PGM images that I am trying to read in Python. 我有16位PGM图片,正尝试用Python读取。 It seems (?) like PIL does not support this format? 似乎(?)像PIL不支持此格式?

import Image
im = Image.open('test.pgm')
im.show()

Shows roughly the image, but it isn't right. 粗略显示图像,但是不正确。 There are dark bands throughout and img is reported to have mode=L . 到处都是黑带,据报道img具有mode=L I think this is related to an early question I had about 16-bit TIFF files . 我认为这与我有关16位TIFF文件的早期问题有关。 Is 16-bit that rare that PIL just does not support it? 是不是PIL不支持16位的情况如此罕见? Any advice how I can read 16-bit PGM files in Python, using PIL or another standard library, or home-grown code? 关于如何使用PIL或其他标准库或本地代码在Python中读取16位PGM文件的任何建议?

You need a mode of "L;16" ; 您需要使用"L;16" however it looks like PIL has a mode of "L" hardcoded into File.c when loading a PGM. 但是,在加载PGM时,PIL似乎具有"L"模式硬编码到File.c中。 You'd have to write your own decoder if you want to be able to read a 16-bit PGM. 如果要读取16位PGM,则必须编写自己的解码器

However, 16-bit image support still seems flaky: 但是,对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

I think PIL is capable of reading images with 16 bits, but actually storing and manipulating them is still experimental. 我认为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)]

See, it just interpreted the 0xCAFE value as 0xFE , which isn't exactly correct. 瞧,它只是将0xCAFE值解释为0xFE ,这并不完全正确。

Here's a generic PNM / PAM reader based on NumPy and an undocumented function in PyPNG . 这是一个基于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)

Of course writing this image format generally doesn't require the assistance of a library... 当然, 编写这种图像格式通常不需要库的帮助。

The following depends only on numpy to load the image, which can be 8-bit or 16-bit raw PGM/PPM. 以下内容仅取决于numpy来加载图像,该图像可以是8位或16位原始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. 使用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()

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). 这种方案可能会减慢任何非平凡的图像处理速度-尽管Python的其他性能问题可能会使这种担忧变得平淡无奇(请参阅下文)。

  • 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 because print(array) outputs decimal). 我还遇到了一些与字节序有关的有趣混淆,因为我正在通过使用pnmdepth 65535预处理图像进行测试,这本身并不能很好地测试字节序,因为低字节和高字节最终可能是相同的(我没有这样做)。请立即注意,因为print(array)输出十进制)。 I should have also applied pnmgamma to save myself some confusion. 我也应该应用pnmgamma来避免一些混乱。

  • Because Python is so slow, numpy tries to be sneaky clever about how it applies certain operations (see broadcasting ). 因为Python是如此缓慢, numpy尝试偷偷摸摸巧妙之如何应用某些操作(见广播 )。 The first rule of thumb for efficiency with numpy is let numpy handle iteration for you (or put another way don't write your own for loops ). 提高numpy效率的第一个经验法则是让numpy为您处理迭代 (或者换句话说,不要编写自己的for循环 )。 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 to reshape . 上面代码中的有趣之处在于,在执行“示例图像处理”时,它仅部分遵循此规则,因此该行的性能与为reshape给出的参数具有极大的依赖性。

  • The next big numpy endianness mystery: Why does newbyteorder() seem to return an array , when it's documented to return a dtype . 下一个大numpy字节序谜:为什么newbyteorder()似乎返回一个数组 ,当它记录返回一个dtype This is relevant if you want to convert to native endian with dst.pixels=dst.pixels.byteswap(True).newbyteorder() . 如果要使用dst.pixels=dst.pixels.byteswap(True).newbyteorder()转换为本机字节序,则此方法很重要。

  • Hints on porting to Python 3: binary input with an ASCII text header, read from stdin 移植到Python 3的提示: 具有ASCII文本标头的二进制输入,可从stdin中读取

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM