Java基于OpenCV+Hog+SVM实现微笑检测

文章目录

  • 一、配置Java的OpenCV环境
  • 二、训练
    • 1.读取所有的图片,截取图片中的人脸并保存
    • 2.把所有的人脸图片进行随机分组
    • 3.计算梯度值
    • 4.提取Hog值
    • 5.训练模型
    • 6.模型评估
    • 7.训练结果
  • 三、预测
    • 1.读取摄像头
    • 2.创建显示图片的窗体
    • 3.捕获读取到的人脸
    • 4.标注人脸
    • 5.对获取到的人脸进行提取Hog特征值并进行预测
    • 6.对预测结果进行相关操作
    • 7.预测结果
  • 四、源代码
    • SmileTrain
    • SmilePredict
  • 五、总结
  • 六、参考

一、配置Java的OpenCV环境

参考https://blog.csdn.net/zmx729618/article/details/78113268

二、训练

1.读取所有的图片,截取图片中的人脸并保存

一共4000张图片。

public static void getFace(String filePath) {
     
        System.out.println("读取人脸......");
        //人脸检测模型
        CascadeClassifier faceDetector = new CascadeClassifier("C:\\OpenCV\\opencv\\sources\\data\\lbpcascades\\lbpcascade_frontalface.xml");
        //定义人脸检测
        MatOfRect faceDetections = new MatOfRect();
        //获取图像所在的文件夹
        File file = new File(filePath);
        //遍历文件夹中的所有图片
        for (File f : file.listFiles()) {
     
            //读取图片
            Mat image = Imgcodecs.imread(f.getAbsolutePath());
            //对图像进行人脸检测
            faceDetector.detectMultiScale(image, faceDetections);
            //获取人脸区域
            Rect[] rects = faceDetections.toArray();

            //存在人脸
            if (faceDetections.toArray().length > 0) {
     
                //获得人脸区域
                for (Rect rect : rects) {
     
                    Mat mat = new Mat(rect.width, rect.width, CvType.CV_8UC3);
                    for (int i = rect.x; i < rect.x + rect.width; i++) {
     
                        for (int j = rect.y; j < rect.y + rect.height; j++) {
     
                            mat.put(j - rect.y, i - rect.x, image.get(j, i));
                        }
                    }
                    //截取人脸区域
                    Imgproc.resize(mat, mat, new Size(64, 64), 0, 0, 0);
                    //保存图像
                    Imgcodecs.imwrite("./genki4k/files1/" + f.getName(), mat);
                }
            }
        }
        System.out.println("读取人脸完毕");
    }

2.把所有的人脸图片进行随机分组

一共3670张图片,分十组。
且前2162张为笑脸,其余不是。

public static void getRandomFile(String filePath) {
     
        System.out.println("图片随机分组......");
        File file = new File(filePath);
        File listFile[] = file.listFiles();
        Random random = new Random();
        //初始化标签
        //前2162为笑脸
        for (int i = 0; i < 3670; i++) {
     
            String no = listFile[i].getName().substring(4, 8);
            if (Integer.parseInt(no) <= 2162)
                flags[Integer.parseInt(no)] = 1;
            else
                flags[(Integer.parseInt(no))] = 0;
        }
        //随机排列,连同标签一起排列
        for (int i = 0; i < 3670; i++) {
     
            int r = random.nextInt(i + 1);
            File tmp = listFile[i];
            listFile[i] = listFile[r];
            listFile[r] = tmp;
            int t = flags[i];
            flags[i] = flags[r];
            flags[r] = t;
        }
        //分组
        for (int i = 0; i < 10; i++) {
     
            for (int j = 0; j < 367; j++) {
     
                randomFiles[i][j] = listFile[i * 367 + j];
            }
        }
        System.out.println("图片随机分组完毕");
    }

3.计算梯度值

使用Sobel算子计算梯度

public static Mat getGradient(Mat src) {
     
        Mat grayMat = new Mat();
        // 灰度
        Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY, 0);
        //梯度计算
        Mat x = new Mat(); //X 方向梯度值矩阵
        Mat y = new Mat(); //Y 方向梯度值矩阵
        //计算 X,Y 方向绝对梯度
        Imgproc.Sobel(grayMat, x, -1, 1, 0, 3, 1, 0);
        Core.convertScaleAbs(x, x);
        Imgproc.Sobel(grayMat, y, -1, 0, 1, 3, 1, 0);
        Core.convertScaleAbs(y, y);
        //XY 综合梯度
        Core.addWeighted(x, 0.5, y, 0.5, 0, grayMat);

        return grayMat;
    }

4.提取Hog值

public static float[] getHOG(Mat src) {
     
        //灰度以及梯度处理
        src = getGradient(src);
        //图像缩放
        Imgproc.resize(src, src, new Size(64, 128), 0, 0, Imgproc.INTER_AREA);
        HOGDescriptor hogDescriptor = new HOGDescriptor();
        MatOfFloat descriptorsValues = new MatOfFloat();
        MatOfPoint locations = new MatOfPoint();
        //计算 Hog 特征值
        hogDescriptor.compute(src, descriptorsValues, new Size(0, 0), new Size(0, 0), locations);
        //特征值维数
        int size = (int) (descriptorsValues.total() * descriptorsValues.channels());
        float[] temp = new float[size];
        descriptorsValues.get(0, 0, temp);
        //特征数组
        float[] resArrays = descriptorsValues.toArray();
        return resArrays;
    }

5.训练模型

使用OpenCV自带的SVM算法实现

public static void train(int t) {
     
        System.out.println("开始训练第" + (t + 1) + "组");
        //定义 SVM 对象
        SVM svm = SVM.create();
        //训练数据 Hog 特征值
        Mat dataMat = new Mat();
        //标签数据
        Mat labelMat = new Mat();
        //SVM类型
        svm.setType(SVM.C_SVC);
        //线性
        svm.setKernel(SVM.LINEAR);
        //迭代终止条件
        svm.setTermCriteria(new TermCriteria(TermCriteria.MAX_ITER, 20000, 1e-6));

        //数据大小
        int size = inputArr[0].length;

        //创建数据和标签对象,训练九组
        dataMat.create(3303, size, CvType.CV_32FC1);
        labelMat.create(3303, 1, CvType.CV_32SC1);

        //加载数据以及标签
        for (int i = 0; i < 3303; i++) {
     
            for (int j = 0; j < size; j++) {
     
                dataMat.put(i, j, inputArr[i][j]);
            }
            labelMat.put(i, 0, resultFlag[i]);
        }
        System.out.println("训练......");
        svm.train(dataMat, Ml.ROW_SAMPLE, labelMat);
        System.out.println("训练完毕");

        System.out.println("保存训练模型......");
        svm.save("./output/result" + t + ".xml"); //保存 SVM 模型
        dataMat.release(); //释放变量
        labelMat.release(); //释放变量
        System.out.println("保存训练模型完毕");
    }

6.模型评估

用测试数据通过训练得到的模型进行预测来计算最终的准确率
Java基于OpenCV+Hog+SVM实现微笑检测_第1张图片

public static double test(int i) {
     
        System.out.println("开始测试");
        System.out.println("开始测试第" + (i + 1) + "组模型......");
        //创建 SVM 模型对象
        SVM svm = SVM.load("./output/result" + i + ".xml");
        // TP 为正样本,并且检测结果为正样本
        // FP 为正样本,并且检测结果为负样本
        // TN 为负样本,并且检测结果为负样本
        // FN 为负样本,并且检测结果为正样本
        double TP = 0;
        double FP = 0;
        double TN = 0;
        double FN = 0;

        for (int k = 0; k < 367; k++) {
     
            Mat testMat = new Mat();
            int size=hogs[i][k].length;
            testMat.create(1,size , CvType.CV_32FC1);
            for (int p = 0; p < size; p++) {
     
                testMat.put(0, p, hogs[i][k][p]);
            }
            float predict = svm.predict(testMat); //预测结果
            if (result[i][k] == 1 && predict == 1) {
     
                TP++; //TP 自增 1
            } else if (result[i][k] == 1 && predict == 0) {
     
                FP++; //FP 自增 1
            } else if (result[i][k] == 0 && predict == 0) {
     
                TN++; //TN 自增 1
            } else if (result[i][k] == 0 && predict == 1) {
     
                FN++; //FN 自增 1
            }
        }
        System.out.println("第" + (i + 1) + "组模型训练完毕");
        //计算正确率
        System.out.println("第" + (i + 1) + "组模型训练结果");
        System.out.println("TP: " + TP + "\t FP: " + FP + "\t TN: " + TN + "\t FN: " + FN);
        System.out.println("准确率:" + 2 * TP / (2 * TP + FP + FN));
        System.out.println();
        System.out.println("测试完毕");
        return 2 * TP / (2 * TP + FP + FN);
    }

7.训练结果

训练过程
Java基于OpenCV+Hog+SVM实现微笑检测_第2张图片
训练结果
其中best为准确率最高的模型
Java基于OpenCV+Hog+SVM实现微笑检测_第3张图片

三、预测

1.读取摄像头

 		//创建Opencv中的视频捕捉对象
        VideoCapture camera = new VideoCapture();
        //open函数中的0代表当前计算机中索引为0的摄像头
        camera.open(0);

2.创建显示图片的窗体

同时需要设置键盘监听,按下s为保存图片,esc退出。

	//创建窗口
    public static JFrame createJFrame(String windowName, int width, int height) {
     
        JFrame frame = new JFrame(windowName);
        label = new JLabel();
        final JScrollPane imageScrollPane = new JScrollPane(label);
        //滚动条
        imageScrollPane.setPreferredSize(new Dimension(width, height));
        frame.add(imageScrollPane, BorderLayout.CENTER);
        //自适应大小
        frame.pack();
        //设置窗口位置
        frame.setLocationRelativeTo(null);
        //窗口可见
        frame.setVisible(true);
        //关闭事件
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用户点击窗口关闭
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        //设置键盘监听
        frame.addKeyListener(new KeyListener() {
     
            @Override
            public void keyTyped(KeyEvent e) {
     

            }

            @Override
            public void keyPressed(KeyEvent e) {
     
                System.out.println(e.getKeyCode());
                //按下s保存
                if(e.getKeyCode()==83){
     
                    flags[2]=true;
                }
                //按下esc退出
                else if(e.getKeyCode()==27){
     
                    flag=false;
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {
     

            }
        });
        return frame;
    }

3.捕获读取到的人脸

 				camera.read(img);//read方法读取摄像头的当前帧
                faceDetector.detectMultiScale(img, faceDetections);
                //获取人脸区域
                Rect[] rects = faceDetections.toArray();

4.标注人脸

						//标注人脸区域
                        //如果是smile则为绿色,否则为红色
                        if(flags[1])
                            Imgproc.rectangle(img, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height),
                                    new Scalar(0, 255, 0), 2);
                        else
                            Imgproc.rectangle(img, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height),
                                    new Scalar(0, 0, 255), 2);

5.对获取到的人脸进行提取Hog特征值并进行预测

//截取人脸区域用于预测
Imgproc.resize(mat, mat, new Size(64, 64), 0, 0, 0);
	float hog[] = SmileTrain.getHOG(mat);
	//预测结果
	if (predict(hog)) {
     
	   flags[1] = true;
	} else {
     
	   flags[1] = false;
	}

预测函数

public static boolean predict(float hog[]){
     
        SVM svm = SVM.load("./output/best.xml");
        Mat predictMat = new Mat();
        predictMat.create(1, hog.length, CvType.CV_32FC1);
        predictMat.put(0, 0, hog);
        float results = svm.predict(predictMat); //预测结果
        if(results==1)
            return true;
        return false;
    }

6.对预测结果进行相关操作

						//根据是否为smile显示不同文字
                        if (flags[1]) {
     
                            Imgproc.putText(img, "smile", new Point(rect.x, rect.y), Imgproc.FONT_HERSHEY_COMPLEX, 2, new Scalar(0, 255, 0));
                        } else {
     
                            Imgproc.putText(img, "no smile", new Point(rect.x, rect.y), Imgproc.FONT_HERSHEY_COMPLEX, 2, new Scalar(0, 0, 255));
                        }

7.预测结果

笑脸
Java基于OpenCV+Hog+SVM实现微笑检测_第4张图片
不笑
Java基于OpenCV+Hog+SVM实现微笑检测_第5张图片
最终截取到的笑脸
Java基于OpenCV+Hog+SVM实现微笑检测_第6张图片

最终截取到的不笑脸
Java基于OpenCV+Hog+SVM实现微笑检测_第7张图片

四、源代码

SmileTrain

package smile;

import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.ml.Ml;
import org.opencv.ml.SVM;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.objdetect.HOGDescriptor;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.Random;

public class SmileTrain {
     

    //每张图片的标签,1笑 0不笑
    static int flags[] = new int[4002];
    //训练标签
    static int resultFlag[] = new int[4002];
    //分组 10组每组367张
    static int result[][] = new int[10][367];
    //随机分组文字
    static File randomFiles[][] = new File[10][367];
    //每张图片的HOG值
    static float inputArr[][] = new float[4002][];
    //每组每张图片的HOG值
    static float hogs[][][] = new float[10][367][];


    public static void main(String args[]) throws IOException {
     
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME); //加载 opencv 配置文件
        //getFace("./genki4k/files/");
        handleFace();
    }

    /**
     * 在给予的图片中找到人脸并保存
     */
    public static void getFace(String filePath) {
     
        System.out.println("读取人脸......");
        //人脸检测模型
        CascadeClassifier faceDetector = new CascadeClassifier("C:\\OpenCV\\opencv\\sources\\data\\lbpcascades\\lbpcascade_frontalface.xml");
        //定义人脸检测
        MatOfRect faceDetections = new MatOfRect();
        //获取图像所在的文件夹
        File file = new File(filePath);
        //遍历文件夹中的所有图片
        for (File f : file.listFiles()) {
     
            //读取图片
            Mat image = Imgcodecs.imread(f.getAbsolutePath());
            //对图像进行人脸检测
            faceDetector.detectMultiScale(image, faceDetections);
            //获取人脸区域
            Rect[] rects = faceDetections.toArray();

            //存在人脸
            if (faceDetections.toArray().length > 0) {
     
                //获得人脸区域
                for (Rect rect : rects) {
     
                    Mat mat = new Mat(rect.width, rect.width, CvType.CV_8UC3);
                    for (int i = rect.x; i < rect.x + rect.width; i++) {
     
                        for (int j = rect.y; j < rect.y + rect.height; j++) {
     
                            mat.put(j - rect.y, i - rect.x, image.get(j, i));
                        }
                    }
                    //截取人脸区域
                    Imgproc.resize(mat, mat, new Size(64, 64), 0, 0, 0);
                    //保存图像
                    Imgcodecs.imwrite("./genki4k/files1/" + f.getName(), mat);
                }
            }
        }
        System.out.println("读取人脸完毕");
    }

    /**
     * 把图片进行随机分组,并且设置图片的标签
     *
     * @param filePath
     */
    public static void getRandomFile(String filePath) {
     
        System.out.println("图片随机分组......");
        File file = new File(filePath);
        File listFile[] = file.listFiles();
        Random random = new Random();
        //初始化标签
        //前2162为笑脸
        for (int i = 0; i < 3670; i++) {
     
            String no = listFile[i].getName().substring(4, 8);
            if (Integer.parseInt(no) <= 2162)
                flags[Integer.parseInt(no)] = 1;
            else
                flags[(Integer.parseInt(no))] = 0;
        }
        //随机排列,连同标签一起排列
        for (int i = 0; i < 3670; i++) {
     
            int r = random.nextInt(i + 1);
            File tmp = listFile[i];
            listFile[i] = listFile[r];
            listFile[r] = tmp;
            int t = flags[i];
            flags[i] = flags[r];
            flags[r] = t;
        }
        //分组
        for (int i = 0; i < 10; i++) {
     
            for (int j = 0; j < 367; j++) {
     
                randomFiles[i][j] = listFile[i * 367 + j];
            }
        }
        System.out.println("图片随机分组完毕");
    }

    /**
     * 十折方式处理初始数据
     */
    public static void handleFace() {
     
        //最好得分
        double best = 0;
        //最好得分模型
        int index = 0;
        System.out.println("处理人脸......");
        int k;
        for (int t = 0; t < 10; t++) {
     
            k = 0;
            getRandomFile("./genki4k/files1/");
            for (int i = 0; i < 10; i++) {
     
                for (int j = 0; j < 367; j++) {
     
                    //读取图片
                    Mat originalImage = Imgcodecs.imread(randomFiles[i][j].getAbsolutePath());
                    //获取HOG值
                    inputArr[k] = getHOG(originalImage);
                    hogs[i][j] = inputArr[k];
                    //获得标签
                    String no = randomFiles[i][j].getName().substring(4, 8);
                    resultFlag[k] = flags[Integer.parseInt(no)];
                    result[i][j] = flags[Integer.parseInt(no)];
                    k++;
                }
            }
            //训练该组数据
            train(t);
            double res = test(t);
            if (res > best) {
     
                best = res;
                index = t;
            }
        }

        //把最好的模型单独复制一份
        File src = new File("./output/result" + index + ".xml");
        File dst = new File("./output/best.xml");
        try {
     
            FileInputStream inputStream = new FileInputStream(src);
            FileOutputStream outputStream = new FileOutputStream(dst);
            // 得到源文件通道
            FileChannel inputChannel = inputStream.getChannel();
            // 得到目标文件通道
            FileChannel outputChannel = outputStream.getChannel();
            //将源文件数据通达连通到目标文件通道进行传输
            inputChannel.transferTo(0, inputChannel.size(), outputChannel);
        } catch (FileNotFoundException e) {
     
            e.printStackTrace();
        } catch (IOException e) {
     
            e.printStackTrace();
        }
        System.out.println("处理人脸完毕");
    }


    public static float[] getHOG(Mat src) {
     
        //灰度以及梯度处理
        src = getGradient(src);
        //图像缩放
        Imgproc.resize(src, src, new Size(64, 128), 0, 0, Imgproc.INTER_AREA);
        HOGDescriptor hogDescriptor = new HOGDescriptor();
        MatOfFloat descriptorsValues = new MatOfFloat();
        MatOfPoint locations = new MatOfPoint();
        //计算 Hog 特征值
        hogDescriptor.compute(src, descriptorsValues, new Size(0, 0), new Size(0, 0), locations);
        //特征值维数
        int size = (int) (descriptorsValues.total() * descriptorsValues.channels());
        float[] temp = new float[size];
        descriptorsValues.get(0, 0, temp);
        //特征数组
        float[] resArrays = descriptorsValues.toArray();
        return resArrays;
    }


    public static Mat getGradient(Mat src) {
     
        Mat grayMat = new Mat();
        // 灰度
        Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY, 0);
        //梯度计算
        Mat x = new Mat(); //X 方向梯度值矩阵
        Mat y = new Mat(); //Y 方向梯度值矩阵
        //计算 X,Y 方向绝对梯度
        Imgproc.Sobel(grayMat, x, -1, 1, 0, 3, 1, 0);
        Core.convertScaleAbs(x, x);
        Imgproc.Sobel(grayMat, y, -1, 0, 1, 3, 1, 0);
        Core.convertScaleAbs(y, y);
        //XY 综合梯度
        Core.addWeighted(x, 0.5, y, 0.5, 0, grayMat);

        return grayMat;
    }

    public static void train(int t) {
     
        System.out.println("开始训练第" + (t + 1) + "组");
        //定义 SVM 对象
        SVM svm = SVM.create();
        //训练数据 Hog 特征值
        Mat dataMat = new Mat();
        //标签数据
        Mat labelMat = new Mat();
        //SVM类型
        svm.setType(SVM.C_SVC);
        //线性
        svm.setKernel(SVM.LINEAR);
        //迭代终止条件
        svm.setTermCriteria(new TermCriteria(TermCriteria.MAX_ITER, 20000, 1e-6));

        //数据大小
        int size = inputArr[0].length;

        //创建数据和标签对象,训练九组
        dataMat.create(3303, size, CvType.CV_32FC1);
        labelMat.create(3303, 1, CvType.CV_32SC1);

        //加载数据以及标签
        for (int i = 0; i < 3303; i++) {
     
            for (int j = 0; j < size; j++) {
     
                dataMat.put(i, j, inputArr[i][j]);
            }
            labelMat.put(i, 0, resultFlag[i]);
        }
        System.out.println("训练......");
        svm.train(dataMat, Ml.ROW_SAMPLE, labelMat);
        System.out.println("训练完毕");

        System.out.println("保存训练模型......");
        svm.save("./output/result" + t + ".xml"); //保存 SVM 模型
        dataMat.release(); //释放变量
        labelMat.release(); //释放变量
        System.out.println("保存训练模型完毕");
    }

    public static double test(int i) {
     
        System.out.println("开始测试");
        System.out.println("开始测试第" + (i + 1) + "组模型......");
        //创建 SVM 模型对象
        SVM svm = SVM.load("./output/result" + i + ".xml");
        // TP 为正样本,并且检测结果为正样本
        // FP 为正样本,并且检测结果为负样本
        // TN 为负样本,并且检测结果为负样本
        // FN 为负样本,并且检测结果为正样本
        double TP = 0;
        double FP = 0;
        double TN = 0;
        double FN = 0;

        for (int k = 0; k < 367; k++) {
     
            Mat testMat = new Mat();
            int size=hogs[i][k].length;
            testMat.create(1,size , CvType.CV_32FC1);
            for (int p = 0; p < size; p++) {
     
                testMat.put(0, p, hogs[i][k][p]);
            }
            float predict = svm.predict(testMat); //预测结果
            if (result[i][k] == 1 && predict == 1) {
     
                TP++; //TP 自增 1
            } else if (result[i][k] == 1 && predict == 0) {
     
                FP++; //FP 自增 1
            } else if (result[i][k] == 0 && predict == 0) {
     
                TN++; //TN 自增 1
            } else if (result[i][k] == 0 && predict == 1) {
     
                FN++; //FN 自增 1
            }
        }
        System.out.println("第" + (i + 1) + "组模型训练完毕");
        //计算正确率
        System.out.println("第" + (i + 1) + "组模型训练结果");
        System.out.println("TP: " + TP + "\t FP: " + FP + "\t TN: " + TN + "\t FN: " + FN);
        System.out.println("准确率:" + 2 * TP / (2 * TP + FP + FN));
        System.out.println();
        System.out.println("测试完毕");
        return 2 * TP / (2 * TP + FP + FN);
    }
}

SmilePredict

package smile;

import org.opencv.core.Point;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.ml.SVM;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;


import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.Scanner;


public class SmilePredict {
     
    //窗体
    static JFrame frame;
    //显示图片
    static JLabel label;
    static boolean flag = true;

    // 第0个flag表示是否正在预测
    // 第1个flag表示预测结果是否为smile
    // 第2个flag表示是否保存当前图片
    static final boolean[] flags = {
     false, false, false};

    public static void main(String args[]) {
     
        //加载 opencv 配置文件
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        //创建Opencv中的视频捕捉对象
        VideoCapture camera = new VideoCapture();
        //open函数中的0代表当前计算机中索引为0的摄像头
        camera.open(0);
        //isOpened函数用来判断摄像头调用是否成功
        if (!camera.isOpened()) {
     
            System.out.println("Camera Error");
        } else {
     
            Mat img = new Mat();//创建一个输出帧
            //记录以及保存的图片数量
            int count =0;
            //初始化窗口
            frame = createJFrame("windowName", 640, 480);
            //官方人脸检测模型
            CascadeClassifier faceDetector = new CascadeClassifier("C:\\OpenCV\\opencv\\sources\\data\\lbpcascades\\lbpcascade_frontalface_improved.xml");
            //定义人脸检测
            MatOfRect faceDetections = new MatOfRect();

            //循环读取摄像头内容
            while (flag) {
     
                camera.read(img);//read方法读取摄像头的当前帧
                faceDetector.detectMultiScale(img, faceDetections);
                //获取人脸区域
                Rect[] rects = faceDetections.toArray();
                //存在人脸
                if (faceDetections.toArray().length > 0) {
     
                    //获得人脸区域
                    for (Rect rect : rects) {
     
                        Mat mat = new Mat(rect.width, rect.width, CvType.CV_8UC3);
                        for (int i = rect.x; i < rect.x + rect.width; i++) {
     
                            for (int j = rect.y; j < rect.y + rect.height; j++) {
     
                                mat.put(j - rect.y, i - rect.x, img.get(j, i));
                            }
                        }

                        //标注人脸区域
                        //如果是smile则为绿色,否则为红色
                        if(flags[1])
                            Imgproc.rectangle(img, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height),
                                    new Scalar(0, 255, 0), 2);
                        else
                            Imgproc.rectangle(img, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height),
                                    new Scalar(0, 0, 255), 2);
                        //是否保存当前图片
                        if(flags[2]){
     
                            //smile or non smile
                            if(flags[1])
                                Imgcodecs.imwrite("./output/smile/" +count+++".jpg", img);
                            else
                                Imgcodecs.imwrite("./output/non_smile/" +count+++".jpg", img);
                            flags[2]=false;
                        }

                        //截取人脸区域用于预测
                        Imgproc.resize(mat, mat, new Size(64, 64), 0, 0, 0);


                        //如果当前没有在预测则新开线程进行预测
                        if (!flags[0]) {
     
                            new Thread(new Runnable() {
     
                                @Override
                                public void run() {
     
                                    flags[0] = true;
                                    //获取hog特征值
                                    float hog[] = SmileTrain.getHOG(mat);
                                    //预测结果
                                    if (predict(hog)) {
     
                                        flags[1] = true;
                                    } else {
     
                                        flags[1] = false;
                                    }
                                    flags[0] = false;
                                }
                            }).start();
                        }
                        //根据是否为smile显示不同文字
                        if (flags[1]) {
     
                            Imgproc.putText(img, "smile", new Point(rect.x, rect.y), Imgproc.FONT_HERSHEY_COMPLEX, 2, new Scalar(0, 255, 0));
                        } else {
     
                            Imgproc.putText(img, "no smile", new Point(rect.x, rect.y), Imgproc.FONT_HERSHEY_COMPLEX, 2, new Scalar(0, 0, 255));
                        }
                    }
                }
                //重新加载窗口图片
                Image loadedImage = toBufferedImage(img);
                label.setIcon(new ImageIcon(loadedImage));
                try {
     
                    Thread.sleep(100);//线程暂停100ms
                } catch (InterruptedException e) {
     
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }
        System.exit(0);
    }

    //把Mat转为Image用于显示
    public static Image toBufferedImage(Mat matrix) {
     
        int type = BufferedImage.TYPE_BYTE_GRAY;
        if (matrix.channels() > 1) {
     
            type = BufferedImage.TYPE_3BYTE_BGR;
        }
        int bufferSize = matrix.channels() * matrix.cols() * matrix.rows();
        byte[] buffer = new byte[bufferSize];
        matrix.get(0, 0, buffer); // 获取所有的像素点
        BufferedImage image = new BufferedImage(matrix.cols(), matrix.rows(), type);
        final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
        System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
        return image;
    }


    //创建窗口
    public static JFrame createJFrame(String windowName, int width, int height) {
     
        JFrame frame = new JFrame(windowName);
        label = new JLabel();
        final JScrollPane imageScrollPane = new JScrollPane(label);
        //滚动条
        imageScrollPane.setPreferredSize(new Dimension(width, height));
        frame.add(imageScrollPane, BorderLayout.CENTER);
        //自适应大小
        frame.pack();
        //设置窗口位置
        frame.setLocationRelativeTo(null);
        //窗口可见
        frame.setVisible(true);
        //关闭事件
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用户点击窗口关闭
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        //设置键盘监听
        frame.addKeyListener(new KeyListener() {
     
            @Override
            public void keyTyped(KeyEvent e) {
     

            }

            @Override
            public void keyPressed(KeyEvent e) {
     
                System.out.println(e.getKeyCode());
                //按下s保存
                if(e.getKeyCode()==83){
     
                    flags[2]=true;
                }
                //按下esc退出
                else if(e.getKeyCode()==27){
     
                    flag=false;
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {
     

            }
        });
        return frame;
    }

    public static boolean predict(float hog[]){
     
        SVM svm = SVM.load("./output/best.xml");
        Mat predictMat = new Mat();
        predictMat.create(1, hog.length, CvType.CV_32FC1);
        predictMat.put(0, 0, hog);
        float results = svm.predict(predictMat); //预测结果
        if(results==1)
            return true;
        return false;
    }

}

五、总结

通过hog特征值配合svm实现简单笑脸检测。当图片预处理的好的时候准确率很大有提升。

六、参考

https://blog.csdn.net/qq_34814092/article/details/103205269
https://blog.csdn.net/zmx729618/article/details/78113268
添加链接描述

你可能感兴趣的:(人工智能,OpenCv,opencv,java,支持向量机)