package com.yuxue.service.impl; import java.io.File; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bytedeco.javacpp.opencv_core.Mat; import org.bytedeco.javacpp.opencv_imgcodecs; 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.google.common.collect.Lists; import com.google.common.collect.Maps; import com.yuxue.constant.Constant; import com.yuxue.easypr.core.CharsRecognise; import com.yuxue.easypr.core.CoreFunc; import com.yuxue.easypr.core.PlateDetect; import com.yuxue.entity.PlateFileEntity; import com.yuxue.entity.PlateRecoDebugEntity; import com.yuxue.entity.TempPlateFileEntity; import com.yuxue.enumtype.PlateColor; import com.yuxue.mapper.PlateFileMapper; import com.yuxue.mapper.PlateRecoDebugMapper; import com.yuxue.mapper.TempPlateFileMapper; import com.yuxue.service.PlateService; import com.yuxue.util.FileUtil; @Service public class PlateServiceImpl implements PlateService { // 车牌定位处理步骤,该map用于表示步骤图片的顺序 private static Map debugMap = Maps.newLinkedHashMap(); static { debugMap.put("result", 99); debugMap.put("debug_GaussianBlur", 0); // 高斯模糊 debugMap.put("debug_gray", 1); // 图像灰度化 debugMap.put("debug_Sobel", 2); // Sobel 算子 debugMap.put("debug_threshold", 3); //图像二值化 debugMap.put("debug_morphology", 4); // 图像闭操作 debugMap.put("debug_Contours", 5); // 提取外部轮廓 debugMap.put("debug_result", 6); // 原图处理结果 debugMap.put("debug_crop", 7); // 切图 debugMap.put("debug_resize", 8); // 切图resize debugMap.put("debug_char_threshold", 9); // // debugMap.put("debug_char_clearLiuDing", 10); // 去除柳钉 debugMap.put("debug_specMat", 11); // debugMap.put("debug_chineseMat", 12); // debugMap.put("debug_char_auxRoi", 13); // } @Autowired private PlateFileMapper plateFileMapper; @Autowired private PlateRecoDebugMapper plateRecoDebugMapper; @Autowired private TempPlateFileMapper tempPlateFileMapper; @Override public Object recognise(String filePath, boolean reRecognise) { filePath = filePath.replaceAll("\\\\", "/"); File f = new File(filePath); PlateFileEntity e = null; Map paramMap = Maps.newHashMap(); paramMap.put("filePath", filePath); List list= plateFileMapper.selectByCondition(paramMap); if(null == list || list.size() <= 0) { if(FileUtil.checkFile(f)) { e = new PlateFileEntity(); e.setFileName(f.getName()); e.setFilePath(f.getAbsolutePath().replaceAll("\\\\", "/")); e.setFileType(f.getName().substring(f.getName().lastIndexOf(".") + 1)); plateFileMapper.insertSelective(e); } reRecognise = true; } else { e = list.get(0); } if(reRecognise) { doRecognise(f, e, 0); // 重新识别 e = plateFileMapper.selectByPrimaryKey(e.getId()); // 重新识别之后,重新获取一下数据 } // 查询数据库,返回结果 paramMap.clear(); paramMap.put("parentId", e.getId()); e.setDebug(plateRecoDebugMapper.selectByCondition(paramMap)); return e; } @Override @Transactional(propagation = Propagation.REQUIRED) public Object refreshFileInfo() { File baseDir = new File(Constant.DEFAULT_DIR); if(!baseDir.exists() || !baseDir.isDirectory()) { return null; } List resultList = Lists.newArrayList(); // 获取baseDir下第一层级的目录, 仅获取文件夹,不递归子目录,遍历 List folderList = FileUtil.listFile(baseDir, ";", false); folderList.parallelStream().forEach(folder -> { if(!folder.getName().equals("temp")) { // 遍历每一个文件夹, 递归获取文件夹下的图片 List 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 recogniseAll() { // 查询到还没有进行车牌识别的图片 List list = plateFileMapper.getUnRecogniseList(); // 开启多线程进行识别 Random r = new Random(99); list.parallelStream().forEach(n->{ File f = new File(n.getFilePath()); if(FileUtil.checkFile(f)) { doRecognise(f, n, r.nextInt()); } }); return 1; } @Override public Object getProcessStep() { return debugMap; } /** * 单张图片 车牌识别 * 拷贝文件到临时目录 * 过程及结果更新数据库 * @param f 调用方需要验证文件存在 * @param result * @return */ public Object doRecognise(File f, PlateFileEntity e, Integer seed) { // 插入识别过程图片数据信息 通过temp文件夹的文件,更新数据库 List debug = Lists.newArrayList(); Long ct = System.currentTimeMillis(); String targetPath = Constant.DEFAULT_TEMP_DIR.concat(ct.toString() + seed) .concat(f.getAbsolutePath().substring(f.getAbsolutePath().lastIndexOf("."))); // 先将文件拷贝并且重命名到不包含中文及特殊字符的目录下 FileUtil.copyAndRename(f.getAbsolutePath(), targetPath); // 开始识别,生成过程及结果切图,将识别结果更新到数据库 Mat src = opencv_imgcodecs.imread(targetPath); String tempPath = Constant.DEFAULT_TEMP_DIR + ct + "/"; FileUtil.createDir(tempPath); // 创建文件夹 // 车牌检测对象 PlateDetect plateDetect = new PlateDetect(); plateDetect.setPDLifemode(true); plateDetect.setDebug(true, tempPath); // 将过程的图块保存到盘符 Vector matVector = new Vector(); if (0 == plateDetect.plateDetect(src, matVector)) { // 定位及判断,获取到车牌图块Mat CharsRecognise cr = new CharsRecognise(); cr.setCRDebug(true); for (int i = 0; i < matVector.size(); ++i) { // 遍历车牌图块Mat,进行识别 Mat img = matVector.get(i); String palte = cr.charsRecognise(img, tempPath); // 字符识别 PlateColor color = CoreFunc.getPlateType(img, true); String fileName = "result_" + i + ".png"; // 识别的车牌,保存图片文件 String str = tempPath + fileName; // 此方法生成的文件,中文名称都是乱码,试了各种编解码均无效,OpenCV自身的编解码问题。 opencv_imgcodecs.imwrite(str, img); // 重命名文件,让生成的文件包含中文 // String newName = palte + "_"+ color + ".png"; // FileUtil.renameFile(str, newName); PlateRecoDebugEntity de = new PlateRecoDebugEntity(); de.setRecoPlate(palte); de.setFilePath(str); de.setFileName(fileName); de.setPlateColor(color.desc); de.setParentId(e.getId()); de.setDebugType("result"); de.setSort(debugMap.get("result")); debug.add(de); } } else { e.setRecoCorrect(3); // 未检测到车牌 } new File(targetPath).delete(); // 删除拷贝的文件 e.setTempPath(tempPath); List debugList = FileUtil.listFile(new File(tempPath), Constant.DEFAULT_TYPE, false); debugList.parallelStream().forEach(d -> { String name = d.getName().substring(0, d.getName().lastIndexOf(".")); Pattern pattern = Pattern.compile("\\d+$"); Matcher matcher = pattern.matcher(name); if(matcher.find()) { name = name.substring(0, name.lastIndexOf("_")); } if(!"result".equals(name)) { PlateRecoDebugEntity de = new PlateRecoDebugEntity(); de.setRecoPlate(""); de.setFilePath(d.getAbsolutePath().replaceAll("\\\\", "/")); de.setFileName(d.getName()); de.setPlateColor(""); de.setParentId(e.getId()); de.setDebugType(name); de.setSort(debugMap.get(name)); debug.add(de); } }); // 更新图片主表信息 plateFileMapper.updateByPrimaryKeySelective(e); plateRecoDebugMapper.deleteByParentId(e.getId()); plateRecoDebugMapper.batchInsert(debug); return 1; } }