信息发布→ 登录 注册 退出

Opencv创建车牌图片识别系统方法详解

发布时间:2026-01-11

点击量:
目录
  • 前言
    • 包含功能
    • 软件版本
    • 软件架构
    • 参考文档
  • 效果图展示
    • 车牌检测过程
      • 图片车牌文字识别过程
        • 部分核心代码

          前言

          这是一个基于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

          fan-wenjie/EasyPR-Java

          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 2025-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 2025-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创建车牌图片识别系统方法详解的详细内容,更多关于Opencv车牌图片识别系统的资料请关注其它相关文章!

          在线客服
          服务热线

          服务热线

          4008888355

          微信咨询
          二维码
          返回顶部
          ×二维码

          截屏,微信识别二维码

          打开微信

          微信号已复制,请打开微信添加咨询详情!