HSV 变换就是HSV色彩和RGB色彩之间可以互相转换。
HSV 即使用色相(Hue)、饱和度(Saturation)、明度(Value)来表示色彩的一种方式。
- 色相:将颜色使用0到360度表示,就是平常所说的颜色名称,如红色、蓝色。色相与数值按下表对应:
红 | 黄 | 绿 | 青色 | 蓝色 | 品红 | 红 |
---|---|---|---|---|---|---|
0 | 60 | 120 | 180 | 240 | 300 | 360 |
- 饱和度:是指色彩的纯度,饱和度越低则颜色越黯淡( 0<= S < 1);
- 明度:即颜色的明暗程度。数值越高越接近白色,数值越低越接近黑色 ( 0 <= V < 1);
从 RGB 色彩表示转换到 HSV 色彩表示通过以下方式计算:
R,G,B的值在[0, 1]之间:
Max = max(R,G,B)
Min = min(R,G,B)
H = { 0 (if Min=Max)
60 x (G-R) / (Max-Min) + 60 (if Min=B)
60 x (B-G) / (Max-Min) + 180 (if Min=R)
60 x (R-B) / (Max-Min) + 300 (if Min=G)
V = Max
S = Max - Min
从 HSV 色彩表示转换到 RGB 色彩表示通过以下方式计算:
C = S
H' = H / 60
X = C (1 - |H' mod 2 - 1|)
(R,G,B) = (V - C) (1,1,1) + { (0, 0, 0) (if H is undefined)
(C, X, 0) (if 0 <= H' < 1)
(X, C, 0) (if 1 <= H' < 2)
(0, C, X) (if 2 <= H' < 3)
(0, X, C) (if 3 <= H' < 4)
(X, 0, C) (if 4 <= H' < 5)
(C, 0, X) (if 5 <= H' < 6)
请将色相反转(色相值加180),然后再用 RGB 色彩空间表示图片。
python实现:
import cv2
import numpy as np
# Read image
img = cv2.imread("imori.jpg").astype(np.float32) / 255.
# RGB > HSV
out = np.zeros_like(img)
max_v = np.max(img, axis=2).copy()
min_v = np.min(img, axis=2).copy()
min_arg = np.argmin(img, axis=2)
H = np.zeros_like(max_v)
H[np.where(max_v == min_v)] = 0
## if min == B
ind = np.where(min_arg == 0)
H[ind] = 60 * (img[..., 1][ind] - img[..., 2][ind]) / (max_v[ind] - min_v[ind]) + 60
## if min == R
ind = np.where(min_arg == 2)
H[ind] = 60 * (img[..., 0][ind] - img[..., 1][ind]) / (max_v[ind] - min_v[ind]) + 180
## if min == G
ind = np.where(min_arg == 1)
H[ind] = 60 * (img[..., 2][ind] - img[..., 0][ind]) / (max_v[ind] - min_v[ind]) + 300
V = max_v.copy()
S = max_v.copy() - min_v.copy()
# Transpose Hue
H = (H + 180) % 360
# HSV > RGB
C = S
H_ = H / 60
X = C * (1 - np.abs( H_ % 2 - 1))
Z = np.zeros_like(H)
vals = [[Z,X,C], [Z,C,X], [X,C,Z], [C,X,Z], [C,Z,X], [X,Z,C]]
for i in range(6):
ind = np.where((i <= H_) & (H_ < (i+1)))
out[..., 0][ind] = (V-C)[ind] + vals[i][0][ind]
out[..., 1][ind] = (V-C)[ind] + vals[i][1][ind]
out[..., 2][ind] = (V-C)[ind] + vals[i][2][ind]
out[np.where(max_v == min_v)] = 0
out = (out * 255).astype(np.uint8)
# Save result
cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
C++实现:
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <math.h>
int main(int argc, const char* argv[]){
cv::Mat img = cv::imread("imori.jpg", cv::IMREAD_COLOR);
int width = img.rows;
int height = img.cols;
double _max, _min;
double r, g ,b;
double h, s, v;
double c, _h, x;
double _r, _g, _b;
cv::Mat out = cv::Mat::zeros(height, width, CV_8UC3);
for (int j=0; j<height; j++){
for (int i=0; i<width; i++){
// HSV
r = (float)img.at<cv::Vec3b>(j,i)[2] / 255;
g = (float)img.at<cv::Vec3b>(j,i)[1] / 255;
b = (float)img.at<cv::Vec3b>(j,i)[0] / 255;
_max = fmax(r, fmax(g, b));
_min = fmin(r, fmin(g, b));
if(_max == _min){
h = 0;
} else if (_min == b) {
h = 60 * (g - r) / (_max - _min) + 60;
} else if (_min == r) {
h = 60 * (b - g) / (_max - _min) + 180;
} else if (_min == g) {
h = 60 * (r - b) / (_max - _min) + 300;
}
v = _max;
s = _max - _min;
// inverse hue
h = fmod((h + 180), 360);
// inverse HSV
c = s;
_h = h / 60;
x = c * (1 - abs(fmod(_h, 2) - 1));
_r = _g = _b = v - c;
if (_h < 1) {
_r += c;
_g += x;
} else if (_h < 2) {
_r += x;
_g += c;
} else if (_h < 3) {
_g += c;
_b += x;
} else if (_h < 4) {
_g += x;
_b += c;
} else if (_h < 5) {
_r += x;
_b += c;
} else if (_h < 6) {
_r += c;
_b += x;
}
out.at<cv::Vec3b>(j,i)[0] = (uchar)(_b * 255);
out.at<cv::Vec3b>(j,i)[1] = (uchar)(_g * 255);
out.at<cv::Vec3b>(j,i)[2] = (uchar)(_r * 255);
}
}
//cv::imwrite("out.jpg", out);
cv::imshow("answer", out);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
输入:
输出: