上一節中我介紹了如何使用Opencv自帶的opencv_traincascade.exe來做訓練,接下來介紹如何使用訓練生成的cascade.xml模型文件來檢測車臉。
首先需要說明的是我這里的訓練數據是卡口數據是在監控下面的照片,主要為了截取卡口攝像頭拍攝下的照片的車臉部分,如下圖是待檢測圖像:
這里主要基於了opencv中自帶的檢測函數,可以對目標物檢測有一個較好的認識,讀者可以用cmake生成Opencv的源碼工程來細看具體實現細節(Cmake真是個神器)。這里用到了timer頭文件,可以計算平均的檢測時間。
一下是主要的代碼部分,程序可以輸出定位出來的坐標到txt文件中,同時也可以通過開關用imshow顯示具體的截取效果。
給出工程鏈接github工程鏈接此處有鏈接!!
我的車是數據大部分都是如下,是卡口監控的拍攝圖片

截出的車臉示意圖,也可以用綠線在原圖上標出,代碼中已經將ROI部分截出,以下是代碼部分。

// facedetection.cpp : 定義控制台應用程序的入口點。
#include<opencv/cv.h>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/opencv.hpp>
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <ctype.h>
#include<vector>
#include<fstream>
#include<sstream>
#include<iostream>
#include "timer.h"
using namespace std;
using namespace cv;
vector<string> testSamples;
cv::CascadeClassifier cascade;
const int detectSizeX = 320;//用來標明是將圖縮放到在320*240
const int detectSizeY = 240;
const int detectRecXStart = 80;//檢測窗口的起始大小
const int detectRecYStart = 80;
const int detectRecYEnd = 220;//檢測窗口的終止大小
//const int detectRecYEnd = 220;
//***************************************************************
// 名稱: find_overlap
// 功能: 計算矩形框的overlap
// 權限: public
// 返回值: float
// 參數: Rect x
// 參數: Rect y
//***************************************************************
float find_overlap(Rect x,Rect y)//判斷框出圖像的重疊面積
{
float endx=max(x.x+x.width,y.x+y.width);
float startx=min(x.x,y.x);
float endy=max(x.y+x.height,y.y+y.height);
float starty=min(x.y,y.y);
float w=x.width+y.width-(endx-startx);
float h=x.height+y.height-(endy-starty);
if (w<=0||h<=0)
return 0;
else
{
float area=w*h;
return area/(x.width*x.height);
}
}
//***************************************************************
// 名稱: readFileList
// 功能: 待檢測的圖像列表
// 權限: public
// 返回值: void
// 參數: string testImgFile txt文件包含待檢測圖像
// 參數: string basePath 基地址,用來連接到txt中的相對位置構成絕對地址
//***************************************************************
//void readFileList(string testImgFile = "D:\\DataSet\\CarFaceTestDataSet\\CarFace_ImageList.txt", string basePath="")
void readFileList(string testImgFile, string basePath)
{
string buffer;
std::ifstream fIn(testImgFile.c_str());
while (fIn)
{
if (getline(fIn,buffer))
{
testSamples.push_back(basePath + buffer);
}
}
cout<<"Load FileList Successfully"<<endl;
}
//***************************************************************
// 名稱: loadCascadeModel
// 功能: 載入cascade模型文件
// 權限: public
// 返回值: void
// 參數: string xmlPath
//***************************************************************
void loadCascadeModel(string xmlPath)
{
cascade.load(xmlPath.c_str());
if (!cascade.empty())
{
cout<<"Load Cascade Model Successfully"<<endl;
}
}
void detecctObject(string savePath)
{
TM_STATE timer;//用來計算檢測的時間
TM_COUNTER start, end;
double duration, max_duration, total_duration, average_duration;
max_duration = total_duration = 0.;
ofstream fout;
fout.open(savePath + "\\cascade_detect_result.txt");//存儲框出圖像的坐標文件名
int num=0;
for (int i = 0; i < testSamples.size(); i++)
{
start_timer (&timer, &start);//啟動計時器
IplImage * img = cvLoadImage(testSamples[i].c_str());
vector<cv::Rect> detectedRect;//存儲檢測到的圖像
IplImage * copyImg = cvCreateImage(cvSize(detectSizeX,detectSizeY), 8, 3);//圖像縮放到detectSizeX*detectSizeY,自己設置合適的數值,不能再原始圖上檢測,太慢,還會引入誤差
cvResize(img, copyImg,1 );
IplImage * grayImage = cvCreateImage(cvGetSize(copyImg), 8, 1);
cvCvtColor(copyImg, grayImage, CV_BGR2GRAY);//轉換到灰度圖檢測
detectedRect.clear();
cascade.detectMultiScale(grayImage, detectedRect, 1.1, 3, CV_HAAR_SCALE_IMAGE | CV_HAAR_DO_CANNY_PRUNING, cvSize(detectRecXStart, detectRecXStart), cvSize(detectRecYEnd, detectRecYEnd));//檢測函數
fout<<testSamples[i].c_str()<<" "<<detectedRect.size()<<" ";
cout<<testSamples[i].c_str()<<" "<<detectedRect.size()<<" ";
for (vector<cv::Rect>::iterator k= detectedRect.begin(); k != detectedRect.end(); k++)
{
Rect r = *k;
vector<cv::Rect>::iterator j= detectedRect.begin();
for (j= detectedRect.begin(); j != detectedRect.end(); j++)
{
if ( k != j && (r & *j) == r)//用來消除嵌套的,一個矩形在另一個矩形框中的情況
break;
if(find_overlap(*k, *j)>0.6 && k->height*k->width < j->height*j->width)//用來消除檢測中兩個寬重疊超過0.6的框,一般視為誤檢測
break;
}
if(j ==detectedRect.end())
{
//此處將框出的圖像放大了0.03倍 使用畫矩陣函數可以看到
//cvRectangle(img, cvPoint(k->x*img->width/detectSizeX-k->width*img->width/detectSizeX*0.03, k->y*img->width/detectSizeX-k->width*img->width/detectSizeX*0.03), cvPoint(k->x*img->width/detectSizeX + k->width*img->width/detectSizeX + k->width*img->width/detectSizeX*0.03, k->y*img->width/detectSizeX + k->height*img->width/detectSizeX +k->width*img->width/detectSizeX*0.03),Scalar(0, 255, 0));
//cvRectangle(img, cvPoint(k->x*img->width/detectSizeX, k->y*img->width/detectSizeX), cvPoint(k->x*img->width/detectSizeX + k->width*img->width/detectSizeX, k->y*img->width/detectSizeX + k->height*img->width/detectSizeX),Scalar(0, 255, 0));
fout << k->x*img->width/detectSizeX-k->width*img->width/detectSizeX*0.03 << " "<<k->y*img->height/detectSizeY-k->width*img->width/detectSizeX*0.03<<" "<<k->width*img->width/detectSizeX + k->width*img->width/detectSizeX*0.06<<" "<<k->height*img->height/detectSizeY + k->width*img->width/detectSizeX*0.06<<" ";
cout << k->x*img->width/detectSizeX-k->width*img->width/detectSizeX*0.03 << " "<<k->y*img->height/detectSizeY-k->width*img->width/detectSizeX*0.03<<" "<<k->width*img->width/detectSizeX + k->width*img->width/detectSizeX*0.06<<" "<<k->height*img->height/detectSizeY + k->width*img->width/detectSizeX*0.06<<" ";
//用來截取車臉設定ROI
cvSetImageROI(img, cvRect(k->x*img->width/detectSizeX - k->width*img->width/detectSizeX*0.03,k->y*img->height/detectSizeY - k->width*img->width/detectSizeX*0.03,k->width*img->width/detectSizeX+ k->width*img->width/detectSizeX*0.06,k->width*img->width/detectSizeX+ k->width*img->width/detectSizeX*0.06));
//cvSetImageROI(img, cvRect(k->x*img->width/detectSizeX,k->y*img->height/detectSizeY,k->width*img->width/detectSizeX,k->width*img->width/detectSizeX));
string filePartName=testSamples[i].substr(testSamples[i].find_first_of("/\\") + 1 ,testSamples[i].find(".jpg") -testSamples[i].find_last_of("/\\") - 1);
std::stringstream ss;
string numstr;
ss<<num;
ss>>numstr;
num++;
string name=savePath+filePartName+"_"+numstr+".jpg";//存儲圖像命名
cout<<name<<endl;
cvSaveImage(name.c_str(),img);
}
}
fout <<endl;
cout<<endl;
stop_timer (&end);
duration = elapsed_time (&timer, &start, &end);
if (duration > max_duration)
max_duration = duration;
total_duration += duration;
cout<<"duration"<<duration<<" max_duration"<<max_duration<<" total_duration"<<total_duration<<endl;
////顯示窗口
cvNamedWindow("output");
cvShowImage("output",copyImg);
cvShowImage("output",img);
waitKey(0);
cvReleaseImage(&img);
cvReleaseImage(&grayImage);
cvReleaseImage(©Img);
}
fout.close();
}
void main ()
{
string testImgFile = "D:\\DataSet\\CarFaceTestDataSet\\CarFace_ImageList.txt";
string basePath="";
string xmlPath = "D:\\WorkSpace\\VS_Projects\\facedetection\\cascade.xml";
string savePath = "D:\\DataSet\\CarFaceTestDataSet\\result";
readFileList(testImgFile,basePath);
loadCascadeModel(xmlPath);
detecctObject(savePath);
}
