diff --git a/src/main/java/com/hy/java/uct/umlrecog/UMLDiagramRecognizer.java b/src/main/java/com/hy/java/uct/umlrecog/UMLDiagramRecognizer.java index 48d9e14862710bc9c5a568823d7dcce8cda2deea..dfdbf4bc27710983030b4c007e81774f6a790fbc 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/UMLDiagramRecognizer.java +++ b/src/main/java/com/hy/java/uct/umlrecog/UMLDiagramRecognizer.java @@ -33,7 +33,7 @@ public class UMLDiagramRecognizer { * 测试一下识别特定的类图和顺序图 */ public static void main(String[] args) { - UMLDiagramRecognizer.recogCD(cd_dir, "Team-MWSU/GroupProject"); + UMLDiagramRecognizer.recogCD(cd_dir, "Salaboy/smart-tasks"); UMLDiagramRecognizer.recogSD(sd_dir, "albanoj2/grp"); } } diff --git a/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassDetector.java b/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassDetector.java index 551c2fed1d56c01965342172c374e7f9b45d7c82..498a20b21ad6a40c6666e17d0ae4ec89fbbdbed1 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassDetector.java +++ b/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassDetector.java @@ -73,9 +73,7 @@ public class ClassDetector { // 这个rect_contours里只存那些检测结果为矩形的contour,用于后面边框涂白,防止矩形干扰关系符号和关系线识别 List rect_contours = new ArrayList<>(); // 根据图片像素计算轮廓面积阈值。如果轮廓面积太小或太大,则直接忽略 - int cd_width = cls_diagram.width(); - int cd_height = cls_diagram.height(); - long cd_area = cd_width * cd_height; + long cd_area = cls_diagram.width() * cls_diagram.height(); double min_cls_area = cd_area * ratio; double max_cls_area = cd_area * 0.5; for (MatOfPoint contour : contours) { @@ -98,7 +96,7 @@ public class ClassDetector { } } // 对所有矩形边框进行涂白,防止其干扰关系符号和关系线识别 - Imgproc.drawContours(cls_diagram, rect_contours, -1, new Scalar(255, 255, 255), 4); + Imgproc.drawContours(cls_diagram, rect_contours, -1, new Scalar(255, 255, 255), 5); // 此时的cls_diagram中矩形已涂白 return Pair.createPair(cls_diagram, all_rect_areas); } @@ -203,10 +201,14 @@ public class ClassDetector { // 将uc中的每个区域写入临时文件,然后识别临时文件中的文字 Imgcodecs.imwrite(temp_res_path, uc.top.getMat()); uc.setTitle(instance.doOCR(new File(temp_res_path))); - Imgcodecs.imwrite(temp_res_path, uc.mid.getMat()); - uc.setAttrisStr(instance.doOCR(new File(temp_res_path))); - Imgcodecs.imwrite(temp_res_path, uc.bottom.getMat()); - uc.setMethodsStr(instance.doOCR(new File(temp_res_path))); + if (uc.mid != null) { + Imgcodecs.imwrite(temp_res_path, uc.mid.getMat()); + uc.setAttrisStr(instance.doOCR(new File(temp_res_path))); + } + if (uc.bottom != null) { + Imgcodecs.imwrite(temp_res_path, uc.bottom.getMat()); + uc.setMethodsStr(instance.doOCR(new File(temp_res_path))); + } } catch (TesseractException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassRelationDetector.java b/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassRelationDetector.java index e73382a40524640f2fdc38d635d9046bdf3b8b56..3afcfc46a7784fd7464b7cb8f277aabee2e86ee1 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassRelationDetector.java +++ b/src/main/java/com/hy/java/uct/umlrecog/cddetector/ClassRelationDetector.java @@ -11,6 +11,7 @@ import org.opencv.core.Scalar; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; +import com.hy.java.uct.umlrecog.util.Line; import com.hy.java.uct.umlrecog.util.UMLClass; import com.hy.java.utility.common.Pair; @@ -27,10 +28,73 @@ public class ClassRelationDetector { } public void recog() { - // 先检测关系类型检测 - Mat temp = detectRelationType(classes_in_cd, 0.000555); - // 再检测关系线 - // detectLines(temp); + // 先检测实线 + List solid_lines = detectSolidLines(classes_in_cd.getLeft(), 0.0505); + // 再检测关系类型检测 + // Mat temp = detectRelationType(classes_in_cd, 0.00002); + // 最后再检测虚线 + // detectLines(classes_in_cd.getLeft()); + } + + private List detectSolidLines(Mat cls_diagram, double ratio) { + System.out.println("识别" + cd_path + "中的实线"); + List result = new ArrayList<>(); + /* + * 先检测边缘,然后从边缘集中初步检测“原始直线” + */ + // 先检测边缘。然后从边缘集中检测直线 + Mat edges = new Mat(); + Imgproc.Canny(cls_diagram, edges, 50, 50 * 3, 3, true); + // 从边缘集中检测直线。实线检测时,“最小直线长度”与图像像素有关;“最大像素间隔”越小越接近实线 + Mat lines = new Mat(); + Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, Math.min(cls_diagram.width() * ratio, cls_diagram.height() * ratio), 3); + // 保存所有刚检测出来的“原始直线” + for (int i = 0; i < lines.rows(); i++) { + result.add(new Line(new Point(lines.get(i, 0)[0], lines.get(i, 0)[1]), new Point(lines.get(i, 0)[2], lines.get(i, 0)[3]))); + } + /* + * 检查“原始直线”集中是否有重合直线,去除冗余或合并 + */ + int current_size = result.size(); + for (int i = 0; i < current_size; i++) { + Line line = result.get(i); + // 将line与other_line做比较 + for (int j = i + 1; j < current_size; j++) { + Line other_line = result.get(j); + // “原始直线”中存在“两端都接近”的情况,将其算为一条线;或其中一端接近、且两直线斜率接近,则合并两直线 + if (line.near(other_line)) { + other_line.should_be_del = true; + } + } + // 去除冗余;合并同斜率 + for (int j = i + 1; j < current_size;) { + Line other_line = result.get(j); + if (other_line.should_be_del) { + if (other_line.merge_with != null) { + // 合并同斜率 + mergeKNearLines(line, other_line); + } + // 去除冗余 + result.remove(other_line); + current_size = result.size(); + } else { + j++; + } + } + } + for (Line l : result) { + System.out.println("(" + l.pt1.x + ", " + l.pt1.y + ")" + "(" + l.pt2.x + ", " + l.pt2.y + ")"); + Imgproc.line(cls_diagram, l.pt1, l.pt2, new Scalar(55, 55, 55), 1); + } + // Writing the image + Imgcodecs.imwrite(temp_res_path, cls_diagram); + System.out.println("识别实线完毕,共" + result.size() + "条"); + return result; + } + + // 将斜率接近的两条直线合并成“最长”的一条线,并存在line中 + private void mergeKNearLines(Line line, Line other_line) { + // 这TM怎么合并啊?!! } /** @@ -74,33 +138,13 @@ public class ClassRelationDetector { Imgproc.fillConvexPoly(classes_in_cd.getLeft(), contour, new Scalar(15, 225, 25)); } // 对所有边框进行涂白 - Imgproc.drawContours(classes_in_cd.getLeft(), rela_shapes, -1, new Scalar(255, 255, 255), 8); + Imgproc.drawContours(classes_in_cd.getLeft(), rela_shapes, -1, new Scalar(15, 225, 25), 1); // 将图片写入result文件 Imgcodecs.imwrite(temp_res_path, classes_in_cd.getLeft()); System.out.println("识别关系符号完毕,共" + rela_shapes.size() + "个"); return classes_in_cd.getLeft(); } - private void detectLines(Mat src) { - // TODO Auto-generated method stub - System.out.println("识别关系"); - // 先检测边缘。然后从边缘集中检测直线 - Mat canny = new Mat(); - Imgproc.Canny(src, canny, 50, 150, 3, true); - // 从边缘集中检测直线 - Mat lines = new Mat(); - Imgproc.HoughLinesP(canny, lines, 1, Math.PI / 4, 12, 17, 20); - // 将检测到的直线绘制到图中 - for (int i = 0; i < lines.rows(); i++) { - Point pt1 = new Point(lines.get(i, 0)[0], lines.get(i, 0)[1]); - Point pt2 = new Point(lines.get(i, 0)[2], lines.get(i, 0)[3]); - Imgproc.line(src, pt1, pt2, new Scalar(187, 255, 255), 2); - } - // Writing the image - Imgcodecs.imwrite("D:\\eclipse-committers\\uml-code-trace\\src\\test\\resources\\result.png", src); - System.out.println("识别关系完毕,共" + lines.rows() + "条"); - } - public Object getResult() { return result; } diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/Line.java b/src/main/java/com/hy/java/uct/umlrecog/util/Line.java new file mode 100644 index 0000000000000000000000000000000000000000..4224daa128b6f6d2a4de2def2156b3efcde95297 --- /dev/null +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Line.java @@ -0,0 +1,72 @@ +package com.hy.java.uct.umlrecog.util; + +import org.opencv.core.Point; + +public class Line { + public Point pt1; + public Point pt2; + public boolean should_be_del = false; + public Line merge_with = null; + + public Line(Point pt1, Point pt2) { + this.pt1 = pt1; + this.pt2 = pt2; + } + + /** + * 判断一条直线是否与另一条直线重合 + */ + public boolean near(Line line) { + boolean result = false; + // 如果该直线的每个端点都与另一条直线重合,则该直线与另一条直线重合 + if (ptNearLine(pt1, line) && ptNearLine(pt2, line)) { + result = true; + } + // 或者该直线某一个端点与另一条直线重合,且两条直线斜率接近,则也认为两条直线重合 + else { + // 先判断该直线是否存在某一个端点与另一条直线重合 + if (ptNearLine(pt1, line) || ptNearLine(pt2, line)) { + // 再判断是否斜率接近 + if (Line.getK(this) == "无穷大" || Line.getK(line) == "无穷大") { + if (Line.getK(this) == Line.getK(line)) { + line.merge_with = this; + result = true; + } + } else if (Math.abs(Double.parseDouble(getK(this)) - Double.parseDouble(getK(line))) <= 0.5) { + line.merge_with = this; + result = true; + } + } + } + return result; + } + + private static String getK(Line line) { + if (line.pt1.x == line.pt2.x) { + return "无穷大"; + } else { + return String.valueOf((line.pt2.y - line.pt1.y) / (line.pt2.x - line.pt1.x)); + } + } + + private boolean ptNearLine(Point pt, Line line) { + boolean result = false; + // 如果该端点与另一条直线的某一个端点接近,则认为该端点与另一条直线重合 + if (ptNearPt(pt, line.pt1) || ptNearPt(pt, line.pt2)) { + result = true; + } + return result; + } + + private boolean ptNearPt(Point p1, Point p2) { + boolean x_near = false; + boolean y_near = false; + if (Math.abs(p1.x - p2.x) <= 3) { + x_near = true; + } + if (Math.abs(p1.y - p2.y) <= 3) { + y_near = true; + } + return x_near && y_near; + } +} diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index bd9e356c945ea18097229d8317c29bf4ad7b9636..16a319315b960adda0f266dd6af8642488dab69d 100644 Binary files a/src/main/resources/cd/temp result.png and b/src/main/resources/cd/temp result.png differ