Pythonおよび16ビットPGM
-
28-10-2019 - |
質問
Pythonで読み込もうとしている16ビットのPGM画像があります。 PILがこの形式をサポートしていないようです(?)?
import Image
im = Image.open('test.pgm')
im.show()
大まかに画像を表示しますが、正しくありません。全体に暗いバンドがあり、IMGは持っていると報告されています mode=L
. 。これは私が持っていた初期の質問に関連していると思います 16ビットTIFFファイル. 。 PILがそれをサポートしていないという16ビットはまれですか? PITHまたは別の標準ライブラリ、または自家製コードを使用して、Pythonで16ビットPGMファイルを読み取る方法はありますか?
解決
以下はにのみ依存します 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)
出力10進数)。私も応募する必要がありましたpnmgamma
混乱を救うために。Pythonがとても遅いので、
numpy
しようとします卑劣特定の操作をどのように適用するかについて賢い(参照 放送)。効率のための最初の経験則numpy
は numpyがあなたのための反復を処理してください (または別の方法を置きます あなた自身を書かないでくださいfor
ループ)。上記のコードの面白いことは、「画像処理の例」を実行するときにこのルールに部分的にのみ従うことです。したがって、その行のパフォーマンスは、に与えられたパラメーターに極端に依存しています。reshape
.次のビッグ
numpy
エンディアンネスミステリー:なぜそうするのですかnewbyteorder()
ように見えます 配列を返します, 、 いつ 文書化 返すにはdtype
. 。これは、ネイティブエンディアンに変換したい場合に関連していますdst.pixels=dst.pixels.byteswap(True).newbyteorder()
.Python 3への移植に関するヒント: ASCIIテキストヘッダーを使用したバイナリ入力、stdinから読む
他のヒント
のモードが必要です "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
, 、それは正確ではありません。
これがジェネリックです pnm/パム に基づいて読者 numpy そして、文書化されていない関数 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)
もちろん 書き込み この画像形式は通常、ライブラリの支援を必要としません...