opencv颜色过滤--------如何选择需要的颜色呢?
2016-06-06 15:21
441 查看
在日常生活中我们表示颜色的时候都喜欢用RGB模型进行表示,RGB分别代表了三原色:红色Red, 绿色Green,蓝色Blue。但是当我们想要从图片中选取某种颜色的时候,比如说红色,用RGB该怎么做?很难啊。
所以当涉及到颜色的时候我们通常都会将图片转化到hsv空间进行表示。这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。
那么该如何选择我们需要的颜色呢?比如说红色,是否就只需要选择一个固定区间就可以了呢?
如果只考虑计算机画出来的颜色,那当然可以了,但是当我们推广到现实生活中的颜色的时候,我发现很多看上去是红色的色彩并不符合理论中的红色,比如说用手机拍照,用扫描仪扫描图片,色彩都是很容易失真,我们看上去是红色,但它已经不再是红色了。
参照
http://www.xuebuyuan.com/2149290.html
里边的说明,它有如下结论
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/a8fd74dca14977824855e1cd52c30cd3)
所以红色区域为
真是这样么?
让我们来做做实验吧。
我按照上面的规则进行过滤,满足规则的像素我设为白色,不满足的则为黑色。
苹果原图为:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/608f9ea87e8aa7ed66f38b9cd7b8d6a5)
最后的结果为空:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/b5ca8349e7a45e6f60fd0cfaee129fa6)
要求太严格了对吧,那我改成:
结果变为了
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/0cf613a781dca63eaffc9e699b1d247c)
效果真棒哦!
这张苹果图片是张好图片,接下来我们来见识一下其它的红色吧。
用同样的条件,下面处理一下这张淡淡的红色吧,图片是用扫描仪扫描得到:
(里边打印的信息被我涂掉了,但是处理的时候是没有的)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/a555688422df4f7ea1dcda0b8574404d)
得到的结果图很磕碜啊,线条基本上都断掉了,完全不能满足要求啊。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/1db8c4ebcc83a254788e81cda8c1ea58)
那么该如何选择我们需求的阈值呢?
下面介绍一个颜色直方图方法,简单地说就是我们截取我们需要的颜色区域,之后统计一下它里边的颜色直方图,之后根据统计得到的信息划定阈值。
下面是颜色直方图所用代码。
比如说对于以下这幅图,我想要只保留图中的红色部分。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/2313154cbddcfc4932c81b29172824ca)
于是我在画图中打开它,然后选择了一小块红色区域裁剪并保存为新图片,如下图所示:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/8abfc4bdd6dab628064ef64cf3a573cd)
之后我对这一小块区域进行直方图测试,得到以下结果
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/53742181fcd4b0b64469ab9542f17c07)
得到如下结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/96126644a374cc244bb43abe7f17e749)
感觉红章这里过滤得太多了哦,那就继续调整吧,上面的结果告诉我们大致在哪一部分区间进行调整。
接着再截取那些过滤得不是特别好的地方进行实验,过滤条件修改为:
这一回结果变为:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/ef1a189133b5a5d024922ba681d1b3ca)
这次结果在红章这部分很好,但是下面的线好像处理得不太好呢。
所以继续放宽条件。
哈哈,这一回结果perfect啊!
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/74d0017f6b26c9bf8b822aa5410de244)
蓝色部分H主要是在100~130之间。
我对下面这张图做实验
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/2d596635b567fb08e922d1559e560ea3)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/7151cf01e0cb0f7c95eb3f30b1a113f7)
说起来S还是蛮影响结果的,如果我S设小一些,那么结果就出现很多噪声了。
比如说S设置>=10,然后结果就变成了
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/3cd7144c6247fd6511bab28b4610c706)
比如对于下面这张图,我首先用S>35作为条件进行过滤。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/e59159718fd7ec6f4e1a25a5bf6eceb5)
结果如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/9393ff45318a3a556032d817a53aa8e8)
可以看到图片上边缘部分还是有些奇怪的边留下,此时再把V加上,条件变为S>35&&V>190,得到结果如下,哈哈,把那条边给去掉啦:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202005/11/10e2047638ce860048c1cddce273ea67)
我给出的条件是根据我这张图定的,具体问题具体分析,阈值的调整还是需要自己根据手里的图片慢慢来选择。
所以当涉及到颜色的时候我们通常都会将图片转化到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,得到结果如下,哈哈,把那条边给去掉啦:
我给出的条件是根据我这张图定的,具体问题具体分析,阈值的调整还是需要自己根据手里的图片慢慢来选择。
相关文章推荐
- 按右键另存图片只能存BMP
- photoshop去除图片上的水印
- upload上传单张图片
- 图片引发的溢出危机(图)
- C# WinForm控件对透明图片重叠时出现图片不透明的简单解决方法
- C#实现把彩色图片灰度化代码分享
- C#将图片和字节流互相转换并显示到页面上
- C#监控文件夹并自动给图片文件打水印的方法
- 纯CSS实现的当鼠标移上图片添加阴影效果代码
- 如何使用C#从word文档中提取图片
- C#实现打开画图的同时载入图片、最大化显示画图窗体的方法
- C#图片添加水印的实现代码
- 随鼠标移动的图片或文字特效代码
- CSS 图片横向排列实现代码
- C#实现将Email地址转成图片显示的方法
- C#实现图片加相框的方法
- 超级经典一套鼠标控制左右滚动图片带自动翻滚
- 用css实现图片垂直居中的使用技巧
- 一起动手编写Android图片加载框架
- C++实现读取图片长度和宽度