mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-12 21:06:39 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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([])
|
||||
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user