標籤:opencv
引子
要進行人臉的識別,尤其是複雜環境下的Face Service,就要在捕獲人臉映像之後對映像進行預先處理的工作,像的大小和灰階的歸一化,頭部姿態的矯正,映像分割等。這樣做的目的是改善映像品質,消除雜訊,統一映像灰階值及尺寸,為後序特徵提取和分類識別打好基礎。
該文將就人臉的剪下和歸一化操作進行學習探討,並做一筆記備用。
具體步驟
由於opencv的Face Service需要輸入圖片具有相同的尺寸和灰階級,所以識別之前還要進行很重要的一步工作,就是針對Face Service的需要調整人臉圖片,具體可以分為以下步驟:
- 圖片去噪
- 圖片灰階化
- 人臉的剪下(crop)
- 調整圖片大小(resize)
- 姿態旋轉(rotate)
人臉剪下的策略
這裡我設想的最基本的人臉剪下方法是根據人眼的位置進行人臉的旋轉和校正的。
基本思路如下:
- 根據兩眼的傾斜角度對映像進行旋轉校正
- 根據兩眼在圖片中的實際距離和自訂的位移量進行圖片的縮放
下面是剪下的,最終的圖片是100*100像素,位移量是0.3
Detecting Face
Cropping Result
其代碼為:
cv::Mat FaceClassifer::cropFacesBasedOnEye(cv::Rect faceRect, cv::Point leftEye,cv::Point rightEye, float offset,int outputWidth,int outputHeight){ int offset_h = floor(offset * outputWidth); int offset_v = floor(offset * outputHeight); int eyegap_h = rightEye.x-leftEye.x; int eyegap_v = rightEye.y-leftEye.y; float eye_distance = sqrt(pow(eyegap_h,2)+pow(eyegap_v,2)); float eye_reference = outputWidth - 2*offset_h; float scale = eye_distance/eye_reference; //rotate original around the left eye cv::Mat rotatedImage; if(eyegap_v != 0) { double rotation = atan2f((float)eyegap_v,(float)eyegap_h); double degree = rotation*180/CV_PI; rotateFace(_image, leftEye, degree, rotatedImage); } //crop the rotated image cv::Point crop_xy(leftEye.x-scale*offset_h,leftEye.y-scale*offset_v); cv::Size crop_size(outputWidth*scale, outputHeight*scale); cv::Rect crop_area(crop_xy, crop_size); cv::Mat cropFace; if(eyegap_v == 0) cropFace = _image(crop_area); else cropFace = rotatedImage(crop_area); //resize the face cv::resize(cropFace,cropFace,cv::Size(outputWidth,outputHeight)); cv::Mat croppedGray; cv::cvtColor(cropFace,croppedGray,CV_BGR2GRAY); cv::equalizeHist(croppedGray, croppedGray); return croppedGray;}
解釋一下代碼:
- 位移量是指眼睛到圖片上邊界和左右邊界的距離比例,比如0.3的位移量,那麼左眼到左邊界,右眼到右邊界的長度占所剪下的人臉圖片寬度為0.3,同理到上邊界的距離也占圖片高度的0.3。
- 根據兩眼的實際距離和兩眼依位移量所應佔據圖片寬度的比例對圖片進行縮放,比如實際距離是求一個歐式距離eye_distance,參考的距離eye_reference是輸出的寬度outputWidth減去左眼到左邊界的0.3outputWidth,再減去右眼到右邊界的0.3 outputWidth。
- 由於最終要做Face Service,需要輸出灰階圖,所以,最後的返回值是經過灰階化並且長條圖均衡化了的圖片
圖片旋轉的API
這裡圖片旋轉用到了OpenCV的函數,warpAffine()。
這裡涉及到了仿射變換,可以參考OpenCV官方文檔,放射變換。
void FaceClassifer::rotateFace(cv::Mat& src,cv::Point& pt,double angle,cv::Mat& dst){ cv::Mat r = cv::getRotationMatrix2D(pt, angle, 1.0); cv::warpAffine(src, dst, r, cv::Size(src.cols,src.rows));}
這裡我以左眼的中心點作為旋轉的輸入原點,cv::getRotationMatrix2D()產生了表示仿射變換的2 * 3矩陣。
轉載請註明作者Jason Ding及其出處
Github首頁(http://jasonding1354.github.io/)
CSDN部落格(http://blog.csdn.net/jasonding1354)
簡書首頁(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)
【電腦視覺】對檢測的人臉進行剪下和歸一化