OpenCV均值平移查找目标,直方图反向投影的结果是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。如果我们已经知道图像中某个物体的大致位置,就可以用概率分布图找到物体的准确位置。窗口中概率最大的位置就是物体最可能出现的位置。因此,我们可以从一个初始位置开始,在周围反复移动以提高局部匹配概率,也许就能找到物体的准确位置。这个实现方法称为均值平移算法。
如何实现
假设我们已经识别出一个感兴趣的物体(例如狒狒的脸),如下图所示:
这次采用HSV色彩空间的色调通道来描述物体。这意味着需要把图像转换成HSV色彩空间并提取色调通道,然后计算指定ROI的一维色调直方图。参见以下代码:
// 读取参考图像
cv::Mat image= cv::imread("baboon01.jpg");
// 狒狒脸部的ROI
cv::Rect rect(110, 45, 35, 45);
cv::Mat imageROI= image(rect);
// 得到狒狒脸部的直方图
int minSat=65;
ColorHistogram hc;
cv::Mat colorhist= hc.getHueHistogram(imageROI, minSat);
我们在ColorHistogram类中增加了一个简便的方法来获得色调直方图,代码如下所示:
// 计算一维色调直方图
// BGR的原图转换成HSV
// 忽略低饱和度的像素
cv::Mat getHueHistogram(const cv::Mat &image, int minSaturation=0) {
cv::Mat hist;
// 转换成HSV色彩空间
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
// 掩码(可能用到,也可能用不到)
cv::Mat mask;
// 根据需要创建掩码
if (minSaturation>0) {
// 将3 个通道分割进3 幅图像
std::vector<cv::Mat> v;
cv::split(hsv, v);
// 屏蔽低饱和度的像素
cv::threshold(v[1], mask, minSaturation,
255, cv::THRESH_BINARY);
}
// 准备一维色调直方图的参数
hranges[0]= 0.0; // 范围为0~180
hranges[1]= 180.0;
channels[0]= 0; // 色调通道
// 计算直方图
cv::calcHist(&hsv, 1, // 只有一幅图像的直方图
channels, // 用到的通道
mask, // 二值掩码
hist, // 生成的直方图
1, // 这是一维直方图
histSize, // 箱子数量
ranges // 像素值范围
);
return hist;
}
然后把得到的直方图传给ContentFinder类的实例,代码如下所示:
ContentFinder finder;
finder.setHistogram(colorhist);
现在打开第二幅图像,我们想在它上面定位狒狒的脸部。首先,需要把这幅图像转换成HSV色彩空间,然后对第一幅图像的直方图做反向投影,参见下面的代码:
image= cv::imread("baboon3.jpg");
// 转换成HSV色彩空间
cv::cvtColor(image, hsv, CV_BGR2HSV);
// 得到色调直方图的反向投影
int ch[1]={0};
finder.setThreshold(-1.0f); // 不做阈值化
cv::Mat result= finder.find(hsv,0.0f,180.0f, ch);
rect对象是一个初始矩形区域(即初始图像中狒狒脸部的位置),现在OpenCV的cv::meanShift
算法将会把它修改成狒狒脸部的新位置,代码如下所示:
// 窗口初始位置
cv::Rect rect(110,260,35,40);
// 用均值偏移法搜索物体
cv::TermCriteria criteria(
cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
10, // 最多迭代10 次
1); // 或者重心移动距离小于1 个像素
cv::meanShift(result, rect, criteria);
脸部的初始位置(红色框)和新位置(绿色框)显示如下。
酷客网相关文章:
评论前必须登录!
注册