From 1d6531aa045e92cc31de4f2979d6de2e27d4cdbd Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Wed, 23 Oct 2013 16:28:41 -0400 Subject: [PATCH] Fixed Python 3 import errors, added alpha and rgba properties --- skimage/novice/_novice.py | 93 ++++++++++++++++++++++------- skimage/novice/tests/test_novice.py | 48 ++++++++++++--- 2 files changed, 111 insertions(+), 30 deletions(-) diff --git a/skimage/novice/_novice.py b/skimage/novice/_novice.py index df2c3fdc..fb593a69 100644 --- a/skimage/novice/_novice.py +++ b/skimage/novice/_novice.py @@ -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): diff --git a/skimage/novice/tests/test_novice.py b/skimage/novice/tests/test_novice.py index 80065e46..da0008fc 100644 --- a/skimage/novice/tests/test_novice.py +++ b/skimage/novice/tests/test_novice.py @@ -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()