opencv颜色过滤--------如何选择需要的颜色呢?
Posted 微卡智享
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv颜色过滤--------如何选择需要的颜色呢?相关的知识,希望对你有一定的参考价值。
文章转自 http://blog.csdn.net/jianjian1992/article/details/51274834
自已学了一个HSV的图片转换,用于图像处理方面
在日常生活中我们表示颜色的时候都喜欢用RGB模型进行表示,RGB分别代表了三原色:红色Red, 绿色Green,蓝色Blue。但是当我们想要从图片中选取某种颜色的时候,比如说红色,用RGB该怎么做?很难啊。
所以当涉及到颜色的时候我们通常都会将图片转化到hsv空间进行表示。这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。
那么该如何选择我们需要的颜色呢?比如说红色,是否就只需要选择一个固定区间就可以了呢?
如果只考虑计算机画出来的颜色,那当然可以了,但是当我们推广到现实生活中的颜色的时候,我发现很多看上去是红色的色彩并不符合理论中的红色,比如说用手机拍照,用扫描仪扫描图片,色彩都是很容易失真,我们看上去是红色,但它已经不再是红色了。
里边的说明,它有如下结论
所以红色区域为
( (H >= 0 && H <= 10) || (H >= 125 && H <= 180)) && S >= 43 && V >= 46
1
真是这样么?
让我们来做做实验吧。
我按照上面的规则进行过滤,满足规则的像素我设为白色,不满足的则为黑色。
苹果原图为:
最后的结果为空:
要求太严格了对吧,那我改成:
( (H >= 0 && H <= 10) || (H >= 125 && H <= 180) ) && S >= 43
1
结果变为了
效果真棒哦!
这张苹果图片是张好图片,接下来我们来见识一下其它的红色吧。
用同样的条件,下面处理一下这张淡淡的红色吧,图片是用扫描仪扫描得到:
(里边打印的信息被我涂掉了,但是处理的时候是没有的)
得到的结果图很磕碜啊,线条基本上都断掉了,完全不能满足要求啊。
那么该如何选择我们需求的阈值呢?
下面介绍一个颜色直方图方法,简单地说就是我们截取我们需要的颜色区域,之后统计一下它里边的颜色直方图,之后根据统计得到的信息划定阈值。
下面是颜色直方图所用代码。
颜色直方图
vector<MatND> getHSVHist(Mat &src){ //输入图片得是三通道彩色图片
assert (!src.empty() && src.channels() == 3); //rgb转hsv图像
Mat hsv;
cvtColor(src, hsv, CV_BGR2HSV); //h的范围是0~180,所以选取30个bin
//s和v的范围都是0~255,那就选择51个bin
int hbins = 30; int sbins = 51; int vbins = 51; int hHistSize[] = {hbins}; int sHistSize[] = {sbins}; int vHistSize[] = {vbins}; float hranges[] = {0, 180}; float sranges[] = {0, 255}; float vranges[] = {0, 255}; const float* hRanges[] = {hranges}; const float* sRanges[] = {sranges}; const float* vRanges[] = {vranges}; vector<MatND> hist; int hChannels[] = {0}; int sChannels[] = {1}; int vChannels[] = {2};
MatND hHist, sHist, vHist;
calcHist(&hsv, 1, hChannels, Mat(), hHist, 1, hHistSize, hRanges);
calcHist(&hsv, 1, sChannels, Mat(), sHist, 1, sHistSize, sRanges);
calcHist(&hsv, 1, vChannels, Mat(), vHist, 1, vHistSize, vRanges);
hist.push_back(hHist);
hist.push_back(sHist);
hist.push_back(vHist);
normalize( hist[0], hist[0], 0, 1, NORM_MINMAX, -1, Mat() );
normalize( hist[1], hist[1], 0, 1, NORM_MINMAX, -1, Mat() );
normalize( hist[2], hist[2], 0, 1, NORM_MINMAX, -1, Mat() ); int i;
int start = -1, end = -1; for(i = 0; i < 30; i++)
{
float value = hist[0].at<float>(i); if (value > 0)
{ if (start == -1)
{
start = i;
end = i;
} else
end = i; cout << "H Value" << i << ": " << value << endl;
} else
{ if (start != -1) cout <<"H:" <<start*6 <<"~"<<(end+1)*6-1<<endl;
start = end = -1;
}
}
if (start != -1) cout <<"H:" <<start*5 <<"~"<<(end+1)*5-1<<endl;
start = -1, end = -1; for(i = 0; i < 51; i++)
{
float value = hist[1].at<float>(i); if (value > 0)
{ if (start == -1)
{
start = i;
end = i;
} else
end = i; cout << "S Value" << i << ": " << value << endl;
} else
{ if (start != -1) cout <<"S:"<< start*5 <<"~"<<(end+1)*5-1<<endl;
start = end = -1;
}
}
if (start != -1) cout <<"S:" <<start*5 <<"~"<<(end+1)*5-1<<endl;
start = -1, end = -1; for(i = 0; i < 51; i++)
{
float value = hist[2].at<float>(i); if (value > 0)
{ if (start == -1)
{
start = i;
end = i;
} else
end = i; cout << "V Value" << i << ": " << value << endl;
} else
{ if (start != -1) cout <<"V:" <<start*5 <<"~"<<(end+1)*5-1<<endl;
start = end = -1;
}
}
if (start != -1) cout <<"V:" <<start*5 <<"~"<<(end+1)*5-1<<endl; return hist;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
比如说对于以下这幅图,我想要只保留图中的红色部分。
于是我在画图中打开它,然后选择了一小块红色区域裁剪并保存为新图片,如下图所示:
之后我对这一小块区域进行直方图测试,得到以下结果
颜色过滤结果
我按照上面的结果进行实验,颜色过滤代码如下:
void filteredRed(const Mat &inputImage, Mat &resultGray, Mat &resultColor){ Mat hsvImage; cvtColor(inputImage, hsvImage, CV_BGR2HSV); resultGray = Mat(hsvImage.rows, hsvImage.cols,CV_8U,cv::Scalar(255)); resultColor = Mat(hsvImage.rows, hsvImage.cols,CV_8UC3,cv::Scalar(255, 255, 255)); double H=0.0,S=0.0,V=0.0; for(int i=0;i<hsvImage.rows;i++) { for(int j=0;j<hsvImage.cols;j++) { H=hsvImage.at<Vec3b>(i,j)[0]; S=hsvImage.at<Vec3b>(i,j)[1]; V=hsvImage.at<Vec3b>(i,j)[2]; if((S >= 70 && S<155) || (S >= 35 && S<65)) { if(( H>=0 && H < 24) && V >= 215) { resultGray.at<uchar>(i,j)=0; resultColor.at<Vec3b>(i, j)[0] = inputImage.at<Vec3b>(i, j)[0]; resultColor.at<Vec3b>(i, j)[1] = inputImage.at<Vec3b>(i, j)[1]; resultColor.at<Vec3b>(i, j)[2] = inputImage.at<Vec3b>(i, j)[2]; } } } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
得到如下结果:
感觉红章这里过滤得太多了哦,那就继续调整吧,上面的结果告诉我们大致在哪一部分区间进行调整。
接着再截取那些过滤得不是特别好的地方进行实验,过滤条件修改为:
S >= 35 && (( H>=0 && H < 24) || H >= 140)
1
2
3
这一回结果变为:
这次结果在红章这部分很好,但是下面的线好像处理得不太好呢。
所以继续放宽条件。
S >= 15 && (( H>=0 && H < 24) || H >= 140)
1
2
3
哈哈,这一回结果perfect啊!
蓝色的查找
在做实验的时候,我发现蓝色特别好找,好神奇的。
蓝色部分H主要是在100~130之间。
我对下面这张图做实验
S >= 40 && ( H>=100 && H <= 130)
1
2
3
说起来S还是蛮影响结果的,如果我S设小一些,那么结果就出现很多噪声了。
比如说S设置>=10,然后结果就变成了
黑色的去除
说起来黑色真心难去掉哦,我试了下发现黑色几乎遍布H的全部区域,如果用S和V作为限制条件,又不能取得太高了,要不然会把自己要的部分也给去掉。超级难哦。
比如对于下面这张图,我首先用S>35作为条件进行过滤。
结果如下:
可以看到图片上边缘部分还是有些奇怪的边留下,此时再把V加上,条件变为S>35&&V>190,得到结果如下,哈哈,把那条边给去掉啦:
我给出的条件是根据我这张图定的,具体问题具体分析,阈值的调整还是需要自己根据手里的图片慢慢来选择。
以上是关于opencv颜色过滤--------如何选择需要的颜色呢?的主要内容,如果未能解决你的问题,请参考以下文章
如何在OpenCV中为InRange阈值选择颜色的最佳HSV值