(학습 노트 3) - 얼굴 인식

얼굴 인식 은 일부 수학 계산 과 일부 알고리즘 에 대한 이해 와 관련 되 어야 한다. 비록 이런 알고리즘 과 계산 opencv 가 우 리 를 도와 완성 되 었 지만 우 리 는 이에 대해 어느 정도 알 아야 얼굴 인식 실천 을 할 수 있다. 왜냐하면 기초 가 튼튼 하지 않 고 상부 구조 도 불안정 하기 때문이다.
얼굴 인식 을 어떻게 하 는 지 이해 하려 면 먼저 주성 분 분석 알고리즘, 즉 PCA 를 이해 해 야 한다. 이 알고리즘 을 사용 하 는 이 유 는 일반 이미지 데이터 의 양 이 너무 많 고 그 중의 대부분 데이터 점 이 우리 의 얼굴 인식 에 큰 도움 이 되 지 않 기 때문에 데 이 터 를 줄 이기 위해 주성 분 분석 법 으로 데 이 터 를 압축 (알고리즘 에서 투영 이 라 고 함) 하기 때문이다.그리고 압축 된 이미지 로 이미지 식별의 진일보 한 응용 을 한다.PCA 의 원 리 는 앞의 글 에서 이미 말 했 으 니 참고 하 시기 바 랍 니 다: PCA 원리.켄 은 이 글 을 읽 고 PCA 에 대해 어느 정도 이 해 를 가지 기 시 작 했 지만 약간 모호 하 다 고 느 꼈 다. 그 글 은 내 가 전재 만 했 을 뿐 자신의 생각 을 많이 표시 하지 않 았 기 때문에 혼 란 스 러 웠 다. 그러면 다음 글 을 계속 읽 을 수 있다.
PCA 원리 상세 설명.
앞의 PCA 원리 소개 에서 두 가지 함수, cvCalcPCA, cvProjectPCA 를 언급 한 적 이 있다.나 는 실제 사용 에서 다른 문장 을 참 고 했 는데, 다른 두 개의 함 수 를 사 용 했 는데, 이 뒤에 말 할 것 이다.이것 은 너무 고민 하지 않 아 도 됩 니 다. 앞의 글 은 학습 원리 의 참고 로 할 수 있 습 니 다. 원 리 를 알 게 된 후에 뒤에 소개 할 두 함수 도 간단 할 것 입 니 다.그 중에서 한 가지 설명 할 것 은 cvCalcPCA 는 새로운 PCA 처리 함수 이 고 그 다음 에 말 할 cvCalcEigenObjects 는 오래된 PCA 처리 함수 입 니 다.
위의 두 글 을 통 해 PCA 의 원리 에 대해 알 아야 한다 고 믿 습 니 다. 다음은 PCA 알고리즘 으로 얼굴 인식 을 어떻게 실현 하 는 지 설명 하 겠 습 니 다.이것 은 이 문장 을 참고 할 수 있다.
http://www.cognotics.com/opencv/servo_2007_series/part_5/index.html
이 글 은 영어 문장 입 니 다. 여러분 들 이 영어 문장 을 많이 읽 어 주시 기 바 랍 니 다. 왜냐하면 우 리 는 학습 과정 에서 국내 교재 가 비교적 적은 상황 을 만나면 보통 외국 의 문장 을 참고 해 야 하고 영어 문장 을 잘 읽 는 것 도 우리 에 게 더욱 넓 은 지식 창고 로 가 는 문 을 열 어 주 었 기 때 문 입 니 다.이 글 을 읽 고 나 면 반드시 스스로 얼굴 인식 프로그램 을 만 들 수 있 을 것 이다. 왜냐하면 이 글 은 이미 매우 상세 하 게 소개 되 었 기 때문이다.
하지만 여기 서 나 는 위의 그 문장의 원 리 를 소개 하 겠 다.내 다음 서술 은 주로 한 이미지 라 이브 러 리 에서 우리 가 지정 한 이미지 와 가장 가 까 운 이미 지 를 찾 는 것 이다. 사람의 얼굴 이미 지 를 예 로 들 면 데이터 베이스 에 내 가 제시 한 이 사람의 얼굴 이 포함 되 어 있 을 것 이다. 데이터 베이스 에 있 는 그림 은 내 가 제시 한 그림 과 같은 그림 이 아니 지만 반드시 같은 사람 이 어야 한다.
우선, 만약 에 제 가 코드 가 1, 2, 3 세 명의 훈련 인의 얼굴 이미지 가 있다 면 사람의 얼굴 크기 를 일치 하 게 조정 할 수 있 습 니 다. 예 를 들 어 글 에서 92 * 112 이 고 우리 의 분석 에서 하나의 픽 셀 점 을 1 차원 으로 하기 때문에 모든 이미 지 는 1 * 10304 의 행 벡터 라 고 볼 수 있 습 니 다. 그러면 세 명의 훈련 자의 얼굴 이미 지 는 3 * 10304 의 행렬 을 구성 할 수 있 습 니 다.(실제 이미지 마다 92 * 112 의 iplimage 구조 가 존재 하 는데 여 기 는 편 의 를 설명 하기 위 한 가상 일 뿐 입 니 다).이 행렬 에 따라 해당 하 는 협 방 차 행렬 을 계산 할 수 있 습 니 다. 크기 는 10304 * 10304 (이 크기 는 제 가 연구 하지 않 았 습 니 다. 저 는 이 크기 라 고 생각 합 니 다. 위 에 있 는 영문 문장 을 해석 할 수 있 기 때 문 입 니 다) 이 어 10304 * 10304 행렬 의 특징 치 와 특징 벡터 를 구 했 고 앞의 nEigens 개 를 구 할 수 있 습 니 다.(이 개 수 는 우리 스스로 확정 합 니 다. 글 에서 nEigens 를 훈련 인 개수 - 1 로 하 는 것 입 니 다) 특징 치 (앞의 특징 치 는 이미 큰 것 에서 작은 것 으로 순 서 를 정 했 기 때문에 이 몇 개 는 가장 큰 것 입 니 다) 에 대응 하 는 특징 벡터 로 10304 * nEigens 크기 투영 행렬 을 구성 합 니 다.(각 열의 규 모 는 한 장의 얼굴 이미지 의 규모 이기 때문이다)즉, 하위 공간 입 니 다. 그 다음 에 각 크기 가 1 * 10304 인 사람의 얼굴 이미지 행렬 을 이 투영 행렬 에 곱 하면 모든 이미지 가 주성 분자 공간 에 투영 되 고 크기 는 1 * nEigens 이 며 이 투영 행렬 로 후속 적 인 분석 을 할 수 있 습 니 다. 이 를 통 해 투영 후 이미지 데 이 터 량 이 크게 줄 어 들 었 음 을 알 수 있 습 니 다. 위의 계산 투영 행렬 과정 은 cvCalcEigenObjects 입 니 다.완성, 투영 은 cvEigenDecomposite 함수 로 이 루어 집 니 다. 구체 적 으로 는 영문 문장 을 볼 수 있 습 니 다. 이 두 함 수 를 이해 하기 위해 서 는 PCA 의 두 가지 주요 함수, 이 공간 에는 두 가지 함 수 를 소개 하 는 글 이 두 편 있 습 니 다.
마찬가지 로 테스트 이미 지 를 제시 할 때 먼저 테스트 얼굴 을 훈련 하 는 사람의 얼굴 과 같은 크기 인 92 * 112 로 확대 한 다음 에 앞의 특징 매트릭스 로 서브 공간 에 투영 한다. 즉, 크기 가 1 * nEigens 인 매트릭스 로 투영 한다. 이 어 이 서브 공간 투영 과 앞 면 의 훈련 이미지 가 서브 공간 에 투영 되 는 것 을 비교 할 수 있다. 가장 가 까 운 것 은 목표 이미지 와 비교 하 는 것 이다.방법 문장 에서 도 언급 되 었 는데, 유럽식 알고리즘 이 있다.
 유클리드 와 새로운... Mahalanobis 알고리즘 입 니 다. 이로써 전체 얼굴 인식 원 리 는 이미 말 이 끝 났 습 니 다. 좀 복잡 하지만 시간 이 걸 려 도 이해 하기 어렵 지 않 습 니 다. 아래 에 글 에 제 시 된 절 차 를 붙 여 제 가 주석 을 달 았 습 니 다. 사용 방법 프로그램 머리 에 도 소개 되 었 습 니 다.
// eigenface.c, by Robin Hewitt, 2007
//
// Example program showing how to implement eigenface with OpenCV

// Usage:
//
// First, you need some face images. I used the ORL face database.
// You can download it for free at
//    www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html
//
// List the training and test face images you want to use in the
// input files train.txt and test.txt. (Example input files are provided
// in the download.) To use these input files exactly as provided, unzip
// the ORL face database, and place train.txt, test.txt, and eigenface.exe
// at the root of the unzipped database.
//
// To run the learning phase of eigenface, enter
//    eigenface train
// at the command prompt. To run the recognition phase, enter
//    eigenface test

#include <stdio.h>
#include <string.h>
#include "cv.h"
#include "cvaux.h"
#include "highgui.h"


////           
IplImage ** faceImgArr        = 0; //               (            )
CvMat    *  personNumTruthMat = 0; //      ID 
int nTrainFaces               = 0; //        
int nEigens                   = 0; //            
IplImage * pAvgTrainImg       = 0; //           
IplImage ** eigenVectArr      = 0; //     ,       
CvMat * eigenValMat           = 0; //    
CvMat * projectedTrainFaceMat = 0; //        


////     
void learn();
void recognize();
void doPCA();
void storeTrainingData();
int  loadTrainingData(CvMat ** pTrainPersonNumMat);
int  findNearestNeighbor(float * projectedTestFace);
int  loadFaceImgArray(char * filename);
void printUsage();



//   ,             ,      ,            
void main( int argc, char** argv )
{
	// validate that an input was specified
	if( argc != 2 )
	{
		printUsage();
		return;
	}
	//                    
	if( !strcmp(argv[1], "train") ) learn();
	else if( !strcmp(argv[1], "test") ) recognize();
	else
	{
		printf("Unknown command: %s
", argv[1]); printUsage(); } } // void learn() { int i, offset; // nTrainFaces = loadFaceImgArray("train.txt"); if( nTrainFaces < 2 ) { fprintf(stderr, "Need 2 or more training faces
" "Input file contains only %d
", nTrainFaces); return; } // doPCA(); // projectedTrainFaceMat = cvCreateMat( nTrainFaces, nEigens, CV_32FC1 ); offset = projectedTrainFaceMat->step / sizeof(float); for(i=0; i<nTrainFaces; i++) { //int offset = i * nEigens; cvEigenDecomposite( faceImgArr[i], nEigens, eigenVectArr, 0, 0, pAvgTrainImg, //projectedTrainFaceMat->data.fl + i*nEigens); projectedTrainFaceMat->data.fl + i*offset); } // , .xml , storeTrainingData(); } // void recognize() { int i, nTestFaces = 0; // CvMat * trainPersonNumMat = 0; // float * projectedTestFace = 0; // , nTestFaces = loadFaceImgArray("test.txt"); printf("%d test faces loaded
", nTestFaces); // .xml if( !loadTrainingData( &trainPersonNumMat ) ) return; // projectedTestFace = (float *)cvAlloc( nEigens*sizeof(float) ); for(i=0; i<nTestFaces; i++) { int iNearest, nearest, truth; // cvEigenDecomposite( faceImgArr[i], nEigens, eigenVectArr, 0, 0, pAvgTrainImg, projectedTestFace); iNearest = findNearestNeighbor(projectedTestFace); truth = personNumTruthMat->data.i[i]; nearest = trainPersonNumMat->data.i[iNearest]; printf("nearest = %d, Truth = %d
", nearest, truth); } } // int loadTrainingData(CvMat ** pTrainPersonNumMat) { CvFileStorage * fileStorage; int i; fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_READ ); if( !fileStorage ) { fprintf(stderr, "Can't open facedata.xml
"); return 0; } nEigens = cvReadIntByName(fileStorage, 0, "nEigens", 0); nTrainFaces = cvReadIntByName(fileStorage, 0, "nTrainFaces", 0); *pTrainPersonNumMat = (CvMat *)cvReadByName(fileStorage, 0, "trainPersonNumMat", 0); eigenValMat = (CvMat *)cvReadByName(fileStorage, 0, "eigenValMat", 0); projectedTrainFaceMat = (CvMat *)cvReadByName(fileStorage, 0, "projectedTrainFaceMat", 0); pAvgTrainImg = (IplImage *)cvReadByName(fileStorage, 0, "avgTrainImg", 0); eigenVectArr = (IplImage **)cvAlloc(nTrainFaces*sizeof(IplImage *)); for(i=0; i<nEigens; i++) { char varname[200]; sprintf( varname, "eigenVect_%d", i ); eigenVectArr[i] = (IplImage *)cvReadByName(fileStorage, 0, varname, 0); } cvReleaseFileStorage( &fileStorage ); return 1; } // void storeTrainingData() { CvFileStorage * fileStorage; int i; fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_WRITE ); // , , cvWriteInt( fileStorage, "nEigens", nEigens ); cvWriteInt( fileStorage, "nTrainFaces", nTrainFaces ); cvWrite(fileStorage, "trainPersonNumMat", personNumTruthMat, cvAttrList(0,0)); cvWrite(fileStorage, "eigenValMat", eigenValMat, cvAttrList(0,0)); cvWrite(fileStorage, "projectedTrainFaceMat", projectedTrainFaceMat, cvAttrList(0,0)); cvWrite(fileStorage, "avgTrainImg", pAvgTrainImg, cvAttrList(0,0)); for(i=0; i<nEigens; i++) { char varname[200]; sprintf( varname, "eigenVect_%d", i ); cvWrite(fileStorage, varname, eigenVectArr[i], cvAttrList(0,0)); } cvReleaseFileStorage( &fileStorage ); } // int findNearestNeighbor(float * projectedTestFace) { double leastDistSq = DBL_MAX; // , int i, iTrain, iNearest = 0; for(iTrain=0; iTrain<nTrainFaces; iTrain++) { double distSq=0; for(i=0; i<nEigens; i++) { float d_i = projectedTestFace[i] - projectedTrainFaceMat->data.fl[iTrain*nEigens + i]; distSq += d_i*d_i / eigenValMat->data.fl[i]; // Mahalanobis // distSq += d_i*d_i; // Euclidean } if(distSq < leastDistSq) { leastDistSq = distSq; iNearest = iTrain; } } return iNearest; } // void doPCA() { int i; CvTermCriteria calcLimit; CvSize faceImgSize; // nEigens = nTrainFaces-1; // faceImgSize.width = faceImgArr[0]->width; faceImgSize.height = faceImgArr[0]->height; eigenVectArr = (IplImage**)cvAlloc(sizeof(IplImage*) * nEigens); // for(i=0; i<nEigens; i++) eigenVectArr[i] = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1); // eigenValMat = cvCreateMat( 1, nEigens, CV_32FC1 ); // pAvgTrainImg = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1); // PCA calcLimit = cvTermCriteria( CV_TERMCRIT_ITER, nEigens, 1); // , , cvCalcEigenObjects( nTrainFaces, (void*)faceImgArr, (void*)eigenVectArr, CV_EIGOBJ_NO_CALLBACK, 0, 0, &calcLimit, pAvgTrainImg, eigenValMat->data.fl); cvNormalize(eigenValMat, eigenValMat, 1, 0, CV_L1, 0); } // txt int loadFaceImgArray(char * filename) { FILE * imgListFile = 0; char imgFilename[512]; int iFace, nFaces=0; if( !(imgListFile = fopen(filename, "r")) ) { fprintf(stderr, "Can\'t open file %s
", filename); return 0; } // while( fgets(imgFilename, 512, imgListFile) ) ++nFaces; rewind(imgListFile); // ID faceImgArr = (IplImage **)cvAlloc( nFaces*sizeof(IplImage *) ); personNumTruthMat = cvCreateMat( 1, nFaces, CV_32SC1 ); for(iFace=0; iFace<nFaces; iFace++) { // fscanf(imgListFile, "%d %s", personNumTruthMat->data.i+iFace, imgFilename); // faceImgArr[iFace] = cvLoadImage(imgFilename, CV_LOAD_IMAGE_GRAYSCALE); if( !faceImgArr[iFace] ) { fprintf(stderr, "Can\'t load image from %s
", imgFilename); return 0; } } fclose(imgListFile); return nFaces; } // void printUsage() { printf("Usage: eigenface <command>
", " Valid commands are
" " train
" " test
"); }
두 개의 주요 함수 cvCalcEigenObjects, cvEigenDecomposite 의 원형 설명 이 cvaux. h 에 포함 되 어 있 기 때문에 이 헤더 파일 을 추가 하 는 것 을 잊 지 마 세 요.
위의 소 개 를 통 해 얼굴 인식 방법 에 대해 알 고 자신의 문제 에 대해 얼굴 인식 프로그램 을 만들어 야 한다.

좋은 웹페이지 즐겨찾기