Opencv創(chuàng)建車(chē)牌圖片識(shí)別系統(tǒng)方法詳解
前言
這是一個(gè)基于spring boot + maven + opencv 實(shí)現(xiàn)的圖像識(shí)別及訓(xùn)練的Demo項(xiàng)目
包含車(chē)牌識(shí)別、人臉識(shí)別等功能,貫穿樣本處理、模型訓(xùn)練、圖像處理、對(duì)象檢測(cè)、對(duì)象識(shí)別等技術(shù)點(diǎn)
java語(yǔ)言的深度學(xué)習(xí)項(xiàng)目,在整個(gè)開(kāi)源社區(qū)來(lái)說(shuō)都相對(duì)較少;
擁有完整的訓(xùn)練過(guò)程、檢測(cè)、識(shí)別過(guò)程的開(kāi)源項(xiàng)目更是少之又少?。?/p>
包含功能
- 藍(lán)、綠、黃車(chē)牌檢測(cè)及車(chē)牌號(hào)碼識(shí)別
- 網(wǎng)上常見(jiàn)的輪廓提取車(chē)牌算法JAVA實(shí)現(xiàn)
- hsv色彩分割提取車(chē)牌算法JAVA實(shí)現(xiàn)
- 基于svm算法的車(chē)牌檢測(cè)訓(xùn)練JAVA實(shí)現(xiàn)
- 基于ann算法的車(chē)牌號(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)目
效果圖展示

車(chē)牌識(shí)別

黃牌識(shí)別

綠牌識(shí)別

夜間識(shí)別

圖片提取工具


接口文檔

車(chē)牌檢測(cè)過(guò)程
高斯模糊:

圖像灰度化:

Sobel 算子:

圖像二值化:

圖像閉操作:

二值圖像降噪:

提取外部輪廓:

外部輪廓篩選:

切圖:

重置切圖尺寸:

車(chē)牌檢測(cè)結(jié)果:
圖片車(chē)牌文字識(shí)別過(guò)程
車(chē)牌檢測(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ù)
}
// 查詢(xún)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() {
// 查詢(xún)到還沒(méi)有進(jìn)行車(chē)牌識(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;
}
/**
* 單張圖片 車(chē)牌識(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)建車(chē)牌圖片識(shí)別系統(tǒng)方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Opencv車(chē)牌圖片識(shí)別系統(tǒng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
jmeter添加自定函數(shù)的實(shí)例(jmeter5.3+IntelliJ IDEA)
這篇文章主要介紹了jmeter添加自定函數(shù)的實(shí)例(jmeter5.3+IntelliJ IDEA),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
SpringBoot中事務(wù)失效的六個(gè)原因解析
這篇文章主要介紹了SpringBoot中事務(wù)失效的六個(gè)原因解析,由于Spring的事務(wù)是基于AOP的方式結(jié)合動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的,因此事務(wù)方法一定要是public的,這樣才能便于被Spring做事務(wù)的代理和增強(qiáng),需要的朋友可以參考下2023-10-10
SpringBoot JavaMailSender發(fā)送郵件功能
這篇文章主要為大家詳細(xì)介紹了SpringBoot JavaMailSender發(fā)送郵件功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
Mybatis整合達(dá)夢(mèng)數(shù)據(jù)庫(kù)的完整步驟記錄
作為國(guó)產(chǎn)數(shù)據(jù)庫(kù),達(dá)夢(mèng)做的不錯(cuò),提供的遷移工具也相當(dāng)不錯(cuò),下面這篇文章主要給大家介紹了關(guān)于Mybatis整合達(dá)夢(mèng)數(shù)據(jù)庫(kù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
mybatis查詢(xún)oracle long類(lèi)型的踩坑記錄
這篇文章主要介紹了mybatis查詢(xún)oracle long類(lèi)型的踩坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
spring通過(guò)jdbc連接數(shù)據(jù)庫(kù)
這篇文章主要為大家詳細(xì)介紹了spring通過(guò)jdbc連接數(shù)據(jù)庫(kù)的相關(guān)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
Java中批處理框架spring batch詳細(xì)介紹
這篇文章主要介紹了Java中批處理框架spring batch詳細(xì)介紹,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07

