原理部分来自阮一峰博客
http://www.ruanyifeng.com/blog/2013/03/similar_image_search_part_ii.html
颜色分布法:
每张图片都可以生成颜色分布的直方图(color histogram)。如果两张图片的直方图很接近,就可以认为它们很相似。
任何一种颜色都是由红绿蓝三原色(RGB)构成的,所以上图共有4张直方图(三原色直方图 + 最后合成的直方图)。
如果每种原色都可以取256个值,那么整个颜色空间共有1600万种颜色(256的三次方)。针对这1600万种颜色比较直方图,计算量实在太大了,因此需要采用简化方法。可以将0~255分成四个区:0~63为第0区,64~127为第1区,128~191为第2区,192~255为第3区。这意味着红绿蓝分别有4个区,总共可以构成64种组合(4的3次方)。
任何一种颜色必然属于这64种组合中的一种,这样就可以统计每一种组合包含的像素数量。
上图是某张图片的颜色分布表,将表中最后一栏提取出来,组成一个64维向量(7414, 230, 0, 0, 8, ..., 109, 0, 0, 3415, 53929)。这个向量就是这张图片的特征值或者叫"指纹"。
于是,寻找相似图片就变成了找出与其最相似的向量。这可以用皮尔逊相关系数或者余弦相似度算出。
实现代码如下:
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace cv;
void getRGBvector(const Mat&src, vector<unsigned int>& count)//得到64维向量
{
int nRows = src.rows,nCols = src.cols * src.channels();
const uchar* p;
for (int i = 0; i < nRows; ++i)
{
p = src.ptr<uchar>(i);
for (int j = 0; j < nCols; j += 3)
{
int r = int(p[j])/64;
int g = int(p[j + 1])/64;
int b = int(p[j + 2])/64;
count[r * 16 + g * 4 + b]++;
}
}
}
double getVectorLength(vector<unsigned int> &vec)
{
long long res = 0;
for (int i = 0; i < vec.size(); i++)
res += vec[i] * vec[i];
return sqrt(res);
}
double getcos(vector<unsigned int> &count1, vector<unsigned int> &count2)
{
double len1 = getVectorLength(count1);
double len2 = getVectorLength(count2);
assert(len1 != 0 && len2 != 0);
long long sum = 0;
for (int i = 0; i < count1.size(); i++)
sum += count1[i] * count2[i];
return (double)sum / len1 / len2 >0 ? (double)sum / len1 / len2:0;
}
double getsimilarity(const Mat&src1, const Mat&src2)
{
vector<unsigned int> count1(64), count2(64);
getRGBvector(src1, count1);
getRGBvector(src2, count2);
double res = getcos(count1, count2);
return res;
}
int main()
{
Mat src1 = imread("晚霞1.jpg",1),src2 = imread("晚霞2.jpg", 1);
imshow("src", src1),imshow("src", src2);
double res = getsimilarity(src1,src2);
cout << res << endl;
waitKey(0);
}