增加绿色车牌识别

devA
yuxue 5 years ago
parent c7fb972a9a
commit 8e8e28c235

@ -111,5 +111,5 @@ debug_char_auxRoi
- 入门级教程项目,本人目前也正在学习图片识别相关技术;大牛请绕路
- 当前项目仅实现了黄牌、蓝牌车牌识别操作,接下来会继续优化代码架构,并且加上绿牌识别、车牌识别训练等操作
- 后续会逐步加入人脸识别等功能
- **车牌图片来源于网络,仅用于交流学习,不得用于商业用途;如有侵权,请联系本人删除**

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -29,14 +29,14 @@ public class CharsRecognise {
* @param plate the input plate
* @return the result of plate recognition
*/
public String charsRecognise(final Mat plate) {
public String charsRecognise(final Mat plate, String tempPath) {
// 车牌字符方块集合
Vector<Mat> matVec = new Vector<Mat>();
// 车牌识别结果
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);

@ -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 {
* <li>-3: null;
* </ul>
*/
public int charsSegment(final Mat input, Vector<Mat> resultVec) {
public int charsSegment(final Mat input, Vector<Mat> resultVec, String tempPath) {
if (input.data().isNull()) {
return -3;
}
@ -84,7 +85,8 @@ public class CharsSegment {
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;
@ -93,21 +95,24 @@ public class CharsSegment {
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<Rect> vecRect = new Vector<Rect>();
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<Rect> newSortedRect = new Vector<Rect>();
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 {
/**
* RectA7003XA
*
* @param vecRect
* @return
*/
private int GetSpecificRect(final Vector<Rect> vecRect) {
private int GetSpecificRect(final Vector<Rect> vecRect, PlateColor color) {
Vector<Integer> xpositions = new Vector<Integer>();
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<Rect> vecRect, Vector<Rect> outRect, int specIndex) {
private int RebuildRect(final Vector<Rect> vecRect, Vector<Rect> 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<Rect> vecRect, Vector<Rect> out) {
public static void SortRect(final Vector<Rect> vecRect, Vector<Rect> out) {
Vector<Integer> orderIndex = new Vector<Integer>();
Vector<Integer> xpositions = new Vector<Integer>();
for (int i = 0; i < vecRect.size(); ++i) {
@ -432,6 +449,4 @@ public class CharsSegment {
}
}

@ -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();

@ -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";

@ -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);
}
});
});*/
}

@ -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<Integer, Integer> 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);*/
}

Loading…
Cancel
Save