为了做人脸匹配,先尝试了直方图匹配,但是效果不甚如意。主要方法为检测出人脸后,计算直方图,然后用不同的方法计算直方图的差距。根据计算的结果(double的数值),判断是否为同一张人脸。代码如下:
int HistogramBins = 256;
float hrangers_arr[]={0,255};
float *phranges = hrangers_arr;
CvHistogram *HistImg1 = cvCreateHist(1, &HistogramBins , CV_HIST_ARRAY, &phranges , 1); //创建一个空的直方图
ICvHistogram * HistImg2 =cvCreateHist(1, &hdims , CV_HIST_ARRAY, &phranges , 1);
cvCalcHist(inPtr1, HistImg1, 0, 0); //计算inPtr1指向图像的数据,并传入HistImg1中
cvCalcHist(inPtr2, HistImg2, 0, 0;
CvInvoke.cvNormalizeHist(HistImg1, 1d); //直方图对比方式
CvInvoke.cvNormalizeHist(HistImg2, 1d);
double compareResult;
compareResult =cvCompareHist(HistImg1, HistImg2, CV_COMP_BHATTACHARYYA);
这样比较下来,同一张人脸和不不同人脸差距不大。(分别使用cvCompareHist的四种方法)
cvCompareHist(),是比较两个统计直方图的分布,总共有四个方法,被定义如下:
#define CV_COMP_CORREL 0
#define CV_COMP_CHISQR 1
#define CV_COMP_INTERSECT 2
#define CV_COMP_BHATTACHARYYA 3
另外,这里需要提一下的是直方图的创建,Bins为每个直方图中的块数,如灰度图,灰度值为0~255,如果Bins为256,则代表一共有256块,即每一个灰度值对应一个统计值,此时rangge为0~255;如果Bins为32,则代表一共有256块,即每8个灰度值对应一个统计值,此时rangge为0~31。
以上内容做实验效果不佳。今天看openCV,可以使用EMD(陆地移动距离)来比较,这样可以滤除一些光线引起的颜色漂移。cvCalcEMD2求结果。该方法没有实验,不知效果。
用直方图处理不如意,采用了SURF特征提取和匹配的方法。这里必须使用OpenCV2.31之后的库。
这里把特征匹配的实现思路记录如下:
1. 计算出descriptor:
其中的detector负责提取图片中的特征点,并由extractor提取出descriptor,最终保存到descImg中。
#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
//-- Step 1: Detect the keypoints using SURF Detector
int minHessian = 400;
SurfFeatureDetector detector( minHessian );
std::vector<KeyPoint> keypoints_1, keypoints_2;
detector.detect( img_1, keypoints_1 );
detector.detect( img_2, keypoints_2 );
//-- Step 2: Calculate descriptors (feature vectors)
SurfDescriptorExtractor extractor;
Mat descriptors_1, descriptors_2;
extractor.compute( img_1, keypoints_1, descriptors_1 );
extractor.compute( img_2, keypoints_2, descriptors_2 );
2. 两张图片的descriptor进行对比
首先会使用BruteForceMatcher找出两个descriptor到对方的配对点,然后使用ratioTest和SymmetryTest来剔除不靠谱的匹配特征点,也就是下面所要说的剔除匹配点的操作。
//-- Step 3: Matching descriptor vectors with a brute force matcher
BruteForceMatcher< L2<float> > matcher;
std::vector<vector< DMatch >> matches1;
std::vector<vector< DMatch >> matches2;
std::vector< DMatch> symMatches;
matcher.knnmatch( descriptors_1, descriptors_2, matches1, 2 );
matcher.knnmatch( descriptors_2, descriptors_1, matches2, 2 );
ratioTest(matches1);
ratioTest(matches2);
symmetryTest(matches1, matches2, symMatches);
3. 剔除低质量匹配点
ratioTest用来剔除距离比例相差过大的配对点,配对点之间的距离相差越大,能匹配上的概率也就越小。这里使用一个参数ratio来控制剔除距离相差在一定范围之外的特征点。
int ratioTest( std::vector<vector< DMatch >>&matches )
{
float ratio = 0.8f;
int removed=0;
// for all matches
for (std::vector<std::vector>::iterator matchIterator= matches.begin();
matchIterator!= matches.end(); ++matchIterator)
{
// if 2 NN has been identified
if (matchIterator->size() > 1)
{
// check distance ratio
if ((*matchIterator)[0].distance/(*matchIterator)[1].distance > ratio)
{
matchIterator->clear(); // remove match
removed++;
}
}
else
{
// does not have 2 neighbours
matchIterator->clear(); // remove match
removed++;
}
}
return removed;
}
symmetryTest用来判断两个图像间的特征点匹配是否是一一映射,对于不是的点则剔除掉。
void symmetryTest( const std::vector<vector< DMatch >> matches1,
const std::vector<vector< DMatch >>matches2, vector< DMatch >& symMatches )
{
// for all matches image 1 -> image 2
for (std::vector<std::vector>::const_iterator matchIterator1= matches1.begin();
matchIterator1!= matches1.end(); ++matchIterator1)
{
// ignore deleted matches
if (matchIterator1->size() < 2)
continue;
// for all matches image 2 -> image 1
for (std::vector<std::vector>::const_iterator matchIterator2= matches2.begin();
matchIterator2!= matches2.end(); ++matchIterator2)
{
// ignore deleted matches
if (matchIterator2->size() < 2)
continue;
// Match symmetry test
if ((*matchIterator1)[0].queryIdx == (*matchIterator2)[0].trainIdx &&
(*matchIterator2)[0].queryIdx == (*matchIterator1)[0].trainIdx)
{
// add symmetrical match
symMatches.push_back( cv::DMatch((*matchIterator1)[0].queryIdx,
(*matchIterator1)[0].trainIdx,
(*matchIterator1)[0].distance));
break; // next match in image 1 -> image 2
}
}
}
}
4.根据距离进一步过滤
std::vector< DMatch> UltiMatches;
for( int i = 0; i < symMatches .size(); i++)
{
Point2f MatchPt1 = keypoints_2[symMatches [i].trainIdx].pt;
Point2f MatchPt2 = keypoints_1[symMatches [i].queryIdx].pt;
double Distance = sqrt((MatchPt1 .x - MatchPt2 ].x)*(MatchPt1.x - MatchPt2.x)+(MatchPt1.y - MatchPt2.y)*(MatchPt1.y -MatchPt2.y));
if(Distance < 100)
{
UltiMatches.push_backsymMatches [i]);
}
//-- Draw only "good" matches
Mat img_matches;
drawMatches( img_1, keypoints_1, img_2, keypoints_2, ultiMatches, img_matches);
//-- Show detected matches
imshow( "Good Matches", img_matches );
这部分参见:http://www.cnblogs.com/meinvlv/archive/2012/10/25/2738068.html
5. 判断是否匹配上
经过上面的特征匹配和剔除,剩下来的应该都是比较靠谱的匹配点了。如果用一个图片来和一个模板集合来进行比对,那么通过简单的比较能够匹配到的特征点数目就能够判断出是否匹配上了。
PS:对于OpenCV中的所有特征匹配算法都可以用这个办法来做,比如SIFT, SURF等等。只需要简单的替换第一步中的extractor和detector就可以了