Opencv創(chuàng)建車牌圖片識(shí)別系統(tǒng)方法詳解
前言
這是一個(gè)基于spring boot + maven + opencv 實(shí)現(xiàn)的圖像識(shí)別及訓(xùn)練的Demo項(xiàng)目
包含車牌識(shí)別、人臉識(shí)別等功能,貫穿樣本處理、模型訓(xùn)練、圖像處理、對(duì)象檢測(cè)、對(duì)象識(shí)別等技術(shù)點(diǎn)
java語(yǔ)言的深度學(xué)習(xí)項(xiàng)目,在整個(gè)開源社區(qū)來(lái)說(shuō)都相對(duì)較少;
擁有完整的訓(xùn)練過(guò)程、檢測(cè)、識(shí)別過(guò)程的開源項(xiàng)目更是少之又少!!
包含功能
- 藍(lán)、綠、黃車牌檢測(cè)及車牌號(hào)碼識(shí)別
- 網(wǎng)上常見的輪廓提取車牌算法JAVA實(shí)現(xiàn)
- hsv色彩分割提取車牌算法JAVA實(shí)現(xiàn)
- 基于svm算法的車牌檢測(cè)訓(xùn)練JAVA實(shí)現(xiàn)
- 基于ann算法的車牌號(hào)碼識(shí)別訓(xùn)練JAVA實(shí)現(xiàn)
- 人臉檢測(cè) 接下來(lái)將實(shí)現(xiàn)人臉識(shí)別
- 圖片處理工具,目前實(shí)現(xiàn)了HSV色彩切割,后續(xù)將添加更多使用的圖片處理工具,用于輔助算法優(yōu)化
軟件版本
- 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版本
軟件架構(gòu)
B/S 架構(gòu),前端html + requireJS,后端java
數(shù)據(jù)庫(kù)使用 sqlite3.0
接口文檔使用swagger 2.0
參考文檔
參考了EasyPR C++項(xiàng)目、以及fan-wenjie的EasyPR-Java項(xiàng)目;同時(shí)查閱了部分opencv官方4.0.1版本C++的源碼,結(jié)合個(gè)人對(duì)java語(yǔ)言的理解,整理出當(dāng)前項(xiàng)目
效果圖展示
車牌識(shí)別
黃牌識(shí)別
綠牌識(shí)別
夜間識(shí)別
圖片提取工具
接口文檔
車牌檢測(cè)過(guò)程
高斯模糊:
圖像灰度化:
Sobel 算子:
圖像二值化:
圖像閉操作:
二值圖像降噪:
提取外部輪廓:
外部輪廓篩選:
切圖:
重置切圖尺寸:
車牌檢測(cè)結(jié)果:
圖片車牌文字識(shí)別過(guò)程
車牌檢測(cè)結(jié)果:
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下第一層級(jí)的目錄, 僅獲取文件夾,不遞歸子目錄,遍歷 List<File> folderList = FileUtil.listFile(baseDir, ";", false); folderList.parallelStream().forEach(folder -> { if(!folder.getName().equals("temp")) { // 遍歷每一個(gè)文件夾, 遞歸獲取文件夾下的圖片 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); // 重新識(shí)別 entity = plateFileMapper.selectByPrimaryKey(entity.getId()); // 重新識(shí)別之后,重新獲取一下數(shù)據(jù) } // 查詢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() { // 查詢到還沒有進(jìn)行車牌識(shí)別的圖片 List<PlateFileEntity> list = plateFileMapper.getUnRecogniseList(); list.parallelStream().forEach(n->{ File f = new File(n.getFilePath()); if(FileUtil.checkFile(f)) { doRecognise(f, n); } }); return 1; } /** * 單張圖片 車牌識(shí)別 * 拷貝文件到臨時(shí)目錄 * 過(guò)程及結(jié)果更新數(shù)據(jù)庫(kù) * @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); // 拷貝文件并且重命名 // 創(chuàng)建臨時(shí)目錄, 存放過(guò)程圖片 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(); // 刪除拷貝的臨時(shí)文件 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); // 返回臨時(shí)路徑給前端 // 獲取圖片的基本信息 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); // 轉(zhuǎn)到HSV空間進(jìn)行處理 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; /** * 服務(wù)實(shí)現(xiàn)層 * @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; /** * 服務(wù)實(shí)現(xiàn)層 * @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); //按層級(jí)封裝,最多三級(jí) 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參數(shù)異常,找不到指定的文件: " + filePath); } if(!f.exists() || f.isDirectory()) { throw new ResultReturnException("讀取圖片異常:" + f.getName()); } return f; } }
以上就是Opencv創(chuàng)建車牌圖片識(shí)別系統(tǒng)方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Opencv車牌圖片識(shí)別系統(tǒng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot使用Thymeleaf報(bào)錯(cuò)常見的幾種解決方案
這篇文章主要介紹了springboot使用Thymeleaf報(bào)錯(cuò)常見的幾種解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11Maven項(xiàng)目報(bào)錯(cuò):“?SLF4J:?Failed?to?load?class?“org.slf4j.imp
這篇文章主要給大家介紹了關(guān)于Maven項(xiàng)目報(bào)錯(cuò):“?SLF4J:?Failed?to?load?class?“org.slf4j.impl.StaticLoggerBinder?”的解決方案,文中給出詳細(xì)的解決思路與方法,需要的朋友可以參考下2022-03-03Java超詳細(xì)講解如何生成隨機(jī)整數(shù)
在?Java?中,生成隨機(jī)數(shù)的場(chǎng)景有很多,所以本文我們就來(lái)盤點(diǎn)一下?幾種生成隨機(jī)數(shù)的方式,以及它們之間的區(qū)別和每種生成方式所對(duì)應(yīng)的場(chǎng)景2022-05-05Java?多個(gè)時(shí)間區(qū)間進(jìn)行合并處理方法
用戶在選擇多個(gè)時(shí)間區(qū)間之后,如選擇的時(shí)間區(qū)間連續(xù)或者有重疊,需要對(duì)所選的時(shí)間區(qū)間進(jìn)行合并,這其實(shí)是一個(gè)區(qū)間合并問題,下面通過(guò)本文給大家介紹Java?多個(gè)時(shí)間區(qū)間進(jìn)行合并處理的解決方案,一起看看吧2024-02-02java 利用反射機(jī)制,獲取實(shí)體所有屬性和方法,并對(duì)屬性賦值
這篇文章主要介紹了 java 利用反射機(jī)制,獲取實(shí)體所有屬性和方法,并對(duì)屬性賦值的相關(guān)資料,需要的朋友可以參考下2017-01-01idea進(jìn)程結(jié)束但是項(xiàng)目頁(yè)面正常運(yùn)行怎么辦
這篇文章主要介紹了idea進(jìn)程結(jié)束但是項(xiàng)目頁(yè)面正常運(yùn)行怎么辦,很多朋友遇到這樣的情況不知道該如何解決了,下面小編給大家?guī)?lái)了idea進(jìn)程結(jié)束但是項(xiàng)目頁(yè)面正常運(yùn)行的解決方法,需要的朋友可以參考下2023-03-03Java?NIO下ByteBuffer的常用方法學(xué)習(xí)
這篇文章主要帶大家來(lái)初步學(xué)習(xí)一下NIO?中的?ByteBuffer的應(yīng)用與常用方法,文中的示例代碼講解詳細(xì),對(duì)我們深入學(xué)習(xí)Java有一定的幫助,感興趣的可以了解一下2023-05-05Java多線程 兩階段終止模式Two-Phase Termination Patter
這篇文章主要介紹了Java多線程 兩階段終止模式Two-Phase Termination Patter,該模式有兩個(gè)角色,分別是Terminator,終止者,負(fù)責(zé)接收終止請(qǐng)求,執(zhí)行終止處理,處理完成后再終止自己。TerminationRequester終止請(qǐng)求發(fā)出者,用來(lái)向Terminator發(fā)出終止請(qǐng)求,需要的朋友可以參考一下2021-10-10