OpenCV實(shí)現(xiàn)人臉對(duì)齊
點(diǎn)擊上方“小白學(xué)視覺(jué)”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時(shí)間送達(dá)
在人臉識(shí)別中有一個(gè)重要的預(yù)處理步驟-人臉對(duì)齊,該操作可以大幅度提高人臉識(shí)別的準(zhǔn)確率與穩(wěn)定性,但是早期的OpenCV版本不支持人臉Landmark檢測(cè),因此一般都是通過(guò)對(duì)人臉進(jìn)行分割,然后通過(guò)角點(diǎn)檢測(cè)來(lái)尋找眼睛兩個(gè)角點(diǎn),連線之后根據(jù)它們有水平線的角度,旋轉(zhuǎn)實(shí)現(xiàn)人臉對(duì)齊之后在提取人臉區(qū)域,OpenCV3.x版本開始支持獲取Landmark數(shù)據(jù),最常見的Landmark數(shù)據(jù)就是人臉的68個(gè)標(biāo)準(zhǔn)點(diǎn)位,圖示如下:

實(shí)現(xiàn)對(duì)齊主要是基于眼睛的位置,對(duì)人臉傾斜進(jìn)行幾何變換,實(shí)現(xiàn)人臉對(duì)齊操作,人臉對(duì)齊對(duì)提高人臉識(shí)別率特別重要,常見的人臉識(shí)別系統(tǒng)都會(huì)包含人臉對(duì)齊操作,舉例如下:

基于OpenCV實(shí)現(xiàn)人臉對(duì)齊主要分為如下幾步
1.人臉檢測(cè)器定義與Landmark檢測(cè)
OpenCV中通過(guò)HAAR或者LBP特征實(shí)現(xiàn)了人臉檢測(cè),最新的OpenCV3.4基于殘差網(wǎng)絡(luò)也實(shí)現(xiàn)了人臉檢測(cè),相關(guān)的文章可以閱讀:?
OpenCV基于殘差網(wǎng)絡(luò)實(shí)現(xiàn)人臉檢測(cè)
?有了人臉之后,我們就可以通過(guò)加載預(yù)訓(xùn)練的Landmark檢測(cè)模型,實(shí)現(xiàn)Landmark檢測(cè),這里使用的模型是局部二值特征(LBF-Local Binary Feature)實(shí)現(xiàn)人臉68個(gè)點(diǎn)位的檢測(cè),這個(gè)也是2014年CVPR的一篇論文。最新的OpenCV3.4 Landmark檢測(cè)器支持自定義人臉檢測(cè)器設(shè)置,所以只要把我們上面的HAAR/LBP/殘差人臉檢測(cè)器設(shè)置過(guò)去就會(huì)自動(dòng)檢測(cè)人臉,然后發(fā)現(xiàn)Landmark數(shù)據(jù)。整個(gè)代碼實(shí)現(xiàn)如下:
// 創(chuàng)建LBF landmark 檢測(cè)器
Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
// 加載模型數(shù)據(jù)
facemark->loadModel("D:/vcprojects/images/lbfmodel.yaml");
cout << "Loaded model" << endl;
// 開始檢測(cè)
printf("start to detect landmarks...\n");
vector<Rect> faces;
facemark->getFaces(img, faces);
vector< vector<Point2f> > shapes;
if (facemark->fit(img, faces, shapes))
{
? ?Point eye_left; // 36th
? ?Point eye_right; // 45th
? ?for (unsigned long i = 0; i
? ? ? ?eye_left = shapes[i][36];
? ? ? ?eye_right = shapes[i][45];
? ? ? ?line(img, eye_left, eye_right, Scalar(255, 0, 0), 2, 8, 0);
? ? ? ?face_alignment(img(faces[i]), eye_left, eye_right, faces[i]);
? ? ? ?// 繪制人臉矩形區(qū)域
? ? ? ?rectangle(img, faces[i], Scalar(255, 0, 0));
? ? ? ?// 繪制人臉68個(gè) landmark點(diǎn)位
? ? ? ?for (unsigned long k = 0; k
? ? ? ? ? ?cv::circle(img, shapes[i][k], 2, cv::Scalar(0, 0, 255), FILLED);
? ?}
? ?namedWindow("Detected_shape");
? ?imshow("Detected_shape", img);
? ?waitKey(0);
}
2.Landmark數(shù)據(jù)處理
對(duì)Landmark數(shù)據(jù)提取獲得眼睛位置坐標(biāo),這里我們獲取的是36與45兩個(gè)點(diǎn)坐標(biāo)計(jì)算角度(參照第一張圖),然后通過(guò)幾何變換實(shí)現(xiàn)人臉對(duì)齊操作。代碼如下:
int offsetx = roi.x;
int offsety = roi.y;
// 計(jì)算中心位置
int cx = roi.width / 2;
int cy = roi.height / 2;
// 計(jì)算角度
int dx = right.x - left.x;
int dy = right.y - left.y;
double degree = 180 * ((atan2(dy, dx)) / CV_PI);
// 旋轉(zhuǎn)矩陣計(jì)算
Mat M = getRotationMatrix2D(Point2f(cx, cy), degree, 1.0);
Point2f center(cx, cy);
Rect bbox = RotatedRect(center, face.size(), degree).boundingRect();
M.at(0, 2) += (bbox.width / 2.0 - center.x);
M.at(1, 2) += (bbox.height / 2.0 - center.y);
// 對(duì)齊
Mat result;
warpAffine(face, result, M, bbox.size());
imshow("face-alignment", result);
3.運(yùn)行效果

完整的程序代碼如下:
#include
#include
#include
#include
using namespace cv;
using namespace cv::face;
using namespace std;
const String ?lbpfilePath = "D:/opencv-3.4/opencv/build/etc/lbpcascades/lbpcascade_frontalface.xml";
bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade);
void face_alignment(Mat &face, Point left, Point right, Rect roi);
int main(int argc, char** argv) {
? ?Mat img = imread("D:/vcprojects/images/gaoyy.png");
? ?namedWindow("input", CV_WINDOW_AUTOSIZE);
? ?imshow("input", img);
? ?CascadeClassifier face_cascade;
? ?face_cascade.load(lbpfilePath);
? ?FacemarkLBF::Params params;
? ?params.n_landmarks = 68; // 68個(gè)標(biāo)注點(diǎn)
? ?params.initShape_n = 10;
? ?params.stages_n = 5; // 算法的5個(gè)強(qiáng)化步驟
? ?params.tree_n = 6; // 模型中每個(gè)標(biāo)注點(diǎn)結(jié)構(gòu)樹 數(shù)目
? ?params.tree_depth = 5; // 決策樹深度
? ?// 創(chuàng)建LBF landmark 檢測(cè)器
? ?Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
? ?facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
? ?// 加載模型數(shù)據(jù)
? ?facemark->loadModel("D:/vcprojects/images/lbfmodel.yaml");
? ?cout << "Loaded model" << endl;
? ?// 開始檢測(cè)
? ?printf("start to detect landmarks...\n");
? ?vector<Rect> faces;
? ?facemark->getFaces(img, faces);
? ?vector< vector<Point2f> > shapes;
? ?if (facemark->fit(img, faces, shapes))
? ?{
? ? ? ?Point eye_left; // 36th
? ? ? ?Point eye_right; // 45th
? ? ? ?for (unsigned long i = 0; i
? ? ? ? ? ?eye_left = shapes[i][36];
? ? ? ? ? ?eye_right = shapes[i][45];
? ? ? ? ? ?line(img, eye_left, eye_right, Scalar(255, 0, 0), 2, 8, 0);
? ? ? ? ? ?face_alignment(img(faces[i]), eye_left, eye_right, faces[i]);
? ? ? ? ? ?// 繪制人臉矩形區(qū)域
? ? ? ? ? ?rectangle(img, faces[i], Scalar(255, 0, 0));
? ? ? ? ? ?// 繪制人臉68個(gè) landmark點(diǎn)位
? ? ? ? ? ?for (unsigned long k = 0; k
? ? ? ? ? ? ? ?cv::circle(img, shapes[i][k], 2, cv::Scalar(0, 0, 255), FILLED);
? ? ? ?}
? ? ? ?namedWindow("Detected_shape");
? ? ? ?imshow("Detected_shape", img);
? ? ? ?waitKey(0);
? ?}
? ?return 0;
}
bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
{
? ?Mat gray;
? ?if (image.channels() > 1)
? ? ? ?cvtColor(image, gray, COLOR_BGR2GRAY);
? ?else
? ? ? ?gray = image.getMat().clone();
? ?equalizeHist(gray, gray);
? ?std::vector<Rect> faces_;
? ?face_cascade->detectMultiScale(gray, faces_, 1.1, 1, CASCADE_SCALE_IMAGE, Size(50, 50));
? ?Mat(faces_).copyTo(faces);
? ?return true;
}
void face_alignment(Mat &face, Point left, Point right, Rect roi) {
? ?int offsetx = roi.x;
? ?int offsety = roi.y;
? ?// 計(jì)算中心位置
? ?int cx = roi.width / 2;
? ?int cy = roi.height / 2;
? ?// 計(jì)算角度
? ?int dx = right.x - left.x;
? ?int dy = right.y - left.y;
? ?double degree = 180 * ((atan2(dy, dx)) / CV_PI);
? ?// 旋轉(zhuǎn)矩陣計(jì)算
? ?Mat M = getRotationMatrix2D(Point2f(cx, cy), degree, 1.0);
? ?Point2f center(cx, cy);
? ?Rect bbox = RotatedRect(center, face.size(), degree).boundingRect();
? ?M.at(0, 2) += (bbox.width / 2.0 - center.x);
? ?M.at(1, 2) += (bbox.height / 2.0 - center.y);
? ?// 對(duì)齊
? ?Mat result;
? ?warpAffine(face, result, M, bbox.size());
? ?imshow("face-alignment", result);
}
交流群
歡迎加入公眾號(hào)讀者群一起和同行交流,目前有SLAM、三維視覺(jué)、傳感器、自動(dòng)駕駛、計(jì)算攝影、檢測(cè)、分割、識(shí)別、醫(yī)學(xué)影像、GAN、算法競(jìng)賽等微信群(以后會(huì)逐漸細(xì)分),請(qǐng)掃描下面微信號(hào)加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三?+?上海交大?+?視覺(jué)SLAM“。請(qǐng)按照格式備注,否則不予通過(guò)。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~

