mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-16 11:21:25 +08:00
added HSV operations to the ColorMixer and fancy imshow.
This commit is contained in:
@@ -125,3 +125,308 @@ def brightness(np.ndarray[np.uint8_t, ndim=3] img,
|
||||
img[i, j, k] = 0
|
||||
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]
|
||||
B = RGB[2]
|
||||
|
||||
if R > 255:
|
||||
R = 255
|
||||
elif R < 0:
|
||||
R = 0
|
||||
else:
|
||||
pass
|
||||
|
||||
if G > 255:
|
||||
G = 255
|
||||
elif G < 0:
|
||||
G = 0
|
||||
else:
|
||||
pass
|
||||
|
||||
if B > 255:
|
||||
B = 255
|
||||
elif B < 0:
|
||||
B = 0
|
||||
else:
|
||||
pass
|
||||
|
||||
if R < G:
|
||||
MIN = R
|
||||
MAX = G
|
||||
else:
|
||||
MIN = G
|
||||
MAX = R
|
||||
|
||||
if B < MIN:
|
||||
MIN = B
|
||||
elif B > MAX:
|
||||
MAX = B
|
||||
else:
|
||||
pass
|
||||
|
||||
V = MAX / 255.
|
||||
|
||||
if MAX == MIN:
|
||||
H = 0.
|
||||
elif MAX == R:
|
||||
H = (60 * (G - B) / (MAX - MIN)) % 360
|
||||
elif MAX == G:
|
||||
H = 60 * (B - R) / (MAX - MIN) + 120
|
||||
else:
|
||||
H = 60 * (R - G) / (MAX - MIN) + 240
|
||||
|
||||
if MAX == 0:
|
||||
S = 0
|
||||
else:
|
||||
S = 1 - MIN / MAX
|
||||
|
||||
HSV[0] = H
|
||||
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
|
||||
|
||||
H = HSV[0]
|
||||
S = HSV[1]
|
||||
V = HSV[2]
|
||||
|
||||
if H > 360:
|
||||
H = 360
|
||||
elif H < 0:
|
||||
H = 0
|
||||
else:
|
||||
pass
|
||||
|
||||
if S > 1:
|
||||
S = 1
|
||||
elif S < 0:
|
||||
S = 0
|
||||
else:
|
||||
pass
|
||||
|
||||
if V > 1:
|
||||
V = 1
|
||||
elif V < 0:
|
||||
V = 0
|
||||
else:
|
||||
pass
|
||||
|
||||
hi = (<int>(H / 60.)) % 6
|
||||
f = (H / 60.) - (<int>(H / 60.))
|
||||
p = V * (1 - S)
|
||||
q = V * (1 - f * S)
|
||||
t = V * (1 - (1 -f) * S)
|
||||
|
||||
if hi == 0:
|
||||
r = V
|
||||
g = t
|
||||
b = p
|
||||
elif hi == 1:
|
||||
r = q
|
||||
g = V
|
||||
b = p
|
||||
elif hi == 2:
|
||||
r = p
|
||||
g = V
|
||||
b = t
|
||||
elif hi == 3:
|
||||
r = p
|
||||
g = q
|
||||
b = V
|
||||
elif hi == 4:
|
||||
r = t
|
||||
g = p
|
||||
b = V
|
||||
else:
|
||||
r = V
|
||||
g = p
|
||||
b = q
|
||||
|
||||
RGB[0] = r
|
||||
RGB[1] = g
|
||||
RGB[2] = b
|
||||
|
||||
@cython.boundscheck(False)
|
||||
def hsv_add(np.ndarray[np.uint8_t, ndim=3] img,
|
||||
np.ndarray[np.uint8_t, ndim=3] stateimg,
|
||||
float h_amt, float s_amt, float v_amt):
|
||||
"""Modify the image color by specifying additive HSV Values.
|
||||
|
||||
Since the underlying images are RGB, all three values HSV
|
||||
must be specified at the same time.
|
||||
|
||||
The RGB triplet in the image is converted to HSV, the operation
|
||||
is applied, and then the HSV triplet is converted back to RGB
|
||||
|
||||
HSV values are scaled to H(0. - 360.), S(0. - 1.), V(0. - 1.)
|
||||
then the operation is performed and any overflow is clipped, then the
|
||||
reverse transform is performed. Those are the ranges to keep in mind,
|
||||
when passing in values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
img : (M, N, 3) ndarray of uint8
|
||||
Output image.
|
||||
stateimg : (M, N, 3) ndarray of uint8
|
||||
Input image.
|
||||
h_amt : float
|
||||
Ammount to add to H channel.
|
||||
s_amt : float
|
||||
Ammount to add to S channel.
|
||||
v_amt : float
|
||||
Ammount to add to V channel.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
cdef int height = img.shape[0]
|
||||
cdef int width = img.shape[1]
|
||||
|
||||
cdef float HSV[3]
|
||||
cdef float RGB[3]
|
||||
|
||||
cdef int i, j
|
||||
|
||||
for i in range(height):
|
||||
for j in range(width):
|
||||
RGB[0] = stateimg[i, j, 0]
|
||||
RGB[1] = stateimg[i, j, 1]
|
||||
RGB[2] = stateimg[i, j, 2]
|
||||
|
||||
rgb_2_hsv(RGB, HSV)
|
||||
|
||||
# Add operation
|
||||
HSV[0] += h_amt
|
||||
HSV[1] += s_amt
|
||||
HSV[2] += v_amt
|
||||
|
||||
hsv_2_rgb(HSV, RGB)
|
||||
|
||||
RGB[0] *= 255
|
||||
RGB[1] *= 255
|
||||
RGB[2] *= 255
|
||||
|
||||
img[i, j, 0] = <np.uint8_t>RGB[0]
|
||||
img[i, j, 1] = <np.uint8_t>RGB[1]
|
||||
img[i, j, 2] = <np.uint8_t>RGB[2]
|
||||
|
||||
@cython.boundscheck(False)
|
||||
def hsv_multiply(np.ndarray[np.uint8_t, ndim=3] img,
|
||||
np.ndarray[np.uint8_t, ndim=3] stateimg,
|
||||
float h_amt, float s_amt, float v_amt):
|
||||
"""Modify the image color by specifying multiplicative HSV Values.
|
||||
|
||||
Since the underlying images are RGB, all three values HSV
|
||||
must be specified at the same time.
|
||||
|
||||
The RGB triplet in the image is converted to HSV, the operation
|
||||
is applied, and then the HSV triplet is converted back to RGB
|
||||
|
||||
HSV values are scaled to H(0. - 360.), S(0. - 1.), V(0. - 1.)
|
||||
then the operation is performed and any overflow is clipped, then the
|
||||
reverse transform is performed. Those are the ranges to keep in mind,
|
||||
when passing in values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
img : (M, N, 3) ndarray of uint8
|
||||
Output image.
|
||||
stateimg : (M, N, 3) ndarray of uint8
|
||||
Input image.
|
||||
h_amt : float
|
||||
Ammount to add to H channel.
|
||||
s_amt : float
|
||||
Ammount to add to S channel.
|
||||
v_amt : float
|
||||
Ammount to add to V channel.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
cdef int height = img.shape[0]
|
||||
cdef int width = img.shape[1]
|
||||
|
||||
cdef float HSV[3]
|
||||
cdef float RGB[3]
|
||||
|
||||
cdef int i, j
|
||||
|
||||
for i in range(height):
|
||||
for j in range(width):
|
||||
RGB[0] = stateimg[i, j, 0]
|
||||
RGB[1] = stateimg[i, j, 1]
|
||||
RGB[2] = stateimg[i, j, 2]
|
||||
|
||||
rgb_2_hsv(RGB, HSV)
|
||||
|
||||
# Multiply operation
|
||||
HSV[0] *= h_amt
|
||||
HSV[1] *= s_amt
|
||||
HSV[2] *= v_amt
|
||||
|
||||
hsv_2_rgb(HSV, RGB)
|
||||
|
||||
RGB[0] *= 255
|
||||
RGB[1] *= 255
|
||||
RGB[2] *= 255
|
||||
|
||||
img[i, j, 0] = <np.uint8_t>RGB[0]
|
||||
img[i, j, 1] = <np.uint8_t>RGB[1]
|
||||
img[i, j, 2] = <np.uint8_t>RGB[2]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -216,6 +216,36 @@ else:
|
||||
self.rgb_widget.layout.addWidget(self.rgb_add_sliders, 2, 0)
|
||||
self.rgb_widget.layout.addWidget(self.rgb_mul_sliders, 2, 0)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# HSV sliders
|
||||
#---------------------------------------------------------------
|
||||
|
||||
# radio buttons
|
||||
self.hsv_add = QtGui.QRadioButton('Additive')
|
||||
self.hsv_mul = QtGui.QRadioButton('Multiplicative')
|
||||
self.hsv_mul.toggled.connect(self.hsv_radio_changed)
|
||||
self.hsv_add.toggled.connect(self.hsv_radio_changed)
|
||||
|
||||
# additive sliders
|
||||
self.hsv_add_sliders = NSliderBlock(3, [(-360, 360, 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),
|
||||
(0, 1000, 500, 'S', .002),
|
||||
(0, 1000, 500, 'V', .002)],
|
||||
self.hsv_mul_changed)
|
||||
|
||||
# layout
|
||||
self.hsv_widget = QWidget()
|
||||
self.hsv_widget.layout = QtGui.QGridLayout(self.hsv_widget)
|
||||
self.hsv_widget.layout.addWidget(self.hsv_add, 0, 0)
|
||||
self.hsv_widget.layout.addWidget(self.hsv_mul, 1, 0)
|
||||
self.hsv_widget.layout.addWidget(self.hsv_add_sliders, 2, 0)
|
||||
self.hsv_widget.layout.addWidget(self.hsv_mul_sliders, 2, 0)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Brightness sliders
|
||||
#---------------------------------------------------------------
|
||||
@@ -244,6 +274,7 @@ else:
|
||||
self.layout = QtGui.QGridLayout(self)
|
||||
self.layout.addWidget(self.combo_box, 0, 0)
|
||||
self.layout.addWidget(self.rgb_widget, 1, 0)
|
||||
self.layout.addWidget(self.hsv_widget, 1, 0)
|
||||
self.layout.addWidget(self.bright_widget, 1, 0)
|
||||
self.layout.addWidget(self.commit_button, 2, 0)
|
||||
self.layout.addWidget(self.revert_button, 3, 0)
|
||||
@@ -256,6 +287,7 @@ else:
|
||||
self.hide_sliders()
|
||||
self.rgb_widget.show()
|
||||
self.rgb_add.setChecked(True)
|
||||
self.hsv_add.setChecked(True)
|
||||
|
||||
def rgb_add_changed(self, name, val):
|
||||
if not self.rgb_add.isChecked():
|
||||
@@ -283,6 +315,24 @@ else:
|
||||
return
|
||||
self.update()
|
||||
|
||||
def hsv_add_changed(self, name, val):
|
||||
if not self.hsv_add.isChecked():
|
||||
return
|
||||
h = self.hsv_add_sliders.sliders['H'].conv_val()
|
||||
s = self.hsv_add_sliders.sliders['S'].conv_val()
|
||||
v = self.hsv_add_sliders.sliders['V'].conv_val()
|
||||
self.mixer.hsv_add(h, s, v)
|
||||
self.update()
|
||||
|
||||
def hsv_mul_changed(self, name, val):
|
||||
if not self.hsv_mul.isChecked():
|
||||
return
|
||||
h = self.hsv_mul_sliders.sliders['H'].conv_val()
|
||||
s = self.hsv_mul_sliders.sliders['S'].conv_val()
|
||||
v = self.hsv_mul_sliders.sliders['V'].conv_val()
|
||||
self.mixer.hsv_multiply(h, s, v)
|
||||
self.update()
|
||||
|
||||
def bright_changed(self, name, val):
|
||||
# doesnt matter which slider changed we need both
|
||||
# values
|
||||
@@ -295,6 +345,7 @@ else:
|
||||
def reset_sliders(self):
|
||||
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.bright_sliders.set_sliders({'+': 0, 'x': 500})
|
||||
|
||||
def combo_box_changed(self, index):
|
||||
@@ -307,6 +358,7 @@ else:
|
||||
|
||||
def hide_sliders(self):
|
||||
self.rgb_widget.hide()
|
||||
self.hsv_widget.hide()
|
||||
self.bright_widget.hide()
|
||||
|
||||
def rgb_radio_changed(self):
|
||||
@@ -323,12 +375,27 @@ else:
|
||||
self.mixer.set_to_stateimg()
|
||||
self.update()
|
||||
|
||||
def hsv_radio_changed(self):
|
||||
if self.hsv_add.isChecked():
|
||||
self.hsv_add_sliders.show()
|
||||
self.hsv_mul_sliders.hide()
|
||||
elif self.hsv_mul.isChecked():
|
||||
self.hsv_mul_sliders.show()
|
||||
self.hsv_add_sliders.hide()
|
||||
else:
|
||||
pass
|
||||
|
||||
self.reset_sliders()
|
||||
self.mixer.set_to_stateimg()
|
||||
self.update()
|
||||
|
||||
def show_rgb(self):
|
||||
self.hide_sliders()
|
||||
self.rgb_widget.show()
|
||||
|
||||
def show_hsv(self):
|
||||
self.hide_sliders()
|
||||
self.hsv_widget.show()
|
||||
|
||||
def show_bright(self):
|
||||
self.hide_sliders()
|
||||
|
||||
@@ -244,4 +244,53 @@ class ColorMixer(object):
|
||||
_colormixer.multiply(self.img, self.stateimg, channel, ammount)
|
||||
|
||||
def brightness(self, offset, factor):
|
||||
'''Adjust the brightness off an image with an offset and factor.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
offset : integer
|
||||
The ammount to add to each channel.
|
||||
factor : float
|
||||
The factor to multiply each channel by.
|
||||
|
||||
result = clip((pixel + offset)*factor)
|
||||
|
||||
'''
|
||||
_colormixer.brightness(self.img, self.stateimg, offset, factor)
|
||||
|
||||
def hsv_add(self, h_amt, s_amt, v_amt):
|
||||
'''Adjust the H, S, V channels of an image by a constant ammount.
|
||||
This is similar to the add() mixer function, but operates over the
|
||||
entire image at once. Thus all three additive values, H, S, V, must
|
||||
be supplied simultaneously.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
h_amt : float
|
||||
The ammount to add to the hue (-360..360)
|
||||
s_amt : float
|
||||
The ammount to add to the saturation (-1..1)
|
||||
v_amt : float
|
||||
The ammount to add to the value (-1..1)
|
||||
|
||||
'''
|
||||
_colormixer.hsv_add(self.img, self.stateimg, h_amt, s_amt, v_amt)
|
||||
|
||||
def hsv_multiply(self, h_amt, s_amt, v_amt):
|
||||
'''Adjust the H, S, V channels of an image by a constant ammount.
|
||||
This is similar to the add() mixer function, but operates over the
|
||||
entire image at once. Thus all three additive values, H, S, V, must
|
||||
be supplied simultaneously.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
h_amt : float
|
||||
The ammount to multiply to the hue (0..1)
|
||||
s_amt : float
|
||||
The ammount to multiply to the saturation (0..1)
|
||||
v_amt : float
|
||||
The ammount to multiply to the value (0..1)
|
||||
|
||||
'''
|
||||
_colormixer.hsv_multiply(self.img, self.stateimg, h_amt, s_amt, v_amt)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user