環境
ubuntu 12.04 LTS
python 2.7.3
opencv 2.3.1-7
安裝依賴
sudo apt-get install libopencv-*
sudo apt-get install python-opencv
sudo apt-get install python-numpy
示例代碼
#!/usr/bin/env python #coding=utf-8 import os from PIL import Image, ImageDraw import cv def detect_object(image): '''檢測圖片,獲取人臉在圖片中的坐標''' grayscale = cv.CreateImage((image.width, image.height), 8, 1) cv.CvtColor(image, grayscale, cv.CV_BGR2GRAY) cascade = cv.Load("/usr/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml") rect = cv.HaarDetectObjects(grayscale, cascade, cv.CreateMemStorage(), 1.1, 2, cv.CV_HAAR_DO_CANNY_PRUNING, (20,20)) result = [] for r in rect: result.append((r[0][0], r[0][1], r[0][0]+r[0][2], r[0][1]+r[0][3])) return result def process(infile): '''在原圖上框出頭像並且截取每個頭像到單獨文件夾''' image = cv.LoadImage(infile); if image: faces = detect_object(image) im = Image.open(infile) path = os.path.abspath(infile) save_path = os.path.splitext(path)[0]+"_face" try: os.mkdir(save_path) except: pass if faces: draw = ImageDraw.Draw(im) count = 0 for f in faces: count += 1 draw.rectangle(f, outline=(255, 0, 0)) a = im.crop(f) file_name = os.path.join(save_path,str(count)+".jpg") # print file_name a.save(file_name) drow_save_path = os.path.join(save_path,"out.jpg") im.save(drow_save_path, "JPEG", quality=80) else: print "Error: cannot detect faces on %s" % infile if __name__ == "__main__": process("./opencv_in.jpg")
轉換效果
原圖:
轉換后
使用感受
對於大部分圖像來說,只要是頭像是正面的,沒有被阻擋,識別基本沒問題,准確性還是很高的。
識別效率有點低,有時候一張圖片能處理七八秒才能處理完,當然這個和機器配置有關。 如果想加速的話可以使用C語言重寫,經測試,C語言版的所花時間大約是python的一半
另外,官方提供了幾個庫可一選擇,這里使用的是haarcascade_frontalface_alt_tree.xml
, 除此之外,/usr/share/opencv/haarcascades/
文件夾下還有幾個庫:
~~/usr/share/opencv/haarcascades>> ll -h 總用量 19M drwxr-xr-x 2 root root 4.0K 3月 22 17:14 ./ drwxr-xr-x 4 root root 4.0K 3月 22 17:14 ../ -rw-r--r-- 1 root root 1.1M 4月 28 2011 haarcascade_eye_tree_eyeglasses.xml -rw-r--r-- 1 root root 495K 4月 28 2011 haarcascade_eye.xml -rw-r--r-- 1 root root 818K 4月 28 2011 haarcascade_frontalface_alt2.xml -rw-r--r-- 1 root root 3.5M 4月 28 2011 haarcascade_frontalface_alt_tree.xml -rw-r--r-- 1 root root 899K 4月 28 2011 haarcascade_frontalface_alt.xml -rw-r--r-- 1 root root 1.2M 4月 28 2011 haarcascade_frontalface_default.xml -rw-r--r-- 1 root root 622K 4月 28 2011 haarcascade_fullbody.xml -rw-r--r-- 1 root root 316K 4月 28 2011 haarcascade_lefteye_2splits.xml -rw-r--r-- 1 root root 520K 4月 28 2011 haarcascade_lowerbody.xml -rw-r--r-- 1 root root 350K 4月 28 2011 haarcascade_mcs_eyepair_big.xml -rw-r--r-- 1 root root 401K 4月 28 2011 haarcascade_mcs_eyepair_small.xml -rw-r--r-- 1 root root 306K 8月 2 2011 haarcascade_mcs_leftear.xml -rw-r--r-- 1 root root 760K 4月 28 2011 haarcascade_mcs_lefteye.xml -rw-r--r-- 1 root root 703K 4月 28 2011 haarcascade_mcs_mouth.xml -rw-r--r-- 1 root root 1.6M 4月 28 2011 haarcascade_mcs_nose.xml -rw-r--r-- 1 root root 318K 8月 2 2011 haarcascade_mcs_rightear.xml -rw-r--r-- 1 root root 1.4M 4月 28 2011 haarcascade_mcs_righteye.xml -rw-r--r-- 1 root root 1.5M 4月 28 2011 haarcascade_mcs_upperbody.xml -rw-r--r-- 1 root root 1.1M 4月 28 2011 haarcascade_profileface.xml -rw-r--r-- 1 root root 317K 4月 28 2011 haarcascade_righteye_2splits.xml -rw-r--r-- 1 root root 1022K 4月 28 2011 haarcascade_upperbody.xml ~/usr/share/opencv/haarcascades>>
根據文件名大家應該能知道是識別什么的。值得一提的是,這里面有四個關於人臉(frontalface)的識別庫, 根據我的使用體驗,default
這個xml識別的最多,這就意味着本來不是頭像的也識別成頭像了。 alt_tree
這個庫雖然是最大的,但並不意味着這個庫是最好的,應該說,用這個庫,識別是最嚴格的, 這就意味着,有些頭像不能被識別,因為根據他的算法,他認為這不是頭像。 其余兩個和alt_tree
差不多。具體識別細節大家可以打開相應的xml看一下。
上面的代碼只是識別面部,並不包括頭發,如果大家想抓一個完整的頭像的話, 可以將識別出來的矩形框的上邊緣增加一定的比例,比如增加20%頭像的高度。
附:C++語言人臉識別代碼
網上找的,親測可用,效率比python高一點。
#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> #ifdef _EiC #define WIN32 #endif 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 main( int argc, char** argv ) { CvCapture* capture = 0; IplImage *frame, *frame_copy = 0; 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 = "/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml"; //opencv裝好后haarcascade_frontalface_alt2.xml的路徑, //也可以把這個文件拷到你的工程文件夾下然后不用寫路徑名cascade_name= "haarcascade_frontalface_alt2.xml"; //或者cascade_name ="C:\\Program Files\\OpenCV\\data\\haarcascades\\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' ); else capture = cvCaptureFromAVI( input_name ); cvNamedWindow( "result", 1 ); 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; } cvReleaseImage( &frame_copy ); cvReleaseCapture( &capture ); } else { const char* filename = input_name ? input_name : (char*)"lena.jpg"; IplImage* image = cvLoadImage( filename, 1 ); if( image ) { detect_and_draw( image ); cvWaitKey(0); 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); cvReleaseImage( &image ); } } fclose(f); } } } // getchar(); cvDestroyWindow("result"); return 0; } 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 = 1.3; 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, 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; 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 ); } } cvShowImage( "result", img ); cvReleaseImage( &gray ); cvReleaseImage( &small_img ); }