Finalized HSV support in the color-mixer and fixed a few subtle bugs therein.

Updated the fancy imshow widget some more.
This commit is contained in:
sccolbert
2009-11-05 01:36:08 +01:00
parent f4a042177f
commit 93fb7de121
3 changed files with 208 additions and 69 deletions
+90 -52
View File
@@ -126,30 +126,8 @@ def brightness(np.ndarray[np.uint8_t, ndim=3] img,
else:
img[i, j, k] = <np.uint8_t>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
+70 -15
View File
@@ -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([])
+48 -2
View File
@@ -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)