之前通过在java环境中使用opencv库对矩形进行了识别,本次将继续使用java语言利用opencv库对图片中的人脸进行识别。


 首先在主函数中要加载本地库,否则将会抛出java.lang.UnsatisfiedLinkError异常。


System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

接下来通过文件路径加载文件生成Mat对象

Mat src = Imgcodecs.imread(filePath+fileName,3);

加载方式为三通道,因此Mat中保存的是彩色对象,这也是方便后期对人脸所在位置进行标识,以及图片裁剪。之后需要将彩色图片转换为灰度图像,通过使用cvtColor函数实现:


Mat gray = new Mat();

Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);


opencv中关于人脸识别已经有相关的库,相关的文件在$OPENCV_HOME/sources/data目录下,在该目录下可以看到有三个文件夹

java人脸识别难吗 java人脸识别算法_加载

分别是依据haar特征、hogc特征、ibp级联对人脸识别训练的模型数据,本例中我们加载的是haar特征中已经训练好的face特征以及eye特征,分别是haarcascade_frontalface_alt文件和haarcascade_eye.xml文件。个人比较推荐讲该文件copy到项目的根目录下。并且创建分类器实例,相关代码如下:

CascadeClassifier eye_Classfier = new CascadeClassifier();
        CascadeClassifier face_Classfier = new CascadeClassifier();

        if(!eye_Classfier.load("./haarcascade_eye.xml")){
            System.out.println("Load haarcascade_eye.xml failed");
            System.exit(0);
        }
        if(!face_Classfier.load("./haarcascade_frontalface_alt.xml")){
            System.out.println("Load haarcascade_frontalface_alt.xml");
            System.exit(0);
        }


下面就是我们的重点detectMultiScale函数,首先声明函数原型:


public  void detectMultiScale(Mat image, MatOfRect objects);
public  void detectMultiScale(Mat image, MatOfRect objects, double scaleFactor, int minNeighbors, int flags, Size minSize, Size maxSize)


在源码中可以看到对该方法进行的介绍


//
    // C++:  void detectMultiScale(Mat image, vector_Rect& objects, double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(), Size maxSize = Size())
    //

    //javadoc: CascadeClassifier::detectMultiScale(image, objects, scaleFactor, minNeighbors, flags, minSize, maxSize


detectMultiScale函数是CascadeClassifier类中的一个函数,该参数有两个重载方法。下面介绍一下该函数各个参数的含义:


1.为待处理的图像,读入所生成的矩阵


2.获取处理结果所保存的MatOfRect对象


3.图像缩放因子,当取默认值为1.1时表名检测矩形每次扩大10%


4.最小相邻矩形,默认为3,即有三个符合条件的邻接矩形人脸区域才会确认。


5.设置为默认值0就好


6.人脸区域的最小值


7.人脸区域的最大值


本例中因为图像质量较好,并且只考虑一张图片中的仅存在一张一脸的简单情况,因此对于参数的配置没有更高要求(因此在下面对识别到的眼睛以及人脸所在的矩形没有做进一步的判断、识别),而采用了参数的默认值。具体代码如下:


MatOfRect eye_matOfRect = new MatOfRect();
MatOfRect face_matOfRect = new MatOfRect();
//find eyes
eye_Classfier.detectMultiScale(Contrast, eye_matOfRect);//, 1.1, 3,  3, new Size(30, 30), new Size(width, height));
System.out.println(eye_matOfRect.size().toString());
face_Classfier.detectMultiScale(Contrast, face_matOfRect);//, 1.1, 3,  3, new Size(30, 30), new Size(width, height));
System.out.println(face_matOfRect.size().toString());


其中eye_matOfRect和face_matOfRec对象是为了保存生成的结果。但是注意该函数对所获取的区域仅仅会返回识别矩形的左上方的点,具体的矩形区域还要根据其他的手段来进行划定。本例中因为需要获取人脸所以对眼睛的区域并没有做更进一步的划分。而脸部区域的划定则是根据“美人的判断标准“——三庭五眼当做基础的比例依据,进行进一步的划分,代码如下:


Point p1 = new Point(eye_matOfRect.get(0, 0));
Point p2 = new Point(eye_matOfRect.get(1, 0));
Point pf = new Point(face_matOfRect.get(0, 0));
Double width = getWild(pf.x, p1.x, p2.x);
Double height = getHeight(pf.y, p1.y, p2.y);
Point init_ps = new Point(pf.x, pf.y - height);
Point init_pe = new Point(pf.x + width * 3, pf.y + height*4);


其中getWild及getHeight为获取长宽的基本单位


private Double getWild(Double start, Double y1, Double y2){ return (y1 + y2 - 2 * start)/2; }
private Double getHeight(Double start, Double y1, Double y2){ return ((y1+y2)/2 - start); }


获得图像的矩形框后还需要对获得的点进行合法性检测,避免坐标点出现负值或划定的矩形超出原图片的情况,具体通过getFinalRect函数实现,该函数声明如下:


private Rect getFinalRect(Point initX, Point initY, Double MaxX, Double MaxY){
    Double x1 = initX.x;
    Double x2 = initY.x;
    Double y1 = initX.y;
    Double y2 = initY.y;
    Double minx = 0.0;
    Double miny = 0.0;
    Double maxX = 0.0;
    Double maxY = 0.0;
    if(x1 > x2){
        if(x2 > 0) minx = x2;
        else minx = 0.0;
        if(x1 <MaxX) maxX = x1;
        else maxX = MaxX;
    }else{
        if( x1 > 0 ) minx = x1;
        else minx = 0.0;
        if(x2 < MaxX) maxX = x2;
        else maxX = MaxX;
    }

    if( y1 > y2){
        if(y2 > 0) miny = y2;
        else miny = 0.0;
        if(y1 < MaxY) maxY = y1;
        else maxY = MaxY;
    }else{
        if(y1 > 0) miny = y1;
        else miny = 0.0;
        if(y2 < MaxY) maxY = y2;
        else maxY = MaxY;
    }
    System.out.println(miny);
    Point ps = new Point(minx, miny);
    Point pe = new Point(maxX, maxY);
    System.out.println("Final : " + ps + pe);
    Rect roi = new Rect(ps, pe);
    return roi;
}


最后通过获取的矩形局域对象(Rect类)将源图像进行剪切获得最后的目标图像。


Rect roi = getFinalRect(init_ps, init_pe, src.width() + 0.0, src.height() + 0.0);
Mat dst = new Mat(src,roi);
Imgcodecs.imwrite("resultFace.jpg", dst);


以上,谢谢阅读!欢迎讨论、拍砖!