【计算机视觉】对检测的人脸进行剪切和归一化

引子

要进行人脸的识别,尤为是复杂环境下的人脸识别,就要在捕获人脸图像以后对图像进行预处理的工做,如图像的大小和灰度的归一化,头部姿态的矫正,图像分割等。这样作的目的是改善图像质量,消除噪声,统一图像灰度值及尺寸,为后序特征提取和分类识别打好基础。
该文将就人脸的剪切和归一化操做进行学习探讨,并作一笔记备用。html

具体步骤

因为opencv的人脸识别须要输入图片具备相同的尺寸和灰度级,因此识别以前还要进行很重要的一步工做,就是针对人脸识别的须要调整人脸图片,具体能够分为如下步骤:git

  • 图片去噪
  • 图片灰度化
  • 人脸的剪切(crop)
  • 调整图片大小(resize)
  • 姿态旋转(rotate)

人脸剪切的策略

这里我设想的最基本的人脸剪切方法是根据人眼的位置进行人脸的旋转和校订的。
基本思路以下:github

  1. 根据两眼的倾斜角度对图像进行旋转校订
  2. 根据两眼在图片中的实际距离和自定义的偏移量进行图片的缩放

下面是剪切的效果图,最终的图片是100*100像素,偏移量是0.3函数

Detecting Face
Detecting Face
Cropping Result
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;
}

解释一下代码:spa

  • 偏移量是指眼睛到图片上边界和左右边界的距离比例,好比0.3的偏移量,那么左眼到左边界,右眼到右边界的长度占所剪切的人脸图片宽度为0.3,同理到上边界的距离也占图片高度的0.3。
  • 根据两眼的实际距离和两眼依偏移量所应占据图片宽度的比例对图片进行缩放,好比实际距离是求一个欧式距离eye_distance,参考的距离eye_reference是输出的宽度outputWidth减去左眼到左边界的0.3outputWidth,再减去右眼到右边界的0.3 outputWidth。
  • 因为最终要作人脸识别,须要输出灰度图,因此,最后的返回值是通过灰度化而且直方图均衡化了的图片

图片旋转的API

这里图片旋转用到了OpenCV的函数,warpAffine()。
这里涉及到了仿射变换,能够参考OpenCV官方文档,放射变换.net

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矩阵。code

转载请注明做者Jason Ding及其出处
Github主页(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
简书主页(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)htm