5

我有 16 位 PGM 图像,我正在尝试用 Python 读取。似乎(?)像 PIL 不支持这种格式?

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

大致显示图像,但它不正确。整个过程中都有黑带,据报道 img 有mode=L我认为这与我有一个关于16 位 TIFF 文件的早期问题有关。PIL 不支持 16 位那么罕见吗?有什么建议我可以使用 PIL 或其他标准库或本地代码在 Python 中读取 16 位 PGM 文件吗?

4

3 回答 3

4

你需要一个模式"L;16";但是看起来 PIL"L"在加载 PGM 时具有硬编码到 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,这并不完全正确。

于 2011-09-09T15:44:29.590 回答
1

这是一个基于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)

当然,编写这种图像格式通常不需要图书馆的帮助......

于 2013-02-03T01:33:35.183 回答
1

下面只依赖numpy来加载图片,可以是8位或者16位的raw 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 文本标头的二进制输入,从标准输入读取

于 2015-07-19T18:36:09.297 回答