You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
4.2 KiB

"""
This module is part of the 'blup' package and contains the definition of a
basic Frame class.
"""
class FrameDimension(object):
""" This class serves as container for the dimensions of a frame. """
def __init__(self, width=18, height=8, depth=2, channels=1):
self.width = width
self.height = height
self.depth = depth
self.channels = channels
def size(self):
return (self.width, self.height)
def depth(self):
return self.depth
def channels(self):
return self.channels
def __eq__(self, other):
if ( self.width == other.width and self.height == other.height and
self.depth == other.depth and self.channels == other.channels ):
return True
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
def __str__(self):
return "(%d, %d, %d, %d)" % (self.width, self.height, self.depth,
self.channels)
class Frame(object):
""" This class describes a single frame. """
def __init__(self, dimension):
"""
Initialize the frame with the given dimensions. The 'depth' parameter
describes, how many different values a single pixel can have.
"""
self.__dimension = dimension
width = dimension.width
height = dimension.height
if dimension.channels == 1:
self.__pixels = [ [ 0 ] * width for i in range(height) ]
else:
self.__pixels = [ [ tuple([0]*dimension.channels) ] * width for i in range(height) ]
@property
def dimension(self):
return self.__dimension
@property
def size(self):
return (self.__dimension.width, self.__dimension.height)
@property
def depth(self):
return self.__dimension.depth
@property
def channels(self):
return self.__dimension.channels
@property
def pixels(self):
return self.__pixels
@pixels.setter
def pixels(self, pixels):
if len(pixels) != self.__dimension.height:
raise ValueError('not enough rows given')
widths = set(map(lambda r: len(r), pixels))
if len(widths) != 1:
raise ValueError('rows are not of the same length')
if widths.pop() != self.__dimension.width:
raise ValueError('rows are too short')
# TODO check depth
# TODO check number of channels
self.__pixels = pixels
def transform(self, depth):
""" Transform the frame to a frame with a different depth. """
oldmaxval = self.__dimension.depth - 1
newmaxval = depth - 1
factor = (newmaxval*1.0) / (oldmaxval*1.0)
newdim = FrameDimension(self.__dimension.width,
self.__dimension.height,
depth,
self.__dimension.channels)
newframe = Frame(newdim)
transformfkt = lambda p: int(round(p * factor))
for y in range(self.__dimension.height):
if self.__dimension.channels == 1:
newframe.__pixels[y] = map(transformfkt, self.__pixels[y])
else:
for x in range(self.__dimension.width):
newframe.__pixels[y][x] = tuple(map(transformfkt, p))
return newframe
def getPixel(self, x, y):
""" Return the pixel value at (x, y). """
return self.__pixels[y][x]
def setPixel(self, x, y, value):
""" Set the pixel value at (x, y). """
if self.__dimension.channels == 1:
if value < 0 or value >= self.__dimension.depth:
raise ValueError('pixel value not in depth range')
else:
if len(value) != self.__dimension.channels:
raise ValueError('channel count does not match')
for v in value:
if v < 0 or v >= self.__dimension.depth:
raise ValueError('pixel value not in depth range')
self.__pixels[y][x] = value
def getPixelsAsBytes(self):
""" Return the frame as a byte string. """
if self.__dimension.channels != 1:
raise Exception('cannot convert a multi-channel frame')
bytes = ''
for y in range(self.__dimension.width):
for x in range(self.__dimension.height):
bytes += chr(self.__pixels[x][y])
return bytes
def invert(self):
""" Ivert the frame. """
for y in range(self.__dimension.height):
for x in range(self.__dimension.width):
if self.__dimension.channels == 1:
self.__pixels[x][y] = ((self.__dimension.depth - 1) -
self.__pixels[x][y])
else:
self.__pixels[y][x] = tuple(map(
lambda x: ((self.depth - 1) - x),
self.__pixels[y][x]))