(학습 노트 3) - 얼굴 인식
얼굴 인식 을 어떻게 하 는 지 이해 하려 면 먼저 주성 분 분석 알고리즘, 즉 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 에 포함 되 어 있 기 때문에 이 헤더 파일 을 추가 하 는 것 을 잊 지 마 세 요.위의 소 개 를 통 해 얼굴 인식 방법 에 대해 알 고 자신의 문제 에 대해 얼굴 인식 프로그램 을 만들어 야 한다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
【Codility Lesson3】FrogJmpA small frog wants to get to the other side of the road. The frog is currently located at position X and wants to get to...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.