OpenCV-4-視頻流整合識別預測模型

OpenCV-4-視頻流整合識別預測模型

使用語言:Java 1.8
操作系統:windows x64
OpenCV:4.1.1


說明一下

在此之前,已經試過了圖片的簡單處理,人臉識別,年齡性別預測。

而視頻的處理呢,其實就是吧視頻流對應的每一幀的圖片拿出來識別一下,然後畫點東西上去,再顯示出來。

大部分的解釋內容都在代碼的註釋裏面了,這種學習使用方法的看代碼是最直觀的。

顯示使用swing的就行了。

(由於電腦性能是視頻質量的問題,限制爲5幀識別一次,不然好卡)

代碼:加載視頻輸出,調用圖像識別

代碼是拆分進行說明的,但是所有代碼都會完整提供的。

之後也會上傳到資源,不過那個要積分的。在博客裏面copy出來整理一下也就可以了。

public class videoTest extends JPanel {
    private BufferedImage mImg;
    
    public static void main(String[] args) {
        //使用Swing生成GUI
        JFrame frame = new JFrame("camera");
        try {
            //加載opencv庫  本地上下文
            System.load("D:\\OpenCV\\opencv\\build\\java\\x64\\opencv_java411.dll");

            //加載普通視頻
//            VideoCapture capture = new VideoCapture("D://VID_20181110_213415.mp4");
            //獲取攝像頭視頻流 參數爲第幾個視頻設備
            VideoCapture capture = new VideoCapture(0);
            System.out.println("獲取視頻流情況:" + capture.isOpened());
            int height = (int) capture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
            int width = (int) capture.get(Videoio.CAP_PROP_FRAME_WIDTH);
            if (height == 0 || width == 0) {
                throw new Exception("camera not found!");
            }
            // 創建swing 窗口
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            videoTest panel = new videoTest();
            frame.setContentPane(panel);
            frame.setVisible(true);
            frame.setSize(width + frame.getInsets().left + frame.getInsets().right,
                    height + frame.getInsets().top + frame.getInsets().bottom);

            Mat capImg = new Mat();
            Mat temp = new Mat();
            long num = 1;
            while (frame.isShowing()) {
                num++;
                //獲取視頻幀
                capture.read(capImg);
                //旋轉 用於調整治療頸椎病的視頻
//                Point center = new Point(capImg.width() / 2.0, capImg.height() / 2.0);
//                Mat affineTrans = Imgproc.getRotationMatrix2D(center, 90.0, 1.0);
//                Imgproc.warpAffine(capImg, capImg, affineTrans, capImg.size(), Imgproc.INTER_NEAREST);

                // 由於電腦性能問題,只是每n幀進行一次識別
                if (num % 10 == 0) {
                    //轉換爲灰度圖
                    Imgproc.cvtColor(capImg, temp, Imgproc.COLOR_RGB2GRAY);
                    //識別人臉
                    Mat image = detectFace(capImg);
                    //轉爲圖像顯示
                    panel.mImg = panel.mat2BI(image);
                    panel.repaint();
                } else {
                    //對未進行識別的幀,進行之前一次識別數據的重繪 參數太多很亂,建議大屏幕查看
                    if (OpenCVTools.getRect() != null) {
//                        for (Rect rect : OpenCVTools.getRect()) {
                        for (int i = 0; i < OpenCVTools.getRect().size(); i++) {

                            Imgproc.rectangle(capImg, new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y),
                                    new Point(OpenCVTools.getRect().get(i).x + OpenCVTools.getRect().get(i).width,
                                            OpenCVTools.getRect().get(i).y + OpenCVTools.getRect().get(i).height),
                                    new Scalar(0, 255, 0), 1);

                            Imgproc.putText(capImg, "age:" + OpenCVTools.rest_age_sex.get(i).get(0),
                                    new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y), Imgproc.FONT_HERSHEY_PLAIN, 0.8,
                                    new Scalar(0, 255, 0), 1);

                            Imgproc.putText(capImg, "sex:" + OpenCVTools.rest_age_sex.get(i).get(1),
                                    new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y - 10), Imgproc.FONT_HERSHEY_PLAIN, 0.8,
                                    new Scalar(0, 255, 0), 1);

                            if(Double.valueOf(OpenCVTools.rest_age_sex.get(i).get(2) ) > 0.8){
                                Imgproc.putText(capImg, "like:"+"WuBinHong",
                                        new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y-20), Imgproc.FONT_HERSHEY_PLAIN,0.8,
                                        new Scalar(0, 255, 0), 1);
                            }
                        }
                    }
                    //轉爲圖像顯示
                    panel.mImg = panel.mat2BI(capImg);
                    panel.repaint();
                }
            }
            capture.release();
            frame.dispose();
        } catch (Exception e) {
            System.out.println("有問題");
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            System.out.println(sw.toString());
        } finally {
            System.out.println("Exit");
            frame.dispose();
        }
        System.exit(0);
    }

 /**
     * 轉換圖像
     *
     * @param mat
     * @return
     */
    private BufferedImage mat2BI(Mat mat) {
        int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
        byte[] data = new byte[dataSize];
        mat.get(0, 0, data);

        int type = mat.channels() == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;
        if (type == BufferedImage.TYPE_3BYTE_BGR) {
            for (int i = 0; i < dataSize; i += 3) {
                byte blue = data[i + 0];
                data[i + 0] = data[i + 2];
                data[i + 2] = blue;
            }
        }
        BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
        image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);

        return image;
    }

    @Override
    public void paint(Graphics g) {
        if (mImg != null) {
            g.drawImage(mImg, 0, 0, mImg.getWidth(), mImg.getHeight(), this);
        }
    }
}

OpenCV的視頻處理預設方法,還要很多,比如每一幀的回調函數,延遲信息,運行狀態等等。由於這裏想弄的不在視頻處理,所以只是用最簡單的獲取每一幀進行處理。

代碼:圖像中人臉識別

由於使用攝像頭拍攝的時候,很多時候拍攝到的都是側臉,所以只使用一個正臉識別感覺不夠。如果還有其他的識別需求,可以看看OpenCV的模型文件夾下,還有很多現成的識別模型(眼睛,身體,等等)。

這裏只做了側臉+正臉的識別。

/**
     * opencv實現人臉識別,同時檢測到人臉和人眼時才截圖
     * @param img  需要識別的圖像
     */
    public static Mat detectFace(Mat img) {
        OpenCVTools.setRect(new ArrayList<Rect>());
        OpenCVTools.rest_age_sex = new ArrayList<ArrayList<String>>();
        System.out.println("Running DetectFace ... ");
        // 從配置文件lbpcascade_frontalface.xml中創建一個人臉識別器,該文件位於opencv安裝目錄中
        CascadeClassifier faceDetector = new CascadeClassifier("D:\\OpenCV\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
        CascadeClassifier profilefaceDetector = new CascadeClassifier("D:\\OpenCV\\opencv\\sources\\data\\haarcascades\\haarcascade_profileface.xml");

        // 在圖片中檢測人臉
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(img, faceDetections);
        System.out.println(String.format("Detected %s faces", faceDetections.toArray().length));
        Rect[] rects = faceDetections.toArray();
        // 獨立出來的,進行對識別人臉圖像的處理
        pointRect(rects, img);  

        // 在圖片中檢測側臉
        MatOfRect profilefaceDetections = new MatOfRect();
        profilefaceDetector.detectMultiScale(img, profilefaceDetections);
        System.out.println(String.format("Detected %s profileface", profilefaceDetections.toArray().length));
        Rect[] profilefaceRects = profilefaceDetections.toArray();
        pointRect(profilefaceRects, img);

        return img;
    }

代碼:人臉年齡性別預測,繪製信息

這裏的性別預測使用的方法也就是上一篇的內容。

/**
     * 繪製人臉以及預測年齡圖像信息
     * 也可以進行人臉截取保存
     * @param rects 識別到的人臉列表
     * @param img   原始識別圖片
     */
    private static void pointRect(Rect[] rects, Mat img) {
        if (rects != null && rects.length >= 1) {
            for (Rect rect : rects) {
                OpenCVTools.getRect().add(rect);
                //給臉 畫矩形
                Imgproc.rectangle(img, new Point(rect.x, rect.y), 
                                  new Point(rect.x + rect.width, rect.y + rect.height),
                        new Scalar(0, 255, 0), 1);

                //年齡識別
                String age = OpenCVTools.predict_age(img.submat(rect));
                Imgproc.putText(img, "age:" + age, new Point(rect.x, rect.y), Imgproc.FONT_HERSHEY_PLAIN, 0.8, new Scalar(0, 255, 0), 1);
                //性別識別
                String sex = OpenCVTools.predict_gender(img.submat(rect));
                Imgproc.putText(img, "sex:" + sex, new Point(rect.x, rect.y - 10), Imgproc.FONT_HERSHEY_PLAIN, 0.8, new Scalar(0, 255, 0), 1);
                ArrayList<String> age_sex = new ArrayList<String>();
                age_sex.add(age);
                age_sex.add(sex);

                //圖像對比
                Double compareNum = OpenCVTools.compare_image(OpenCVTools.faceImg, img.submat(rect));
                Double compareNum2 = OpenCVTools.compare_image(OpenCVTools.faceImg2, img.submat(rect));
                age_sex.add(compareNum > compareNum2 ? compareNum.toString() : compareNum2.toString());
                if (compareNum > 0.8 || compareNum2 > 0.8) {
                    Imgproc.putText(img, "like:" + "One People", new Point(rect.x, rect.y - 20), Imgproc.FONT_HERSHEY_PLAIN, 0.8, new Scalar(0, 255, 0), 1);
                } else {
                    //保存不被識別的人臉照片
//                    save(img, rect, "D:\\ijworkspace\\meaen_test\\data\\face\\" + new Random().nextInt(2000) + ".jpg");
                }
                //保存 識別的信息,由於其他幀的重繪
                OpenCVTools.rest_age_sex.add(age_sex);
            }
        }
    }

    /**
     * opencv將人臉進行截圖並保存
     * @param img
     */
    private static void save(Mat img, Rect rect, String outFile) {
        Mat sub = img.submat(rect);
        Mat mat = new Mat();
        Size size = new Size(300, 300);
        Imgproc.resize(sub, mat, size);
        Imgcodecs.imwrite(outFile, mat);
    }

代碼:補充的一下代碼

這裏的參數是會用到的,作爲識別幀保存的識別數據,用於重繪。

public  class OpenCVTools {
    public static Mat faceImg = Imgcodecs.imread("D:\\ijworkspace\\meaen_test\\data\\1625.jpg");
    public static Mat faceImg2 = Imgcodecs.imread("D:\\ijworkspace\\meaen_test\\data\\914.jpg");

    private static List<Rect> rect = null;
    public static ArrayList<ArrayList<String>> rest_age_sex =  new ArrayList<ArrayList<String>>();

    public static  java.util.List<Rect> getRect() {
        return rect;
    }

    public static void setRect( java.util.List<Rect> rect) {
        OpenCVTools.rect = rect;
    }
    // 文章的一下封裝的靜態方法都是放在這個類裏面的,只是爲了文章需要拆分開了。
}

小結一下

這個視頻流進行圖像識別的代碼,的結果就是攝像頭實時圖像識別,代碼中還有一個簡單的圖像對比,這樣就可以蠻當做一個識別攝像頭的程序使用了。【最終效果就是前一篇文字的圖片,只不過這個是視頻】

比如,使用老闆的頭像數據做對比,在檢查到比配度很好的情況下,提示些信息,或者切換到桌面 罒ω罒


2019-11-05 小杭

使用攝像頭來進行實時圖像識別完成了,學習結束了。(然而並沒有

接下來,就要研究一下識別模型和深度學習訓練和算法了。。。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章