From 328275c1afed1fa4d1483b6e2462d9d444aee697 Mon Sep 17 00:00:00 2001 From: chief Date: Tue, 22 Dec 2020 22:58:34 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E5=88=A9=E7=94=A8UML=5Fclasses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cddetector/ClassRelationDetector.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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 ca07a20..3db26ba 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 @@ -36,15 +36,17 @@ public class ClassRelationDetector { public void recog() { // 先检测实线。检测结果需借助类的位置关系、去除“单纯的线” - Set solid_lines = detectLines(classes_in_cd.getLeft(), 0.0504, true); + Set solid_lines = detectLines(classes_in_cd, 0.0504, true); // 再检测虚线。虚线检测除了间隔设置与实线不同外,其他完全一样 - Set dash_lines = detectLines(classes_in_cd.getLeft(), 0.0504, false); + Set dash_lines = detectLines(classes_in_cd, 0.0504, false); // 再检测关系类型检测 // List relation_types = detectRelationType(classes_in_cd, 0.0001); } - private Set detectLines(Mat cls_diagram, double ratio, boolean detect_solid) { + private Set detectLines(Pair> classes_in_cd, double ratio, boolean detect_solid) { + Mat cls_diagram = classes_in_cd.getLeft(); + List UML_classes = classes_in_cd.getRight(); if (detect_solid) { System.out.println("开始识别" + cd_path + "中的实线"); } else { @@ -108,8 +110,7 @@ public class ClassRelationDetector { for (int j = i + 1; j < current_size; j++) { Line other_line = line_segments.get(j); if (Line.Knear(line, other_line)) { - if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) - || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { + if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { other_line.should_be_del = true; } } @@ -132,6 +133,13 @@ public class ClassRelationDetector { * 对所有实线,将可能的“折线”识别并合并。合并为折线的直线,其belonged_polygonal_line会标识其所属折线 */ result = mergePolygonalLines(line_segments); + /* + * 利用UML_classes的位置,进一步筛选关系线 + */ + + /* + * 在图中抹掉关系线,防止其影响后续识别 + */ if (detect_solid) { // 实线识别结果 for (PolygonalLine pl : result) { -- Gitee From 16f4a5907004f259b92532b9fa1e7ba924bba3bd Mon Sep 17 00:00:00 2001 From: origin Date: Wed, 23 Dec 2020 12:17:14 +0800 Subject: [PATCH 2/7] =?UTF-8?q?UMLClass=E9=9C=80=E8=A6=81=E6=9C=89?= =?UTF-8?q?=E2=80=9C=E8=8E=B7=E5=BE=97=E5=9B=9B=E6=9D=A1=E8=BE=B9=E6=A1=86?= =?UTF-8?q?=E2=80=9D=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../uct/umlrecog/cddetector/ClassRelationDetector.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 3db26ba..547d651 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 @@ -110,7 +110,8 @@ public class ClassRelationDetector { for (int j = i + 1; j < current_size; j++) { Line other_line = line_segments.get(j); if (Line.Knear(line, other_line)) { - if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { + if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) + || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { other_line.should_be_del = true; } } @@ -135,8 +136,10 @@ public class ClassRelationDetector { result = mergePolygonalLines(line_segments); /* * 利用UML_classes的位置,进一步筛选关系线 + * + * 针对每条候选关系线,计算每个类方框的4根边框线,看哪个边框线与当前候选关系线相交(允许一定像素的误差),则认为这条线确实是关系线,且属于相交的类 */ - + /* * 在图中抹掉关系线,防止其影响后续识别 */ -- Gitee From f22c9a65a0f73eb40b03a17e6458cb2a5f710037 Mon Sep 17 00:00:00 2001 From: chief Date: Thu, 24 Dec 2020 13:15:22 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=BE=97=E5=88=B0=E4=BA=86=E7=B1=BB?= =?UTF-8?q?=E6=96=B9=E6=A1=86=E7=9A=844=E6=A0=B9=E8=BE=B9=E6=A1=86?= =?UTF-8?q?=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umlrecog/cddetector/ClassDetector.java | 18 +++++++++++++++++ .../cddetector/ClassRelationDetector.java | 19 +++++++++++++++--- .../hy/java/uct/umlrecog/util/Rectangle.java | 20 ++++++++++++++++++- .../hy/java/uct/umlrecog/util/UMLClass.java | 7 +++++++ 4 files changed, 60 insertions(+), 4 deletions(-) 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 8615e35..1628ed7 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 @@ -185,6 +185,24 @@ public class ClassDetector { } } } + // 计算类框整体大小。 + int whole_x = 0; + int whole_y = 0; + int whole_width = 0; + int whole_height = 0; + if (uml_class.top != null) { + whole_x = uml_class.top.x; + whole_y = uml_class.top.y; + whole_width = uml_class.top.width; + whole_height += uml_class.top.height; + } + if (uml_class.mid != null) { + whole_height += uml_class.mid.height; + } + if (uml_class.bottom != null) { + whole_height += uml_class.bottom.height; + } + uml_class.whole = new Rectangle(null, null, whole_x, whole_y, whole_width, whole_height); // 针对当前矩形,与所有其他矩形对比完毕后,则完成了该类的识别 result.add(uml_class); } 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 547d651..686a2c5 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 @@ -110,8 +110,7 @@ public class ClassRelationDetector { for (int j = i + 1; j < current_size; j++) { Line other_line = line_segments.get(j); if (Line.Knear(line, other_line)) { - if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) - || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { + if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { other_line.should_be_del = true; } } @@ -139,7 +138,21 @@ public class ClassRelationDetector { * * 针对每条候选关系线,计算每个类方框的4根边框线,看哪个边框线与当前候选关系线相交(允许一定像素的误差),则认为这条线确实是关系线,且属于相交的类 */ - + for (PolygonalLine pl : result) { + for (UMLClass uml_class : UML_classes) { + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + // 这就是类方框的4根边框线,用吧~ + uml_class.whole.shapeLines(); + } + } /* * 在图中抹掉关系线,防止其影响后续识别 */ diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/Rectangle.java b/src/main/java/com/hy/java/uct/umlrecog/util/Rectangle.java index fd57663..f374ad6 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/Rectangle.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Rectangle.java @@ -1,5 +1,8 @@ package com.hy.java.uct.umlrecog.util; +import java.util.ArrayList; +import java.util.List; + import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.MatOfPoint2f; @@ -17,7 +20,7 @@ public class Rectangle extends Rect { this.contour = contour; } - private Rectangle(Mat mat, MatOfPoint contour, int x, int y, int width, int height) { + public Rectangle(Mat mat, MatOfPoint contour, int x, int y, int width, int height) { super(x, y, width, height); this.mat = mat; this.contour = contour; @@ -38,4 +41,19 @@ public class Rectangle extends Rect { public Rectangle clone() { return new Rectangle(mat, contour, this.x, this.y, this.width, this.height); } + + /** + * 获取类框四条直线,从顶部开始顺时针 + * + * @return 四条直线的list,上、右、下、左 + */ + public List shapeLines() { + List result = new ArrayList<>(); + result.add(new Line(new Point(x, y), new Point(x + width, y))); + result.add(new Line(new Point(x + width, y), new Point(x + width, y + height))); + result.add(new Line(new Point(x + width, y + height), new Point(x, y + height))); + result.add(new Line(new Point(x, y + height), new Point(x, y))); + return result; + } + } diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java b/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java index 5b2e173..1013e79 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java @@ -4,11 +4,18 @@ import java.util.ArrayList; import java.util.List; public class UMLClass { + /** + * 初始检测时用。后续不再使用 + */ public List list = new ArrayList<>(); public Rectangle temp_rect = null; + /** + * 后续需要使用的信息 + */ public Rectangle top = null; public Rectangle mid = null; public Rectangle bottom = null; + public Rectangle whole = null; private String title; private String attris_str; private String methods_str; -- Gitee From e31e985496da364469f6beeb1c7f452373a7f2b2 Mon Sep 17 00:00:00 2001 From: chief Date: Thu, 24 Dec 2020 22:20:18 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Relation=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E5=AD=98=E5=82=A8=E5=85=B3=E7=B3=BB=E3=80=82=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E5=B0=86=E6=8A=98=E7=BA=BF=E4=B8=8E=E7=B1=BB=E5=81=9A?= =?UTF-8?q?=E4=BA=86=E5=AF=B9=E6=AF=94=EF=BC=8C=E8=BF=98=E6=9C=AA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cddetector/ClassRelationDetector.java | 60 ++++++++++--------- .../com/hy/java/uct/umlrecog/util/Line.java | 7 ++- .../java/uct/umlrecog/util/PolygonalLine.java | 3 +- .../hy/java/uct/umlrecog/util/Relation.java | 11 ++++ .../hy/java/uct/umlrecog/util/UMLClass.java | 1 + 5 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/hy/java/uct/umlrecog/util/Relation.java 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 686a2c5..c9b1c8f 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 @@ -18,6 +18,7 @@ import org.opencv.imgproc.Imgproc; import com.hy.java.uct.umlrecog.util.Line; import com.hy.java.uct.umlrecog.util.PolygonalLine; import com.hy.java.uct.umlrecog.util.Rectangle; +import com.hy.java.uct.umlrecog.util.Relation; import com.hy.java.uct.umlrecog.util.RelationType; import com.hy.java.uct.umlrecog.util.UMLClass; import com.hy.java.utility.common.Pair; @@ -36,27 +37,27 @@ public class ClassRelationDetector { public void recog() { // 先检测实线。检测结果需借助类的位置关系、去除“单纯的线” - Set solid_lines = detectLines(classes_in_cd, 0.0504, true); + Set solid_lines = detectLines(classes_in_cd, 0.0504, true); // 再检测虚线。虚线检测除了间隔设置与实线不同外,其他完全一样 - Set dash_lines = detectLines(classes_in_cd, 0.0504, false); + Set dash_lines = detectLines(classes_in_cd, 0.0504, false); // 再检测关系类型检测 // List relation_types = detectRelationType(classes_in_cd, 0.0001); } - private Set detectLines(Pair> classes_in_cd, double ratio, boolean detect_solid) { - Mat cls_diagram = classes_in_cd.getLeft(); - List UML_classes = classes_in_cd.getRight(); + private Set detectLines(Pair> classes_in_cd, double ratio, boolean detect_solid) { + Set result = new HashSet<>(); if (detect_solid) { System.out.println("开始识别" + cd_path + "中的实线"); } else { System.out.println("开始识别" + cd_path + "中的虚线"); } - Set result = null; - List line_segments = new ArrayList<>(); + Mat cls_diagram = classes_in_cd.getLeft(); + List UML_classes = classes_in_cd.getRight(); /* * 先检测边缘,然后从边缘集中初步检测“原始直线” */ + List line_segments = new ArrayList<>(); // 先检测边缘。然后从边缘集中检测直线 Mat edges = new Mat(); Imgproc.Canny(cls_diagram, edges, 50, 50 * 3, 3, true); @@ -132,25 +133,26 @@ public class ClassRelationDetector { /* * 对所有实线,将可能的“折线”识别并合并。合并为折线的直线,其belonged_polygonal_line会标识其所属折线 */ - result = mergePolygonalLines(line_segments); + Set poly_lines = mergePolygonalLines(line_segments); /* - * 利用UML_classes的位置,进一步筛选关系线 + * 利用UML_classes的位置,进一步筛选关系线,存在result中 * * 针对每条候选关系线,计算每个类方框的4根边框线,看哪个边框线与当前候选关系线相交(允许一定像素的误差),则认为这条线确实是关系线,且属于相交的类 */ - for (PolygonalLine pl : result) { + for (PolygonalLine pl : poly_lines) { + Relation rela = new Relation(pl); for (UMLClass uml_class : UML_classes) { - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - // 这就是类方框的4根边框线,用吧~ - uml_class.whole.shapeLines(); + // 暂时将与pt1相近的类记为source、与pt2相近的类为target,后面识别出符号来后再调 + List class_shape_lines = uml_class.whole.shapeLines(); + if (Line.ptInLine(pl.pt1, class_shape_lines.get(0)) || Line.ptInLine(pl.pt1, class_shape_lines.get(1)) || Line.ptInLine(pl.pt1, class_shape_lines.get(2)) || Line.ptInLine(pl.pt1, class_shape_lines.get(3))) { + rela.source = uml_class; + } + if (Line.ptInLine(pl.pt2, class_shape_lines.get(0)) || Line.ptInLine(pl.pt2, class_shape_lines.get(1)) || Line.ptInLine(pl.pt2, class_shape_lines.get(2)) || Line.ptInLine(pl.pt2, class_shape_lines.get(3))) { + rela.target = uml_class; + } + } + if (rela.source != null && rela.target != null) { + result.add(rela); } } /* @@ -158,27 +160,27 @@ public class ClassRelationDetector { */ if (detect_solid) { // 实线识别结果 - for (PolygonalLine pl : result) { - System.out.println("(" + pl.pt1.x + ", " + pl.pt1.y + ")" + "(" + pl.pt2.x + ", " + pl.pt2.y + ")"); + for (Relation rela : result) { + System.out.println("(" + rela.poly_line.pt1.x + ", " + rela.poly_line.pt1.y + ")" + "(" + rela.poly_line.pt2.x + ", " + rela.poly_line.pt2.y + ")"); // 在图中抹掉已识别的线,防止干扰后续识别 - for (Line segment : pl.line_list) { + for (Line segment : rela.poly_line.line_list) { Imgproc.line(cls_diagram, segment.pt1, segment.pt2, new Scalar(255, 255, 255), 5); } // 看一眼。看结果相当于把抹掉的线再画出来,会影响后续识别,所以看完就注释掉 - // Imgproc.line(cls_diagram, pl.pt1, pl.pt2, new Scalar(55, 55, 55), 5); + // Imgproc.line(cls_diagram, rela.poly_line.pt1, rela.poly_line.pt2, new Scalar(55, 55, 55), 5); } Imgcodecs.imwrite(temp_res_path, cls_diagram); System.out.println("完成对" + cd_path + "中的实线识别,共" + result.size() + "条"); } else { // 虚线识别结果 - for (PolygonalLine pl : result) { - System.out.println("(" + pl.pt1.x + ", " + pl.pt1.y + ")" + "(" + pl.pt2.x + ", " + pl.pt2.y + ")"); + for (Relation rela : result) { + System.out.println("(" + rela.poly_line.pt1.x + ", " + rela.poly_line.pt1.y + ")" + "(" + rela.poly_line.pt2.x + ", " + rela.poly_line.pt2.y + ")"); // 在图中抹掉已识别的线,防止干扰后续识别 - for (Line segment : pl.line_list) { + for (Line segment : rela.poly_line.line_list) { Imgproc.line(cls_diagram, segment.pt1, segment.pt2, new Scalar(255, 255, 255), 5); } // 看一眼。看结果相当于把抹掉的线再画出来,会影响后续识别,所以看完就注释掉 - Imgproc.line(cls_diagram, pl.pt1, pl.pt2, new Scalar(55, 55, 55), 5); + Imgproc.line(cls_diagram, rela.poly_line.pt1, rela.poly_line.pt2, new Scalar(55, 55, 55), 5); } Imgcodecs.imwrite(temp_res_path, cls_diagram); System.out.println("完成对" + cd_path + "中的虚线识别,共" + result.size() + "条"); 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 index cc3e431..17c440c 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/Line.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Line.java @@ -71,6 +71,8 @@ public class Line { /** * 判断某一点是否与另一条直线重合。即“是否与另一条直线的某一端点接近”、“是否在另一条直线内”。记录接近的直线端点 + * + * 在识别直线、去除冗余时使用,其他时候不要用 */ public static Pair ptNearLine(Point pt, Line line) { Pair result = new Pair<>(false, null); @@ -130,8 +132,11 @@ public class Line { /** * 判断点是否在直线内。计算点到直线距离,与阈值比较 + * + * @param pt 点 + * @param line 直线 */ - private static boolean ptInLine(Point pt, Line line) { + public static boolean ptInLine(Point pt, Line line) { double threshold = 2.0; double a = Math.abs(line.equation.a * pt.x + line.equation.b * pt.y + line.equation.c); double b = Math.sqrt(Math.pow(line.equation.a, 2) + Math.pow(line.equation.b, 2)); diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java b/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java index 1b2d5c5..54afb82 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java @@ -7,9 +7,10 @@ import org.opencv.core.Point; /** * “将两条线合并为一根折线”时的结果。包括该折线的“两端”、“线段” + * + * 最终作为Relation的一个属性 */ public class PolygonalLine { - public Point pt1; public Point pt2; public List line_list = new ArrayList<>(); diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java b/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java new file mode 100644 index 0000000..a5a5a06 --- /dev/null +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java @@ -0,0 +1,11 @@ +package com.hy.java.uct.umlrecog.util; + +public class Relation { + public PolygonalLine poly_line; + public UMLClass source; + public UMLClass target; + + public Relation(PolygonalLine poly_line) { + this.poly_line = poly_line; + } +} diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java b/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java index 1013e79..bbbd137 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java @@ -19,6 +19,7 @@ public class UMLClass { private String title; private String attris_str; private String methods_str; + public List relas = new ArrayList<>(); public String getTitle() { return title; -- Gitee From dfcbd4b9fe66e6976431dfc92ab438c43bbe1e2a Mon Sep 17 00:00:00 2001 From: origin Date: Fri, 25 Dec 2020 12:00:51 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E5=AF=B9pl.pt1=E5=92=8Cpl.pt2=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E5=B0=86=E5=85=B6=E5=BB=B6=E7=9B=B4=E7=BA=BF?= =?UTF-8?q?=E6=96=B9=E5=90=91=E5=BB=B6=E9=95=BF=E4=B8=80=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cddetector/ClassRelationDetector.java | 16 +++++++++++++--- .../java/com/hy/java/uct/umlrecog/util/Line.java | 13 +++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) 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 c9b1c8f..e090231 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 @@ -111,7 +111,8 @@ public class ClassRelationDetector { for (int j = i + 1; j < current_size; j++) { Line other_line = line_segments.get(j); if (Line.Knear(line, other_line)) { - if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { + if (Line.ptNearPt(line.pt1, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt1, other_line.pt2, false, true, other_line) + || Line.ptNearPt(line.pt2, other_line.pt1, false, true, other_line) || Line.ptNearPt(line.pt2, other_line.pt2, false, true, other_line)) { other_line.should_be_del = true; } } @@ -144,10 +145,19 @@ public class ClassRelationDetector { for (UMLClass uml_class : UML_classes) { // 暂时将与pt1相近的类记为source、与pt2相近的类为target,后面识别出符号来后再调 List class_shape_lines = uml_class.whole.shapeLines(); - if (Line.ptInLine(pl.pt1, class_shape_lines.get(0)) || Line.ptInLine(pl.pt1, class_shape_lines.get(1)) || Line.ptInLine(pl.pt1, class_shape_lines.get(2)) || Line.ptInLine(pl.pt1, class_shape_lines.get(3))) { + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 + + if (Line.ptInLine(pl.pt1, class_shape_lines.get(0), true) || Line.ptInLine(pl.pt1, class_shape_lines.get(1), true) || Line.ptInLine(pl.pt1, class_shape_lines.get(2), true) + || Line.ptInLine(pl.pt1, class_shape_lines.get(3), true)) { rela.source = uml_class; } - if (Line.ptInLine(pl.pt2, class_shape_lines.get(0)) || Line.ptInLine(pl.pt2, class_shape_lines.get(1)) || Line.ptInLine(pl.pt2, class_shape_lines.get(2)) || Line.ptInLine(pl.pt2, class_shape_lines.get(3))) { + if (Line.ptInLine(pl.pt2, class_shape_lines.get(0), true) || Line.ptInLine(pl.pt2, class_shape_lines.get(1), true) || Line.ptInLine(pl.pt2, class_shape_lines.get(2), true) + || Line.ptInLine(pl.pt2, class_shape_lines.get(3), true)) { rela.target = uml_class; } } 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 index 17c440c..378bc50 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/Line.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Line.java @@ -88,7 +88,7 @@ public class Line { return result; } // “是否在另一条直线内” - if (Line.ptInLine(pt, line)) { + if (Line.ptInLine(pt, line, false)) { result.setLeft(true); if (Line.ptNearPt(pt, line.pt1, true, false, line)) { result.setRight(line.pt1); @@ -131,13 +131,18 @@ public class Line { } /** - * 判断点是否在直线内。计算点到直线距离,与阈值比较 + * 判断点是否在直线内。计算点到直线距离,与阈值比较。如果是关系线与元素对比,则阈值可以大一点 * * @param pt 点 * @param line 直线 */ - public static boolean ptInLine(Point pt, Line line) { - double threshold = 2.0; + public static boolean ptInLine(Point pt, Line line, boolean rela_ele) { + double threshold; + if (rela_ele) { + threshold = 12.0; + } else { + threshold = 2.0; + } double a = Math.abs(line.equation.a * pt.x + line.equation.b * pt.y + line.equation.c); double b = Math.sqrt(Math.pow(line.equation.a, 2) + Math.pow(line.equation.b, 2)); return a / b <= threshold; -- Gitee From c100ceed5f4381dc7fc461babef55b873d4ff274 Mon Sep 17 00:00:00 2001 From: origin Date: Mon, 28 Dec 2020 23:00:39 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=B1=BB=E6=A1=86?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E3=80=82=E5=AE=8C=E5=96=84=E5=85=B3=E7=B3=BB?= =?UTF-8?q?=E7=BA=BF=E8=AF=86=E5=88=AB=E3=80=82=E7=9B=AE=E5=89=8D=E5=87=86?= =?UTF-8?q?=E7=A1=AE=E7=8E=87=E8=BE=BE100%=E3=80=82=E6=8E=A5=E4=B8=8B?= =?UTF-8?q?=E6=9D=A5=E8=BF=98=E5=89=A9=E5=85=B3=E7=B3=BB=E7=AC=A6=E5=8F=B7?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E3=80=82=E9=92=88=E5=AF=B9=E5=8C=85=E8=A3=B9?= =?UTF-8?q?=E5=85=B3=E7=B3=BB=E7=AC=A6=E5=8F=B7=E7=9A=84=E7=9F=A9=E5=BD=A2?= =?UTF-8?q?=EF=BC=8C=E6=A3=80=E6=B5=8B=E5=85=B6=E4=B8=8E=E6=AF=8F=E6=9D=A1?= =?UTF-8?q?=E7=BA=BF=E7=AB=AF=E7=82=B9=E7=9A=84=E4=BD=8D=E7=BD=AE=E5=85=B3?= =?UTF-8?q?=E7=B3=BB=EF=BC=8C=E6=9B=B4=E6=96=B0=E5=85=B3=E7=B3=BB=E7=BA=BF?= =?UTF-8?q?=E7=9A=84source=E5=92=8Ctarget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umlrecog/cddetector/ClassDetector.java | 62 +++++++--- .../cddetector/ClassRelationDetector.java | 107 ++++++++++++------ .../com/hy/java/uct/umlrecog/util/Line.java | 29 +++-- .../java/uct/umlrecog/util/PolygonalLine.java | 2 + src/main/resources/cd/temp result.png | Bin 6377 -> 5961 bytes .../com/hy/java/uct/umlrecog/OtherTests.java | 11 ++ 6 files changed, 152 insertions(+), 59 deletions(-) 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 1628ed7..ae649ed 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 @@ -8,6 +8,7 @@ import java.util.List; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.MatOfPoint2f; +import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; @@ -49,7 +50,7 @@ public class ClassDetector { // 对矩形进行整合,形成类区域 List classes = mergeIntoClass(all_rect_areas_in_cd); // 对类区域进行文字检测 - result = Pair.createPair(all_rect_areas_in_cd.getLeft(), detectText(classes)); + result = Pair.createPair(all_rect_areas_in_cd.getLeft(), detectText(Imgcodecs.imread(cd_path, Imgcodecs.IMREAD_GRAYSCALE), classes)); } /** @@ -74,7 +75,7 @@ public class ClassDetector { List rect_contours = new ArrayList<>(); // 根据图片像素计算轮廓面积阈值。如果轮廓面积太小或太大,则直接忽略 long cd_area = cls_diagram.width() * cls_diagram.height(); - double min_cls_area = cd_area * ratio; + double min_cls_area = 60; double max_cls_area = cd_area * 0.5; for (MatOfPoint contour : contours) { // 如果轮廓面积太小或太大,则直接忽略 @@ -103,6 +104,7 @@ public class ClassDetector { private List mergeIntoClass(Pair> all_rect_areas_in_cd) { List result = new ArrayList<>(); + Mat cls_diagram = all_rect_areas_in_cd.getLeft(); // 获取所有矩形 List rect_area_list = all_rect_areas_in_cd.getRight(); /* @@ -185,24 +187,38 @@ public class ClassDetector { } } } - // 计算类框整体大小。 - int whole_x = 0; - int whole_y = 0; - int whole_width = 0; - int whole_height = 0; - if (uml_class.top != null) { - whole_x = uml_class.top.x; - whole_y = uml_class.top.y; - whole_width = uml_class.top.width; - whole_height += uml_class.top.height; + /* + * 此时会出现top其实是整个矩形的情况。需将top、mid、bottom重新分割 + */ + if (uml_class.mid != null) { + if (uml_class.bottom != null) { + if (uml_class.top.y + uml_class.top.height >= uml_class.bottom.y + uml_class.bottom.height) { + int top_old_height = uml_class.top.height; + uml_class.top = uml_class.mid.clone(); + uml_class.mid = uml_class.bottom.clone(); + uml_class.bottom.y = uml_class.top.y + uml_class.top.height + uml_class.mid.height; + uml_class.bottom.height = top_old_height - uml_class.top.height - uml_class.mid.height; + } + } else { + if (uml_class.top.y + uml_class.top.height >= uml_class.mid.y + uml_class.mid.height) { + int top_old_height = uml_class.top.height; + uml_class.top = uml_class.mid.clone(); + uml_class.mid.y = uml_class.top.y + uml_class.top.height; + uml_class.mid.height = top_old_height - uml_class.top.height; + } + } } + // 计算类框整体大小 + int whole_height = 0; + // 先将whole的高度设置为top的高度 + whole_height += uml_class.top.height; if (uml_class.mid != null) { whole_height += uml_class.mid.height; } if (uml_class.bottom != null) { whole_height += uml_class.bottom.height; } - uml_class.whole = new Rectangle(null, null, whole_x, whole_y, whole_width, whole_height); + uml_class.whole = new Rectangle(null, null, uml_class.top.x, uml_class.top.y, uml_class.top.width, whole_height); // 针对当前矩形,与所有其他矩形对比完毕后,则完成了该类的识别 result.add(uml_class); } @@ -210,21 +226,23 @@ public class ClassDetector { return result; } - private List detectText(List classes) { + private List detectText(Mat cls_diagram, List classes) { System.out.println("开始识别" + cd_path + "中每个类的文字"); for (UMLClass uc : classes) { ITesseract instance = new Tesseract(); instance.setDatapath(UMLDiagramRecognizer.tessdata_path); try { // 将uc中的每个区域写入临时文件,然后识别临时文件中的文字 - Imgcodecs.imwrite(temp_res_path, uc.top.getMat()); - uc.setTitle(instance.doOCR(new File(temp_res_path))); + if (uc.top != null) { + Imgcodecs.imwrite(temp_res_path, cutImage(cls_diagram, uc.top)); + uc.setTitle(instance.doOCR(new File(temp_res_path))); + } if (uc.mid != null) { - Imgcodecs.imwrite(temp_res_path, uc.mid.getMat()); + Imgcodecs.imwrite(temp_res_path, cutImage(cls_diagram, uc.mid)); uc.setAttrisStr(instance.doOCR(new File(temp_res_path))); } if (uc.bottom != null) { - Imgcodecs.imwrite(temp_res_path, uc.bottom.getMat()); + Imgcodecs.imwrite(temp_res_path, cutImage(cls_diagram, uc.bottom)); uc.setMethodsStr(instance.doOCR(new File(temp_res_path))); } } catch (TesseractException e) { @@ -236,6 +254,14 @@ public class ClassDetector { return classes; } + public Mat cutImage(Mat src, Rect rect) { + Mat result = new Mat(); + // 图片裁剪 + Mat src_with_roi = new Mat(src, rect); + src_with_roi.copyTo(result); + return result; + } + public Pair> getResult() { return result; } 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 e090231..33becd6 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 @@ -6,10 +6,12 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.MatOfPoint2f; import org.opencv.core.Point; +import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.imgcodecs.Imgcodecs; @@ -41,7 +43,7 @@ public class ClassRelationDetector { // 再检测虚线。虚线检测除了间隔设置与实线不同外,其他完全一样 Set dash_lines = detectLines(classes_in_cd, 0.0504, false); // 再检测关系类型检测 - // List relation_types = detectRelationType(classes_in_cd, 0.0001); + List relation_types = detectRelationType(classes_in_cd, solid_lines, dash_lines, 0.0001); } @@ -69,7 +71,7 @@ public class ClassRelationDetector { // 实线检测时,“最小直线长度”与图像像素有关;“最大像素间隔”越小越接近实线 Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 8, 29, 2); } else { - Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, Math.min(cls_diagram.width() * ratio, cls_diagram.height() * ratio), 10); + Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 8, 29, 10); } // 保存所有刚检测出来的“原始直线” for (int i = 0; i < lines.rows(); i++) { @@ -142,22 +144,16 @@ public class ClassRelationDetector { */ for (PolygonalLine pl : poly_lines) { Relation rela = new Relation(pl); + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点。 + Point pt1 = reachPt(pl.pt1, pl.l1, 35); + Point pt2 = reachPt(pl.pt2, pl.l2, 35); for (UMLClass uml_class : UML_classes) { // 暂时将与pt1相近的类记为source、与pt2相近的类为target,后面识别出符号来后再调 - List class_shape_lines = uml_class.whole.shapeLines(); - // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 - // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 - // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 - // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 - // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 - // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点 - - if (Line.ptInLine(pl.pt1, class_shape_lines.get(0), true) || Line.ptInLine(pl.pt1, class_shape_lines.get(1), true) || Line.ptInLine(pl.pt1, class_shape_lines.get(2), true) - || Line.ptInLine(pl.pt1, class_shape_lines.get(3), true)) { + // 检查延长后的端点 + if (uml_class.whole.contains(pt1)) { rela.source = uml_class; } - if (Line.ptInLine(pl.pt2, class_shape_lines.get(0), true) || Line.ptInLine(pl.pt2, class_shape_lines.get(1), true) || Line.ptInLine(pl.pt2, class_shape_lines.get(2), true) - || Line.ptInLine(pl.pt2, class_shape_lines.get(3), true)) { + if (uml_class.whole.contains(pt2)) { rela.target = uml_class; } } @@ -190,7 +186,7 @@ public class ClassRelationDetector { Imgproc.line(cls_diagram, segment.pt1, segment.pt2, new Scalar(255, 255, 255), 5); } // 看一眼。看结果相当于把抹掉的线再画出来,会影响后续识别,所以看完就注释掉 - Imgproc.line(cls_diagram, rela.poly_line.pt1, rela.poly_line.pt2, new Scalar(55, 55, 55), 5); + // Imgproc.line(cls_diagram, rela.poly_line.pt1, rela.poly_line.pt2, new Scalar(55, 55, 55), 5); } Imgcodecs.imwrite(temp_res_path, cls_diagram); System.out.println("完成对" + cd_path + "中的虚线识别,共" + result.size() + "条"); @@ -305,7 +301,7 @@ public class ClassRelationDetector { } } } - // 设置每条折线的两端 + // 设置每条折线的两端。同时记录两端的点所在的线段 for (PolygonalLine pl : result) { for (Line l : pl.line_list) { Point pt = l.notInPolyPt(); @@ -314,13 +310,17 @@ public class ClassRelationDetector { if (pt.x == 23579 && pt.y == 59873572) { pl.pt1 = l.pt1; pl.pt2 = l.pt2; + pl.l1 = l; + pl.l2 = l; // 无需再检查(也没得可检查,因为pl.line_list中只有这一根线) break; } else { if (pl.pt1 == null) { pl.pt1 = pt; + pl.l1 = l; } else if (pl.pt2 == null) { pl.pt2 = pt; + pl.l2 = l; } } } @@ -329,13 +329,48 @@ public class ClassRelationDetector { return result; } + private Point reachPt(Point pt, Line line, double l) { + Point result = pt.clone(); + String k_str = Line.getK(line); + // 如果折线端点所在线段的斜率是无穷大,则只延长坐标y即可 + if (k_str == "无穷大") { + // 根据坐标y的方向进行延长 + if (line.otherPt(pt).y - pt.y <= 0) { + // 延长 + result.y += l; + } else { + // 延长 + result.y -= l; + } + } + // 否则,则需延直线斜率方向延长 + else { + double k = Double.parseDouble(k_str); + double b = -line.equation.c / line.equation.b; + // 根据坐标x的方向进行延长 + if (line.otherPt(pt).x - pt.x <= 0) { + // 延长 + result.x += l * Math.cos(Math.atan(Math.abs(k))); + result.y = k * result.x + b; + } else { + // 延长 + result.x -= l * Math.cos(Math.atan(Math.abs(k))); + result.y = k * result.x + b; + } + } + return result; + } + /** - * 识别关系符号 + * 识别关系符号,并根据关系符号与关系线端点的位置关系更新关系线的source和target * * @param classes_in_cd + * @param dash_lines + * @param solid_lines + * @param dash_lines * @return */ - private List detectRelationType(Pair> classes_in_cd, double ratio) { + private List detectRelationType(Pair> classes_in_cd, Set solid_lines, Set dash_lines, double ratio) { System.out.println("开始识别" + cd_path + "中所有关系符号"); List result = new ArrayList<>(); Mat cls_diagram = classes_in_cd.getLeft(); @@ -346,7 +381,7 @@ public class ClassRelationDetector { Imgproc.erode(cls_diagram, cls_diagram, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3.5, 3.5)), new Point(-1, -1), 4); Imgproc.dilate(cls_diagram, cls_diagram, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1.7, 1.7)), new Point(-1, -1), 5); List contours = new ArrayList<>(); - Imgproc.findContours(classes_in_cd.getLeft(), contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); + Imgproc.findContours(cls_diagram, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); /* * 对每个轮廓contour,检测是否为某种关系符号,并将contour和检测结果存在Rectangle中。所有轮廓的检测结果最终存在rela_shapes中 */ @@ -354,34 +389,40 @@ public class ClassRelationDetector { long cd_area = cls_diagram.width() * cls_diagram.height(); double min_area = cd_area * ratio; double max_area = cd_area * 0.5; - List rela_shapes = new ArrayList<>(); for (MatOfPoint contour : contours) { // 如果轮廓面积太小或太大,则直接忽略 double contour_area = Imgproc.contourArea(contour); if (contour_area < min_area || contour_area > max_area) { // 将噪点直接涂白 - Imgproc.fillConvexPoly(classes_in_cd.getLeft(), contour, new Scalar(255, 255, 255)); + Imgproc.fillConvexPoly(cls_diagram, contour, new Scalar(255, 255, 255)); continue; } - // 如果轮廓面积合适,则检测是否为关系符号形状。采用多边形逼近法 + // 如果轮廓面积合适,则用矩形将其包裹,并检测矩形与关系端点的位置关系 MatOfPoint2f curve = new MatOfPoint2f(contour.toArray()); MatOfPoint2f approx_curve = new MatOfPoint2f(); Imgproc.approxPolyDP(curve, approx_curve, 0.01 * Imgproc.arcLength(curve, true), true); - // 将每个关系符号存在别处。存完后将其从图中抹掉,防止其干扰直线识别 - RelationType relation_type = new RelationType(); + Rect rect = Imgproc.boundingRect(new MatOfPoint(approx_curve.toArray())); + // 此时可以看一眼 + // Imgproc.rectangle(cls_diagram, rect, new Scalar(5, 5, 5), 2); + // Imgcodecs.imwrite(temp_res_path, cls_diagram); + // 针对包裹矩形,检测其与每条线端点的位置关系 /* - * 目前不可能从approx_curve中找到关系符号形状。只能先确定关系符号位置、大小。或许可以根据关系符号大小,画一个圆。 + * 写!!!!!!!!!!!!!!!!!!! * + * + * + */ + for (Relation rela : solid_lines) { + if (rect.contains(reachPt(rela.poly_line.pt1, rela.poly_line.l1, 5))) { + + } + // 将每个关系符号存在别处。存完后将其从图中抹掉,防止其干扰后续识别 + RelationType relation_type = new RelationType(); + } + /* + * 傲娇 */ - rela_shapes.add(contour); - System.out.println(contour.dump()); - // 将关系符号从图中抹掉(涂白)。后面还需对所有边框进行涂白 - Imgproc.fillConvexPoly(classes_in_cd.getLeft(), contour, new Scalar(55, 55, 55)); } - // 对所有边框进行涂白 - Imgproc.drawContours(classes_in_cd.getLeft(), rela_shapes, -1, new Scalar(55, 55, 55), 5); - // 将图片写入result文件 - Imgcodecs.imwrite(temp_res_path, classes_in_cd.getLeft()); System.out.println("完成对" + cd_path + "中识别关系符号的识别,共" + result.size() + "个"); 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 index 378bc50..17d5b93 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/Line.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Line.java @@ -7,7 +7,7 @@ import com.hy.java.utility.common.Pair; public class Line { public Point pt1; public Point pt2; - Equation equation; + public Equation equation; public double length; /** @@ -139,7 +139,7 @@ public class Line { public static boolean ptInLine(Point pt, Line line, boolean rela_ele) { double threshold; if (rela_ele) { - threshold = 12.0; + threshold = 10.0; } else { threshold = 2.0; } @@ -169,8 +169,8 @@ public class Line { /** * 获得直线斜率 */ - private static String getK(Line line) { - if (line.pt1.x == line.pt2.x) { + public static String getK(Line line) { + if (Math.abs(line.pt1.x - line.pt2.x) <= 5) { return "无穷大"; } else { return String.valueOf((line.pt2.y - line.pt1.y) / (line.pt2.x - line.pt1.x)); @@ -193,10 +193,23 @@ public class Line { } } - private class Equation { - double a; - double b; - double c; + /** + * 输入一个端点,返回另个一端点 + */ + public Point otherPt(Point pt) { + if (pt.equals(pt1)) { + return pt2; + } else if (pt.equals(pt2)) { + return pt1; + } else { + return null; + } + } + + public class Equation { + public double a; + public double b; + public double c; Equation(Line line) { a = line.pt2.y - line.pt1.y; diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java b/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java index 54afb82..faad1c0 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/PolygonalLine.java @@ -13,6 +13,8 @@ import org.opencv.core.Point; public class PolygonalLine { public Point pt1; public Point pt2; + public Line l1; + public Line l2; public List line_list = new ArrayList<>(); public PolygonalLine(Line line) { diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index 257dd37729af022763690fb2b4bbc111e1ca4820..9b0c831fb13365338743a2509a2056b0fe970180 100644 GIT binary patch literal 5961 zcmeHLe@vTY9)Fr9_WZH3#N<%t3xBv~9!;!j*Ej;q>U5@{n^O{!=U)%HIp$Fgo_hEGBu}dKrOY_LeSc$=p&Vijx zQuP^@i4ca3IRYUmm2wVBeH%DdB0wG|CN7~$Db^*!Gchkgu>VO!4zX*cN^iBau0vxW zxCI7B4_6^|rG=n}j|!JFZi(`=5cGkBv)u7hyiXgD5`xIT1%7apHTW=*qQS*FIOP~u zdEY141-tOhtE5uO>AH>Y#xu96&+y$luMphuU9z^>K~t<_tw^zrq8B@3=kS3y=@S(Tmp7`sPq<7$@ccO zqVPKb(L?j8 zG4?E{>x}?14|^=^g7r$xd0C5_m32r+HuLl6pIis|9A52W)j>|37xEXsj6>q3sFpGB z8;!h)MSGzpJkJ}C9C*Ne?~r)ud+;DxWTY%~m*gbQ z@_|{Wca-Tfp|0$xqFewn!K6;#0QIKmOR>tEEyKJg@SvpbG9J9E#%_weQ)yu*Mku%C zgAuV=-gH6i^e7;}83t~6TCB=31KV!6RI4Dew}$Xkhs=`u9IvSb?s&>kd_ZWwQIF>a zD&JqhkNkEUUcq8#HyGbauOH_qbEP16C`G35}cyidt6bWg>~_yuE7 z*bLx@x=WSO!+?1x{0G;}KG`QH*yNx@Tej8i?13!BgUr5V{!pZXs+!$miazMbrXb=w z7X4#0Q2wL)1L2ZnA}|8lW_QCa^i~kIbxfu*dT8L(lnrqQ-qq#i0luJta;)&#WYVC* zBf{>Flj+WAyP1NtKgZxl&FN9JvSI8nZ9>?B;}kl}cmyeTwC1&IFAKX!@=ikVW?HX8>V zOlq_DT@&3(hOSrf&S33k+I;+4zzBfOf}?JKgf=g>Wvthk0rj#=1Bp<(3CMhZQlYGR zo=A{Wq?B2bbj375aN=0(T(#Z#4~c(GmHG`#Q(ToSQ9#7<+Kuwc_`mq>F=)QRa_|Wb z{eLj0U8Ag-X!bu%u*u`q>JOc+@9#`C<{I@ey}`gmPmM*#K1wLy_C{MN1siIuaeBKl9R77fH5X_M8@{BIKMnGuDfzyy#aO+SjF{l|L`@ zm!^Z!Hk+Zpwiv0tqK;5wT5MDN+!%l+6O8_a3jae(7|ZnIOBt1+ra$TCLhv%Dh9`inn6uu1tV2SNJ^kECeWZE~a$f8S31M_#g_biL{E`d7XM;-=#L literal 6377 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 TufL1_hoO5mbCqvp=`a5YkJjjB diff --git a/src/test/java/com/hy/java/uct/umlrecog/OtherTests.java b/src/test/java/com/hy/java/uct/umlrecog/OtherTests.java index fc7e8db..d14aaa7 100644 --- a/src/test/java/com/hy/java/uct/umlrecog/OtherTests.java +++ b/src/test/java/com/hy/java/uct/umlrecog/OtherTests.java @@ -4,6 +4,9 @@ import java.util.ArrayList; import java.util.List; import org.junit.Test; +import org.opencv.core.Point; + +import com.hy.java.uct.umlrecog.util.Line; public class OtherTests { @Test @@ -27,6 +30,14 @@ public class OtherTests { } } + @Test + public void line() { + Line line = new Line(new Point(1, 1), new Point(3, 3)); + Point pt = line.pt1; + Point other_pt = line.otherPt(pt); + System.out.println(other_pt.toString()); + } + private List all_other_es(List list, String temp) { List result = new ArrayList<>(); list.remove(temp); -- Gitee From 97203442ee6d78fca06ad1314550b9c41e0be7c3 Mon Sep 17 00:00:00 2001 From: origin Date: Wed, 30 Dec 2020 19:01:06 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=B7=B2=E5=AE=8C=E6=88=90=E8=99=9A?= =?UTF-8?q?=E7=BA=BF=E7=AC=A6=E5=8F=B7=E8=AF=86=E5=88=AB=E3=80=82=E7=8E=B0?= =?UTF-8?q?=E5=AF=B9=E5=AE=9E=E7=BA=BF=E7=AC=A6=E5=8F=B7=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E3=80=82=E4=B9=8B=E5=90=8E=E5=B0=B1=E6=98=AF?= =?UTF-8?q?=E6=95=B4=E5=90=88=E7=BB=93=E6=9E=9C=E5=8D=B3=E5=8F=AF=EF=BC=8C?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E6=89=93=E5=8D=B0=E4=B8=80=E4=B8=AA=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E3=80=81=E6=88=96=E8=80=85=E7=9B=B4=E6=8E=A5=E5=9C=A8?= =?UTF-8?q?=E5=8E=9F=E5=9B=BE=E9=87=8C=E7=94=BB=E5=87=BA=E6=9D=A5=EF=BC=8C?= =?UTF-8?q?=E6=AF=94=E8=BE=83=E9=86=92=E7=9B=AE=EF=BC=88=E6=8E=A8=E8=8D=90?= =?UTF-8?q?=E7=94=BB=E5=87=BA=E6=9D=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umlrecog/cddetector/ClassDetector.java | 18 +- .../cddetector/ClassRelationDetector.java | 175 ++++++++++++------ .../java/uct/umlrecog/util/ImgProcessor.java | 58 ++++++ .../hy/java/uct/umlrecog/util/Relation.java | 6 + src/main/resources/cd/temp result.png | Bin 5961 -> 101 bytes 5 files changed, 185 insertions(+), 72 deletions(-) create mode 100644 src/main/java/com/hy/java/uct/umlrecog/util/ImgProcessor.java 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 ae649ed..103d825 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 @@ -8,12 +8,12 @@ import java.util.List; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.MatOfPoint2f; -import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import com.hy.java.uct.umlrecog.UMLDiagramRecognizer; +import com.hy.java.uct.umlrecog.util.ImgProcessor; import com.hy.java.uct.umlrecog.util.Rectangle; import com.hy.java.uct.umlrecog.util.UMLClass; import com.hy.java.utility.common.Pair; @@ -104,7 +104,7 @@ public class ClassDetector { private List mergeIntoClass(Pair> all_rect_areas_in_cd) { List result = new ArrayList<>(); - Mat cls_diagram = all_rect_areas_in_cd.getLeft(); + // Mat cls_diagram = all_rect_areas_in_cd.getLeft(); // 获取所有矩形 List rect_area_list = all_rect_areas_in_cd.getRight(); /* @@ -234,15 +234,15 @@ public class ClassDetector { try { // 将uc中的每个区域写入临时文件,然后识别临时文件中的文字 if (uc.top != null) { - Imgcodecs.imwrite(temp_res_path, cutImage(cls_diagram, uc.top)); + Imgcodecs.imwrite(temp_res_path, ImgProcessor.cutImage(cls_diagram, uc.top)); uc.setTitle(instance.doOCR(new File(temp_res_path))); } if (uc.mid != null) { - Imgcodecs.imwrite(temp_res_path, cutImage(cls_diagram, uc.mid)); + Imgcodecs.imwrite(temp_res_path, ImgProcessor.cutImage(cls_diagram, uc.mid)); uc.setAttrisStr(instance.doOCR(new File(temp_res_path))); } if (uc.bottom != null) { - Imgcodecs.imwrite(temp_res_path, cutImage(cls_diagram, uc.bottom)); + Imgcodecs.imwrite(temp_res_path, ImgProcessor.cutImage(cls_diagram, uc.bottom)); uc.setMethodsStr(instance.doOCR(new File(temp_res_path))); } } catch (TesseractException e) { @@ -254,14 +254,6 @@ public class ClassDetector { return classes; } - public Mat cutImage(Mat src, Rect rect) { - Mat result = new Mat(); - // 图片裁剪 - Mat src_with_roi = new Mat(src, rect); - src_with_roi.copyTo(result); - return result; - } - public Pair> getResult() { return result; } 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 33becd6..80cb846 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 @@ -7,6 +7,7 @@ import java.util.List; import java.util.Set; import org.opencv.core.Core; +import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.MatOfPoint2f; @@ -17,6 +18,7 @@ import org.opencv.core.Size; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; +import com.hy.java.uct.umlrecog.util.ImgProcessor; import com.hy.java.uct.umlrecog.util.Line; import com.hy.java.uct.umlrecog.util.PolygonalLine; import com.hy.java.uct.umlrecog.util.Rectangle; @@ -43,7 +45,7 @@ public class ClassRelationDetector { // 再检测虚线。虚线检测除了间隔设置与实线不同外,其他完全一样 Set dash_lines = detectLines(classes_in_cd, 0.0504, false); // 再检测关系类型检测 - List relation_types = detectRelationType(classes_in_cd, solid_lines, dash_lines, 0.0001); + Set relation_types = detectRelationType(classes_in_cd, solid_lines, dash_lines, 0.0001); } @@ -144,16 +146,15 @@ public class ClassRelationDetector { */ for (PolygonalLine pl : poly_lines) { Relation rela = new Relation(pl); - // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点。 - Point pt1 = reachPt(pl.pt1, pl.l1, 35); - Point pt2 = reachPt(pl.pt2, pl.l2, 35); + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点,检查延长后的端点。 for (UMLClass uml_class : UML_classes) { - // 暂时将与pt1相近的类记为source、与pt2相近的类为target,后面识别出符号来后再调 // 检查延长后的端点 - if (uml_class.whole.contains(pt1)) { + if (uml_class.whole.contains(ImgProcessor.reachPt(pl.pt1, pl.l1, 35, false))) { + // 暂时将与pt1相近的类记为source、与pt2相近的类为target,后面识别出符号来后再调 rela.source = uml_class; } - if (uml_class.whole.contains(pt2)) { + if (uml_class.whole.contains(ImgProcessor.reachPt(pl.pt2, pl.l2, 35, false))) { + // 暂时将与pt1相近的类记为source、与pt2相近的类为target,后面识别出符号来后再调 rela.target = uml_class; } } @@ -329,38 +330,6 @@ public class ClassRelationDetector { return result; } - private Point reachPt(Point pt, Line line, double l) { - Point result = pt.clone(); - String k_str = Line.getK(line); - // 如果折线端点所在线段的斜率是无穷大,则只延长坐标y即可 - if (k_str == "无穷大") { - // 根据坐标y的方向进行延长 - if (line.otherPt(pt).y - pt.y <= 0) { - // 延长 - result.y += l; - } else { - // 延长 - result.y -= l; - } - } - // 否则,则需延直线斜率方向延长 - else { - double k = Double.parseDouble(k_str); - double b = -line.equation.c / line.equation.b; - // 根据坐标x的方向进行延长 - if (line.otherPt(pt).x - pt.x <= 0) { - // 延长 - result.x += l * Math.cos(Math.atan(Math.abs(k))); - result.y = k * result.x + b; - } else { - // 延长 - result.x -= l * Math.cos(Math.atan(Math.abs(k))); - result.y = k * result.x + b; - } - } - return result; - } - /** * 识别关系符号,并根据关系符号与关系线端点的位置关系更新关系线的source和target * @@ -370,10 +339,11 @@ public class ClassRelationDetector { * @param dash_lines * @return */ - private List detectRelationType(Pair> classes_in_cd, Set solid_lines, Set dash_lines, double ratio) { + private Set detectRelationType(Pair> classes_in_cd, Set solid_lines, Set dash_lines, double ratio) { System.out.println("开始识别" + cd_path + "中所有关系符号"); - List result = new ArrayList<>(); + Set result = new HashSet<>(); Mat cls_diagram = classes_in_cd.getLeft(); + Mat origin_cls_diagram = Imgcodecs.imread(cd_path, Imgcodecs.IMREAD_GRAYSCALE); /* * 识别图中所有“轮廓”并存在contours中 */ @@ -397,31 +367,118 @@ public class ClassRelationDetector { Imgproc.fillConvexPoly(cls_diagram, contour, new Scalar(255, 255, 255)); continue; } - // 如果轮廓面积合适,则用矩形将其包裹,并检测矩形与关系端点的位置关系 + // 如果轮廓面积合适,则用矩形将其包裹,并裁剪原图中的矩形区域、重新识别符号 MatOfPoint2f curve = new MatOfPoint2f(contour.toArray()); MatOfPoint2f approx_curve = new MatOfPoint2f(); Imgproc.approxPolyDP(curve, approx_curve, 0.01 * Imgproc.arcLength(curve, true), true); + // 获取包络矩形 Rect rect = Imgproc.boundingRect(new MatOfPoint(approx_curve.toArray())); - // 此时可以看一眼 - // Imgproc.rectangle(cls_diagram, rect, new Scalar(5, 5, 5), 2); - // Imgcodecs.imwrite(temp_res_path, cls_diagram); - // 针对包裹矩形,检测其与每条线端点的位置关系 - /* - * 写!!!!!!!!!!!!!!!!!!! - * - * - * - */ - for (Relation rela : solid_lines) { - if (rect.contains(reachPt(rela.poly_line.pt1, rela.poly_line.l1, 5))) { + // 如果包络矩形包含某一条关系线的某一端,则检测该矩形内的符号 + // 对于虚线,由于只可能是实现或生命线,所以只需要知道是否包含即可,不用检测矩形内部图形 + boolean belong_to_dash_rela = false; + for (Relation dash_rela : dash_lines) { + // 如果dash_rela.source_pt_index != -1,则说明该dash_rela已经与某一包络矩形检测并对应上了,则跳过 + if (dash_rela.source_pt_index != -1) { + continue; + } + // 对线的每个端点,检查其本身、延伸、反向延伸这三个点是否被包含于包络矩形中。如果包含,则belong_to_dash_line = true; + if (rect.contains(dash_rela.poly_line.pt1) || rect.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt1, dash_rela.poly_line.l1, 5, false)) + || rect.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt1, dash_rela.poly_line.l1, 5, true))) { + // 包络矩形确实包含关系线的端点pt1 + belong_to_dash_rela = true; + // 重新检查source和target。目前的端点pt1必须对应target类 + if (dash_rela.source.whole.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt1, dash_rela.poly_line.l1, 35, false))) { + UMLClass temp = dash_rela.source; + dash_rela.source = dash_rela.target; + dash_rela.target = temp; + } + dash_rela.source_pt_index = 2; + dash_rela.type = "实现"; + break; + } else if (rect.contains(dash_rela.poly_line.pt2) || rect.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 5, false)) + || rect.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 5, true))) { + belong_to_dash_rela = true; + // 重新检查source和target。目前的端点pt2必须对应target类 + if (dash_rela.source.whole.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 35, false))) { + UMLClass temp = dash_rela.source; + dash_rela.source = dash_rela.target; + dash_rela.target = temp; + } + dash_rela.source_pt_index = 1; + dash_rela.type = "实现"; + break; + } + } + if (!belong_to_dash_rela) { + // 对于实线,需检测矩形内部图形,识别继承、聚合 + for (Relation solid_rela : solid_lines) { + // 如果solid_line.source_pt_index != -1,则说明该solid_line已经与某一包络矩形检测并对应上了,则跳过 + if (solid_rela.source_pt_index != -1) { + continue; + } + // 记录该包络矩形是否属于某关系线。如果是的话,再去检测矩形内图形 + boolean belong_to_this_solid_rela = false; + // 方法同对dash_rela的检测。对线的每个端点,检查其本身、延伸、反向延伸这三个点是否被包含于包络矩形中。 + if (rect.contains(solid_rela.poly_line.pt1) || rect.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt1, solid_rela.poly_line.l1, 5, false)) + || rect.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt1, solid_rela.poly_line.l1, 5, true))) { + // 包络矩形确实包含关系线的端点pt1 + belong_to_this_solid_rela = true; + // 重新检查source和target。目前的端点pt1必须对应target类 + if (solid_rela.source.whole.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt1, solid_rela.poly_line.l1, 35, false))) { + UMLClass temp = solid_rela.source; + solid_rela.source = solid_rela.target; + solid_rela.target = temp; + } + solid_rela.source_pt_index = 2; + } else if (rect.contains(solid_rela.poly_line.pt2) || rect.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt2, solid_rela.poly_line.l2, 5, false)) + || rect.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt2, solid_rela.poly_line.l2, 5, true))) { + belong_to_this_solid_rela = true; + // 重新检查source和target。目前的端点pt2必须对应target类 + if (solid_rela.source.whole.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt2, solid_rela.poly_line.l2, 35, false))) { + UMLClass temp = solid_rela.source; + solid_rela.source = solid_rela.target; + solid_rela.target = temp; + } + solid_rela.source_pt_index = 1; + } + if (belong_to_this_solid_rela) { + String type = "继承"; + // 裁剪原图中矩形区域 + Mat origin_rela_type_area = ImgProcessor.cutImage(origin_cls_diagram, rect); + Imgproc.erode(origin_rela_type_area, origin_rela_type_area, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2)), new Point(-1, -1), 2); + Imgproc.dilate(origin_rela_type_area, origin_rela_type_area, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1)), new Point(-1, -1), 2); + // 重新识别符号 + List origin_contours = new ArrayList<>(); + Imgproc.findContours(origin_rela_type_area, origin_contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); + // 针对识别出来的区域做筛选 + for (MatOfPoint origin_contour : origin_contours) { + /* + * 识别一下吧,估计不咋滴。下面的代码可以都删掉 + */ + MatOfPoint2f origin_curve = new MatOfPoint2f(origin_contour.toArray()); + MatOfPoint2f origin_approx_curve = new MatOfPoint2f(); + Imgproc.approxPolyDP(origin_curve, origin_approx_curve, 0.01 * Imgproc.arcLength(origin_curve, false), true); + + List temp_list = new ArrayList<>(); + MatOfPoint matOfPoint = new MatOfPoint(); + origin_approx_curve.convertTo(matOfPoint, CvType.CV_32S); + temp_list.add(matOfPoint); + Imgproc.polylines(origin_rela_type_area, temp_list, true, new Scalar(5, 5, 5), 5); + Imgcodecs.imwrite(temp_res_path, origin_rela_type_area); + System.out.println(origin_approx_curve.dump()); + System.out.println(origin_approx_curve.toArray().length); + /* + * 识别完毕,差不多得了。上面的代码可以都删掉 + */ + } + solid_rela.type = type; + // 检测完符号类别后,对当前区域就算检测完了,不用再检测其他实线了 + break; + } } - // 将每个关系符号存在别处。存完后将其从图中抹掉,防止其干扰后续识别 - RelationType relation_type = new RelationType(); + // 如果运行到这儿(检测完符号类别后break会直接到这儿),说明对当前区域的包络矩形与实线之间的检测完毕。相当于对当前区域检测完毕,开始检测下一个区域 } - /* - * 傲娇 - */ } System.out.println("完成对" + cd_path + "中识别关系符号的识别,共" + result.size() + "个"); return result; diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/ImgProcessor.java b/src/main/java/com/hy/java/uct/umlrecog/util/ImgProcessor.java new file mode 100644 index 0000000..d5b8852 --- /dev/null +++ b/src/main/java/com/hy/java/uct/umlrecog/util/ImgProcessor.java @@ -0,0 +1,58 @@ +package com.hy.java.uct.umlrecog.util; + +import org.opencv.core.Mat; +import org.opencv.core.Point; +import org.opencv.core.Rect; + +public class ImgProcessor { + /** + * 图片裁剪。裁剪src中的矩形区域 + */ + public static Mat cutImage(Mat src, Rect rect) { + Mat result = new Mat(); + Mat src_with_roi = new Mat(src, rect); + src_with_roi.copyTo(result); + return result; + } + + /** + * 基于line的端点pt的坐标,延着line向着该端点的方向延伸,延伸长度为l。若reverse为真,则反向延伸 + * + * @return 延伸后的一个Point + */ + public static Point reachPt(Point pt, Line line, double l, boolean reverse) { + Point result = pt.clone(); + String k_str = Line.getK(line); + double flag = 1.0; + if (reverse) { + flag = -1.0; + } + // 如果折线端点所在线段的斜率是无穷大,则只延长坐标y即可 + if (k_str == "无穷大") { + // 根据坐标y的方向进行延长 + if (line.otherPt(pt).y - pt.y <= 0) { + // 延长 + result.y += flag * l; + } else { + // 延长 + result.y -= flag * l; + } + } + // 否则,则需延直线斜率方向延长 + else { + double k = Double.parseDouble(k_str); + double b = -line.equation.c / line.equation.b; + // 根据坐标x的方向进行延长 + if (line.otherPt(pt).x - pt.x <= 0) { + // 延长 + result.x += flag * l * Math.cos(Math.atan(Math.abs(k))); + result.y = k * result.x + b; + } else { + // 延长 + result.x -= flag * l * Math.cos(Math.atan(Math.abs(k))); + result.y = k * result.x + b; + } + } + return result; + } +} diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java b/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java index a5a5a06..9d2edd8 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java @@ -4,6 +4,12 @@ public class Relation { public PolygonalLine poly_line; public UMLClass source; public UMLClass target; + /** + * 标识poly_line中的哪个端点对应source类。若为-1,则说明还未确定哪个端点对应source类 + */ + public int source_pt_index = -1; + // 依赖→实现。组合→聚合,聚合→继承 + public String type = null; public Relation(PolygonalLine poly_line) { this.poly_line = poly_line; diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index 9b0c831fb13365338743a2509a2056b0fe970180..4e790beef5a46e348376e6323133972ccda356e7 100644 GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^l0eMQ0VEhqc&#mgl#Zv1V~B)g=|MwAAg@`$_Dy^x eC)=?^87#=qE1DrXNh-7zq~Fuk&t;ucLK6V(=ooPT literal 5961 zcmeHLe@vTY9)Fr9_WZH3#N<%t3xBv~9!;!j*Ej;q>U5@{n^O{!=U)%HIp$Fgo_hEGBu}dKrOY_LeSc$=p&Vijx zQuP^@i4ca3IRYUmm2wVBeH%DdB0wG|CN7~$Db^*!Gchkgu>VO!4zX*cN^iBau0vxW zxCI7B4_6^|rG=n}j|!JFZi(`=5cGkBv)u7hyiXgD5`xIT1%7apHTW=*qQS*FIOP~u zdEY141-tOhtE5uO>AH>Y#xu96&+y$luMphuU9z^>K~t<_tw^zrq8B@3=kS3y=@S(Tmp7`sPq<7$@ccO zqVPKb(L?j8 zG4?E{>x}?14|^=^g7r$xd0C5_m32r+HuLl6pIis|9A52W)j>|37xEXsj6>q3sFpGB z8;!h)MSGzpJkJ}C9C*Ne?~r)ud+;DxWTY%~m*gbQ z@_|{Wca-Tfp|0$xqFewn!K6;#0QIKmOR>tEEyKJg@SvpbG9J9E#%_weQ)yu*Mku%C zgAuV=-gH6i^e7;}83t~6TCB=31KV!6RI4Dew}$Xkhs=`u9IvSb?s&>kd_ZWwQIF>a zD&JqhkNkEUUcq8#HyGbauOH_qbEP16C`G35}cyidt6bWg>~_yuE7 z*bLx@x=WSO!+?1x{0G;}KG`QH*yNx@Tej8i?13!BgUr5V{!pZXs+!$miazMbrXb=w z7X4#0Q2wL)1L2ZnA}|8lW_QCa^i~kIbxfu*dT8L(lnrqQ-qq#i0luJta;)&#WYVC* zBf{>Flj+WAyP1NtKgZxl&FN9JvSI8nZ9>?B;}kl}cmyeTwC1&IFAKX!@=ikVW?HX8>V zOlq_DT@&3(hOSrf&S33k+I;+4zzBfOf}?JKgf=g>Wvthk0rj#=1Bp<(3CMhZQlYGR zo=A{Wq?B2bbj375aN=0(T(#Z#4~c(GmHG`#Q(ToSQ9#7<+Kuwc_`mq>F=)QRa_|Wb z{eLj0U8Ag-X!bu%u*u`q>JOc+@9#`C<{I@ey}`gmPmM*#K1wLy_C{MN1siIuaeBKl9R77fH5X_M8@{BIKMnGuDfzyy#aO+SjF{l|L`@ zm!^Z!Hk+Zpwiv0tqK;5wT5MDN+!%l+6O8_a3jae(7|ZnIOBt1+ra$TCLhv%Dh9`inn6uu1tV2SNJ^kECeWZE~a$f8S31M_#g_biL{E`d7XM;-=#L -- Gitee