yunx li 4 years ago
commit 91f6309229

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="module" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/OpenCV-4.4.0"/>
<classpathentry kind="lib" path="lib/opencv-4.0.1-1.4.4-windows-x86_64.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

@ -0,0 +1,13 @@
### 该问题是怎么引起的?
### 重现步骤
### 报错信息

@ -0,0 +1,15 @@
### 相关的Issue
### 原因(目的、解决的问题等)
### 描述(做了什么,变更了什么)
### 测试用例(新增、改动、可能影响的功能)

23
.gitignore vendored

@ -0,0 +1,23 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>yx-image-recognition</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

@ -0,0 +1,5 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8

@ -0,0 +1,9 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, yuxue
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,136 @@
# yx-image-recognition
#### 介绍
- **spring boot + maven 实现的车牌识别及训练系统**
- 基于java语言的深度学习项目在整个开源社区来说都相对较少而基于java语言实现车牌识别EasyPR-Java项目最后的更新已经是五年以前。
- **本人参考了EasyPR原版C++项目、以及fan-wenjie的EasyPR-Java项目同时查阅了部分opencv官方4.0.1版本C++的源码结合个人对java语言理解整理出当前项目**
- 这是一个**入门级教程项目**,本人目前也正在学习图片识别相关技术;大牛请绕路
- 当前项目在原有EasyPR项目基础上增加了绿牌识别功能只不过当前的训练库文件包含绿牌的样本太少还需要重新增加绿牌样本的训练后续会逐步上传
- 当前已经添加基于svm算法的车牌检测训练、以及基于ann算法的车牌号码识别训练功能
- 后续会逐步加入证件识别、人脸识别等功能
#### 包含功能
- ****$\color{yellow}{黄}$** **$\color{blue}{蓝}$** **$\color{green}{绿}$**** **黄蓝绿车牌检测及车牌号码识别**
- 单张图片、多张图片并发、单图片多车牌检测及识别
- **图片车牌检测训练**
- **图片文字识别训练**
- 包含两种依赖包的实现方式: 基于org.bytedeco.javacpp包的实现方式 基于org.opencv官方包的实现方式
- org.opencv官方包提供了java语言apijava项目可以通过build path方式或者环境变量的方式引用
- org.bytedeco.javacpp包JavaCPP是一个开源库它提供了在 Java 中高效访问本地 C++的方法在pom中引入坐标依赖即可
#### 软件版本
- jdk 1.8.61+
- maven 3.0+
- opencv 4.0.1 javacpp1.4.4opencv-platform 4.0.1-1.4.4
- spring boot 2.1.5.RELEASE
- yx-image-recognition 1.0.0版本
#### 软件架构
- B/S 架构前端html + requireJS后端java
- 数据库使用 sqlite3.0
- 接口文档使用swagger 2.0
#### 操作界面
![1.png](./doc/doc_image/1.png)
#### 车牌检测过程
高斯模糊:
![1.png](./doc/doc_image/debug_GaussianBlur.jpg)
图像灰度化:
![1.png](./doc/doc_image/debug_gray.jpg)
Sobel 算子:
![1.png](./doc/doc_image/debug_Sobel.jpg)
图像二值化:
![1.png](./doc/doc_image/debug_threshold.jpg)
图像闭操作:
![1.png](./doc/doc_image/debug_morphology.jpg)
二值图像降噪:
![1.png](./doc/doc_image/debug_morphology1.jpg)
提取外部轮廓:
![1.png](./doc/doc_image/debug_Contours.jpg)
外部轮廓筛选:
![1.png](./doc/doc_image/107_screenblock.jpg)
切图:
![1.png](./doc/doc_image/debug_crop_1.jpg)
![1.png](./doc/doc_image/debug_crop_2.jpg)
![1.png](./doc/doc_image/debug_crop_3.jpg)
重置切图尺寸:
![1.png](./doc/doc_image/debug_resize_1.jpg)
![1.png](./doc/doc_image/debug_resize_2.jpg)
![1.png](./doc/doc_image/debug_resize_3.jpg)
车牌检测结果:
![1.png](./doc/doc_image/result_0.png)
#### 图片车牌文字识别过程
debug_char_threshold
![1.png](./doc/doc_image/debug_char_threshold.jpg)
debug_char_clearLiuDing
![1.png](./doc/doc_image/debug_char_clearLiuDing.jpg)
debug_specMat
![1.png](./doc/doc_image/debug_specMat.jpg)
debug_chineseMat
![1.png](./doc/doc_image/debug_chineseMat.jpg)
debug_char_auxRoi
![1.png](./doc/doc_image/debug_char_auxRoi_0.jpg)
![1.png](./doc/doc_image/debug_char_auxRoi_1.jpg)
![1.png](./doc/doc_image/debug_char_auxRoi_2.jpg)
![1.png](./doc/doc_image/debug_char_auxRoi_3.jpg)
![1.png](./doc/doc_image/debug_char_auxRoi_4.jpg)
![1.png](./doc/doc_image/debug_char_auxRoi_5.jpg)
![1.png](./doc/doc_image/debug_char_auxRoi_6.jpg)
#### 安装教程
- 开发环境搭建: [./doc/01_开发环境搭建.md](./doc/01_开发环境搭建.md)
- 将项目拉取到本地PlateDetect文件夹拷贝到d盘下默认车牌识别操作均在d:/PlateDetect/目录下处理
- 需要修改操作路径修改com/yuxue/constant/Constant.java文件常量参数即可可以使用绝对盘符路径也可以使用项目相对路径
- spring boot方式运行项目浏览器上输入 http://localhost:16666/index 即可打开操作界面
- 浏览器上输入 http://localhost:16666/swagger-ui.html 即可打开接口文档页面
#### 使用说明
- **车牌图片来源于网络,仅用于交流学习,不得用于商业用途;如有侵权,请联系本人删除**
- 转发请注明出处; 本项目作者yuxue一个不资深的java语言从业者
- 作者gitee地址: https://gitee.com/admin_yu
- 作者csdn微博地址https://blog.csdn.net/weixin_42686388
#### 参考文档
- liuruoze/EasyPRhttps://gitee.com/easypr/EasyPR?_from=gitee_search
- fan-wenjie/EasyPR-Java https://github.com/fan-wenjie/EasyPR-Java
- opencv官方 https://opencv.org/

@ -0,0 +1,52 @@
## 基础概念
- OpenCV是一个基于BSD许可开源发行的跨平台计算机视觉和机器学习软件库轻量级而且高效由一系列C函数和少量C++类构成该库的核心是用C ++编写的
- 本项目是java语言项目其本质是通过java调用opencv c++的接口实现的。
- 本项目提供了两种方式去调用c++的接口,
- 1、通过javacpp调用这种方式是原版EasyPR-Java项目的使用方式在这里小编更新了javacpp版本以及切换了依赖包的引用方式为maven pom引入
- 2、通过opencv官方提供的java 语言的api调用个人推荐这种方式调用毕竟是官方的版本
以上两种方式本质上都是java调用c++的接口实现具体可以自行了解一下jni或者jna这里不做深入介绍了
## 开发环境搭建
### 软件版本
- jdk 1.8.61+
- maven 3.0+
- opencv 4.0.1 javacpp1.4.4opencv-platform 4.0.1-1.4.4
- spring boot 2.1.5.RELEASE
### 操作步骤
- 安装jdk不会的自行百度
- 安装maven不会的自行百度
- 将项目导入eclipse不会的自行百度其他的开发工具的自行百度
- **javacpp方式调用**
- 添加pom依赖坐标
```xml
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>opencv</artifactId>
<version>4.0.1-1.4.4</version>
</dependency>
<!-- 引入这个包会把所有平台的依赖包都导入大小有200+M实际上用不到 -->
<!-- 只需要从lib目录引入对应的操作系统版本包(lib/opencv-4.0.1-1.4.4-windows-x86_64.jar)即可 -->
<!-- <dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>opencv-platform</artifactId>
<version>4.0.1-1.4.4</version>
</dependency> -->
```
- 将 /lib/opencv-4.0.1-1.4.4-windows-x86_64.jar 依赖包添加到build path也可以在pom文件中使用opencv-platform增加exclusion标签排出多余的包
- maven update 一下项目即可
- 如果不需要这种方式的实现,删除多余的代码即可
- **官方的api调用**
- https://opencv.org/releases/ 官方下载对应版本的安装包;傻瓜式安装,不会的自行百度
- 添加user library 方法https://blog.csdn.net/qq_32447301/article/details/78494913
- **注意:** 需要将opencv安装目录\build\java\x64目录下dll文件拷贝到\build\x64\vc14\bin目录下其中x64、vc14根据实际版本自行调整
- 如果不需要这种方式的实现,删除多余的代码即可

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,203 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yuxue</groupId>
<artifactId>yx-image-recognition</artifactId>
<version>1.0.0</version>
<description>图像识别技术 车牌识别、训练等</description>
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<!-- AOP 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- SpringBoot Web项目依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 前端模板文件支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 单元测试 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis支持 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- sqlite驱动 -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
</dependency>
<!--druid连接池-springBoot依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- 分页插件,一对多关系分页,页码会有问题,需要自行处理 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<!-- mybatis支持 -->
<!-- <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency> -->
<!-- pgsql数据库驱动 -->
<!-- <dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency> -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sourceforge.tess4j/tess4j -->
<!-- 图片文字识别 -->
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>4.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bytedeco/opencv -->
<!-- 包含了opencv4.0.1 及javacpp1.4.4版本 -->
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>opencv</artifactId>
<version>4.0.1-1.4.4</version>
</dependency>
<!-- org.bytedeco.javacpp-presets.opencv的包已经传递依赖了当前包 -->
<!-- <dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.4.4</version>
</dependency> -->
<!-- 引入这个包会把所有平台的依赖包都导入大小有200+M实际上用不到 -->
<!-- 只需要从lib目录引入对应的操作系统版本包(lib/opencv-4.0.1-1.4.4-windows-x86_64.jar)即可 -->
<!-- <dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>opencv-platform</artifactId>
<version>4.0.1-1.4.4</version>
</dependency> -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.yuxue.Application</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/webapp</directory>
<includes>
<include>**/**</include>
</includes>
</resource>
</resources>
</build>
</project>

@ -0,0 +1,94 @@
General Data Share License
通用数据共享协议
Version 0.1, 2015-01-25
当您开始使用EasyPR中的GDTS(General Data Test Set通用数据测试集)也就是image/general_test文件夹里的任一数据时您必须遵守以下协议的条款。
EasyPR只允许对GDTS里的数据进行非商业性目的的使用任何商业行为(售卖或者随产品附赠等)都属于违反本协议约定的行为。
EasyPR保留任何权利。
本协议的起草参考了以下两个协议GPL v2.0与ODL(Open Database License)。其核心思想与这两个协议基本相同,但也有许多相异的地方。
本协议与GPL协议相同的地方在于本协议与GPL协议都是“传染性”协议。
当您使用拷贝转移了GDTS中数据(或其中一部分时),您务必要保证这个协议随数据同行。
同时假若您把GDTS中数据(或其中一部分时)进行修改或与其他数据进行合并时那您需要确保新的数据集也必须遵循此GDSL协议的约定条款。
与GPL协议不同的地方在于GPL协议保护的是代码(code)以及基于代码的工作(work),而本协议保护的是数据(data)。
注意,本协议中所保护的数据仅保护这些原始的图片数据,并不针对于您通过这些图片数据训练出的模型,
以及您通过这些图片数据截取出的仅包含车牌的图片,您可以将这些训练模型或车牌图片按照非本协议约定的条款进行操作。
本协议与ODL协议相同的地方在于本协议与ODL协议都是保护数据(data)的协议,并且也提倡数据的开放,共享。
但ODL协议针对的主要是结构化数据而本协议针对的主要是图片数据。
另外ODL协议强调的开放不限制对数据的商业性使用。但本协议规定了数据仅仅只能用于进行非商业性的目的行为
包括学习与研究,但不包括商业性行为,例如销售与随产品赠送等等。
目前本协议的版本为0.1修正稿,任何对本协议的修改与建议都可以跟本协议的组织方联系(easypr_dev@163.com) 。
本协议主要分为三个部分:
1.版权声明:约定了对GDTS数据使用的规范。
2.捐赠说明:说明了如何对GDTS数据进行捐赠的方法。
3.免责声明:声明免责条款。
如果您仅仅是使用EasyPR进行开发与研究那您仅需要读完第一部分。如果您愿意对EasyPR进行捐赠那您需要读完第二部分。
第三部分声明了EasyPR在各个部分的免责条款。
一.版权声明
EasyPR中GDTS(通用数据测试集)的数据仅用作学习与研究之用。尽管EasyPR遵循的是商业友好的开源协议Acache2.0,但那个协议仅适用于您对代码的修改权。
这些测试数据集并不在您可以修改并且出卖的范围之内请确保这些数据集仅仅作为您进行EasyPR图像测试效率的验证与参考。
这些图片是用来测试EasyPR的效果与指标的仅仅用于开源学习目的。任何商业化的使用这些数据例如出卖数据或者将这些数据作为产品的附赠都是不允许的。
为了保证EasyPR中使用的测试数据不有任何侵犯权利的可能性每个在GDTS(通用数据测试集)上上传的数据都具有以下几个特征:
1.年代久远,不具有时效性的数据(例如,至少半年以前的数据)或者已经处理过相关版权事宜的数据。
2.在上传前使用EasyPR提供的函数对图片进行模糊化裁剪性处理确保图片不透露任何可能关于地点时间位置等相关信息。
3.在上传前使用EasyPR提供的反人脸识别工具进行处理确保图片不侵犯到任何人的隐私权和肖像权。
如果您发现EasyPR中存在任何侵犯您可能权利的图片时请跟我们联系(easypr_dev@163.com) 。我们的工作人员会跟您协商,将这些图片修改或做删除处理。
二.捐赠说明
EasyPR里的数据来源广泛部分来自于网络公开途径也有好心的网友以及匿名人士对EasyPR的捐赠。通过这些数据
有效地改善了EasyPR的识别效果与准确率为推广车牌识别技术在中国的开源发展做出了贡献。如果您愿意您也可以向EasyPR捐赠。
在一般的开源与众包软件中捐赠的方式一般是金钱等物质性财产。但EasyPR的捐赠略有不同我们不接受财物的捐赠相反我们接受的是数据的捐赠。
如果您有车牌的图片符合以下三个条件并且您愿意捐赠给EasyPR作为研究与开发的帮助那么您可以跟我们联系(easypr_dev@163.com)
一般选择捐赠的图片在5-30张之间最好每张图片有足够不同的特点我们非常高兴能够接受您对我们开发与研究的支持。
捐赠方式可以选择公开或匿名并且您可以选择捐赠后的数据是纳入GDTS作为通用测试集或者保持私密性仅仅作为EasyPR核心团队训练与测试的图片。
捐赠的图片数据不需要太多我们建议您不要捐赠超过30张以上的图片。感谢您为中国开源软件的发展与数据开源运动所做出的贡献
为了保证EasyPR中使用的测试数据不有任何侵犯权利的可能性每个在GDTS(通用数据测试集)上上传的数据都具有以下几个特征:
1.年代久远,不具有时效性的数据(例如,至少半年以前的数据)或者已经处理过相关版权事宜的数据
2.在上传前使用EasyPR提供的函数对图片进行模糊化裁剪性处理确保图片不透露任何可能关于地点时间位置等相关信息
3.在上传前使用EasyPR提供的反人脸识别工具进行处理确保图片不侵犯到任何人的隐私权和肖像权
只有经过以上处理满足以上三个条件的数据EasyPR才会纳入到GDTS里作为车牌识别的准确率衡判依据。
任何不满足以上三个条件的数据EasyPR都会保持这些数据的隐秘不公开等特性并且确保这些数据处于保密状态。
EasyPR团队保证这些研究仅仅作为开源社区学习机器学习、深度学习、图像识别以及计算机视觉的相关资料与参考不会用作任何商用或者恶意行为。
三.免责声明
EasyPR中GDTS(通用数据测试集)的使用仅仅是为了研究与学习目的。
任何使用这些数据进行商用或者恶意窥探目的的行为都违反EasyPR所遵循的开源法则以及研究目的。EasyPR不对这些恶意后果负有任何责任。
EasyPR谴责这些行为但EasyPR不为这些违反开源原则行为的后果负有责任。
当恶意使用者以及用数据牟利者违反了EasyPR约定的这些条款时也就意味着EasyPR不会对他们所造成的任何行为负有责任。
当您使用这些数据时就意味着您已经同意EasyPR的这些约定您对EasyPR通用测试数据集的滥用以及恶意窥探目行为的后果需要您自己承担
EasyPR及其开源团队与贡献者不承担任何相关的责任。
EasyPR团队保留所有权利。
联系方式
EasyPR开发团队的官方邮箱
(easypr_dev@163.com)
###当您复制了EasyPR声明权利的这些数据(或者其中一部分)的同时,您也必须将这份协议复制一份,并保持协议随数据时刻同行。

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

@ -0,0 +1,14 @@
1.general_test存放通用数据测试集GDTS的文件夹EasyPR的开发者会依照这里的图片来判断EasyPR新算法的改进性。
其他开发者也可以通过这个测试集测试自己修改的程序在通用图片集上的表现。
一般来说这个数据集的里的效果表现是低于下面的native_test的。
你可以通过启动EasyPR->2.批量测试->1.general_test来测试EasyPR在通用数据集上的效果表现。
2.native_test存放使用者特定图片数据集的地方。把你特定环境下的图片放到这里进行测试跟GDTS分开。
由于你希望EasyPR只在你的数据集下表现良好那么你可以只把你的数据放到native_test文件夹下把里面原有的文件删除然后运行测试。
你可以试着改变图像处理算法也可以通过EasyPR的训练功能对你的数据集进行训练然后用训练好的模型替代EasyPR
的模型这样的模型比EasyPR的原模型对你的数据适应性更好
你可以通过启动EasyPR->2.批量测试->2.native_test来测试你的效果表现。
3.tmp文件夹存放EasyPR处理过程的中间图片用于调试使用。
注意:通用数据测试集(GDTS)里的数据遵循GDSL协议请阅读同目录下的"GDSL.txt"获得更多信息。

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,36 @@
package com.yuxue;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import lombok.extern.slf4j.Slf4j;
/**
* spring boot
* @author yuxue
* @date 2019-12-06
*/
@SpringBootApplication
@MapperScan("mapper")
@EnableScheduling //开启对定时任务的支持
@Slf4j
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
String version = System.getProperty("java.version");
if (Integer.parseInt(
version.substring(0,1)) == 1
&& Integer.parseInt(version.substring(2, 3)) >= 8
&& Integer.parseInt(version.substring(6)) >= 60
|| Integer.parseInt(version.substring(0,1))>=9) {
SpringApplication.run(Application.class, args);
} else {
log.error("java version need greater than 1.8.60, and do not use open jdk !!!");
}
}
}

@ -0,0 +1,22 @@
package com.yuxue.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* controllerapi
*
* @author yuxue
* @date 2019-08-19
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetExclude {
String value() default "";
}

@ -0,0 +1,42 @@
package com.yuxue.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.yuxue.annotation.RetExclude;
import com.yuxue.entity.Result;
import lombok.extern.slf4j.Slf4j;
/**
* method
* @author yuxue
* @date 2019-08-20
*/
@Slf4j
public class AroundMethod implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object ret = null;
try {
ret = methodInvocation.proceed();
if(ret instanceof Result) { // 防止出现二次封装
return ret;
}
} catch (Throwable e) {
// 运行时出现异常抛出由ResultReturnExceptionHandler统一处理
throw e;
}
RetExclude re = methodInvocation.getMethod().getAnnotation(RetExclude.class);
if(null != re && null != re.value()) {
log.info("api添加了封装排除注解");
return ret;
}
// log.info("封装返回值");
return Result.ok(ret);
}
}

@ -0,0 +1,34 @@
package com.yuxue.aop;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* aop
* @author yuxue
* @date 2019-08-20
*/
@Configuration
@ConditionalOnMissingBean(DefaultPointcutAdvisor.class)
public class DefaultAopConfig {
// @Value("${test.aop.pointcut:com.yuxue..*.controller..*.*(..)}")
@Value("${test.aop.pointcut:com.yuxue.controller..*.*(..)}")
private String pattern;
@Bean("resultAop")
public DefaultPointcutAdvisor resultAop() {
DefaultPointcutAdvisor pfb = new DefaultPointcutAdvisor();
JdkRegexpMethodPointcut j = new JdkRegexpMethodPointcut();
j.setPattern(pattern);
AroundMethod method = new AroundMethod();
pfb.setAdvice(method);
pfb.setPointcut(j);
return pfb;
}
}

@ -0,0 +1,56 @@
package com.yuxue.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* controller aop
* @author yuxue
* @date 2018-09-07
*/
@Aspect
@Slf4j
@Component
public class WebAop {
@Pointcut("execution(* com.yuxue.controller..*.*(..))")
public void webLog() {}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("====================");
log.info("Cookie: " + request.getHeader("Cookie"));
log.info(request.getMethod() + "=>" + request.getRequestURL().toString());
log.info("IP: " + request.getRemoteAddr());
log.info("CLASS_METHOD: "
+ joinPoint.getSignature().getDeclaringTypeName()
+ "."
+ joinPoint.getSignature().getName());
log.info("ARGS: " + Arrays.toString(joinPoint.getArgs()));
log.info("====================\n");
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 关闭: 返回前进行内容结果日志输出
log.info("RESPONSE: " + ret);
log.info("====================\n");
}
}

@ -0,0 +1,33 @@
package com.yuxue.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
*
*
*/
@Slf4j
@Component
public class CommandRunner implements CommandLineRunner {
@Value("${server.port}")
private String port;
@Override
public void run(String... args) {
try {
String os = System.getProperty("os.name").toLowerCase();
if(os.contains("windows")) {
// 默认浏览器打开
// Runtime.getRuntime().exec("cmd /c start http://localhost:" + port + "/index");
}
} catch (Exception ex) {
ex.printStackTrace();
log.error("打开默认浏览器异常", ex);
}
}
}

@ -0,0 +1,40 @@
package com.yuxue.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*
* @CrossOrigin
* @author yuxue
* @date 2018-09-07
*/
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
/** * 至少需要addMapping *** */
registry
.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("PUT", "DELETE", "GET", "POST", "OPTIONS", "HEAD")
.allowedHeaders("Content-Type", "X-Requested-With", "accept", "Authorization", "Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers")
.allowCredentials(true)//是否带上cookie
.maxAge(3600)
.exposedHeaders(
"access-control-allow-headers",
"access-control-allow-methods",
"access-control-allow-origin",
"access-Control-allow-credentials",
"access-control-max-age",
"X-Frame-Options");
}
};
}
}

@ -0,0 +1,26 @@
package com.yuxue.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
/**
*
* @author yuxue
* @date 2019-06-13
*/
@Configuration
public class DefaultMvcConfig {
/**
*
*
* @return
*/
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}

@ -0,0 +1,48 @@
package com.yuxue.config;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.google.common.collect.Lists;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* druid,sql
*/
@Configuration
public class DruidConfig {
// 这个注解读取配置文件前缀为prefix的配置将外部的配置文件与这里绑定
// 容器的开启与关闭
@ConfigurationProperties(prefix = "spring.druid")
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setProxyFilters(Lists.newArrayList(statFilter()));
return dataSource;
}
// bean注解成为spring的bean利用filter将慢sql的日志打印出来
//@Bean
public Filter statFilter() {
StatFilter statFilter = new StatFilter();
// 多长时间定义为慢sql这里定义为5s
statFilter.setSlowSqlMillis(5000);
// 是否打印出慢日志
statFilter.setLogSlowSql(true);
// 是否将日志合并起来
statFilter.setMergeSql(true);
return statFilter;
}
// 这是配置druid的监控
@Bean
public ServletRegistrationBean servletRegistrationBean() {
return new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
}
}

@ -0,0 +1,29 @@
package com.yuxue.config;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
@Configuration
public class LocalDateTimeSerializerConfig {
@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
@Bean
public LocalDateTimeSerializer localDateTimeDeserializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
}
}

@ -0,0 +1,32 @@
package com.yuxue.config;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
*
* @author yuxue
* @date 2019-06-13
*/
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String l = request.getParameter("i18n");
Locale locale = Locale.getDefault();
if(!StringUtils.isEmpty(l)){
String[] split = l.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}

@ -0,0 +1,30 @@
package com.yuxue.config;
import com.github.pagehelper.PageInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
*
* @author yuxue
* @date 2018-09-07
*/
@Configuration
public class PageHelperConfig {
@Value("${pagehelper.helperDialect}")
private String helperDialect;
@Bean
public PageInterceptor pageInterceptor() {
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();
properties.setProperty("helperDialect", helperDialect);
pageInterceptor.setProperties(properties);
return pageInterceptor;
}
}

@ -0,0 +1,51 @@
package com.yuxue.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* swagger-annotations jar 1.5.X, io.swagger.annotations.API description
* swagger使Web api 使 description Controller api
* 使tag便
* Controllertagkeytagvalue
* @ApiOperation valueapiapi 120
* @ApiOperation notesapiapi
* @ApiOperation produces apijsonutf8
*/
/**
* swagger2
* @author yuxue
* @date 2018-09-07
*/
@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Image Recognition API")
.description("图像识别技术")
.version("1.0.0")
.build();
}
}

@ -0,0 +1,51 @@
package com.yuxue.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@ComponentScan("com.yuxue.auth.service.impl")
@EnableAsync
public class ThreadPoolConfig implements AsyncConfigurer {
@Bean(name = "taskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(4);
// 设置最大线程数
executor.setMaxPoolSize(8);
// 设置队列容量
executor.setQueueCapacity(100);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("localThread:");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
@Override
public Executor getAsyncExecutor() {
return taskExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}

@ -0,0 +1,126 @@
package com.yuxue.constant;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author yuxue
* @date 2018-09-07
*/
public class Constant {
public static final String UTF8 = "UTF-8";
// 车牌识别, 默认车牌图片保存路径
// public static String DEFAULT_DIR = "./PlateDetect/"; // 使用项目的相对路径
public static String DEFAULT_DIR = "D:/PlateDetect/"; // 使用盘符的绝对路径
// 车牌识别, 默认车牌图片处理过程temp路径
// public static String DEFAULT_TEMP_DIR = "./PlateDetect/temp/"; // 使用项目的相对路径
public static String DEFAULT_TEMP_DIR = "D:/PlateDetect/temp/"; // 使用盘符的绝对路径
// 车牌识别,默认处理图片类型
public static String DEFAULT_TYPE = "png,jpg,jpeg";
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 final int DEFAULT_WIDTH = 136; // cols
public static final int DEFAULT_HEIGHT = 36; // rows
// 车牌识别,判断是否车牌的正则表达式
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
public final static char strCharacters[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', /* 没有I */ 'J', 'K', 'L', 'M', 'N', /* 没有O */'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
// 没有I和0, 10个数字与24个英文字符之和
public final static Integer numCharacter = strCharacters.length;
// 并不全面,有些省份没有训练数据所以没有字符
// 有些后面加数字2的表示在训练时常看到字符的一种变形也作为训练数据存储
public final static String strChinese[] = {
"zh_cuan", /*川*/
"zh_e", /*鄂*/
"zh_gan", /*赣*/
"zh_gan1", /*甘*/
"zh_gui", /*贵*/
"zh_gui1", /*桂*/
"zh_hei", /*黑*/
"zh_hu", /*沪*/
"zh_ji", /*冀*/
"zh_jin", /*津*/
"zh_jing", /*京*/
"zh_jl", /*吉*/
"zh_liao", /*辽*/
"zh_lu", /*鲁*/
"zh_meng", /*蒙*/
"zh_min", /*闽*/
"zh_ning", /*宁*/
"zh_qing", /*青*/
"zh_qiong", /*琼*/
"zh_shan", /*陕*/
"zh_su", /*苏*/
"zh_sx", /*晋*/
"zh_wan", /*皖*/
"zh_xiang", /*湘*/
"zh_xin", /*新*/
"zh_yu", /*豫*/
"zh_yu1", /*渝*/
"zh_yue", /*粤*/
"zh_yun", /*云*/
"zh_zang", /*藏*/
"zh_zhe" /*浙*/
};
/* 34+31=65 34个字符跟31个汉字 */
public final static Integer numAll = strCharacters.length + strChinese.length;
public static Map<String, String> KEY_CHINESE_MAP = new HashMap<String, String>();
static {
if (KEY_CHINESE_MAP.isEmpty()) {
KEY_CHINESE_MAP.put("zh_cuan", "川");
KEY_CHINESE_MAP.put("zh_e", "鄂");
KEY_CHINESE_MAP.put("zh_gan", "赣");
KEY_CHINESE_MAP.put("zh_gan1", "甘");
KEY_CHINESE_MAP.put("zh_gui", "贵");
KEY_CHINESE_MAP.put("zh_gui1", "桂");
KEY_CHINESE_MAP.put("zh_hei", "黑");
KEY_CHINESE_MAP.put("zh_hu", "沪");
KEY_CHINESE_MAP.put("zh_ji", "冀");
KEY_CHINESE_MAP.put("zh_jin", "津");
KEY_CHINESE_MAP.put("zh_jing", "京");
KEY_CHINESE_MAP.put("zh_jl", "吉");
KEY_CHINESE_MAP.put("zh_liao", "辽");
KEY_CHINESE_MAP.put("zh_lu", "鲁");
KEY_CHINESE_MAP.put("zh_meng", "蒙");
KEY_CHINESE_MAP.put("zh_min", "闽");
KEY_CHINESE_MAP.put("zh_ning", "宁");
KEY_CHINESE_MAP.put("zh_qing", "青");
KEY_CHINESE_MAP.put("zh_qiong", "琼");
KEY_CHINESE_MAP.put("zh_shan", "陕");
KEY_CHINESE_MAP.put("zh_su", "苏");
KEY_CHINESE_MAP.put("zh_sx", "晋");
KEY_CHINESE_MAP.put("zh_wan", "皖");
KEY_CHINESE_MAP.put("zh_xiang", "湘");
KEY_CHINESE_MAP.put("zh_xin", "新");
KEY_CHINESE_MAP.put("zh_yu", "豫");
KEY_CHINESE_MAP.put("zh_yu1", "渝");
KEY_CHINESE_MAP.put("zh_yue", "粤");
KEY_CHINESE_MAP.put("zh_yun", "云");
KEY_CHINESE_MAP.put("zh_zang", "藏");
KEY_CHINESE_MAP.put("zh_zhe", "浙");
}
}
}

@ -0,0 +1,41 @@
package com.yuxue.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.yuxue.annotation.RetExclude;
import springfox.documentation.annotations.ApiIgnore;
@ApiIgnore
@Controller
public class CommonController {
@RetExclude
@RequestMapping(value = "", method = { RequestMethod.GET })
public String doc() {
return "redirect:swagger-ui.html";
}
@RetExclude
@RequestMapping(value = "login", method = { RequestMethod.GET })
public String loginPage() {
return "home/login";
}
@RetExclude
@RequestMapping(value = "index", method = { RequestMethod.GET })
public String indexPage() {
return "home/index";
}
@RetExclude
@RequestMapping(value = "unauthorized", method = { RequestMethod.GET })
public String unauthorizedPage() {
return "unauthorized";
}
}

@ -0,0 +1,49 @@
package com.yuxue.controller;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
/**
*
* Detects faces in an image, draws boxes around them,
* and writes the results to "faceDetection.png".
*/
public class FaceController {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
// Create a face detector from the cascade file in the resources directory.
// 创建识别器
CascadeClassifier faceDetector = new CascadeClassifier("/src/main/resources/haarcascades/lbpcascade_frontalface.xml");
String imgPath = "/src/main/resources/DetectFace/AverageMaleFace.jpg";
Mat image = Imgcodecs.imread(imgPath);
Mat dst = new Mat();
Imgproc.Canny(image, dst, 130, 250);
// Detect faces in the image. MatOfRect is a special container class for Rect.
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(dst, faceDetections);
System.out.println(String.format("识别出 %s 张人脸", faceDetections.toArray().length));
// Draw a bounding box around each face.
for (Rect rect : faceDetections.toArray()) {
// Core.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0));
}
// Save the visualized detection.
// System.out.println(String.format("Writing %s", filename));
//Highgui.imwrite(filename, image);
}
}

@ -0,0 +1,90 @@
package com.yuxue.controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.yuxue.annotation.RetExclude;
import com.yuxue.exception.ResultReturnException;
import com.yuxue.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
@Api(description = "文件管理")
@RestController
@RequestMapping("/file")
public class FileController {
@Autowired
private FileService service;
/**
*
* D:\\PlateDetect\\ png,jpg,jpeg
* list
* @param dir
* @return
*/
@ApiOperation(value = "获取文件结构", notes = "")
@ApiImplicitParam(name = "dir", value = "文件夹路径", required = true, paramType = "query", dataType = "String")
@RequestMapping(value = "/getFileTreeByDir", method = RequestMethod.GET)
public Object getFileTreeByDir(String dir, String typeFilter) {
try {
if(null != dir) {
dir = URLDecoder.decode(dir, "utf-8");
}
} catch (UnsupportedEncodingException e) {
throw new ResultReturnException("dir参数异常");
}
return service.getFileTreeByDir(dir, typeFilter);
}
/**
*
* @param filePath
* @param response
* @return
* @throws IOException
*/
@RetExclude
@ApiOperation(value = "预览文件", notes = "根据路径,直接读取盘符文件; 返回输出流")
@GetMapping(value = "/readFile", produces= {"image/jpeg"})
public ResponseEntity<InputStreamResource> readFile(String filePath, HttpServletResponse response) throws IOException {
try {
filePath = URLDecoder.decode(filePath, "utf-8");
} catch (UnsupportedEncodingException e) {
throw new ResultReturnException("filePath参数异常");
}
//文件输出流,输出到客户端
File file = service.readFile(filePath);
InputStreamResource isr = new InputStreamResource(new FileInputStream(file));
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<InputStreamResource>(isr, headers, HttpStatus.OK);
}
}

@ -0,0 +1,49 @@
package com.yuxue.controller;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
/**
* opencv demo
* opencv
* windows
* 1openvphttps://opencv.org/releases/page/2/ 当前使用4.0.1版本
* 2exe \build\java\x64\opencv_java401.dll \build\x64\vc14\bin\
* 3eclipseUser Libraries
* 4build pathlib
*
* demoopencvdemo使maven
*
* @author yuxue
* @date 2020-04-22 14:04
*/
public class OpencvDemo {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
System.out.println("Welcome to OpenCV " + Core.VERSION);
Mat m1 = Mat.eye(3, 3, CvType.CV_8UC1);
System.out.println("m = " + m1.dump());
System.err.println("==================");
Mat m = new Mat(5, 10, CvType.CV_8UC1, new Scalar(0));
System.out.println("OpenCV Mat: " + m);
Mat mr1 = m.row(1);
mr1.setTo(new Scalar(1));
Mat mc5 = m.col(5);
mc5.setTo(new Scalar(5));
System.out.println("OpenCV Mat data:\n" + m.dump());
}
}

@ -0,0 +1,92 @@
package com.yuxue.controller;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.yuxue.exception.ResultReturnException;
import com.yuxue.service.PlateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
@Api(description = "车牌识别")
@RestController
@RequestMapping("/plate")
public class PlateController {
@Autowired
private PlateService service;
/**
* d:/PlateDetect
*
* temp
*/
@ApiOperation(value = "更新IMG文件基础信息", notes = "")
@RequestMapping(value = "/refreshFileInfo", method = RequestMethod.GET)
public void refreshFileInfo() {
service.refreshFileInfo();
}
/**
*
*
* ; 线
*/
@ApiOperation(value = "图片车牌识别", notes = "路径不能包含中文opencv路径转码过程乱码会报异常")
@RequestMapping(value = "/recogniseAll", method = RequestMethod.GET)
public Object recogniseAll() {
return service.recogniseAll();
}
/**
*
* path
* temp/timestamptimestamp
* temp
*
*/
@ApiOperation(value = "图片车牌识别", notes = "路径不能包含中文opencv路径转码过程乱码会报异常")
@ApiImplicitParams({
@ApiImplicitParam(name = "filePath", value = "文件路径", required = true, paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "reRecognise", value = "重新识别", paramType = "query", dataType = "Boolean", defaultValue="false")
})
@RequestMapping(value = "/recognise", method = RequestMethod.GET)
public Object recognise(String filePath, Boolean reRecognise) {
try {
if(null != filePath) {
filePath = URLDecoder.decode(filePath, "utf-8");
}
if(null == reRecognise) {
reRecognise = false;
}
} catch (UnsupportedEncodingException e) {
throw new ResultReturnException("filePath参数异常");
}
return service.recognise(filePath, reRecognise);
}
@ApiOperation(value = "获取处理步骤", notes = "")
@RequestMapping(value = "/getProcessStep", method = RequestMethod.GET)
public Object getProcessStep() {
return service.getProcessStep();
}
}

@ -0,0 +1,84 @@
package com.yuxue.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.yuxue.entity.SystemMenuEntity;
import com.yuxue.service.SystemMenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
@Api(description = "菜单管理")
@RestController
@RequestMapping("/systemMenu")
public class SystemMenuController {
@Autowired
private SystemMenuService service;
/**
*
* @param pageNo
* @param pageSize
* @param entity
*/
@ApiOperation(value = "分页获取记录", notes = "分页获取记录")
@ApiImplicitParams({
@ApiImplicitParam(name = "pageNo", value = "当前页码", required = true, paramType = "query", dataType = "Integer", defaultValue = "1"),
@ApiImplicitParam(name = "pageSize", value = "每页数量", required = true, paramType = "query", dataType = "Integer", defaultValue = "10"),
@ApiImplicitParam(name = "map", value = "举例:{} or {\"name\":\"张三\"}", dataType = "entity")
})
@RequestMapping(value = "/queryByPage", method = RequestMethod.POST)
public Object queryByPage(@RequestParam Integer pageNo, @RequestParam Integer pageSize, @RequestBody Map<String, Object> map) {
return service.queryByPage(pageNo, pageSize, map);
}
@ApiOperation(value = "按条件查询", notes = "不分页", response = SystemMenuEntity.class)
@ApiImplicitParams({
@ApiImplicitParam(name = "map", value = "举例:{} or {\"name\":\"张三\"}", dataType = "entity")
})
@RequestMapping(value = "/queryByCondition", method = RequestMethod.POST)
public Object queryByCondition(@RequestBody Map<String, Object> map) {
return service.queryByCondition(map);
}
/**
* PostID
* @param entity
*/
@ApiOperation(value = "新增数据成功返回ID", notes = "新增数据成功返回ID")
@ApiImplicitParam(name = "entity", value = "举例:{} or {\"name\":\"张三\"}", required = true, dataType = "entity")
@RequestMapping(value = "", method = RequestMethod.POST)
public Object save(@RequestBody SystemMenuEntity entity) {
return service.save(entity);
}
/**
*
* @return
*/
@ApiOperation(value = "获取登录用户菜单", notes = "")
@GetMapping("/getUserMenu")
public Object getUserMenu() {
return service.getUserMenu();
}
}

@ -0,0 +1,83 @@
package com.yuxue.easypr.core;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_ml.ANN_MLP;
import com.yuxue.constant.Constant;
import com.yuxue.util.Convert;
/**
*
* @author yuxue
* @date 2020-04-24 15:31
*/
public class CharsIdentify {
private ANN_MLP ann=ANN_MLP.create();
public CharsIdentify() {
loadModel(Constant.DEFAULT_ANN_PATH);
}
public void loadModel(String path) {
this.ann.clear();
// 加载ann配置文件 图像转文字的训练库文件
//ann=ANN_MLP.loadANN_MLP(path, "ann");
ann = ANN_MLP.load(path);
}
/**
* @param input
* @param isChinese
* @return
*/
public String charsIdentify(final Mat input, final Boolean isChinese, final Boolean isSpeci) {
String result = "";
/*String name = "D:/PlateDetect/train/chars_recognise_ann/" + System.currentTimeMillis() + ".jpg";
opencv_imgcodecs.imwrite(name, input);
Mat img = opencv_imgcodecs.imread(name);
Mat f = CoreFunc.features(img, Constant.predictSize);*/
Mat f = CoreFunc.features(input, Constant.predictSize);
int index = this.classify(f, isChinese, isSpeci);
System.err.print(index);
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(result);
return result;
}
private int classify(final Mat f, final Boolean isChinses, final Boolean isSpeci) {
int result = -1;
Mat output = new Mat(1, 140, opencv_core.CV_32F);
ann.predict(f, output, 0); // 预测结果
int ann_min = (!isChinses) ? ((isSpeci) ? 10 : 0) : Constant.numCharacter;
int ann_max = (!isChinses) ? Constant.numCharacter : Constant.numAll;
float maxVal = -2;
for (int j = ann_min; j < ann_max; j++) {
float val = Convert.toFloat(output.ptr(0, j));
if (val > maxVal) {
maxVal = val;
result = j;
}
}
return result;
}
}

@ -0,0 +1,136 @@
package com.yuxue.easypr.core;
import java.util.Vector;
import org.bytedeco.javacpp.opencv_core.Mat;
import com.yuxue.enumtype.PlateColor;
/**
*
*
* @author yuxue
* @date 2020-04-24 15:31
*/
public class CharsRecognise {
private CharsSegment charsSegment = new CharsSegment();
private CharsIdentify charsIdentify = new CharsIdentify();
public void loadANN(final String s) {
charsIdentify.loadModel(s);
}
/**
* Chars segment and identify
*
* @param plate the input plate
* @return the result of plate recognition
*/
public String charsRecognise(final Mat plate, String tempPath) {
// 车牌字符方块集合
Vector<Mat> matVec = new Vector<Mat>();
// 车牌识别结果
String plateIdentify = "";
int result = charsSegment.charsSegment(plate, matVec, tempPath);
if (0 == result) {
for (int j = 0; j < matVec.size(); j++) {
Mat charMat = matVec.get(j);
// 默认首个字符块是中文字符 第二个字符块是字母
String charcater = charsIdentify.charsIdentify(charMat, (0 == j), (1 == j));
plateIdentify = plateIdentify + charcater;
}
}
return plateIdentify;
}
/**
*
*
* @param isDebug
*/
public void setCRDebug(final boolean isDebug) {
charsSegment.setDebug(isDebug);
}
/**
*
*
* @return
*/
public boolean getCRDebug() {
return charsSegment.getDebug();
}
/**
*
*
* @param input
* @return
*/
public final String getPlateType(final Mat input) {
PlateColor result = CoreFunc.getPlateType(input, true);
return result.desc;
}
/**
*
*
* @param param
*/
public void setLiuDingSize(final int param) {
charsSegment.setLiuDingSize(param);
}
/**
*
*
* @param param
*/
public void setColorThreshold(final int param) {
charsSegment.setColorThreshold(param);
}
/**
*
*
* @param param
*/
public void setBluePercent(final float param) {
charsSegment.setBluePercent(param);
}
/**
*
*
* @param param
*/
public final float getBluePercent() {
return charsSegment.getBluePercent();
}
/**
*
*
* @param param
*/
public void setWhitePercent(final float param) {
charsSegment.setWhitePercent(param);
}
/**
*
*
* @param param
*/
public final float getWhitePercent() {
return charsSegment.getWhitePercent();
}
}

@ -0,0 +1,453 @@
package com.yuxue.easypr.core;
import static com.yuxue.easypr.core.CoreFunc.getPlateType;
import static org.bytedeco.javacpp.opencv_core.CV_32F;
import static org.bytedeco.javacpp.opencv_core.countNonZero;
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.CV_RGB2GRAY;
import static org.bytedeco.javacpp.opencv_imgproc.CV_THRESH_BINARY;
import static org.bytedeco.javacpp.opencv_imgproc.CV_THRESH_BINARY_INV;
import static org.bytedeco.javacpp.opencv_imgproc.CV_THRESH_OTSU;
import static org.bytedeco.javacpp.opencv_imgproc.INTER_LINEAR;
import static org.bytedeco.javacpp.opencv_imgproc.boundingRect;
import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
import static org.bytedeco.javacpp.opencv_imgproc.findContours;
import static org.bytedeco.javacpp.opencv_imgproc.resize;
import static org.bytedeco.javacpp.opencv_imgproc.threshold;
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_imgcodecs;
import com.yuxue.enumtype.PlateColor;
import com.yuxue.util.Convert;
/**
*
* @author yuxue
* @date 2020-04-28 09:45
*/
public class CharsSegment {
// preprocessChar所用常量
final static int CHAR_SIZE = 20;
final static int HORIZONTAL = 1;
final static int VERTICAL = 0;
final static int DEFAULT_LIUDING_SIZE = 7;
final static int DEFAULT_MAT_WIDTH = 136;
final static int DEFAULT_COLORTHRESHOLD = 150;
final static float DEFAULT_BLUEPERCEMT = 0.3f;
final static float DEFAULT_WHITEPERCEMT = 0.1f;
private int liuDingSize = DEFAULT_LIUDING_SIZE;
private int theMatWidth = DEFAULT_MAT_WIDTH;
private int colorThreshold = DEFAULT_COLORTHRESHOLD;
private float bluePercent = DEFAULT_BLUEPERCEMT;
private float whitePercent = DEFAULT_WHITEPERCEMT;
private boolean isDebug = true;
/**
*
*
* @param input
* @param resultVec
* @return <ul>
* <li>more than zero: the number of chars;
* <li>-3: null;
* </ul>
*/
public int charsSegment(final Mat input, Vector<Mat> resultVec, String tempPath) {
if (input.data().isNull()) {
return -3;
}
// 判断车牌颜色以此确认threshold方法
Mat img_threshold = new Mat();
Mat input_grey = new Mat();
cvtColor(input, input_grey, CV_RGB2GRAY);
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)));
PlateColor color= getPlateType(tmpMat, true);
switch (color) {
case BLUE:
threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
break;
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(tempPath + "debug_char_threshold.jpg", img_threshold);
}
// 去除车牌上方的柳钉以及下方的横线等干扰 //会导致虚拟机崩溃
// clearLiuDing(img_threshold);
/*if (this.isDebug) {
String str = tempPath + "debug_char_clearLiuDing.jpg";
opencv_imgcodecs.imwrite(str, img_threshold);
}*/
// 找轮廓
Mat img_contours = new Mat();
img_threshold.copyTo(img_contours);
MatVector contours = new MatVector();
findContours(img_contours, contours, // a vector of contours
CV_RETR_EXTERNAL, // retrieve the external contours
CV_CHAIN_APPROX_NONE); // all pixels of each contours
// Remove patch that are no inside limits of aspect ratio and area.
// 将不符合特定尺寸的图块排除出去
Vector<Rect> vecRect = new Vector<Rect>();
for (int i = 0; i < contours.size(); ++i) {
Rect mr = boundingRect(contours.get(i));
Mat contour = new Mat(img_threshold, mr);
if (this.isDebug) {
String str = tempPath + "debug_char_contour"+i+".jpg";
opencv_imgcodecs.imwrite(str, contour);
}
if (verifySizes(contour)) { // 将不符合特定尺寸的图块排除出去
vecRect.add(mr);
}
}
if (vecRect.size() == 0) {
return -3;
}
Vector<Rect> sortedRect = new Vector<Rect>();
// 对符合尺寸的图块按照从左到右进行排序
SortRect(vecRect, sortedRect);
// 获得指示城市的特定Rect,如苏A的"A"
int specIndex = GetSpecificRect(sortedRect, color);
if (this.isDebug) {
if (specIndex < sortedRect.size()) {
Mat specMat = new Mat(img_threshold, sortedRect.get(specIndex));
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));
} else {
return -3;
}
if (this.isDebug) {
Mat chineseMat = new Mat(img_threshold, chineseRect);
String str = tempPath + "debug_chineseMat.jpg";
opencv_imgcodecs.imwrite(str, chineseMat);
}
// 新建一个全新的排序Rect
// 将中文字符Rect第一个加进来因为它肯定是最左边的
// 其余的Rect只按照顺序去6个车牌只可能是7个字符这样可以避免阴影导致的“1”字符
Vector<Rect> newSortedRect = new Vector<Rect>();
newSortedRect.add(chineseRect);
RebuildRect(sortedRect, newSortedRect, specIndex, color);
if (newSortedRect.size() == 0) {
return -3;
}
for (int i = 0; i < newSortedRect.size(); i++) {
Rect mr = newSortedRect.get(i);
Mat auxRoi = new Mat(img_threshold, mr);
auxRoi = preprocessChar(auxRoi);
if (this.isDebug) {
String str = tempPath + "debug_char_auxRoi_" + Integer.valueOf(i).toString() + ".jpg";
opencv_imgcodecs.imwrite(str, auxRoi);
}
resultVec.add(auxRoi);
}
return 0;
}
/**
*
* @param r
* @return
*/
public static Boolean verifySizes(Mat r) {
float aspect = 45.0f / 90.0f;
float charAspect = (float) r.cols() / (float) r.rows();
float error = 0.7f;
float minHeight = 10f;
float maxHeight = 35f;
// We have a different aspect ratio for number 1, and it can be ~0.2
float minAspect = 0.05f;
float maxAspect = aspect + aspect * error;
// area of pixels
float area = countNonZero(r);
// bb area
float bbArea = r.cols() * r.rows();
// % of pixel in area
float percPixels = area / bbArea;
return percPixels <= 1 && charAspect > minAspect && charAspect < maxAspect && r.rows() >= minHeight && r.rows() < maxHeight;
}
/**
* :
*
* @param in
* @return
*/
private Mat preprocessChar(Mat in) {
int h = in.rows();
int w = in.cols();
int charSize = CHAR_SIZE;
Mat transformMat = Mat.eye(2, 3, CV_32F).asMat();
int m = Math.max(w, h);
transformMat.ptr(0, 2).put(Convert.getBytes(((m - w) / 2f)));
transformMat.ptr(1, 2).put(Convert.getBytes((m - h) / 2f));
Mat warpImage = new Mat(m, m, in.type());
warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, opencv_core.BORDER_CONSTANT, new Scalar(0));
Mat out = new Mat();
resize(warpImage, out, new Size(charSize, charSize));
return out;
}
/**
*
* <p>
* X0 X
*
* @param img
* @return
*/
private Mat clearLiuDing(Mat img) {
final int x = this.liuDingSize;
Mat jump = Mat.zeros(1, img.rows(), CV_32F).asMat();
for (int i = 0; i < img.rows(); i++) {
int jumpCount = 0;
for (int j = 0; j < img.cols() - 1; j++) {
if (img.ptr(i, j).get() != img.ptr(i, j + 1).get())
jumpCount++;
}
jump.ptr(i).put(Convert.getBytes((float) jumpCount));
}
for (int i = 0; i < img.rows(); i++) {
if (Convert.toFloat(jump.ptr(i)) <= x) {
for (int j = 0; j < img.cols(); j++) {
img.ptr(i, j).put((byte) 0);
}
}
}
return img;
}
/**
*
*
* @param rectSpe
* @return
*/
private Rect GetChineseRect(final Rect rectSpe) {
int height = rectSpe.height();
float newwidth = rectSpe.width() * 1.15f;
int x = rectSpe.x();
int y = rectSpe.y();
int newx = x - (int) (newwidth * 1.15);
newx = Math.max(newx, 0);
Rect a = new Rect(newx, y, (int) newwidth, height);
return a;
}
/**
* RectA7003XA
*
* @param vecRect
* @return
*/
private int GetSpecificRect(final Vector<Rect> vecRect, PlateColor color) {
Vector<Integer> xpositions = new Vector<Integer>();
int maxHeight = 0;
int maxWidth = 0;
for (int i = 0; i < vecRect.size(); i++) {
xpositions.add(vecRect.get(i).x());
if (vecRect.get(i).height() > maxHeight) {
maxHeight = vecRect.get(i).height();
}
if (vecRect.get(i).width() > maxWidth) {
maxWidth = vecRect.get(i).width();
}
}
int specIndex = 0;
for (int i = 0; i < vecRect.size(); i++) {
Rect mr = vecRect.get(i);
int midx = mr.x() + mr.width() / 2;
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;
}
}
}
return specIndex;
}
/**
*
* <ul>
* <li>RectRect;
* <li>Rect6Rect
* <ul>
*
* @param vecRect
* @param outRect
* @param specIndex
* @return
*/
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)
continue;
outRect.add(vecRect.get(i));
if (--count == 0)
break;
}
return 0;
}
/**
* Rect
*
* @param vecRect
* @param out
* @return
*/
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) {
orderIndex.add(i);
xpositions.add(vecRect.get(i).x());
}
float min = xpositions.get(0);
int minIdx;
for (int i = 0; i < xpositions.size(); ++i) {
min = xpositions.get(i);
minIdx = i;
for (int j = i; j < xpositions.size(); ++j) {
if (xpositions.get(j) < min) {
min = xpositions.get(j);
minIdx = j;
}
}
int aux_i = orderIndex.get(i);
int aux_min = orderIndex.get(minIdx);
orderIndex.remove(i);
orderIndex.insertElementAt(aux_min, i);
orderIndex.remove(minIdx);
orderIndex.insertElementAt(aux_i, minIdx);
float aux_xi = xpositions.get(i);
float aux_xmin = xpositions.get(minIdx);
xpositions.remove(i);
xpositions.insertElementAt((int) aux_xmin, i);
xpositions.remove(minIdx);
xpositions.insertElementAt((int) aux_xi, minIdx);
}
for (int i = 0; i < orderIndex.size(); i++)
out.add(vecRect.get(orderIndex.get(i)));
return;
}
public void setLiuDingSize(int param) {
this.liuDingSize = param;
}
public void setColorThreshold(int param) {
this.colorThreshold = param;
}
public void setBluePercent(float param) {
this.bluePercent = param;
}
public final float getBluePercent() {
return this.bluePercent;
}
public void setWhitePercent(float param) {
this.whitePercent = param;
}
public final float getWhitePercent() {
return this.whitePercent;
}
public boolean getDebug() {
return this.isDebug;
}
public void setDebug(boolean isDebug) {
this.isDebug = isDebug;
}
}

@ -0,0 +1,269 @@
package com.yuxue.easypr.core;
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;
import com.yuxue.enumtype.PlateColor;
/**
*
* @author yuxue
* @date 2020-05-16 21:09
*/
public class CoreFunc {
/**
*
*
* @param src
* RGB
* @param r
*
* @param adaptive_minsv
* SVadaptive_minsvbool
* <ul>
* <li>trueH
* <li>false使minabs_sv
* </ul>
* @return 02552550
*/
public static Mat colorMatch(final Mat src, final PlateColor r, final boolean adaptive_minsv) {
final float max_sv = 255;
final float minref_sv = 64;
final float minabs_sv = 95;
// 转到HSV空间进行处理颜色搜索主要使用的是H分量进行蓝色与黄色的匹配工作
Mat src_hsv = new Mat();
opencv_imgproc.cvtColor(src, src_hsv, opencv_imgproc.CV_BGR2HSV);
MatVector hsvSplit = new MatVector();
opencv_core.split(src_hsv, hsvSplit);
opencv_imgproc.equalizeHist(hsvSplit.get(2), hsvSplit.get(2));
opencv_core.merge(hsvSplit, src_hsv);
// 匹配模板基色,切换以查找想要的基色
int min_h = r.minH;
int max_h = r.maxH;
float diff_h = (float) ((max_h - min_h) / 2);
int avg_h = (int) (min_h + diff_h);
int channels = src_hsv.channels();
int nRows = src_hsv.rows();
// 图像数据列需要考虑通道数的影响;
int nCols = src_hsv.cols() * channels;
// 连续存储的数据,按一行处理
if (src_hsv.isContinuous()) {
nCols *= nRows;
nRows = 1;
}
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;
boolean colorMatched = false;
if (H > min_h && H < max_h) {
int Hdiff = 0;
if (H > avg_h)
Hdiff = H - avg_h;
else
Hdiff = avg_h - H;
float Hdiff_p = Hdiff / diff_h;
float min_sv = 0;
if (true == adaptive_minsv)
min_sv = minref_sv - minref_sv / 2 * (1 - Hdiff_p);
else
min_sv = minabs_sv;
if ((S > min_sv && S <= max_sv) && (V > min_sv && V <= max_sv))
colorMatched = true;
}
if (colorMatched == true) {
p.put(j, (byte) 0);
p.put(j + 1, (byte) 0);
p.put(j + 2, (byte) 255);
} else {
p.put(j, (byte) 0);
p.put(j + 1, (byte) 0);
p.put(j + 2, (byte) 0);
}
}
}
// 获取颜色匹配后的二值灰度图
MatVector hsvSplit_done = new MatVector();
opencv_core.split(src_hsv, hsvSplit_done);
Mat src_grey = hsvSplit_done.get(2);
return src_grey;
}
/**
*
*
* @param src
* mat
* @param r
*
* @param adaptive_minsv
* SVadaptive_minsvbool
* <ul>
* <li>trueH
* <li>false使minabs_sv
* </ul>
* @return
*/
public static boolean plateColorJudge(final Mat src, final PlateColor color, final boolean adaptive_minsv) {
// 判断阈值
final float thresh = 0.49f;
Mat gray = colorMatch(src, color, adaptive_minsv);
float percent = (float) opencv_core.countNonZero(gray) / (gray.rows() * gray.cols());
return (percent > thresh) ? true : false;
}
/**
* getPlateType
*
* @param src
* @param adaptive_minsv
* SVadaptive_minsvbool
* <ul>
* <li>trueH
* <li>false使minabs_sv
* </ul>
* @return
*/
public static PlateColor getPlateType(final Mat src, final boolean adaptive_minsv) {
if (plateColorJudge(src, PlateColor.BLUE, adaptive_minsv) == true) {
return PlateColor.BLUE;
} else if (plateColorJudge(src, PlateColor.YELLOW, adaptive_minsv) == true) {
return PlateColor.YELLOW;
} else if (plateColorJudge(src, PlateColor.GREEN, adaptive_minsv) == true) {
return PlateColor.GREEN;
} else {
return PlateColor.UNKNOWN;
}
}
/**
*
*
* @param img
* @param direction
* @return
*/
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];
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 = opencv_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;
}
/**
* Assign values to feature
* <p>
*
*
* @param in
* @param sizeData
* size = sizeData*sizeData, 0
* @return
*/
public static Mat features(final Mat in, final int sizeData) {
float[] vhist = projectedHistogram(in, Direction.VERTICAL);
float[] hhist = projectedHistogram(in, Direction.HORIZONTAL);
Mat lowData = new Mat();
if (sizeData > 0) {
// resize.cpp:3784: error: (-215:Assertion failed) !ssize.empty() in function 'cv::resize'
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, opencv_core.CV_32F).asMat();
FloatIndexer idx = out.createIndexer();
int j = 0;
for (int i = 0; i < vhist.length; ++i, ++j) {
idx.put(0, j, vhist[i]);
}
for (int i = 0; i < hhist.length; ++i, ++j) {
idx.put(0, j, hhist[i]);
}
for (int x = 0; x < lowData.cols(); x++) {
for (int y = 0; y < lowData.rows(); y++, ++j) {
float val = lowData.ptr(x, y).get(0) & 0xFF;
idx.put(0, j, val);
}
}
return out;
}
/**
*
* @param title
* @param src
*/
public static void showImage(final String title, final Mat src) {
if (src != null) {
opencv_highgui.imshow(title, src);
opencv_highgui.cvWaitKey(0);
}
}
}

@ -0,0 +1,90 @@
package com.yuxue.easypr.core;
import static com.yuxue.easypr.core.CoreFunc.features;
import static org.bytedeco.javacpp.opencv_core.merge;
import static org.bytedeco.javacpp.opencv_core.split;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.MatVector;
import org.bytedeco.javacpp.opencv_imgproc;
/**
*
* @author yuxue
* @date 2020-05-05 08:26
*/
public class Features implements SVMCallback {
/***
* EasyPRgetFeatures
*
* @param image
* @return
*/
@Override
public Mat getHisteqFeatures(final Mat image) {
return histeq(image);
}
private Mat histeq(Mat in) {
Mat out = new Mat(in.size(), in.type());
if (in.channels() == 3) {
Mat hsv = new Mat();
MatVector hsvSplit = new MatVector();
opencv_imgproc.cvtColor(in, hsv, opencv_imgproc.CV_BGR2HSV);
split(hsv, hsvSplit);
opencv_imgproc.equalizeHist(hsvSplit.get(2), hsvSplit.get(2));
merge(hsvSplit, hsv);
opencv_imgproc.cvtColor(hsv, out, opencv_imgproc.CV_HSV2BGR);
hsv = null;
hsvSplit = null;
System.gc();
} else if (in.channels() == 1) {
opencv_imgproc.equalizeHist(in, out);
}
return out;
}
/**
* EasyPRgetFeatures
*
* @param image
* @return
*/
@Override
public Mat getHistogramFeatures(Mat image) {
Mat grayImage = new Mat();
opencv_imgproc.cvtColor(image, grayImage, opencv_imgproc.CV_RGB2GRAY);
Mat img_threshold = new Mat();
opencv_imgproc.threshold(grayImage, img_threshold, 0, 255, opencv_imgproc.CV_THRESH_OTSU + opencv_imgproc.CV_THRESH_BINARY);
return features(img_threshold, 0);
}
/**
* SITF
*
* @param image
* @return
*/
@Override
public Mat getSIFTFeatures(final Mat image) {
// TODO: 待完善
return null;
}
/**
* HOG
*
* @param image
* @return
*/
@Override
public Mat getHOGFeatures(final Mat image) {
// TODO: 待完善
return null;
}
}

@ -0,0 +1,111 @@
package com.yuxue.easypr.core;
import java.util.Vector;
import org.bytedeco.javacpp.opencv_core.Mat;
/**
*
* 1 2
* @author yuxue
* @date 2020-04-24 15:33
*/
public class PlateDetect {
// 车牌定位, 图片处理对象
private PlateLocate plateLocate = new PlateLocate();
// 切图判断对象
private PlateJudge plateJudge = new PlateJudge();
/**
* @param src
* @param resultVec
* @return the error number
* <ul>
* <li>0: plate detected successfully;
* <li>-1: source Mat is empty;
* <li>-2: plate not detected.
* </ul>
*/
public int plateDetect(final Mat src, Vector<Mat> resultVec) {
Vector<Mat> matVec = plateLocate.plateLocate(src); // 定位
if (0 == matVec.size()) {
return -1;
}
if (0 != plateJudge.plateJudge(matVec, resultVec)) { //对多幅图像进行SVM判断
return -2;
}
return 0;
}
/**
*
* @param pdLifemode
*/
public void setPDLifemode(boolean pdLifemode) {
plateLocate.setLifemode(pdLifemode);
}
public void setGaussianBlurSize(int gaussianBlurSize) {
plateLocate.setGaussianBlurSize(gaussianBlurSize);
}
public final int getGaussianBlurSize() {
return plateLocate.getGaussianBlurSize();
}
public void setMorphSizeWidth(int morphSizeWidth) {
plateLocate.setMorphSizeWidth(morphSizeWidth);
}
public final int getMorphSizeWidth() {
return plateLocate.getMorphSizeWidth();
}
public void setMorphSizeHeight(int morphSizeHeight) {
plateLocate.setMorphSizeHeight(morphSizeHeight);
}
public final int getMorphSizeHeight() {
return plateLocate.getMorphSizeHeight();
}
public void setVerifyError(float verifyError) {
plateLocate.setVerifyError(verifyError);
}
public final float getVerifyError() {
return plateLocate.getVerifyError();
}
public void setVerifyAspect(float verifyAspect) {
plateLocate.setVerifyAspect(verifyAspect);
}
public final float getVerifyAspect() {
return plateLocate.getVerifyAspect();
}
public void setVerifyMin(int verifyMin) {
plateLocate.setVerifyMin(verifyMin);
}
public void setVerifyMax(int verifyMax) {
plateLocate.setVerifyMax(verifyMax);
}
public void setJudgeAngle(int judgeAngle) {
plateLocate.setJudgeAngle(judgeAngle);
}
public void setDebug(boolean debug, String tempPath) {
plateLocate.setDebug(debug);
plateLocate.setTempPath(tempPath);
}
}

@ -0,0 +1,107 @@
package com.yuxue.easypr.core;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_imgproc;
import java.util.Vector;
import org.bytedeco.javacpp.opencv_core.Mat;
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;
/**
*
* @author yuxue
* @date 2020-04-26 15: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);
}
/**
* EasyPRgetFeatures, imagesvmfeatures
*/
private SVMCallback features = new Features();
/**
* SVM
* @param inMat
* @return
*/
public int plateJudge(final Mat inMat) {
int ret = 1;
// 使用com.yuxue.train.SVMTrain 生成的训练库文件
Mat features = this.features.getHistogramFeatures(inMat);
/*Mat samples = features.reshape(1, 1);
samples.convertTo(samples, opencv_core.CV_32F);*/
Mat p = features.reshape(1, 1);
p.convertTo(p, opencv_core.CV_32FC1);
ret = (int) svm.predict(features);
return ret;
// 使用com.yuxue.train.PlateRecoTrain 生成的训练库文件
// 在使用的过程中,传入的样本切图要跟训练的时候处理切图的方法一致
/*Mat grayImage = new Mat();
opencv_imgproc.cvtColor(inMat, grayImage, opencv_imgproc.CV_RGB2GRAY);
Mat dst = new Mat();
opencv_imgproc.Canny(grayImage, dst, 130, 250);
Mat samples = dst.reshape(1, 1);
samples.convertTo(samples, opencv_core.CV_32F);*/
// 正样本为0 负样本为1
/*if(svm.predict(samples) <= 0) {
ret = 1;
}*/
/*ret = (int)svm.predict(samples);
System.err.println(ret);
return ret ;*/
}
/**
* SVM
* @param inVec
* @param resultVec
* @return
*/
public int plateJudge(Vector<Mat> inVec, Vector<Mat> resultVec) {
for (int j = 0; j < inVec.size(); j++) {
Mat inMat = inVec.get(j);
if (1 == plateJudge(inMat)) {
resultVec.add(inMat);
} else { // 再取中间部分判断一次
int w = inMat.cols();
int h = inMat.rows();
Mat tmpDes = inMat.clone();
Mat tmpMat = new Mat(inMat, new Rect((int) (w * 0.05), (int) (h * 0.1), (int) (w * 0.9), (int) (h * 0.8)));
opencv_imgproc.resize(tmpMat, tmpDes, new Size(inMat.size()));
if (plateJudge(tmpDes) == 1) {
resultVec.add(inMat);
}
}
}
return 0;
}
}

@ -0,0 +1,354 @@
package com.yuxue.easypr.core;
import java.util.Vector;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
import com.yuxue.constant.Constant;
import org.bytedeco.javacpp.opencv_imgcodecs;
import org.bytedeco.javacpp.opencv_core.CvPoint2D32f;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.MatVector;
import org.bytedeco.javacpp.opencv_core.Point;
import org.bytedeco.javacpp.opencv_core.Point2f;
import org.bytedeco.javacpp.opencv_core.RotatedRect;
import org.bytedeco.javacpp.opencv_core.Scalar;
import org.bytedeco.javacpp.opencv_core.Size;
/**
*
* @author yuxue
* @date 2020-04-24 15:33
*/
public class PlateLocate {
// PlateLocate所用常量
public static final int DEFAULT_GAUSSIANBLUR_SIZE = 5;
public static final int SOBEL_SCALE = 1;
public static final int SOBEL_DELTA = 0;
public static final int SOBEL_DDEPTH = CV_16S;
public static final int SOBEL_X_WEIGHT = 1;
public static final int SOBEL_Y_WEIGHT = 0;
public static final int DEFAULT_MORPH_SIZE_WIDTH = 17;
public static final int DEFAULT_MORPH_SIZE_HEIGHT = 3;
// showResultMat所用常量
public static final int WIDTH = 136;
public static final int HEIGHT = 36;
public static final int TYPE = CV_8UC3;
// verifySize所用常量
public static final int DEFAULT_VERIFY_MIN = 3;
public static final int DEFAULT_VERIFY_MAX = 20;
final float DEFAULT_ERROR = 0.6f;
final float DEFAULT_ASPECT = 3.75f;
// 角度判断所用常量
public static final int DEFAULT_ANGLE = 30;
// 高斯模糊所用变量
protected int gaussianBlurSize = DEFAULT_GAUSSIANBLUR_SIZE;
// 连接操作所用变量
protected int morphSizeWidth = DEFAULT_MORPH_SIZE_WIDTH;
protected int morphSizeHeight = DEFAULT_MORPH_SIZE_HEIGHT;
// verifySize所用变量
protected float error = DEFAULT_ERROR;
protected float aspect = DEFAULT_ASPECT;
protected int verifyMin = DEFAULT_VERIFY_MIN;
protected int verifyMax = DEFAULT_VERIFY_MAX;
// 角度判断所用变量
protected int angle = DEFAULT_ANGLE;
// 是否开启调试模式0关闭非0开启
protected boolean debug = true;
// 开启调试模式之后,切图文件保存路径
protected String tempPath = Constant.DEFAULT_TEMP_DIR + System.currentTimeMillis() + "/";
/**
*
* @param islifemode
*
*
*/
public void setLifemode(boolean islifemode) {
if (islifemode) {
setGaussianBlurSize(5);
setMorphSizeWidth(9);
setMorphSizeHeight(3);
setVerifyError(0.9f);
setVerifyAspect(4);
setVerifyMin(1);
setVerifyMax(30);
} else {
setGaussianBlurSize(DEFAULT_GAUSSIANBLUR_SIZE);
setMorphSizeWidth(DEFAULT_MORPH_SIZE_WIDTH);
setMorphSizeHeight(DEFAULT_MORPH_SIZE_HEIGHT);
setVerifyError(DEFAULT_ERROR);
setVerifyAspect(DEFAULT_ASPECT);
setVerifyMin(DEFAULT_VERIFY_MIN);
setVerifyMax(DEFAULT_VERIFY_MAX);
}
}
/**
*
* @param src
* @return Mat
*/
public Vector<Mat> plateLocate(Mat src) {
Vector<Mat> resultVec = new Vector<Mat>();
Mat src_blur = new Mat();
Mat src_gray = new Mat();
Mat grad = new Mat();
int scale = SOBEL_SCALE;
int delta = SOBEL_DELTA;
int ddepth = SOBEL_DDEPTH;
// 高斯模糊。Size中的数字影响车牌定位的效果。
GaussianBlur(src, src_blur, new Size(gaussianBlurSize, gaussianBlurSize), 0, 0, BORDER_DEFAULT);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + "debug_GaussianBlur.jpg", src_blur);
}
// Convert it to gray 将图像进行灰度化
cvtColor(src_blur, src_gray, CV_RGB2GRAY);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + "debug_gray.jpg", src_gray);
}
// 对图像进行Sobel 运算,得到的是图像的一阶水平方向导数。
// Generate grad_x and grad_y
Mat grad_x = new Mat();
Mat grad_y = new Mat();
Mat abs_grad_x = new Mat();
Mat abs_grad_y = new Mat();
Sobel(src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
Sobel(src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
// Total Gradient (approximate)
addWeighted(abs_grad_x, SOBEL_X_WEIGHT, abs_grad_y, SOBEL_Y_WEIGHT, 0, grad);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + "debug_Sobel.jpg", grad);
}
// 对图像进行二值化。将灰度图像每个像素点有256 个取值可能转化为二值图像每个像素点仅有1 和0 两个取值可能)。
Mat img_threshold = new Mat();
threshold(grad, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + "debug_threshold.jpg", img_threshold);
}
// 使用闭操作。对图像进行闭操作以后,可以看到车牌区域被连接成一个矩形装的区域。
Mat element = getStructuringElement(MORPH_RECT, new Size(morphSizeWidth, morphSizeHeight));
morphologyEx(img_threshold, img_threshold, MORPH_CLOSE, element);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + "debug_morphology.jpg", img_threshold);
}
// Find 轮廓 of possibles plates 求轮廓。求出图中所有的轮廓。这个算法会把全图的轮廓都计算出来,因此要进行筛选。
MatVector contours = new MatVector();
findContours(img_threshold, contours, // a vector of contours
CV_RETR_EXTERNAL, // 提取外部轮廓
CV_CHAIN_APPROX_NONE); // all pixels of each contours
Mat result = new Mat();
if (debug) {
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<RotatedRect> rects = new Vector<RotatedRect>();
for (int i = 0; i < contours.size(); ++i) {
RotatedRect mr = minAreaRect(contours.get(i));
if (verifySizes(mr))
rects.add(mr);
}
int k = 1;
for (int i = 0; i < rects.size(); i++) {
RotatedRect minRect = rects.get(i);
/*if (debug) {
Point2f rect_points = new Point2f(4);
minRect.points(rect_points);
for (int j = 0; j < 4; j++) {
Point pt1 = new Point(new CvPoint2D32f(rect_points.position(j)));
Point pt2 = new Point(new CvPoint2D32f(rect_points.position((j + 1) % 4)));
line(result, pt1, pt2, new Scalar(0, 255, 255, 255), 1, 8, 0);
}
}*/
// rotated rectangle drawing
// 旋转这部分代码确实可以将某些倾斜的车牌调整正,但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。
// 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试这段代码。
float r = minRect.size().width() / minRect.size().height();
float angle = minRect.angle();
Size rect_size = new Size((int) minRect.size().width(), (int) minRect.size().height());
if (r < 1) {
angle = 90 + angle;
rect_size = new Size(rect_size.height(), rect_size.width());
}
// 如果抓取的方块旋转超过m_angle角度则不是车牌放弃处理
if (angle - this.angle < 0 && angle + this.angle > 0) {
Mat img_rotated = new Mat();
Mat rotmat = getRotationMatrix2D(minRect.center(), angle, 1);
warpAffine(src, img_rotated, rotmat, src.size()); // CV_INTER_CUBIC
Mat resultMat = showResultMat(img_rotated, rect_size, minRect.center(), k++);
resultVec.add(resultMat);
}
}
return resultVec;
}
/**
* minAreaRect
*
* @param mr
* @return
*/
private boolean verifySizes(RotatedRect mr) {
float error = this.error;
// China car plate size: 440mm*140mmaspect 3.142857
float aspect = this.aspect;
int min = 44 * 14 * verifyMin; // minimum area
int max = 44 * 14 * verifyMax; // maximum area
// Get only patchs that match to a respect ratio.
float rmin = aspect - aspect * error;
float rmax = aspect + aspect * error;
int area = (int) (mr.size().height() * mr.size().width());
float r = mr.size().width() / mr.size().height();
if (r < 1)
r = mr.size().height() / mr.size().width();
return area >= min && area <= max && r >= rmin && r <= rmax;
}
/**
* 便
* @param src
* @param rect_size
* @param center
* @param index
* @return
*/
private Mat showResultMat(Mat src, Size rect_size, Point2f center, int index) {
Mat img_crop = new Mat();
getRectSubPix(src, rect_size, center, img_crop);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + "debug_crop_" + index + ".jpg", img_crop);
}
Mat resultResized = new Mat();
resultResized.create(HEIGHT, WIDTH, TYPE);
resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + "debug_resize_" + index + ".jpg", resultResized);
}
return resultResized;
}
public String getTempPath() {
return tempPath;
}
public void setTempPath(String tempPath) {
this.tempPath = tempPath;
}
public void setGaussianBlurSize(int gaussianBlurSize) {
this.gaussianBlurSize = gaussianBlurSize;
}
public final int getGaussianBlurSize() {
return this.gaussianBlurSize;
}
public void setMorphSizeWidth(int morphSizeWidth) {
this.morphSizeWidth = morphSizeWidth;
}
public final int getMorphSizeWidth() {
return this.morphSizeWidth;
}
public void setMorphSizeHeight(int morphSizeHeight) {
this.morphSizeHeight = morphSizeHeight;
}
public final int getMorphSizeHeight() {
return this.morphSizeHeight;
}
public void setVerifyError(float error) {
this.error = error;
}
public final float getVerifyError() {
return this.error;
}
public void setVerifyAspect(float aspect) {
this.aspect = aspect;
}
public final float getVerifyAspect() {
return this.aspect;
}
public void setVerifyMin(int verifyMin) {
this.verifyMin = verifyMin;
}
public void setVerifyMax(int verifyMax) {
this.verifyMax = verifyMax;
}
public void setJudgeAngle(int angle) {
this.angle = angle;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public boolean getDebug() {
return debug;
}
}

@ -0,0 +1,44 @@
package com.yuxue.easypr.core;
import org.bytedeco.javacpp.opencv_core.Mat;
/**
* @author Created by fanwenjie
* @author lin.yao
*
*/
public interface SVMCallback {
/***
* EasyPRgetFeatures,
*
* @param image
* @return
*/
public abstract Mat getHisteqFeatures(final Mat image);
/**
* EasyPRgetFeatures,
*
* @param image
* @return
*/
public abstract Mat getHistogramFeatures(final Mat image);
/**
* SITF
*
* @param image
* @return
*/
public abstract Mat getSIFTFeatures(final Mat image);
/**
* HOG
*
* @param image
* @return
*/
public abstract Mat getHOGFeatures(final Mat image);
}

@ -0,0 +1,81 @@
package com.yuxue.entity;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* t_plate_file
* @author yuxue
* 2020-04-30 11:04:47.169
*/
@Data
@NoArgsConstructor
public class PlateFileEntity implements Serializable {
/**
* id
*/
private Integer id;
/**
* fileName
*/
private String fileName;
/**
* filePath
*/
private String filePath;
/**
* fileType
*/
private String fileType;
/**
* fileLength
*/
private Integer fileLength;
/**
* plate
*/
private String plate;
/**
* plateColor
*/
private String plateColor;
/**
* lastRecoTime
*/
private String lastRecoTime;
/**
* tempPath
*/
private String tempPath;
/**
* recoPlate
*/
private String recoPlate;
/**
* recoColor
*/
private String recoColor;
/**
* recoCorrect
* 0 1 2 3
*/
private Integer recoCorrect;
private List<PlateRecoDebugEntity> debug;
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,66 @@
package com.yuxue.entity;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* t_plate_reco_debug
* @author yuxue
* 2020-04-30 16:17:58.795
*/
@Data
@NoArgsConstructor
public class PlateRecoDebugEntity implements Serializable {
/**
* id
*/
private Integer id;
/**
* parentId
*/
private Integer parentId;
/**
* fileName
*/
private String fileName;
/**
* filePath
*/
private String filePath;
/**
* debugType
*/
private String debugType;
/**
* fileLength
*/
private Integer fileLength;
/**
* lastRecoTime
*/
private String lastRecoTime;
/**
* recoPlate
*/
private String recoPlate;
/**
* plateColor
*/
private String plateColor;
/**
* sort
*/
private Integer sort;
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,93 @@
package com.yuxue.entity;
import java.util.HashMap;
import com.yuxue.exception.ErrorEnum;
/**
*
* @author yuxue
* @date 2018-09-07
*/
public class Result extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private static final Integer SUCCESS_CODE = 200;
private static final String SUCCESS_INFO = "Success!";
public Result() {
put("code", SUCCESS_CODE);
put("msg", SUCCESS_INFO);
put("success", true);
}
public Result(Object obj) {
put("code", SUCCESS_CODE);
put("msg", SUCCESS_INFO);
put("obj", obj);
put("success", true);
}
public static Result ok() {
return new Result();
}
public static Result ok(Object obj) {
return new Result(obj);
}
/**
*
*
*
* @param todo
* @return
*/
public static Result ok(Object obj, Object todo) {
Result result = new Result(obj);
result.put("todo", todo);
return result;
}
public static Result error() {
return error(ErrorEnum.COMMON_ERROR);
}
public static Result error(String msg) {
Result result = error(ErrorEnum.COMMON_ERROR);
result.put("msg", msg);
return result;
}
public static Result error(String msg, int code) {
Result result = error(ErrorEnum.COMMON_ERROR);
result.put("msg", msg);
result.put("code", code);
return result;
}
public static Result error(ErrorEnum fwWebError) {
Result result = new Result();
result.put("code", fwWebError.code);
result.put("msg", fwWebError.msg);
result.put("success", false);
return result;
}
public static Result error(int code, String msg) {
Result result = new Result();
result.put("code", code);
result.put("msg", msg);
result.put("success", false);
return result;
}
@Override
public Result put(String key, Object value) {
super.put(key, value);
return this;
}
}

@ -0,0 +1,53 @@
package com.yuxue.entity;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* t_system_menu
* @author
*/
@Data
@NoArgsConstructor
public class SystemMenuEntity implements Serializable {
private Integer id;
private String menuName;
private String menuUrl;
private Integer parentId;
private Integer sort;
private Integer menuLevel;
private String menuIcon;
private Integer showFlag;
private Integer platform;
private Integer menuType;
private String permission;
private Date updateTime;
private Integer editorId;
private String createTime;
private Integer creatorId;
private Integer version;
private Integer delFlag;
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,51 @@
package com.yuxue.entity;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* temp_plate_file
* @author yuxue
* 2020-04-30 09:39:59.928
*/
@Data
@NoArgsConstructor
public class TempPlateFileEntity implements Serializable {
/**
* id
*/
private Integer id;
/**
* fileName
*/
private String fileName;
/**
* filePath
*/
private String filePath;
/**
* fileType
*/
private String fileType;
/**
* fileLength
*/
private Long fileLength;
/**
* parentId
*/
private Integer parentId;
/**
* level
*/
private Integer level;
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,46 @@
package com.yuxue.enumtype;
public enum Direction {
VERTICAL("VERTICAL","垂直"),
HORIZONTAL("HORIZONTAL","水平"),
UNKNOWN("UNKNOWN","未知");
public final String code;
public final String desc;
Direction(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static String getDesc(String code) {
Direction[] enums = values();
for (Direction type : enums) {
if (type.code().equals(code)) {
return type.desc();
}
}
return null;
}
public static String getCode(String desc) {
Direction[] enums = values();
for (Direction type : enums) {
if (type.desc().equals(desc)) {
return type.code();
}
}
return null;
}
public String code() {
return this.code;
}
public String desc() {
return this.desc;
}
}

@ -0,0 +1,59 @@
package com.yuxue.enumtype;
/**
*
* @author yuxue
* @date 2020-05-08 12:38
*/
public enum PlateColor {
BLUE("BLUE","蓝牌", 100, 130),
GREEN("GREEN","绿牌", 38, 100),
YELLOW("YELLOW","黄牌", 15, 40),
UNKNOWN("UNKNOWN","未知", 0, 0);
public final String code;
public final String desc;
// opencv颜色识别的HSV中各个颜色所对应的H的范围 Orange 0-22 Yellow 22- 38 Green 38-75 Blue 75-130
public final int minH;
public final int maxH;
PlateColor(String code, String desc, int minH, int maxH) {
this.code = code;
this.desc = desc;
this.minH = minH;
this.maxH = maxH;
}
public static String getDesc(String code) {
PlateColor[] enums = values();
for (PlateColor type : enums) {
if (type.code().equals(code)) {
return type.desc();
}
}
return null;
}
public static String getCode(String desc) {
PlateColor[] enums = values();
for (PlateColor type : enums) {
if (type.desc().equals(desc)) {
return type.code();
}
}
return null;
}
public String code() {
return this.code;
}
public String desc() {
return this.desc;
}
}

@ -0,0 +1,69 @@
package com.yuxue.exception;
/**
*
* @author yuxue
* @date 2018-09-07
*/
public enum ErrorEnum {
// 200-->Success!
// 6000-->Fail
// common
COMMON_ERROR("Fail", 6000),
COMMON_PARAMS_ERR("提交参数不合法", 6001),
COMMON_PARAMS_ID_ERR("提交参数ID不合法", 6002),
COMMON_EMPTY_CONDITION_RESULT("没有找到符合条件的数据", 6003),
COMMON_PARAMS_NOT_EXIST("提交的字段不存在,或者参数格式错误", 6004),
// sql
SQL_ERROR("mysql通用错误", 6100),
SQL_INSERT_FAIL("增加失败", 6101),
SQL_DELETE_FAIL("删除失败", 6102),
SQL_UPDATE_FAIL("修改失败", 6103),
SQL_RECORD_EXIST("添加重复记录", 6104),
SQL_ID_NOT_EXIST("主键ID不能为空", 6105),
SQL_VERSION_NOT_EXIST("数据版本version不能为空", 6106),
// io
FILE_IO_ERROR("io通用错误", 6200),
FILE_NOT_EXIST("文件没找到,请联系管理员", 6201),
FILE_DATA_NULL("文档中不不存在有效的数据", 6202),
FILE_DATA_ERR("文档中的数据格式错误", 6203),
// form
INVALID_PASSWORD("密码格式错误", 6300),
INVALID_EMAIL("邮件格式错误", 6301),
INVALID_NAME("账号格式错误", 6302),
INVALID_PARAMS("填写字段不合法", 6303),
// shiro-login
NO_LOGIN("用户未登录", 401),
UNAUTHORIZED("权限不足", 7001),
ADMIN_ONLY("只有管理员账号可以调用这个接口", 6402),
NO_PERSSIOM("没有权限请求", 6403),
WRONG_ACCOUNT_OR_PSW("账号或密码错误", 6404),
WRONG_ACCOUNT_PSW("账号密码错误", 6405),
WRONG_ACCOUNT_WRONG("用户没有权限(令牌、用户名、密码错误)", 401),
// uploading
UPLOAD_FILE_TYPE_ERROR("上传文件格式错误", 6500),
UPLOAD_FILE_UPLOADING("uploading", 6501),
UPLOAD_FILE_NOT_EXIST("文件不存在", 6502),
UPLOAD_FILE_SIZE_MAX("上传的文件大小超出限制", 6503),
// es
ES_BIG_PAGE_SEARCH("单页查询数据不能超过10000!", 9000);
// NoSQL
public final String msg;
public final int code;
ErrorEnum(String msg, int code) {
this.msg = msg;
this.code = code;
}
}

@ -0,0 +1,53 @@
package com.yuxue.exception;
/**
* runtime
* @author yuxue
* @date 2018-09-07
*/
public class ResultReturnException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String msg = ErrorEnum.COMMON_ERROR.msg;
private int code = ErrorEnum.COMMON_ERROR.code;
public ResultReturnException(ErrorEnum error) {
super(error.msg);
this.msg = error.msg;
this.code = error.code;
}
public ResultReturnException(String msg) {
super(msg);
this.msg = msg;
}
public ResultReturnException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
@Deprecated
public ResultReturnException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
@Deprecated
public ResultReturnException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public int getCode() {
return code;
}
}

@ -0,0 +1,84 @@
package com.yuxue.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;
import com.yuxue.entity.Result;
/**
* RestController
* @author yuxue
* @date 2018-09-06
*/
@RestControllerAdvice
public class ResultReturnExceptionHandler {
protected static Logger log=LoggerFactory.getLogger(ResultReturnExceptionHandler.class);
/** 捕捉shiro的异常 *//*
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(ShiroException.class)
public Result handle401(ShiroException e) {
log.error(e.getMessage(), e);
return Result.error(ErrorEnum.UNAUTHORIZED);
}
*//** 捕捉UnauthorizedException *//*
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(UnauthorizedException.class)
public Result handle401() {
return Result.error(ErrorEnum.UNAUTHORIZED);
}*/
/** 文件上传大小异常 */
@ExceptionHandler(MultipartException.class)
public Result handleMultipart(Throwable t) {
log.error(t.getMessage(), t);
return Result.error(ErrorEnum.UPLOAD_FILE_SIZE_MAX);
}
/** jackson转换Bean * */
@ExceptionHandler(HttpMessageNotReadableException.class)
public Result handleJsonConv(Throwable t) {
log.error(t.getMessage(), t);
return Result.error(ErrorEnum.COMMON_PARAMS_NOT_EXIST);
}
/** 异常参数处理器 */
@ExceptionHandler(IllegalArgumentException.class)
public Result handleRRException(Throwable e) {
//log.error(e.getMessage(), e);
return Result.error(ErrorEnum.COMMON_PARAMS_ERR.code, e.getMessage());
}
/** 自定义异常 */
@ExceptionHandler(ResultReturnException.class)
public Result handleRRException(ResultReturnException e) {
log.error(exTraceBack(e), e);
return Result.error(e.getCode(), e.getMsg());
}
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
log.error(exTraceBack(e), e);
return Result.error("系统发生错误,请联系管理员");
}
public static String exTraceBack(Exception e) {
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = e.getStackTrace();
for (int i = 0; i < stackTrace.length; i++) {
sb.append("<---");
sb.append(String.format("[%s * %s] ", stackTrace[i].getClassName(), stackTrace[i].getMethodName()));
}
sb.append(e.getMessage());
return sb.toString();
}
}

@ -0,0 +1,25 @@
package com.yuxue.mapper;
import com.yuxue.entity.PlateFileEntity;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PlateFileMapper {
int deleteByPrimaryKey(Integer id);
int insert(PlateFileEntity record);
int insertSelective(PlateFileEntity record);
PlateFileEntity selectByPrimaryKey(Integer id);
List<PlateFileEntity> selectByCondition(Map map);
int updateByPrimaryKeySelective(PlateFileEntity record);
int updateByPrimaryKey(PlateFileEntity record);
List<PlateFileEntity> getUnRecogniseList();
}

@ -0,0 +1,30 @@
package com.yuxue.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.yuxue.entity.PlateRecoDebugEntity;
@Mapper
public interface PlateRecoDebugMapper {
int deleteByPrimaryKey(Integer id);
int insert(PlateRecoDebugEntity record);
int insertSelective(PlateRecoDebugEntity record);
PlateRecoDebugEntity selectByPrimaryKey(Integer id);
List<PlateRecoDebugEntity> selectByCondition(Map map);
int updateByPrimaryKeySelective(PlateRecoDebugEntity record);
int updateByPrimaryKey(PlateRecoDebugEntity record);
int deleteByParentId(@Param("parentId")Integer parentId);
int batchInsert(@Param("list")List<PlateRecoDebugEntity> list);
}

@ -0,0 +1,25 @@
package com.yuxue.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import com.yuxue.entity.SystemMenuEntity;
@Mapper
public interface SystemMenuMapper {
int deleteByPrimaryKey(Integer id);
int insert(SystemMenuEntity record);
int insertSelective(SystemMenuEntity record);
SystemMenuEntity selectByPrimaryKey(Integer id);
List<SystemMenuEntity> selectByCondition(Map map);
int updateByPrimaryKeySelective(SystemMenuEntity record);
int updateByPrimaryKey(SystemMenuEntity record);
}

@ -0,0 +1,34 @@
package com.yuxue.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.yuxue.entity.TempPlateFileEntity;
@Mapper
public interface TempPlateFileMapper {
int deleteByPrimaryKey(Integer id);
int insert(TempPlateFileEntity record);
int insertSelective(TempPlateFileEntity record);
TempPlateFileEntity selectByPrimaryKey(Integer id);
List<TempPlateFileEntity> selectByCondition(Map map);
int updateByPrimaryKeySelective(TempPlateFileEntity record);
int updateByPrimaryKey(TempPlateFileEntity record);
int turncateTable();
int batchInsert(@Param("list")List<TempPlateFileEntity> list);
int updateFileInfo();
}

@ -0,0 +1,16 @@
package com.yuxue.service;
import java.io.File;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
public interface FileService {
List<JSONObject> getFileTreeByDir(String dir, String typeFilter);
File readFile(String filePath);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save