2015年1月29日 星期四

[Java]JavaCV 人臉偵測(Face Detcet)

上一篇筆記 紀錄了如何用JavaCV開啟WebCam ,這篇來紀錄一下人臉偵測。

人臉偵測其實很常見,一般的相機,手機的相機APP 都支援人臉偵測,在拍照時可以以偵測

到的人臉做為對焦區域,Face Detect基本上就是找出人臉所在的位置,本篇所使用的原理

參考這一篇  , 在opencv中有己經訓練好的檔案可以使用,常見包含人臉偵測,雙眼偵

測,以本篇來說只要換一下訓練的檔案,程式不用改一個字,就可以改成偵測雙眼的功能。


人臉偵測和人臉識別是2個不同的東西,就好比我看到人(偵測)以及我認識這個人(識

別),是二個不同層面的事情,當然你要識出這個人之前你要先看到這個人的人臉。


程式流程如下:

取得WebCam畫面 -> 轉成灰階 ->偵測人臉位置 ->把人臉標記在WebCam畫面上 ->顯

示 -> 重頭來過


package javacv2;

import java.io.File;
import java.net.URL;
import org.bytedeco.javacpp.Loader;
import static org.bytedeco.javacpp.helper.opencv_objdetect.cvHaarDetectObjects;
import static org.bytedeco.javacpp.opencv_core.CV_AA;
import org.bytedeco.javacpp.opencv_core.CvMemStorage;
import org.bytedeco.javacpp.opencv_core.CvRect;
import org.bytedeco.javacpp.opencv_core.CvScalar;
import org.bytedeco.javacpp.opencv_core.CvSeq;
import static org.bytedeco.javacpp.opencv_core.IPL_DEPTH_8U;
import org.bytedeco.javacpp.opencv_core.IplImage;
import static org.bytedeco.javacpp.opencv_core.cvClearMemStorage;
import static org.bytedeco.javacpp.opencv_core.cvGetSeqElem;
import static org.bytedeco.javacpp.opencv_core.cvLoad;
import static org.bytedeco.javacpp.opencv_core.cvPoint;
import static org.bytedeco.javacpp.opencv_core.cvRectangle;
import static org.bytedeco.javacpp.opencv_imgproc.CV_BGR2GRAY;
import static org.bytedeco.javacpp.opencv_imgproc.cvCvtColor;
import org.bytedeco.javacpp.opencv_objdetect;
import static org.bytedeco.javacpp.opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING;
import org.bytedeco.javacpp.opencv_objdetect.CvHaarClassifierCascade;
import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.OpenCVFrameGrabber;

public class JavaCV2 {

    public static void main(String args[]) {

        //用 FrameGrabber 取得 得webcam
        FrameGrabber grabber = new OpenCVFrameGrabber("");

        //宣告一個CanvasFrame ,就把它當作是一個JFrame用。
        CanvasFrame canvas = new CanvasFrame("Webcam", CanvasFrame.getDefaultGamma() / grabber.getGamma());
        //設定這一個JFrame關閉時順便把程式結束。
        canvas.setDefaultCloseOperation(
                javax.swing.JFrame.EXIT_ON_CLOSE);

        try {
            //從網路上取得訓練好的檔案(不太建議用2.4版本的會有問題)
            URL url = new URL("https://raw.github.com/Itseez/opencv/2.2/data/haarcascades/haarcascade_frontalface_alt.xml");
            //把網頁上取回來的資料寫入本地端檔案
            File file = Loader.extractResource(url, null, "classifier", ".xml");
            //假如有相同檔名己存在則刪除舊友的
            file.deleteOnExit();
            //指定classifier檔案名稱(也就是剛才由網路上捉下來的檔案路徑)
            String classifierName = file.getAbsolutePath();

            // 預先載入 opencv_objdetect module .
            Loader.load(opencv_objdetect.class);
            //初始化HaarClassifierCascade
            CvHaarClassifierCascade classifier = new CvHaarClassifierCascade(cvLoad(classifierName));
            //假如載入HaarClassifierCascade 失敗則結束程式
            if (classifier.isNull()) {
                System.err.println("Error loading classifier file \"" + classifierName + "\".");
                System.exit(1);
            }

            //啟動WebCam
            grabber.start();

            //定義一個IplImage 存放取出來的影像
            IplImage img;

            //先取一張畫面放入IplImage            
            img = grabber.grab();
            //取得影像的長寬
            int width = img.width();
            int height = img.height();

            //建立一個灰階影像(用IPL_DEPTH_8U 原因為,灰階影像每一個pixel R,G,B值皆相等,只需要存一份即可)
            IplImage grayImage = IplImage.create(width, height, IPL_DEPTH_8U, 1);
            //建立一個CvMemStorage做運算用
            CvMemStorage storage = CvMemStorage.create();
            
            //設定顯示用的CanvasFrame為取出的影像大小
            canvas.setCanvasSize(grabber.getImageWidth(),
                    grabber.getImageHeight());

            while (canvas.isVisible() && (img = grabber.grab()) != null) {
                //重置運算用的CvMemStorage
                cvClearMemStorage(storage);

                // 把原始影像變成灰階
                cvCvtColor(img, grayImage, CV_BGR2GRAY);
                //偵測人臉
                CvSeq faces = cvHaarDetectObjects(grayImage, classifier, storage,
                        1.1, 3, CV_HAAR_DO_CANNY_PRUNING);
                //取得捉到的人臉
                int total = faces.total();
                //在人臉上畫一個紅色的矩型
                for (int i = 0; i < total; i++) {
                    CvRect r = new CvRect(cvGetSeqElem(faces, i));
                    int x = r.x(), y = r.y(), w = r.width(), h = r.height();
                    cvRectangle(img, cvPoint(x, y), cvPoint(x + w, y + h), CvScalar.RED, 1, CV_AA, 0);
                }
                //顯示圖片
                canvas.showImage(img);
                
            }
        }
        catch(Exception e)
        {
            
        }
        

    }
}




至於主要用來偵測的cvHaarDetectObjects 用法如下 -資料來源

cvHaarDetectObjects( const CvArr* image, CvHaarClassifierCascade* cascade,
                            CvMemStorage* storage, double scale_factor=1.1,
                            int min_neighbors=3, int flags=0,
                            CvSize min_size=cvSize(0,0) );


image
被檢圖像
cascade
harr 分類器級聯的內部標識形式
storage
用來存儲檢測到的一序列候選目標矩形框的記憶體區域。
scale_factor
在前後兩次相繼的掃描中,搜索視窗的比例繫數。例如1.1指將搜索視窗依次擴大10%。
min_neighbors
構成檢測目標的相鄰矩形的最小個數(預設-1)。如果組成檢測目標的小矩形的個數和小於min_neighbors-1 都會被排除。如果min_neighbors 為 0, 則函數不做任何操作就返回所有的被檢候選矩形框,這種設定值一般用在用戶自定義對檢測結果的組合程式上。
flags
操作方式。當前唯一可以定義的操作方式是 CV_HAAR_DO_CANNY_PRUNING。如果被設定,函數利用Canny邊緣檢測器來排除一些邊緣很少或者很多的圖像區域,因為這樣的區域一般不含被檢目標。人臉檢測中通過設定閾值使用了這種方法,並因此提高了檢測速度。
min_size
檢測視窗的最小尺寸。預設的情況下被設為分類器訓練時採用的樣本尺寸(人臉檢測中預設大小是~20×20)。


接下來我們把程式中的
  //從網路上取得訓練好的檔案(不太建議用2.4版本的會有問題)
            URL url = new URL("https://raw.github.com/Itseez/opencv/2.2/data/haarcascades/haarcascade_frontalface_alt.xml");

換成

  //從網路上取得訓練好的檔案(不太建議用2.4版本的會有問題)
URL url = new URL("https://raw.githubusercontent.com/Itseez/opencv/2.2/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml");



網路上也有一些訓練好的檔案的資料可以使用,不限於只有人臉或雙眼,參考下列網址 https://github.com/Itseez/opencv/tree/master/data/haarcascades

沒有留言:

張貼留言