从RGB 到 HSV 的转换详细介绍
1.RGB
RGB是从颜色发光的原理来设计定的,通俗点说它的颜色混合方式就好像有红、绿、蓝三盏灯,当它们的光相互叠合的时候,色彩相混,而亮度却等于两者亮度之总和,越混合亮度越高,即加法混合。
红、绿、蓝三个颜色通道每种色各分为256阶亮度,在0时“灯”最弱——是关掉的,而在255时“灯”最亮。当三色灰度数值相同时,产生不同灰度值的灰色调,即三色灰度都为0时,是最暗的黑色调;三色灰度都为255时,是最亮的白色调。
在电脑中,RGB的所谓“多少”就是指亮度,并使用整数来表示。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2...直到255。注意虽然数字最高是255,但0也是数值之一,因此共256级。
图1.1 RGB
2.HSV
HSV是一种比较直观的颜色模型,所以在许多图像编辑工具中应用比较广泛,这个模型中颜色的参数分别是:色调(H, Hue),饱和度(S,Saturation),明度(V, Value)。
色调H
用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
饱和度S
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
明度V
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
图2.1 HSV
2.1应用openCV中HSV取值范围说明
我们需要注意的在不同应用场景中,HSV取值范围是不尽相同的。
1.PS软件时,H取值范围是0-360,S取值范围是(0%-100%),V取值范围是(0%-100%)。
2.利用openCV中cvSplit函数的在选择图像IPL_DEPTH_32F类型时,H取值范围是0-360,S取值范围是0-1(0%-100%),V取值范围是0-1(0%-100%)。
3.利用openCV中cvSplit函数的在选择图像IPL_DEPTH_8UC类型时,H取值范围是0-180,S取值范围是0-255,V取值范围是0-255。
3.RGB转HSV
3.1公式
3.2代码
测试样例1
#include "cv.h" #include "highgui.h" #include "cxcore.h" /*--------------copyright-hanshanbuleng--------------------*/ // 将色调H的取值范围转换到0~180之间 int main() { float H,S,V,H1,S1,V1; IplImage *src = cvLoadImage("F:\\vs2010program\\RGB_HSV\\study_test\\2.jpg", 1); IplImage *hsv_img = cvCreateImage(cvGetSize(src), 8 , 3); IplImage *h_img = cvCreateImage(cvGetSize(src), 8, 1); IplImage *s_img = cvCreateImage(cvGetSize(src), 8, 1); IplImage *v_img = cvCreateImage(cvGetSize(src), 8, 1); cvCvtColor(src, hsv_img, CV_BGR2HSV); cvSplit(hsv_img, h_img, s_img, v_img, NULL); for(int y = 0; y < hsv_img->height; y++){ for(int x = 0; x < hsv_img->width; x++) { H1 = cvGetReal2D(h_img, y, x); S1 = cvGetReal2D(s_img, y, x); V1 = cvGetReal2D(v_img, y, x); //地址法 H = (uchar)h_img->imageData[y*h_img->widthStep + x*h_img->nChannels]; S = (uchar)s_img->imageData[y*s_img->widthStep + x*s_img->nChannels]; V = (uchar)v_img->imageData[y*v_img->widthStep + x*v_img->nChannels]; printf("H:%f S:%f V:%f \n",H,S,V); } } cvNamedWindow("hsv_img", 0); //HSV图 cvShowImage("hsv_img", hsv_img); cvNamedWindow("h_img", 0); //H通道 cvShowImage("h_img", h_img); cvNamedWindow("s_img", 0); //S通道 cvShowImage("s_img", s_img); cvNamedWindow("v_img", 0); //V通道 cvShowImage("v_img", v_img); cvWaitKey(0); cvReleaseImage(&hsv_img); cvReleaseImage(&h_img); cvReleaseImage(&s_img); cvReleaseImage(&v_img); cvDestroyWindow("hsv_img"); cvDestroyWindow("h_img"); cvDestroyWindow("s_img"); cvDestroyWindow("v_img"); return 0; }
测试样例2
#include "cv.h" #include "highgui.h" #include "cxcore.h" /*---------------copyright-hanshanbuleng------------- 问题描述: 用cvShowImage显示32bits float(IPL_DEPTH_32F)型单通道灰度图像时就出了问题, 图像只有黑白两种颜色,没有灰色的,出现了严重失真,这就是没有正确显示; 问题原因: 如果图像是32位float型,cvShowImage会把像素值乘以255然后再与[0,255]的colormap结合起来显示图像, 也就是说,原来32位folat型图像中值为0的像素被显示成黑色,值大于或等于1的像素被显示成白色。 ---------------------------------------------------*/ //此时H的范围只能在0~360之间 int main() { float H,S,V,H1,S1,V1; IplImage *src = cvLoadImage("F:\\vs2010program\\RGB_HSV\\study_test\\2.jpg", 1); IplImage *hsv_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F , 3); cvConvertScale(src, hsv_img, 1.0, 0.0); IplImage *hsv_img1 = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F , 3); IplImage *h_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1); IplImage *s_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1); IplImage *v_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1); IplImage *h_img1 = cvCreateImage(cvGetSize(src), 8, 1); IplImage *s_img1 = cvCreateImage(cvGetSize(src), 8, 1); IplImage *v_img1 = cvCreateImage(cvGetSize(src), 8, 1); cvCvtColor(hsv_img, hsv_img1, CV_BGR2HSV); cvSplit(hsv_img1, h_img, s_img, v_img, NULL); for(int y = 0; y < hsv_img->height; y++){ for(int x = 0; x < hsv_img->width; x++) { H1 = cvGetReal2D(h_img, y, x); //0-360 S1 = cvGetReal2D(s_img, y, x); //0-1 V1 = cvGetReal2D(v_img, y, x); //0-255 ////地址法 还是有问题 待研究 //H = (double)h_img->imageData[y*h_img->widthStep + x*h_img->nChannels]; //S = s_img->imageData[y*s_img->widthStep + x*s_img->nChannels]; //V = v_img->imageData[y*v_img->widthStep + x*v_img->nChannels]; printf("H:%f S:%f V:%f \n",H1,S1,V1); } } cvCvtScale(v_img,h_img1,255.0/360.0); cvNamedWindow("hsv_img"); //HSV图 cvShowImage("hsv_img", hsv_img); cvNamedWindow("h_img"); //H通道 0-360 显示不正常 cvShowImage("h_img", h_img); cvNamedWindow("s_img"); //S通道 0-1 cvShowImage("s_img", s_img); cvNamedWindow("h_img1"); //V通道 0-255 cvShowImage("h_img1", h_img1); cvNamedWindow("v_img"); cvShowImage("v_img", v_img); cvWaitKey(0); cvReleaseImage(&hsv_img); cvReleaseImage(&h_img); cvReleaseImage(&s_img); cvReleaseImage(&v_img); cvDestroyWindow("hsv_img"); cvDestroyWindow("h_img"); cvDestroyWindow("s_img"); cvDestroyWindow("v_img"); return 0; }
测试样例3
#include "cv.h" #include "highgui.h" #include "cxcore.h" /*--------------copyright-hanshanbuleng--------------------*/ //此时H,S,V的范围均在0~255之间 int main() { float H,S,V,H1,S1,V1; IplImage *src = cvLoadImage("F:\\vs2010program\\RGB_HSV\\study_test\\2.jpg", 1); IplImage *hsv_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F , 3); cvConvertScale(src, hsv_img, 1.0, 0.0); IplImage *hsv_img1 = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F , 3); IplImage *h_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1); IplImage *s_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1); IplImage *v_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1); IplImage *h = cvCreateImage(cvGetSize(src), 8, 1); IplImage *s = cvCreateImage(cvGetSize(src), 8, 1); IplImage *v = cvCreateImage(cvGetSize(src), 8, 1); cvCvtColor(hsv_img, hsv_img1, CV_BGR2HSV); cvSplit(hsv_img1, h_img, s_img, v_img, NULL); //转换表示范围 cvConvertScale(h_img, h, (1.0/360)*255, 0.0); cvConvertScale(s_img, s, 255.0, 0.0); cvConvertScale(v_img, v, 1.0, 0.0); for(int y = 0; y < hsv_img->height; y++){ for(int x = 0; x < hsv_img->width; x++) { //转换后的h_img,s_img,v_img 取值查看 /*----------------------------------*/ //转换后的h,s,v 取值查看 // H1 = cvGetReal2D(h_img, y, x); // S1 = cvGetReal2D(s_img, y, x); // V1 = cvGetReal2D(v_img, y, x); // // //地址法 对比 // H = (double)h_img->imageData[y*h_img->widthStep + x*h_img->nChannels]; // S = (double)s_img->imageData[y*s_img->widthStep + x*s_img->nChannels]; // V = v_img->imageData[y*v_img->widthStep + x*v_img->nChannels]; /*----------------------------------*/ //转换后的h,s,v 取值查看 /*----------------------------------*/ H1 = cvGetReal2D(h, y, x); S1 = cvGetReal2D(s, y, x); V1 = cvGetReal2D(v, y, x); //地址法 对比 H = (uchar)h->imageData[y*h->widthStep + x*h->nChannels]; S = (uchar)s->imageData[y*s->widthStep + x*s->nChannels]; V = (uchar)v->imageData[y*s->widthStep + x*s->nChannels]; /*----------------------------------*/ printf("H:%f S:%f V:%f \n",H,S,V); } } cvNamedWindow("hsv_img", 0); //HSV图 cvShowImage("hsv_img", hsv_img); cvNamedWindow("h_img", 0); //H通道 cvShowImage("h_img", h_img); cvNamedWindow("s_img", 0); //S通道 cvShowImage("s_img", s_img); cvNamedWindow("v_img", 0); //V通道 cvShowImage("v_img", v_img); cvWaitKey(0); cvReleaseImage(&hsv_img); cvReleaseImage(&h_img); cvReleaseImage(&s_img); cvReleaseImage(&v_img); cvDestroyWindow("hsv_img"); cvDestroyWindow("h_img"); cvDestroyWindow("s_img"); cvDestroyWindow("v_img"); return 0; }
4.引申—本实例使用opencv函数简介
函数介绍百度一下就可以,在此我只是简单说明一下
cvLoadImage() //加载图片
cvCreateImage()//创建图片大小
cvCvtColor() //空间转换
cvSplit() //分离不同通道
cvCvtScale() //调整比例
cvNamedWindow()//创建图像显示窗口
cvReleaseImage()//释放创建的图片
cvWaitKey() //等待
cvDestroyWindow()//销毁窗口
5.参考链接
https://blog.csdn.net/yangdashi888/article/details/53782481
https://blog.csdn.net/zhupananhui/article/details/21157541
https://baike.baidu.com/item/HSV/547122