diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 6903e72c..ecbbbd90 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -10,8 +10,8 @@ - Mahipal Raythattha Documentation infrastructure -- Chris Colbert - OpenCV wrappers +- S. Chris Colbert + OpenCV wrappers, Scivi, Qt and Gtk gui bits. - Holger Rapp OpenCV functions and better OSX library loader diff --git a/scikits/image/io/_plugins/qt_plugin.ini b/scikits/image/io/_plugins/qt_plugin.ini index ae6cfbd4..9e98ba81 100644 --- a/scikits/image/io/_plugins/qt_plugin.ini +++ b/scikits/image/io/_plugins/qt_plugin.ini @@ -1,4 +1,4 @@ [qt] description = Fast image display using the Qt library -provides = imshow, _app_show +provides = imshow, _app_show, imsave diff --git a/scikits/image/io/_plugins/qt_plugin.py b/scikits/image/io/_plugins/qt_plugin.py index d66a9f74..a667439d 100644 --- a/scikits/image/io/_plugins/qt_plugin.py +++ b/scikits/image/io/_plugins/qt_plugin.py @@ -1,5 +1,5 @@ from util import prepare_for_display, window_manager, GuiLockError - +from textwrap import dedent import numpy as np import sys @@ -14,12 +14,8 @@ except GuiLockError, gle: else: try: from PyQt4.QtGui import (QApplication, QMainWindow, QImage, QPixmap, - QLabel, QWidget, QVBoxLayout, QSlider, - QPainter, QColor, QFrame, QLayoutItem) + QLabel, QWidget) from PyQt4 import QtCore, QtGui - from PyQt4.QtCore import Qt - from q_color_mixer import MixerPanel - from q_histogram import QuadHistogram except ImportError: print 'PyQT4 libraries not installed. Plugin not loaded.' @@ -29,10 +25,10 @@ else: app = None - class LabelImage(QLabel): + class ImageLabel(QLabel): def __init__(self, parent, arr): QLabel.__init__(self) - self.parent = parent + # we need to hold a reference to # arr because QImage doesn't copy the data # and the buffer must be alive as long @@ -47,14 +43,10 @@ else: self.img = QImage(arr.data, arr.shape[1], arr.shape[0], arr.strides[0], QImage.Format_RGB888) self.pm = QPixmap.fromImage(self.img) - self.setAlignment(Qt.AlignTop) self.setPixmap(self.pm) self.setAlignment(QtCore.Qt.AlignTop) self.setMinimumSize(100, 100) - def mouseMoveEvent(self, evt): - self.parent.label_mouseMoveEvent(evt) - def resizeEvent(self, evt): width = self.width() pm = QPixmap.fromImage(self.img) @@ -62,22 +54,16 @@ else: self.setPixmap(self.pm) - def update_image(self): - width = self.width() - pm = QPixmap.fromImage(self.img) - pm = pm.scaledToWidth(width) - self.setPixmap(pm) - - class ImageWindow(QMainWindow): def __init__(self, arr, mgr): QMainWindow.__init__(self) + self.setWindowTitle('scikits.image') self.mgr = mgr self.main_widget = QWidget() self.layout = QtGui.QGridLayout(self.main_widget) self.setCentralWidget(self.main_widget) - self.label = LabelImage(self, arr) + self.label = ImageLabel(self, arr) self.layout.addWidget(self.label, 0, 0) self.layout.addLayout self.mgr.add_window(self) @@ -88,156 +74,6 @@ else: # references to it self.mgr.remove_window(self) - def label_mouseMoveEvent(self, evt): - pass - - - class RGBHSVDisplay(QWidget): - def __init__(self): - QWidget.__init__(self) - self.posx_label = QLabel('X-pos:') - self.posx_value = QLabel() - self.posy_label = QLabel('Y-pos:') - self.posy_value = QLabel() - self.r_label = QLabel('R:') - self.r_value = QLabel() - self.g_label = QLabel('G:') - self.g_value = QLabel() - self.b_label = QLabel('B:') - self.b_value = QLabel() - self.h_label = QLabel('H:') - self.h_value = QLabel() - self.s_label = QLabel('S:') - self.s_value = QLabel() - self.v_label = QLabel('V:') - self.v_value = QLabel() - - self.layout = QtGui.QGridLayout(self) - self.layout.addWidget(self.posx_label, 0, 0) - self.layout.addWidget(self.posx_value, 0, 1) - self.layout.addWidget(self.posy_label, 1, 0) - self.layout.addWidget(self.posy_value, 1, 1) - self.layout.addWidget(self.r_label, 0, 2) - self.layout.addWidget(self.r_value, 0, 3) - self.layout.addWidget(self.g_label, 1, 2) - self.layout.addWidget(self.g_value, 1, 3) - self.layout.addWidget(self.b_label, 2, 2) - self.layout.addWidget(self.b_value, 2, 3) - self.layout.addWidget(self.h_label, 0, 4) - self.layout.addWidget(self.h_value, 0, 5) - self.layout.addWidget(self.s_label, 1, 4) - self.layout.addWidget(self.s_value, 1, 5) - self.layout.addWidget(self.v_label, 2, 4) - self.layout.addWidget(self.v_value, 2, 5) - - def update_vals(self, data): - xpos, ypos, r, g, b, h, s, v = data - self.posx_value.setText(str(xpos)[:5]) - self.posy_value.setText(str(ypos)[:5]) - self.r_value.setText(str(r)[:5]) - self.g_value.setText(str(g)[:5]) - self.b_value.setText(str(b)[:5]) - self.h_value.setText(str(h)[:5]) - self.s_value.setText(str(s)[:5]) - self.v_value.setText(str(v)[:5]) - - - class FancyImageWindow(ImageWindow): - def __init__(self, arr, mgr): - ImageWindow.__init__(self, arr, mgr) - self.arr = arr - - self.label.setMouseTracking(True) - - self.mixer_panel = MixerPanel(self.arr) - self.layout.addWidget(self.mixer_panel, 0, 2) - self.mixer_panel.show() - self.mixer_panel.set_callback(self.refresh_image) - - self.rgbv_hist = QuadHistogram(self.arr) - self.layout.addWidget(self.rgbv_hist, 0, 1) - self.rgbv_hist.show() - - self.rgb_hsv_disp = RGBHSVDisplay() - self.layout.addWidget(self.rgb_hsv_disp, 1, 0) - self.rgb_hsv_disp.show() - - self.layout.setColumnStretch(0, 1) - self.layout.setRowStretch(0, 1) - - self.save_file = QtGui.QPushButton('Save to File') - self.save_file.clicked.connect(self.save_to_file) - self.save_variable = QtGui.QPushButton('Save to Variable') - self.save_variable.clicked.connect(self.save_to_variable) - self.save_file.show() - self.save_variable.show() - - self.layout.addWidget(self.save_variable, 1, 1) - self.layout.addWidget(self.save_file, 1, 2) - - - def update_histograms(self): - self.rgbv_hist.update_hists(self.arr) - - def save_to_variable(self): - from scikits.image import io - from textwrap import dedent - img = self.arr.copy() - io.push(img) - msg = dedent(''' - The image has been pushed to the io stack. - Use io.pop() to retrieve the most recently pushed image.''') - msglabel = QLabel(msg) - dialog = QtGui.QDialog() - ok = QtGui.QPushButton('OK', dialog) - ok.clicked.connect(dialog.accept) - ok.setDefault(True) - dialog.layout = QtGui.QGridLayout(dialog) - dialog.layout.addWidget(msglabel, 0, 0, 1, 3) - dialog.layout.addWidget(ok, 1, 1) - dialog.exec_() - - - def save_to_file(self): - from scikits.image import io - filename = str(QtGui.QFileDialog.getSaveFileName()) - if len(filename) == 0: - return - io.imsave(filename, self.arr) - - def refresh_image(self): - self.label.update_image() - self.update_histograms() - - def scale_mouse_pos(self, x, y): - width = self.label.pm.width() - height = self.label.pm.height() - x_frac = 1. * x / width - y_frac = 1. * y / height - width = self.arr.shape[1] - height = self.arr.shape[0] - new_x = int(width * x_frac) - new_y = int(height * y_frac) - return(new_x, new_y) - - def label_mouseMoveEvent(self, evt): - x = evt.x() - y = evt.y() - x, y = self.scale_mouse_pos(x, y) - - # handle tracking out of array bounds - maxw = self.arr.shape[1] - maxh = self.arr.shape[0] - if x >= maxw or y >= maxh or x < 0 or y < 0: - r = g = b = h = s = v = '' - else: - r = self.arr[y,x,0] - g = self.arr[y,x,1] - b = self.arr[y,x,2] - h, s, v = self.mixer_panel.mixer.rgb_2_hsv_pixel(r, g, b) - - self.rgb_hsv_disp.update_vals((x, y, r, g, b, h, s, v)) - def imshow(arr, fancy=False): global app @@ -249,7 +85,8 @@ else: if not fancy: iw = ImageWindow(arr, window_manager) else: - iw = FancyImageWindow(arr, window_manager) + from scivi import SciviImageWindow + iw = SciviImageWindow(arr, window_manager) iw.show() @@ -260,3 +97,17 @@ else: app.exec_() else: print 'No images to show. See `imshow`.' + + + def imsave(filename, img): + # we can support for other than 3D uint8 here... + img = prepare_for_display(img) + qimg = QImage(img.data, img.shape[1], img.shape[0], + img.strides[0], QImage.Format_RGB888) + saved = qimg.save(filename) + if not saved: + msg = dedent( + '''The image was not saved. Allowable file formats + for the QT imsave plugin are: + BMP, JPG, JPEG, PNG, PPM, TIFF, XBM, XPM''') + raise RuntimeError(msg) diff --git a/scikits/image/io/_plugins/scivi.py b/scikits/image/io/_plugins/scivi.py new file mode 100644 index 00000000..c6197717 --- /dev/null +++ b/scikits/image/io/_plugins/scivi.py @@ -0,0 +1,233 @@ +''' +Scivi is written/maintained/developed by: + +S. Chris Colbert - sccolbert@gmail.com + +Scivi is free software and is part of the scikits.image project. + +Scivi is governed by the licenses of the scikits.image project. + +Please report any bugs to the author. + +The scivi module is not meant to be used directly. + +Use scikits.image.io.imshow(img, fancy=True)''' + +from textwrap import dedent +import numpy as np +import sys + +from PyQt4 import QtCore, QtGui +from PyQt4.QtGui import (QApplication, QMainWindow, QImage, QPixmap, + QLabel, QWidget, QVBoxLayout, QSlider, + QPainter, QColor, QFrame, QLayoutItem) + +from q_color_mixer import MixerPanel +from q_histogram import QuadHistogram + + +class ImageLabel(QLabel): + def __init__(self, parent, arr): + QLabel.__init__(self) + self.parent = parent + + # we need to hold a reference to + # arr because QImage doesn't copy the data + # and the buffer must be alive as long + # as the image is alive. + self.arr = arr + + # we also need to pass in the row-stride to + # the constructor, because we can't guarantee + # that every row of the numpy data is + # 4-byte aligned. Which Qt would require + # if we didnt pass the stride. + self.img = QImage(arr.data, arr.shape[1], arr.shape[0], + arr.strides[0], QImage.Format_RGB888) + self.pm = QPixmap.fromImage(self.img) + self.setPixmap(self.pm) + self.setAlignment(QtCore.Qt.AlignTop) + self.setMinimumSize(100, 100) + self.setMouseTracking(True) + + def mouseMoveEvent(self, evt): + self.parent.label_mouseMoveEvent(evt) + + def resizeEvent(self, evt): + width = self.width() + pm = QPixmap.fromImage(self.img) + self.pm = pm.scaledToWidth(width) + self.setPixmap(self.pm) + + def update_image(self): + width = self.width() + pm = QPixmap.fromImage(self.img) + pm = pm.scaledToWidth(width) + self.setPixmap(pm) + + +class RGBHSVDisplay(QWidget): + def __init__(self): + QWidget.__init__(self) + self.posx_label = QLabel('X-pos:') + self.posx_value = QLabel() + self.posy_label = QLabel('Y-pos:') + self.posy_value = QLabel() + self.r_label = QLabel('R:') + self.r_value = QLabel() + self.g_label = QLabel('G:') + self.g_value = QLabel() + self.b_label = QLabel('B:') + self.b_value = QLabel() + self.h_label = QLabel('H:') + self.h_value = QLabel() + self.s_label = QLabel('S:') + self.s_value = QLabel() + self.v_label = QLabel('V:') + self.v_value = QLabel() + + self.layout = QtGui.QGridLayout(self) + self.layout.addWidget(self.posx_label, 0, 0) + self.layout.addWidget(self.posx_value, 0, 1) + self.layout.addWidget(self.posy_label, 1, 0) + self.layout.addWidget(self.posy_value, 1, 1) + self.layout.addWidget(self.r_label, 0, 2) + self.layout.addWidget(self.r_value, 0, 3) + self.layout.addWidget(self.g_label, 1, 2) + self.layout.addWidget(self.g_value, 1, 3) + self.layout.addWidget(self.b_label, 2, 2) + self.layout.addWidget(self.b_value, 2, 3) + self.layout.addWidget(self.h_label, 0, 4) + self.layout.addWidget(self.h_value, 0, 5) + self.layout.addWidget(self.s_label, 1, 4) + self.layout.addWidget(self.s_value, 1, 5) + self.layout.addWidget(self.v_label, 2, 4) + self.layout.addWidget(self.v_value, 2, 5) + + def update_vals(self, data): + xpos, ypos, r, g, b, h, s, v = data + self.posx_value.setText(str(xpos)[:5]) + self.posy_value.setText(str(ypos)[:5]) + self.r_value.setText(str(r)[:5]) + self.g_value.setText(str(g)[:5]) + self.b_value.setText(str(b)[:5]) + self.h_value.setText(str(h)[:5]) + self.s_value.setText(str(s)[:5]) + self.v_value.setText(str(v)[:5]) + + + +class SciviImageWindow(QMainWindow): + def __init__(self, arr, mgr): + QMainWindow.__init__(self) + + self.arr = arr + + self.mgr = mgr + self.main_widget = QWidget() + self.layout = QtGui.QGridLayout(self.main_widget) + self.setCentralWidget(self.main_widget) + + self.label = ImageLabel(self, arr) + self.layout.addWidget(self.label, 0, 0) + self.layout.addLayout + self.mgr.add_window(self) + self.main_widget.show() + + self.setWindowTitle('Scivi - The scikits.image viewer.') + + self.mixer_panel = MixerPanel(self.arr) + self.layout.addWidget(self.mixer_panel, 0, 2) + self.mixer_panel.show() + self.mixer_panel.set_callback(self.refresh_image) + + self.rgbv_hist = QuadHistogram(self.arr) + self.layout.addWidget(self.rgbv_hist, 0, 1) + self.rgbv_hist.show() + + self.rgb_hsv_disp = RGBHSVDisplay() + self.layout.addWidget(self.rgb_hsv_disp, 1, 0) + self.rgb_hsv_disp.show() + + self.layout.setColumnStretch(0, 1) + self.layout.setRowStretch(0, 1) + + self.save_file = QtGui.QPushButton('Save to File') + self.save_file.clicked.connect(self.save_to_file) + self.save_stack = QtGui.QPushButton('Save to Stack') + self.save_stack.clicked.connect(self.save_to_stack) + self.save_file.show() + self.save_stack.show() + + self.layout.addWidget(self.save_stack, 1, 1) + self.layout.addWidget(self.save_file, 1, 2) + + + def closeEvent(self, event): + # Allow window to be destroyed by removing any + # references to it + self.mgr.remove_window(self) + + def update_histograms(self): + self.rgbv_hist.update_hists(self.arr) + + def refresh_image(self): + self.label.update_image() + self.update_histograms() + + def scale_mouse_pos(self, x, y): + width = self.label.pm.width() + height = self.label.pm.height() + x_frac = 1. * x / width + y_frac = 1. * y / height + width = self.arr.shape[1] + height = self.arr.shape[0] + new_x = int(width * x_frac) + new_y = int(height * y_frac) + return(new_x, new_y) + + def label_mouseMoveEvent(self, evt): + x = evt.x() + y = evt.y() + x, y = self.scale_mouse_pos(x, y) + + # handle tracking out of array bounds + maxw = self.arr.shape[1] + maxh = self.arr.shape[0] + if x >= maxw or y >= maxh or x < 0 or y < 0: + r = g = b = h = s = v = '' + else: + r = self.arr[y,x,0] + g = self.arr[y,x,1] + b = self.arr[y,x,2] + h, s, v = self.mixer_panel.mixer.rgb_2_hsv_pixel(r, g, b) + + self.rgb_hsv_disp.update_vals((x, y, r, g, b, h, s, v)) + + + def save_to_stack(self): + from scikits.image import io + img = self.arr.copy() + io.push(img) + msg = dedent(''' + The image has been pushed to the io stack. + Use io.pop() to retrieve the most recently + pushed image.''') + msglabel = QLabel(msg) + dialog = QtGui.QDialog() + ok = QtGui.QPushButton('OK', dialog) + ok.clicked.connect(dialog.accept) + ok.setDefault(True) + dialog.layout = QtGui.QGridLayout(dialog) + dialog.layout.addWidget(msglabel, 0, 0, 1, 3) + dialog.layout.addWidget(ok, 1, 1) + dialog.exec_() + + def save_to_file(self): + from scikits.image import io + filename = str(QtGui.QFileDialog.getSaveFileName()) + if len(filename) == 0: + return + io.imsave(filename, self.arr) + + diff --git a/scikits/image/io/_plugins/tests/test_colormixer.py b/scikits/image/io/_plugins/tests/test_colormixer.py deleted file mode 100644 index 6ea620eb..00000000 --- a/scikits/image/io/_plugins/tests/test_colormixer.py +++ /dev/null @@ -1,49 +0,0 @@ -from numpy.testing import * -import numpy as np - -import scikits.image.io._plugins._colormixer as cm - -class ColorMixerTest(object): - def setup(self): - self.state = np.ones((18, 33, 3), dtype=np.uint8) * 200 - self.img = np.zeros_like(self.state) - - def test_basic(self): - self.op(self.img, self.state, 0, self.positive) - assert_array_equal(self.img[..., 0], - self.py_op(self.state[..., 0], self.positive)) - - def test_clip(self): - self.op(self.img, self.state, 0, self.positive_clip) - assert_array_equal(self.img[..., 0], - np.ones_like(self.img[..., 0]) * 255) - - def test_negative(self): - self.op(self.img, self.state, 0, self.negative) - assert_array_equal(self.img[..., 0], - self.py_op(self.state[..., 0], self.negative)) - - def test_negative_clip(self): - self.op(self.img, self.state, 0, self.negative_clip) - assert_array_equal(self.img[..., 0], - np.zeros_like(self.img[..., 0])) - -class TestColorMixerAdd(ColorMixerTest): - op = cm.add - py_op = np.add - positive = 50 - positive_clip = 56 - negative = -50 - negative_clip = -220 - -class TestColorMixerMul(ColorMixerTest): - op = cm.multiply - py_op = np.multiply - positive = 1.2 - positive_clip = 2 - negative = 0.5 - negative_clip = -0.5 - - -if __name__ == "__main__": - run_module_suite() diff --git a/scikits/image/io/setup.py b/scikits/image/io/setup.py index 9c9cf023..769ead42 100644 --- a/scikits/image/io/setup.py +++ b/scikits/image/io/setup.py @@ -11,6 +11,7 @@ def configuration(parent_package='', top_path=None): config = Configuration('io', parent_package, top_path) config.add_data_dir('tests') + config.add_data_dir('_plugins/tests') config.add_data_files('_plugins/*.ini') # This function tries to create C files from the given .pyx files. If diff --git a/scikits/image/io/tests/test_colormixer.py b/scikits/image/io/tests/test_colormixer.py new file mode 100644 index 00000000..72e4aed0 --- /dev/null +++ b/scikits/image/io/tests/test_colormixer.py @@ -0,0 +1,140 @@ +from numpy.testing import * +import numpy as np + +import scikits.image.io._plugins._colormixer as cm + +class ColorMixerTest(object): + def setup(self): + self.state = np.ones((18, 33, 3), dtype=np.uint8) * 200 + self.img = np.zeros_like(self.state) + + def test_basic(self): + self.op(self.img, self.state, 0, self.positive) + assert_array_equal(self.img[..., 0], + self.py_op(self.state[..., 0], self.positive)) + + def test_clip(self): + self.op(self.img, self.state, 0, self.positive_clip) + assert_array_equal(self.img[..., 0], + np.ones_like(self.img[..., 0]) * 255) + + def test_negative(self): + self.op(self.img, self.state, 0, self.negative) + assert_array_equal(self.img[..., 0], + self.py_op(self.state[..., 0], self.negative)) + + def test_negative_clip(self): + self.op(self.img, self.state, 0, self.negative_clip) + assert_array_equal(self.img[..., 0], + np.zeros_like(self.img[..., 0])) + + +class TestColorMixerAdd(ColorMixerTest): + op = cm.add + py_op = np.add + positive = 50 + positive_clip = 56 + negative = -50 + negative_clip = -220 + + +class TestColorMixerMul(ColorMixerTest): + op = cm.multiply + py_op = np.multiply + positive = 1.2 + positive_clip = 2 + negative = 0.5 + negative_clip = -0.5 + + +class TestColorMixerBright(object): + + def setup(self): + self.state = np.ones((18, 33, 3), dtype=np.uint8) * 200 + self.img = np.zeros_like(self.state) + + def test_brightness_pos(self): + cm.brightness(self.img, self.state, 1.25, 1) + assert_array_equal(self.img, np.ones_like(self.img) * 251) + + def test_brightness_neg(self): + cm.brightness(self.img, self.state, 0.5, -50) + assert_array_equal(self.img, np.ones_like(self.img) * 50) + + def test_brightness_pos_clip(self): + cm.brightness(self.img, self.state, 2, 0) + assert_array_equal(self.img, np.ones_like(self.img) * 255) + + def test_brightness_neg_clip(self): + cm.brightness(self.img, self.state, 0, 0) + assert_array_equal(self.img, np.zeros_like(self.img)) + + +class TestColorMixer(object): + + def setup(self): + self.state = np.ones((18, 33, 3), dtype=np.uint8) * 50 + self.img = np.zeros_like(self.state) + + def test_sigmoid(self): + import math + alpha = 1.5 + beta = 1.5 + c1 = 1 / (1 + math.exp(beta)) + c2 = 1 / (1 + math.exp(beta - alpha)) - c1 + state = self.state / 255. + cm.sigmoid_gamma(self.img, self.state, alpha, beta) + img = 1 / (1 + np.exp(beta - state * alpha)) + img = np.asarray((img - c1) / c2 * 255, dtype='uint8') + assert_almost_equal(img, self.img) + + def test_gamma(self): + gamma = 1.5 + cm.gamma(self.img, self.state, gamma) + img = np.asarray(((self.state/255.)**(1/gamma))*255, dtype='uint8') + assert_array_almost_equal(img, self.img) + + def test_rgb_2_hsv(self): + r = 255 + g = 0 + b = 0 + h, s, v = cm.py_rgb_2_hsv(r, g, b) + assert_almost_equal(np.array([h]), np.array([0])) + assert_almost_equal(np.array([s]), np.array([1])) + assert_almost_equal(np.array([v]), np.array([1])) + + def test_hsv_2_rgb(self): + h = 0 + s = 1 + v = 1 + r, g, b = cm.py_hsv_2_rgb(h, s, v) + assert_almost_equal(np.array([r]), np.array([255])) + assert_almost_equal(np.array([g]), np.array([0])) + assert_almost_equal(np.array([b]), np.array([0])) + + + def test_hsv_add(self): + cm.hsv_add(self.img, self.state, 360, 0, 0) + assert_almost_equal(self.img, self.state) + + def test_hsv_add_clip_neg(self): + cm.hsv_add(self.img, self.state, 0, 0, -1) + assert_equal(self.img, np.zeros_like(self.state)) + + def test_hsv_add_clip_pos(self): + cm.hsv_add(self.img, self.state, 0, 0, 1) + assert_equal(self.img, np.ones_like(self.state)*255) + + def test_hsv_mul(self): + cm.hsv_multiply(self.img, self.state, 360, 1, 1) + assert_almost_equal(self.img, self.state) + + def test_hsv_mul_clip_neg(self): + cm.hsv_multiply(self.img, self.state, 0, 0, 0) + assert_equal(self.img, np.zeros_like(self.state)) + + + + +if __name__ == "__main__": + run_module_suite() diff --git a/scikits/image/io/_plugins/tests/test_histograms.py b/scikits/image/io/tests/test_histograms.py similarity index 52% rename from scikits/image/io/_plugins/tests/test_histograms.py rename to scikits/image/io/tests/test_histograms.py index f1cbe1c5..76589e3b 100644 --- a/scikits/image/io/_plugins/tests/test_histograms.py +++ b/scikits/image/io/tests/test_histograms.py @@ -12,5 +12,17 @@ class TestHistogram: for band in (r, g, b, v): yield assert_equal, band.sum(), 50*50 + def test_counts(self): + channel = np.arange(255).reshape(51, 5) + img = np.empty((51, 5, 3), dtype='uint8') + img[:,:,0] = channel + img[:,:,1] = channel + img[:,:,2] = channel + r, g, b, v = histograms(img, 255) + assert_array_equal(r, g) + assert_array_equal(r, b) + assert_array_equal(r, v) + assert_array_equal(r, np.ones(255)) + if __name__ == "__main__": run_module_suite()