Fixed Python 3 import errors, added alpha and rgba properties

This commit is contained in:
Michael Hansen
2013-10-23 16:28:41 -04:00
parent 9d91751ed9
commit 1d6531aa04
2 changed files with 111 additions and 30 deletions
+70 -23
View File
@@ -1,10 +1,7 @@
import os
import imghdr
import shutil
from collections import namedtuple
from io import BytesIO
from urlparse import urlparse
from urllib2 import urlopen
import numpy as np
from skimage import io
@@ -13,6 +10,10 @@ from skimage.transform import resize
from skimage.color import color_dict
from skimage._shared import six
from six.moves.urllib_parse import urlparse
from six.moves.urllib import request
urlopen = request.urlopen
# Convert colors from `skimage.color` to uint8 and allow access through
# dict or a named tuple.
@@ -75,15 +76,18 @@ class Pixel(object):
Vertical coordinate of this pixel (bottom = 0).
rgb : tuple
RGB tuple with red, green, and blue components (0-255)
alpha : int
Transparency component (0-255), 255 (opaque) by default
"""
def __init__(self, pic, array, x, y, rgb):
def __init__(self, pic, array, x, y, rgb, alpha=255):
self._picture = pic
self._x = x
self._y = y
self._red = self._validate(rgb[0])
self._green = self._validate(rgb[1])
self._blue = self._validate(rgb[2])
self._alpha = self._validate(alpha)
@property
def x(self):
@@ -125,6 +129,16 @@ class Pixel(object):
self._blue = self._validate(value)
self._setpixel()
@property
def alpha(self):
"""The transparency component of the pixel (0-255)."""
return self._alpha
@alpha.setter
def alpha(self, value):
self._alpha = self._validate(value)
self._setpixel()
@property
def rgb(self):
"""The RGB color components of the pixel (3 values 0-255)."""
@@ -132,7 +146,21 @@ class Pixel(object):
@rgb.setter
def rgb(self, value):
self._red, self._green, self._blue = (self._validate(v) for v in value)
if len(value) == 4:
self.rgba = value
else:
self._red, self._green, self._blue = (self._validate(v) for v in value)
self._alpha = 255
self._setpixel()
@property
def rgba(self):
"""The RGB color and transparency components of the pixel (4 values 0-255)."""
return (self.red, self.green, self.blue, self.alpha)
@rgba.setter
def rgba(self, value):
self._red, self._green, self._blue, self._alpha = (self._validate(v) for v in value)
self._setpixel()
def _validate(self, value):
@@ -149,16 +177,16 @@ class Pixel(object):
def _setpixel(self):
# RGB + alpha
self._picture.xy_array[self._x, self._y] = self.rgb + (255,)
self._picture.xy_array[self._x, self._y] = self.rgba
self._picture._array_modified()
def __eq__(self, other):
if isinstance(other, Pixel):
return self.rgb == other.rgb
return self.rgba == other.rgba
def __repr__(self):
args = self.red, self.green, self.blue
return "Pixel(red={0}, green={1}, blue={2})".format(*args)
args = self.red, self.green, self.blue, self.alpha
return "Pixel(red={0}, green={1}, blue={2}, alpha={3})".format(*args)
class Picture(object):
@@ -169,9 +197,9 @@ class Picture(object):
path : str
Path to an image file to load.
array : array
Raw RGB image data [0-255], with origin at top-left.
Raw RGB or RGBA image data [0-255], with origin at top-left.
xy_array : array
Raw RGB image data [0-255], with origin at bottom-left.
Raw RGB or RGBA image data [0-255], with origin at bottom-left.
Examples
--------
@@ -215,24 +243,21 @@ class Picture(object):
msg = "Must provide a single keyword arg (path, array, xy_array)."
ValueError(msg)
elif path is not None:
self.array = img_as_ubyte(io.imread(path))
self._path = path
# Copy to in-memory buffer to get format (in case path was a url)
if urlparse(path).scheme == "":
self.array = img_as_ubyte(io.imread(path))
self._format = imghdr.what(path)
else:
data = BytesIO()
shutil.copyfileobj(urlopen(path), data)
data.seek(0)
self._format = imghdr.what(data)
del data
data = urlopen(path).read()
self.array = img_as_ubyte(io.imread(BytesIO(data)))
self._format = imghdr.what("", h=data)
elif array is not None:
self.array = array
elif xy_array is not None:
self.xy_array = xy_array
# Force RGBA internally (max alpha)
# Force RGBA internally (use max alpha)
if self.array.shape[-1] == 3:
self.array = np.insert(self.array, 3, values=255, axis=2)
@@ -245,14 +270,18 @@ class Picture(object):
size : tuple
Width and height of the picture in pixels.
color : tuple or str
RGB tuple with the fill color for the picture [0-255] or a valid
RGB or RGBA tuple with the fill color for the picture [0-255] or a valid
key in `color_dict`.
"""
if isinstance(color, six.string_types):
color = color_dict[color]
rgb_size = tuple(size) + (3,)
rgb_size = tuple(size) + (len(color),)
array = np.ones(rgb_size, dtype=np.uint8) * color
array = np.insert(array, 3, values=255, axis=2)
# Force RGBA internally (use max alpha)
if array.shape[-1] == 3:
array = np.insert(array, 3, values=255, axis=2)
return Picture(array=array)
@property
@@ -396,13 +425,31 @@ class Picture(object):
def blue(self, value):
self._set_channel(2, value)
@property
def alpha(self):
"""The transparency component of the pixel (0-255)."""
return self._get_channel(3).ravel()
@alpha.setter
def alpha(self, value):
self._set_channel(3, value)
@property
def rgb(self):
"""The RGB color components of the pixel (3 values 0-255)."""
return self.xy_array
return self.xy_array[:, :, :3]
@rgb.setter
def rgb(self, value):
self.xy_array[:, :, :3] = value
@property
def rgba(self):
"""The RGBA color components of the pixel (4 values 0-255)."""
return self.xy_array
@rgba.setter
def rgba(self, value):
self.xy_array[:] = value
def __iter__(self):
+41 -7
View File
@@ -13,8 +13,8 @@ IMAGE_PATH = os.path.join(data_dir, "chelsea.png")
SMALL_IMAGE_PATH = os.path.join(data_dir, "block.png")
def _array_2d_to_RGB(array):
return np.tile(array[:, :, np.newaxis], (1, 1, 3))
def _array_2d_to_RGBA(array):
return np.tile(array[:, :, np.newaxis], (1, 1, 4))
def test_xy_to_array_origin():
@@ -82,6 +82,28 @@ def test_pixel_rgb():
for i, channel in enumerate((pixel.red, pixel.green, pixel.blue)):
assert_equal(channel, i + 3)
pixel.rgb = np.arange(4)
assert_equal(pixel.rgb, np.arange(3))
def test_pixel_rgba():
pic = novice.Picture.from_size((3, 3), color=(10, 10, 10))
pixel = pic[0, 0]
pixel.rgba = np.arange(4)
assert_equal(pixel.rgba, np.arange(4))
for i, channel in enumerate((pixel.red, pixel.green, pixel.blue, pixel.alpha)):
assert_equal(channel, i)
pixel.red = 3
pixel.green = 4
pixel.blue = 5
pixel.alpha = 6
assert_equal(pixel.rgba, np.arange(4) + 3)
for i, channel in enumerate((pixel.red, pixel.green, pixel.blue, pixel.alpha)):
assert_equal(channel, i + 3)
def test_pixel_rgb_float():
pixel = novice.Picture.from_size((1, 1))[0, 0]
@@ -89,6 +111,12 @@ def test_pixel_rgb_float():
assert_equal(pixel.rgb, (1, 1, 1))
def test_pixel_rgba_float():
pixel = novice.Picture.from_size((1, 1))[0, 0]
pixel.rgba = (1.1, 1.1, 1.1, 1.1)
assert_equal(pixel.rgba, (1, 1, 1, 1))
def test_modified_on_set():
pic = novice.Picture(SMALL_IMAGE_PATH)
pic[0, 0] = (1, 1, 1)
@@ -157,7 +185,7 @@ def test_indexing():
def test_picture_slice():
array = _array_2d_to_RGB(np.arange(0, 10)[np.newaxis, :])
array = _array_2d_to_RGBA(np.arange(0, 10)[np.newaxis, :])
pic = novice.Picture(array=array)
x_slice = slice(3, 8)
@@ -167,7 +195,7 @@ def test_picture_slice():
def test_move_slice():
h, w = 3, 12
array = _array_2d_to_RGB(np.linspace(0, 255, h * w).reshape(h, w))
array = _array_2d_to_RGBA(np.linspace(0, 255, h * w).reshape(h, w))
array = array.astype(np.uint8)
pic = novice.Picture(array=array)
@@ -187,7 +215,7 @@ def test_move_slice():
def test_negative_index():
n = 10
array = _array_2d_to_RGB(np.arange(0, n)[np.newaxis, :])
array = _array_2d_to_RGBA(np.arange(0, n)[np.newaxis, :])
# Test both x and y indices.
pic = novice.Picture(array=array)
assert pic[-1, 0] == pic[n - 1, 0]
@@ -197,7 +225,7 @@ def test_negative_index():
def test_negative_slice():
n = 10
array = _array_2d_to_RGB(np.arange(0, n)[np.newaxis, :])
array = _array_2d_to_RGBA(np.arange(0, n)[np.newaxis, :])
# Test both x and y slices.
pic = novice.Picture(array=array)
assert pic[-3:, 0] == pic[n - 3:, 0]
@@ -207,7 +235,7 @@ def test_negative_slice():
def test_getitem_with_step():
h, w = 5, 5
array = _array_2d_to_RGB(np.linspace(0, 255, h * w).reshape(h, w))
array = _array_2d_to_RGBA(np.linspace(0, 255, h * w).reshape(h, w))
pic = novice.Picture(array=array)
sliced_pic = pic[::2, ::2]
assert sliced_pic == novice.Picture(array=array[::2, ::2])
@@ -267,6 +295,12 @@ def test_pixel_blue_raises():
pixel.blue = 256
@raises(ValueError)
def test_pixel_alpha_raises():
pixel = novice.Picture.from_size((1, 1))[0, 0]
pixel.alpha = 256
if __name__ == '__main__':
from numpy import testing
testing.run_module_suite()