2011-09-09 39 views
5

Tôi có hình ảnh PGM 16 bit mà tôi đang cố gắng đọc bằng Python. Dường như (?) Như PIL không hỗ trợ định dạng này?Python và 16 bit PGM

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

Hiển thị gần đúng hình ảnh nhưng không đúng. Có các dải tối trong suốt và img được báo cáo là có mode=L. Tôi nghĩ điều này liên quan đến câu hỏi ban đầu tôi có về 16-bit TIFF files. Là 16-bit hiếm hoi mà PIL chỉ không hỗ trợ nó? Bất kỳ lời khuyên nào về cách tôi có thể đọc các tệp PGM 16 bit bằng Python, sử dụng PIL hoặc một thư viện chuẩn khác hoặc mã được phát triển tại nhà?

Trả lời

1

Sau đây chỉ phụ thuộc vào numpy để tải hình ảnh, có thể là 8-bit hoặc PGM/PPM thô 16 bit. Tôi cũng hiển thị một vài cách khác nhau để xem hình ảnh. Người sử dụng PIL (import Image) yêu cầu dữ liệu đầu tiên được chuyển đổi thành 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() 

Cách sử dụng ghi chú

  • tôi cuối cùng đã tìm ra "làm thế nào nó quyết định về endianness" - nó thực sự lưu trữ các hình ảnh trong bộ nhớ lớn-endian (chứ không phải là mẹ đẻ). Đề án này có thể làm chậm quá trình xử lý ảnh không tầm thường - mặc dù các vấn đề hiệu suất khác với Python có thể làm giảm bớt mối quan tâm này với một tầm thường (xem bên dưới).

  • Tôi đã đặt câu hỏi liên quan đến mối quan tâm cuối cùng here. Tôi cũng gặp phải một số rắc rối thú vị liên quan đến tính cuối cùng với điều này bởi vì tôi đã thử nghiệm bằng cách xử lý trước hình ảnh với pnmdepth 65535, điều này không tốt (vì chính nó) để kiểm tra độ tin cậy vì các byte thấp và cao có thể kết thúc giống nhau (tôi không thông báo ngay vì print(array) kết quả đầu ra thập phân). Tôi cũng nên áp dụng pnmgamma để tự khắc phục sự nhầm lẫn.

  • Vì Python quá chậm, numpy cố gắng là lén lút thông minh về cách ứng dụng hoạt động nhất định (xem broadcasting). Quy tắc đầu tiên của ngón tay cái cho hiệu quả với numpyđể lặp lại xử lý gọn gàng cho bạn (hoặc đặt cách khác don't write your own for loops). Điều thú vị trong đoạn mã ở trên là nó chỉ tuân theo quy tắc này khi thực hiện "xử lý hình ảnh mẫu", và do đó hiệu suất của dòng đó có sự phụ thuộc cao vào các tham số được gán cho reshape.

  • Tiếp theo lớn numpy endianness bí ẩn: Tại sao newbyteorder() dường như return an array, khi nó documented để trả về một dtype. Điều này có liên quan nếu bạn muốn chuyển đổi thành người bản ngữ cuối cùng với dst.pixels=dst.pixels.byteswap(True).newbyteorder().

  • gợi ý về porting để Python 3: binary input with an ASCII text header, read from stdin

+0

Tại sao cố gắng để viết các chương trình Python có vẻ tầm thường dường như luôn luôn dẫn đến một cuộc phiêu lưu thông qua Stack Overflow? – nobar

+0

Một trong những điều khiến tôi phát điên về Python là các bản sao cạn, chẳng hạn như 'dst = src' ở trên. Đôi khi tôi nghĩ rằng Python chỉ là quá khó khăn cho một lập trình viên C++ để hiểu. – nobar

+0

... Tôi thấy một số câu trả lời bình chọn thấp nhất [ở đây] (http://stackoverflow.com/questions/9541025/how-to-copy-a-python-class) là hữu ích nhất. Đặc biệt, có vẻ như tôi có thể giải quyết vấn đề của tôi ở trên bằng cách thực hiện 'dst = src()'. – nobar

4

Bạn cần một chế độ "L;16"; tuy nhiên có vẻ như PIL có chế độ là "L" được mã hóa cứng thành File.c khi tải PGM. Bạn sẽ phải write your own decoder nếu bạn muốn có thể đọc PGM 16 bit.

Tuy nhiên, hỗ trợ hình ảnh 16-bit vẫn có vẻ flaky:

>>> 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 

Tôi nghĩ PIL có khả năng đọc hình ảnh với 16 bit, nhưng thực sự lưu trữ và thao tác chúng vẫn còn thực nghiệm.

>>> 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)] 

Xem, nó chỉ giải thích giá trị 0xCAFE như 0xFE, mà không phải là chính xác đúng.

+0

Tôi rất sẵn lòng chỉ đọc chúng. Nếu tôi cần viết, tôi sẽ sử dụng PNG. Tôi cũng OK với thao tác chúng như dữ liệu trong numpy chứ không phải là một hình ảnh trong PIL. Bài đăng của bạn hữu ích nhưng bạn có thể mở rộng cách tôi có thể đọc chính xác trong dữ liệu không? – mankoff

+0

Bạn có nghĩa là viết bộ giải mã cho PIL hoặc cách diễn giải PGM không? –

+0

Đọc '' 'in nghiêng' 'làm cho tôi nghĩ rằng có một số mẹo để có thể làm cho nó hoạt động đúng không? Tôi đang cố gắng để thích ứng với công việc xung quanh đây (http://stackoverflow.com/questions/7247371/python-and-16-bit-tiff) nhưng không mất bit. Nếu một bộ giải mã tùy chỉnh là cần thiết, tôi sẽ viết nó dựa trên hướng dẫn PIL. Định dạng PGM có vẻ khá cơ bản, vì vậy có lẽ tôi chỉ nên đọc nó trực tiếp vào một số ... – mankoff

1

Đây là trình đọc phổ biến PNM/PAM dựa trên NumPy và chức năng không có giấy tờ trong 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) 

Tất nhiên viết định dạng hình ảnh này thường không yêu cầu sự hỗ trợ của một thư viện ...

+0

Tôi đã mượn một số ý tưởng từ [câu hỏi liên quan này] (http://stackoverflow.com/questions/7368739/numpy-and-16-bit-pgm). – nobar

+0

Đối với hỗ trợ 'PAM', hàm 'read_pnm_header()' được sử dụng ở đây không trả về 'TUPLTYPE', nhưng nó trả về giá trị đúng cho' DEPTH' (mà tôi gọi là 'mẫu'). – nobar

+0

Xem [câu hỏi này] (http://stackoverflow.com/questions/2850893/reading-binary-data-from-stdin) để biết các lưu ý quan trọng về việc sử dụng stdio thay vì tệp. – nobar

Các vấn đề liên quan