diff --git a/README.md b/README.md index bd110d6b..5361a772 100644 --- a/README.md +++ b/README.md @@ -60,10 +60,6 @@ debug_Contours: ![1.png](./res/doc_image/debug_Contours.jpg) -debug_result: - -![1.png](./res/doc_image/debug_result.jpg) - debug_crop: ![1.png](./res/doc_image/debug_crop_1.jpg) diff --git a/res/doc_image/debug_result.jpg b/res/doc_image/debug_result.jpg deleted file mode 100644 index 5edacfd7..00000000 Binary files a/res/doc_image/debug_result.jpg and /dev/null differ diff --git a/src/main/java/com/yuxue/util/FileUtil.java b/src/main/java/com/yuxue/util/FileUtil.java index 9527bcf5..404afad2 100644 --- a/src/main/java/com/yuxue/util/FileUtil.java +++ b/src/main/java/com/yuxue/util/FileUtil.java @@ -44,15 +44,32 @@ public class FileUtil { return false; } - + /** + * 重命名文件 + * @param file + * @param newName 可以是文件名,也可以是路径+文件名 + * @return + */ public static boolean renameFile(String filePath, String newName) { File file = new File(filePath); return renameFile(file, newName); } + /** + * 重命名文件 + * @param file + * @param newName 可以是文件名,也可以是路径+文件名 + * @return + */ public static boolean renameFile(File file, String newName) { if(file.exists()) { - String targetPath = file.getParentFile().getAbsolutePath() + "/" + newName; + String targetPath = null; + if(newName.indexOf("/") >= 0 || newName.indexOf("\\\\") >= 0) { + targetPath = newName; + } else { + targetPath = file.getParentFile().getAbsolutePath() + "/" + newName; + } + File targetFile = new File(targetPath); file.renameTo(targetFile); return true; diff --git a/src/main/java/com/yuxue/util/ImageUtil.java b/src/main/java/com/yuxue/util/ImageUtil.java index 032bb7a9..84bec6f2 100644 --- a/src/main/java/com/yuxue/util/ImageUtil.java +++ b/src/main/java/com/yuxue/util/ImageUtil.java @@ -1,6 +1,5 @@ package com.yuxue.util; - import java.util.Arrays; import java.util.Map; import java.util.Set; @@ -9,6 +8,7 @@ import org.bytedeco.javacpp.BytePointer; import org.bytedeco.javacpp.opencv_core; import org.bytedeco.javacpp.opencv_core.Mat; import org.bytedeco.javacpp.opencv_core.MatVector; +import org.bytedeco.javacpp.opencv_core.Scalar; import org.bytedeco.javacpp.opencv_core.Size; import org.bytedeco.javacpp.opencv_imgcodecs; import org.bytedeco.javacpp.opencv_imgproc; @@ -25,31 +25,53 @@ public class ImageUtil { private static String DEFAULT_BASE_TEST_PATH = "D:/PlateDetect/temp/"; + // 车牌定位处理步骤,该map用于表示步骤图片的顺序 + private static Map debugMap = Maps.newLinkedHashMap(); + static { + // debugMap.put("result", 99); + debugMap.put("gaussianBlur", 0); // 高斯模糊 + debugMap.put("gray", 1); // 图像灰度化 + debugMap.put("sobel", 2); // Sobel 算子 + debugMap.put("threshold", 3); //图像二值化 + debugMap.put("morphology", 4); // 图像闭操作 + debugMap.put("contours", 5); // 提取外部轮廓 + debugMap.put("result", 6); // 原图处理结果 + debugMap.put("crop", 7); // 切图 + debugMap.put("resize", 8); // 切图resize + debugMap.put("char_threshold", 9); // + // debugMap.put("char_clearLiuDing", 10); // 去除柳钉 + // debugMap.put("specMat", 11); + // debugMap.put("chineseMat", 12); + // debugMap.put("char_auxRoi", 13); + } + + public static void main(String[] args) { + + String filename = DEFAULT_BASE_TEST_PATH + "test.jpg"; + // String filename = DEFAULT_BASE_TEST_PATH + "test01.jpg"; String tempPath = DEFAULT_BASE_TEST_PATH + System.currentTimeMillis() + "/"; FileUtil.createDir(tempPath); // 创建文件夹 - - // String filename = DEFAULT_BASE_TEST_PATH + "test01.jpg"; - String filename = DEFAULT_BASE_TEST_PATH + "test.png"; - Mat inMat = opencv_imgcodecs.imread(filename); + FileUtil.renameFile(filename, tempPath + "000_yuantu.jpg"); + + Mat inMat = opencv_imgcodecs.imread(filename); + Boolean debug = true; Mat gsMat = ImageUtil.gaussianBlur(inMat, debug, tempPath); - + Mat grey = ImageUtil.grey(gsMat, debug, tempPath); - + Mat sobel = ImageUtil.sobel(grey, debug, tempPath); - - - - + + // ImageUtil.rgb2Hsv(inMat, debug, tempPath); } - + /** * 高斯模糊 * @param inMat @@ -61,12 +83,12 @@ public class ImageUtil { Mat dst = new Mat(); opencv_imgproc.GaussianBlur(inMat, dst, new Size(DEFAULT_GAUSSIANBLUR_SIZE, DEFAULT_GAUSSIANBLUR_SIZE), 0, 0, opencv_core.BORDER_DEFAULT); if (debug) { - opencv_imgcodecs.imwrite(tempPath + "gaussianBlur.jpg", dst); + opencv_imgcodecs.imwrite(tempPath + (debugMap.get("gaussianBlur") + 100) + "_gaussianBlur.jpg", dst); } return dst; } - - + + /** * 将图像进行灰度化 * @param inMat @@ -78,12 +100,12 @@ public class ImageUtil { Mat dst = new Mat(); opencv_imgproc.cvtColor(inMat, dst, opencv_imgproc.CV_RGB2GRAY); if (debug) { - opencv_imgcodecs.imwrite(tempPath + "debugGray.jpg", dst); + opencv_imgcodecs.imwrite(tempPath + (debugMap.get("gray") + 100) + "_gray.jpg", dst); } return dst; } - - + + /** * 对图像进行Sobel 运算,得到图像的一阶水平方向导数 * @param inMat @@ -97,14 +119,14 @@ public class ImageUtil { public static final int SOBEL_X_WEIGHT = 1; public static final int SOBEL_Y_WEIGHT = 0; public static Mat sobel(Mat inMat, Boolean debug, String tempPath) { - + Mat dst = new Mat(); - + Mat grad_x = new Mat(); Mat grad_y = new Mat(); Mat abs_grad_x = new Mat(); Mat abs_grad_y = new Mat(); - + opencv_imgproc.Sobel(inMat, grad_x, SOBEL_DDEPTH, 1, 0, 3, SOBEL_SCALE, SOBEL_DELTA, opencv_core.BORDER_DEFAULT); opencv_core.convertScaleAbs(grad_x, abs_grad_x); @@ -113,15 +135,15 @@ public class ImageUtil { // Total Gradient (approximate) opencv_core.addWeighted(abs_grad_x, SOBEL_X_WEIGHT, abs_grad_y, SOBEL_Y_WEIGHT, 0, dst); - + if (debug) { - opencv_imgcodecs.imwrite(tempPath + "debugSobel.jpg", dst); + opencv_imgcodecs.imwrite(tempPath + (debugMap.get("sobel") + 100) + "_sobel.jpg", dst); } return dst; } - - - + + + /** * 对图像进行二值化。将灰度图像(每个像素点有256 个取值可能)转化为二值图像(每个像素点仅有1 和0 两个取值可能) * @param inMat @@ -133,12 +155,63 @@ public class ImageUtil { Mat dst = new Mat(); opencv_imgproc.threshold(inMat, dst, 0, 255, opencv_imgproc.CV_THRESH_OTSU + opencv_imgproc.CV_THRESH_BINARY); if (debug) { - opencv_imgcodecs.imwrite(tempPath + "debugThreshold.jpg", dst); + opencv_imgcodecs.imwrite(tempPath + (debugMap.get("threshold") + 100) + "_threshold.jpg", dst); + } + return dst; + } + + + + + + /** + * 使用闭操作。对图像进行闭操作以后,可以看到车牌区域被连接成一个矩形装的区域 + * @param inMat + * @param debug + * @param tempPath + * @return + */ + public static final int DEFAULT_MORPH_SIZE_WIDTH = 17; + public static final int DEFAULT_MORPH_SIZE_HEIGHT = 3; + public static Mat morphology(Mat inMat, Boolean debug, String tempPath) { + Mat dst = new Mat(); + Size size = new Size(DEFAULT_MORPH_SIZE_WIDTH, DEFAULT_MORPH_SIZE_HEIGHT); + + Mat element = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, size); + opencv_imgproc.morphologyEx(inMat, dst, opencv_imgproc.MORPH_CLOSE, element); + + if (debug) { + opencv_imgcodecs.imwrite(tempPath + (debugMap.get("morphology") + 100) + "_morphology.jpg", dst); } return dst; } + /** + * Find 轮廓 of possibles plates 求轮廓。求出图中所有的轮廓。 + * 这个算法会把全图的轮廓都计算出来,因此要进行筛选。 + * @param src 原图 + * @param inMat morphology Mat + * @param debug + * @param tempPath + * @return + */ + public static MatVector contours(Mat src, Mat inMat, Boolean debug, String tempPath) { + MatVector contours = new MatVector(); + opencv_imgproc.findContours(inMat, contours, // a vector of contours + opencv_imgproc.CV_RETR_EXTERNAL, // 提取外部轮廓 + opencv_imgproc.CV_CHAIN_APPROX_NONE); // all pixels of each contours + + if (debug) { + // 将轮廓描绘到原图 + opencv_imgproc.drawContours(src, contours, -1, new Scalar(0, 0, 255, 255)); + opencv_imgcodecs.imwrite(tempPath + (debugMap.get("contours") + 100) + "_contours.jpg", src); + } + return contours; + } + + + /** @@ -157,13 +230,13 @@ public class ImageUtil { // 直方图均衡化是一种常见的增强图像对比度的方法,使用该方法可以增强局部图像的对比度,尤其在数据较为相似的图像中作用更加明显 opencv_imgproc.equalizeHist(hsvSplit.get(2), hsvSplit.get(2)); opencv_core.merge(hsvSplit, dst); - + if (debug) { opencv_imgcodecs.imwrite(tempPath + "hsvMat_"+System.currentTimeMillis()+".jpg", dst); } return dst; } - + /** * 获取HSV中各个颜色所对应的H的范围 @@ -175,7 +248,7 @@ public class ImageUtil { * @param debug */ public static void getHSVValue(Mat inMat, Boolean debug, String tempPath) { - + int channels = inMat.channels(); int nRows = inMat.rows(); // 图像数据列需要考虑通道数的影响; @@ -200,7 +273,6 @@ public class ImageUtil { } else { map.put(H, 1); } - } } @@ -214,5 +286,5 @@ public class ImageUtil { return; } - + }