#include <math.h> #include "highgui.h" #include "cv.h" #include "time.h" #include <iostream> int main() { IplImage* image = cvLoadImage("test.png"); IplImage* hsv = cvCreateImage( cvGetSize(image), 8, 3 ); cvCvtColor(image,hsv,CV_BGR2HSV); cvNamedWindow("saturate_SV",CV_WINDOW_AUTOSIZE); cvShowImage("saturate_SV",hsv); cvWaitKey(0); cvReleaseImage(&hsv); cvDestroyWindow("saturate_SV"); return 0; }
函数 cvCvtColor 将输入图像从一个色彩空间转换为另外一个色彩空间。但凡图像处理软件,都会提供色相、饱和度、明度调整功能,比如常见的PS或者美图秀秀。在调整色相、饱和度、明度时,需要将每个像素点的rgb色彩空间转换到hsv色彩空间,然后做相应计算调整,最后在将计算结果转换回rgb颜色空间进行显示。
补充:
HSL 和 HSV(也叫HSB)是对RGB 色彩空间中点的两种有关系的表示,它们尝试描述比 RGB 更准确的感知颜色联系,并仍保持在计算上简单。
H指hue(色相)、S指saturation(饱和度)、L指lightness(亮度)、V指value(色调)、B指brightness(明度)。
HSL 和 HSV 二者都把颜色描述在圆柱坐标系内的点,这个圆柱的中心轴取值为自底部的黑色到顶部的白色而在它们中间是的灰色,绕这个轴的角度对应于“色相”,到这个轴的距离对应于“饱和度”,而沿着这个轴的高度对应于“亮度”,“色调”或“明度”。
这两种表示在用目的上类似,但在方法上有区别。二者在数学上都是圆柱,但 HSV(色相,饱和度,色调)在概念上可以被认为是颜色的倒圆锥体(黑点在下顶点,白色在上底面圆心),HSL 在概念上表示了一个双圆锥体和圆球体(白色在上顶点,黑色在下顶点,最大横切面的圆心是半程灰色)。注意尽管在 HSL 和 HSV 中“色相”指称相同的性质,它们的“饱和度”的定义是明显不同的。
因为 HSL 和 HSV 是设备依赖的 RGB 的简单变换,(h, s, l) 或 (h, s, v) 三元组定义的颜色依赖于所使用的特定红色、绿色和蓝色“加法原色”。每个独特的 RGB 设备都伴随着一个独特的 HSL 和 HSV 空间。但是 (h, s, l) 或 (h, s, v) 三元组在被约束于特定 RGB 空间比如 sRGB 的时候就变成明确的了。
HSV 模型在 1978 年由埃尔维·雷·史密斯创立,它是三原色光模式的一种非线性变换。
HSV 模型通常用于计算机图形应用中。在用户必须选择一个颜色应用于特定图形元素各种应用环境中,经常使用 HSV 色轮(如右图)。
HSV编辑器:
GIMP 支持在 HSV 色彩空间内的选取颜色的多种方法,包括带有色相滑块的色轮和色方。
从 HSV 到 RGB 的转换:
对于每个颜色向量 (r, g, b),
RGB | HSL | HSV | 结果 |
---|---|---|---|
(1, 0, 0) | (0°, 1, 0.5) | (0°, 1, 1) | |
(0.5, 1, 0.5) | (120°, 1, 0.75) | (120°, 0.5, 1) | |
(0, 0, 0.5) | (240°, 1, 0.25) | (240°, 1, 0.5) |
struct BGR { uchar b; uchar g; uchar r; }; struct HSV { int h; double s; double v; }; bool IsEquals(double val1 , double val2 ) { return fabs( val1 - val2) < 0.001; } //RGB(BGR: 0~255)转HSV(H: [0~360), S: [0~1], V: [0~1]) void BGR2HSV(BGR &bgr, HSV &hsv) { double b, g, r; double h, s, v; double min, max; double delta; b = bgr.b / 255.0; g = bgr.g / 255.0; r = bgr.r / 255.0; if (r > g) { max = MAX( r, b); min = MIN( g, b); } else { max = MAX( g, b); min = MIN( r, b); } v = max; delta = max - min; if (IsEquals( max, 0)) { s = 0.0; } else { s = delta/ max; } if (max == min) { h = 0.0; } else { if ( IsEquals( r, max) && g >= b) { h = 60 * ( g - b)/ delta + 0; } else if ( IsEquals( r, max) && g < b) { h = 60 * ( g - b)/ delta + 360; } else if ( IsEquals( g, max)) { h = 60 * ( b - r)/ delta + 120; } else if ( IsEquals( b, max)) { h = 60 * ( r - g)/ delta + 240; } } hsv.h = ( int)( h + 0.5); hsv.h = ( hsv. h > 359) ? ( hsv. h - 360) : hsv. h; hsv.h = ( hsv. h < 0) ? ( hsv. h + 360) : hsv. h; hsv.s = s; hsv.v = v; } //RGB(BGR: 0~255)转HSV(H: [0~360), S: [0~1], V: [0~1]) void HSV2BGR(HSV &hsv, BGR &bgr) { int h = hsv.h; double s = hsv. s; double v = hsv. v; double b = 0.0; double g = 0.0; double r = 0.0; int flag = ( int) abs( h/60.0 ); double f = h/60.0 - flag; double p = v * (1 - s); double q = v * (1 - f* s); double t = v * (1 - (1- f)* s); switch ( flag) { case 0: b = p; g = t; r = v; break; case 1: b = p; g = v; r = q; break; case 2: b = t; g = v; r = p; break; case 3: b = v; g = q; r = p; break; case 4: b = v; g = p; r = t; break; case 5: b = q; g = p; r = v; break; default: break; } int blue = int( b * 255); bgr.b = ( blue > 255) ? 255 : blue; bgr.b = ( blue < 0) ? 0 : bgr. b; int green = int( g * 255); bgr.g = ( green > 255) ? 255 : green; bgr.g = ( green < 0) ? 0 : bgr. g; int red = int( r * 255); bgr.r = ( red > 255) ? 255 : red; bgr.r = ( red < 0) ? 0 : bgr. r; } int main() { string imgName = "e:/MyProjects/image/lena.jpg"; Mat img = cv::imread(imgName); Mat tmp = img.clone(); if (img.data == NULL) { cout<< "Could not open or find the image"<<endl; return -1; } int row = img. rows; int col = img. cols * img.channels(); uchar * pImg = NULL; BGR bgr; HSV hsv; for ( int i = 0; i < row; i++) { pImg = img. ptr< uchar>( i); // 遍历时用img.at()效率会降很多, 很耗时 for ( int j = 0; j < col; j+=3) { bgr. b = pImg[ j]; bgr. g = pImg[ j + 1]; bgr. r = pImg[ j + 2]; BGR2HSV( bgr, hsv); hsv. h = hsv. h + 60;// h取值范围为[0, 360) hsv. h = ( hsv. h > 359) ? hsv. h - 360) : hsv. h; hsv. h = ( hsv. h < 0) ? ( hsv. h + 360) : hsv. h; HSV2BGR( hsv, bgr); pImg[j] = bgr. b; pImg[j + 1] = bgr. g; pImg[j + 2] = bgr. r; } } imshow("dd", img); cvtColor(tmp, tmp, CV_BGR2HSV); vector< Mat> channels; split( tmp, channels); channels[0] += 30;// 这里没有做范围判断,opencv的h取值范围为[0, 180) merge(channels, tmp); cvtColor(tmp, tmp, CV_HSV2BGR); imshow("opencv", tmp); cv::waitKey(); return 0; }
参考网址