在标定目标时,除了目标自身的原因(光照、噪声等)外,背景的复杂程度也是在选取目标时能否顺利的一个重要因素。而在与目标相似的背景下,往往会因颜色等相近而导致目标选取失败,为了解决这种情况,下面我来简单地介绍一个自己的思路~~
首先我们先来看下效果~~
原 图 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()把目标在原图中画出
效果图如下