我正在使用二进制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的偏移量。
只接受快速解决的答案。我不希望将所有位加入为1维数组,因为如果图像很大(例如,它可能是3000x5000像素),它可能会太慢。
我知道我可以使用imagemagick
或freeimage
等模块,但我只能使用标准库(没有其他模块)。我需要没有绑定或非默认模块的纯Python解决方案。
答案 0 :(得分:2)
如果self._array_
是一个整数数组,每个整数表示原始图像中的一个字节的光栅图像数据,那么您可以使用普通的位操作技术提取所需的位。这是一个详细的解释(根据评论中的要求):
我们需要每行的宽度(以字节为单位)。这是以像素为单位的宽度除以8,除了PBM格式为每行填充最多7个虚拟像素以使每行成为精确的字节数。所以我们需要将宽度除以8并将向上,这可以使用整数算法来完成,如下所示:
row_width = (width + 7) // 8
然后我们需要找到包含我们想要的像素的字节。 PBM栅格数据按行主要顺序排列,因此(x, y)
处的像素位于此字节中:
pixel_byte = self._array_[y * row_width + x // 8]
您可以使用操作b
从右侧i
提取位号(i >> b) & 1
(从右侧编号,最低有效位编号为0)(右移b
位并掩盖最低位)。但PBM在big-endian order中列出其像素,第一个像素位于字节中的最高位。所以我们想要的位是位号7 - x % 8
:
(pixel_byte >> (7 - x % 8)) & 1
这应该可以解决您的直接问题。但它看起来好像你的代码对于你想要做的事情非常复杂。一些评论:
调用isinstance
并自己提出TypeError
是毫无意义的,因为当你尝试对参数进行整数运算时,无论如何都会发生这种情况。
x > (self.width-1)
最好写成x >= self.width
。
Python的切片表示法允许任何负整数,而不仅仅是-1
。例如:
>>> range(10)[-7:-4]
[3, 4, 5]
您计算了一个数字p
,但您没有使用它。
您导入函数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