ColorHistogram.cpp获取直方图后,可以对直方图均衡化,通过查找表修改图像外观,在此不再详述,有兴趣可以自己尝试,直方图是图像内容的重要特性,如果某个区域中显示独特的纹理或者独特的物体,那么这个区域的直方图可以看做一个概率函数,它给出的是某个像素属于该纹理或者物体的概率。反投影直方图的结果是一个概率映射,体现了已知的图像内容出现在图像中特定位置的概率。假设我们已经知道物体的近似位置,概率映射可用于找到对象的确切位置。最有可能的位置是在已知窗口区域中得到最大概率的位置。通过从初始位置开始迭代移动,便可以找到物体的位置。这就是均值漂移算法。
假设我们想要找的位置是双目视觉中右图的花朵,我们要用HSV颜色空间的色调(HUE)来描述它。
主函数:
#include "stdafx.h"
#include "ColorHistogram.h"
#include "ObjectFinder.h"
#include <opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\video\video.hpp>
int main(void)
{
GetHistogram gh;
ObjectFinder of;
cv::Mat img1, img2;
cv::Mat imgROI;
cv::Mat histogram;
cv::Mat backProject;
img1 = cv::imread("imR.png");
if(!img1.data)
return 0;
imgROI = img1(cv::Rect(70,0,20,20));
cv::rectangle(img1,cv::Rect(70,0,20,20),cv::Scalar(0,0,255));
cv::namedWindow("img1");
cv::imshow("img1", img1);
//获得imgROI的直方图
histogram = gh.getHueHistogram(imgROI, 65);
cv::normalize(histogram,histogram,1.0);
of.setHistogram(histogram);
//获取并处理第二幅图像
cv::Mat hsv;
std::vector<cv::Mat> hue;
img2 = cv::imread("imL.png");
cv::cvtColor(img2, hsv, CV_BGR2HSV);
cv::split(hsv, hue);
cv::threshold(hue[1], hue[1], 10, 255, cv::THRESH_BINARY);//这个是为了将低饱和度的图像从反向投影结果中剔除
//对于第二幅图进行反向投影,使用第一幅图ROI的直方图
backProject = of.finder(img2);
cv::bitwise_and(backProject, hue[1], backProject);
cv::namedWindow("backProject");
cv::imshow("backProject",backProject);
//均值漂移
cv::Rect rect(70,0,20,20);
cv::rectangle(img2, rect, cv::Scalar(0,0,255));
cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,100,0.01);//最大迭代次数是10,移动距离阈值是0.01
cv::meanShift(backProject,rect,criteria);
cv::rectangle(img2, rect, cv::Scalar(0,255,0));
cv::namedWindow("img2");
cv::imshow("img2",img2);
cv::waitKey(0);
return 0;
}
ColorHistogram.cpp
#include "stdafx.h"
#include"ColorHistogram.h"
//获得色相的直方图
cv::MatND GetHistogram::getHueHistogram(const cv::Mat &image, int minSaturation)
{
cv::MatND hist;
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
std::vector<cv::Mat> hue;
cv::split(image, hue);
cv::Mat mask;
//将低饱和度的像素从掩码中去掉
if (minSaturation > 0)
cv::threshold(hue[1], mask, minSaturation, 255, cv::THRESH_BINARY);
cv::calcHist(&hue[0],
1,
HueChannels,
mask,
hist,
1,
HueHistSize,
HueBinRanges
);
return hist;
}
ObjectFinder.cpp
#include "stdafx.h"
#include"ObjectFinder.h"
cv::Mat ObjectFinder::finder(const cv::Mat &image)
{
cv::Mat backProject;
cv::Mat hsv;
std::vector<cv::Mat> hue;
cv::cvtColor(image, hsv, CV_BGR2HSV);
cv::split(hsv, hue);
cv::calcBackProject(&hue[1],
1,
HueChannels,
histogram,
backProject,
HueBinRanges,
255.0
);
return backProject;
}
ColorHistogram.h
#if !defined GETHISTOGRAM_H
#define GETHISTOGRAM_H
#include<opencv2\core\core.hpp>
#include<opencv2\imgproc\imgproc.hpp>
class GetHistogram
{
private:
//calchist中的参数
int HueChannels[1];
int HueHistSize[1] ;
float HueValueRanges[2];
const float *HueBinRanges[1] ;
public:
GetHistogram()
{
HueChannels[0] = 0;
HueHistSize[0] = 180;
HueValueRanges[0] = 0.0;
HueValueRanges[1] = 180.0;
HueBinRanges[0] = HueValueRanges;
}
//获得色相的直方图
cv::MatND getHueHistogram(const cv::Mat &image, int minSaturation);
};
#endif
ObjectFinder.h
#if !defined OBJECTFINDER_H
#define OBJECTFINDER_H
#include<opencv2\core\core.hpp>
#include<opencv2\imgproc\imgproc.hpp>
class ObjectFinder
{
private:
//calcBackProject的参数
int HueChannels[1];
cv::MatND histogram;
float HueValueRanges[2];
const float *HueBinRanges[1];
public:
ObjectFinder()
{
HueChannels[0] = 0;
HueValueRanges[0] = 0.0;
HueValueRanges[1] = 180.0;
HueBinRanges[0] = HueValueRanges;
}
//得到calcHist的直方图
void setHistogram(cv::MatND hist)
{
histogram = hist;
}
//利用直方图对图像反向投影
cv::Mat finder(const cv::Mat &image);
};
#endif
我写的过程中虽然参考了书籍和教程,但是错误很多,后悔的是花掉了一个多小时的时间,而这个跟我研究的课题没有什么太大的关系,索性的是这是一个锻炼的过程,同时也了解了一些关于目标跟踪的原理。
效果也一般,如下:
红色为原来位置,绿色为跟踪后的位置,算法属于暴力迭代搜索,阈值还需要自己不断调整,我分析效果不好的原因是因为花朵的图案本身重复的问题,这类目标跟踪算法可能适合的是比较独特或色彩区分度高的图像,有兴趣的朋友可以了解一下均值漂移,在这里就不深入探讨了,仅仅是了解直方图的应用和编程技巧。