From a40705f15a1a37e2917e4723cd5fc13aa4fd121c Mon Sep 17 00:00:00 2001 From: yuxue Date: Sun, 17 May 2020 14:48:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + .../java/com/yuxue/constant/Constant.java | 5 +- .../com/yuxue/easypr/core/CharsIdentify.java | 7 ++- .../java/com/yuxue/easypr/core/CoreFunc.java | 47 ++++++++----------- .../java/com/yuxue/easypr/core/Features.java | 2 +- .../com/yuxue/easypr/core/PlateDetect.java | 2 +- .../com/yuxue/easypr/core/PlateJudge.java | 34 +++++--------- .../com/yuxue/easypr/core/PlateLocate.java | 5 +- .../yuxue/service/impl/PlateServiceImpl.java | 1 - src/main/java/com/yuxue/train/SVMTrain1.java | 6 +-- src/main/java/com/yuxue/util/ImageUtil.java | 13 ----- src/main/java/com/yuxue/util/PalteUtil.java | 2 - 12 files changed, 46 insertions(+), 80 deletions(-) delete mode 100644 src/main/java/com/yuxue/util/ImageUtil.java diff --git a/README.md b/README.md index 3ad01c71..bd110d6b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ - **图片车牌检测训练** - **图片文字识别训练** - 包含两种依赖包的实现方式: 基于org.bytedeco.javacpp包的实现方式; 基于org.opencv官方包的实现方式 +- org.opencv官方包,提供了java语言api;java项目可以通过build path方式或者环境变量的方式引用; +- org.bytedeco.javacpp包,JavaCPP是一个开源库,它提供了在 Java 中高效访问本地 C++的方法;在pom中引入坐标依赖即可 #### 软件版本 - jdk 1.8.61+ diff --git a/src/main/java/com/yuxue/constant/Constant.java b/src/main/java/com/yuxue/constant/Constant.java index b758ea68..ce9b00ca 100644 --- a/src/main/java/com/yuxue/constant/Constant.java +++ b/src/main/java/com/yuxue/constant/Constant.java @@ -25,14 +25,15 @@ public class Constant { public static String DEFAULT_ANN_PATH = "res/model/ann.xml"; //public static String DEFAULT_ANN_PATH = "D:/PlateDetect/train/chars_recognise_ann/ann.xml"; - - + + public static String DEFAULT_SVM_PATH = "res/model/svm.xml"; // 车牌识别,判断是否车牌的正则表达式 public static String plateReg = "([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1})"; public static int predictSize = 10; + public static int neurons = 40; // 中国车牌; 34个字符; 没有 字母I、字母O diff --git a/src/main/java/com/yuxue/easypr/core/CharsIdentify.java b/src/main/java/com/yuxue/easypr/core/CharsIdentify.java index e0dc3fca..6daf5b84 100644 --- a/src/main/java/com/yuxue/easypr/core/CharsIdentify.java +++ b/src/main/java/com/yuxue/easypr/core/CharsIdentify.java @@ -1,7 +1,6 @@ package com.yuxue.easypr.core; import org.bytedeco.javacpp.opencv_core; -import org.bytedeco.javacpp.opencv_imgcodecs; import org.bytedeco.javacpp.opencv_core.Mat; import org.bytedeco.javacpp.opencv_ml.ANN_MLP; @@ -22,11 +21,11 @@ public class CharsIdentify { loadModel(Constant.DEFAULT_ANN_PATH); } - public void loadModel(String s) { + public void loadModel(String path) { this.ann.clear(); // 加载ann配置文件 图像转文字的训练库文件 - //ann=ANN_MLP.loadANN_MLP(s, "ann"); - ann = ANN_MLP.load(s); + //ann=ANN_MLP.loadANN_MLP(path, "ann"); + ann = ANN_MLP.load(path); } diff --git a/src/main/java/com/yuxue/easypr/core/CoreFunc.java b/src/main/java/com/yuxue/easypr/core/CoreFunc.java index 0d91855a..c3d368a0 100644 --- a/src/main/java/com/yuxue/easypr/core/CoreFunc.java +++ b/src/main/java/com/yuxue/easypr/core/CoreFunc.java @@ -1,21 +1,12 @@ package com.yuxue.easypr.core; -import static org.bytedeco.javacpp.opencv_core.CV_32F; -import static org.bytedeco.javacpp.opencv_core.countNonZero; -import static org.bytedeco.javacpp.opencv_core.extractChannel; -import static org.bytedeco.javacpp.opencv_core.merge; -import static org.bytedeco.javacpp.opencv_core.split; -import static org.bytedeco.javacpp.opencv_highgui.cvWaitKey; -import static org.bytedeco.javacpp.opencv_imgproc.CV_BGR2HSV; -import static org.bytedeco.javacpp.opencv_imgproc.cvtColor; -import static org.bytedeco.javacpp.opencv_imgproc.equalizeHist; -import static org.bytedeco.javacpp.opencv_imgproc.resize; - import org.bytedeco.javacpp.BytePointer; +import org.bytedeco.javacpp.opencv_core; import org.bytedeco.javacpp.opencv_core.Mat; import org.bytedeco.javacpp.opencv_core.MatVector; import org.bytedeco.javacpp.opencv_core.Size; import org.bytedeco.javacpp.opencv_highgui; +import org.bytedeco.javacpp.opencv_imgproc; import org.bytedeco.javacpp.indexer.FloatIndexer; import com.yuxue.enumtype.Direction; @@ -49,8 +40,7 @@ public class CoreFunc { final float minref_sv = 64; final float minabs_sv = 95; - - //opencv颜色识别的HSV中各个颜色所对应的H的范围: Orange 0-22 Yellow 22- 38 Green 38-75 Blue 75-130 + // opencv颜色识别的HSV中各个颜色所对应的H的范围: Orange 0-22 Yellow 22- 38 Green 38-75 Blue 75-130 // blue的H范围 final int min_blue = 100; final int max_blue = 140; @@ -58,18 +48,18 @@ public class CoreFunc { // yellow的H范围 final int min_yellow = 15; final int max_yellow = 40; - + // green的H范围 final int min_green = 8; final int max_green = 150; - + // 转到HSV空间进行处理,颜色搜索主要使用的是H分量进行蓝色与黄色的匹配工作 Mat src_hsv = new Mat(); - cvtColor(src, src_hsv, CV_BGR2HSV); + opencv_imgproc.cvtColor(src, src_hsv, opencv_imgproc.CV_BGR2HSV); MatVector hsvSplit = new MatVector(); - split(src_hsv, hsvSplit); - equalizeHist(hsvSplit.get(2), hsvSplit.get(2)); - merge(hsvSplit, src_hsv); + opencv_core.split(src_hsv, hsvSplit); + opencv_imgproc.equalizeHist(hsvSplit.get(2), hsvSplit.get(2)); + opencv_core.merge(hsvSplit, src_hsv); // 匹配模板基色,切换以查找想要的基色 int min_h = 0; @@ -147,7 +137,7 @@ public class CoreFunc { // 获取颜色匹配后的二值灰度图 MatVector hsvSplit_done = new MatVector(); - split(src_hsv, hsvSplit_done); + opencv_core.split(src_hsv, hsvSplit_done); Mat src_grey = hsvSplit_done.get(2); return src_grey; @@ -174,7 +164,7 @@ public class CoreFunc { Mat gray = colorMatch(src, color, adaptive_minsv); - float percent = (float) countNonZero(gray) / (gray.rows() * gray.cols()); + float percent = (float) opencv_core.countNonZero(gray) / (gray.rows() * gray.cols()); return (percent > thresh) ? true : false; } @@ -227,10 +217,10 @@ public class CoreFunc { // 统计这一行或一列中,非零元素的个数,并保存到nonZeroMat中 float[] nonZeroMat = new float[sz]; - extractChannel(img, img, 0); + opencv_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 = countNonZero(data); + int count = opencv_core.countNonZero(data); nonZeroMat[j] = count; } @@ -267,11 +257,11 @@ public class CoreFunc { Mat lowData = new Mat(); if (sizeData > 0) { // resize.cpp:3784: error: (-215:Assertion failed) !ssize.empty() in function 'cv::resize' - resize(in, lowData, new Size(sizeData, sizeData)); + opencv_imgproc.resize(in, lowData, new Size(sizeData, sizeData)); } int numCols = vhist.length + hhist.length + lowData.cols() * lowData.rows(); - Mat out = Mat.zeros(1, numCols, CV_32F).asMat(); + Mat out = Mat.zeros(1, numCols, opencv_core.CV_32F).asMat(); FloatIndexer idx = out.createIndexer(); int j = 0; @@ -291,16 +281,17 @@ public class CoreFunc { return out; } + + /** * 显示图像 - * * @param title * @param src */ public static void showImage(final String title, final Mat src) { - if(src!=null){ + if (src != null) { opencv_highgui.imshow(title, src); - cvWaitKey(0); + opencv_highgui.cvWaitKey(0); } } diff --git a/src/main/java/com/yuxue/easypr/core/Features.java b/src/main/java/com/yuxue/easypr/core/Features.java index de379539..6384828b 100644 --- a/src/main/java/com/yuxue/easypr/core/Features.java +++ b/src/main/java/com/yuxue/easypr/core/Features.java @@ -9,7 +9,7 @@ import org.bytedeco.javacpp.opencv_core.MatVector; import org.bytedeco.javacpp.opencv_imgproc; /** - * 特征 + * * @author yuxue * @date 2020-05-05 08:26 */ diff --git a/src/main/java/com/yuxue/easypr/core/PlateDetect.java b/src/main/java/com/yuxue/easypr/core/PlateDetect.java index 0d3d34c4..d3041770 100644 --- a/src/main/java/com/yuxue/easypr/core/PlateDetect.java +++ b/src/main/java/com/yuxue/easypr/core/PlateDetect.java @@ -6,7 +6,7 @@ import org.bytedeco.javacpp.opencv_core.Mat; /** - * 车牌检测 + * 车牌检测识别 * 分两个步骤: 1、车牌定位 2、车牌判断 * @author yuxue * @date 2020-04-24 15:33 diff --git a/src/main/java/com/yuxue/easypr/core/PlateJudge.java b/src/main/java/com/yuxue/easypr/core/PlateJudge.java index e28fc9eb..a743ea45 100644 --- a/src/main/java/com/yuxue/easypr/core/PlateJudge.java +++ b/src/main/java/com/yuxue/easypr/core/PlateJudge.java @@ -10,6 +10,8 @@ import org.bytedeco.javacpp.opencv_core.Rect; import org.bytedeco.javacpp.opencv_core.Size; import org.bytedeco.javacpp.opencv_ml.SVM; +import com.yuxue.constant.Constant; + /** * 车牌判断 @@ -20,22 +22,21 @@ public class PlateJudge { private SVM svm = SVM.create(); + public PlateJudge() { + loadSVM(Constant.DEFAULT_SVM_PATH); + } + + public void loadSVM(String path) { + svm.clear(); + // svm=SVM.loadSVM(path, "svm"); + svm=SVM.load(path); + } + /** * EasyPR的getFeatures回调函数, 用于从车牌的image生成svm的训练特征features */ private SVMCallback features = new Features(); - - /** - * 模型存储路径 - */ - private String path = "res/model/svm.xml"; - - - public PlateJudge() { - svm.clear(); - svm=SVM.loadSVM(path, "svm"); - //svm=SVM.load(path); - } + /** * 对单幅图像进行SVM判断 @@ -54,7 +55,6 @@ public class PlateJudge { ret = (int) svm.predict(features); return ret; - // 使用com.yuxue.train.PlateRecoTrain 生成的训练库文件 // 在使用的过程中,传入的样本切图要跟训练的时候处理切图的方法一致 /*Mat grayImage = new Mat(); @@ -104,12 +104,4 @@ public class PlateJudge { } - public void setModelPath(String path) { - this.path = path; - } - - public final String getModelPath() { - return path; - } - } diff --git a/src/main/java/com/yuxue/easypr/core/PlateLocate.java b/src/main/java/com/yuxue/easypr/core/PlateLocate.java index 33ba33d4..750cd530 100644 --- a/src/main/java/com/yuxue/easypr/core/PlateLocate.java +++ b/src/main/java/com/yuxue/easypr/core/PlateLocate.java @@ -72,8 +72,6 @@ public class PlateLocate { // 开启调试模式之后,切图文件保存路径 protected String tempPath = Constant.DEFAULT_TEMP_DIR + System.currentTimeMillis() + "/"; - - /** * 生活模式与工业模式切换 * @param islifemode @@ -176,15 +174,14 @@ public class PlateLocate { Mat result = new Mat(); if (debug) { - // Draw red contours on the source image src.copyTo(result); + // 将轮廓描绘到图上输出 drawContours(result, contours, -1, new Scalar(0, 0, 255, 255)); opencv_imgcodecs.imwrite(tempPath + "debug_Contours.jpg", result); } // Start to iterate to each contour founded // 筛选。对轮廓求最小外接矩形,然后验证,不满足条件的淘汰。 - Vector rects = new Vector(); for (int i = 0; i < contours.size(); ++i) { diff --git a/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java b/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java index 3d79bddc..dc8c49a1 100644 --- a/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java +++ b/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java @@ -37,7 +37,6 @@ import com.yuxue.util.FileUtil; public class PlateServiceImpl implements PlateService { - // 车牌定位处理步骤,该map用于表示步骤图片的顺序 private static Map debugMap = Maps.newLinkedHashMap(); static { diff --git a/src/main/java/com/yuxue/train/SVMTrain1.java b/src/main/java/com/yuxue/train/SVMTrain1.java index 23d653c2..d2f01432 100644 --- a/src/main/java/com/yuxue/train/SVMTrain1.java +++ b/src/main/java/com/yuxue/train/SVMTrain1.java @@ -40,10 +40,10 @@ public class SVMTrain1 { private static final String hasPlate = "HasPlate"; private static final String noPlate = "NoPlate"; - public SVMTrain() { + public SVMTrain1() { } - public SVMTrain(SVMCallback callback) { + public SVMTrain1(SVMCallback callback) { this.callback = callback; } @@ -374,7 +374,7 @@ public class SVMTrain1 { } public static void main(String[] args) { - SVMTrain s = new SVMTrain(); + SVMTrain1 s = new SVMTrain1(); s.svmTrain(true); s.svmPredict(); } diff --git a/src/main/java/com/yuxue/util/ImageUtil.java b/src/main/java/com/yuxue/util/ImageUtil.java deleted file mode 100644 index 01f642a7..00000000 --- a/src/main/java/com/yuxue/util/ImageUtil.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.yuxue.util; - - -/** - * 图片处理工具类 - * @author yuxue - * @date 2020-05-10 19:31 - */ -public class ImageUtil { - - - -} diff --git a/src/main/java/com/yuxue/util/PalteUtil.java b/src/main/java/com/yuxue/util/PalteUtil.java index ad31395e..65805995 100644 --- a/src/main/java/com/yuxue/util/PalteUtil.java +++ b/src/main/java/com/yuxue/util/PalteUtil.java @@ -13,9 +13,7 @@ public class PalteUtil { * @return */ public static Boolean isPlate(String str) { - Pattern p = Pattern.compile(Constant.plateReg); - Boolean bl = false; //提取车牌