我的OpenCV学习笔记(14):用直方图对比完成基于内容的图像检索

我们知道,直方图可以在一定程度上反应图像的一些统计信息。所以,可以考虑用直方图对比的方法,进行基于内容的图像检索。

通常我们搜索图片,都是根据图片的标签搜索的。基于内容的搜索,就是假设我们不知道标签,而是直接输入一幅图像,然后从得出一些跟这幅图像的直方图比较相似的图像。

那么我们不禁要问,如何度量两幅直方图的相似程度呢?

OpenCV的compareHist函数提供了一个参数供你选择。最简单的就是CV_COMP_INTERSECT。这个方法原理其实很简单,就是对于两幅的同一个bin,选择他们的最小值,然后把所有的bin都加起来。举个例子,如果两幅图像没有任何相同的颜色,那么这个比较的计算结果就为0;如果是两幅完全相同的图像,那么计算结果就应该是整幅图像的像素数。其他的度量方法可以参考OpenCV的帮助手册,这里就不一一细说了。

下面看看代码吧。因为需要计算彩色图像的直方图,所以先建立一个计算彩色图像直方图的类:

#if!defined COLORHISTOGRAM
#define COLORHISTOGRAM

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;

class ColorHistogram
{
private:
	int histSize[3];
	float hranges[2];
	const float* ranges[3];
	int channels[3];
public:

	//构造函数
	ColorHistogram()
	{
		histSize[0]= histSize[1]= histSize[2]= 256;
		hranges[0] = 0.0;
		hranges[1] = 255.0;
		ranges[0] = hranges;
		ranges[1] = hranges;
		ranges[2] = hranges;
		channels[0] = 0;
		channels[1] = 1;
		channels[2] = 2;
	}

	//计算彩色图像直方图
	Mat getHistogram(const Mat& image)
	{
		Mat hist;

		//BGR直方图
		hranges[0]= 0.0;    
		hranges[1]= 255.0;
		channels[0]= 0;	
		channels[1]= 1; 
		channels[2]= 2; 

		//计算
		calcHist(&image,1,channels,Mat(),hist,3,histSize,ranges);
		return hist;
	}

	//计算颜色的直方图
	Mat getHueHistogram(const Mat &image)
	{
		Mat hist;
		Mat hue;
		//转换到HSV空间
		cvtColor(image,hue,CV_BGR2HSV);

		//设置1维直方图使用的参数
		hranges[0] = 0.0;
		hranges[1] = 180.0;
		channels[0] = 0;
		//计算直方图
		calcHist(&hue,1,channels,Mat(),hist,1,histSize,ranges);
		return hist;

	}

	//减少颜色
	Mat colorReduce(const Mat &image,int div = 64)
	{
		int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
		uchar mask = 0xFF<<n;
		Mat_<Vec3b>::const_iterator it = image.begin<Vec3b>();
		Mat_<Vec3b>::const_iterator itend = image.end<Vec3b>();
		//设置输出图像
		Mat result(image.rows,image.cols,image.type());
		Mat_<Vec3b>::iterator itr = result.begin<Vec3b>();
		for(;it != itend;++it,++itr)
		{
			(*itr)[0] = ((*it)[0]&mask) + div/2;
			(*itr)[1] = ((*it)[1]&mask) + div/2;
			(*itr)[2] = ((*it)[2]&mask) + div/2;
		}
		return result;
	}

};


#endif


然后再建立一个比较直方图的类:

#if!defined IMAGECOMPARATOR
#define IMAGECOMPARATOR

#include "colorhistogram.h"
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;

class ImageComparator
{
private:
	Mat reference;
	Mat input;
	Mat refH;
	Mat inputH;
	ColorHistogram hist;
	int div;
public:
	ImageComparator():div(32){}

	void setColorReducation(int factor)
	{
		div = factor;
	}

	int getColorReduction()
	{
		return div;
	}

	void setRefrenceImage(const Mat &image)
	{
		reference = hist.colorReduce(image,div);
		refH = hist.getHistogram(reference);
	}

	double compare(const Mat &image)
	{
		input = hist.colorReduce(image,div);
		inputH = hist.getHistogram(input);
		return compareHist(refH,inputH,CV_COMP_INTERSECT);
	}
};


#endif


注意到在计算直方图前,我对图像的颜色进行了减少,这样做的主要目的是为了减少运算量。

有了这两个类,我们的主程序就变得简单多了:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "imageComparator.h"

#include <iostream>

using namespace std;

int main()
{
	Mat image = imread("D:/picture/images/waves.jpg");
	if(!image.data)
		return -1;
	imshow("待检测图像",image);
	cout<<"图像像素数为:"<<image.cols*image.rows<<endl;

	ImageComparator c;
	c.setRefrenceImage(image);

	//跟自己比
	Mat input = imread("D:/picture/images/waves.jpg");
	imshow("自己",input);
	cout<<"waves VS waves:"<<c.compare(input)<<endl;

	input = imread("D:/picture/images/dog.jpg");
	imshow("dog",input);
	cout<<"waves VS dog:"<<c.compare(input)<<endl;

	input = imread("D:/picture/images/marais.jpg");
	imshow("沼泽",input);
	cout<<"waves VS marsh:"<<c.compare(input)<<endl;

	input= cv::imread("D:/picture/images/bear.jpg");
	imshow("熊",input);
	cout<<"waves VS bear:"<<c.compare(input)<<endl;


	waitKey(0);
	return 0;
}


正如前面所说的,如果自己跟自己比较,结果就是整幅图像像素数。

最后,做一点小小的说明,这里演示的直方图比较法其实不是特别靠谱。举一个简单的例子,对于一幅图像,如果把它进行翻转,或者分割以后在重新随机组合起来(尽管结果可能没有什么意义),尽管图像发生了显著地变化,但是他的直方图是不会变的。所以又有人想出了对图像分块,然后再做直方图之类的方法。我也不是这方面的专家,如果大家真的想弄一个靠谱的做法,最好是查查论文,看看牛人们都是怎么搞的。

你可能感兴趣的:(image,iterator,input,div,float,reference)