Opencv 机器学习 ---- 完整的图片训练例子

准备

首先准备好图片,所有图片都是统一大小。

  1. 我为了训练速度更快一些,制作了 20*15 像素的图片,这样做只是为了练习方法的使用,图片太小会丢失很多细节。
  2. 我使用的是 ACDSee 制作的图片样本,这个软件的批量改大小确实很好用。

样本展示

所有图片均来自网络

正样本:
图片名称从左往右依次:0.jpg, 1.jpg, 2.jpg, 3.jpg

负样本:
图片名称从左往右依次:4.jpg, 5.jpg, 6.jpg, 7.jpg, 8.jpg, 9.jpg, 10.jpg, 11.jpg, 12.jpg, 13.jpg, 14.jpg

代码

训练代码

public class Test_02 {

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    /**
     * @author idmin
     * 样本的数量
     */
    private  static final int SAMPLE_NUMBER=18; 

    public static void main(String[] args) {
        //用于存放所有样本矩阵
        Mat trainingDataMat = null;

        //标记:正样本用 0 表示,负样本用 1 表示。
        //图片命名:
        //正样本: 0.jpg  1.jpg  2.jpg  3.jpg  4.jpg  
        //负样本:5.jpg  6.jpg  7.jpg   ...   17.jpg
        int labels[]  = {0,0,0,0,
                           1,1,1,1,1,1,1,
                           1,1,1,1,1,1,1};

        //存放标记的Mat,每个图片都要给一个标记。SAMPLE_NUMBER 是自己定义的图片数量
        Mat labelsMat = new Mat(SAMPLE_NUMBER,1,CvType.CV_32SC1);
        labelsMat.put(0, 0, labels);

        //这里的意思是,trainingDataMat 存放18张图片的矩阵,trainingDataMat 的每一行存放一张图片的矩阵。
        for(int i = 0;i"D:\\xunlian\\a\\" + i + ".jpg" ;
            Mat src = Imgcodecs.imread(path);

            //创建一个行数为18(正负样本总数量为18),列数为 rows*cols 的矩阵
            if(trainingDataMat == null) {
                trainingDataMat = new Mat(SAMPLE_NUMBER, src.rows()*src.cols(),CvType.CV_32FC1);// CV_32FC1 是规定的训练用的图片格式。
            }

            //转成灰度图并检测边缘
            //这里是为了过滤不需要的特征,减少训练时间。实际处理按情况论。
            Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
            Mat dst = new Mat(src.rows(),src.cols(),src.type());//此时的 dst 是8u1c。
            Imgproc.Canny(src, dst, 130, 250);

            //转成数组再添加。
            //失败案例:这里我试图用 get(row,col,data)方法获取数组,但是结果和这个结果不一样,原因未知。
            float[] arr =new float[dst.rows()*dst.cols()];
            int l=0;
            for (int j=0;jfor(int k=0;kdouble[] a=dst.get(j, k);
                    arr[l]=(float)a[0];
                    l++;
                }
            } 
            trainingDataMat.put(i, 0, arr);
        }

        //每次训练的结果得到的 xml 文件都不一样,原因未知。猜测是我的样本数太太太太小
//      MySvm(trainingDataMat, labelsMat, "./Result/a.xml");
//      MySvm(trainingDataMat, labelsMat, "./Result/b.xml");
//      MySvm(trainingDataMat, labelsMat, "./Result/c.xml");
//      MySvm(trainingDataMat, labelsMat, "./Result/d.xml");
//      MySvm(trainingDataMat, labelsMat, "./Result/e.xml");
        MySvm(trainingDataMat, labelsMat, "./Result/f.xml");
    }

    /**
     * SVM 支持向量机
     * @param trainingDataMat 存放样本的矩阵
     * @param labelsMat 存放标识的矩阵
     * @param savePath 保存路径 。例如:d:/svm.xml
     */
    public static void MySvm(Mat trainingDataMat, Mat labelsMat, String savePath) {

        SVM svm = SVM.create();
        // 配置SVM训练器参数
        TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER, 1000, 0);
        svm.setTermCriteria(criteria);// 指定
        svm.setKernel(SVM.LINEAR);// 使用预先定义的内核初始化
        svm.setType(SVM.C_SVC); // SVM的类型,默认是:SVM.C_SVC
        svm.setGamma(0.5);// 核函数的参数
        svm.setNu(0.5);// SVM优化问题参数
        svm.setC(1);// SVM优化问题的参数C

        TrainData td = TrainData.create(trainingDataMat, Ml.ROW_SAMPLE, labelsMat);// 类封装的训练数据
        boolean success = svm.train(td.getSamples(), Ml.ROW_SAMPLE, td.getResponses());// 训练统计模型
        System.out.println("Svm training result: " + success);
        svm.save(savePath);// 保存模型
    }
}

预测代码

public class Pridect {

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    public static void main(String[] args) {
        Mat src = Imgcodecs.imread("D:\\xunlian\\a\\0.jpg");//图片大小要和样本一致
        Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
        Mat dst = new Mat();
        Imgproc.Canny(src, dst, 40, 200);
        test(dst);
    }

    public static void test(Mat src) {
        SVM svm = SVM.load("./Result/a.xml");//加载训练得到的 xml

        Mat samples = new Mat(1,src.cols()*src.rows(),CvType.CV_32FC1);

        //转换 src 图像的 cvtype
        //失败案例:我试图用 src.convertTo(src, CvType.CV_32FC1); 转换,但是失败了,原因未知。猜测: 内部的数据类型没有转换?
        float[] dataArr = new float[src.cols()*src.rows()];
        for(int i =0,f = 0 ;ifor(int j = 0;jfloat pixel = (float)src.get(i, j)[0];
                dataArr[f] = pixel;
                f++;
            }
        }
        samples.put(0, 0, dataArr);

        //预测用的方法,返回定义的标识。
//      int labels[]  = {9,9,9,9,
//                 1,1,1,1,1,1,1,
//                 1,1,1,1,1,1,1};
//      如果训练时使用这个标识,那么符合的图像会返回9.0
        float flag = svm.predict(samples);

        System.out.println("预测结果:"+flag);
        if(flag == 0) {
            System.out.println("目标符合");
        }
        if(flag == 1) {
            System.out.println("目标不符合");
        }
    }
}

你可能感兴趣的:(OpenCV)