@ -0,0 +1,36 @@
|
|||||||
|
<?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/JavaCV"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/Lombok"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/opencv"/>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
After Width: | Height: | Size: 661 B |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 200 KiB |
After Width: | Height: | Size: 200 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 212 KiB |
@ -0,0 +1,33 @@
|
|||||||
|
package com.yuxue;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
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) {
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
String version = System.getProperty("java.version");
|
||||||
|
if (Integer.parseInt(version.substring(0,1)) == 1 ) {
|
||||||
|
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,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,59 @@
|
|||||||
|
package com.yuxue.aop;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
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,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 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,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,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,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 {
|
||||||
|
|
||||||
|
/***
|
||||||
|
* EasyPR的getFeatures回调函数
|
||||||
|
* 本函数是生成直方图均衡特征的回调函数
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EasyPR的getFeatures回调函数
|
||||||
|
* 本函数是获取垂直和水平的直方图图值
|
||||||
|
* @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,104 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EasyPR的getFeatures回调函数, 用于从车牌的image生成svm的训练特征features
|
||||||
|
*/
|
||||||
|
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,44 @@
|
|||||||
|
package com.yuxue.easypr.core;
|
||||||
|
|
||||||
|
import org.bytedeco.javacpp.opencv_core.Mat;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Created by fanwenjie
|
||||||
|
* @author lin.yao
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface SVMCallback {
|
||||||
|
|
||||||
|
/***
|
||||||
|
* EasyPR的getFeatures回调函数,本函数是生成直方图均衡特征的回调函数
|
||||||
|
*
|
||||||
|
* @param image
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract Mat getHisteqFeatures(final Mat image);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EasyPR的getFeatures回调函数, 本函数是获取垂直和水平的直方图图值
|
||||||
|
*
|
||||||
|
* @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,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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.yuxue.service;
|
||||||
|
|
||||||
|
|
||||||
|
public interface PlateService {
|
||||||
|
|
||||||
|
public Object getProcessStep();
|
||||||
|
|
||||||
|
Object recognise(String filePath, boolean reRecognise);
|
||||||
|
|
||||||
|
Object refreshFileInfo();
|
||||||
|
|
||||||
|
Object recogniseAll();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.yuxue.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.github.pagehelper.PageInfo;
|
||||||
|
import com.yuxue.entity.SystemMenuEntity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务实现层接口
|
||||||
|
* @author yuxue
|
||||||
|
* @date 2019-06-20 16:15:23
|
||||||
|
*/
|
||||||
|
public interface SystemMenuService {
|
||||||
|
|
||||||
|
public SystemMenuEntity getByPrimaryKey(Integer id);
|
||||||
|
|
||||||
|
public PageInfo<SystemMenuEntity> queryByPage(Integer pageNo, Integer pageSize, Map<String, Object> map);
|
||||||
|
|
||||||
|
public List<SystemMenuEntity> queryByCondition(Map<String, Object> map);
|
||||||
|
|
||||||
|
public Map<String, Object> save(SystemMenuEntity systemMenuEntity);
|
||||||
|
|
||||||
|
public Integer deleteById(Integer id);
|
||||||
|
|
||||||
|
public Integer updateById(SystemMenuEntity systemMenuEntity);
|
||||||
|
|
||||||
|
public Object getUserMenu();
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package com.yuxue.service.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.pagehelper.PageHelper;
|
||||||
|
import com.github.pagehelper.PageInfo;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.yuxue.entity.SystemMenuEntity;
|
||||||
|
import com.yuxue.mapper.SystemMenuMapper;
|
||||||
|
import com.yuxue.service.SystemMenuService;
|
||||||
|
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务实现层
|
||||||
|
* @author yuxue
|
||||||
|
* @date 2019-06-20 16:15:23
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class SystemMenuServiceImpl implements SystemMenuService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SystemMenuMapper systemMenuMapper;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemMenuEntity getByPrimaryKey(Integer id) {
|
||||||
|
SystemMenuEntity entity = systemMenuMapper.selectByPrimaryKey(id);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageInfo<SystemMenuEntity> queryByPage(Integer pageNo, Integer pageSize, Map<String, Object> map) {
|
||||||
|
PageHelper.startPage(pageNo, pageSize);
|
||||||
|
PageInfo<SystemMenuEntity> page = new PageInfo(systemMenuMapper.selectByCondition(map));
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SystemMenuEntity> queryByCondition(Map<String, Object> map) {
|
||||||
|
return systemMenuMapper.selectByCondition(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public Map<String, Object> save(SystemMenuEntity entity) {
|
||||||
|
entity.setId(0);
|
||||||
|
systemMenuMapper.insertSelective(entity);
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("id" , entity.getId());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public Integer deleteById(Integer id){
|
||||||
|
return systemMenuMapper.deleteByPrimaryKey(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public Integer updateById(SystemMenuEntity systemMenuEntity) {
|
||||||
|
if(null == systemMenuEntity || systemMenuEntity.getId() <= 0){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return systemMenuMapper.updateByPrimaryKeySelective(systemMenuEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getUserMenu() {
|
||||||
|
Map<String, Object> map = Maps.newHashMap();
|
||||||
|
//根据角色查询菜单--未完成 //根据层级 sort排序
|
||||||
|
map.put("showFlag", 1);
|
||||||
|
List<SystemMenuEntity> menus = systemMenuMapper.selectByCondition(map);
|
||||||
|
|
||||||
|
//按层级封装,最多三级
|
||||||
|
Map<String, Object> result = Maps.newHashMap();
|
||||||
|
|
||||||
|
result.put("first", menus.stream().filter(n -> {
|
||||||
|
return n.getMenuLevel() == 1;
|
||||||
|
}));
|
||||||
|
result.put("second", menus.stream().filter(n -> {
|
||||||
|
return n.getMenuLevel() == 2;
|
||||||
|
}));
|
||||||
|
result.put("third", menus.stream().filter(n -> {
|
||||||
|
return n.getMenuLevel() == 3;
|
||||||
|
}));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package com.yuxue.util;
|
||||||
|
|
||||||
|
import org.bytedeco.javacpp.BytePointer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are 3 kinds of convert functions:
|
||||||
|
* 1. [float|double|int|long] to[Float|Double|Int|Long](BytePointer pointer)
|
||||||
|
* 2. byte[] getBytes([float|double|int|long] value)
|
||||||
|
* 3. [float|double|int|long] to[Float|Double|Int|Long](byte[] value)
|
||||||
|
*
|
||||||
|
* @author lin.yao
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Convert {
|
||||||
|
|
||||||
|
public static float toFloat(BytePointer pointer) {
|
||||||
|
byte[] buffer = new byte[4];
|
||||||
|
pointer.get(buffer);
|
||||||
|
return toFloat(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double toDouble(BytePointer pointer) {
|
||||||
|
byte[] buffer = new byte[8];
|
||||||
|
pointer.get(buffer);
|
||||||
|
return toDouble(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toInt(BytePointer pointer) {
|
||||||
|
byte[] buffer = new byte[4];
|
||||||
|
pointer.get(buffer);
|
||||||
|
return toInt(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long toLong(BytePointer pointer) {
|
||||||
|
byte[] buffer = new byte[8];
|
||||||
|
pointer.get(buffer);
|
||||||
|
return toLong(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getBytes(float value) {
|
||||||
|
return getBytes(Float.floatToIntBits(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getBytes(double value) {
|
||||||
|
return getBytes(Double.doubleToLongBits(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getBytes(int value) {
|
||||||
|
final int length = 4;
|
||||||
|
byte[] buffer = new byte[length];
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
buffer[i] = (byte) ((value >> (i * 8)) & 0xFF);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getBytes(long value) {
|
||||||
|
final int length = 8;
|
||||||
|
byte[] buffer = new byte[length];
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
buffer[i] = (byte) ((value >> (i * 8)) & 0xFF);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toInt(byte[] value) {
|
||||||
|
final int length = 4;
|
||||||
|
int n = 0;
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
n += (value[i] & 0xFF) << (i * 8);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long toLong(byte[] value) {
|
||||||
|
final int length = 8;
|
||||||
|
long n = 0;
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
n += ((long) (value[i] & 0xFF)) << (i * 8);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double toDouble(byte[] value) {
|
||||||
|
return Double.longBitsToDouble(toLong(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float toFloat(byte[] value) {
|
||||||
|
return Float.intBitsToFloat(toInt(value));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.yuxue.video;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import static org.bytedeco.javacpp.opencv_core.*;
|
||||||
|
|
||||||
|
import org.bytedeco.javacpp.Pointer;
|
||||||
|
import org.bytedeco.javacpp.opencv_core.Mat;
|
||||||
|
import org.bytedeco.javacv.Frame;
|
||||||
|
|
||||||
|
import com.yuxue.constant.Constant;
|
||||||
|
import com.yuxue.easypr.core.CharsRecognise;
|
||||||
|
import com.yuxue.easypr.core.PlateDetect;
|
||||||
|
|
||||||
|
public class Judge {
|
||||||
|
|
||||||
|
private static CharsRecognise CR = new CharsRecognise();
|
||||||
|
|
||||||
|
private static PlateDetect PD = new PlateDetect();
|
||||||
|
|
||||||
|
|
||||||
|
public String judge(Frame frame, int flag) {
|
||||||
|
Mat mat = convertToMat(frame);
|
||||||
|
Vector<Mat> resultVec = new Vector<Mat>(1);
|
||||||
|
if (0 == PD.plateDetect(mat, resultVec)) {
|
||||||
|
if(resultVec.size()>0){
|
||||||
|
String dirname = "D:/PlateDetect/Video_Grab/Temp/" + System.currentTimeMillis() +"/";
|
||||||
|
File d = new File(dirname);
|
||||||
|
d.mkdirs();
|
||||||
|
return CR.charsRecognise(resultVec.get(0), dirname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat convertToMat(Frame frame) {
|
||||||
|
Mat mat = null;
|
||||||
|
if (frame == null || frame.image == null) {
|
||||||
|
return null;
|
||||||
|
} else if (frame.opaque instanceof Mat) {
|
||||||
|
return (Mat)frame.opaque;
|
||||||
|
} else if (!isEqual(frame, mat)) {
|
||||||
|
int depth = getMatDepth(frame.imageDepth);
|
||||||
|
mat = depth < 0 ? null : new Mat(frame.imageHeight, frame.imageWidth, CV_MAKETYPE(depth, frame.imageChannels),
|
||||||
|
new Pointer(frame.image[0].position(0)), frame.imageStride * Math.abs(frame.imageDepth) / 8);
|
||||||
|
}
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int CV_MAKETYPE(int depth, int imageChannels) {
|
||||||
|
switch (depth) {
|
||||||
|
case CV_8U: return CV_8UC3;
|
||||||
|
case CV_8S: return CV_8SC3;
|
||||||
|
case CV_16U: return CV_16UC3;
|
||||||
|
case CV_16S: return CV_16SC3;
|
||||||
|
case CV_32F: return CV_32FC3;
|
||||||
|
case CV_32S: return CV_32SC3;
|
||||||
|
case CV_64F: return CV_64FC3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isEqual(Frame frame, Mat mat) {
|
||||||
|
return mat != null && frame != null && frame.image != null && frame.image.length > 0
|
||||||
|
&& frame.imageWidth == mat.cols() && frame.imageHeight == mat.rows()
|
||||||
|
&& frame.imageChannels == mat.channels() && getMatDepth(frame.imageDepth) == mat.depth()
|
||||||
|
&& new Pointer(frame.image[0]).address() == mat.data().address()
|
||||||
|
&& frame.imageStride * Math.abs(frame.imageDepth) / 8 == (int)mat.step();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getMatDepth(int depth) {
|
||||||
|
switch (depth) {
|
||||||
|
case Frame.DEPTH_UBYTE: return CV_8U;
|
||||||
|
case Frame.DEPTH_BYTE: return CV_8S;
|
||||||
|
case Frame.DEPTH_USHORT: return CV_16U;
|
||||||
|
case Frame.DEPTH_SHORT: return CV_16S;
|
||||||
|
case Frame.DEPTH_FLOAT: return CV_32F;
|
||||||
|
case Frame.DEPTH_INT: return CV_32S;
|
||||||
|
case Frame.DEPTH_DOUBLE: return CV_64F;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.yuxue.video;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.bytedeco.javacv.FFmpegFrameGrabber;
|
||||||
|
import org.bytedeco.javacv.Frame;
|
||||||
|
import org.bytedeco.javacv.Java2DFrameConverter;
|
||||||
|
|
||||||
|
public class VideoDemo {
|
||||||
|
|
||||||
|
private static Judge judge = new Judge();
|
||||||
|
|
||||||
|
private static final String Video_Path = "D:/PlateDetect/Video/Test1.mp4";
|
||||||
|
|
||||||
|
private static final String Save_Path = "D:/PlateDetect/Video_Grab/";
|
||||||
|
|
||||||
|
public void Video() {
|
||||||
|
//Frame对象
|
||||||
|
Frame frame = null;
|
||||||
|
//标识
|
||||||
|
int flag = 0;
|
||||||
|
//打开视频文件
|
||||||
|
FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(Video_Path);
|
||||||
|
|
||||||
|
String A = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
fFmpegFrameGrabber.start();
|
||||||
|
//获取总帧数
|
||||||
|
int count = fFmpegFrameGrabber.getLengthInFrames();
|
||||||
|
//获取帧率
|
||||||
|
int fps = (int) fFmpegFrameGrabber.getFrameRate();
|
||||||
|
//获取总时长
|
||||||
|
double Length = count / fps;
|
||||||
|
|
||||||
|
int Temp = 2;
|
||||||
|
|
||||||
|
while (flag <= Length) {
|
||||||
|
//每秒获取一次图片
|
||||||
|
for(int i=0;i<Temp*fps;i++) {
|
||||||
|
frame = fFmpegFrameGrabber.grabImage();
|
||||||
|
}
|
||||||
|
if (frame != null) {
|
||||||
|
A = judge.judge(frame, flag);
|
||||||
|
if(A != null) {
|
||||||
|
char[] cs = A.toCharArray();
|
||||||
|
if(cs.length == 7) {
|
||||||
|
//文件绝对路径+名字
|
||||||
|
String fileName = Save_Path + A + ".jpg";
|
||||||
|
//文件储存对象
|
||||||
|
File outPut = new File(fileName);
|
||||||
|
ImageIO.write(FrameToBufferedImage(frame), "jpg", outPut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flag = flag + Temp;
|
||||||
|
}
|
||||||
|
System.out.println("识别已结束");
|
||||||
|
fFmpegFrameGrabber.stop();
|
||||||
|
} catch (Exception E) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage FrameToBufferedImage(Frame frame) {
|
||||||
|
//创建BufferedImage对象
|
||||||
|
Java2DFrameConverter converter = new Java2DFrameConverter();
|
||||||
|
BufferedImage bufferedImage = converter.getBufferedImage(frame);
|
||||||
|
return bufferedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
VideoDemo V = new VideoDemo();
|
||||||
|
V.Video();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 258 KiB |
@ -0,0 +1,52 @@
|
|||||||
|
server:
|
||||||
|
port: 16666
|
||||||
|
servlet:
|
||||||
|
context-path: /
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name : demo
|
||||||
|
mvc:
|
||||||
|
favicon:
|
||||||
|
enabled: true
|
||||||
|
messages:
|
||||||
|
basename: i18n.login
|
||||||
|
|
||||||
|
jackson:
|
||||||
|
time-zone: GMT+8
|
||||||
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
|
|
||||||
|
## 环境配置文件 dev sqlite
|
||||||
|
profiles:
|
||||||
|
active: dev
|
||||||
|
|
||||||
|
## 静态页面配置
|
||||||
|
thymeleaf:
|
||||||
|
#热部署文件,页面不产生缓存,及时更新
|
||||||
|
cache: false
|
||||||
|
prefix: classpath:static/templates/
|
||||||
|
suffix: .html
|
||||||
|
encoding: UTF-8
|
||||||
|
|
||||||
|
## Mybatis config
|
||||||
|
mybatis:
|
||||||
|
mapperLocations: classpath:mapper/**/*.xml
|
||||||
|
configLocation: classpath:mybatis.xml
|
||||||
|
|
||||||
|
## pagehelper
|
||||||
|
pagehelper:
|
||||||
|
helperDialect: sqlite #postgresql
|
||||||
|
reasonable: true
|
||||||
|
supportMethodsArguments: true
|
||||||
|
params: countSql
|
||||||
|
count: countSql
|
||||||
|
returnPageInfo: check
|
||||||
|
|
||||||
|
## 记录日志
|
||||||
|
logging:
|
||||||
|
config: classpath:logback-spring.xml
|
||||||
|
## Start logging
|
||||||
|
level:
|
||||||
|
root: INFO
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
██╗ ██╗██╗ ██╗██╗ ██╗██╗ ██╗███████╗
|
||||||
|
╚██╗ ██╔╝██║ ██║╚██╗██╔╝██║ ██║██╔════╝
|
||||||
|
╚████╔╝ ██║ ██║ ╚███╔╝ ██║ ██║█████╗
|
||||||
|
╚██╔╝ ██║ ██║ ██╔██╗ ██║ ██║██╔══╝
|
||||||
|
██║ ╚██████╔╝██╔╝ ██╗╚██████╔╝███████╗
|
||||||
|
╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
||||||
|
:: YX Boot :: Power By SpringBoot (v2.1.0.RELEASE)
|
@ -0,0 +1,454 @@
|
|||||||
|
#include "easypr/core/chars_identify.h"
|
||||||
|
#include "easypr/core/character.hpp"
|
||||||
|
#include "easypr/core/core_func.h"
|
||||||
|
#include "easypr/core/feature.h"
|
||||||
|
#include "easypr/core/params.h"
|
||||||
|
#include "easypr/config.h"
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
|
||||||
|
namespace easypr {
|
||||||
|
|
||||||
|
CharsIdentify* CharsIdentify::instance_ = nullptr;
|
||||||
|
|
||||||
|
CharsIdentify* CharsIdentify::instance() {
|
||||||
|
if (!instance_) {
|
||||||
|
instance_ = new CharsIdentify;
|
||||||
|
}
|
||||||
|
return instance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharsIdentify::CharsIdentify() {
|
||||||
|
LOAD_ANN_MODEL(ann_, kDefaultAnnPath);
|
||||||
|
LOAD_ANN_MODEL(annChinese_, kChineseAnnPath);
|
||||||
|
LOAD_ANN_MODEL(annGray_, kGrayAnnPath);
|
||||||
|
|
||||||
|
kv_ = std::shared_ptr<Kv>(new Kv);
|
||||||
|
kv_->load(kChineseMappingPath);
|
||||||
|
|
||||||
|
extractFeature = getGrayPlusProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharsIdentify::LoadModel(std::string path) {
|
||||||
|
if (path != std::string(kDefaultAnnPath)) {
|
||||||
|
if (!ann_->empty())
|
||||||
|
ann_->clear();
|
||||||
|
LOAD_ANN_MODEL(ann_, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharsIdentify::LoadChineseModel(std::string path) {
|
||||||
|
if (path != std::string(kChineseAnnPath)) {
|
||||||
|
if (!annChinese_->empty())
|
||||||
|
annChinese_->clear();
|
||||||
|
LOAD_ANN_MODEL(annChinese_, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharsIdentify::LoadGrayChANN(std::string path) {
|
||||||
|
if (path != std::string(kGrayAnnPath)) {
|
||||||
|
if (!annGray_->empty())
|
||||||
|
annGray_->clear();
|
||||||
|
LOAD_ANN_MODEL(annGray_, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharsIdentify::LoadChineseMapping(std::string path) {
|
||||||
|
kv_->clear();
|
||||||
|
kv_->load(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharsIdentify::classify(cv::Mat featureRows, std::vector<int>& out_maxIndexs,
|
||||||
|
std::vector<float>& out_maxVals, std::vector<bool> isChineseVec){
|
||||||
|
int rowNum = featureRows.rows;
|
||||||
|
|
||||||
|
cv::Mat output(rowNum, kCharsTotalNumber, CV_32FC1);
|
||||||
|
ann_->predict(featureRows, output);
|
||||||
|
|
||||||
|
for (int output_index = 0; output_index < rowNum; output_index++) {
|
||||||
|
Mat output_row = output.row(output_index);
|
||||||
|
int result = 0;
|
||||||
|
float maxVal = -2.f;
|
||||||
|
bool isChinses = isChineseVec[output_index];
|
||||||
|
if (!isChinses) {
|
||||||
|
result = 0;
|
||||||
|
for (int j = 0; j < kCharactersNumber; j++) {
|
||||||
|
float val = output_row.at<float>(j);
|
||||||
|
// std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = kCharactersNumber;
|
||||||
|
for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) {
|
||||||
|
float val = output_row.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out_maxIndexs[output_index] = result;
|
||||||
|
out_maxVals[output_index] = maxVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CharsIdentify::classify(std::vector<CCharacter>& charVec){
|
||||||
|
size_t charVecSize = charVec.size();
|
||||||
|
|
||||||
|
if (charVecSize == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Mat featureRows;
|
||||||
|
for (size_t index = 0; index < charVecSize; index++) {
|
||||||
|
Mat charInput = charVec[index].getCharacterMat();
|
||||||
|
Mat feature = charFeatures(charInput, kPredictSize);
|
||||||
|
featureRows.push_back(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat output(charVecSize, kCharsTotalNumber, CV_32FC1);
|
||||||
|
ann_->predict(featureRows, output);
|
||||||
|
|
||||||
|
for (size_t output_index = 0; output_index < charVecSize; output_index++) {
|
||||||
|
CCharacter& character = charVec[output_index];
|
||||||
|
Mat output_row = output.row(output_index);
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
float maxVal = -2.f;
|
||||||
|
std::string label = "";
|
||||||
|
|
||||||
|
bool isChinses = character.getIsChinese();
|
||||||
|
if (!isChinses) {
|
||||||
|
result = 0;
|
||||||
|
for (int j = 0; j < kCharactersNumber; j++) {
|
||||||
|
float val = output_row.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label = std::make_pair(kChars[result], kChars[result]).second;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = kCharactersNumber;
|
||||||
|
for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) {
|
||||||
|
float val = output_row.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char* key = kChars[result];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
label = std::make_pair(s, province).second;
|
||||||
|
}
|
||||||
|
/*std::cout << "result:" << result << std::endl;
|
||||||
|
std::cout << "maxVal:" << maxVal << std::endl;*/
|
||||||
|
character.setCharacterScore(maxVal);
|
||||||
|
character.setCharacterStr(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CharsIdentify::classifyChineseGray(std::vector<CCharacter>& charVec){
|
||||||
|
size_t charVecSize = charVec.size();
|
||||||
|
if (charVecSize == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Mat featureRows;
|
||||||
|
for (size_t index = 0; index < charVecSize; index++) {
|
||||||
|
Mat charInput = charVec[index].getCharacterMat();
|
||||||
|
cv::Mat feature;
|
||||||
|
extractFeature(charInput, feature);
|
||||||
|
featureRows.push_back(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat output(charVecSize, kChineseNumber, CV_32FC1);
|
||||||
|
annGray_->predict(featureRows, output);
|
||||||
|
|
||||||
|
for (size_t output_index = 0; output_index < charVecSize; output_index++) {
|
||||||
|
CCharacter& character = charVec[output_index];
|
||||||
|
Mat output_row = output.row(output_index);
|
||||||
|
bool isChinese = true;
|
||||||
|
|
||||||
|
float maxVal = -2;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
for (int j = 0; j < kChineseNumber; j++) {
|
||||||
|
float val = output_row.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no match
|
||||||
|
if (-1 == result) {
|
||||||
|
result = 0;
|
||||||
|
maxVal = 0;
|
||||||
|
isChinese = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = result + kCharsTotalNumber - kChineseNumber;
|
||||||
|
const char* key = kChars[index];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
|
||||||
|
/*std::cout << "result:" << result << std::endl;
|
||||||
|
std::cout << "maxVal:" << maxVal << std::endl;*/
|
||||||
|
|
||||||
|
character.setCharacterScore(maxVal);
|
||||||
|
character.setCharacterStr(province);
|
||||||
|
character.setIsChinese(isChinese);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharsIdentify::classifyChinese(std::vector<CCharacter>& charVec){
|
||||||
|
size_t charVecSize = charVec.size();
|
||||||
|
|
||||||
|
if (charVecSize == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Mat featureRows;
|
||||||
|
for (size_t index = 0; index < charVecSize; index++) {
|
||||||
|
Mat charInput = charVec[index].getCharacterMat();
|
||||||
|
Mat feature = charFeatures(charInput, kChineseSize);
|
||||||
|
featureRows.push_back(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat output(charVecSize, kChineseNumber, CV_32FC1);
|
||||||
|
annChinese_->predict(featureRows, output);
|
||||||
|
|
||||||
|
for (size_t output_index = 0; output_index < charVecSize; output_index++) {
|
||||||
|
CCharacter& character = charVec[output_index];
|
||||||
|
Mat output_row = output.row(output_index);
|
||||||
|
bool isChinese = true;
|
||||||
|
|
||||||
|
float maxVal = -2;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
for (int j = 0; j < kChineseNumber; j++) {
|
||||||
|
float val = output_row.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no match
|
||||||
|
if (-1 == result) {
|
||||||
|
result = 0;
|
||||||
|
maxVal = 0;
|
||||||
|
isChinese = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = result + kCharsTotalNumber - kChineseNumber;
|
||||||
|
const char* key = kChars[index];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
|
||||||
|
/*std::cout << "result:" << result << std::endl;
|
||||||
|
std::cout << "maxVal:" << maxVal << std::endl;*/
|
||||||
|
|
||||||
|
character.setCharacterScore(maxVal);
|
||||||
|
character.setCharacterStr(province);
|
||||||
|
character.setIsChinese(isChinese);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CharsIdentify::classify(cv::Mat f, float& maxVal, bool isChinses, bool isAlphabet){
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
cv::Mat output(1, kCharsTotalNumber, CV_32FC1);
|
||||||
|
ann_->predict(f, output);
|
||||||
|
|
||||||
|
maxVal = -2.f;
|
||||||
|
if (!isChinses) {
|
||||||
|
if (!isAlphabet) {
|
||||||
|
result = 0;
|
||||||
|
for (int j = 0; j < kCharactersNumber; j++) {
|
||||||
|
float val = output.at<float>(j);
|
||||||
|
// std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = 0;
|
||||||
|
// begin with 11th char, which is 'A'
|
||||||
|
for (int j = 10; j < kCharactersNumber; j++) {
|
||||||
|
float val = output.at<float>(j);
|
||||||
|
// std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = kCharactersNumber;
|
||||||
|
for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) {
|
||||||
|
float val = output.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//std::cout << "maxVal:" << maxVal << std::endl;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CharsIdentify::isCharacter(cv::Mat input, std::string& label, float& maxVal, bool isChinese) {
|
||||||
|
cv::Mat feature = charFeatures(input, kPredictSize);
|
||||||
|
auto index = static_cast<int>(classify(feature, maxVal, isChinese));
|
||||||
|
|
||||||
|
if (isChinese) {
|
||||||
|
//std::cout << "maxVal:" << maxVal << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
float chineseMaxThresh = 0.2f;
|
||||||
|
|
||||||
|
if (maxVal >= 0.9 || (isChinese && maxVal >= chineseMaxThresh)) {
|
||||||
|
if (index < kCharactersNumber) {
|
||||||
|
label = std::make_pair(kChars[index], kChars[index]).second;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const char* key = kChars[index];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
label = std::make_pair(s, province).second;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> CharsIdentify::identifyChinese(cv::Mat input, float& out, bool& isChinese) {
|
||||||
|
cv::Mat feature = charFeatures(input, kChineseSize);
|
||||||
|
float maxVal = -2;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
cv::Mat output(1, kChineseNumber, CV_32FC1);
|
||||||
|
annChinese_->predict(feature, output);
|
||||||
|
|
||||||
|
for (int j = 0; j < kChineseNumber; j++) {
|
||||||
|
float val = output.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no match
|
||||||
|
if (-1 == result) {
|
||||||
|
result = 0;
|
||||||
|
maxVal = 0;
|
||||||
|
isChinese = false;
|
||||||
|
}
|
||||||
|
else if (maxVal > 0.9){
|
||||||
|
isChinese = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = result + kCharsTotalNumber - kChineseNumber;
|
||||||
|
const char* key = kChars[index];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
out = maxVal;
|
||||||
|
|
||||||
|
return std::make_pair(s, province);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> CharsIdentify::identifyChineseGray(cv::Mat input, float& out, bool& isChinese) {
|
||||||
|
cv::Mat feature;
|
||||||
|
extractFeature(input, feature);
|
||||||
|
float maxVal = -2;
|
||||||
|
int result = 0;
|
||||||
|
cv::Mat output(1, kChineseNumber, CV_32FC1);
|
||||||
|
annGray_->predict(feature, output);
|
||||||
|
|
||||||
|
for (int j = 0; j < kChineseNumber; j++) {
|
||||||
|
float val = output.at<float>(j);
|
||||||
|
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
result = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no match
|
||||||
|
if (-1 == result) {
|
||||||
|
result = 0;
|
||||||
|
maxVal = 0;
|
||||||
|
isChinese = false;
|
||||||
|
} else if (maxVal > 0.9){
|
||||||
|
isChinese = true;
|
||||||
|
}
|
||||||
|
auto index = result + kCharsTotalNumber - kChineseNumber;
|
||||||
|
const char* key = kChars[index];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
out = maxVal;
|
||||||
|
return std::make_pair(s, province);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> CharsIdentify::identify(cv::Mat input, bool isChinese, bool isAlphabet) {
|
||||||
|
cv::Mat feature = charFeatures(input, kPredictSize);
|
||||||
|
float maxVal = -2;
|
||||||
|
auto index = static_cast<int>(classify(feature, maxVal, isChinese, isAlphabet));
|
||||||
|
if (index < kCharactersNumber) {
|
||||||
|
return std::make_pair(kChars[index], kChars[index]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const char* key = kChars[index];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
return std::make_pair(s, province);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CharsIdentify::identify(std::vector<cv::Mat> inputs, std::vector<std::pair<std::string, std::string>>& outputs,
|
||||||
|
std::vector<bool> isChineseVec) {
|
||||||
|
Mat featureRows;
|
||||||
|
size_t input_size = inputs.size();
|
||||||
|
for (size_t i = 0; i < input_size; i++) {
|
||||||
|
Mat input = inputs[i];
|
||||||
|
cv::Mat feature = charFeatures(input, kPredictSize);
|
||||||
|
featureRows.push_back(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> maxIndexs;
|
||||||
|
std::vector<float> maxVals;
|
||||||
|
classify(featureRows, maxIndexs, maxVals, isChineseVec);
|
||||||
|
|
||||||
|
for (size_t row_index = 0; row_index < input_size; row_index++) {
|
||||||
|
int index = maxIndexs[row_index];
|
||||||
|
if (index < kCharactersNumber) {
|
||||||
|
outputs[row_index] = std::make_pair(kChars[index], kChars[index]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const char* key = kChars[index];
|
||||||
|
std::string s = key;
|
||||||
|
std::string province = kv_->get(s);
|
||||||
|
outputs[row_index] = std::make_pair(s, province);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
#include "easypr/core/chars_recognise.h"
|
||||||
|
#include "easypr/core/character.hpp"
|
||||||
|
#include "easypr/util/util.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace easypr {
|
||||||
|
|
||||||
|
CCharsRecognise::CCharsRecognise() { m_charsSegment = new CCharsSegment(); }
|
||||||
|
|
||||||
|
CCharsRecognise::~CCharsRecognise() { SAFE_RELEASE(m_charsSegment); }
|
||||||
|
|
||||||
|
int CCharsRecognise::charsRecognise(Mat plate, std::string& plateLicense) {
|
||||||
|
std::vector<Mat> matChars;
|
||||||
|
int result = m_charsSegment->charsSegment(plate, matChars);
|
||||||
|
if (result == 0) {
|
||||||
|
int num = matChars.size();
|
||||||
|
for (int j = 0; j < num; j++)
|
||||||
|
{
|
||||||
|
Mat charMat = matChars.at(j);
|
||||||
|
bool isChinses = false;
|
||||||
|
float maxVal = 0;
|
||||||
|
if (j == 0) {
|
||||||
|
bool judge = true;
|
||||||
|
isChinses = true;
|
||||||
|
auto character = CharsIdentify::instance()->identifyChinese(charMat, maxVal, judge);
|
||||||
|
plateLicense.append(character.second);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isChinses = false;
|
||||||
|
auto character = CharsIdentify::instance()->identify(charMat, isChinses);
|
||||||
|
plateLicense.append(character.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (plateLicense.size() < 7) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) {
|
||||||
|
std::vector<Mat> matChars;
|
||||||
|
std::vector<Mat> grayChars;
|
||||||
|
Mat plateMat = plate.getPlateMat();
|
||||||
|
if (0) writeTempImage(plateMat, "plateMat/plate");
|
||||||
|
Color color;
|
||||||
|
if (plate.getPlateLocateType() == CMSER) {
|
||||||
|
color = plate.getPlateColor();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int w = plateMat.cols;
|
||||||
|
int h = plateMat.rows;
|
||||||
|
Mat tmpMat = plateMat(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
|
||||||
|
color = getPlateType(tmpMat, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = m_charsSegment->charsSegmentUsingOSTU(plateMat, matChars, grayChars, color);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
int num = matChars.size();
|
||||||
|
for (int j = 0; j < num; j++)
|
||||||
|
{
|
||||||
|
Mat charMat = matChars.at(j);
|
||||||
|
Mat grayChar = grayChars.at(j);
|
||||||
|
if (color != Color::BLUE)
|
||||||
|
grayChar = 255 - grayChar;
|
||||||
|
|
||||||
|
bool isChinses = false;
|
||||||
|
std::pair<std::string, std::string> character;
|
||||||
|
float maxVal;
|
||||||
|
if (0 == j) {
|
||||||
|
isChinses = true;
|
||||||
|
bool judge = true;
|
||||||
|
character = CharsIdentify::instance()->identifyChineseGray(grayChar, maxVal, judge);
|
||||||
|
plateLicense.append(character.second);
|
||||||
|
|
||||||
|
// set plate chinese mat and str
|
||||||
|
plate.setChineseMat(grayChar);
|
||||||
|
plate.setChineseKey(character.first);
|
||||||
|
if (0) writeTempImage(grayChar, "char_data/" + character.first + "/chars_");
|
||||||
|
}
|
||||||
|
else if (1 == j) {
|
||||||
|
isChinses = false;
|
||||||
|
bool isAbc = true;
|
||||||
|
character = CharsIdentify::instance()->identify(charMat, isChinses, isAbc);
|
||||||
|
plateLicense.append(character.second);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isChinses = false;
|
||||||
|
SHOW_IMAGE(charMat, 0);
|
||||||
|
character = CharsIdentify::instance()->identify(charMat, isChinses);
|
||||||
|
plateLicense.append(character.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
CCharacter charResult;
|
||||||
|
charResult.setCharacterMat(charMat);
|
||||||
|
charResult.setCharacterGrayMat(grayChar);
|
||||||
|
if (isChinses)
|
||||||
|
charResult.setCharacterStr(character.first);
|
||||||
|
else
|
||||||
|
charResult.setCharacterStr(character.second);
|
||||||
|
|
||||||
|
plate.addReutCharacter(charResult);
|
||||||
|
}
|
||||||
|
if (plateLicense.size() < 7) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,466 @@
|
|||||||
|
#include "easypr/core/feature.h"
|
||||||
|
#include "easypr/core/core_func.h"
|
||||||
|
#include "thirdparty/LBP/lbp.hpp"
|
||||||
|
|
||||||
|
namespace easypr {
|
||||||
|
|
||||||
|
|
||||||
|
Mat getHistogram(Mat in) {
|
||||||
|
const int VERTICAL = 0;
|
||||||
|
const int HORIZONTAL = 1;
|
||||||
|
|
||||||
|
// Histogram features
|
||||||
|
Mat vhist = ProjectedHistogram(in, VERTICAL);
|
||||||
|
Mat hhist = ProjectedHistogram(in, HORIZONTAL);
|
||||||
|
|
||||||
|
// Last 10 is the number of moments components
|
||||||
|
int numCols = vhist.cols + hhist.cols;
|
||||||
|
|
||||||
|
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < vhist.cols; i++) {
|
||||||
|
out.at<float>(j) = vhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < hhist.cols; i++) {
|
||||||
|
out.at<float>(j) = hhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getHistogramFeatures(const Mat& image, Mat& features) {
|
||||||
|
Mat grayImage;
|
||||||
|
cvtColor(image, grayImage, CV_RGB2GRAY);
|
||||||
|
|
||||||
|
//grayImage = histeq(grayImage);
|
||||||
|
|
||||||
|
Mat img_threshold;
|
||||||
|
threshold(grayImage, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
|
||||||
|
//Mat img_threshold = grayImage.clone();
|
||||||
|
//spatial_ostu(img_threshold, 8, 2, getPlateType(image, false));
|
||||||
|
|
||||||
|
features = getHistogram(img_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute color histom
|
||||||
|
void getColorFeatures(const Mat& src, Mat& features) {
|
||||||
|
Mat src_hsv;
|
||||||
|
|
||||||
|
//grayImage = histeq(grayImage);
|
||||||
|
cvtColor(src, src_hsv, CV_BGR2HSV);
|
||||||
|
int channels = src_hsv.channels();
|
||||||
|
int nRows = src_hsv.rows;
|
||||||
|
|
||||||
|
// consider multi channel image
|
||||||
|
int nCols = src_hsv.cols * channels;
|
||||||
|
if (src_hsv.isContinuous()) {
|
||||||
|
nCols *= nRows;
|
||||||
|
nRows = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int sz = 180;
|
||||||
|
int h[sz] = { 0 };
|
||||||
|
|
||||||
|
uchar* p;
|
||||||
|
for (int i = 0; i < nRows; ++i) {
|
||||||
|
p = src_hsv.ptr<uchar>(i);
|
||||||
|
for (int j = 0; j < nCols; j += 3) {
|
||||||
|
int H = int(p[j]); // 0-180
|
||||||
|
if (H > sz - 1) H = sz - 1;
|
||||||
|
if (H < 0) H = 0;
|
||||||
|
h[H]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat mhist = Mat::zeros(1, sz, CV_32F);
|
||||||
|
for (int j = 0; j < sz; j++) {
|
||||||
|
mhist.at<float>(j) = (float)h[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize histogram
|
||||||
|
double min, max;
|
||||||
|
minMaxLoc(mhist, &min, &max);
|
||||||
|
|
||||||
|
if (max > 0)
|
||||||
|
mhist.convertTo(mhist, -1, 1.0f / max, 0);
|
||||||
|
|
||||||
|
features = mhist;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void getHistomPlusColoFeatures(const Mat& image, Mat& features) {
|
||||||
|
// TODO
|
||||||
|
Mat feature1, feature2;
|
||||||
|
getHistogramFeatures(image, feature1);
|
||||||
|
getColorFeatures(image, feature2);
|
||||||
|
hconcat(feature1.reshape(1, 1), feature2.reshape(1, 1), features);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void getSIFTFeatures(const Mat& image, Mat& features) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//HOG Features
|
||||||
|
void getHOGFeatures(const Mat& image, Mat& features) {
|
||||||
|
//HOG descripter
|
||||||
|
HOGDescriptor hog(cvSize(128, 64), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 3); //these parameters work well
|
||||||
|
std::vector<float> descriptor;
|
||||||
|
|
||||||
|
// resize input image to (128,64) for compute
|
||||||
|
Size dsize = Size(128,64);
|
||||||
|
Mat trainImg = Mat(dsize, CV_32S);
|
||||||
|
resize(image, trainImg, dsize);
|
||||||
|
|
||||||
|
// compute descripter
|
||||||
|
hog.compute(trainImg, descriptor, Size(8, 8));
|
||||||
|
|
||||||
|
// copy the result
|
||||||
|
Mat mat_featrue(descriptor);
|
||||||
|
mat_featrue.copyTo(features);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void getHSVHistFeatures(const Mat& image, Mat& features) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
//! LBP feature
|
||||||
|
void getLBPFeatures(const Mat& image, Mat& features) {
|
||||||
|
|
||||||
|
Mat grayImage;
|
||||||
|
cvtColor(image, grayImage, CV_RGB2GRAY);
|
||||||
|
|
||||||
|
Mat lbpimage;
|
||||||
|
lbpimage = libfacerec::olbp(grayImage);
|
||||||
|
Mat lbp_hist = libfacerec::spatial_histogram(lbpimage, 32, 4, 4);
|
||||||
|
|
||||||
|
features = lbp_hist;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat charFeatures(Mat in, int sizeData) {
|
||||||
|
const int VERTICAL = 0;
|
||||||
|
const int HORIZONTAL = 1;
|
||||||
|
|
||||||
|
// cut the cetner, will afect 5% perices.
|
||||||
|
Rect _rect = GetCenterRect(in);
|
||||||
|
Mat tmpIn = CutTheRect(in, _rect);
|
||||||
|
//Mat tmpIn = in.clone();
|
||||||
|
|
||||||
|
// Low data feature
|
||||||
|
Mat lowData;
|
||||||
|
resize(tmpIn, lowData, Size(sizeData, sizeData));
|
||||||
|
|
||||||
|
// Histogram features
|
||||||
|
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
|
||||||
|
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
|
||||||
|
|
||||||
|
// Last 10 is the number of moments components
|
||||||
|
int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;
|
||||||
|
|
||||||
|
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||||
|
// Asign values to
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < vhist.cols; i++) {
|
||||||
|
out.at<float>(j) = vhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < hhist.cols; i++) {
|
||||||
|
out.at<float>(j) = hhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
for (int x = 0; x < lowData.cols; x++) {
|
||||||
|
for (int y = 0; y < lowData.rows; y++) {
|
||||||
|
out.at<float>(j) += (float)lowData.at <unsigned char>(x, y);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << out << std::endl;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Mat charFeatures2(Mat in, int sizeData) {
|
||||||
|
const int VERTICAL = 0;
|
||||||
|
const int HORIZONTAL = 1;
|
||||||
|
|
||||||
|
// cut the cetner, will afect 5% perices.
|
||||||
|
Rect _rect = GetCenterRect(in);
|
||||||
|
Mat tmpIn = CutTheRect(in, _rect);
|
||||||
|
//Mat tmpIn = in.clone();
|
||||||
|
|
||||||
|
// Low data feature
|
||||||
|
Mat lowData;
|
||||||
|
resize(tmpIn, lowData, Size(sizeData, sizeData));
|
||||||
|
|
||||||
|
// Histogram features
|
||||||
|
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
|
||||||
|
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
|
||||||
|
|
||||||
|
// Last 10 is the number of moments components
|
||||||
|
int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;
|
||||||
|
|
||||||
|
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < vhist.cols; i++) {
|
||||||
|
out.at<float>(j) = vhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < hhist.cols; i++) {
|
||||||
|
out.at<float>(j) = hhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
for (int x = 0; x < lowData.cols; x++) {
|
||||||
|
for (int y = 0; y < lowData.rows; y++) {
|
||||||
|
out.at<float>(j) += (float)lowData.at <unsigned char>(x, y);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << out << std::endl;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat charProjectFeatures(const Mat& in, int sizeData) {
|
||||||
|
const int VERTICAL = 0;
|
||||||
|
const int HORIZONTAL = 1;
|
||||||
|
|
||||||
|
SHOW_IMAGE(in, 0);
|
||||||
|
// cut the cetner, will afect 5% perices.
|
||||||
|
|
||||||
|
Mat lowData;
|
||||||
|
resize(in, lowData, Size(sizeData, sizeData));
|
||||||
|
|
||||||
|
SHOW_IMAGE(lowData, 0);
|
||||||
|
// Histogram features
|
||||||
|
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
|
||||||
|
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
|
||||||
|
|
||||||
|
// Last 10 is the number of moments components
|
||||||
|
int numCols = vhist.cols + hhist.cols;
|
||||||
|
|
||||||
|
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < vhist.cols; i++) {
|
||||||
|
out.at<float>(j) = vhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < hhist.cols; i++) {
|
||||||
|
out.at<float>(j) = hhist.at<float>(i);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
//std::cout << out << std::endl;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getGrayCharFeatures(const Mat& grayChar, Mat& features) {
|
||||||
|
// TODO: check channnels == 1
|
||||||
|
SHOW_IMAGE(grayChar, 0);
|
||||||
|
SHOW_IMAGE(255 - grayChar, 0);
|
||||||
|
|
||||||
|
// resize to uniform size, like 20x32
|
||||||
|
bool useResize = false;
|
||||||
|
bool useConvert = true;
|
||||||
|
bool useMean = true;
|
||||||
|
bool useLBP = false;
|
||||||
|
|
||||||
|
Mat char_mat;
|
||||||
|
if (useResize) {
|
||||||
|
char_mat.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
|
||||||
|
resize(grayChar, char_mat, char_mat.size(), 0, 0, INTER_LINEAR);
|
||||||
|
} else {
|
||||||
|
char_mat = grayChar;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(char_mat, 0);
|
||||||
|
|
||||||
|
// convert to float
|
||||||
|
Mat float_img;
|
||||||
|
if (useConvert) {
|
||||||
|
float scale = 1.f / 255;
|
||||||
|
char_mat.convertTo(float_img, CV_32FC1, scale, 0);
|
||||||
|
} else {
|
||||||
|
float_img = char_mat;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(float_img, 0);
|
||||||
|
|
||||||
|
// cut from mean, it can be optional
|
||||||
|
|
||||||
|
Mat mean_img;
|
||||||
|
if (useMean) {
|
||||||
|
float_img -= mean(float_img);
|
||||||
|
mean_img = float_img;
|
||||||
|
} else {
|
||||||
|
mean_img = float_img;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(mean_img, 0);
|
||||||
|
|
||||||
|
// use lbp to get features, it can be changed to other
|
||||||
|
Mat feautreImg;
|
||||||
|
if (useLBP) {
|
||||||
|
Mat lbpimage = libfacerec::olbp(char_mat);
|
||||||
|
SHOW_IMAGE(lbpimage, 0);
|
||||||
|
feautreImg = libfacerec::spatial_histogram(lbpimage, kCharLBPPatterns, kCharLBPGridX, kCharLBPGridY);
|
||||||
|
} else {
|
||||||
|
feautreImg = mean_img.reshape(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return back
|
||||||
|
features = feautreImg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void getGrayPlusProject(const Mat& grayChar, Mat& features)
|
||||||
|
{
|
||||||
|
// TODO: check channnels == 1
|
||||||
|
SHOW_IMAGE(grayChar, 0);
|
||||||
|
SHOW_IMAGE(255 - grayChar, 0);
|
||||||
|
|
||||||
|
// resize to uniform size, like 20x32
|
||||||
|
bool useResize = false;
|
||||||
|
bool useConvert = true;
|
||||||
|
bool useMean = true;
|
||||||
|
bool useLBP = false;
|
||||||
|
|
||||||
|
Mat char_mat;
|
||||||
|
if (useResize) {
|
||||||
|
char_mat.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
|
||||||
|
resize(grayChar, char_mat, char_mat.size(), 0, 0, INTER_LINEAR);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char_mat = grayChar;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(char_mat, 0);
|
||||||
|
|
||||||
|
// convert to float
|
||||||
|
Mat float_img;
|
||||||
|
if (useConvert) {
|
||||||
|
float scale = 1.f / 255;
|
||||||
|
char_mat.convertTo(float_img, CV_32FC1, scale, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float_img = char_mat;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(float_img, 0);
|
||||||
|
|
||||||
|
// cut from mean, it can be optional
|
||||||
|
|
||||||
|
Mat mean_img;
|
||||||
|
if (useMean) {
|
||||||
|
float_img -= mean(float_img);
|
||||||
|
mean_img = float_img;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mean_img = float_img;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(mean_img, 0);
|
||||||
|
|
||||||
|
// use lbp to get features, it can be changed to other
|
||||||
|
Mat feautreImg;
|
||||||
|
if (useLBP) {
|
||||||
|
Mat lbpimage = libfacerec::olbp(char_mat);
|
||||||
|
SHOW_IMAGE(lbpimage, 0);
|
||||||
|
feautreImg = libfacerec::spatial_histogram(lbpimage, kCharLBPPatterns, kCharLBPGridX, kCharLBPGridY);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
feautreImg = mean_img.reshape(1, 1);
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(grayChar, 0);
|
||||||
|
Mat binaryChar;
|
||||||
|
threshold(grayChar, binaryChar, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
|
||||||
|
SHOW_IMAGE(binaryChar, 0);
|
||||||
|
Mat projectFeature = charProjectFeatures(binaryChar, 32);
|
||||||
|
|
||||||
|
hconcat(feautreImg.reshape(1, 1), projectFeature.reshape(1, 1), features);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void getGrayPlusLBP(const Mat& grayChar, Mat& features)
|
||||||
|
{
|
||||||
|
// TODO: check channnels == 1
|
||||||
|
SHOW_IMAGE(grayChar, 0);
|
||||||
|
SHOW_IMAGE(255 - grayChar, 0);
|
||||||
|
|
||||||
|
// resize to uniform size, like 20x32
|
||||||
|
bool useResize = false;
|
||||||
|
bool useConvert = true;
|
||||||
|
bool useMean = true;
|
||||||
|
bool useLBP = true;
|
||||||
|
|
||||||
|
Mat char_mat;
|
||||||
|
if (useResize) {
|
||||||
|
char_mat.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
|
||||||
|
resize(grayChar, char_mat, char_mat.size(), 0, 0, INTER_LINEAR);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char_mat = grayChar;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(char_mat, 0);
|
||||||
|
|
||||||
|
// convert to float
|
||||||
|
Mat float_img;
|
||||||
|
if (useConvert) {
|
||||||
|
float scale = 1.f / 255;
|
||||||
|
char_mat.convertTo(float_img, CV_32FC1, scale, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float_img = char_mat;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(float_img, 0);
|
||||||
|
|
||||||
|
// cut from mean, it can be optional
|
||||||
|
|
||||||
|
Mat mean_img;
|
||||||
|
if (useMean) {
|
||||||
|
float_img -= mean(float_img);
|
||||||
|
mean_img = float_img;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mean_img = float_img;
|
||||||
|
}
|
||||||
|
SHOW_IMAGE(mean_img, 0);
|
||||||
|
|
||||||
|
// use lbp to get features, it can be changed to other
|
||||||
|
Mat originImage = mean_img.clone();
|
||||||
|
Mat lbpimage = libfacerec::olbp(mean_img);
|
||||||
|
SHOW_IMAGE(lbpimage, 0);
|
||||||
|
lbpimage = libfacerec::spatial_histogram(lbpimage, kCharLBPPatterns, kCharLBPGridX, kCharLBPGridY);
|
||||||
|
|
||||||
|
// 32x20 + 16x16
|
||||||
|
hconcat(mean_img.reshape(1, 1), lbpimage.reshape(1, 1), features);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getLBPplusHistFeatures(const Mat& image, Mat& features) {
|
||||||
|
Mat grayImage;
|
||||||
|
cvtColor(image, grayImage, CV_RGB2GRAY);
|
||||||
|
|
||||||
|
Mat lbpimage;
|
||||||
|
lbpimage = libfacerec::olbp(grayImage);
|
||||||
|
Mat lbp_hist = libfacerec::spatial_histogram(lbpimage, 64, 8, 4);
|
||||||
|
//features = lbp_hist.reshape(1, 1);
|
||||||
|
|
||||||
|
Mat greyImage;
|
||||||
|
cvtColor(image, greyImage, CV_RGB2GRAY);
|
||||||
|
|
||||||
|
//grayImage = histeq(grayImage);
|
||||||
|
Mat img_threshold;
|
||||||
|
threshold(greyImage, img_threshold, 0, 255,
|
||||||
|
CV_THRESH_OTSU + CV_THRESH_BINARY);
|
||||||
|
Mat histomFeatures = getHistogram(img_threshold);
|
||||||
|
|
||||||
|
hconcat(lbp_hist.reshape(1, 1), histomFeatures.reshape(1, 1), features);
|
||||||
|
//std::cout << features << std::endl;
|
||||||
|
//features = histomFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
#include "easypr/core/params.h"
|
||||||
|
|
||||||
|
namespace easypr {
|
||||||
|
CParams* CParams::instance_ = nullptr;
|
||||||
|
|
||||||
|
CParams* CParams::instance() {
|
||||||
|
if (!instance_) {
|
||||||
|
instance_ = new CParams;
|
||||||
|
}
|
||||||
|
return instance_;
|
||||||
|
}
|
||||||
|
}/*! \namespace easypr*/
|
@ -0,0 +1,77 @@
|
|||||||
|
#include "easypr/core/plate_detect.h"
|
||||||
|
#include "easypr/util/util.h"
|
||||||
|
#include "easypr/core/core_func.h"
|
||||||
|
#include "easypr/config.h"
|
||||||
|
|
||||||
|
namespace easypr {
|
||||||
|
|
||||||
|
CPlateDetect::CPlateDetect() {
|
||||||
|
m_plateLocate = new CPlateLocate();
|
||||||
|
m_maxPlates = 3;
|
||||||
|
m_type = 0;
|
||||||
|
m_showDetect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPlateDetect::~CPlateDetect() { SAFE_RELEASE(m_plateLocate); }
|
||||||
|
|
||||||
|
int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int type,
|
||||||
|
bool showDetectArea, int img_index) {
|
||||||
|
std::vector<CPlate> sobel_Plates;
|
||||||
|
sobel_Plates.reserve(16);
|
||||||
|
std::vector<CPlate> color_Plates;
|
||||||
|
color_Plates.reserve(16);
|
||||||
|
std::vector<CPlate> mser_Plates;
|
||||||
|
mser_Plates.reserve(16);
|
||||||
|
std::vector<CPlate> all_result_Plates;
|
||||||
|
all_result_Plates.reserve(64);
|
||||||
|
#pragma omp parallel sections
|
||||||
|
{
|
||||||
|
#pragma omp section
|
||||||
|
{
|
||||||
|
if (!type || type & PR_DETECT_SOBEL) {
|
||||||
|
m_plateLocate->plateSobelLocate(src, sobel_Plates, img_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma omp section
|
||||||
|
{
|
||||||
|
if (!type || type & PR_DETECT_COLOR) {
|
||||||
|
m_plateLocate->plateColorLocate(src, color_Plates, img_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma omp section
|
||||||
|
{
|
||||||
|
if (!type || type & PR_DETECT_CMSER) {
|
||||||
|
m_plateLocate->plateMserLocate(src, mser_Plates, img_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto plate : sobel_Plates) {
|
||||||
|
plate.setPlateLocateType(SOBEL);
|
||||||
|
all_result_Plates.push_back(plate);
|
||||||
|
}
|
||||||
|
for (auto plate : color_Plates) {
|
||||||
|
plate.setPlateLocateType(COLOR);
|
||||||
|
all_result_Plates.push_back(plate);
|
||||||
|
}
|
||||||
|
for (auto plate : mser_Plates) {
|
||||||
|
plate.setPlateLocateType(CMSER);
|
||||||
|
all_result_Plates.push_back(plate);
|
||||||
|
}
|
||||||
|
// use nms to judge plate
|
||||||
|
PlateJudge::instance()->plateJudgeUsingNMS(all_result_Plates, resultVec, m_maxPlates);
|
||||||
|
|
||||||
|
if (0)
|
||||||
|
showDectectResults(src, resultVec, m_maxPlates);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int img_index) {
|
||||||
|
int result = plateDetect(src, resultVec, m_type, false, img_index);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlateDetect::LoadSVM(std::string path) {
|
||||||
|
PlateJudge::instance()->LoadModel(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
#include "easypr/core/plate_recognize.h"
|
||||||
|
#include "easypr/config.h"
|
||||||
|
#include "thirdparty/textDetect/erfilter.hpp"
|
||||||
|
|
||||||
|
namespace easypr {
|
||||||
|
|
||||||
|
CPlateRecognize::CPlateRecognize() {
|
||||||
|
m_showResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// main method, plate recognize, contain two parts
|
||||||
|
// 1. plate detect
|
||||||
|
// 2. chars recognize
|
||||||
|
int CPlateRecognize::plateRecognize(const Mat& src, std::vector<CPlate> &plateVecOut, int img_index) {
|
||||||
|
// resize to uniform sizes
|
||||||
|
float scale = 1.f;
|
||||||
|
Mat img = uniformResize(src, scale);
|
||||||
|
|
||||||
|
// 1. plate detect
|
||||||
|
std::vector<CPlate> plateVec;
|
||||||
|
int resultPD = plateDetect(img, plateVec, img_index);
|
||||||
|
if (resultPD == 0) {
|
||||||
|
size_t num = plateVec.size();
|
||||||
|
for (size_t j = 0; j < num; j++) {
|
||||||
|
CPlate& item = plateVec.at(j);
|
||||||
|
Mat plateMat = item.getPlateMat();
|
||||||
|
SHOW_IMAGE(plateMat, 0);
|
||||||
|
|
||||||
|
// scale the rect to src;
|
||||||
|
item.setPlateScale(scale);
|
||||||
|
RotatedRect rect = item.getPlatePos();
|
||||||
|
item.setPlatePos(scaleBackRRect(rect, 1.f / scale));
|
||||||
|
|
||||||
|
// get plate color
|
||||||
|
Color color = item.getPlateColor();
|
||||||
|
if (color == UNKNOWN) {
|
||||||
|
color = getPlateType(plateMat, true);
|
||||||
|
item.setPlateColor(color);
|
||||||
|
}
|
||||||
|
std::string plateColor = getPlateColor(color);
|
||||||
|
if (0) {
|
||||||
|
std::cout << "plateColor:" << plateColor << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. chars recognize
|
||||||
|
std::string plateIdentify = "";
|
||||||
|
int resultCR = charsRecognise(item, plateIdentify);
|
||||||
|
if (resultCR == 0) {
|
||||||
|
std::string license = plateColor + ":" + plateIdentify;
|
||||||
|
item.setPlateStr(license);
|
||||||
|
plateVecOut.push_back(item);
|
||||||
|
if (0) std::cout << "resultCR:" << resultCR << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::string license = plateColor;
|
||||||
|
item.setPlateStr(license);
|
||||||
|
plateVecOut.push_back(item);
|
||||||
|
if (0) std::cout << "resultCR:" << resultCR << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (getResultShow()) {
|
||||||
|
// param type: 0 detect, 1 recognize;
|
||||||
|
int showType = 1;
|
||||||
|
if (0 == showType)
|
||||||
|
showDectectResults(img, plateVec, num);
|
||||||
|
else
|
||||||
|
showDectectResults(img, plateVecOut, num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultPD;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlateRecognize::LoadSVM(std::string path) {
|
||||||
|
PlateJudge::instance()->LoadModel(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlateRecognize::LoadANN(std::string path) {
|
||||||
|
CharsIdentify::instance()->LoadModel(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlateRecognize::LoadChineseANN(std::string path) {
|
||||||
|
CharsIdentify::instance()->LoadChineseModel(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlateRecognize::LoadGrayChANN(std::string path) {
|
||||||
|
CharsIdentify::instance()->LoadGrayChANN(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlateRecognize::LoadChineseMapping(std::string path) {
|
||||||
|
CharsIdentify::instance()->LoadChineseMapping(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// deprected
|
||||||
|
int CPlateRecognize::plateRecognize(const Mat& src, std::vector<std::string> &licenseVec) {
|
||||||
|
vector<CPlate> plates;
|
||||||
|
int resultPR = plateRecognize(src, plates, 0);
|
||||||
|
|
||||||
|
for (auto plate : plates) {
|
||||||
|
licenseVec.push_back(plate.getPlateStr());
|
||||||
|
}
|
||||||
|
return resultPR;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
zh_cuan 川
|
||||||
|
zh_gan1 甘
|
||||||
|
zh_hei 黑
|
||||||
|
zh_jin 津
|
||||||
|
zh_liao 辽
|
||||||
|
zh_min 闽
|
||||||
|
zh_qiong 琼
|
||||||
|
zh_sx 晋
|
||||||
|
zh_xin 新
|
||||||
|
zh_yue 粤
|
||||||
|
zh_zhe 浙
|
||||||
|
zh_e 鄂
|
||||||
|
zh_gui 贵
|
||||||
|
zh_hu 沪
|
||||||
|
zh_jing 京
|
||||||
|
zh_lu 鲁
|
||||||
|
zh_ning 宁
|
||||||
|
zh_shan 陕
|
||||||
|
zh_wan 皖
|
||||||
|
zh_yu 豫
|
||||||
|
zh_yun 云
|
||||||
|
zh_gan 赣
|
||||||
|
zh_gui1 桂
|
||||||
|
zh_ji 冀
|
||||||
|
zh_jl 吉
|
||||||
|
zh_meng 蒙
|
||||||
|
zh_qing 青
|
||||||
|
zh_su 苏
|
||||||
|
zh_xiang 湘
|
||||||
|
zh_yu1 渝
|
||||||
|
zh_zang 藏
|
@ -0,0 +1,196 @@
|
|||||||
|
#include "easypr/train/svm_train.h"
|
||||||
|
#include "easypr/util/util.h"
|
||||||
|
#include "easypr/config.h"
|
||||||
|
|
||||||
|
#ifdef OS_WINDOWS
|
||||||
|
#include <ctime>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
using namespace cv::ml;
|
||||||
|
|
||||||
|
|
||||||
|
// 原版C++语言 训练代码
|
||||||
|
namespace easypr {
|
||||||
|
|
||||||
|
SvmTrain::SvmTrain(const char* plates_folder, const char* xml): plates_folder_(plates_folder), svm_xml_(xml) {
|
||||||
|
assert(plates_folder);
|
||||||
|
assert(xml);
|
||||||
|
extractFeature = getHistomPlusColoFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SvmTrain::train() {
|
||||||
|
svm_ = cv::ml::SVM::create();
|
||||||
|
svm_->setType(cv::ml::SVM::C_SVC);
|
||||||
|
svm_->setKernel(cv::ml::SVM::RBF);
|
||||||
|
svm_->setDegree(0.1);
|
||||||
|
// 1.4 bug fix: old 1.4 ver gamma is 1
|
||||||
|
svm_->setGamma(0.1);
|
||||||
|
svm_->setCoef0(0.1);
|
||||||
|
svm_->setC(1);
|
||||||
|
svm_->setNu(0.1);
|
||||||
|
svm_->setP(0.1);
|
||||||
|
svm_->setTermCriteria(cvTermCriteria(CV_TERMCRIT_ITER, 20000, 0.0001));
|
||||||
|
|
||||||
|
this->prepare();
|
||||||
|
|
||||||
|
if (train_file_list_.size() == 0) {
|
||||||
|
fprintf(stdout, "No file found in the train folder!\n");
|
||||||
|
fprintf(stdout, "You should create a folder named \"tmp\" in EasyPR main folder.\n");
|
||||||
|
fprintf(stdout, "Copy train data folder(like \"SVM\") under \"tmp\". \n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto train_data = tdata();
|
||||||
|
|
||||||
|
fprintf(stdout, ">> Training SVM model, please wait...\n");
|
||||||
|
long start = utils::getTimestamp();
|
||||||
|
svm_->trainAuto(train_data, 10, SVM::getDefaultGrid(SVM::C),
|
||||||
|
SVM::getDefaultGrid(SVM::GAMMA), SVM::getDefaultGrid(SVM::P),
|
||||||
|
SVM::getDefaultGrid(SVM::NU), SVM::getDefaultGrid(SVM::COEF),
|
||||||
|
SVM::getDefaultGrid(SVM::DEGREE), true);
|
||||||
|
//svm_->train(train_data);
|
||||||
|
|
||||||
|
long end = utils::getTimestamp();
|
||||||
|
fprintf(stdout, ">> Training done. Time elapse: %ldms\n", end - start);
|
||||||
|
fprintf(stdout, ">> Saving model file...\n");
|
||||||
|
svm_->save(svm_xml_);
|
||||||
|
|
||||||
|
fprintf(stdout, ">> Your SVM Model was saved to %s\n", svm_xml_);
|
||||||
|
fprintf(stdout, ">> Testing...\n");
|
||||||
|
|
||||||
|
this->test();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SvmTrain::test() {
|
||||||
|
// 1.4 bug fix: old 1.4 ver there is no null judge
|
||||||
|
// if (NULL == svm_)
|
||||||
|
LOAD_SVM_MODEL(svm_, svm_xml_);
|
||||||
|
|
||||||
|
if (test_file_list_.empty()) {
|
||||||
|
this->prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
double count_all = test_file_list_.size();
|
||||||
|
double ptrue_rtrue = 0;
|
||||||
|
double ptrue_rfalse = 0;
|
||||||
|
double pfalse_rtrue = 0;
|
||||||
|
double pfalse_rfalse = 0;
|
||||||
|
|
||||||
|
for (auto item : test_file_list_) {
|
||||||
|
auto image = cv::imread(item.file);
|
||||||
|
if (!image.data) {
|
||||||
|
std::cout << "no" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cv::Mat feature;
|
||||||
|
extractFeature(image, feature);
|
||||||
|
|
||||||
|
auto predict = int(svm_->predict(feature));
|
||||||
|
//std::cout << "predict: " << predict << std::endl;
|
||||||
|
|
||||||
|
auto real = item.label;
|
||||||
|
if (predict == kForward && real == kForward) ptrue_rtrue++;
|
||||||
|
if (predict == kForward && real == kInverse) ptrue_rfalse++;
|
||||||
|
if (predict == kInverse && real == kForward) pfalse_rtrue++;
|
||||||
|
if (predict == kInverse && real == kInverse) pfalse_rfalse++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "count_all: " << count_all << std::endl;
|
||||||
|
std::cout << "ptrue_rtrue: " << ptrue_rtrue << std::endl;
|
||||||
|
std::cout << "ptrue_rfalse: " << ptrue_rfalse << std::endl;
|
||||||
|
std::cout << "pfalse_rtrue: " << pfalse_rtrue << std::endl;
|
||||||
|
std::cout << "pfalse_rfalse: " << pfalse_rfalse << std::endl;
|
||||||
|
|
||||||
|
double precise = 0;
|
||||||
|
if (ptrue_rtrue + ptrue_rfalse != 0) {
|
||||||
|
precise = ptrue_rtrue / (ptrue_rtrue + ptrue_rfalse);
|
||||||
|
std::cout << "precise: " << precise << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "precise: "
|
||||||
|
<< "NA" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
double recall = 0;
|
||||||
|
if (ptrue_rtrue + pfalse_rtrue != 0) {
|
||||||
|
recall = ptrue_rtrue / (ptrue_rtrue + pfalse_rtrue);
|
||||||
|
std::cout << "recall: " << recall << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "recall: "
|
||||||
|
<< "NA" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Fsocre = 0;
|
||||||
|
if (precise + recall != 0) {
|
||||||
|
Fsocre = 2 * (precise * recall) / (precise + recall);
|
||||||
|
std::cout << "Fsocre: " << Fsocre << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Fsocre: "
|
||||||
|
<< "NA" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SvmTrain::prepare() {
|
||||||
|
srand(unsigned(time(NULL)));
|
||||||
|
|
||||||
|
char buffer[260] = {0};
|
||||||
|
|
||||||
|
sprintf(buffer, "%s/has/train", plates_folder_);
|
||||||
|
auto has_file_train_list = utils::getFiles(buffer);
|
||||||
|
std::random_shuffle(has_file_train_list.begin(), has_file_train_list.end());
|
||||||
|
|
||||||
|
sprintf(buffer, "%s/has/test", plates_folder_);
|
||||||
|
auto has_file_test_list = utils::getFiles(buffer);
|
||||||
|
std::random_shuffle(has_file_test_list.begin(), has_file_test_list.end());
|
||||||
|
|
||||||
|
sprintf(buffer, "%s/no/train", plates_folder_);
|
||||||
|
auto no_file_train_list = utils::getFiles(buffer);
|
||||||
|
std::random_shuffle(no_file_train_list.begin(), no_file_train_list.end());
|
||||||
|
|
||||||
|
sprintf(buffer, "%s/no/test", plates_folder_);
|
||||||
|
auto no_file_test_list = utils::getFiles(buffer);
|
||||||
|
std::random_shuffle(no_file_test_list.begin(), no_file_test_list.end());
|
||||||
|
|
||||||
|
fprintf(stdout, ">> Collecting train data...\n");
|
||||||
|
|
||||||
|
for (auto file : has_file_train_list)
|
||||||
|
train_file_list_.push_back({ file, kForward });
|
||||||
|
|
||||||
|
for (auto file : no_file_train_list)
|
||||||
|
train_file_list_.push_back({ file, kInverse });
|
||||||
|
|
||||||
|
fprintf(stdout, ">> Collecting test data...\n");
|
||||||
|
|
||||||
|
for (auto file : has_file_test_list)
|
||||||
|
test_file_list_.push_back({ file, kForward });
|
||||||
|
|
||||||
|
for (auto file : no_file_test_list)
|
||||||
|
test_file_list_.push_back({ file, kInverse });
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Ptr<cv::ml::TrainData> SvmTrain::tdata() {
|
||||||
|
cv::Mat samples;
|
||||||
|
std::vector<int> responses;
|
||||||
|
|
||||||
|
for (auto f : train_file_list_) {
|
||||||
|
auto image = cv::imread(f.file);
|
||||||
|
if (!image.data) {
|
||||||
|
fprintf(stdout, ">> Invalid image: %s ignore.\n", f.file.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cv::Mat feature;
|
||||||
|
extractFeature(image, feature);
|
||||||
|
feature = feature.reshape(1, 1);
|
||||||
|
|
||||||
|
samples.push_back(feature);
|
||||||
|
responses.push_back(int(f.label));
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat samples_, responses_;
|
||||||
|
samples.convertTo(samples_, CV_32FC1);
|
||||||
|
cv::Mat(responses).copyTo(responses_);
|
||||||
|
|
||||||
|
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE, responses_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace easypr
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|