【opencv】论如何在与目标颜色相似的背景下找到目标

       在标定目标时,除了目标自身的原因(光照、噪声等)外,背景的复杂程度也是在选取目标时能否顺利的一个重要因素。而在与目标相似的背景下,往往会因颜色等相近而导致目标选取失败,为了解决这种情况,下面我来简单地介绍一个自己的思路~~

                                                    首先我们先来看下效果~~

      

                                                   原 图 1

         

                                                 效 果 图 1

                                                  原 图 2

                                                效 果 图 2

       可以看到,在背景与目标的颜色相似的情况下,计算机依旧能把我想要的目标找出来,而实现这些总的功能的代码量却只有短短的110行

       实现的原理如下

                            ———————H通道分离———————

        我们知道,在颜色空间上,有个模型叫六角锥体模型,这个模型的颜色参数分别是:色调(H)、饱和度(S)和亮度(V)。而色调H代表色彩信息,即所处的光谱颜色的位置,饱和度S代表了所选颜色的纯度和该颜色最大的纯度之间的比率,亮度V表示色彩的明亮程度。

        由此可知,我们可以去掉一张照片中饱和度和亮度的影响,剩下的就是在色调上面做功夫了,下面就是图片进行通道分离后的结果图。

       

                                       原 图 1 的 H 通 道

——————————————————————————————————————————————————————

       可以看到,H通道上面的照片,接近白色的目标出现在中间,背景接近黑色。但是,如果我把另外一张照片的H通道分离出来,却出现了下面这种效果

                                         原 图 2 的 H 通 道

      可以看到,目标虽然出现在中间,但是背景却是接近于白色的,而不是我们想要的接近黑色,而目标恰好接近黑色,因为颜色是有深浅之分的,当颜色偏于深时,在分离后时接近于白色的,而颜色偏于浅时,分离后时接近于黑色的,为什么会这样?在此不做详讨~~懒!

       为此,我们需要在这里加多一个判断,确保我们的目标时接近于白色。而判断该怎么添加?我们在后面会有说道。

                            ————H通道图与背景图相减—————

           在制作背景图时,我首选是采用了opencv自带的函数:meanStdDev()

C++: 
void meanStdDev(InputArray src,OutputArray mean,OutputArray stddev,inputArray mask=noArray())

Parameter:
Src:输出矩阵,这个矩阵应该是1-4通道的,这可以将计算结果存在Scalar中
Mean:输出参数,计算均值
Stddev:输出参数,计算标准值
Mask:可选参数

                                            ———主要代码如下———

	Mat dst;
	Mat std;
	Mat stddev;
	int m,s;
	int height = src.rows;
	int width = src.cols;

	dst = src.clone();

	meanStdDev(src,std,stddev);
	m = std.at<double>(0,0);
	
	for(int row = 0;row < height;row++)
	{
		for(int col = 0;col < width;col++)
			dst.at<uchar>(row,col) = m;
	}

得到的结果图如下

            

然后我们让H通道的照片与均值背景图所对应的像素相减,具体操作如下

                         

得出以下结果

          

至此,我们已经可以去掉大部分的背景

           ——————————去光差——————————

再来,我们把图片的光差去掉

                                               ———主要代码如下———

Mat dst;
Mat srcclone = src.clone();

Mat mask = Mat::zeros(radius*2,radius*2,CV_8U);
circle(mask,Point(radius,radius),radius,Scalar(255),-1);
	
erode(srcclone,srcclone,mask);
dilate(srcclone,srcclone,mask);

效果图如下

           

             —————取二值图并取出面积最大区域——————

这两个步骤在网上有很多的程序教程,在此我就不一一详述

int max = 0;
int maxcontours = -1;
	
std::vector<std::vector<Point> > contours;

findContours(src,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);

for(int i=0;i < contours.size();i++)
{
	int tmp = contourArea(contours[i]);

	if(maxcontours < tmp)
	{
		max = i;
		maxcontours = tmp;
	}
}

效果图如下

           

                        ——————画出目标——————

最后,用opencv自带的画图函数drawContours()把目标在原图中画出

效果图如下

             

猜你喜欢

转载自blog.csdn.net/qq_41884002/article/details/83186946