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 8615e3542e91f1e810ce5c620be59cb6093caa8e..103d8256e2f4689be6d17e81007f0b8162ebc3be 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 @@ -13,6 +13,7 @@ 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; @@ -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,6 +187,38 @@ public class ClassDetector { } } } + /* + * 此时会出现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, uml_class.top.x, uml_class.top.y, uml_class.top.width, whole_height); // 针对当前矩形,与所有其他矩形对比完毕后,则完成了该类的识别 result.add(uml_class); } @@ -192,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, ImgProcessor.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, ImgProcessor.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, ImgProcessor.cutImage(cls_diagram, uc.bottom)); uc.setMethodsStr(instance.doOCR(new File(temp_res_path))); } } catch (TesseractException e) { 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 ca07a20cfdd58b31b9eb5c2056b542e6d8728e68..80cb8461622f174d2555337789805d3326bc68ea 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,18 +6,23 @@ import java.util.HashSet; 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; 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; 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; +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,25 +41,27 @@ 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); + Set relation_types = detectRelationType(classes_in_cd, solid_lines, dash_lines, 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) { + 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); @@ -66,7 +73,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++) { @@ -131,30 +138,56 @@ public class ClassRelationDetector { /* * 对所有实线,将可能的“折线”识别并合并。合并为折线的直线,其belonged_polygonal_line会标识其所属折线 */ - result = mergePolygonalLines(line_segments); + Set poly_lines = mergePolygonalLines(line_segments); + /* + * 利用UML_classes的位置,进一步筛选关系线,存在result中 + * + * 针对每条候选关系线,计算每个类方框的4根边框线,看哪个边框线与当前候选关系线相交(允许一定像素的误差),则认为这条线确实是关系线,且属于相交的类 + */ + for (PolygonalLine pl : poly_lines) { + Relation rela = new Relation(pl); + // 对pl.pt1和pl.pt2,需要将其延直线方向延长一点,检查延长后的端点。 + for (UMLClass uml_class : UML_classes) { + // 检查延长后的端点 + 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(ImgProcessor.reachPt(pl.pt2, pl.l2, 35, false))) { + // 暂时将与pt1相近的类记为source、与pt2相近的类为target,后面识别出符号来后再调 + rela.target = uml_class; + } + } + if (rela.source != null && rela.target != null) { + result.add(rela); + } + } + /* + * 在图中抹掉关系线,防止其影响后续识别 + */ 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() + "条"); @@ -269,7 +302,7 @@ public class ClassRelationDetector { } } } - // 设置每条折线的两端 + // 设置每条折线的两端。同时记录两端的点所在的线段 for (PolygonalLine pl : result) { for (Line l : pl.line_list) { Point pt = l.notInPolyPt(); @@ -278,13 +311,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; } } } @@ -294,15 +331,19 @@ public class ClassRelationDetector { } /** - * 识别关系符号 + * 识别关系符号,并根据关系符号与关系线端点的位置关系更新关系线的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 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中 */ @@ -310,7 +351,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中 */ @@ -318,34 +359,127 @@ 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(); - /* - * 目前不可能从approx_curve中找到关系符号形状。只能先确定关系符号位置、大小。或许可以根据关系符号大小,画一个圆。 - * - */ - rela_shapes.add(contour); - System.out.println(contour.dump()); - // 将关系符号从图中抹掉(涂白)。后面还需对所有边框进行涂白 - Imgproc.fillConvexPoly(classes_in_cd.getLeft(), contour, new Scalar(55, 55, 55)); + // 获取包络矩形 + Rect rect = Imgproc.boundingRect(new MatOfPoint(approx_curve.toArray())); + // 如果包络矩形包含某一条关系线的某一端,则检测该矩形内的符号 + // 对于虚线,由于只可能是实现或生命线,所以只需要知道是否包含即可,不用检测矩形内部图形 + 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; + } + } + // 如果运行到这儿(检测完符号类别后break会直接到这儿),说明对当前区域的包络矩形与实线之间的检测完毕。相当于对当前区域检测完毕,开始检测下一个区域 + } } - // 对所有边框进行涂白 - 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/ImgProcessor.java b/src/main/java/com/hy/java/uct/umlrecog/util/ImgProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..d5b8852a11b275ea40275ab4a42ab74030839f7b --- /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/Line.java b/src/main/java/com/hy/java/uct/umlrecog/util/Line.java index cc3e431bd563cb9a498ddd675e596285c3802da4..17d5b9387543214c9e4aabd9f453d156f21b9a2b 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; /** @@ -71,6 +71,8 @@ public class Line { /** * 判断某一点是否与另一条直线重合。即“是否与另一条直线的某一端点接近”、“是否在另一条直线内”。记录接近的直线端点 + * + * 在识别直线、去除冗余时使用,其他时候不要用 */ public static Pair ptNearLine(Point pt, Line line) { Pair result = new Pair<>(false, null); @@ -86,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); @@ -129,10 +131,18 @@ public class Line { } /** - * 判断点是否在直线内。计算点到直线距离,与阈值比较 + * 判断点是否在直线内。计算点到直线距离,与阈值比较。如果是关系线与元素对比,则阈值可以大一点 + * + * @param pt 点 + * @param line 直线 */ - private 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 = 10.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; @@ -159,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)); @@ -183,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 1b2d5c549b1e5e1000ba97218f99cb06b5351f99..faad1c015d03d0f7f28df70927fe94dea245fccb 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,11 +7,14 @@ import org.opencv.core.Point; /** * “将两条线合并为一根折线”时的结果。包括该折线的“两端”、“线段” + * + * 最终作为Relation的一个属性 */ 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/java/com/hy/java/uct/umlrecog/util/Rectangle.java b/src/main/java/com/hy/java/uct/umlrecog/util/Rectangle.java index fd5766365736bb27a98fdd1b839530638997436e..f374ad620940da72aeacf55fdf1f6bd24c7fb995 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/Relation.java b/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java new file mode 100644 index 0000000000000000000000000000000000000000..9d2edd8900a0a12500e550c39aa9727d068357ed --- /dev/null +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Relation.java @@ -0,0 +1,17 @@ +package com.hy.java.uct.umlrecog.util; + +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/java/com/hy/java/uct/umlrecog/util/UMLClass.java b/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java index 5b2e173a7d7fb83fdd474b0c3cd91954e49bd965..bbbd1378ed406b12f30a0af609c0bc10364c319b 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,14 +4,22 @@ 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; + public List relas = new ArrayList<>(); public String getTitle() { return title; diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index 257dd37729af022763690fb2b4bbc111e1ca4820..4e790beef5a46e348376e6323133972ccda356e7 100644 Binary files a/src/main/resources/cd/temp result.png and b/src/main/resources/cd/temp result.png differ 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 fc7e8db30065dca7ae7ce354857d67fc73338d60..d14aaa742f650b6d1df7bb6db5706c8ace88c92e 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);