1

私はバイナリPBM形式で作業しています。それを読むと、整数の配列があります。整数はバイトの序数です。配列内の各整数は、バイナリ表現として0と1の整数のリストに変換され、次にこのリストを逆にします。ピクセルグリッドは0:0から始まるため、最初のピクセルの位置は[0:0]です。

x> = 8の場合、ピクセルカラーを取得する必要があります。x<8の場合、すべてがうまく機能します。ピクセルカラーを取得するためのコード。

  def getpixel(self, x, y):
    '''PNMReader.getpixel(x, y) -> int

    Get pixel at coordinates [x:y] and return it as integer.'''
    if not isinstance(x, int) or not isinstance(y, int):
      raise(TypeError('both x and y must be integers'))
    if x < -1 or y < -1:
      raise(ValueError('both x and y are interpreted as in slice notation'))
    if x > (self.width-1):
      raise(ValueError('x cannot be equal or greater than width'))
    if y > (self.height-1):
      raise(ValueError('x cannot be equal or greater than height'))
    width, height = self.width, self.height
    x = (x, width-1)[x == -1]
    y = [y, height-1][y == -1]
    p = (y *height) +x
    width, height = self.width, self.height
    pixels = self._array_
    q = (8, width)[width -8 < 0]
    if x >= q:
      while x % q:
        y += 1
        x -= 1
    from pprint import pprint
    color = bitarray(pixels[y])[::-1][:q][x]
    print(color)

bitarrayここで確認できるのは、整数のビットをリストとして取得するための私の定義済み関数です。self._array_整数のシーケンスです(これは、PBMから読み取られたバイトの単なる序数です)。

x> = 8の場合、ピクセルカラーを取得するためにこの関数を修正する必要があります。このような状況でxとyのオフセットを計算する方法がわかりません。

迅速に機能する回答のみが受け入れられます。画像が大きい場合(たとえば、3000x5000ピクセルの場合)、速度が遅くなる可能性があるため、すべてのビットを1次元配列として結合する必要はありません。

imagemagickやなどのモジュールを使用できることは知っていますがfreeimage、使用できるのは標準ライブラリのみです(追加のモジュールは使用できません)。バインディングやデフォルト以外のモジュールのない純粋なPythonソリューションが必要です。

4

1 に答える 1

2

self._array_が整数の配列で、それぞれが元のイメージからの 1 バイトのラスター イメージ データを表す場合、通常のビット操作手法を使用して必要なビットを抽出できます。詳細な説明は次のとおりです(コメントで要求されているとおり):

  1. 各行の幅がバイト単位で必要です。これは、ピクセル単位の幅を 8 で割ったものです。ただし、PBM 形式では、各行を正確なバイト数にするために、各行に最大 7 個のダミー ピクセルが埋め込まれます。したがって、幅を 8 で割り、切り上げる必要がありますこれは、次のような整数演算を使用して実行できます。

    row_width = (width + 7) // 8
    
  2. 次に、必要なピクセルを含むバイトを見つける必要があります。PBM ラスター データは行優先順で配置されるため、ピクセル(x, y)は次のバイトにあります。

    pixel_byte = self._array_[y * row_width + x // 8]
    
  3. 演算(ビット単位で右シフトし、最下位ビットをマスクする) を使用しbて、整数からビット番号 (右から番号付け、最下位ビットは 0) を抽出できます。ただし、PBM はピクセルをビッグ エンディアン順でレイアウトし、最初のピクセルがバイトの最上位ビットになります。したがって、必要なビットはビット番号です。i(i >> b) & 1b7 - x % 8

    (pixel_byte >> (7 - x % 8)) & 1
    

それはあなたの差し迫った問題を解決するはずです。しかし、あなたのコードは、あなたがやろうとしていることに対して非常に複雑であるように見えます。いくつかのコメント:

  1. isinstance自分でa を呼び出したり上げたりするのは無意味TypeErrorです。これは、引数に対して整数演算を実行しようとするといずれにせよ発生するからです。

  2. x > (self.width-1)のように書いたほうがよいでしょうx >= self.width

  3. Python のスライス表記では、 だけでなく、任意の負の整数を使用できます-1。例えば:

    >>> range(10)[-7:-4]
    [3, 4, 5]
    
  4. 数値を計算しpますが、使用しません。

  5. 関数をインポートしたpprint後、それを呼び出さないでください。

私は次のように書きます:

import re

class Pbm(object):
    """
    Load a Binary Portable Bitmap (PBM) files and provide access to
    its pixels.  See <http://netpbm.sourceforge.net/doc/pbm.html>
    """
    _pbm_re = re.compile(r'''
       (P4)                     # 1. Magic number
       (?:\s+|\#.*\n)*          # Whitespace or comments
       ([0-9]+)                 # 2. Width of image in pixels
       (?:\s+|\#.*\n)*          # Whitespace or comments
       ([0-9]+)                 # 3. Height of image in pixels
       (?:\#.*\n)*              # Possible comments
       \s                       # A single whitespace character
       ([\000-\377]*)           # 4. Raster image data
    ''', re.X)

    def __init__(self, f):
        m = self._pbm_re.match(f.read())
        if not m:
            raise IOError("Can't parse PBM file.")
        self.width = int(m.group(2))             # Width in pixels
        self.height = int(m.group(3))            # Height in pixels
        self.row = (self.width + 7) // 8         # Width in bytes
        self.raster = m.group(4)
        if len(self.raster) != self.height * self.row:
            raise IOError("Size of raster is {} but width x height = {}."
                          .format(len(self.raster), self.height * self.row))

    def getpixel(self, x, y):
        # Negative coordinates are treated as offsets from the end,
        # like Python's slice indexes.
        if x < 0: x += self.width
        if y < 0: y += self.height
        if x < 0 or x >= self.width or y < 0 or y >= self.height:
            raise ValueError("Coords ({},{}) are out of range (0,0)-({},{})."
                             .format(x, y, self.width - 1, self.height - 1))
        return (ord(self.raster[y * self.row + x // 8]) >> (7 - x % 8)) & 1
于 2012-09-14T15:38:31.360 に答える