1.項目內容
我最近在負責項目中openCV的部分,此篇文章講述通過灰度直方圖和HSV直方圖得到圖片相似度(c++)。
在之前團隊已經實現了利用face++接口得到人臉相似度比較,但是項目需要快速比較大量人臉,得到相似度,判斷是否爲一個人,老師希望我們使用openCv本地化獲取人臉相似度。
我嘗試用openCV獲得人臉相似度比,已經實現灰度直方圖和HSV直方圖比較,設定閾值HISTCMP_CORREL >0.5 && HISTCMP_BHATTACHARYYA<0.5才能劃分爲一類。
灰度直方圖和HSV直方圖比較我個人認爲不科學,最近在考慮dlib和openCV,使用歐式距離設定閾值判斷,思路還不太清晰(我個人認爲本地化人臉相似度比較結果不太科學,奈何團隊要求),如果大家有什麼好的建議,請在下面留言,謝謝!
2.直方圖判斷標準
做項目時參考的網址
https://blog.csdn.net/liurong_cn/article/details/8752427
HSV使用色調和飽和度兩個通道,而灰色直方圖只使用了灰度值。
直方圖判斷4個標準 :
1.CV_COMP_CORREL 靠近1最好
2.Chi-Square ( CV_COMP_CHISQR 靠近0最好
3.Intersection ( CV_COMP_INTERSECT )
4.Bhattacharyya 距離( CV_COMP_BHATTACHARYYA ) 靠近0最好
我使用 Chi-Square ( CV_COMP_CHISQR 和Bhattacharyya 距離( CV_COMP_BHATTACHARYYA )聯合判斷,閾值大家可以自己試,我使用的是HISTCMP_CHISQR <0.5 &&HISTCMP_BHATTACHARYYA<0.5
3.代碼
大家配置好openCv和頭文件,請自行編譯。
我使用的openCv4.1,開發時有部分文件缺失報錯,百度缺失文件,在openCv 3.x版本上找到相應文件補全。
#include<opencv2/objdetect/objdetect.hpp>
#include<opencv2/highgui/highgui.hpp> //簡單的界面操作
#include<opencv2/imgproc/imgproc.hpp> //處理圖像的功能,如圖像過濾和幾何變換
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include<opencv2/imgproc/types_c.h>
#include<opencv2/objdetect/objdetect_c.h>
#include<opencv2/opencv.hpp>
CompareFaceHSV()得出結果爲圖片1和自己比較,自己和自己半身圖片(hsv_half_down)比較,自己和其他兩張圖片比較的4個對比標準的結果。
大家可以通過這個來尋找自己的閾值。我使用的是HISTCMP_CHISQR <0.5 && HISTCMP_BHATTACHARYYA<0.5 。
HSV直方圖的通道我沒有使用亮度,只使用了色調和飽和度。
void CompareFaceHSV(){ //原本的的OpenCvfaceCompareConfidence()用的灰色直方圖 HSV肯定準一些
Mat src_base, hsv_base;
Mat src_test1, hsv_test1;
Mat src_test2, hsv_test2;
Mat hsv_half_down; // 裝載三張背景環境不同的圖像
\\大家換成自己圖片吧
src_base = imread( "..\\faceSet\\3.jpg", 1 );
src_test1 = imread( "..\\faceSet\\5.jpg", 1 );
src_test2 = imread( "..\\faceSet\\4.jpg", 1 ); // 轉換到 HSV
cvtColor( src_base, hsv_base, CV_BGR2HSV );
cvtColor( src_test1, hsv_test1, CV_BGR2HSV );
cvtColor( src_test2, hsv_test2, CV_BGR2HSV );
hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) );
// 對hue通道使用30個bin,對saturatoin通道使用32個bin
int h_bins = 50; int s_bins = 60; int histSize[] = { h_bins, s_bins };
// hue的取值範圍從0到256, saturation取值範圍從0到180 不使用亮度,亮度影響較大
float h_ranges[] = { 0, 256 };
float s_ranges[] = { 0, 180 };
const float* ranges[] = { h_ranges, s_ranges }; // 使用第0和第1通道
int channels[] = { 0, 1 }; // 用到0 1 通道
Mat hist_base;
Mat hist_half_down;
Mat hist_test1;
Mat hist_test2; // 計算HSV圖像的直方圖 1代表輸入圖像個數
calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false );
normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false );
normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false );
normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false );
normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() ); //應用不同的直方圖對比方法
// HISTCMP_CORREL 1 HISTCMP_CHISQR 0 HISTCMP_INTERSECT HISTCMP_BHATTACHARYYA 0
for( int i = 0; i < 4; i++ ) {
int compare_method = i;
double base_base = compareHist( hist_base, hist_base, compare_method );
double base_half = compareHist( hist_base, hist_half_down, compare_method );
double base_test1 = compareHist( hist_base, hist_test1, compare_method );
double base_test2 = compareHist( hist_base, hist_test2, compare_method );
printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 );
}
printf( "Done \n" );
}
灰度直方圖
最後附上openCv灰度直方圖比較的代碼,肯定沒有HSV科學。大家自行尋找閾值。
IplImage* detect( Mat& img){
Mat gray;
cvtColor(img, gray, CV_BGR2GRAY );
equalizeHist( gray, gray );
IplImage pBinary = IplImage(gray);;
IplImage *input = cvCloneImage(&pBinary);
return input;
//cvResize(img, dst);
}
void CompareHist(IplImage *faceImage1, IplImage *faceImage2){ //只考慮了灰色直方圖
int HistogramBins = 256;
float HistogramRange1[2]={0,255};
float *HistogramRange[1]={&HistogramRange1[0]};
CvHistogram *Histogram1 = cvCreateHist(1, &HistogramBins, CV_HIST_ARRAY,HistogramRange);
CvHistogram *Histogram2 = cvCreateHist(1, &HistogramBins, CV_HIST_ARRAY,HistogramRange);
cvCalcHist(&faceImage1, Histogram1); // IplImage** image,
cvCalcHist(&faceImage2, Histogram2);
cvNormalizeHist(Histogram1, 1); //兩幅圖片大小不同也沒關係 bin都是概率
cvNormalizeHist(Histogram2, 1);
// CV_COMP_CHISQR,CV_COMP_BHATTACHARYYA這兩種都可以用來做直方圖的比較,值越小,說明圖形越相似 0最好
printf("CV_COMP_CHISQR : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_CHISQR));
printf("CV_COMP_BHATTACHARYYA : %.4f\n", cvCompareHist(Histogram1,
Histogram2, CV_COMP_BHATTACHARYYA));
cvReleaseHist(&Histogram1);
cvReleaseHist(&Histogram2);
}
void OpenCvfaceCompareConfidence(){
// face_cascade;
Mat srcImg =imread("..\\faceSet\\3.jpg"); //1 :3通道讀取
Mat targetImg=imread("..\\faceSet\\5.jpg");
qDebug() << "can read the OpenCvfaceCompareConfidence 2 images";
if (!targetImg.data || !srcImg.data){
qDebug() << "Couldn't read the image";
return ;
}
else {
IplImage* faceImage1=detect(srcImg); //Mat->IplImag*
IplImage* faceImage2=detect(targetImg);
CompareHist(faceImage1, faceImage2); //IplImag*才能傳入
// imshow("image1", Mat(faceImage1));
// imshow("image2", Mat(faceImage2));
//閾值還沒確定
cvReleaseImage(&faceImage1);
cvReleaseImage(&faceImage2);
}
}
參考網址
- 直方圖判斷4個標準 https://blog.csdn.net/liurong_cn/article/details/8752427