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 像素),它可能会太慢。

我知道我可以使用一些模块,如imagemagickorfreeimage等​​,但我只能使用标准库(没有额外的模块)。我需要没有绑定或非默认模块的纯 Python 解决方案。

4

1 回答 1

2

如果self._array_是一个整数数组,每个整数代表原始图像中一个字节的光栅图像数据,那么您可以使用普通的位操作技术提取您想要的位。这是一个详细的解释(根据评论中的要求):

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