大纲
1. hsv颜色空间简介
2. 为什么是HSV
3. 识别方法说明
4. 识别步骤解析(代码片段)
4. 检测结果
一、HSV颜色空间
由色调(Hue)、饱和度(Saturation)、亮度(Value)三个分量构成,HSV更接近于人眼的主观感受。我们可以通过下面的图来展示HSV颜色分布情况:
使用了下面的matlab程序画出上面的图形,感兴趣的可以仿真一下。程序很简单。
% 创建hsv分量
hue = repmat(linspace(0,1,100),100,1);
saturation = repmat([linspace(0,1,50) linspace(1,0,50)].',1,100);
value = repmat([ones(1,50) linspace(1,0,50)].',1,100);
% 生成hsv图像
hsvImage = cat(3,hue,saturation,value);
% 转换成rgb图像
rgbImage = hsv2rgb(hsvImage);
% 构造座标系
theta = linspace(0,2*pi,100);
Xcor = [zeros(1,100); cos(theta); zeros(1,100)];
Ycor = [zeros(1,100); sin(theta); zeros(1,100)];
Zcor = [2.*ones(2,100); zeros(1,100)];
surf(Xcor,Ycor,Zcor,rgbImage,'Clipping','on','FaceColor','texturemap','EdgeColor','none');
axis equal
二、为什么是HSV
对于图像而言,识别相应的颜色在RGB空间、HSV空间或者其它颜色空间都是可行的。之所以选择HSV,是因为H代表的色调基本上可以确定某种颜色,再结合饱和度和亮度信息判断大于某一个阈值(这里是40到255)。而RGB由三个分量构成,需要判断每种分量的贡献比例。比如:R = 200, G = 20, B = 30,可以看到R值很大,所以是红色,再看GB值相对较小,可以判断为深红色。但如果变成:R = 200, G = 190, B = 180,实际上颜色已经接近灰色。
三、识别方法说明
基本原理很简单,读入图片后,首先转换成HSV颜色空间。然后逐一的判断每个像素是否在一定范围内,并标识出来(是就标识为255,不是就标识为0)。这样就可以用查找轮廓的方式,把每个颜色区域标识出来。
四、识别步骤解析(代码片段)
1. 读入待测试图片,并预览。
image = imread("test4.png", CV_LOAD_IMAGE_COLOR);
if (!image.data)
{
cout << "Could not open or find the image" << std::endl;
getchar();
return -1;
}
imshow("Display image", image);
2. 默认读入的颜色空间为RGB,这里首先转换成HSV。
cvtColor(image, hsvImg, COLOR_BGR2HSV);
3. 指定识别颜色的取值范围,这里大概的分成了R、G、B三种色彩,如果需要识别更多颜色,可以增加取值,也可以调整值的范围,满足特定需要。
enum colorType{Red = 0, Green, Blue, ColorButt};
const Scalar hsvRedLo( 0, 40, 40);
const Scalar hsvRedHi(40, 255, 255);
const Scalar hsvGreenLo(41, 40, 40);
const Scalar hsvGreenHi(90, 255, 255);
const Scalar hsvBlueLo(100, 40, 40);
const Scalar hsvBlueHi(140, 255, 255);
vector<Scalar> hsvLo{hsvRedLo, hsvGreenLo, hsvBlueLo};
vector<Scalar> hsvHi{hsvRedHi, hsvGreenHi, hsvBlueHi};
vector<String> textColor{"R", "G", "B"};
4. 针对每一种颜色,做下面的几个步骤。
1)在图片中查找相关颜色,并转换成二值图
// 查找指定范围内的颜色
inRange(hsvImg, hsvLo[colorIdx], hsvHi[colorIdx], imgThresholded);
// 转换成二值图
threshold(imgThresholded, imgThresholded, 1, 255, THRESH_BINARY);
2)将所得的二值图像四边都增加一个像素,再查找轮廓。这样做的一个明显情况是当图像为纯色的时候,整图只有一种颜色的判断。当然可能还有各种情况,需要分别判断。
copyMakeBorder(imgThresholded, imag_1, 1, 1, 1, 1, BORDER_CONSTANT, 0);
vector<vector<Point> > contours0;
vector<Vec4i> hierarchy;
findContours(imag_1, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
3) 检查所有的轮廓中心点,如果非0,判断为所需要查找的颜色区域,用文本标记颜色。如蓝色标记为字母‘B’。
for (int idx = 0; idx < contours0.size(); idx++ )
{
Rect bound = boundingRect(Mat(contours0[idx]));
Point bc = Point(bound.x + bound.width / 2,
bound.y + bound.height / 2);
uchar x = imgThresholded.at<uchar>(bc);
if (x > 0)
{
org = bc;
putText(image, textColor[colorIdx], org, fontFace, 1, color,
thickness, lineType, bottomLeftOrigin);
}
}
5. 重复上面步骤,直到所有颜色都识别完毕。
五、检测结果
用excel画的颜色图,图中可以看到颜色都正确的标识了。第三行不是没有识别出来,而是字母的颜色与本来的颜色一样,这里懒得设置了。