1、均值平移算法查找目标
直方图反向投影的结果是一个概率分布图,表示一个指定图像片段出现在特定位置的频率。假设我们已经知道了图像中某个物体的大致位置,就可以用概率分布图找到物体的准确位置。可以从一个初始位置开始,在周围反复移动,就可能找到物体所在的准确位置。这叫均值平移算法。
均值偏移算法是一个迭代的过程,用于定位概率函数的局部最大值。定位的方法是寻找预定义窗口内部数据点的重心或加权平均值。然后把窗口移动到重心的位置,并重复该过程,知道窗口中心收敛到一个稳定的点。OpenCV实现该算法时定义了两个停止条件:(1)迭代次数达到最大值;(2)窗口中心的偏移值小于某个限至,可以认为该位置收敛到了一个稳点。这两个条件存储在cv::TerCriteria实例中,cv::meanShift函数返回已经执行的迭代次数。
// initial window position
cv::Rect rect(110,260,35,40);
cv::rectangle(image, rect, cv::Scalar(0,0,255));
// search objet with mean shift
cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;
2、比较直方图搜索相似图像
仅仅比较两幅图的直方图测出两个图的相似度。在OpenCV中cv::compareHist函数可以实现,它输入两个图像的直方图,返回他们之间的差距,如果使用交叉点的方法(CV_COMP_INTERSECT),该方法只是逐个箱子地比较每个直方图中的数值,并保存最小值,然后把这些最小值相加,作为相似度的测量值。如果两个没有相同颜色的直方图得到的直方图交叉值为0,。
定义以下类:
class ImageComparator {
private:
cv::Mat refH; // reference histogram
cv::Mat inputH; // histogram of input image
ColorHistogram hist;
int nBins; // number of bins used in each color channel
public:
ImageComparator() :nBins(8) {
}
// Set number of bins used when comparing the histograms
void setNumberOfBins( int bins) {
nBins= bins;
}
int getNumberOfBins() {
return nBins;
}
// compute histogram of reference image
void setReferenceImage(const cv::Mat& image) {
hist.setSize(nBins);
refH= hist.getHistogram(image);
}
// compare the image using their BGR histograms
double compare(const cv::Mat& image) {
inputH= hist.getHistogram(image);
return cv::compareHist(refH,inputH,CV_COMP_INTERSECT);
}
};
测试代码:
#include <iostream>
using namespace std;
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include "imageComparator.h"
int main()
{
// Read reference image
cv::Mat image= cv::imread("monkey.jpg");
if (!image.data)
return 0;
// Display image
cv::namedWindow("Query Image");
cv::imshow("Query Image",image);
ImageComparator c;
c.setReferenceImage(image);
// Read an image and compare it with reference
cv::Mat input= cv::imread("dog.jpg");
cout << "monkey vs dog: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("pig.jpg");
cout << "monkey vs pig: " << c.compare(input) << endl;
return 0;
}
3、用积分图像统计像素
积分图像可以提高统计图像子区域像素的效率。积分图像可用来快速计算矩形区域内的像素累加和。
积分图像:取图像左上侧的全部像素计算累加和,并用这个累加和替换图像中的每一个像素,得到的图像就是积分图像。
计算积分图像代码:
//计算积分图像
cv::Mat intergralImage;
cv::intergral(image,intergralImage,CV_32S);
//用三个加/减运算得到一个区域的累加值
...
int sumInt=intergralImage.at<int>(y0+height,x0+width)
-intergralImage.at<int>(y0,x0+width)
-intergralImage.at<int>(y0+height,x0)
+intergralImage.at<int>(y0,x0);
...
积分图像的计算一旦完成,只需要添加四个像素点就能得到兴趣区域的累加和,与区域的尺寸大小无关。
4、自适应阈值化
自适应阈值化根据每个像素的领域计算阈值,包括将每个像素的值与领域的平均值进行比较,如果某像素的值与它的局部平均值差别很大,就会被当作异常值在阈值化过程中被剔除。
一次自适应阈值化需要计算每个像素周围的局部平均值,使用积分图像可以提高效率。
// compute integral image
cv::Mat iimage;
cv::integral(image,iimage,CV_32S);
// for each row
int halfSize= blockSize/2;
for (int j=halfSize; j<nl-halfSize-1; j++) {
// get the address of row j
uchar* data= binary.ptr<uchar>(j);
int* idata1= iimage.ptr<int>(j-halfSize);
int* idata2= iimage.ptr<int>(j+halfSize+1);
// for pixel of a line
for (int i=halfSize; i<nc-halfSize-1; i++) {
// compute sum
int sum= (idata2[i+halfSize+1]-idata2[i-halfSize]-
idata1[i+halfSize+1]+idata1[i-halfSize])/(blockSize*blockSize);
// apply adaptive threshold
if (data[i]<(sum-threshold))
data[i]= 0;
else
data[i]=255;
}
}