测试工具类

devA
yuxue 5 years ago
parent 19575f5e9b
commit c5d6723405

@ -24,7 +24,7 @@ import org.bytedeco.javacpp.opencv_core.Size;
* @date 2020-04-24 15:33 * @date 2020-04-24 15:33
*/ */
public class PlateLocate { public class PlateLocate {
// PlateLocate所用常量 // PlateLocate所用常量
public static final int DEFAULT_GAUSSIANBLUR_SIZE = 5; public static final int DEFAULT_GAUSSIANBLUR_SIZE = 5;
public static final int SOBEL_SCALE = 1; public static final int SOBEL_SCALE = 1;
@ -46,7 +46,7 @@ public class PlateLocate {
final float DEFAULT_ERROR = 0.6f; final float DEFAULT_ERROR = 0.6f;
final float DEFAULT_ASPECT = 3.75f; final float DEFAULT_ASPECT = 3.75f;
// 角度判断所用常量 // 角度判断所用常量
public static final int DEFAULT_ANGLE = 30; public static final int DEFAULT_ANGLE = 30;
@ -68,7 +68,7 @@ public class PlateLocate {
// 是否开启调试模式0关闭非0开启 // 是否开启调试模式0关闭非0开启
protected boolean debug = true; protected boolean debug = true;
// 开启调试模式之后,切图文件保存路径 // 开启调试模式之后,切图文件保存路径
protected String tempPath = Constant.DEFAULT_TEMP_DIR + System.currentTimeMillis() + "/"; protected String tempPath = Constant.DEFAULT_TEMP_DIR + System.currentTimeMillis() + "/";
@ -193,51 +193,45 @@ public class PlateLocate {
int k = 1; int k = 1;
for (int i = 0; i < rects.size(); i++) { for (int i = 0; i < rects.size(); i++) {
RotatedRect minRect = rects.get(i); RotatedRect minRect = rects.get(i);
if (verifySizes(minRect)) { if (debug) {
Point2f rect_points = new Point2f(4);
if (debug) { minRect.points(rect_points);
Point2f rect_points = new Point2f(4);
minRect.points(rect_points);
for (int j = 0; j < 4; j++) {
Point pt1 = new Point(new CvPoint2D32f(rect_points.position(j)));
Point pt2 = new Point(new CvPoint2D32f(rect_points.position((j + 1) % 4)));
line(result, pt1, pt2, new Scalar(0, 255, 255, 255), 1, 8, 0);
}
}
// rotated rectangle drawing for (int j = 0; j < 4; j++) {
// 旋转这部分代码确实可以将某些倾斜的车牌调整正,但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。 Point pt1 = new Point(new CvPoint2D32f(rect_points.position(j)));
// 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试这段代码。 Point pt2 = new Point(new CvPoint2D32f(rect_points.position((j + 1) % 4)));
float r = minRect.size().width() / minRect.size().height(); line(result, pt1, pt2, new Scalar(0, 255, 255, 255), 1, 8, 0);
float angle = minRect.angle();
Size rect_size = new Size((int) minRect.size().width(), (int) minRect.size().height());
if (r < 1) {
angle = 90 + angle;
rect_size = new Size(rect_size.height(), rect_size.width());
}
// 如果抓取的方块旋转超过m_angle角度则不是车牌放弃处理
if (angle - this.angle < 0 && angle + this.angle > 0) {
// Create and rotate image
Mat rotmat = getRotationMatrix2D(minRect.center(), angle, 1);
Mat img_rotated = new Mat();
warpAffine(src, img_rotated, rotmat, src.size()); // CV_INTER_CUBIC
Mat resultMat = showResultMat(img_rotated, rect_size, minRect.center(), k++);
resultVec.add(resultMat);
} }
} }
}
if (debug) { // rotated rectangle drawing
opencv_imgcodecs.imwrite(tempPath + "debug_result.jpg", result); // 旋转这部分代码确实可以将某些倾斜的车牌调整正,但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。
// 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试这段代码。
float r = minRect.size().width() / minRect.size().height();
float angle = minRect.angle();
Size rect_size = new Size((int) minRect.size().width(), (int) minRect.size().height());
if (r < 1) {
angle = 90 + angle;
rect_size = new Size(rect_size.height(), rect_size.width());
}
// 如果抓取的方块旋转超过m_angle角度则不是车牌放弃处理
if (angle - this.angle < 0 && angle + this.angle > 0) {
// Create and rotate image
Mat rotmat = getRotationMatrix2D(minRect.center(), angle, 1);
Mat img_rotated = new Mat();
warpAffine(src, img_rotated, rotmat, src.size()); // CV_INTER_CUBIC
Mat resultMat = showResultMat(img_rotated, rect_size, minRect.center(), k++);
resultVec.add(resultMat);
}
} }
return resultVec; return resultVec;
} }
/** /**
* minAreaRect * minAreaRect
* *
@ -251,7 +245,7 @@ public class PlateLocate {
float aspect = this.aspect; float aspect = this.aspect;
int min = 44 * 14 * verifyMin; // minimum area int min = 44 * 14 * verifyMin; // minimum area
int max = 44 * 14 * verifyMax; // maximum area int max = 44 * 14 * verifyMax; // maximum area
// Get only patchs that match to a respect ratio. // Get only patchs that match to a respect ratio.
float rmin = aspect - aspect * error; float rmin = aspect - aspect * error;
float rmax = aspect + aspect * error; float rmax = aspect + aspect * error;
@ -260,7 +254,7 @@ public class PlateLocate {
float r = mr.size().width() / mr.size().height(); float r = mr.size().width() / mr.size().height();
if (r < 1) if (r < 1)
r = mr.size().height() / mr.size().width(); r = mr.size().height() / mr.size().width();
return area >= min && area <= max && r >= rmin && r <= rmax; return area >= min && area <= max && r >= rmin && r <= rmax;
} }
@ -289,8 +283,8 @@ public class PlateLocate {
return resultResized; return resultResized;
} }
public String getTempPath() { public String getTempPath() {
return tempPath; return tempPath;
} }
@ -298,7 +292,7 @@ public class PlateLocate {
public void setTempPath(String tempPath) { public void setTempPath(String tempPath) {
this.tempPath = tempPath; this.tempPath = tempPath;
} }
public void setGaussianBlurSize(int gaussianBlurSize) { public void setGaussianBlurSize(int gaussianBlurSize) {
this.gaussianBlurSize = gaussianBlurSize; this.gaussianBlurSize = gaussianBlurSize;
} }
@ -357,5 +351,5 @@ public class PlateLocate {
public boolean getDebug() { public boolean getDebug() {
return debug; return debug;
} }
} }

@ -1,6 +1,5 @@
package com.yuxue.util; package com.yuxue.util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -19,7 +18,6 @@ import org.bytedeco.javacpp.opencv_imgcodecs;
import org.bytedeco.javacpp.opencv_imgproc; import org.bytedeco.javacpp.opencv_imgproc;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.yuxue.constant.Constant;
/** /**
@ -30,23 +28,23 @@ import com.yuxue.constant.Constant;
public class ImageUtil { public class ImageUtil {
private static SVM svm = SVM.create(); private static SVM svm = SVM.create();
private static ANN_MLP ann=ANN_MLP.create(); private static ANN_MLP ann=ANN_MLP.create();
private static String DEFAULT_BASE_TEST_PATH = "D:/PlateDetect/temp/"; private static String DEFAULT_BASE_TEST_PATH = "D:/PlateDetect/temp/";
public static void loadSVM(String path) { public static void loadSvmModel(String path) {
svm.clear(); svm.clear();
svm=SVM.load(path); svm=SVM.load(path);
} }
// 加载ann配置文件 图像转文字的训练库文件 // 加载ann配置文件 图像转文字的训练库文件
public static void loadModel(String path) { public static void loadAnnModel(String path) {
ann.clear(); ann.clear();
ann = ANN_MLP.load(path); ann = ANN_MLP.load(path);
} }
// 车牌定位处理步骤该map用于表示步骤图片的顺序 // 车牌定位处理步骤该map用于表示步骤图片的顺序
private static Map<String, Integer> debugMap = Maps.newLinkedHashMap(); private static Map<String, Integer> debugMap = Maps.newLinkedHashMap();
static { static {
@ -67,36 +65,40 @@ public class ImageUtil {
// debugMap.put("specMat", 11); // debugMap.put("specMat", 11);
// debugMap.put("chineseMat", 12); // debugMap.put("chineseMat", 12);
// debugMap.put("char_auxRoi", 13); // debugMap.put("char_auxRoi", 13);
// 加载训练库文件 // 加载训练库文件
//loadModel(Constant.DEFAULT_ANN_PATH); //loadAnnModel(Constant.DEFAULT_ANN_PATH);
//loadSVM(Constant.DEFAULT_SVM_PATH); //loadSvmModel(Constant.DEFAULT_SVM_PATH);
} }
public static void main(String[] args) { public static void main(String[] args) {
String tempPath = DEFAULT_BASE_TEST_PATH + "test/"; String tempPath = DEFAULT_BASE_TEST_PATH + "test/";
String filename = tempPath + "/100_yuantu.jpg"; String filename = tempPath + "/100_yuantu.jpg";
Mat inMat = opencv_imgcodecs.imread(filename); Mat src = opencv_imgcodecs.imread(filename);
Boolean debug = true; Boolean debug = true;
Mat gsMat = ImageUtil.gaussianBlur(inMat, debug, tempPath); Mat gsMat = ImageUtil.gaussianBlur(src, debug, tempPath);
Mat grey = ImageUtil.grey(gsMat, debug, tempPath); Mat grey = ImageUtil.grey(gsMat, debug, tempPath);
Mat sobel = ImageUtil.sobel(grey, debug, tempPath); Mat sobel = ImageUtil.sobel(grey, debug, tempPath);
Mat threshold = ImageUtil.threshold(sobel, debug, tempPath); Mat threshold = ImageUtil.threshold(sobel, debug, tempPath);
Mat morphology = ImageUtil.morphology(threshold, debug, tempPath); Mat morphology = ImageUtil.morphology(threshold, debug, tempPath);
MatVector contours = ImageUtil.contours(inMat, morphology, debug, tempPath);
MatVector contours = ImageUtil.contours(src, morphology, debug, tempPath);
Vector<Mat> rects = ImageUtil.screenBlock(src, contours, debug, tempPath);
// ImageUtil.rgb2Hsv(inMat, debug, tempPath); // ImageUtil.rgb2Hsv(inMat, debug, tempPath);
System.err.println("done!!!");
} }
@ -117,7 +119,7 @@ public class ImageUtil {
return dst; return dst;
} }
/** /**
* *
* @param inMat * @param inMat
@ -200,7 +202,7 @@ public class ImageUtil {
public static Mat morphology(Mat inMat, Boolean debug, String tempPath) { public static Mat morphology(Mat inMat, Boolean debug, String tempPath) {
Mat dst = new Mat(); Mat dst = new Mat();
Size size = new Size(DEFAULT_MORPH_SIZE_WIDTH, DEFAULT_MORPH_SIZE_HEIGHT); Size size = new Size(DEFAULT_MORPH_SIZE_WIDTH, DEFAULT_MORPH_SIZE_HEIGHT);
Mat element = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, size); Mat element = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, size);
opencv_imgproc.morphologyEx(inMat, dst, opencv_imgproc.MORPH_CLOSE, element); opencv_imgproc.morphologyEx(inMat, dst, opencv_imgproc.MORPH_CLOSE, element);
@ -209,8 +211,8 @@ public class ImageUtil {
} }
return dst; return dst;
} }
/** /**
* Find of possibles plates * Find of possibles plates
* *
@ -222,20 +224,21 @@ public class ImageUtil {
*/ */
public static MatVector contours(Mat src, Mat inMat, Boolean debug, String tempPath) { public static MatVector contours(Mat src, Mat inMat, Boolean debug, String tempPath) {
MatVector contours = new MatVector(); MatVector contours = new MatVector();
opencv_imgproc.findContours(inMat, contours, // a vector of contours // 提取外部轮廓
opencv_imgproc.CV_RETR_EXTERNAL, // 提取外部轮廓 opencv_imgproc.findContours(inMat, contours, opencv_imgproc.CV_RETR_EXTERNAL, opencv_imgproc.CV_CHAIN_APPROX_NONE);
opencv_imgproc.CV_CHAIN_APPROX_NONE); // all pixels of each contours
if (debug) { if (debug) {
Mat result = new Mat();
src.copyTo(result); // 复制一张图,不在原图上进行操作,防止后续需要使用原图
// 将轮廓描绘到原图 // 将轮廓描绘到原图
opencv_imgproc.drawContours(src, contours, -1, new Scalar(0, 0, 255, 255)); opencv_imgproc.drawContours(result, contours, -1, new Scalar(0, 0, 255, 255));
// 输出带轮廓的原图 // 输出带轮廓的原图
opencv_imgcodecs.imwrite(tempPath + (debugMap.get("contours") + 100) + "_contours.jpg", src); opencv_imgcodecs.imwrite(tempPath + (debugMap.get("contours") + 100) + "_contours.jpg", result);
} }
return contours; return contours;
} }
/** /**
* *
* @param src * @param src
@ -249,78 +252,94 @@ public class ImageUtil {
public static final int DEFAULT_VERIFY_MIN = 3; public static final int DEFAULT_VERIFY_MIN = 3;
public static final int DEFAULT_VERIFY_MAX = 20; public static final int DEFAULT_VERIFY_MAX = 20;
public static final int DEFAULT_ANGLE = 30; // 角度判断所用常量 public static final int DEFAULT_ANGLE = 30; // 角度判断所用常量
public static final int WIDTH = 136;
public static final int HEIGHT = 36;
public static final int TYPE = opencv_core.CV_8UC3;
@SuppressWarnings("resource")
public static Vector<Mat> screenBlock(Mat src, MatVector contours, Boolean debug, String tempPath){ public static Vector<Mat> screenBlock(Mat src, MatVector contours, Boolean debug, String tempPath){
MatVector rects = new MatVector();
// Vector<RotatedRect> rects = new Vector<RotatedRect>(); Vector<Mat> dst = new Vector<Mat>();
MatVector mv = new MatVector();
for (int i = 0; i < contours.size(); ++i) { for (int i = 0; i < contours.size(); ++i) {
// RotatedRect 该类表示平面上的旋转矩形,有三个属性: 矩形中心点(质心); 边长(长和宽); 旋转角度 // RotatedRect 该类表示平面上的旋转矩形,有三个属性: 矩形中心点(质心); 边长(长和宽); 旋转角度
RotatedRect mr = opencv_imgproc.minAreaRect(contours.get(i)); RotatedRect mr = opencv_imgproc.minAreaRect(contours.get(i));
float angle = Math.abs(mr.angle()); float angle = Math.abs(mr.angle());
if (verifySizes(mr)) { if (verifySizes(mr) && angle <= DEFAULT_ANGLE) { // 判断尺寸及旋转角度 ±30°排除不合法的图块
// rects.add(mr);
// 判断旋转角度 ±30° if (debug) { // 描绘出筛选后的轮廓
if (angle <= DEFAULT_ANGLE) { mv.put(contours.get(i));
rects.put(mr); Mat result = new Mat();
// 旋转角度 src.copyTo(result); // 复制一张图,不在原图上进行操作,防止后续需要使用原图
Mat rotmat = opencv_imgproc.getRotationMatrix2D(mr.center(), angle, 1); // 将轮廓描绘到原图
Mat img_rotated = new Mat(); opencv_imgproc.drawContours(result, mv, -1, new Scalar(0, 0, 255, 255));
opencv_imgproc.warpAffine(src, img_rotated, rotmat, src.size()); // CV_INTER_CUBIC // 输出带轮廓的原图
opencv_imgcodecs.imwrite(tempPath + (debugMap.get("screenblock") + 100) + "_screenblock.jpg", result);
}
// 旋转角度,根据需要是否进行角度旋转
Size rect_size = new Size((int) mr.size().width(), (int) mr.size().height());
if (mr.size().width() / mr.size().height() < 1) { // 宽度小于高度
angle = 90 + angle; // 旋转90°
rect_size = new Size(rect_size.height(), rect_size.width());
} }
Mat rotmat = opencv_imgproc.getRotationMatrix2D(mr.center(), angle, 1);
Mat img_rotated = new Mat();
opencv_imgproc.warpAffine(src, img_rotated, rotmat, src.size()); // CV_INTER_CUBIC
// 切图
Mat img_crop = new Mat();
opencv_imgproc.getRectSubPix(src, rect_size, mr.center(), img_crop);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + (debugMap.get("crop") + 100) + "_crop_" + i + ".jpg", img_crop);
}
// 处理切图,调整为指定大小
Mat resized = new Mat(HEIGHT, WIDTH, TYPE);
opencv_imgproc.resize(img_crop, resized, resized.size(), 0, 0, opencv_imgproc.INTER_CUBIC);
if (debug) {
opencv_imgcodecs.imwrite(tempPath + (debugMap.get("resize") + 100) + "_resize_" + i + ".jpg", resized);
}
dst.add(resized);
} }
} }
if (debug) { return dst;
// 将轮廓描绘到原图
opencv_imgproc.drawContours(src, rects, -1, new Scalar(0, 0, 255, 255));
// 输出带轮廓的原图
opencv_imgcodecs.imwrite(tempPath + (debugMap.get("screenblock") + 100) + "_screenblock.jpg", src);
}
return null;
} }
/** /**
* minAreaRect * minAreaRect
* @param mr * @param mr
* @return * @return
*/ */
private static boolean verifySizes(RotatedRect mr) { private static boolean verifySizes(RotatedRect mr) {
// China car plate size: 440mm*140mmaspect 3.142857 // China car plate size: 440mm*140mmaspect 3.142857
int min = 44 * 14 * DEFAULT_VERIFY_MIN; int min = 44 * 14 * DEFAULT_VERIFY_MIN;
int max = 44 * 14 * DEFAULT_VERIFY_MAX; int max = 44 * 14 * DEFAULT_VERIFY_MAX;
// Get only patchs that match to a respect ratio. // Get only patchs that match to a respect ratio.
float rmin = DEFAULT_ASPECT - DEFAULT_ASPECT * DEFAULT_ERROR; float rmin = DEFAULT_ASPECT - DEFAULT_ASPECT * DEFAULT_ERROR;
float rmax = DEFAULT_ASPECT + DEFAULT_ASPECT * DEFAULT_ERROR; float rmax = DEFAULT_ASPECT + DEFAULT_ASPECT * DEFAULT_ERROR;
// 计算面积 // 计算面积
int area = (int) (mr.size().height() * mr.size().width()); int area = (int) (mr.size().height() * mr.size().width());
// 计算纵横比 // 计算纵横比
float r = mr.size().width() / mr.size().height(); float r = mr.size().width() / mr.size().height();
if (r < 1) { if (r < 1) {
r = mr.size().height() / mr.size().width(); r = mr.size().height() / mr.size().width();
} }
return min <= area && area <= max && rmin <= r && r <= rmax; return min <= area && area <= max && rmin <= r && r <= rmax;
} }
/** /**
* rgbhsv * rgbhsv

Loading…
Cancel
Save