/*
特别说明:
1.笔者使用的opencv 2.4.3,其他版本未测试
2.本程序涉及使用人脸图库,路径是c:\face\s[x]\[y].jpg 其中x是从1开始,最大值由文中的limit设定,同理于y
3.其中程序有几个按键操作,纯属娱乐,其中s是保存人脸,q是切换保存位置(人脸库/程序牡蛎),a是初始化人脸库路径,相当于每按一次切一个人
4.默认阈值为5W
5.有很多东西没有标注,比如tellwho 是一个输入ID,返回人名的函数,只是一个DEMO,没多写
6.视频标注人脸只标注我自己~哈哈
*/
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <ctype.h>
//begin 识别用
#include "cxcore.h"
#include <iostream>
//end 识别用
#ifdef _EiC
#define WIN32
#endif
//begin识别用
#define CV_COMP_CORREL 0
#define CV_COMP_CHISQR 1
#define CV_COMP_INTERSECT 2
#define CV_COMP_BHATTACHARYYA 3
int HistogramBins = 256;
float HistogramRange1[2]={0,255};
float *HistogramRange[1]={&HistogramRange1[0]};
//end识别用
static CvMemStorage* storage = 0;
static CvHaarClassifierCascade* cascade = 0;
void detect_and_draw( IplImage* image );
const char* cascade_name =
"haarcascade_frontalface_alt.xml";
/* "haarcascade_profileface.xml";*/
int isOwen=0,isSave=0;
char who[20][20]={"1\\","2\\","3\\","4\\","5\\","6\\"}; //c:\\face\\s1\\//
char s[11][3]={"1","2","3","4","5","6","7","8","9","10"}; //c:\\face\\s1\\1
int main( int argc, char** argv )
{
CvCapture* capture = 0;
IplImage *frame, *frame_copy = 0;
//IplImage* image;//87
int optlen = strlen("--cascade=");
const char* input_name;
if( argc > 1 && strncmp( argv[1], "--cascade=", optlen ) == 0 )
{
cascade_name = argv[1] + optlen;
input_name = argc > 2 ? argv[2] : 0;
}
else
{
cascade_name = "C:\\OpenCV\\data\\haarcascades\\haarcascade_frontalface_alt2.xml";
//opencv装好后haarcascade_frontalface_alt2.xml的路径,
//也可以把这个文件拷到你的工程文件夹下然后不用写路径名cascade_name= "haarcascade_frontalface_alt2.xml";
input_name = argc > 1 ? argv[1] : 0;
}
cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );//
if( !cascade )
{
fprintf( stderr, "ERROR: Could not load classifier cascade\n" );
fprintf( stderr,
"Usage: facedetect --cascade=\"<cascade_path>\" [filename|camera_index]\n" );
return -1;
}
storage = cvCreateMemStorage(0);
if( !input_name || (isdigit(input_name[0]) && input_name[1] == '\0') )
capture = cvCaptureFromCAM( !input_name ? 0 : input_name[0] - '0' ); //cvcam类,用于
else
{
//image=cvLoadImage( input_name,1 );//87
//if(!image)//image=0//87
capture = cvCaptureFromAVI( input_name );
}
//获取捕捉源
cvNamedWindow( "result", 1 );//0可以改大小//1默认大小//4支持OPENGL
if( capture )
{
for(;;) //死循环
{
if( !cvGrabFrame( capture ))break;
frame = cvRetrieveFrame( capture );
if( !frame )break;
if( !frame_copy )
frame_copy = cvCreateImage( cvSize(frame->width,frame->height),
IPL_DEPTH_8U, frame->nChannels );
if( frame->origin == IPL_ORIGIN_TL )cvCopy( frame, frame_copy, 0 );
else cvFlip( frame, frame_copy, 0 );
detect_and_draw( frame_copy );
if( cvWaitKey( 10 ) >= 0 );//是否理解为缓冲?删除后显示停滞,显示过快?//break;
}
//cvWaitKey(0); //for exit?//87
cvReleaseImage( &frame_copy );
cvReleaseCapture( &capture );
}
else
{
const char* filename = input_name ? input_name : (char*)"lena.jpg"; //needless in 87
IplImage* image = cvLoadImage( filename, 1 ); //needless in 87
if( image )
{
detect_and_draw( image );
// cvWaitKey(0);//88
cvReleaseImage( &image );
}
else
{
/* assume it is a text file containing the
list of the image filenames to be processed - one per line */
FILE* f = fopen( filename, "rt" );
if( f )
{
char buf[1000+1];
while( fgets( buf, 1000, f ) )
{
int len = (int)strlen(buf);
while( len > 0 && isspace(buf[len-1]) )
len--;
buf[len] = '\0';
image = cvLoadImage( buf, 1 );
if( image )
{
detect_and_draw( image );
//cvWaitKey(0); //88
cvReleaseImage( &image );
}
}
fclose(f);
}
}
}
cvDestroyWindow("result");
return 0;
}
void tellwho(int id)
{
char *ans="";
switch(id)
{
case 0:{ans="陆zc(LIGHT)";break;}
case 1:{ans="陆zc(DARK)";break;}
case 2:{ans="白人男3";break;}
case 3:{ans="白人男4";break;}
case 4:{ans="白人男5";break;}
case 5:{ans="白人女6";break;}
case 6:{ans="亚洲女1";break;}
default:ans="ioi";
}
printf("%s",ans);
}
void detect_and_draw( IplImage* img )
{
static CvScalar colors[] =
{
{{0,0,255}},
{{0,128,255}},
{{0,255,255}},
{{0,255,0}},
{{255,128,0}},
{{255,255,0}},
{{255,0,0}},
{{255,0,255}}
};
double scale = 5.0;//图像大小-直接影响速率
IplImage* gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
IplImage* small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
cvRound (img->height/scale)),
8, 1 );
int i;
cvCvtColor( img, gray, CV_BGR2GRAY );
cvResize( gray, small_img, CV_INTER_LINEAR );
cvEqualizeHist( small_img, small_img );
cvClearMemStorage( storage );
if( cascade ) //找到人脸的返回值?
{
double t = (double)cvGetTickCount(); //计时开始
CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage, //检测人脸方法cvHaarDetectObjects
1.1, 2, 0/*可换成:CV_HAAR_DO_CANNY_PRUNING*/, //更换的效果是?
cvSize(30, 30) );
t = (double)cvGetTickCount() - t; //计时结束
//printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
for( i = 0; i < (faces ? faces->total : 0); i++ )//遍历每张人脸
{
CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
CvPoint center,Lp,Rp;
int radius;
/*//以圆圈标出人脸
center.x = cvRound((r->x + r->width*0.5)*scale);
center.y = cvRound((r->y + r->height*0.5)*scale);
radius = cvRound((r->width + r->height)*0.25*scale);
cvCircle( img, center, radius, colors[i%8], 3, 8, 0 );
*/
//以矩形标出人脸
Lp.x=cvRound((r->x)*scale);//87
Lp.y=cvRound((r->y)*scale);
Rp.x=cvRound((r->x + r->width)*scale);
Rp.y=cvRound((r->y + r->width)*scale);
CvRect Region;
Region.x=r->x*scale;
Region.y=r->y*scale;
Region.height=r->height*scale;
Region.width=r->width*scale;
Region=cvRect(Region.x,Region.y,Region.width,Region.height);//87
cvRectangle( img, Lp,Rp, colors[i%8], 3, 8, 0 );//在视频框中画方框
/**///以下程序为提取&保存人脸图片(未做其他处理)
//begin
static int nFileNameCount=0,nFolder=0;
static char Path2Save[20]="c:\\face\\s",oPath[20]="c:\\face\\s",tPath[20]="c:\\face\\s";
int key;
key=cvWaitKey(5);
if(key=='s')
{
printf("Save\n");
cvSetImageROI(img, Region);
CvRect Temp_region= cvGetImageROI(img);
IplImage* Image1=cvCreateImage(cvSize(Temp_region.width,Temp_region.height),img->depth,3);
cvCopy(img,Image1,0);
IplImage* OutImage=cvCreateImage(cvSize(96,96),8,3);//(cvSize(96,96),8,3);////保存的图片大小250*250 depth=8 channel=3
cvResize(Image1,OutImage);
char strCount[10]="";
nFileNameCount++;//存N张
//nFileNameCount=1;//存1张
itoa(nFileNameCount,strCount,10);
IplImage* image= cvCreateImage( cvGetSize(OutImage), 8, 1 ); //创建灰度图
cvCvtColor(OutImage, image, CV_BGR2GRAY); //色彩空间转换//OpenCV1函数
//cvtColor(OutImage,image,CV_BGR2GRAY);//opencv2
//cvZero( image );
//cvSaveImage(strcat(strCount,".pgm"),image);//pgm格式 //image=灰度图//OutImage=彩图
if(isSave==0)
{
cvSaveImage(strcat(strCount,".jpg"),image);//jpg格式
printf("Saved %d.jpg!\n",nFileNameCount);
}
else {
strcat(Path2Save,strCount);////"c:\\face\\s2\\1"
cvSaveImage(strcat(Path2Save,".jpg"),image);//
printf("Save %s\n",Path2Save);
strcpy(Path2Save,tPath);
}
//
cvResetImageROI(img);
cvReleaseImage(&Image1);
cvReleaseImage(&OutImage);
}//if(key=='s')//end
else if(key=='a')//isSave=1
{
if(isSave==1){
strcpy(Path2Save,oPath);
isSave=1;
nFileNameCount=0;
nFolder+=1;//1-> 2 3 4 5...
strcat(Path2Save,who[nFolder]);//"c:\\face\\s2\\"
strcpy(tPath,Path2Save);
printf("存入人脸库路径%s\n",Path2Save);
}
else printf("先按Q\n");
}
/**/
else if(key=='q'){
if(isSave==1){isSave=0;printf("普通截图\n");}
else {isSave=1;printf("从s2开始存储");nFolder=0;printf(",按A设置文件夹\n");}
}
//else if(key=='f')
{
//printf("识别中\n");
//目标图片
cvSetImageROI(img, Region);
CvRect Temp_region= cvGetImageROI(img);
IplImage* Image0=cvCreateImage(cvSize(Temp_region.width,Temp_region.height),img->depth,3);
cvCopy(img,Image0,0);
IplImage* OutImage=cvCreateImage(cvSize(96,96),8,3);//(cvSize(96,96),8,3);////保存的图片大小250*250 depth=8 channel=3
cvResize(Image0,OutImage);
IplImage* Image1= cvCreateImage( cvGetSize(OutImage), 8, 1 ); //创建灰度图
cvCvtColor(OutImage, Image1, CV_BGR2GRAY); //色彩空间转换//OpenCV1函数
CvHistogram *Histogram1=cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);
cvCalcHist(&Image1,Histogram1,0,0);
cvNormalizeHist(Histogram1,1);
//IplImage *Image1=cvLoadImage("C:\\ruita\\2013-8\\openCV_from_2013.8.5\\CVprj01\\CVprj01\\1.jpg",0);// //("c:\\face\\s1\\10.jpg",0); //如果没有对象,直接输出为完美匹配
char path[20]="c:\\face\\s";
char way[20]="c:\\face\\s";
double thre = 20.0;//阈值//下面的程序推荐5000以下,其实也不可能用到5000,官方给的更大1.7*10^308
double res;//对比结果
double max_ores=50000.0;
double sum_res=max_ores,avr_res=max_ores;//对比结果总和,对比结果平均值,
double min_res=max_ores;//对比结果的最小值
double ores[3]={max_ores,max_ores,max_ores};//保存最小值
//int resID[3]={999,999,999};
char res_TM[99]={123,234,456};//结果排列数组 //大小与人脸库有关
char TM[99][3]={"0","1","2","3","4","5","6","7","8","9","10"}; //组别,用于读取组别,记录最优结果
int limitpeople=5; //对比总人数
int limitpic=10; //对比时每人的头像数
//813printf("阈值=%.2f,总人数=%d,其中每人提供的头像数=%d\n",thre,limitpeople,limitpic);
for(int readTM=0;readTM<limitpeople;readTM++){//人数//
//sum_res=0; //avr_res=0;
min_res=max_ores;
for(int readID=0;readID<limitpic;readID++){//每人头像数
strcat(path,who[readTM]); //c:\\face\\s1\\ //
strcat(path,s[readID]); //c:\\face\\s1\\1
strcat(path,".jpg"); //c:\\face\\s1\\1.jpg
IplImage *Image2=cvLoadImage(path,0);
//printf("\n%s ",path); //打印路径
strcpy(path,way); //还原
CvHistogram *Histogram2=cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);
cvCalcHist(&Image2,Histogram2,0,0);
cvNormalizeHist(Histogram2,1);
double res=cvCompareHist(Histogram1,Histogram2,CV_COMP_CHISQR);
//printf("%d= %.3f\n",readTM,res);//卡方
//最小值法
if(res<min_res){min_res=res;}//如果比记录的最小值还小
else if(res>thre){min_res=max_ores;break;}//超出阈值,本组不再比较
}// for readID
//最小值法
//if(min_res<max_ores)printf("★%d min=%.2f\n",readTM,min_res);
//else printf("☆%d no match\n",readTM);
if(min_res<ores[0])//如果本次比记录的结果小,把他放进第一位
{
//改变均值队列
ores[2]=ores[1];
ores[1]=ores[0];
ores[0]=min_res;//代替第一位
//改变结果队列
res_TM[2]=res_TM[1];
res_TM[1]=res_TM[0];
res_TM[0]=readTM; //printf("0\n");
}
else if(min_res<ores[1])
{
//改变均值队列
ores[2]=ores[1];
ores[1]=min_res;
//改变结果队列
res_TM[2]=res_TM[1];
res_TM[1]=readTM; //printf("1\n");
}
else if(min_res<ores[2])
{
//改变均值队列
ores[2]=min_res;
//改变结果队列
res_TM[2]=readTM; //printf("2\n");
}
}//for readTM
//玩玩
if(ores[0]==max_ores){
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
printf("在阈值范围内没有最合适的人\n");
}
else if(ores[1]==max_ores){
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
//printf("阈值范围内,只有%d(%.2f)最像\n",res_TM[0],ores[0]);
printf("阈值范围内,只像");printf("(%.2f)",ores[0]);
tellwho(res_TM[0]);
}
else if(ores[2]==max_ores){
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
//printf("最像%d(%.2f),次像%d(%.2f)\n",res_TM[0],ores[0],res_TM[1],ores[1]);
printf("阈值范围内只有两个像:最像");
tellwho(res_TM[0]);printf("(%.2f)",ores[0]);
printf(",比较像");
tellwho(res_TM[1]);printf("(%.2f)",ores[1]);
}
else {
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
printf("最像");
tellwho(res_TM[0]);printf("(%.2f)",ores[0]);
printf(",比较像");
tellwho(res_TM[1]);printf("(%.2f)",ores[1]);
printf(",有点像");
tellwho(res_TM[2]);printf("(%.2f)",ores[2]);
//printf("最像%d(%.2f),次像%d(%.2f),或像%d(%.2f)\n",++res_TM[0],ores[0],++res_TM[1],ores[1],++res_TM[2],ores[2]);
}//else 3种情况
//printf("Finish!\n");
cvResetImageROI(img);
cvReleaseImage(&Image0);
cvReleaseImage(&OutImage);
if(res_TM[0]==1)
{//玩玩
//printf("f\n");
char text[20] = "zc_LU";
CvPoint point = cvPoint(Rp.x-250, Rp.y+50);
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_DUPLEX, 1.0f,1.0f, 1, 2);
cvPutText(img, text, point, &font,CV_RGB(255, 0, 0) );
}
}//else if(key=='f'
}
}
cvShowImage( "result", img );
cvMoveWindow("result",600,0);
cvReleaseImage( &gray );
cvReleaseImage( &small_img );
}