복붙노트

[PYTHON] Numpy 및 16 비트 PGM

PYTHON

Numpy 및 16 비트 PGM

numpy로 파이썬에서 16 비트 PGM 이미지를 읽기위한 효율적이고 명확한 방법은 무엇입니까?

PIL 버그로 인해 PIL을 사용하여 16 비트 PGM 이미지를로드 할 수 없습니다. 다음 코드를 사용하여 헤더를 읽을 수 있습니다.

dt = np.dtype([('type', 'a2'),
               ('space_0', 'a1', ),
               ('x', 'a3', ),
               ('space_1', 'a1', ),
               ('y', 'a3', ),
               ('space_2', 'a1', ),
               ('maxval', 'a5')])
header = np.fromfile( 'img.pgm', dtype=dt )
print header

올바른 데이터를 인쇄합니다. ( 'P5', '', '640', '', '480', '', '65535') 그러나 나는 가장 좋은 방법이 아닌 느낌을 가지고 있습니다. 그 외에도 크기 (헤더)가 16 비트 인 y (이 경우 640x480)의 다음 x 데이터를 읽는 방법을 파악하는 방법에 문제가 있습니다.

수정 : 이미지 추가

이미지를 읽고 표시하는 MATLAB 코드는 다음과 같습니다.

I = imread('foo.pgm'); 
imagesc(I);

그리고 다음과 같이 보입니다.

해결법

  1. ==============================

    1.

    import re
    import numpy
    
    def read_pgm(filename, byteorder='>'):
        """Return image data from a raw PGM file as numpy array.
    
        Format specification: http://netpbm.sourceforge.net/doc/pgm.html
    
        """
        with open(filename, 'rb') as f:
            buffer = f.read()
        try:
            header, width, height, maxval = re.search(
                b"(^P5\s(?:\s*#.*[\r\n])*"
                b"(\d+)\s(?:\s*#.*[\r\n])*"
                b"(\d+)\s(?:\s*#.*[\r\n])*"
                b"(\d+)\s(?:\s*#.*[\r\n]\s)*)", buffer).groups()
        except AttributeError:
            raise ValueError("Not a raw PGM file: '%s'" % filename)
        return numpy.frombuffer(buffer,
                                dtype='u1' if int(maxval) < 256 else byteorder+'u2',
                                count=int(width)*int(height),
                                offset=len(header)
                                ).reshape((int(height), int(width)))
    
    
    if __name__ == "__main__":
        from matplotlib import pyplot
        image = read_pgm("foo.pgm", byteorder='<')
        pyplot.imshow(image, pyplot.cm.gray)
        pyplot.show()
    
  2. ==============================

    2.PGM 형식에 익숙하지 않지만 일반적으로 numpy.fromfile을 사용합니다. fromfile은 전달 된 파일 포인터가있는 위치에서 시작하므로 헤더의 끝까지 간단히 검색 (또는 읽음) 한 다음 fromfile을 사용하여 나머지를 읽을 수 있습니다.

    PGM 형식에 익숙하지 않지만 일반적으로 numpy.fromfile을 사용합니다. fromfile은 전달 된 파일 포인터가있는 위치에서 시작하므로 헤더의 끝까지 간단히 검색 (또는 읽음) 한 다음 fromfile을 사용하여 나머지를 읽을 수 있습니다.

    다음 (infile) 대신 infile.readline ()을 사용해야합니다.

    import numpy as np
    
    with open('foo.pgm', 'r') as infile:
        header = infile.readline()
        width, height, maxval = [int(item) for item in header.split()[1:]]
        image = np.fromfile(infile, dtype=np.uint16).reshape((height, width))
    

    부수적으로 메모에서 가리키는 "foo.pgm"파일이 헤더의 잘못된 행 수를 지정하는 것으로 보입니다.

    잠재적으로 그 문제가있는 많은 파일을 읽는다면 배열에 0을 덧붙이거나 자르면됩니다.

    import numpy as np
    
    with open('foo.pgm', 'r') as infile:
        header = next(infile)
        width, height, maxval = [int(item) for item in header.split()[1:]]
        image = np.fromfile(infile, dtype=np.uint16)
        if image.size < width * height:
            pad = np.zeros(width * height - image.size, dtype=np.uint16)
            image = np.hstack([image, pad])
        if image.size > width * height:
            image = image[:width * height]
        image = image.reshape((height, width))
    

  3. ==============================

    3.실제로, 헤더 뒤의 '문자열'은 파일의 바이너리입니다. 나는 아래에서 그것을 해결했다 : (ndarray : [2047 2047 2047 ..., 540 539 539])하지만 또 다른 문제가있다 : 파일이 충분히 길지 않다. 640 * 480 대신 289872 숫자 만 계산합니다 ...

    실제로, 헤더 뒤의 '문자열'은 파일의 바이너리입니다. 나는 아래에서 그것을 해결했다 : (ndarray : [2047 2047 2047 ..., 540 539 539])하지만 또 다른 문제가있다 : 파일이 충분히 길지 않다. 640 * 480 대신 289872 숫자 만 계산합니다 ...

    나는 그것을위한 수업을 만들어서 나의 exageration을 대단히 유감스럽게 생각한다. ..

    import numpy as np
    import Image
    
    class PGM(object):
        def __init__(self, filepath):
    
            with open(filepath) as f:
    
                # suppose all header info in first line:
                info = f.readline().split()
                self.type = info[0]
                self.width, self.height, self.maxval = [int(v) for v in info[1:]]
                size = self.width * self.height
    
                lines = f.readlines()
                dt = [np.int8, np.int16][self.maxval > 255]
                try:
                    # this will work if lines are integers separated by e.g. spaces
                    self.data = np.array([l.split() for l in lines], dtype=dt).T
                except ValueError:
                    # data is binary
                    data = np.fromstring(lines[0], dtype=dt)
                    if data.size < size:
                        # this is the case for the 'db.tt/phaR587 (foo.pgm)'
                        #raise ValueError('data binary string probably uncomplete')
                        data = np.hstack((data, np.zeros(size-data.size)))
                    self.data = data[:size].reshape((self.width, self.height))
    
                assert (self.width, self.height) == self.data.shape
                assert self.maxval >= self.data.max()
    
            self._img = None
    
        def get_img(self):
            if self._img is None:
                # only executed once
                size = (self.width, self.height)
                mode = 'L'
                data = self.data
                self.img = Image.frombuffer(mode, size, data)
    
            return self.img
    
        Image = property(get_img)
    
    mypgm = PGM('foo.pgm')
    
    mypgm.Image
    

    편집 : 이미지를 0으로 채우는 조 킹턴의 위대한 아이디어!

  4. ==============================

    4.여기에서 나는 헤더 정보가 공백, 캐리지 리턴 또는 기타로 구분 될 수 있음을 이해합니다. 너의 것을 공백으로 구분하면 (나에게 알리면) 다음과 같이 할 수있다.

    여기에서 나는 헤더 정보가 공백, 캐리지 리턴 또는 기타로 구분 될 수 있음을 이해합니다. 너의 것을 공백으로 구분하면 (나에게 알리면) 다음과 같이 할 수있다.

    with open('img.pgm') as f:
        lines = f.readlines()
        data = np.array([line.split() for line in lines[1:]], dtype=np.int16).T
    

    이제 데이터가 int16 형식의 배열이됩니다!

    헤더 정보에 계속 관심이 있다고 가정하면 다음을 수행 할 수 있습니다.

    class Header(object):
        def __init__(self, type, width, height, maxval):
            self.type = type
            self.width = int(width)
            self.height = int(height)
            self.maxval = int(maxval)
    
    h = Header(*lines[0].split()[:4])
    

    읽기 라인에 대해 이미지 데이터를 확인할 수 있습니다.

    assert (h.width, h.height) == data.shape    
    assert h.maxval >= data.max()
    

    편집 : 이미지 데이터가 바이너리 인 경우 파일을 'rb'로 열고 헤더 정보 다음부터 읽어야합니다.

    import numpy as np
    
    def as_array(filepath):
        f = open(filepath, 'r')
        w, h = size = tuple(int(v) for v in next(f).split()[1:3])
        data_size = w * h * 2
    
        f.seek(0, 2)
        filesize = f.tell()
        f.close()
        i_header_end = filesize - (data_size)
    
        f = open(filepath, 'rb')
        f.seek(i_header_end)
        buffer = f.read()
        f.close()
    
        # convert binary data to an array of the right shape
        data = np.frombuffer(buffer, dtype=np.uint16).reshape((w, h))
    
        return data
    
    a = as_array('foo.pgm')
    
  5. ==============================

    5.이에 대해 @ joe-kington이 대답 해 주신 것에 감사드립니다. 해결책은 다음과 같습니다.

    이에 대해 @ joe-kington이 대답 해 주신 것에 감사드립니다. 해결책은 다음과 같습니다.

    알려진 헤더 길이를 하드 코딩하지 않는 약간의 추가 작업이 있습니다 (17 바이트 이 경우), 헤더에서 결정합니다. PGM 표준에 따르면 헤더는 일반적으로 개행 문자로 끝나지만 공백으로 끝날 수 있습니다. 이 코드는 끝 부분의 delimeter에 개행 문자가 아닌 공백 문자를 사용하는 PGM에서 중단 될 것이라고 생각합니다. 이 경우 헤더 크기는 너비, 높이 및 최대 크기를 포함하는 변수의 크기와 'P5'의 2 바이트와 공백의 4 바이트를 더한 값으로 결정됩니다.

    이것이 깨질 수있는 다른 경우는 너비 또는 높이가 int (매우 큰 이미지)보다 큰 경우입니다. 또는 PGM이 16 비트가 아닌 8 비트 (maxval 및 가능한 너비, 높이 및 파일 크기에서 결정될 수 있음) 인 경우

    #!/usr/bin/python
    import numpy as np
    import matplotlib.pyplot as plt
    
    file='foo.pgm'
    infile = open(file,'r')
    header = next(infile)
    width, height, maxval = [int(item) for item in header.split()[1:]]
    infile.seek(len(header))
    image = np.fromfile(infile, dtype=np.uint16).reshape((height, width))
    print width, height, maxval
    plt.figimage(image)
    
  6. from https://stackoverflow.com/questions/7368739/numpy-and-16-bit-pgm by cc-by-sa and MIT license