From 6a2be3ab5010e95fc0d8548ea32485b3bdf039a9 Mon Sep 17 00:00:00 2001 From: chief Date: Thu, 26 Nov 2020 23:19:50 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E7=9B=B4=E7=BA=BF=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E5=8F=98=E6=88=90=EF=BC=9A=E5=85=88=E6=A3=80=E6=B5=8B=E5=AE=9E?= =?UTF-8?q?=E7=BA=BF=E3=80=81=E5=86=8D=E6=A3=80=E6=B5=8B=E5=85=B3=E7=B3=BB?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E3=80=81=E5=86=8D=E6=A3=80=E6=B5=8B=E8=99=9A?= =?UTF-8?q?=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../uct/umlrecog/UMLDiagramRecognizer.java | 2 +- .../umlrecog/cddetector/ClassDetector.java | 14 +++-- .../cddetector/ClassRelationDetector.java | 53 ++++++++++--------- 3 files changed, 38 insertions(+), 31 deletions(-) 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 48d9e14..dfdbf4b 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 551c2fe..56bf8e7 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 @@ -98,7 +98,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 +203,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 e73382a..7ae1fd9 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 @@ -27,10 +27,33 @@ public class ClassRelationDetector { } public void recog() { - // 先检测关系类型检测 - Mat temp = detectRelationType(classes_in_cd, 0.000555); - // 再检测关系线 - // detectLines(temp); + // 先检测实线 + detectLines(classes_in_cd.getLeft()); + // 再检测关系类型检测 + // Mat temp = detectRelationType(classes_in_cd, 0.00002); + // 最后再检测虚线 + // detectLines(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 / 180, 12, 17, 5); + // 将检测到的直线绘制到图中 + 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), 1); + } + // Writing the image + Imgcodecs.imwrite(temp_res_path, src); + System.out.println("识别关系完毕,共" + lines.rows() + "条"); } /** @@ -74,33 +97,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; } -- Gitee From 3925515072a7474f03da976ea9236e5a6529fb1a Mon Sep 17 00:00:00 2001 From: chief Date: Fri, 27 Nov 2020 21:02:28 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=AE=9E=E7=BA=BF=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E5=B0=8F=E4=BF=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cddetector/ClassRelationDetector.java | 22 ++++++++---------- src/main/resources/cd/temp result.png | Bin 5383 -> 8779 bytes 2 files changed, 10 insertions(+), 12 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 7ae1fd9..d4be5a9 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 @@ -28,32 +28,30 @@ public class ClassRelationDetector { public void recog() { // 先检测实线 - detectLines(classes_in_cd.getLeft()); + detectSolidLines(classes_in_cd.getLeft()); // 再检测关系类型检测 // Mat temp = detectRelationType(classes_in_cd, 0.00002); // 最后再检测虚线 // detectLines(classes_in_cd.getLeft()); } - private void detectLines(Mat src) { - // TODO Auto-generated method stub - System.out.println("识别关系"); + private void detectSolidLines(Mat cls_diagram) { + System.out.println("识别实线"); // 先检测边缘。然后从边缘集中检测直线 - Mat canny = new Mat(); - Imgproc.Canny(src, canny, 50, 150, 3, true); - // 从边缘集中检测直线 + Mat edges = new Mat(); + Imgproc.Canny(cls_diagram, edges, 50, 150, 3, true); + // 从边缘集中检测直线。实线检测时,“最大像素间隔”越小越准确 Mat lines = new Mat(); - // 这些步长可有的调了。得按像素来 - Imgproc.HoughLinesP(canny, lines, 1, Math.PI / 180, 12, 17, 5); + Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, 17, 5); // 将检测到的直线绘制到图中 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), 1); + Imgproc.line(cls_diagram, pt1, pt2, new Scalar(55, 55, 55), 1); } // Writing the image - Imgcodecs.imwrite(temp_res_path, src); - System.out.println("识别关系完毕,共" + lines.rows() + "条"); + Imgcodecs.imwrite(temp_res_path, cls_diagram); + System.out.println("识别实线完毕,共" + lines.rows() + "条"); } /** diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index bd9e356c945ea18097229d8317c29bf4ad7b9636..4e04dc1d1e0963823d878a297d4975d30e596878 100644 GIT binary patch literal 8779 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 Date: Fri, 27 Nov 2020 21:16:31 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=83=8F=E7=B4=A0=E9=97=B4=E9=9A=94?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umlrecog/cddetector/ClassRelationDetector.java | 14 +++++++++----- 1 file changed, 9 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 d4be5a9..840502f 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 @@ -28,26 +28,30 @@ public class ClassRelationDetector { public void recog() { // 先检测实线 - detectSolidLines(classes_in_cd.getLeft()); + detectSolidLines(classes_in_cd.getLeft(), 0.00003025); // 再检测关系类型检测 // Mat temp = detectRelationType(classes_in_cd, 0.00002); // 最后再检测虚线 // detectLines(classes_in_cd.getLeft()); } - private void detectSolidLines(Mat cls_diagram) { + private void detectSolidLines(Mat cls_diagram, double ratio) { System.out.println("识别实线"); // 先检测边缘。然后从边缘集中检测直线 Mat edges = new Mat(); Imgproc.Canny(cls_diagram, edges, 50, 150, 3, true); - // 从边缘集中检测直线。实线检测时,“最大像素间隔”越小越准确 + // 从边缘集中检测直线。实线检测时,“最大像素间隔”越小,要求像素连接越密集 Mat lines = new Mat(); - Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, 17, 5); + int cd_width = cls_diagram.width(); + int cd_height = cls_diagram.height(); + long cd_area = cd_width * cd_height; + double min_line_length = cd_area * ratio; + Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, min_line_length, 3); // 将检测到的直线绘制到图中 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(cls_diagram, pt1, pt2, new Scalar(55, 55, 55), 1); + Imgproc.line(cls_diagram, pt1, pt2, new Scalar(55, 55, 55), 5); } // Writing the image Imgcodecs.imwrite(temp_res_path, cls_diagram); -- Gitee From 9a6db276771fe34963d509d3f79dcdc76a76ad20 Mon Sep 17 00:00:00 2001 From: chief Date: Fri, 27 Nov 2020 21:24:31 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E8=B0=83=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umlrecog/cddetector/ClassDetector.java | 4 +--- .../cddetector/ClassRelationDetector.java | 10 ++++------ src/main/resources/cd/temp result.png | Bin 8779 -> 7724 bytes 3 files changed, 5 insertions(+), 9 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 56bf8e7..498a20b 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) { 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 840502f..b275e74 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 @@ -28,7 +28,7 @@ public class ClassRelationDetector { public void recog() { // 先检测实线 - detectSolidLines(classes_in_cd.getLeft(), 0.00003025); + detectSolidLines(classes_in_cd.getLeft(), 0.0505); // 再检测关系类型检测 // Mat temp = detectRelationType(classes_in_cd, 0.00002); // 最后再检测虚线 @@ -42,11 +42,9 @@ public class ClassRelationDetector { Imgproc.Canny(cls_diagram, edges, 50, 150, 3, true); // 从边缘集中检测直线。实线检测时,“最大像素间隔”越小,要求像素连接越密集 Mat lines = new Mat(); - int cd_width = cls_diagram.width(); - int cd_height = cls_diagram.height(); - long cd_area = cd_width * cd_height; - double min_line_length = cd_area * ratio; - Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, min_line_length, 3); + double min_line_length_width = cls_diagram.width() * ratio; + double min_line_length_height = cls_diagram.height() * ratio; + Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, Math.min(min_line_length_width, min_line_length_height), 3); // 将检测到的直线绘制到图中 for (int i = 0; i < lines.rows(); i++) { Point pt1 = new Point(lines.get(i, 0)[0], lines.get(i, 0)[1]); diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index 4e04dc1d1e0963823d878a297d4975d30e596878..172a35d8bc910b4842721dbf4c96c79e14160b35 100644 GIT binary patch literal 7724 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
  • 29I(5znO#zNB$a24xBNr>tM_&&FN_ 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 Date: Mon, 30 Nov 2020 22:28:04 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E7=9B=B4=E7=BA=BF=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E6=89=80=E6=9C=89=E5=88=9A=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E5=87=BA=E6=9D=A5=E7=9A=84=E2=80=9C=E5=8E=9F=E5=A7=8B=E7=9B=B4?= =?UTF-8?q?=E7=BA=BF=E2=80=9D=EF=BC=8C=E5=B9=B6=E5=BC=80=E5=A7=8B=E5=AF=B9?= =?UTF-8?q?=E2=80=9C=E5=8E=9F=E5=A7=8B=E7=9B=B4=E7=BA=BF=E2=80=9D=E5=81=9A?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cddetector/ClassRelationDetector.java | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 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 b275e74..ccf2ebf 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; @@ -35,25 +36,36 @@ public class ClassRelationDetector { // detectLines(classes_in_cd.getLeft()); } - private void detectSolidLines(Mat cls_diagram, double ratio) { - System.out.println("识别实线"); + 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, 150, 3, true); - // 从边缘集中检测直线。实线检测时,“最大像素间隔”越小,要求像素连接越密集 + Imgproc.Canny(cls_diagram, edges, 50, 50 * 3, 3, true); + // 从边缘集中检测直线。实线检测时,“最大像素间隔”(与图像像素做比)越小,要求像素连接越密集 Mat lines = new Mat(); - double min_line_length_width = cls_diagram.width() * ratio; - double min_line_length_height = cls_diagram.height() * ratio; - Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 12, Math.min(min_line_length_width, min_line_length_height), 3); - // 将检测到的直线绘制到图中 + 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++) { - 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(cls_diagram, pt1, pt2, new Scalar(55, 55, 55), 5); + 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]))); + } + /* + * 对“原始直线”进行处理,校正关系线对象Line的信息 + */ + // “原始直线”中存在“两端都接近”的情况,将其算为一条线 + for (Line l : result) { + if (l.isNear(null)) { + result.remove(l); + } + 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("识别实线完毕,共" + lines.rows() + "条"); + System.out.println("识别实线完毕,共" + result.size() + "条"); + return result; } /** -- Gitee From b07ddcee1b6485110aca55c9c899cbe22a09bb55 Mon Sep 17 00:00:00 2001 From: chief Date: Mon, 30 Nov 2020 22:28:28 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=B8=8E=E7=9B=B4?= =?UTF-8?q?=E7=BA=BF=E6=9C=89=E5=85=B3=E7=9A=84=E7=B1=BBLine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hy/java/uct/umlrecog/util/Line.java | 19 ++++++++++++++++++ src/main/resources/cd/temp result.png | Bin 7724 -> 7897 bytes 2 files changed, 19 insertions(+) create mode 100644 src/main/java/com/hy/java/uct/umlrecog/util/Line.java 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 0000000..da92e1c --- /dev/null +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Line.java @@ -0,0 +1,19 @@ +package com.hy.java.uct.umlrecog.util; + +import org.opencv.core.Point; + +public class Line { + public Point pt1; + public Point pt2; + + public Line(Point pt1, Point pt2) { + this.pt1 = pt1; + this.pt2 = pt2; + } + + public boolean isNear(Line line) { + boolean result = false; + + return result; + } +} diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index 172a35d8bc910b4842721dbf4c96c79e14160b35..7da472524d9f0ce08b3fdf18669215046df8940c 100644 GIT binary patch literal 7897 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 literal 7724 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
  • 29I(5znO#zNB$a24xBNr>tM_&&FN_ zr@UUSN@!$$^rWcDg#?`w%R8;~rC14u8rz>Ma<)FXaTZd3oY(%qDm7<( z65f-`b>MI;pEnVLbtlg_7VdPNPbN9(9K7Z$K~V+H_u)*BT!)n Date: Wed, 2 Dec 2020 23:40:38 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=AE=9E=E7=BA=BF=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=EF=BC=8C=E8=BF=98=E5=B7=AE=E4=B8=AA=E2=80=9C=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E4=B8=80=E7=AB=AF=E9=87=8D=E5=90=88=E3=80=81=E6=96=9C=E7=8E=87?= =?UTF-8?q?=E7=9B=B8=E8=BF=91=E7=9A=84=E4=B8=A4=E6=9D=A1=E7=BA=BF=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cddetector/ClassRelationDetector.java | 43 ++++++++++--- .../com/hy/java/uct/umlrecog/util/Line.java | 57 +++++++++++++++++- src/main/resources/cd/temp result.png | Bin 7897 -> 7310 bytes 3 files changed, 91 insertions(+), 9 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 ccf2ebf..3afcfc4 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 @@ -29,7 +29,7 @@ public class ClassRelationDetector { public void recog() { // 先检测实线 - detectSolidLines(classes_in_cd.getLeft(), 0.0505); + List solid_lines = detectSolidLines(classes_in_cd.getLeft(), 0.0505); // 再检测关系类型检测 // Mat temp = detectRelationType(classes_in_cd, 0.00002); // 最后再检测虚线 @@ -45,7 +45,7 @@ public class ClassRelationDetector { // 先检测边缘。然后从边缘集中检测直线 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); // 保存所有刚检测出来的“原始直线” @@ -53,13 +53,37 @@ public class ClassRelationDetector { 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]))); } /* - * 对“原始直线”进行处理,校正关系线对象Line的信息 + * 检查“原始直线”集中是否有重合直线,去除冗余或合并 */ - // “原始直线”中存在“两端都接近”的情况,将其算为一条线 - for (Line l : result) { - if (l.isNear(null)) { - result.remove(l); + 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 @@ -68,6 +92,11 @@ public class ClassRelationDetector { return result; } + // 将斜率接近的两条直线合并成“最长”的一条线,并存在line中 + private void mergeKNearLines(Line line, Line other_line) { + // 这TM怎么合并啊?!! + } + /** * 识别关系符号 * 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 da92e1c..4224daa 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 @@ -5,15 +5,68 @@ 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 isNear(Line line) { + /** + * 判断一条直线是否与另一条直线重合 + */ + 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 7da472524d9f0ce08b3fdf18669215046df8940c..16a319315b960adda0f266dd6af8642488dab69d 100644 GIT binary patch literal 7310 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 WSJr%0@QL_GBSoxVb1d(LxBd&}EQ#^} literal 7897 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 -- Gitee