基于Opencv实现车牌图片识别系统
Posted 编程指南针
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Opencv实现车牌图片识别系统相关的知识,希望对你有一定的参考价值。
- 这是一个基于spring boot + maven + opencv 实现的图像识别及训练的Demo项目
- 包含车牌识别、人脸识别等功能,贯穿样本处理、模型训练、图像处理、对象检测、对象识别等技术点
- java语言的深度学习项目,在整个开源社区来说都相对较少;
- 拥有完整的训练过程、检测、识别过程的开源项目更是少之又少!!
包含功能
- 蓝、绿、黄车牌检测及车牌号码识别
- 网上常见的轮廓提取车牌算法JAVA实现
- hsv色彩分割提取车牌算法JAVA实现
- 基于svm算法的车牌检测训练JAVA实现
- 基于ann算法的车牌号码识别训练JAVA实现
- 人脸检测 接下来将实现人脸识别
- 图片处理工具,目前实现了HSV色彩切割,后续将添加更多使用的图片处理工具,用于辅助算法优化
操作界面
软件版本
- jdk 1.8.61+
- maven 3.0+
- opencv 4.0.1 ; javacpp1.4.4;opencv-platform 4.0.1-1.4.4
- spring boot 2.1.5.RELEASE
- yx-image-recognition 1.0.0版本
软件架构
- B/S 架构,前端html + requireJS,后端java
- 数据库使用 sqlite3.0
- 接口文档使用swagger 2.0
参考文档
- 参考了EasyPR C++项目、以及fan-wenjie的EasyPR-Java项目;同时查阅了部分opencv官方4.0.1版本C++的源码,结合个人对java语言的理解,整理出当前项目
- liuruoze/EasyPR:https://gitee.com/easypr/EasyPR?_from=gitee_search
- fan-wenjie/EasyPR-Java: GitHub - fan-wenjie/EasyPR-Java: 车牌识别软件EasyPR的Java版本
- opencv官方: Home - OpenCV
相关功能实现展示:
车牌识别
黄牌识别
绿牌识别
夜间识别
图片提取工具
人脸识别
训练
接口文档
车牌检测过程
高斯模糊:
图像灰度化:
Sobel 算子:
图像二值化:
图像闭操作:
二值图像降噪:
提取外部轮廓:
外部轮廓筛选:
切图:
重置切图尺寸:
车牌检测结果:
图片车牌文字识别过程
车牌检测结果:
图片车牌文字识别过程
debug_char_threshold:
debug_char_clearLiuDing:
debug_specMat:
debug_chineseMat:
debug_char_auxRoi:
部门核心代码:
package com.yuxue.service.impl;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.yuxue.constant.Constant;
import com.yuxue.entity.PlateFileEntity;
import com.yuxue.entity.TempPlateFileEntity;
import com.yuxue.enumtype.PlateColor;
import com.yuxue.mapper.PlateFileMapper;
import com.yuxue.mapper.TempPlateFileMapper;
import com.yuxue.service.PlateService;
import com.yuxue.util.FileUtil;
import com.yuxue.util.GenerateIdUtil;
import com.yuxue.util.PlateUtil;
@Service
public class PlateServiceImpl implements PlateService
@Autowired
private PlateFileMapper plateFileMapper;
@Autowired
private TempPlateFileMapper tempPlateFileMapper;
static
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Object refreshFileInfo()
File baseDir = new File(Constant.DEFAULT_DIR);
if(!baseDir.exists() || !baseDir.isDirectory())
return null;
List<TempPlateFileEntity> resultList = Lists.newArrayList();
// 获取baseDir下第一层级的目录, 仅获取文件夹,不递归子目录,遍历
List<File> folderList = FileUtil.listFile(baseDir, ";", false);
folderList.parallelStream().forEach(folder ->
if(!folder.getName().equals("temp"))
// 遍历每一个文件夹, 递归获取文件夹下的图片
List<File> imgList = FileUtil.listFile(folder, Constant.DEFAULT_TYPE, true);
if(null != imgList && imgList.size() > 0)
imgList.parallelStream().forEach(n->
TempPlateFileEntity entity = new TempPlateFileEntity();
entity.setFilePath(n.getAbsolutePath().replaceAll("\\\\\\\\", "/"));
entity.setFileName(n.getName());
entity.setFileType(n.getName().substring(n.getName().lastIndexOf(".") + 1));
resultList.add(entity);
);
);
tempPlateFileMapper.turncateTable();
tempPlateFileMapper.batchInsert(resultList);
tempPlateFileMapper.updateFileInfo();
return 1;
@Override
public Object recognise(String filePath, boolean reRecognise)
filePath = filePath.replaceAll("\\\\\\\\", "/");
File f = new File(filePath);
PlateFileEntity entity = null;
Map<String, Object> paramMap = Maps.newHashMap();
paramMap.put("filePath", filePath);
List<PlateFileEntity> list= plateFileMapper.selectByCondition(paramMap);
if(null == list || list.size() <= 0)
if(FileUtil.checkFile(f))
entity = new PlateFileEntity();
entity.setFileName(f.getName());
entity.setFilePath(f.getAbsolutePath().replaceAll("\\\\\\\\", "/"));
entity.setFileType(f.getName().substring(f.getName().lastIndexOf(".") + 1));
plateFileMapper.insertSelective(entity);
reRecognise = true;
else
entity = list.get(0);
if(reRecognise || StringUtils.isEmpty(entity.getTempPath()))
doRecognise(f, entity); // 重新识别
entity = plateFileMapper.selectByPrimaryKey(entity.getId()); // 重新识别之后,重新获取一下数据
// 查询debug文件
if(!StringUtils.isEmpty(entity.getTempPath()))
Vector<String> debugFiles = new Vector<String>();
FileUtil.getFiles(entity.getTempPath(), debugFiles);
entity.setDebugFiles(debugFiles);
return entity;
@Override
public Object recogniseAll()
// 查询到还没有进行车牌识别的图片
List<PlateFileEntity> list = plateFileMapper.getUnRecogniseList();
list.parallelStream().forEach(n->
File f = new File(n.getFilePath());
if(FileUtil.checkFile(f))
doRecognise(f, n);
);
return 1;
/**
* 单张图片 车牌识别
* 拷贝文件到临时目录
* 过程及结果更新数据库
* @param f
* @param e
* @return
*/
public Object doRecognise(File f, PlateFileEntity e)
if(!f.exists())
return null;
String ct = GenerateIdUtil.getStrId();
String targetPath = Constant.DEFAULT_TEMP_DIR + ct + (f.getName().substring(f.getName().lastIndexOf(".")));
FileUtil.copyAndRename(f.getAbsolutePath(), targetPath); // 拷贝文件并且重命名
// 创建临时目录, 存放过程图片
String tempPath = Constant.DEFAULT_TEMP_DIR + ct + "/";
FileUtil.createDir(tempPath);
e.setTempPath(tempPath);
Boolean debug = false;
Vector<Mat> dst = new Vector<Mat>();
PlateUtil.getPlateMat(targetPath, dst, debug, tempPath);
Set<String> plates = Sets.newHashSet();
dst.stream().forEach(inMat ->
PlateColor color = PlateUtil.getPlateColor(inMat, true, false, tempPath);
String plate = PlateUtil.charsSegment(inMat, color, debug, tempPath);
plates.add("<" + plate + "," + color.desc + ">");
);
e.setRecoPlate(plates.toString());
new File(targetPath).delete(); // 删除拷贝的临时文件
plateFileMapper.updateByPrimaryKeySelective(e);
return 1;
@Override
public Object getImgInfo(String imgPath)
Map<String, Object> result = Maps.newHashMap();
String ct = GenerateIdUtil.getStrId();
File f = new File(imgPath);
if(f.exists())
String targetPath = Constant.DEFAULT_TEMP_DIR + ct + (f.getName().substring(f.getName().lastIndexOf(".")));
FileUtil.copyAndRename(f.getAbsolutePath(), targetPath);
result.put("targetPath", targetPath); // 返回临时路径给前端
// 获取图片的基本信息
Mat inMat = Imgcodecs.imread(targetPath);
result.put("rows", inMat.rows());
result.put("cols", inMat.cols());
return result;
@Override
public Object getHSVValue(String imgPath, Integer row, Integer col)
Map<String, Object> result = Maps.newHashMap();
Mat inMat = Imgcodecs.imread(imgPath);
double[] rgb = inMat.get(row, col);
result.put("RGB", JSONObject.toJSONString(rgb));
Mat dst = new Mat(inMat.rows(), inMat.cols(), CvType.CV_32FC3);
Imgproc.cvtColor(inMat, dst, Imgproc.COLOR_BGR2HSV); // 转到HSV空间进行处理
double[] hsv = dst.get(row, col);
result.put("HSV", (int)hsv[0] + ", " + (int)hsv[1] + ", " + (int)hsv[2]);
return result;
package com.znz.service.impl;
import com.znz.service.PlateTypeService;
import com.znz.entity.PlateTypeEntity;
import com.znz.mapper.PlateTypeMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
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.Map;
import java.util.List;
/**
* 服务实现层
* @author znz
* @date 2020-09-30T16:54:41.823
*/
@Service
public class PlateTypeServiceImpl implements PlateTypeService
@Autowired
private PlateTypeMapper plateTypeMapper;
@Override
public PlateTypeEntity getByPrimaryKey(Integer id)
PlateTypeEntity entity = plateTypeMapper.selectByPrimaryKey(id);
return entity;
@Override
public PageInfo<PlateTypeEntity> queryByPage(Integer pageNo, Integer pageSize, Map<String, Object> map)
PageHelper.startPage(pageNo, pageSize);
PageInfo<PlateTypeEntity> page = new PageInfo(plateTypeMapper.selectByCondition(map));
return page;
@Override
public List<PlateTypeEntity> queryByCondition(Map<String, Object> map)
return plateTypeMapper.selectByCondition(map);
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Map<String, Object> save(PlateTypeEntity plateTypeEntity)
plateTypeEntity.setId(0);
plateTypeMapper.insertSelective(plateTypeEntity);
Map<String, Object> result = new HashMap<>();
result.put("id" , plateTypeEntity.getId());
return result;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Integer deleteById(Integer id)
return plateTypeMapper.deleteByPrimaryKey(id);
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Integer updateById(PlateTypeEntity plateTypeEntity)
if(null == plateTypeEntity || plateTypeEntity.getId() <= 0)
return 0;
return plateTypeMapper.updateByPrimaryKeySelective(plateTypeEntity);
package com.znz.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.znz.entity.SystemMenuEntity;
import com.znz.mapper.SystemMenuMapper;
import com.znz.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 znz
* @date 2021-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();
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;
package com.znz.service.impl;
import java.io.File;
import java.util.List;
import org.springframework.stereotype.Service;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.znz.constant.Constant;
import com.znz.exception.ResultReturnException;
import com.znz.service.FileService;
import com.znz.util.FileUtil;
@Service
public class FileServiceImpl implements FileService
@Override
public List<JSONObject> getFileTreeByDir(String rootPath, String dir, String typeFilter)
if(StringUtils.isEmpty(dir))
if(StringUtils.isEmpty(rootPath))
dir = Constant.DEFAULT_DIR;
else
dir = rootPath;
if(StringUtils.isEmpty(typeFilter))
typeFilter = Constant.DEFAULT_TYPE;
File f = new File(dir);
List<File> list = FileUtil.listFile(f, typeFilter, false);
List<JSONObject> result = Lists.newArrayList();
list.stream().forEach(n->
JSONObject jo = new JSONObject();
jo.put("id", n.getAbsolutePath());
jo.put("pid", n.getParentFile().getAbsolutePath());
jo.put("filePath", n.getAbsolutePath());
jo.put("fileName", n.getName());
jo.put("isDir", n.isDirectory());
result.add(jo);
);
return result;
@Override
public File readFile(String filePath)
File f = new File(filePath);
if(!f.exists() || f.isDirectory())
throw new ResultReturnException("filePath参数异常,找不到指定的文件: " + filePath);
if(!f.exists() || f.isDirectory())
throw new ResultReturnException("读取图片异常:" + f.getName());
return f;
以上是关于基于Opencv实现车牌图片识别系统的主要内容,如果未能解决你的问题,请参考以下文章
基于SpringBoot+OpenCV的停车场车牌识别管理系统含人工智能识别算法(附源码论文)
基于SpringBoot+OpenCV的停车场车牌识别管理系统含人工智能识别算法(附源码论文)