首先准备好图片,所有图片都是统一大小。
- 我为了训练速度更快一些,制作了 20*15 像素的图片,这样做只是为了练习方法的使用,图片太小会丢失很多细节。
- 我使用的是 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("目标不符合");
}
}
}