您的位置:首页 > 运维架构

opencv颜色过滤--------如何选择需要的颜色呢?

2016-06-06 15:21 441 查看
在日常生活中我们表示颜色的时候都喜欢用RGB模型进行表示,RGB分别代表了三原色:红色Red, 绿色Green,蓝色Blue。但是当我们想要从图片中选取某种颜色的时候,比如说红色,用RGB该怎么做?很难啊。

所以当涉及到颜色的时候我们通常都会将图片转化到hsv空间进行表示。这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。

那么该如何选择我们需要的颜色呢?比如说红色,是否就只需要选择一个固定区间就可以了呢?

如果只考虑计算机画出来的颜色,那当然可以了,但是当我们推广到现实生活中的颜色的时候,我发现很多看上去是红色的色彩并不符合理论中的红色,比如说用手机拍照,用扫描仪扫描图片,色彩都是很容易失真,我们看上去是红色,但它已经不再是红色了。

参照

http://www.xuebuyuan.com/2149290.html

里边的说明,它有如下结论



所以红色区域为

( (H >= 0  && H <= 10) || (H >= 125  && H <= 180)) && S >= 43 && V >= 46


真是这样么?

让我们来做做实验吧。

我按照上面的规则进行过滤,满足规则的像素我设为白色,不满足的则为黑色。

苹果原图为:



最后的结果为空:



要求太严格了对吧,那我改成:

( (H >= 0  && H <= 10) || (H >= 125  && H <= 180) ) && S >= 43


结果变为了



效果真棒哦!

这张苹果图片是张好图片,接下来我们来见识一下其它的红色吧。

用同样的条件,下面处理一下这张淡淡的红色吧,图片是用扫描仪扫描得到:

(里边打印的信息被我涂掉了,但是处理的时候是没有的)



得到的结果图很磕碜啊,线条基本上都断掉了,完全不能满足要求啊。



那么该如何选择我们需求的阈值呢?

下面介绍一个颜色直方图方法,简单地说就是我们截取我们需要的颜色区域,之后统计一下它里边的颜色直方图,之后根据统计得到的信息划定阈值。

下面是颜色直方图所用代码。

颜色直方图

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;
}


比如说对于以下这幅图,我想要只保留图中的红色部分。



于是我在画图中打开它,然后选择了一小块红色区域裁剪并保存为新图片,如下图所示:



之后我对这一小块区域进行直方图测试,得到以下结果



颜色过滤结果

我按照上面的结果进行实验,颜色过滤代码如下:

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];
}

}
}
}
}


得到如下结果:



感觉红章这里过滤得太多了哦,那就继续调整吧,上面的结果告诉我们大致在哪一部分区间进行调整。

接着再截取那些过滤得不是特别好的地方进行实验,过滤条件修改为:

S >= 35
&&
((  H>=0 && H < 24) || H >= 140)


这一回结果变为:



这次结果在红章这部分很好,但是下面的线好像处理得不太好呢。

所以继续放宽条件。

S >= 15
&&
((  H>=0 && H < 24) || H >= 140)


哈哈,这一回结果perfect啊!



蓝色的查找

在做实验的时候,我发现蓝色特别好找,好神奇的。

蓝色部分H主要是在100~130之间。

我对下面这张图做实验



S >= 40
&&
(  H>=100 && H <= 130)




说起来S还是蛮影响结果的,如果我S设小一些,那么结果就出现很多噪声了。

比如说S设置>=10,然后结果就变成了



黑色的去除

说起来黑色真心难去掉哦,我试了下发现黑色几乎遍布H的全部区域,如果用S和V作为限制条件,又不能取得太高了,要不然会把自己要的部分也给去掉。超级难哦。

比如对于下面这张图,我首先用S>35作为条件进行过滤。



结果如下:



可以看到图片上边缘部分还是有些奇怪的边留下,此时再把V加上,条件变为S>35&&V>190,得到结果如下,哈哈,把那条边给去掉啦:



我给出的条件是根据我这张图定的,具体问题具体分析,阈值的调整还是需要自己根据手里的图片慢慢来选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息