no commit message

devA
yuxue 5 years ago
parent 2d0ba56295
commit 474df80320

@ -298,6 +298,7 @@ public class CharsSegment {
/**
* RectA7003XA
*
* @param vecRect
* @return
*/

@ -16,15 +16,17 @@ import org.opencv.ml.Ml;
import org.opencv.ml.TrainData;
import com.yuxue.constant.Constant;
import com.yuxue.enumtype.Direction;
import com.yuxue.util.FileUtil;
import com.yuxue.util.PlateUtil;
/**
* org.opencv
*
*
*
*
*
*
*
* ann.xml
* 1res/model/ann.xml
@ -48,75 +50,6 @@ public class ANNTrain {
private static final String MODEL_PATH = DEFAULT_PATH + "ann.xml";
public static float[] projectedHistogram(final Mat img, Direction direction) {
int sz = 0;
switch (direction) {
case HORIZONTAL:
sz = img.rows();
break;
case VERTICAL:
sz = img.cols();
break;
default:
break;
}
// 统计这一行或一列中非零元素的个数并保存到nonZeroMat中
float[] nonZeroMat = new float[sz];
Core.extractChannel(img, img, 0);
for (int j = 0; j < sz; j++) {
Mat data = (direction == Direction.HORIZONTAL) ? img.row(j) : img.col(j);
int count = Core.countNonZero(data);
nonZeroMat[j] = count;
}
// Normalize histogram
float max = 0;
for (int j = 0; j < nonZeroMat.length; ++j) {
max = Math.max(max, nonZeroMat[j]);
}
if (max > 0) {
for (int j = 0; j < nonZeroMat.length; ++j) {
nonZeroMat[j] /= max;
}
}
return nonZeroMat;
}
public Mat features(Mat in, int sizeData) {
float[] vhist = projectedHistogram(in, Direction.VERTICAL);
float[] hhist = projectedHistogram(in, Direction.HORIZONTAL);
Mat lowData = new Mat();
if (sizeData > 0) {
Imgproc.resize(in, lowData, new Size(sizeData, sizeData));
}
int numCols = vhist.length + hhist.length + lowData.cols() * lowData.rows();
Mat out = new Mat(1, numCols, CvType.CV_32F);
int j = 0;
for (int i = 0; i < vhist.length; ++i, ++j) {
out.put(0, j, vhist[i]);
}
for (int i = 0; i < hhist.length; ++i, ++j) {
out.put(0, j, hhist[i]);
}
for (int x = 0; x < lowData.cols(); x++) {
for (int y = 0; y < lowData.rows(); y++, ++j) {
double[] val = lowData.get(x, y);
out.put(0, j, val[0]);
}
}
return out;
}
/**
*
* @param inMat
@ -170,7 +103,6 @@ public class ANNTrain {
/**
*
* @param img
@ -221,24 +153,24 @@ public class ANNTrain {
for (int j = 0; j < count; j++) {
Mat img = Imgcodecs.imread(files.get(rand.nextInt(files.size() - 1)), 0);
Mat f = features(img, _predictsize);
Mat f = PlateUtil.features(img, _predictsize);
samples.push_back(f);
trainingLabels.add(i); // 每一幅字符图片所对应的字符类别索引下标
// 增加随机平移样本
samples.push_back(features(randTranslate(img), _predictsize));
samples.push_back(PlateUtil.features(randTranslate(img), _predictsize));
trainingLabels.add(i);
// 增加随机旋转样本
samples.push_back(features(randRotate(img), _predictsize));
samples.push_back(PlateUtil.features(randRotate(img), _predictsize));
trainingLabels.add(i);
// 增加膨胀样本
/*samples.push_back(features(dilate(img), _predictsize));
/*samples.push_back(PlateUtil.features(dilate(img), _predictsize));
trainingLabels.add(i);*/
// 增加腐蚀样本
/*samples.push_back(features(erode(img), _predictsize));
/*samples.push_back(PlateUtil.features(erode(img), _predictsize));
trainingLabels.add(i); */
}
}
@ -252,24 +184,24 @@ public class ANNTrain {
int count = 200; // 控制从训练样本中,抽取指定数量的样本
for (int j = 0; j < count; j++) {
Mat img = Imgcodecs.imread(files.get(rand.nextInt(files.size() - 1)), 0);
Mat f = features(img, _predictsize);
Mat f = PlateUtil.features(img, _predictsize);
samples.push_back(f);
trainingLabels.add(i + Constant.numCharacter);
// 增加随机平移样本
samples.push_back(features(randTranslate(img), _predictsize));
samples.push_back(PlateUtil.features(randTranslate(img), _predictsize));
trainingLabels.add(i + Constant.numCharacter);
// 增加随机旋转样本
samples.push_back(features(randRotate(img), _predictsize));
samples.push_back(PlateUtil.features(randRotate(img), _predictsize));
trainingLabels.add(i + Constant.numCharacter);
// 增加膨胀样本
/*samples.push_back(features(dilate(img), _predictsize));
/*samples.push_back(PlateUtil.features(dilate(img), _predictsize));
trainingLabels.add(i + Constant.numCharacter);*/
// 增加腐蚀样本
samples.push_back(features(erode(img), _predictsize));
samples.push_back(PlateUtil.features(erode(img), _predictsize));
trainingLabels.add(i + Constant.numCharacter);
}
}
@ -318,7 +250,7 @@ public class ANNTrain {
String plate = "";
for (String string : files) {
Mat img = Imgcodecs.imread(string, 0);
Mat f = features(img, Constant.predictSize);
Mat f = PlateUtil.features(img, Constant.predictSize);
int index = 0;
double maxVal = -2;
@ -333,7 +265,7 @@ public class ANNTrain {
}
// 随机平移
/*f = features(randTranslate(img), Constant.predictSize);
/*f = PlateUtil.features(randTranslate(img), Constant.predictSize);
ann.predict(f, output); // 预测结果
for (int j = 0; j < Constant.numAll; j++) {
double val = output.get(0, j)[0];
@ -344,7 +276,7 @@ public class ANNTrain {
}*/
// 随机旋转
/*f = features(randRotate(img), Constant.predictSize);
/*f = PlateUtil.features(randRotate(img), Constant.predictSize);
ann.predict(f, output); // 预测结果
for (int j = 0; j < Constant.numAll; j++) {
double val = output.get(0, j)[0];
@ -355,7 +287,7 @@ public class ANNTrain {
}*/
// 膨胀
/*f = features(dilate(img), Constant.predictSize);
/*f = PlateUtil.features(dilate(img), Constant.predictSize);
ann.predict(f, output); // 预测结果
for (int j = 0; j < Constant.numAll; j++) {
double val = output.get(0, j)[0];
@ -366,7 +298,7 @@ public class ANNTrain {
}*/
// 腐蚀 -- 识别中文字符效果会好一点,识别数字及字母效果会更差
/*f = features(erode(img), Constant.predictSize);
/*f = PlateUtil.features(erode(img), Constant.predictSize);
ann.predict(f, output); // 预测结果
for (int j = 0; j < Constant.numAll; j++) {
double val = output.get(0, j)[0];
@ -392,7 +324,8 @@ public class ANNTrain {
ANNTrain annT = new ANNTrain();
// 这里演示只训练model文件夹下的ann.xml此模型是一个predictSize=10,neurons=40的ANN模型
// 可根据需要训练不同的predictSize或者neurons的ANN模型
// 根据机器的不同训练时间不一样但一般需要10分钟左右所以慢慢等一会吧。
// 根据机器的不同训练时间不一样但一般需要10分钟左右所以慢慢等一会吧
// 可以考虑中文,数字字母分开训练跟识别,提高准确性
annT.train(Constant.predictSize, Constant.neurons);
annT.predict();

@ -1,159 +0,0 @@
package com.yuxue.train;
import java.util.Vector;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_ml.*;
import org.bytedeco.javacpp.opencv_imgcodecs;
import org.bytedeco.javacpp.opencv_core.Mat;
import com.yuxue.constant.Constant;
import com.yuxue.easypr.core.CoreFunc;
import com.yuxue.util.FileUtil;
/**
* org.bytedeco.javacpp
*
*
*
*
* ann.xml
* 1res/model/ann.xml
* 2com.yuxue.easypr.core.CharsIdentify.charsIdentify(Mat, Boolean, Boolean)
*
* @author yuxue
* @date 2020-05-14 22:16
*/
public class ANNTrain1 {
private ANN_MLP ann = ANN_MLP.create();
// 默认的训练操作的根目录
private static final String DEFAULT_PATH = "D:/PlateDetect/train/chars_recognise_ann/";
// 训练模型文件保存位置
private static final String MODEL_PATH = "res/model/ann.xml";
public void train(int _predictsize, int _neurons) {
Mat samples = new Mat(); // 使用push_back行数列数不能赋初始值
Vector<Integer> trainingLabels = new Vector<Integer>();
// 加载数字及字母字符
for (int i = 0; i < Constant.numCharacter; i++) {
String str = DEFAULT_PATH + "learn/" + Constant.strCharacters[i];
Vector<String> files = new Vector<String>();
FileUtil.getFiles(str, files);
int size = (int) files.size();
for (int j = 0; j < size; j++) {
Mat img = opencv_imgcodecs.imread(files.get(j), 0);
// System.err.println(files.get(j)); // 文件名不能包含中文
Mat f = CoreFunc.features(img, _predictsize);
samples.push_back(f);
trainingLabels.add(i); // 每一幅字符图片所对应的字符类别索引下标
}
}
// 加载汉字字符
for (int i = 0; i < Constant.strChinese.length; i++) {
String str = DEFAULT_PATH + "learn/" + Constant.strChinese[i];
Vector<String> files = new Vector<String>();
FileUtil.getFiles(str, files);
int size = (int) files.size();
for (int j = 0; j < size; j++) {
Mat img = opencv_imgcodecs.imread(files.get(j), 0);
// System.err.println(files.get(j)); // 文件名不能包含中文
Mat f = CoreFunc.features(img, _predictsize);
samples.push_back(f);
trainingLabels.add(i + Constant.numCharacter);
}
}
//440 vhist.length + hhist.length + lowData.cols() * lowData.rows();
// CV_32FC1 CV_32SC1 CV_32F
Mat classes = new Mat(trainingLabels.size(), Constant.numAll, CV_32F);
float[] labels = new float[trainingLabels.size()];
for (int i = 0; i < labels.length; ++i) {
classes.ptr(i, trainingLabels.get(i)).putFloat(1.f);
}
// samples.type() == CV_32F || samples.type() == CV_32S
TrainData train_data = TrainData.create(samples, ROW_SAMPLE, classes);
ann.clear();
Mat layers = new Mat(1, 3, CV_32SC1);
layers.ptr(0, 0).putInt(samples.cols());
layers.ptr(0, 1).putInt(_neurons);
layers.ptr(0, 2).putInt(classes.cols());
System.out.println(layers);
ann.setLayerSizes(layers);
ann.setActivationFunction(ANN_MLP.SIGMOID_SYM, 1, 1);
ann.setTrainMethod(ANN_MLP.BACKPROP);
TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER, 30000, 0.0001);
ann.setTermCriteria(criteria);
ann.setBackpropWeightScale(0.1);
ann.setBackpropMomentumScale(0.1);
ann.train(train_data);
//FileStorage fsto = new FileStorage(MODEL_PATH, FileStorage.WRITE);
//ann.write(fsto, "ann");
ann.save(MODEL_PATH);
}
public void predict() {
ann.clear();
ann = ANN_MLP.load(MODEL_PATH);
//ann = ANN_MLP.loadANN_MLP(MODEL_PATH, "ann");
Vector<String> files = new Vector<String>();
FileUtil.getFiles(DEFAULT_PATH + "test/", files);
for (String string : files) {
Mat img = opencv_imgcodecs.imread(string);
Mat f = CoreFunc.features(img, Constant.predictSize);
// 140 predictSize = 10; vhist.length + hhist.length + lowData.cols() * lowData.rows();
// 440 predictSize = 20;
Mat output = new Mat(1, 140, CV_32F);
//ann.predict(f, output, 0); // 预测结果
// System.err.println(string + "===>" + (int) ann.predict(f, output, 0));
int index = (int) ann.predict(f, output, 0);
String result = "";
if (index < Constant.numCharacter) {
result = String.valueOf(Constant.strCharacters[index]);
} else {
String s = Constant.strChinese[index - Constant.numCharacter];
result = Constant.KEY_CHINESE_MAP.get(s); // 编码转中文
}
System.err.println(string + "===>" + result);
// ann.predict(f, output, 0);
// System.err.println(string + "===>" + output.get(0, 0)[0]);
}
}
public static void main(String[] args) {
ANNTrain1 annT = new ANNTrain1();
// 这里演示只训练model文件夹下的ann.xml此模型是一个predictSize=10,neurons=40的ANN模型
// 可根据需要训练不同的predictSize或者neurons的ANN模型
// 根据机器的不同训练时间不一样但一般需要10分钟左右所以慢慢等一会吧。
annT.train(Constant.predictSize, Constant.neurons);
annT.predict();
System.out.println("The end.");
}
}

@ -0,0 +1,228 @@
package com.yuxue.train;
import java.io.File;
import java.util.Random;
import java.util.Vector;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.TermCriteria;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.ml.ANN_MLP;
import org.opencv.ml.Ml;
import org.opencv.ml.TrainData;
import com.yuxue.constant.Constant;
import com.yuxue.util.FileUtil;
import com.yuxue.util.PlateUtil;
/**
* org.opencv
*
*
*
*
*
* @author yuxue
* @date 2020-07-02 22:16
*/
public class CnANNTrain {
private ANN_MLP ann = ANN_MLP.create();
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
// 默认的训练操作的根目录
private static final String DEFAULT_PATH = "D:/PlateDetect/train/chars_recognise_ann/";
// 训练模型文件保存位置
private static final String MODEL_PATH = DEFAULT_PATH + "ann_cn.xml";
public void train(int _predictsize, int _neurons) {
Mat samples = new Mat(); // 使用push_back行数列数不能赋初始值
Vector<Integer> trainingLabels = new Vector<Integer>();
Random rand = new Random();
// 加载汉字字符
for (int i = 0; i < Constant.strChinese.length; i++) {
String str = DEFAULT_PATH + "learn/" + Constant.strChinese[i];
Vector<String> files = new Vector<String>();
FileUtil.getFiles(str, files);
// int count = 300; // 控制从训练样本中,抽取指定数量的样本
int count = files.size(); // 不添加随机样本
for (int j = 0; j < count; j++) {
String filename = "";
if(j < files.size()) {
filename = files.get(j);
} else {
filename = files.get(rand.nextInt(files.size() - 1)); // 样本不足,随机重复提取已有的样本
}
Mat img = Imgcodecs.imread(filename, 0);
// 原图样本
samples.push_back(PlateUtil.features(img, _predictsize));
trainingLabels.add(i);
// 增加随机平移样本
samples.push_back(PlateUtil.features(PlateUtil.randTranslate(img), _predictsize));
trainingLabels.add(i);
// 增加随机旋转样本
samples.push_back(PlateUtil.features(PlateUtil.randRotate(img), _predictsize));
trainingLabels.add(i);
// 增加腐蚀样本
samples.push_back(PlateUtil.features(PlateUtil.erode(img), _predictsize));
trainingLabels.add(i);
}
}
samples.convertTo(samples, CvType.CV_32F);
//440 vhist.length + hhist.length + lowData.cols() * lowData.rows();
// CV_32FC1 CV_32SC1 CV_32F
Mat classes = Mat.zeros(trainingLabels.size(), Constant.strChinese.length, CvType.CV_32F);
float[] labels = new float[trainingLabels.size()];
for (int i = 0; i < labels.length; ++i) {
classes.put(i, trainingLabels.get(i), 1.f);
}
// samples.type() == CV_32F || samples.type() == CV_32S
TrainData train_data = TrainData.create(samples, Ml.ROW_SAMPLE, classes);
ann.clear();
Mat layers = new Mat(1, 3, CvType.CV_32F);
layers.put(0, 0, samples.cols()); // 样本特征数 140 10*10 + 20+20
layers.put(0, 1, _neurons); // 神经元个数
layers.put(0, 2, classes.cols()); // 字符数
ann.setLayerSizes(layers);
ann.setActivationFunction(ANN_MLP.SIGMOID_SYM, 1, 1);
ann.setTrainMethod(ANN_MLP.BACKPROP);
TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER, 30000, 0.0001);
ann.setTermCriteria(criteria);
ann.setBackpropWeightScale(0.1);
ann.setBackpropMomentumScale(0.1);
ann.train(train_data);
// FileStorage fsto = new FileStorage(MODEL_PATH, FileStorage.WRITE);
// ann.write(fsto, "ann");
ann.save(MODEL_PATH);
}
public void predict() {
ann.clear();
ann = ANN_MLP.load(MODEL_PATH);
int total = 0;
int correct = 0;
// 遍历测试样本下的所有文件,计算预测准确率
for (int i = 0; i < Constant.strChinese.length; i++) {
String strChinese = Constant.strChinese[i];
String path = DEFAULT_PATH + "learn/" + strChinese;
Vector<String> files = new Vector<String>();
FileUtil.getFiles(path, files);
for (String filePath : files) {
Mat img = Imgcodecs.imread(filePath, 0);
Mat f = PlateUtil.features(img, Constant.predictSize);
int index = 0;
double maxVal = -2;
Mat output = new Mat(1, Constant.strChinese.length, CvType.CV_32F);
ann.predict(f, output); // 预测结果
for (int j = 0; j < Constant.strChinese.length; j++) {
double val = output.get(0, j)[0];
if (val > maxVal) {
maxVal = val;
index = j;
}
}
// 腐蚀 -- 识别中文字符效果会好一点,识别数字及字母效果会更差
f = PlateUtil.features(PlateUtil.erode(img), Constant.predictSize);
ann.predict(f, output); // 预测结果
for (int j = 0; j < Constant.strChinese.length; j++) {
double val = output.get(0, j)[0];
if (val > maxVal) {
maxVal = val;
index = j;
}
}
String result = Constant.strChinese[index];
if(result.equals(strChinese)) {
correct++;
} else {
// 删除异常样本
/*File f1 = new File(filePath);
f1.delete();*/
System.err.print(filePath);
System.err.println("\t预测结果" + Constant.KEY_CHINESE_MAP.get(result));
}
total++;
}
}
System.out.print("total:" + total);
System.out.print("\tcorrect:" + correct);
System.out.print("\terror:" + (total - correct));
System.out.println("\t计算准确率为" + correct / (total * 1.0));
//预测结果:
//单字符100样本数 total:3230 correct:2725 error:505 计算准确率为0.8436532507739938
//单字符200样本数 total:3230 correct:2889 error:341 计算准确率为0.8944272445820434
//单字符300样本数 total:3230 correct:2943 error:287 计算准确率为0.9111455108359133
//单字符400样本数 total:3230 correct:2937 error:293 计算准确率为0.9092879256965944
//无随机样本 total:3230 correct:3050 error:180 计算准确率为0.9442724458204335
//无随机,删除异常样本 total:3050 correct:2987 error:63 计算准确率为0.979344262295082
//无随机,删除异常样本 total:2987 correct:2973 error:14 计算准确率为0.9953130231001004
//无随机,删除异常样本 total:2987 correct:2932 error:55 计算准确率为0.9815868764646802
//无随机,删除异常样本 total:2987 correct:2971 error:16 计算准确率为0.9946434549715434
// 个人测试多次之后,得出结论:
// 1、每个字符下样本数量不一致最多的299个样本最少的不到10个样本从测试结果来看样本太少会影响预测结果
// 2、这里的训练跟测试的样本都是基于相同的样本文件所以测试结果存在一定的局限性仅供参考
// 3、测试过程中使用了随机样本实际上发现重复样本对预测结果影响不大
// 4、中文字符分离出来之后预测准确性要高很多
// 5、随机平移、随机旋转、膨胀、腐蚀会增加样本数量同时增加预测准确性
// 6、每次重新训练后结果是不一致的没有重新训练多次使用样本预测结果是一致的
// 7、经过多次测试这里的训练方案跟预测结果准确率在90%左右
// 8、用于训练的样本尽量要多一点样本特征丰富一点这样子可以提高准确性但是用于预测的样本要尽量规范、正常
return;
}
public static void main(String[] args) {
CnANNTrain annT = new CnANNTrain();
// 这里演示只训练model文件夹下的ann.xml此模型是一个predictSize=10,neurons=40的ANN模型
// 可根据需要训练不同的predictSize或者neurons的ANN模型
// 根据机器的不同训练时间不一样但一般需要10分钟左右所以慢慢等一会吧
// 可以考虑中文,数字字母分开训练跟识别,提高准确性
annT.train(Constant.predictSize, Constant.neurons);
annT.predict();
System.out.println("The end.");
return;
}
}

@ -1,382 +0,0 @@
package com.yuxue.train;
import java.util.*;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_ml.*;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_imgcodecs;
import com.yuxue.easypr.core.Features;
import com.yuxue.easypr.core.SVMCallback;
import com.yuxue.util.Convert;
import com.yuxue.util.FileUtil;
/**
* org.bytedeco.javacpp
* JavaCPP Java 访 C++
*
*
*
*
* svm.xml
* 1res/model/svm.xml
* 2com.yuxue.easypr.core.PlateJudge.plateJudge(Mat)
*
* @author yuxue
* @date 2020-05-14 22:16
*/
public class SVMTrain1 {
private SVMCallback callback = new Features();
// 默认的训练操作的根目录
private static final String DEFAULT_PATH = "D:/PlateDetect/train/plate_detect_svm/";
// 训练模型文件保存位置
private static final String MODEL_PATH = DEFAULT_PATH + "svm.xml";
private static final String hasPlate = "HasPlate";
private static final String noPlate = "NoPlate";
public SVMTrain1() {
}
public SVMTrain1(SVMCallback callback) {
this.callback = callback;
}
/**
* learntain testhasPalte noPlate
* bound%30%
* @param bound
* @param name
*/
private void learn2Plate(float bound, final String name) {
final String filePath = DEFAULT_PATH + "learn/" + name;
Vector<String> files = new Vector<String>();
//// 获取该路径下的所有文件
FileUtil.getFiles(filePath, files);
int size = files.size();
if (0 == size) {
System.err.println("当前目录下没有文件: " + filePath);
return;
}
Collections.shuffle(files, new Random(new Date().getTime()));
//// 随机选取70%作为训练数据30%作为测试数据
int boundry = (int) (bound * size);
// 重新创建目录
FileUtil.recreateDir(DEFAULT_PATH + "train/" + name);
FileUtil.recreateDir(DEFAULT_PATH + "test/" + name);
for (int i = 0; i < boundry; i++) {
Mat img = opencv_imgcodecs.imread(files.get(i));
String str = DEFAULT_PATH + "train/" + name + "/" + name + "_" + Integer.valueOf(i).toString() + ".jpg";
opencv_imgcodecs.imwrite(str, img);
}
for (int i = boundry; i < size; i++) {
Mat img = opencv_imgcodecs.imread(files.get(i));
String str = DEFAULT_PATH + "test/" + name + "/" + name + "_" + Integer.valueOf(i).toString() + ".jpg";
opencv_imgcodecs.imwrite(str, img);
}
}
/**
*
* @param trainingImages
* @param trainingLabels
* @param name
*/
private void getPlateTrain(Mat trainingImages, Vector<Integer> trainingLabels, final String name, int label) {
// int label = 1;
final String filePath = DEFAULT_PATH + "train/" + name;
Vector<String> files = new Vector<String>();
// 获取该路径下的所有文件
FileUtil.getFiles(filePath, files);
int size = files.size();
if (null == files || size <= 0) {
System.out.println("File not found in " + filePath);
return;
}
for (int i = 0; i < size; i++) {
// System.out.println(files.get(i));
Mat inMat = opencv_imgcodecs.imread(files.get(i));
// 调用回调函数决定特征
// Mat features = this.callback.getHisteqFeatures(inMat);
Mat features = this.callback.getHistogramFeatures(inMat);
// 通过直方图均衡化后的彩色图进行预测
Mat p = features.reshape(1, 1);
p.convertTo(p, opencv_core.CV_32F);
// 136 36 14688 1 变换尺寸
// System.err.println(inMat.cols() + "\t" + inMat.rows() + "\t" + p.cols() + "\t" + p.rows());
trainingImages.push_back(p); // 合并成一张图片
trainingLabels.add(label);
}
}
private void getPlateTest(MatVector testingImages, Vector<Integer> testingLabels, final String name, int label) {
// int label = 1;
final String filePath = DEFAULT_PATH + "test/" + name;
Vector<String> files = new Vector<String>();
FileUtil.getFiles(filePath, files);
int size = files.size();
if (0 == size) {
System.out.println("File not found in " + filePath);
return;
}
System.out.println("get " + name + " test!");
for (int i = 0; i < size; i++) {
Mat inMat = opencv_imgcodecs.imread(files.get(i));
testingImages.push_back(inMat);
testingLabels.add(label);
}
}
// ! 测试SVM的准确率回归率以及FScore
public void getAccuracy(Mat testingclasses_preditc, Mat testingclasses_real) {
int channels = testingclasses_preditc.channels();
System.out.println("channels: " + Integer.valueOf(channels).toString());
int nRows = testingclasses_preditc.rows();
System.out.println("nRows: " + Integer.valueOf(nRows).toString());
int nCols = testingclasses_preditc.cols() * channels;
System.out.println("nCols: " + Integer.valueOf(nCols).toString());
int channels_real = testingclasses_real.channels();
System.out.println("channels_real: " + Integer.valueOf(channels_real).toString());
int nRows_real = testingclasses_real.rows();
System.out.println("nRows_real: " + Integer.valueOf(nRows_real).toString());
int nCols_real = testingclasses_real.cols() * channels;
System.out.println("nCols_real: " + Integer.valueOf(nCols_real).toString());
double count_all = 0;
double ptrue_rtrue = 0;
double ptrue_rfalse = 0;
double pfalse_rtrue = 0;
double pfalse_rfalse = 0;
for (int i = 0; i < nRows; i++) {
final float predict = Convert.toFloat(testingclasses_preditc.ptr(i));
final float real = Convert.toFloat(testingclasses_real.ptr(i));
count_all++;
// System.out.println("predict:" << predict).toString());
// System.out.println("real:" << real).toString());
if (predict == 1.0 && real == 1.0)
ptrue_rtrue++;
if (predict == 1.0 && real == 0)
ptrue_rfalse++;
if (predict == 0 && real == 1.0)
pfalse_rtrue++;
if (predict == 0 && real == 0)
pfalse_rfalse++;
}
System.out.println("count_all: " + Double.valueOf(count_all).toString());
System.out.println("ptrue_rtrue: " + Double.valueOf(ptrue_rtrue).toString());
System.out.println("ptrue_rfalse: " + Double.valueOf(ptrue_rfalse).toString());
System.out.println("pfalse_rtrue: " + Double.valueOf(pfalse_rtrue).toString());
System.out.println("pfalse_rfalse: " + Double.valueOf(pfalse_rfalse).toString());
double precise = 0;
if (ptrue_rtrue + ptrue_rfalse != 0) {
precise = ptrue_rtrue / (ptrue_rtrue + ptrue_rfalse);
System.out.println("precise: " + Double.valueOf(precise).toString());
} else {
System.out.println("precise: NA");
}
double recall = 0;
if (ptrue_rtrue + pfalse_rtrue != 0) {
recall = ptrue_rtrue / (ptrue_rtrue + pfalse_rtrue);
System.out.println("recall: " + Double.valueOf(recall).toString());
} else {
System.out.println("recall: NA");
}
if (precise + recall != 0) {
double F = (precise * recall) / (precise + recall);
System.out.println("F: " + Double.valueOf(F).toString());
} else {
System.out.println("F: NA");
}
}
/**
*
* @param dividePrepared
* @return
*/
public int svmTrain(boolean dividePrepared) {
Mat classes = new Mat();
Mat trainingData = new Mat();
Mat trainingImages = new Mat();
Vector<Integer> trainingLabels = new Vector<Integer>();
// 分割learn里的数据到train和test里 // 从库里面选取训练样本
if (!dividePrepared) {
learn2Plate(0.1f, hasPlate); // 性能不好的机器,最好不要挑选太多的样本,这个方案太消耗资源了。
learn2Plate(0.1f, noPlate);
}
// System.err.println("Begin to get train data to memory");
getPlateTrain(trainingImages, trainingLabels, hasPlate, 0);
getPlateTrain(trainingImages, trainingLabels, noPlate, 1);
// System.err.println(trainingImages.cols());
trainingImages.copyTo(trainingData);
trainingData.convertTo(trainingData, CV_32F);
int[] labels = new int[trainingLabels.size()];
for (int i = 0; i < trainingLabels.size(); ++i) {
labels[i] = trainingLabels.get(i).intValue();
}
new Mat(labels).copyTo(classes);
TrainData train_data = TrainData.create(trainingData, ROW_SAMPLE, classes);
SVM svm = SVM.create();
try {
TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER, 20000, 0.0001);
svm.setTermCriteria(criteria); // 指定
svm.setKernel(SVM.RBF); // 使用预先定义的内核初始化
svm.setType(SVM.C_SVC); // SVM的类型,默认是SVM.C_SVC
svm.setGamma(0.1); // 核函数的参数
svm.setNu(0.1); // SVM优化问题参数
svm.setC(1); // SVM优化问题的参数C
svm.setP(0.1);
svm.setDegree(0.1);
svm.setCoef0(0.1);
svm.trainAuto(train_data, 10,
SVM.getDefaultGrid(SVM.C),
SVM.getDefaultGrid(SVM.GAMMA),
SVM.getDefaultGrid(SVM.P),
SVM.getDefaultGrid(SVM.NU),
SVM.getDefaultGrid(SVM.COEF),
SVM.getDefaultGrid(SVM.DEGREE),
true);
} catch (Exception err) {
System.out.println(err.getMessage());
}
System.out.println("Svm generate done!");
/*FileStorage fsTo = new FileStorage(MODEL_PATH, FileStorage.WRITE);
svm.write(fsTo, "svm");*/
svm.save(MODEL_PATH);
return 0;
}
// 测试
public int svmPredict() {
SVM svm = SVM.create();
try {
svm.clear();
// svm = SVM.loadSVM(MODEL_PATH, "svm");
svm = SVM.load(MODEL_PATH);
} catch (Exception err) {
System.err.println(err.getMessage());
return 0; // next predict requires svm
}
System.out.println("Begin to predict");
// Test SVM
MatVector testingImages = new MatVector();
Vector<Integer> testingLabels_real = new Vector<Integer>();
// 将测试数据加载入内存
getPlateTest(testingImages, testingLabels_real, hasPlate, 0);
getPlateTest(testingImages, testingLabels_real, noPlate, 1);
double count_all = 0;
double ptrue_rtrue = 0;
double ptrue_rfalse = 0;
double pfalse_rtrue = 0;
double pfalse_rfalse = 0;
long size = testingImages.size();
System.err.println(size);
for (int i = 0; i < size; i++) {
Mat inMat = testingImages.get(i);
// Mat features = callback.getHisteqFeatures(inMat);
Mat features = callback.getHistogramFeatures(inMat);
Mat p = features.reshape(1, 1);
p.convertTo(p, opencv_core.CV_32F);
// System.out.println(p.cols() + "\t" + p.rows() + "\t" + p.type());
// samples.cols == var_count && samples.type() == CV_32F
// var_count 的值会在svm.xml库文件中有体现
float predoct = svm.predict(features);
int predict = (int) predoct; // 预期值
int real = testingLabels_real.get(i); // 实际值
if (predict == 1 && real == 1)
ptrue_rtrue++;
if (predict == 1 && real == 0)
ptrue_rfalse++;
if (predict == 0 && real == 1)
pfalse_rtrue++;
if (predict == 0 && real == 0)
pfalse_rfalse++;
}
count_all = size;
System.out.println("Get the Accuracy!");
System.out.println("count_all: " + Double.valueOf(count_all).toString());
System.out.println("ptrue_rtrue: " + Double.valueOf(ptrue_rtrue).toString());
System.out.println("ptrue_rfalse: " + Double.valueOf(ptrue_rfalse).toString());
System.out.println("pfalse_rtrue: " + Double.valueOf(pfalse_rtrue).toString());
System.out.println("pfalse_rfalse: " + Double.valueOf(pfalse_rfalse).toString());
double precise = 0;
if (ptrue_rtrue + ptrue_rfalse != 0) {
precise = ptrue_rtrue / (ptrue_rtrue + ptrue_rfalse);
System.out.println("precise: " + Double.valueOf(precise).toString());
} else
System.out.println("precise: NA");
double recall = 0;
if (ptrue_rtrue + pfalse_rtrue != 0) {
recall = ptrue_rtrue / (ptrue_rtrue + pfalse_rtrue);
System.out.println("recall: " + Double.valueOf(recall).toString());
} else
System.out.println("recall: NA");
double Fsocre = 0;
if (precise + recall != 0) {
Fsocre = 2 * (precise * recall) / (precise + recall);
System.out.println("Fsocre: " + Double.valueOf(Fsocre).toString());
} else
System.out.println("Fsocre: NA");
return 0;
}
public static void main(String[] args) {
SVMTrain1 s = new SVMTrain1();
s.svmTrain(true);
s.svmPredict();
}
}

@ -3,6 +3,7 @@ package com.yuxue.util;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import java.util.Map.Entry;
@ -13,8 +14,10 @@ import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.ml.ANN_MLP;
@ -23,8 +26,8 @@ import org.opencv.ml.SVM;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yuxue.constant.Constant;
import com.yuxue.enumtype.Direction;
import com.yuxue.enumtype.PlateColor;
import com.yuxue.train.ANNTrain;
import com.yuxue.train.SVMTrain;
@ -293,7 +296,6 @@ public class PlateUtil {
String plate = "";
Vector<Mat> dst = new Vector<Mat>();
ANNTrain annT = new ANNTrain();
for (int i = 0; i < sorted.size(); i++) {
Mat img_crop = new Mat(threshold, sorted.get(i));
img_crop = preprocessChar(img_crop);
@ -302,7 +304,7 @@ public class PlateUtil {
Imgcodecs.imwrite(tempPath + debugMap.get("plateCrop") + "_plateCrop_" + i + ".jpg", img_crop);
}
Mat f = annT.features(img_crop, Constant.predictSize);
Mat f = features(img_crop, Constant.predictSize);
// 字符预测
Mat output = new Mat(1, 140, CvType.CV_32F);
@ -382,6 +384,163 @@ public class PlateUtil {
public static float[] projectedHistogram(final Mat img, Direction direction) {
int sz = 0;
switch (direction) {
case HORIZONTAL:
sz = img.rows();
break;
case VERTICAL:
sz = img.cols();
break;
default:
break;
}
// 统计这一行或一列中非零元素的个数并保存到nonZeroMat中
float[] nonZeroMat = new float[sz];
Core.extractChannel(img, img, 0);
for (int j = 0; j < sz; j++) {
Mat data = (direction == Direction.HORIZONTAL) ? img.row(j) : img.col(j);
int count = Core.countNonZero(data);
nonZeroMat[j] = count;
}
// Normalize histogram
float max = 0;
for (int j = 0; j < nonZeroMat.length; ++j) {
max = Math.max(max, nonZeroMat[j]);
}
if (max > 0) {
for (int j = 0; j < nonZeroMat.length; ++j) {
nonZeroMat[j] /= max;
}
}
return nonZeroMat;
}
public static Mat features(Mat in, int sizeData) {
float[] vhist = projectedHistogram(in, Direction.VERTICAL);
float[] hhist = projectedHistogram(in, Direction.HORIZONTAL);
Mat lowData = new Mat();
if (sizeData > 0) {
Imgproc.resize(in, lowData, new Size(sizeData, sizeData));
}
int numCols = vhist.length + hhist.length + lowData.cols() * lowData.rows();
Mat out = new Mat(1, numCols, CvType.CV_32F);
int j = 0;
for (int i = 0; i < vhist.length; ++i, ++j) {
out.put(0, j, vhist[i]);
}
for (int i = 0; i < hhist.length; ++i, ++j) {
out.put(0, j, hhist[i]);
}
for (int x = 0; x < lowData.cols(); x++) {
for (int y = 0; y < lowData.rows(); y++, ++j) {
double[] val = lowData.get(x, y);
out.put(0, j, val[0]);
}
}
return out;
}
/**
*
* @param inMat
* @return
*/
public static Mat dilate(Mat inMat) {
Mat result = inMat.clone();
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2));
Imgproc.dilate(inMat, result, element);
return result;
}
/**
*
* @param inMat
* @return
*/
public static Mat erode(Mat inMat) {
Mat result = inMat.clone();
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2));
Imgproc.erode(inMat, result, element);
return result;
}
/**
*
* @param inMat
* @return
*/
public static Mat randTranslate(Mat inMat) {
Random rand = new Random();
Mat result = inMat.clone();
int ran_x = rand.nextInt(10000) % 5 - 2; // 控制在-2~3个像素范围内
int ran_y = rand.nextInt(10000) % 5 - 2;
return translateImg(result, ran_x, ran_y);
}
/**
*
* @param inMat
* @return
*/
public static Mat randRotate(Mat inMat) {
Random rand = new Random();
Mat result = inMat.clone();
float angle = (float) (rand.nextInt(10000) % 15 - 7); // 旋转角度控制在-7~8°范围内
return rotateImg(result, angle);
}
/**
*
* @param img
* @param offsetx
* @param offsety
* @return
*/
public static Mat translateImg(Mat img, int offsetx, int offsety){
Mat dst = new Mat();
//定义平移矩阵
Mat trans_mat = Mat.zeros(2, 3, CvType.CV_32FC1);
trans_mat.put(0, 0, 1);
trans_mat.put(0, 2, offsetx);
trans_mat.put(1, 1, 1);
trans_mat.put(1, 2, offsety);
Imgproc.warpAffine(img, dst, trans_mat, img.size()); // 仿射变换
return dst;
}
/**
*
* @param source
* @param angle
* @return
*/
public static Mat rotateImg(Mat source, float angle){
Point src_center = new Point(source.cols() / 2.0F, source.rows() / 2.0F);
Mat rot_mat = Imgproc.getRotationMatrix2D(src_center, angle, 1);
Mat dst = new Mat();
// 仿射变换 可以考虑使用投影变换; 这里使用放射变换进行旋转,对于实际效果来说感觉意义不大,反而会干扰结果预测
Imgproc.warpAffine(source, dst, rot_mat, source.size());
return dst;
}
}

Loading…
Cancel
Save