1. 程式人生 > >java使用opencv識別人臉

java使用opencv識別人臉

環境搭建見:https://blog.csdn.net/v_gbird/article/details/80299931

 之前通過在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目錄下,在該目錄下可以看到有三個資料夾

分別是依據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);
以上,謝謝閱讀!歡迎討論、拍磚!原工程檔案參見GitHub:https://github.com/ZCY2013/DetecRectangle.git