From 8e8e28c2351bce9709399d72661ffe5d7a575e57 Mon Sep 17 00:00:00 2001 From: yuxue Date: Tue, 12 May 2020 11:31:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BB=BF=E8=89=B2=E8=BD=A6?= =?UTF-8?q?=E7=89=8C=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- res/image/test_image/result_0.png | Bin 0 -> 10907 bytes .../com/yuxue/easypr/core/CharsRecognise.java | 4 +- .../com/yuxue/easypr/core/CharsSegment.java | 81 +++++++++++------- .../java/com/yuxue/easypr/core/CoreFunc.java | 4 +- .../yuxue/service/impl/PlateServiceImpl.java | 10 +-- .../static/js/model/plate/plateContentMod.js | 4 +- src/test/java/com/yuxue/test/EasyPrTest.java | 66 +++++++++++++- 8 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 res/image/test_image/result_0.png diff --git a/README.md b/README.md index 25656f9e..c0dcd575 100644 --- a/README.md +++ b/README.md @@ -111,5 +111,5 @@ debug_char_auxRoi: - 入门级教程项目,本人目前也正在学习图片识别相关技术;大牛请绕路 - 当前项目仅实现了黄牌、蓝牌车牌识别操作,接下来会继续优化代码架构,并且加上绿牌识别、车牌识别训练等操作 - 后续会逐步加入人脸识别等功能 - +- **车牌图片来源于网络,仅用于交流学习,不得用于商业用途;如有侵权,请联系本人删除** diff --git a/res/image/test_image/result_0.png b/res/image/test_image/result_0.png new file mode 100644 index 0000000000000000000000000000000000000000..28ee2be2537babc1dd8ef905a156fac2fb5e2a8c GIT binary patch literal 10907 zcmV;MDrD7(P)?Lq^WPgy+0ZW*vSH{Eva_kM0}%vKh#<09 ztY*_m6s??f0OEOGu~;Y;3R)`y0Qmy|;8#QfkTSEdQrZ{;Ak6X=01y#?IOpIiL8I0L&~R%KoMx*g!v2e~QrEI_x zCeXz~wp`4Gp-;rlCd?K=2t-&ob(6`Y-|LMh<0Od{seCTisIOJ4RnPMf06_o%1@J3{ zg5ZR$Fd`~ai0GUnqA(K%LEzXem&+)MMI8JKLL>x5Xp}NU1PCGm00;s=@)dvxvtt3E zuM~j*f+&R`Vy$I%@CO7y0QnOLhyVZ!Ad(_Q3K3Y?i30!x01*%tVFVF&BCM1$S|Nxq z$MGtY2{$)4^M&FfN(RHxXgrDH7!egIA^=1LLPGfyB7%U3Ab z!Z5qGRxOpvT9f<%L;ygH@jTBA2E(J1i^1HPV2yMJcfR@WGQKJn!hAN!X8pid0L;!3 zfhJN)3-fd`>G%7S$;76q(JG(IR;$%gspJ_$Um<`12*XzdK!^lD$SeTB%t?}Xo>wZB zecxLym($rSj$#0zf1!vJfI?6J1oEGO!2d_!D`623L{dr-DF9)12qMe?K!k(}5LkpA z3or-*BH^DDDWtEE5KtIdgh3cV5RmXIK@mhG0%QgdM3~K{zVEl&ZEb>iluqW$`Eu!; zQ>ciDg%OdE009w21Oxy90Re!8K_E$z`Fx%v2_h+_JkJZmjAwiR5dcIGfiz8{C`yuK zHk;kt4g1p+R0&lEw_g1>zDK2ESjcB;m13<{4tyix1ek!d@c?AGT+U|GXce(zQWW@J zE}IQA8LhQZiUnYOOubGui+GGb5s+KiD+wbbFo7-DEQ959fFN$YB9>fLkyB zgYRLvl+EXY_4Ud|s}cBK60g!U0j0uhL1~>NX&lGGj39_Wq>R>DkyhGhr4#{zFw0lK z0KxzuK!nB^0hrC^lksG=T1C++NfKjBsZ^;}E166vEX<66NPq;0NM8W~fB-Nv{7L>q zh)4*4!Ym^22P7mU6c%O{5hPSf6QM9TXN83b1%R0WfQT5_N-7|TKuCmuBEk&9ipW!1 z5ed6!wVaG6^Z6`^lh`_kMg^H{K3^)8ve_IFT4zB301*KRg@r{B5E0lp79k`=Visl= z5kMeBQc4kt05c`&PsT{Ol+9~+@$1HzfcFqB!XACKY07Rs;7J|ej)>-EqJ133_Q7dJL2vH$vr3oZWk~E2j!(r$4 zZn0X4=VuCqVyTqR=Y8KNBtSqUr3eA7v&@WN0WhTP-AwycrL(y4;@<^6lq=aVP_0&dXJ<2)4cIzBvZNep20{Q3 zFh+gF&RJ�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 x5GO(a8b~|w9QZ=8O6L9X matVec = new Vector(); // 车牌识别结果 String plateIdentify = ""; - int result = charsSegment.charsSegment(plate, matVec); + int result = charsSegment.charsSegment(plate, matVec, tempPath); if (0 == result) { for (int j = 0; j < matVec.size(); j++) { Mat charMat = matVec.get(j); diff --git a/src/main/java/com/yuxue/easypr/core/CharsSegment.java b/src/main/java/com/yuxue/easypr/core/CharsSegment.java index f4368bbb..71bf2e41 100644 --- a/src/main/java/com/yuxue/easypr/core/CharsSegment.java +++ b/src/main/java/com/yuxue/easypr/core/CharsSegment.java @@ -19,14 +19,15 @@ import static org.bytedeco.javacpp.opencv_imgproc.warpAffine; import java.util.Vector; +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.Rect; import org.bytedeco.javacpp.opencv_core.Scalar; import org.bytedeco.javacpp.opencv_core.Size; -import org.bytedeco.javacpp.opencv_core; import org.bytedeco.javacpp.opencv_imgcodecs; +import com.yuxue.enumtype.PlateColor; import com.yuxue.util.Convert; /** @@ -56,7 +57,7 @@ public class CharsSegment { private float bluePercent = DEFAULT_BLUEPERCEMT; private float whitePercent = DEFAULT_WHITEPERCEMT; - private boolean isDebug = false; + private boolean isDebug = true; /** @@ -69,7 +70,7 @@ public class CharsSegment { *
  • -3: null; * */ - public int charsSegment(final Mat input, Vector resultVec) { + public int charsSegment(final Mat input, Vector resultVec, String tempPath) { if (input.data().isNull()) { return -3; } @@ -83,8 +84,9 @@ public class CharsSegment { int w = input.cols(); int h = input.rows(); Mat tmpMat = new Mat(input, new Rect((int) (w * 0.1), (int) (h * 0.1), (int) (w * 0.8), (int) (h * 0.8))); - - switch (getPlateType(tmpMat, true)) { + + PlateColor color= getPlateType(tmpMat, true); + switch (color) { case BLUE: threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); break; @@ -92,22 +94,25 @@ public class CharsSegment { case YELLOW: threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); break; + + case GREEN: + threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); + break; default: return -3; } if (this.isDebug) { - opencv_imgcodecs.imwrite("tmp/debug_char_threshold.jpg", img_threshold); + opencv_imgcodecs.imwrite(tempPath + "debug_char_threshold.jpg", img_threshold); } // 去除车牌上方的柳钉以及下方的横线等干扰 //会导致虚拟机崩溃 // clearLiuDing(img_threshold); - - if (this.isDebug) { - String str = "tmp/debug_char_clearLiuDing.jpg"; + /*if (this.isDebug) { + String str = tempPath + "debug_char_clearLiuDing.jpg"; opencv_imgcodecs.imwrite(str, img_threshold); - } + }*/ // 找轮廓 Mat img_contours = new Mat(); @@ -126,10 +131,14 @@ public class CharsSegment { Vector vecRect = new Vector(); for (int i = 0; i < contours.size(); ++i) { Rect mr = boundingRect(contours.get(i)); - if (verifySizes(new Mat(img_threshold, mr))) + /*Mat temp = new Mat(img_threshold, mr); + String str = tempPath + "temp_"+i+".jpg"; + opencv_imgcodecs.imwrite(str, temp);*/ + + if (verifySizes(new Mat(img_threshold, mr))) { vecRect.add(mr); + } } - if (vecRect.size() == 0) { return -3; } @@ -139,19 +148,20 @@ public class CharsSegment { SortRect(vecRect, sortedRect); // 获得指示城市的特定Rect,如苏A的"A" - int specIndex = GetSpecificRect(sortedRect); + int specIndex = GetSpecificRect(sortedRect, color); + System.err.println(specIndex); if (this.isDebug) { if (specIndex < sortedRect.size()) { Mat specMat = new Mat(img_threshold, sortedRect.get(specIndex)); - String str = "tmp/debug_specMat.jpg"; + String str = tempPath + "debug_specMat.jpg"; opencv_imgcodecs.imwrite(str, specMat); } } // 根据特定Rect向左反推出中文字符 // 这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能 - // 退过特定算法来指定 + // 通过特定算法来指定 Rect chineseRect = new Rect(); if (specIndex < sortedRect.size()) { chineseRect = GetChineseRect(sortedRect.get(specIndex)); @@ -161,7 +171,7 @@ public class CharsSegment { if (this.isDebug) { Mat chineseMat = new Mat(img_threshold, chineseRect); - String str = "tmp/debug_chineseMat.jpg"; + String str = tempPath + "debug_chineseMat.jpg"; opencv_imgcodecs.imwrite(str, chineseMat); } @@ -170,7 +180,7 @@ public class CharsSegment { // 其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符 Vector newSortedRect = new Vector(); newSortedRect.add(chineseRect); - RebuildRect(sortedRect, newSortedRect, specIndex); + RebuildRect(sortedRect, newSortedRect, specIndex, color); if (newSortedRect.size() == 0) { return -3; @@ -182,7 +192,7 @@ public class CharsSegment { auxRoi = preprocessChar(auxRoi); if (this.isDebug) { - String str = "tmp/debug_char_auxRoi_" + Integer.valueOf(i).toString() + ".jpg"; + String str = tempPath + "debug_char_auxRoi_" + Integer.valueOf(i).toString() + ".jpg"; opencv_imgcodecs.imwrite(str, auxRoi); } resultVec.add(auxRoi); @@ -191,12 +201,11 @@ public class CharsSegment { } /** - * 字符尺寸验证 - * + * 字符尺寸验证;去掉尺寸不符合的图块 * @param r * @return */ - private Boolean verifySizes(Mat r) { + public static Boolean verifySizes(Mat r) { float aspect = 45.0f / 90.0f; float charAspect = (float) r.cols() / (float) r.rows(); float error = 0.7f; @@ -212,8 +221,7 @@ public class CharsSegment { // % of pixel in area float percPixels = area / bbArea; - return percPixels <= 1 && charAspect > minAspect && charAspect < maxAspect && r.rows() >= minHeight - && r.rows() < maxHeight; + return percPixels <= 1 && charAspect > minAspect && charAspect < maxAspect && r.rows() >= minHeight && r.rows() < maxHeight; } /** @@ -290,11 +298,10 @@ public class CharsSegment { /** * 找出指示城市的字符的Rect,例如苏A7003X,就是A的位置 - * * @param vecRect * @return */ - private int GetSpecificRect(final Vector vecRect) { + private int GetSpecificRect(final Vector vecRect, PlateColor color) { Vector xpositions = new Vector(); int maxHeight = 0; int maxWidth = 0; @@ -314,10 +321,17 @@ public class CharsSegment { Rect mr = vecRect.get(i); int midx = mr.x() + mr.width() / 2; - // 如果一个字符有一定的大小,并且在整个车牌的1/7到2/7之间,则是我们要找的特殊车牌 - if ((mr.width() > maxWidth * 0.8 || mr.height() > maxHeight * 0.8) - && (midx < this.theMatWidth * 2 / 7 && midx > this.theMatWidth / 7)) { - specIndex = i; + if(PlateColor.GREEN.equals(color)) { + if ((mr.width() > maxWidth * 0.8 || mr.height() > maxHeight * 0.8) + && (midx < this.theMatWidth * 2 / 8 && midx > this.theMatWidth / 8)) { + specIndex = i; + } + } else { + // 如果一个字符有一定的大小,并且在整个车牌的1/7到2/7之间,则是我们要找的特殊车牌 + if ((mr.width() > maxWidth * 0.8 || mr.height() > maxHeight * 0.8) + && (midx < this.theMatWidth * 2 / 7 && midx > this.theMatWidth / 7)) { + specIndex = i; + } } } @@ -336,9 +350,12 @@ public class CharsSegment { * @param specIndex * @return */ - private int RebuildRect(final Vector vecRect, Vector outRect, int specIndex) { + private int RebuildRect(final Vector vecRect, Vector outRect, int specIndex, PlateColor color) { // 最大只能有7个Rect,减去中文的就只有6个Rect int count = 6; + if(PlateColor.GREEN.equals(color)) { + count = 7; // 绿牌要多一个 + } for (int i = 0; i < vecRect.size(); i++) { // 将特殊字符左边的Rect去掉,这个可能会去掉中文Rect,不过没关系,我们后面会重建。 if (i < specIndex) @@ -359,7 +376,7 @@ public class CharsSegment { * @param out * @return */ - private void SortRect(final Vector vecRect, Vector out) { + public static void SortRect(final Vector vecRect, Vector out) { Vector orderIndex = new Vector(); Vector xpositions = new Vector(); for (int i = 0; i < vecRect.size(); ++i) { @@ -432,6 +449,4 @@ public class CharsSegment { } - - } diff --git a/src/main/java/com/yuxue/easypr/core/CoreFunc.java b/src/main/java/com/yuxue/easypr/core/CoreFunc.java index 2902a346..a6cd2f5b 100644 --- a/src/main/java/com/yuxue/easypr/core/CoreFunc.java +++ b/src/main/java/com/yuxue/easypr/core/CoreFunc.java @@ -59,8 +59,8 @@ public class CoreFunc { final int max_yellow = 40; // green的H范围 - final int min_green = 38; - final int max_green = 75; + final int min_green = 8; + final int max_green = 150; // 转到HSV空间进行处理,颜色搜索主要使用的是H分量进行蓝色与黄色的匹配工作 Mat src_hsv = new Mat(); diff --git a/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java b/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java index 3afb57f8..3d79bddc 100644 --- a/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java +++ b/src/main/java/com/yuxue/service/impl/PlateServiceImpl.java @@ -51,12 +51,11 @@ public class PlateServiceImpl implements PlateService { debugMap.put("debug_result", 6); // 原图处理结果 debugMap.put("debug_crop", 7); // 切图 debugMap.put("debug_resize", 8); // 切图resize - /*debugMap.put("debug_char_threshold", 9); // - debugMap.put("debug_char_clearLiuDing", 10); // 去除柳钉 + debugMap.put("debug_char_threshold", 9); // + // debugMap.put("debug_char_clearLiuDing", 10); // 去除柳钉 debugMap.put("debug_specMat", 11); // debugMap.put("debug_chineseMat", 12); // debugMap.put("debug_char_auxRoi", 13); // - */ } @@ -83,7 +82,7 @@ public class PlateServiceImpl implements PlateService { if(FileUtil.checkFile(f)) { e = new PlateFileEntity(); e.setFileName(f.getName()); - e.setFilePath(f.getAbsolutePath()); + e.setFilePath(f.getAbsolutePath().replaceAll("\\\\", "/")); e.setFileType(f.getName().substring(f.getName().lastIndexOf(".") + 1)); plateFileMapper.insertSelective(e); } @@ -201,10 +200,11 @@ public class PlateServiceImpl implements PlateService { if (0 == plateDetect.plateDetect(src, matVector)) { // 定位及判断,获取到车牌图块Mat CharsRecognise cr = new CharsRecognise(); + cr.setCRDebug(true); for (int i = 0; i < matVector.size(); ++i) { // 遍历车牌图块Mat,进行识别 Mat img = matVector.get(i); - String palte = cr.charsRecognise(img); // 字符识别 + String palte = cr.charsRecognise(img, tempPath); // 字符识别 PlateColor color = CoreFunc.getPlateType(img, true); String fileName = "result_" + i + ".png"; diff --git a/src/main/resources/static/js/model/plate/plateContentMod.js b/src/main/resources/static/js/model/plate/plateContentMod.js index 02e43626..952f3fb3 100644 --- a/src/main/resources/static/js/model/plate/plateContentMod.js +++ b/src/main/resources/static/js/model/plate/plateContentMod.js @@ -11,14 +11,14 @@ define(['api', 'utils'], function(api, utils){ bindBtnEvent(); // 监听按钮事件 - $('body').keydown(function (e) { + /*$('body').keydown(function (e) { if (event.keyCode==116){ //回车键 //F5按键 e.preventDefault(); setTimeout(function () { $("#refreshPlate").trigger('click'); }, 200); } - }); + });*/ } diff --git a/src/test/java/com/yuxue/test/EasyPrTest.java b/src/test/java/com/yuxue/test/EasyPrTest.java index 3b38d795..a9f0123b 100644 --- a/src/test/java/com/yuxue/test/EasyPrTest.java +++ b/src/test/java/com/yuxue/test/EasyPrTest.java @@ -1,14 +1,22 @@ package com.yuxue.test; +import static org.bytedeco.javacpp.opencv_imgproc.CV_CHAIN_APPROX_NONE; +import static org.bytedeco.javacpp.opencv_imgproc.CV_RETR_EXTERNAL; +import static org.bytedeco.javacpp.opencv_imgproc.boundingRect; +import static org.bytedeco.javacpp.opencv_imgproc.findContours; + import java.io.File; import java.util.Vector; import org.bytedeco.javacpp.opencv_core.Mat; +import org.bytedeco.javacpp.opencv_core.MatVector; +import org.bytedeco.javacpp.opencv_core.Rect; import org.bytedeco.javacpp.opencv_imgcodecs; import org.junit.Test; import com.yuxue.easypr.core.CharsIdentify; import com.yuxue.easypr.core.CharsRecognise; +import com.yuxue.easypr.core.CharsSegment; import com.yuxue.easypr.core.CoreFunc; import com.yuxue.easypr.core.PlateDetect; import com.yuxue.easypr.core.PlateLocate; @@ -49,7 +57,7 @@ public class EasyPrTest { for (int i = 0; i < matVector.size(); ++i) { // 遍历检测返回的Mat集合,进行识别 Mat img = matVector.get(i); - String palte = cr.charsRecognise(img); // 字符识别 + String palte = cr.charsRecognise(img, "tem/"); // 字符识别 PlateColor color = CoreFunc.getPlateType(img, true); System.err.println("识别到的车牌: " + palte + "_" + color); @@ -86,7 +94,7 @@ public class EasyPrTest { for (int i = 0; i < matVector.size(); ++i) { Mat img = matVector.get(i); // 弹窗显示 - //opencv_imgcodecs.showImage("Plate Detected", img); + //opencv_highgui.imshow("Plate Detected", img); String str = "d:/test/" + i + ".png"; opencv_imgcodecs.imwrite(str, img); @@ -134,7 +142,7 @@ public class EasyPrTest { Mat src = opencv_imgcodecs.imread(imgPath); CharsRecognise cr = new CharsRecognise(); cr.setCRDebug(true); - String result = cr.charsRecognise(src); + String result = cr.charsRecognise(src, "tem/"); System.out.println("Chars Recognised: " + result); } @@ -175,6 +183,58 @@ public class EasyPrTest { System.out.println(result); } + @Test + public void testGreenPlate() { + /*String imgPath = "res/image/test_image/result_0.png"; + Mat src = opencv_imgcodecs.imread(imgPath);*/ + + // 获取绿牌的H值范围 + /*MatVector hsvSplit = new MatVector(); + split(src_hsv, hsvSplit); + equalizeHist(hsvSplit.get(2), hsvSplit.get(2)); + merge(hsvSplit, src_hsv); + + int channels = src_hsv.channels(); + int nRows = src_hsv.rows(); + // 图像数据列需要考虑通道数的影响; + int nCols = src_hsv.cols() * channels; + + // 连续存储的数据,按一行处理 + if (src_hsv.isContinuous()) { + nCols *= nRows; + nRows = 1; + } + Map map = Maps.newHashMap(); + for (int i = 0; i < nRows; ++i) { + BytePointer p = src_hsv.ptr(i); + for (int j = 0; j < nCols; j += 3) { + int H = p.get(j) & 0xFF; + int S = p.get(j + 1) & 0xFF; + int V = p.get(j + 2) & 0xFF; + + if(map.containsKey(H)) { + int count = map.get(H); + map.put(H, count+1); + } else { + map.put(H, 1); + } + + } + } + map.entrySet().forEach(n->{ + System.err.println(n.getKey() + "\t" + n.getValue()); + });*/ + + // 判断绿色车牌 + /*Mat src_hsv = new Mat(); + cvtColor(src, src_hsv, CV_BGR2HSV); + src_hsv = CoreFunc.colorMatch(src, PlateColor.GREEN, true); + System.err.println(CoreFunc.plateColorJudge(src, PlateColor.GREEN, true)); + String str = "d:/PlateDetect/src_hsv.png"; + opencv_imgcodecs.imwrite(str, src_hsv);*/ + + } +