欢迎光临散文网 会员登陆 & 注册

基于Opencv实现车牌图片识别系统

2022-03-02 10:05 作者:指南针毕业设计  | 我要投稿



  • 这是一个基于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;@Servicepublic 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 */@Servicepublic 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 */@Servicepublic 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;@Servicepublic 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实现车牌图片识别系统的评论 (共 条)

分享到微博请遵守国家法律