diff --git a/scikits/image/io/_plugins/_colormixer.pyx b/scikits/image/io/_plugins/_colormixer.pyx index f229f4ea..9f1f01a3 100644 --- a/scikits/image/io/_plugins/_colormixer.pyx +++ b/scikits/image/io/_plugins/_colormixer.pyx @@ -126,30 +126,8 @@ def brightness(np.ndarray[np.uint8_t, ndim=3] img, else: img[i, j, k] = op_result + cdef void rgb_2_hsv(float* RGB, float* HSV): - '''Convert an HSV value to RGB. - - Automatic clipping. - - Parameters - ---------- - R : float - From 0. - 255. - G : float - From 0. - 255. - B : float - From 0. - 255. - - Returns - ------- - out : (H, S, V) Floats - Ranges (0...360), (0...1), (0...1) - - conversion convention from here: - http://en.wikipedia.org/wiki/HSL_and_HSV - - ''' - cdef float R, G, B, H, S, V, MAX, MIN R = RGB[0] G = RGB[1] @@ -195,7 +173,7 @@ cdef void rgb_2_hsv(float* RGB, float* HSV): if MAX == MIN: H = 0. elif MAX == R: - H = (60 * (G - B) / (MAX - MIN)) % 360 + H = (60 * (G - B) / (MAX - MIN) + 360) % 360 elif MAX == G: H = 60 * (B - R) / (MAX - MIN) + 120 else: @@ -210,30 +188,8 @@ cdef void rgb_2_hsv(float* RGB, float* HSV): HSV[1] = S HSV[2] = V + cdef void hsv_2_rgb(float* HSV, float* RGB): - '''Convert an HSV value to RGB. - - Automatic clipping. - - Parameters - ---------- - H : float - From 0. - 360. - S : float - From 0. - 1. - V : float - From 0. - 1. - - Returns - ------- - out : (R, G, B) Floats - Each from 0. - 1. - - conversion convention from here: - http://en.wikipedia.org/wiki/HSL_and_HSV - - ''' - cdef float H, S, V cdef float f, p, q, t, r, g, b cdef int hi @@ -243,9 +199,9 @@ cdef void hsv_2_rgb(float* HSV, float* RGB): V = HSV[2] if H > 360: - H = 360 + H = H % 360 elif H < 0: - H = 0 + H = 360 - ((-1 * H) % 360) else: pass @@ -298,6 +254,84 @@ cdef void hsv_2_rgb(float* HSV, float* RGB): RGB[1] = g RGB[2] = b + +def py_hsv_2_rgb(H, S, V): + '''Convert an HSV value to RGB. + + Automatic clipping. + + Parameters + ---------- + H : float + From 0. - 360. + S : float + From 0. - 1. + V : float + From 0. - 1. + + Returns + ------- + out : (R, G, B) ints + Each from 0 - 255 + + conversion convention from here: + http://en.wikipedia.org/wiki/HSL_and_HSV + + ''' + cdef float HSV[3] + cdef float RGB[3] + + HSV[0] = H + HSV[1] = S + HSV[2] = V + + hsv_2_rgb(HSV, RGB) + + R = int(RGB[0] * 255) + G = int(RGB[1] * 255) + B = int(RGB[2] * 255) + + return (R, G, B) + +def py_rgb_2_hsv(R, G, B): + '''Convert an HSV value to RGB. + + Automatic clipping. + + Parameters + ---------- + R : int + From 0. - 255. + G : int + From 0. - 255. + B : int + From 0. - 255. + + Returns + ------- + out : (H, S, V) floats + Ranges (0...360), (0...1), (0...1) + + conversion convention from here: + http://en.wikipedia.org/wiki/HSL_and_HSV + + ''' + cdef float HSV[3] + cdef float RGB[3] + + RGB[0] = R + RGB[1] = G + RGB[2] = B + + rgb_2_hsv(RGB, HSV) + + H = HSV[0] + S = HSV[1] + V = HSV[2] + + return (H, S, V) + + @cython.boundscheck(False) def hsv_add(np.ndarray[np.uint8_t, ndim=3] img, np.ndarray[np.uint8_t, ndim=3] stateimg, @@ -379,6 +413,10 @@ def hsv_multiply(np.ndarray[np.uint8_t, ndim=3] img, reverse transform is performed. Those are the ranges to keep in mind, when passing in values. + Note that since hue is in degrees, it makes no sense to multiply + that channel, thus an add operation is performed on the hue. And the + values given for h_amt, should be the same as for hsv_add + Parameters ---------- img : (M, N, 3) ndarray of uint8 @@ -388,9 +426,9 @@ def hsv_multiply(np.ndarray[np.uint8_t, ndim=3] img, h_amt : float Ammount to add to H channel. s_amt : float - Ammount to add to S channel. + Ammount by which to multiply S channel. v_amt : float - Ammount to add to V channel. + Ammount by which to multiply V channel. """ @@ -412,7 +450,7 @@ def hsv_multiply(np.ndarray[np.uint8_t, ndim=3] img, rgb_2_hsv(RGB, HSV) # Multiply operation - HSV[0] *= h_amt + HSV[0] += h_amt HSV[1] *= s_amt HSV[2] *= v_amt diff --git a/scikits/image/io/_plugins/qt_plugin.py b/scikits/image/io/_plugins/qt_plugin.py index bd690d89..b39476b1 100644 --- a/scikits/image/io/_plugins/qt_plugin.py +++ b/scikits/image/io/_plugins/qt_plugin.py @@ -55,11 +55,11 @@ else: QMainWindow.__init__(self) self.mgr = mgr self.main_widget = QWidget() - self.layout = QtGui.QHBoxLayout(self.main_widget) + self.layout = QtGui.QGridLayout(self.main_widget) self.setCentralWidget(self.main_widget) self.label = LabelImage(self, arr) - self.layout.addWidget(self.label) + self.layout.addWidget(self.label, 0, 0) self.mgr.add_window(self) self.main_widget.show() @@ -227,13 +227,13 @@ else: self.hsv_add.toggled.connect(self.hsv_radio_changed) # additive sliders - self.hsv_add_sliders = NSliderBlock(3, [(-360, 360, 0, 'H', 1), + self.hsv_add_sliders = NSliderBlock(3, [(-180, 180, 0, 'H', 1), (-100, 100, 0, 'S', .01), (-100, 100, 0, 'V', .01)], self.hsv_add_changed) # multiplicative sliders - self.hsv_mul_sliders = NSliderBlock(3, [(0, 1000, 500, 'H', .002), + self.hsv_mul_sliders = NSliderBlock(3, [(-180, 180, 0, 'H', 1), (0, 1000, 500, 'S', .002), (0, 1000, 500, 'V', .002)], self.hsv_mul_changed) @@ -286,8 +286,8 @@ else: self.combo_box.setCurrentIndex(0) self.hide_sliders() self.rgb_widget.show() - self.rgb_add.setChecked(True) - self.hsv_add.setChecked(True) + self.rgb_mul.setChecked(True) + self.hsv_mul.setChecked(True) def rgb_add_changed(self, name, val): if not self.rgb_add.isChecked(): @@ -346,6 +346,7 @@ else: self.rgb_add_sliders.set_sliders({'R': 0, 'G': 0, 'B': 0}) self.rgb_mul_sliders.set_sliders({'R': 500, 'G': 500, 'B': 500}) self.hsv_add_sliders.set_sliders({'H': 0, 'S': 0, 'V': 0}) + self.hsv_mul_sliders.set_sliders({'H': 0, 'S': 500, 'V': 500}) self.bright_sliders.set_sliders({'+': 0, 'x': 500}) def combo_box_changed(self, index): @@ -414,20 +415,76 @@ else: self.update() + 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.statusBar().showMessage('X: Y: ') self.label.setScaledContents(True) self.label.setMouseTracking(True) + self.label.setMinimumSize(QtCore.QSize(100, 100)) self.mixer_panel = MixerPanel(self.arr, self.refresh_image) - self.layout.addWidget(self.mixer_panel) + self.layout.addWidget(self.mixer_panel, 0, 1, 2, 1) self.mixer_panel.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) + def refresh_image(self): pm = QPixmap.fromImage(self.label.img) self.label.setPixmap(pm) @@ -447,17 +504,15 @@ else: x = evt.x() y = evt.y() x, y = self.scale_mouse_pos(x, y) - msg = 'X: %d, Y: %d ' % (x, y) - R = self.arr[y,x,0] - G = self.arr[y,x,1] - B = self.arr[y,x,2] - msg += 'R: %s, G:, %s, B: %s' % (R, G, B) - self.statusBar().showMessage(msg) + 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 - if not app: app = QApplication([]) diff --git a/scikits/image/io/_plugins/util.py b/scikits/image/io/_plugins/util.py index 2bc21819..69886fc5 100644 --- a/scikits/image/io/_plugins/util.py +++ b/scikits/image/io/_plugins/util.py @@ -267,7 +267,7 @@ class ColorMixer(object): Parameters ---------- h_amt : float - The ammount to add to the hue (-360..360) + The ammount to add to the hue (-180..180) s_amt : float The ammount to add to the saturation (-1..1) v_amt : float @@ -282,10 +282,14 @@ class ColorMixer(object): entire image at once. Thus all three additive values, H, S, V, must be supplied simultaneously. + Note that since hue is in degrees, it makes no sense to multiply + that channel, thus an add operation is performed on the hue. And the + values given for h_amt, should be the same as for hsv_add + Parameters ---------- h_amt : float - The ammount to multiply to the hue (0..1) + The ammount to to add to the hue (-180..180) s_amt : float The ammount to multiply to the saturation (0..1) v_amt : float @@ -294,3 +298,45 @@ class ColorMixer(object): ''' _colormixer.hsv_multiply(self.img, self.stateimg, h_amt, s_amt, v_amt) + + def rgb_2_hsv_pixel(self, R, G, B): + '''Convert an RGB value to HSV + + Parameters + ---------- + R : int + Red value + G : int + Green value + B : int + Blue value + + Returns + ------- + out : (H, S, V) Floats + The HSV values + + ''' + H, S, V = _colormixer.py_rgb_2_hsv(R, G, B) + return (H, S, V) + + def hsv_2_rgb_pixel(self, H, S, V): + '''Convert an HSV value to RGB + + Parameters + ---------- + H : float + Hue value + S : float + Saturation value + V : float + Intensity value + + Returns + ------- + out : (R, G, B) ints + The RGB values + + ''' + R, G, B = _colormixer.py_hsv_2_rgb(H, S, V) + return (R, G, B) \ No newline at end of file