diff --git a/src/main/java/com/hy/java/uct/umlrecog/SequenceDiagramRecognizer.java b/src/main/java/com/hy/java/uct/umlrecog/SequenceDiagramRecognizer.java index d25d6847497fa88b9455be71fb39fbfa36e24632..4093a0f3dc9be406fbbf3ada4ed6509f3e5440ab 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/SequenceDiagramRecognizer.java +++ b/src/main/java/com/hy/java/uct/umlrecog/SequenceDiagramRecognizer.java @@ -5,10 +5,10 @@ import java.util.List; import org.opencv.core.Core; import org.opencv.core.Mat; -import com.hy.java.uct.umlrecog.cddetector.ClassDetector; -import com.hy.java.uct.umlrecog.cddetector.ClassRelationDetector; -import com.hy.java.uct.umlrecog.util.Relation; -import com.hy.java.uct.umlrecog.util.UMLClass; +import com.hy.java.uct.umlrecog.sddetector.MessageDetector; +import com.hy.java.uct.umlrecog.sddetector.ObjectDetector; +import com.hy.java.uct.umlrecog.util.Message; +import com.hy.java.uct.umlrecog.util.UMLObject; import com.hy.java.utility.common.FileEditor; import com.hy.java.utility.common.Pair; import com.hy.java.utility.common.Traverser; @@ -18,33 +18,33 @@ public class SequenceDiagramRecognizer { private static String temp_res_path = null; - public static void recog(String cd_dir, String repo_name) { - // 遍历cd_dir下的文件,寻找repo_name对应的图片,存在repo_cd_path中 - String repo_cd_path = findCD(cd_dir, repo_name); - if (repo_cd_path != null) { + public static void recog(String sd_dir, String repo_name) { + // 遍历cd_dir下的文件,寻找repo_name对应的图片,存在repo_sd_path中 + String repo_sd_path = findSD(sd_dir, repo_name); + if (repo_sd_path != null) { // 导入OpenCV库,开始识别 System.loadLibrary(Core.NATIVE_LIBRARY_NAME); /* - * 识别类图中的类 + * 识别顺序图中的对象 */ - ClassDetector cls_detector = new ClassDetector(repo_cd_path); - cls_detector.recog(); - Pair> classes = cls_detector.getResult(); + ObjectDetector obj_detector = new ObjectDetector(repo_sd_path); + obj_detector.recog(); + Pair> objects = obj_detector.getResult(); /* - * 识别类图中的关系 + * 识别顺序图中的消息 */ - ClassRelationDetector cls_relation_detector = new ClassRelationDetector(repo_cd_path, classes); - cls_relation_detector.recog(); - Pair> classes_with_relations = cls_relation_detector.getResult(); + MessageDetector obj_msg_detector = new MessageDetector(repo_sd_path, objects); + obj_msg_detector.recog(); + Pair> classes_with_relations = obj_msg_detector.getResult(); /* * 目前classes_with_relations中包含所有类。每个类包含这些信息:类名、属性、方法;关系(分为in和out) * * 将所有类整合为一个txt,以每个类为划分,包含每个类的信息 */ // 图包路径+项目名称+.txt - FileEditor fe = new FileEditor(cd_dir + repo_name.replaceAll("/", "_") + ".txt"); - List class_list = classes_with_relations.getRight(); - for (UMLClass UML_class : class_list) { + FileEditor fe = new FileEditor(sd_dir + repo_name.replaceAll("/", "_") + ".txt"); + List class_list = classes_with_relations.getRight(); + for (UMLObject UML_class : class_list) { // 想想写的格式!!!!!!!!! // 想想写的格式!!!!!!!!! // 想想写的格式!!!!!!!!! @@ -56,12 +56,10 @@ public class SequenceDiagramRecognizer { // 想想写的格式!!!!!!!!! // 比如@、#、¥、%等分隔符等级 fe.write(UML_class.getTitle() + "@", true); - fe.write(UML_class.getAttrisStr() + "@", true); - fe.write(UML_class.getMethodsStr() + "@", true); - for (Relation out_rel : UML_class.out_relas) { + for (Message out_rel : UML_class.out_msgs) { } - for (Relation in_rel : UML_class.in_relas) { + for (Message in_rel : UML_class.in_msgs) { } } @@ -74,25 +72,25 @@ public class SequenceDiagramRecognizer { */ } else { - System.err.println("不存在" + repo_name + "的类图"); + System.err.println("不存在" + repo_name + "的顺序图"); } } - private static String findCD(String cd_dir, String repo_name) { + private static String findSD(String sd_dir, String repo_name) { String result = null; // 用repo_name去查找图片时所使用的字符串 - String search_string = cd_dir + "cd-" + repo_name.replaceAll("/", "_"); + String search_string = sd_dir + "sd-" + repo_name.replaceAll("/", "_"); // 遍历cd_dir下的文件 - List cd_dir_files = Traverser.traverseDir(cd_dir).children; - for (FileNode cd_dir_file : cd_dir_files) { + List sd_dir_files = Traverser.traverseDir(sd_dir).children; + for (FileNode sd_dir_file : sd_dir_files) { // 过滤掉result.txt文件,只找图片 - if (cd_dir_file.path.equals(cd_dir + "result.txt")) { + if (sd_dir_file.path.equals(sd_dir + "result.txt")) { continue; } // 针对图片名,去掉后缀后,与search_string做匹配 - if (search_string.equals(cd_dir_file.path.substring(0, cd_dir_file.path.lastIndexOf(".")))) { + if (search_string.equals(sd_dir_file.path.substring(0, sd_dir_file.path.lastIndexOf(".")))) { // 如果匹配成功,则保存图片路径 - result = cd_dir_file.path; + result = sd_dir_file.path; // 找到指定图片后就可以结束查找了 break; } 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 dfdbf4bc27710983030b4c007e81774f6a790fbc..c2b4740fb2f639561a37c9c7ddaea7d0cffe97cd 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/UMLDiagramRecognizer.java +++ b/src/main/java/com/hy/java/uct/umlrecog/UMLDiagramRecognizer.java @@ -17,23 +17,37 @@ public class UMLDiagramRecognizer { /** * 识别目录下指定的类图并将结果存为文件 + * + * @param check */ - public static void recogCD(String cd_dir, String repo_name) { - ClassDiagramRecognizer.recog(cd_dir, repo_name); + public static void recogCD(String cd_dir, String repo_name, boolean todo) { + if (todo) { + ClassDiagramRecognizer.recog(cd_dir, repo_name); + } } /** * 识别目录下指定的顺序图并将结果存为文件 + * + * @param todo */ - public static void recogSD(String sd_dir, String repo_name) { - SequenceDiagramRecognizer.recog(sd_dir, repo_name); + public static void recogSD(String sd_dir, String repo_name, boolean todo) { + if (todo) { + SequenceDiagramRecognizer.recog(sd_dir, repo_name); + } } /* * 测试一下识别特定的类图和顺序图 */ public static void main(String[] args) { - UMLDiagramRecognizer.recogCD(cd_dir, "Salaboy/smart-tasks"); - UMLDiagramRecognizer.recogSD(sd_dir, "albanoj2/grp"); + // 类图实验 + UMLDiagramRecognizer.recogCD(cd_dir, "rodrigoazevedomartins/TrabalhoFinalLTPIII", false); + UMLDiagramRecognizer.recogCD(cd_dir, "ultragdb/org.eclipse.cdt", false); + UMLDiagramRecognizer.recogCD(cd_dir, "badqiu/rapid-framework", false); + UMLDiagramRecognizer.recogCD(cd_dir, "C204-242-DJSMT/Assignment-1", false); + UMLDiagramRecognizer.recogCD(cd_dir, "cscfa/bartleby", false); + // 顺序图实验 + UMLDiagramRecognizer.recogSD(sd_dir, "coinvent/coinvent", true); } } 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 103d8256e2f4689be6d17e81007f0b8162ebc3be..7756c410c8e29a737b162137eb8e5eea89777835 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 @@ -41,7 +41,7 @@ public class ClassDetector { // 高斯锐化,提升类图图形清晰度 // Imgproc.Laplacian(mat, mat, 2); // 二值化,用于后续处理 - Imgproc.threshold(mat, mat, 160, 255, Imgproc.THRESH_BINARY); + Imgproc.threshold(mat, mat, 150, 255, Imgproc.THRESH_BINARY); /* * 识别类 */ @@ -98,6 +98,38 @@ public class ClassDetector { } // 对所有矩形边框进行涂白,防止其干扰关系符号和关系线识别 Imgproc.drawContours(cls_diagram, rect_contours, -1, new Scalar(255, 255, 255), 5); + // 由于灰度问题,此时可能会有遗留矩形,所以重新检测一遍 + contours.clear(); + rect_contours.clear(); + Imgproc.findContours(cls_diagram, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); + for (MatOfPoint contour : contours) { + // 如果轮廓面积太小或太大,则直接忽略 + double contour_area = Imgproc.contourArea(contour); + if (contour_area < min_cls_area || contour_area > max_cls_area) { + continue; + } + // 如果轮廓面积合适,则检测是否为矩形。采用多边形逼近法,将轮廓转化为curve向多边形做逼近,并将逼近结果存在approx_curve中 + MatOfPoint2f curve = new MatOfPoint2f(contour.toArray()); + MatOfPoint2f approx_curve = new MatOfPoint2f(); + Imgproc.approxPolyDP(curve, approx_curve, 0.05 * Imgproc.arcLength(curve, false), true); + Imgproc.approxPolyDP(approx_curve, approx_curve, 0.01 * Imgproc.arcLength(approx_curve, true), true); + // 针对逼近结果approx_curve,若其共有4个顶点,则可认为是矩形。将检测结果绘制在原图中,便于后续处理 + if (approx_curve.toArray().length == 4) { + // 将矩形存在all_rect_areas中用于类的整合和文字识别。存完后将其从原图中抹掉,防止其干扰关系符号和关系线识别 + all_rect_areas.add(new Rectangle(cls_diagram.clone(), contour, approx_curve)); + rect_contours.add(contour); + // 存完后将矩形从图中抹掉(涂白)。后面还需对所有边框进行涂白 + Imgproc.fillConvexPoly(cls_diagram, contour, new Scalar(255, 255, 255)); + // 可以不涂白,看一下识别结果。最后注释掉 + // Imgproc.fillConvexPoly(cls_diagram, contour, new Scalar(55, 55, 55)); + // Imgcodecs.imwrite(temp_res_path, cls_diagram); + // System.out.println(approx_curve.dump()); + } + } + // 对所有矩形边框进行涂白,防止其干扰关系符号和关系线识别 + Imgproc.drawContours(cls_diagram, rect_contours, -1, new Scalar(255, 255, 255), 5); + // 看一眼,最后注释掉 + // Imgcodecs.imwrite(temp_res_path, cls_diagram); // 此时的cls_diagram中矩形已涂白 return Pair.createPair(cls_diagram, all_rect_areas); } @@ -121,13 +153,13 @@ public class ClassDetector { for (int all_rect_index = 0; all_rect_index < rect_area_list.size(); all_rect_index++) { Rectangle current_rect = rect_area_list.get(all_rect_index); // 如果当前rect已经属于某个类,则跳过它 - if (current_rect.within_class) { + if (current_rect.within_cls_obj) { continue; } // 如果当前rect不属于任何类,则将其赋给一个新的类 UMLClass uml_class = new UMLClass(); uml_class.list.add(current_rect); - current_rect.within_class = true; + current_rect.within_cls_obj = true; // 这个temp_rect用于每次拼接之后更新类的总大小。实际每次都是用temp_rect与其他未拼接的类作比较 uml_class.temp_rect = current_rect.clone(); // 对当前类的temp_rect,与all_rect_areas中其他所有矩形做比较 @@ -141,7 +173,7 @@ public class ClassDetector { if (uml_class.temp_rect.tl().y < other_rect.tl().y) { if (other_rect.tl().y - uml_class.temp_rect.tl().y - uml_class.temp_rect.height <= 5) { uml_class.list.add(other_rect); - other_rect.within_class = true; + other_rect.within_cls_obj = true; // 更新类的temp_rect uml_class.temp_rect.height += other_rect.height; } @@ -150,7 +182,7 @@ public class ClassDetector { else { if (uml_class.temp_rect.tl().y - other_rect.tl().y - other_rect.height <= 5) { uml_class.list.add(other_rect); - other_rect.within_class = true; + other_rect.within_cls_obj = true; // 更新类的temp_rect uml_class.temp_rect.x = other_rect.x; uml_class.temp_rect.y = other_rect.y; 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 bb0b4127823b56fd99e5c28ab7e5967843e2111f..9e8787bf99d05226946b2cb2073acf13ec505767 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 @@ -40,7 +40,7 @@ public class ClassRelationDetector { Set solid_lines = detectLines(classes_in_cd, 0.0504, true); // 再检测虚线。虚线检测除了间隔设置与实线不同外,其他完全一样 Set dash_lines = detectLines(classes_in_cd, 0.0504, false); - // 再检测关系类型检测。由于会遍历所有关系,所以顺便将关系存在classes_in_cd中每个UMLClass的关系列表中 + // 再检测关系类型。由于会遍历所有关系,所以顺便将关系存在classes_in_cd中每个UMLClass的关系列表中 result = detectRelationType(classes_in_cd, solid_lines, dash_lines, 0.0001); } @@ -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; } } @@ -132,7 +133,7 @@ public class ClassRelationDetector { /* * 对所有实线,将可能的“折线”识别并合并。合并为折线的直线,其belonged_polygonal_line会标识其所属折线 */ - Set poly_lines = mergePolygonalLines(line_segments); + Set poly_lines = mergePolygonalLines(line_segments, cls_diagram); /* * 利用UML_classes的位置,进一步筛选关系线,存在result中 * @@ -163,7 +164,8 @@ public class ClassRelationDetector { // 实线识别结果 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 + ")"); + // 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 : rela.poly_line.line_list) { Imgproc.line(cls_diagram, segment.pt1, segment.pt2, new Scalar(255, 255, 255), 5); @@ -177,7 +179,8 @@ public class ClassRelationDetector { // 虚线识别结果 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 + ")"); + // 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 : rela.poly_line.line_list) { Imgproc.line(cls_diagram, segment.pt1, segment.pt2, new Scalar(255, 255, 255), 5); @@ -227,9 +230,13 @@ public class ClassRelationDetector { /** * 将可能的“折线”识别并合并 + * + * @param cls_diagram */ - private Set mergePolygonalLines(List lines) { + private Set mergePolygonalLines(List lines, Mat cls_diagram) { Set result = new HashSet<>(); + Set init_result = new HashSet<>(); + Set temp_result = new HashSet<>(); // 从第一条线开始,检查当前线与每条后面的线是否能合并 for (int i = 0; i < lines.size(); i++) { Line line = lines.get(i); @@ -239,7 +246,7 @@ public class ClassRelationDetector { if (line.belonged_poly == null) { // 将当前直线作为一条新折线的一段 line.belonged_poly = new PolygonalLine(line); - result.add(line.belonged_poly); + init_result.add(line.belonged_poly); } // 检查当前线与每条后面的线是否能合并 for (int j = i + 1; j < lines.size(); j++) { @@ -298,8 +305,25 @@ public class ClassRelationDetector { } } } + // 清理过长的线(比如边框线) + for (PolygonalLine pl : init_result) { + boolean should_be_del = false; + for (Line l : pl.line_list) { + if (l.length >= 0.6 * cls_diagram.width() || l.length >= 0.6 * cls_diagram.height()) { + should_be_del = true; + break; + } + } + if (!should_be_del) { + temp_result.add(pl); + } + } // 设置每条折线的两端。同时记录两端的点所在的线段 - for (PolygonalLine pl : result) { + for (PolygonalLine pl : temp_result) { + // 看一下每条折线,最后注释掉 + /* + * for (Line l : pl.line_list) { Imgproc.line(cls_diagram, l.pt1, l.pt2, new Scalar(55, 55, 55), 5); } Imgcodecs.imwrite(temp_res_path, cls_diagram); + */ for (Line l : pl.line_list) { Point pt = l.notInPolyPt(); if (pt != null) { @@ -323,6 +347,12 @@ public class ClassRelationDetector { } } } + // 此时仍有折线两端都是null,剔除掉 + for (PolygonalLine pl : temp_result) { + if (pl.pt1 != null || pl.pt2 != null) { + result.add(pl); + } + } return result; } @@ -393,7 +423,8 @@ public class ClassRelationDetector { dash_rela.source.out_relas.add(dash_rela); dash_rela.target.in_relas.add(dash_rela); break; - } else if (rect_containing_rela_type.contains(dash_rela.poly_line.pt2) || rect_containing_rela_type.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 5, false)) + } else if (rect_containing_rela_type.contains(dash_rela.poly_line.pt2) + || rect_containing_rela_type.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 5, false)) || rect_containing_rela_type.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 5, true))) { belong_to_dash_rela = true; // 重新设置虚线的source和target。由于目前虚线的端点pt2必须对应target类,所以如果虚线的原source类对应了pt2,则需对换source和target @@ -421,7 +452,8 @@ public class ClassRelationDetector { // 记录该包络矩形是否属于当前实线关系线solid_rela。如果是的话,再去检测矩形内图形 boolean belong_to_this_solid_rela = false; // 方法同对dash_rela的检测。对线的每个端点,检查其本身、延伸、反向延伸这三个点是否被包含于包络矩形中。 - if (rect_containing_rela_type.contains(solid_rela.poly_line.pt1) || rect_containing_rela_type.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt1, solid_rela.poly_line.l1, 5, false)) + if (rect_containing_rela_type.contains(solid_rela.poly_line.pt1) + || rect_containing_rela_type.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt1, solid_rela.poly_line.l1, 5, false)) || rect_containing_rela_type.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt1, solid_rela.poly_line.l1, 5, true))) { // 包络矩形确实包含关系线的端点pt1 belong_to_this_solid_rela = true; @@ -432,7 +464,8 @@ public class ClassRelationDetector { solid_rela.target = temp; } solid_rela.source_pt_index = 2; - } else if (rect_containing_rela_type.contains(solid_rela.poly_line.pt2) || rect_containing_rela_type.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt2, solid_rela.poly_line.l2, 5, false)) + } else if (rect_containing_rela_type.contains(solid_rela.poly_line.pt2) + || rect_containing_rela_type.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt2, solid_rela.poly_line.l2, 5, false)) || rect_containing_rela_type.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类,所以如果虚线的原source类对应了pt2,则需对换source和target @@ -445,7 +478,7 @@ public class ClassRelationDetector { } // 目前该关系符号确实属于当前实线关系solid_rela,所以需检查其是聚合还是继承 if (belong_to_this_solid_rela) { - String type = "聚合"; + String type = "依赖"; // 裁剪原图中包含关系符号的矩形区域 Mat cutted_origin_rela_type_area = ImgProcessor.cutImage(origin_cls_diagram, rect_containing_rela_type); Imgproc.erode(cutted_origin_rela_type_area, cutted_origin_rela_type_area, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2)), new Point(-1, -1), 2); @@ -474,7 +507,8 @@ public class ClassRelationDetector { target_line = solid_rela.poly_line.l2; } // 如果rect_possible_containing_ext包含当前关系线solid_rela的target端点或延长,则就是继承关系了 - if (rect_possible_containing_ext.contains(target_point) || rect_possible_containing_ext.contains(ImgProcessor.reachPt(target_point, target_line, 5, false)) || rect_possible_containing_ext.contains(ImgProcessor.reachPt(target_point, target_line, 5, true))) { + if (rect_possible_containing_ext.contains(target_point) || rect_possible_containing_ext.contains(ImgProcessor.reachPt(target_point, target_line, 5, false)) + || rect_possible_containing_ext.contains(ImgProcessor.reachPt(target_point, target_line, 5, true))) { type = "继承"; } /* diff --git a/src/main/java/com/hy/java/uct/umlrecog/sddetector/MessageDetector.java b/src/main/java/com/hy/java/uct/umlrecog/sddetector/MessageDetector.java index 5f1ab5016551af3c4883dd0bb71aa920f5eb81f3..7a2fb22e83f39170268d0084f051cb682f99eaee 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/sddetector/MessageDetector.java +++ b/src/main/java/com/hy/java/uct/umlrecog/sddetector/MessageDetector.java @@ -1,14 +1,12 @@ package com.hy.java.uct.umlrecog.sddetector; +import java.io.File; import java.util.ArrayList; -import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; 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; @@ -16,491 +14,241 @@ import org.opencv.core.Size; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; -import com.hy.java.uct.umlrecog.cddetector.ClassRelationDetector; +import com.hy.java.uct.umlrecog.UMLDiagramRecognizer; 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.Relation; -import com.hy.java.uct.umlrecog.util.UMLClass; +import com.hy.java.uct.umlrecog.util.Message; +import com.hy.java.uct.umlrecog.util.UMLObject; import com.hy.java.utility.common.Pair; +import net.sourceforge.tess4j.ITesseract; +import net.sourceforge.tess4j.Tesseract; +import net.sourceforge.tess4j.TesseractException; + public class MessageDetector { - private String cd_path = null; + private String sd_path = null; private String temp_res_path = null; - private Pair> classes_in_cd = null; - private Pair> result = null; + private Pair> objects_in_sd = null; + private Pair> result = null; - public MessageDetector(String repo_cd_path, Pair> classes) { - this.cd_path = repo_cd_path; - this.temp_res_path = cd_path.replaceAll(cd_path.substring(cd_path.lastIndexOf("\\") + 1, cd_path.lastIndexOf(".")), "temp result"); - this.classes_in_cd = classes; + public MessageDetector(String repo_sd_path, Pair> objects) { + this.sd_path = repo_sd_path; + this.temp_res_path = sd_path.replaceAll(sd_path.substring(sd_path.lastIndexOf("\\") + 1, sd_path.lastIndexOf(".")), "temp result"); + this.objects_in_sd = objects; } public void recog() { - // 先检测实线。检测结果需借助类的位置关系、去除“单纯的线” - Set solid_lines = detectLines(classes_in_cd, 0.0504, true); + // 先检测实线。检测结果需借助对象的位置关系、去除“单纯的线” + Set solid_msgs = detectLines(objects_in_sd, 0.0504, true); // 再检测虚线。虚线检测除了间隔设置与实线不同外,其他完全一样 - Set dash_lines = detectLines(classes_in_cd, 0.0504, false); - // 再检测关系类型检测。由于会遍历所有关系,所以顺便将关系存在classes_in_cd中每个UMLClass的关系列表中 - result = detectRelationType(classes_in_cd, solid_lines, dash_lines, 0.0001); + // Set dash_msgs = detectLines(objects_in_sd, 0.0504, false); + Set dash_msgs = null; + // 合并实线和虚线识别结果。对所有UMLObject设置out_relas和in_relas + result = mergeMessages(objects_in_sd, solid_msgs, dash_msgs); } - private Set detectLines(Pair> classes_in_cd, double ratio, boolean detect_solid) { - Set result = new HashSet<>(); + private Set detectLines(Pair> objects_in_sd, double ratio, boolean detect_solid) { + Set result = new HashSet<>(); + List temp_result = new ArrayList<>(); if (detect_solid) { - System.out.println("开始识别" + cd_path + "中的实线"); + System.out.println("开始识别" + sd_path + "中的实线"); } else { - System.out.println("开始识别" + cd_path + "中的虚线"); + System.out.println("开始识别" + sd_path + "中的虚线"); } - Mat cls_diagram = classes_in_cd.getLeft(); - List UML_classes = classes_in_cd.getRight(); + Mat sd_diagram = objects_in_sd.getLeft(); + Mat origin_sd_diagram = Imgcodecs.imread(sd_path, Imgcodecs.IMREAD_GRAYSCALE); + List UML_objects = objects_in_sd.getRight(); /* * 先检测边缘,然后从边缘集中初步检测“原始直线” */ List line_segments = new ArrayList<>(); // 先检测边缘。然后从边缘集中检测直线 Mat edges = new Mat(); - Imgproc.Canny(cls_diagram, edges, 50, 50 * 3, 3, true); + Imgproc.Canny(sd_diagram, edges, 50, 50 * 3, 3, true); // 从边缘集中检测直线。膨胀(将线外面的空白区域膨胀)、腐蚀(让线侵蚀周围的空白区域),可以提高识别准确率 - Imgproc.dilate(cls_diagram, cls_diagram, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1.7, 1.7)), new Point(-1, -1), 2); - Imgproc.erode(cls_diagram, cls_diagram, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1)), new Point(-1, -1), 1); + Imgproc.dilate(sd_diagram, sd_diagram, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1.7, 1.7)), new Point(-1, -1), 2); + Imgproc.erode(sd_diagram, sd_diagram, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1)), new Point(-1, -1), 1); Mat lines = new Mat(); if (detect_solid) { // 实线检测时,“最小直线长度”与图像像素有关;“最大像素间隔”越小越接近实线 - Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 8, 29, 2); + Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 2, 8, 29, 1); } else { - Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, 8, 29, 10); + Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 2, 8, 29, 8); } // 保存所有刚检测出来的“原始直线” for (int i = 0; i < lines.rows(); i++) { line_segments.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]))); } + // 看一下效果,最后注释掉 /* - * 检查“原始直线”集中是否有重合直线,去除冗余或合并 + * for (Line l : line_segments) { Imgproc.line(sd_diagram, l.pt1, l.pt2, new Scalar(25, 25, 25), 5); Imgcodecs.imwrite(temp_res_path, sd_diagram); } */ - int current_size = line_segments.size(); - for (int i = 0; i < current_size; i++) { - Line line = line_segments.get(i); - // 将line与other_line做比较,标记出所有与当前直线重合、或需合并的直线 - for (int j = i + 1; j < current_size; j++) { - Line other_line = line_segments.get(j); - // 下列情况算为一条线:“两端都接近”;或其中一端接近、且两直线斜率接近;或有一部分重合 - if (line.isCoincideWith(other_line)) { - other_line.should_be_del = true; - } + /* + * 现在所有线已识别出来 + * + * 首先找出斜率为0的每条直线 + */ + List lines_k0 = new ArrayList<>(); + for (Line l : line_segments) { + if (Line.getK(l) == "无穷大") { + continue; } - // 去除冗余;合并同斜率 - for (int j = i + 1; j < current_size;) { - Line other_line = line_segments.get(j); - if (other_line.should_be_del) { - // 将两条直线合并为一条长直线,存在line中 - line_segments.set(i, mergeCoincideLines(line, other_line)); - // 去除冗余 - line_segments.remove(other_line); - current_size = line_segments.size(); - } else { - j++; - } + if (Double.parseDouble(Line.getK(l)) <= 0.1) { + lines_k0.add(l); } } - // 此时可以还有“斜率接近,但端点不接近”的线段,如果这种线段间距很小,则也合并 - current_size = line_segments.size(); - for (int i = 0; i < current_size; i++) { - Line line = line_segments.get(i); - // 将line与other_line做比较 - 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)) { - other_line.should_be_del = true; - } - } + // 针对斜率为0的直线,如果两条直线y坐标相差像素较少,则合并为一条线(顺序图特点:同一行上只能有一条消息线) + List possible_message_lines = new ArrayList<>(); + int lines_k0_size = lines_k0.size(); + for (int i = 0; i < lines_k0_size; i++) { + Line l = lines_k0.get(i); + // 看一下效果,最后注释掉 + // Imgproc.line(sd_diagram, l.pt1, l.pt2, new Scalar(25, 25, 25), 5); + // Imgcodecs.imwrite(temp_res_path, sd_diagram); + if (l.should_be_del) { + continue; } - // 去除冗余;合并同斜率 - for (int j = i + 1; j < current_size;) { - Line other_line = line_segments.get(j); - if (other_line.should_be_del) { - // 将两条直线合并为一条长直线,存在line中 - line_segments.set(i, mergeCoincideLines(line, other_line)); - // 去除冗余 - line_segments.remove(other_line); - current_size = line_segments.size(); - } else { - j++; + for (int j = i + 1; j < lines_k0_size; j++) { + Line other_l = lines_k0.get(j); + // 记录l的左右端点 + Point l_left_pt = l.pt1.x < l.pt2.x ? l.pt1 : l.pt2; + Point l_right_pt = l.pt1.x > l.pt2.x ? l.pt1 : l.pt2; + // 记录other_l的左右端点 + Point other_l_left_pt = other_l.pt1.x < other_l.pt2.x ? other_l.pt1 : other_l.pt2; + Point other_l_right_pt = other_l.pt1.x > other_l.pt2.x ? other_l.pt1 : other_l.pt2; + if (Math.abs(l_left_pt.y - other_l_left_pt.y) <= 4) { + other_l.should_be_del = true; + Point merged_l_left_pt = l_left_pt.x < other_l_left_pt.x ? l_left_pt : other_l_left_pt; + Point merged_l_right_pt = l_right_pt.x > other_l_right_pt.x ? l_right_pt : other_l_right_pt; + l = new Line(merged_l_left_pt, merged_l_right_pt); } } + possible_message_lines.add(l); } + // 看一下效果,最后注释掉 /* - * 对所有实线,将可能的“折线”识别并合并。合并为折线的直线,其belonged_polygonal_line会标识其所属折线 + * for (Line l : possible_message_lines) { Imgproc.line(sd_diagram, l.pt1, l.pt2, new Scalar(25, 25, 25), 5); Imgcodecs.imwrite(temp_res_path, sd_diagram); } */ - 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; + // 然后对possible_message_lines中的每条线,在原图中切下包含该直线的部分,单独识别。将两端坐标与每个对象做比较,设置source、target + for (Line l : possible_message_lines) { + // 获取l的包络矩形 + Point left_pt = l.pt1.x < l.pt2.x ? l.pt1 : l.pt2; + // 注意y坐标的变换、矩形高度设置,将影响后续识别。切的太多太少都不行 + Rect rect = new Rect(0, (int) left_pt.y - 17, sd_diagram.width(), 22); + Mat cutted_img = ImgProcessor.cutImage(origin_sd_diagram, rect); + // 可看到截取原图中包含关系线,最后注释掉 + Imgcodecs.imwrite(temp_res_path, cutted_img); + Mat edges_in_cutted = new Mat(); + Imgproc.Canny(cutted_img, edges_in_cutted, 50, 50 * 3, 3, true); + // 从边缘集中检测直线。膨胀(将线外面的空白区域膨胀)、腐蚀(让线侵蚀周围的空白区域),可以提高识别准确率 + Mat lines_in_cutted = new Mat(); + if (detect_solid) { + // 实线检测时,“最小直线长度”与图像像素有关;“最大像素间隔”越小越接近实线 + Imgproc.HoughLinesP(edges_in_cutted, lines_in_cutted, 1, Math.PI / 2, 8, 29, 1); + } else { + Imgproc.HoughLinesP(edges_in_cutted, lines_in_cutted, 1, Math.PI / 2, 8, 29, 8); + } + List line_segments_in_cutted = new ArrayList<>(); + // 保存所有刚检测出来的“原始直线” + for (int i = 0; i < lines_in_cutted.rows(); i++) { + line_segments_in_cutted.add(new Line(new Point(lines_in_cutted.get(i, 0)[0], lines_in_cutted.get(i, 0)[1]), new Point(lines_in_cutted.get(i, 0)[2], lines_in_cutted.get(i, 0)[3]))); + } + // 目前line_segments_in_cutted里的“原始直线”都画完后,一定包含原关系线。现在需找出真正的那条线、舍去其他的 + // 方法:看line_segments_in_cutted里的线是否与其他对象有关系。如果有,则设置source和target + for (Line l_in_cutted : line_segments_in_cutted) { + // 由于cutted的图中的线的坐标与原图坐标不同,所以需将在cutted图中识别的线坐标转换为原图中的线坐标 + Line l_converted_to_uncutted = new Line(new Point(l_in_cutted.pt1.x, l_in_cutted.pt1.y + left_pt.y - 17), new Point(l_in_cutted.pt2.x, l_in_cutted.pt2.y + left_pt.y - 17)); + // 看一下效果,最后注释掉 + // Imgproc.line(sd_diagram, l_converted_to_uncutted.pt1, l_converted_to_uncutted.pt2, new Scalar(25, 25, 25), 5); + // Imgcodecs.imwrite(temp_res_path, sd_diagram); + // 检测线与对象间的位置关系 + Point uncutted_left_pt = l_converted_to_uncutted.pt1.x < l_converted_to_uncutted.pt2.x ? l_converted_to_uncutted.pt1 : l_converted_to_uncutted.pt2; + Point uncutted_right_pt = l_converted_to_uncutted.pt1.x > l_converted_to_uncutted.pt2.x ? l_converted_to_uncutted.pt1 : l_converted_to_uncutted.pt2; + for (UMLObject UML_object : UML_objects) { + if (Math.abs(UML_object.whole.x + UML_object.whole.width / 2 - uncutted_left_pt.x) < UML_object.whole.width / 4) { + boolean found_target = false; + for (UMLObject possible_target_UML_object : UML_objects) { + if (Math.abs(possible_target_UML_object.whole.x + possible_target_UML_object.whole.width / 2 - uncutted_right_pt.x) < possible_target_UML_object.whole.width / 4) { + Message msg = new Message(l_converted_to_uncutted); + msg.source = UML_object; + msg.target = possible_target_UML_object; + // 识别文字 + ITesseract instance = new Tesseract(); + instance.setDatapath(UMLDiagramRecognizer.tessdata_path); + try { + // 注意这里用的就是刚写入的cutted_img + msg.msg = instance.doOCR(new File(temp_res_path)); + } catch (TesseractException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 识别完文字、添加进结果,就完事了。现在关系识别正确率100% + temp_result.add(msg); + found_target = true; + break; + } + } + if (found_target) { + break; + } + } } } - if (rela.source != null && rela.target != null) { - result.add(rela); + } + // 目前所有关系已识别出来,但可能有重复。去除一下冗余 + int temp_result_size = temp_result.size(); + for (int i = 0; i < temp_result_size; i++) { + Message msg = temp_result.get(i); + // 看一下效果,最后注释掉 + // Imgproc.line(sd_diagram, msg.line.pt1, msg.line.pt2, new Scalar(25, 25, 25), 5); + // Imgproc.putText(sd_diagram, msg.msg, msg.line.pt1, 5, 5, new Scalar(25, 25, 25)); + // Imgcodecs.imwrite(temp_res_path, sd_diagram); + if (msg.is_redu) { + continue; + } + for (int j = i + 1; j < temp_result_size; j++) { + Message other_msg = temp_result.get(j); + if (Math.abs(msg.line.pt1.y - other_msg.line.pt1.y) <= 2) { + other_msg.is_redu = true; + } } + result.add(msg); } /* * 在图中抹掉关系线,防止其影响后续识别 + * + * 有可能不需要 */ if (detect_solid) { // 实线识别结果 - 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 (Message msg : result) { // 在图中抹掉已识别的线,防止干扰后续识别 - for (Line segment : rela.poly_line.line_list) { - Imgproc.line(cls_diagram, segment.pt1, segment.pt2, new Scalar(255, 255, 255), 5); - } + Imgproc.line(sd_diagram, msg.line.pt1, msg.line.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(sd_diagram, rela.line.pt1, rela.line.pt2, new Scalar(25, 25, 25), 5); } - Imgcodecs.imwrite(temp_res_path, cls_diagram); - System.out.println("完成对" + cd_path + "中的实线识别,共" + result.size() + "条"); + Imgcodecs.imwrite(temp_res_path, sd_diagram); + System.out.println("完成对" + sd_path + "中的实线识别,共" + result.size() + "条"); } else { // 虚线识别结果 - 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 (Message msg : result) { // 在图中抹掉已识别的线,防止干扰后续识别 - for (Line segment : rela.poly_line.line_list) { - Imgproc.line(cls_diagram, segment.pt1, segment.pt2, new Scalar(255, 255, 255), 5); - } + Imgproc.line(sd_diagram, msg.line.pt1, msg.line.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); - } - Imgcodecs.imwrite(temp_res_path, cls_diagram); - System.out.println("完成对" + cd_path + "中的虚线识别,共" + result.size() + "条"); - } - return result; - } - - /** - * 将斜率接近的两条直线合并成“最长”的一条线,并存在line中 - */ - private Line mergeCoincideLines(Line line, Line other_line) { - // 两条线的4个端点共可能有4C2条直线,把最长的一条存在line中即可 - Line l1 = new Line(line.pt1, line.pt2); - Line l2 = new Line(line.pt1, other_line.pt1); - Line l3 = new Line(line.pt1, other_line.pt2); - Line l4 = new Line(line.pt2, other_line.pt1); - Line l5 = new Line(line.pt2, other_line.pt2); - Line l6 = new Line(other_line.pt1, other_line.pt2); - List lines = new ArrayList<>(); - lines.add(l1); - lines.add(l2); - lines.add(l3); - lines.add(l4); - lines.add(l5); - lines.add(l6); - return MessageDetector.longestLine(lines); - } - - /** - * 返回一列直线中最长的直线 - */ - private static Line longestLine(List lines) { - lines.sort(new Comparator() { - @Override - public int compare(Line l1, Line l2) { - return Integer.compare((int) l1.length, (int) l2.length); - } - }); - return lines.get(lines.size() - 1); - } - - /** - * 将可能的“折线”识别并合并 - */ - private Set mergePolygonalLines(List lines) { - Set result = new HashSet<>(); - // 从第一条线开始,检查当前线与每条后面的线是否能合并 - for (int i = 0; i < lines.size(); i++) { - Line line = lines.get(i); - if (line.pt1_in_polyline && line.pt2_in_polyline) { - continue; - } - if (line.belonged_poly == null) { - // 将当前直线作为一条新折线的一段 - line.belonged_poly = new PolygonalLine(line); - result.add(line.belonged_poly); - } - // 检查当前线与每条后面的线是否能合并 - for (int j = i + 1; j < lines.size(); j++) { - Line other_line = lines.get(j); - Pair pt1_other_line = Line.ptNearLine(line.pt1, other_line); - Pair pt2_other_line = Line.ptNearLine(line.pt2, other_line); - if (pt1_other_line.getLeft() == true) { - if (pt1_other_line.getRight() == other_line.pt1) { - if (Line.ptNearPt(line.pt1, other_line.pt1, false, false, other_line)) { - // 在折线的线段list中添加other_line - line.belonged_poly.line_list.add(other_line); - // 更新line和other_line的端点状态 - line.pt1_in_polyline = true; - other_line.pt1_in_polyline = true; - // 顺便给other_line也存上折线 - other_line.belonged_poly = line.belonged_poly; - } - - } else { - if (Line.ptNearPt(line.pt1, other_line.pt2, false, false, other_line)) { - // 在折线的线段list中添加other_line - line.belonged_poly.line_list.add(other_line); - // 更新line和other_line的端点状态 - line.pt1_in_polyline = true; - other_line.pt2_in_polyline = true; - // 顺便给other_line也存上折线 - other_line.belonged_poly = line.belonged_poly; - } - } - - } - // 同上,只是换一个端点 - else if (pt2_other_line.getLeft() == true) { - if (pt2_other_line.getRight() == other_line.pt1) { - if (Line.ptNearPt(line.pt2, other_line.pt1, false, false, other_line)) { - // 在折线的线段list中添加other_line - line.belonged_poly.line_list.add(other_line); - // 更新line和other_line的端点状态 - line.pt2_in_polyline = true; - other_line.pt1_in_polyline = true; - // 顺便给other_line也存上折线 - other_line.belonged_poly = line.belonged_poly; - } - - } else { - if (Line.ptNearPt(line.pt2, other_line.pt2, false, false, other_line)) { - // 在折线的线段list中添加other_line - line.belonged_poly.line_list.add(other_line); - // 更新line和other_line的端点状态 - line.pt2_in_polyline = true; - other_line.pt2_in_polyline = true; - // 顺便给other_line也存上折线 - other_line.belonged_poly = line.belonged_poly; - } - } - } - } - } - // 设置每条折线的两端。同时记录两端的点所在的线段 - for (PolygonalLine pl : result) { - for (Line l : pl.line_list) { - Point pt = l.notInPolyPt(); - if (pt != null) { - // 如果该直线两端都“暂时不在折线中”,说明这是根单独的线。将其两端设置为折线两端即可 - 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; - } - } - } + // Imgproc.line(sd_diagram, rela.line.pt1, rela.line.pt2, new Scalar(25, 25, 25), 5); } + Imgcodecs.imwrite(temp_res_path, sd_diagram); + System.out.println("完成对" + sd_path + "中的虚线识别,共" + result.size() + "条"); } return result; } - /** - * 识别关系符号,并根据关系符号与关系线端点的位置关系更新关系线的source和target - * - * @param classes_in_cd - * @param dash_lines - * @param solid_lines - * @param dash_lines - * @return - */ - private Pair> detectRelationType(Pair> classes_in_cd, Set solid_lines, Set dash_lines, double ratio) { - System.out.println("开始识别" + cd_path + "中所有关系符号"); - Mat cls_diagram = classes_in_cd.getLeft(); - Mat origin_cls_diagram = Imgcodecs.imread(cd_path, Imgcodecs.IMREAD_GRAYSCALE); - /* - * 识别图中所有“轮廓”并存在contours中 - */ - // 先腐蚀(让符号像素侵蚀周围空间,得以连接起来)、后膨胀(让点周围的空白区域膨胀,从而去掉孤立的噪点),提高识别准确率 - 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(cls_diagram, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); - /* - * 对每个轮廓contour,检测是否为某种关系符号 - */ - // 根据图片像素计算轮廓面积阈值。如果轮廓面积太小或太大,则直接忽略 - long cd_area = cls_diagram.width() * cls_diagram.height(); - double min_area = cd_area * ratio; - double max_area = cd_area * 0.5; - for (MatOfPoint contour : contours) { - // 如果轮廓面积太小或太大,则直接忽略 - double contour_area = Imgproc.contourArea(contour); - if (contour_area < min_area || contour_area > max_area) { - // 将噪点直接涂白 - 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_containing_rela_type = Imgproc.boundingRect(new MatOfPoint(approx_curve.toArray())); - // 如果包络矩形包含某一条关系线的某一端,则检测该矩形内的符号 - // 对于虚线,由于只可能是实现或生命线,所以只需要知道是否包含即可,不用检测矩形内部图形。belong_to_dash_rela为true表示当前关系符号属于某条虚线关系线 - 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_containing_rela_type.contains(dash_rela.poly_line.pt1) || rect_containing_rela_type.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt1, dash_rela.poly_line.l1, 5, false)) - || rect_containing_rela_type.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类,所以如果虚线的原source类对应了pt1,则需对换source和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 = "实现"; - // 记录当前关系到UMLClass里 - dash_rela.source.out_relas.add(dash_rela); - dash_rela.target.in_relas.add(dash_rela); - break; - } else if (rect_containing_rela_type.contains(dash_rela.poly_line.pt2) || rect_containing_rela_type.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 5, false)) - || rect_containing_rela_type.contains(ImgProcessor.reachPt(dash_rela.poly_line.pt2, dash_rela.poly_line.l2, 5, true))) { - belong_to_dash_rela = true; - // 重新设置虚线的source和target。由于目前虚线的端点pt2必须对应target类,所以如果虚线的原source类对应了pt2,则需对换source和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 = "实现"; - // 记录当前关系到UMLClass里 - dash_rela.source.out_relas.add(dash_rela); - dash_rela.target.in_relas.add(dash_rela); - 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; - } - // 记录该包络矩形是否属于当前实线关系线solid_rela。如果是的话,再去检测矩形内图形 - boolean belong_to_this_solid_rela = false; - // 方法同对dash_rela的检测。对线的每个端点,检查其本身、延伸、反向延伸这三个点是否被包含于包络矩形中。 - if (rect_containing_rela_type.contains(solid_rela.poly_line.pt1) || rect_containing_rela_type.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt1, solid_rela.poly_line.l1, 5, false)) - || rect_containing_rela_type.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类,所以如果虚线的原source类对应了pt1,则需对换source和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_containing_rela_type.contains(solid_rela.poly_line.pt2) || rect_containing_rela_type.contains(ImgProcessor.reachPt(solid_rela.poly_line.pt2, solid_rela.poly_line.l2, 5, false)) - || rect_containing_rela_type.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类,所以如果虚线的原source类对应了pt2,则需对换source和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; - } - // 目前该关系符号确实属于当前实线关系solid_rela,所以需检查其是聚合还是继承 - if (belong_to_this_solid_rela) { - String type = "聚合"; - // 裁剪原图中包含关系符号的矩形区域 - Mat cutted_origin_rela_type_area = ImgProcessor.cutImage(origin_cls_diagram, rect_containing_rela_type); - Imgproc.erode(cutted_origin_rela_type_area, cutted_origin_rela_type_area, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2)), new Point(-1, -1), 2); - Imgproc.dilate(cutted_origin_rela_type_area, cutted_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(cutted_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.05 * Imgproc.arcLength(origin_curve, false), true); - Imgproc.approxPolyDP(origin_approx_curve, origin_approx_curve, 0.01 * Imgproc.arcLength(origin_approx_curve, true), true); - // 如果逼近得到三角形,则基本就是继承了,再检查一下位置即可(其实检不检查作用不大) - if (origin_approx_curve.toArray().length == 3) { - // 获取三角形的包络矩形 - Rect rect_possible_containing_ext = Imgproc.boundingRect(new MatOfPoint(origin_approx_curve.toArray())); - // 检查包络矩形rect_possible_containing_ext是否真的包含当前关系线solid_rela的target端点的延长线。如果是的话,则确定是继承关系 - // 先找到关系线solid_rela的target端点和对应线段。然后检查target端点的延长与包络矩形的关系 - Point target_point = solid_rela.poly_line.pt1; - Line target_line = solid_rela.poly_line.l1; - if (solid_rela.source_pt_index == 1) { - // 如果source端点是1,则target端点是pt2 - target_point = solid_rela.poly_line.pt2; - target_line = solid_rela.poly_line.l2; - } - // 如果rect_possible_containing_ext包含当前关系线solid_rela的target端点或延长,则就是继承关系了 - if (rect_possible_containing_ext.contains(target_point) || rect_possible_containing_ext.contains(ImgProcessor.reachPt(target_point, target_line, 5, false)) || rect_possible_containing_ext.contains(ImgProcessor.reachPt(target_point, target_line, 5, true))) { - type = "继承"; - } - /* - * 看一下实线符号识别情况。可注释掉 - */ - // Imgproc.rectangle(cutted_origin_rela_type_area, rect_possible_containing_ext, new Scalar(5, 5, 5), 3); - // Imgcodecs.imwrite(temp_res_path, cutted_origin_rela_type_area); - } - } - solid_rela.type = type; - // 记录当前关系到result里 - solid_rela.source.out_relas.add(solid_rela); - solid_rela.target.in_relas.add(solid_rela); - // 检测完符号类别后,对当前区域就算检测完了,不用再检测其他实线了 - break; - } - } - // 如果运行到这儿(检测完符号类别后break会直接到这儿),说明对当前区域的包络矩形与实线之间的检测完毕。相当于对当前区域检测完毕,开始检测下一个区域 - } - } - System.out.println("完成对" + cd_path + "中识别关系符号的识别。"); - return classes_in_cd; + private Pair> mergeMessages(Pair> objects_in_sd, Set solid_msgs, Set dash_msgs) { + // TODO Auto-generated method stub + return null; } - public Pair> getResult() { + public Pair> getResult() { return result; } } diff --git a/src/main/java/com/hy/java/uct/umlrecog/sddetector/ObjectDetector.java b/src/main/java/com/hy/java/uct/umlrecog/sddetector/ObjectDetector.java index 56274be3aefd50ba30746df2c363b223fad1f107..2bb03b7f3a69ce7c0b1b77938d39f27759c3433f 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/sddetector/ObjectDetector.java +++ b/src/main/java/com/hy/java/uct/umlrecog/sddetector/ObjectDetector.java @@ -15,7 +15,7 @@ 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.uct.umlrecog.util.UMLObject; import com.hy.java.utility.common.Pair; import net.sourceforge.tess4j.ITesseract; @@ -23,13 +23,13 @@ import net.sourceforge.tess4j.Tesseract; import net.sourceforge.tess4j.TesseractException; public class ObjectDetector { - private String cd_path = null; + private String sd_path = null; private String temp_res_path = null; - private Pair> result = null; + private Pair> result = null; - public ObjectDetector(String repo_cd_path) { - this.cd_path = repo_cd_path; - this.temp_res_path = cd_path.replaceAll(cd_path.substring(cd_path.lastIndexOf("\\") + 1, cd_path.lastIndexOf(".")), "temp result"); + public ObjectDetector(String repo_sd_path) { + this.sd_path = repo_sd_path; + this.temp_res_path = sd_path.replaceAll(sd_path.substring(sd_path.lastIndexOf("\\") + 1, sd_path.lastIndexOf(".")), "temp result"); } public void recog() { @@ -37,36 +37,36 @@ public class ObjectDetector { * 预处理 */ // 读取图片。并灰度处理 - Mat mat = Imgcodecs.imread(cd_path, Imgcodecs.IMREAD_GRAYSCALE); + Mat mat = Imgcodecs.imread(sd_path, Imgcodecs.IMREAD_GRAYSCALE); // 高斯锐化,提升类图图形清晰度 // Imgproc.Laplacian(mat, mat, 2); // 二值化,用于后续处理 Imgproc.threshold(mat, mat, 160, 255, Imgproc.THRESH_BINARY); /* - * 识别类 + * 识别对象 */ // 矩形检测。识别所有矩形区域 - Pair> all_rect_areas_in_cd = detectRectArea(mat, 0.000555); + Pair> all_rect_areas_in_sd = detectRectArea(mat, 0.000555); // 对矩形进行整合,形成类区域 - List classes = mergeIntoClass(all_rect_areas_in_cd); + List objects = mergeIntoObject(all_rect_areas_in_sd); // 对类区域进行文字检测 - result = Pair.createPair(all_rect_areas_in_cd.getLeft(), detectText(Imgcodecs.imread(cd_path, Imgcodecs.IMREAD_GRAYSCALE), classes)); + result = Pair.createPair(all_rect_areas_in_sd.getLeft(), detectText(Imgcodecs.imread(sd_path, Imgcodecs.IMREAD_GRAYSCALE), objects)); } /** * 检测方框 * - * @param cls_diagram - * @param ratio 检测最小矩形占全图面积的比例(0~1之间的一个小数)。如果类图中类的面积很小,则该比例应设的很小。 + * @param s_diagram + * @param ratio 检测最小矩形占全图面积的比例(0~1之间的一个小数)。如果类图中类的面积很小,则该比例应设的很小。 * @return */ - private Pair> detectRectArea(Mat cls_diagram, double ratio) { - System.out.println("开始识别" + cd_path + "中所有矩形"); + private Pair> detectRectArea(Mat s_diagram, double ratio) { + System.out.println("开始识别" + sd_path + "中所有矩形"); /* * 识别图中所有“轮廓”并存在contours中 */ List contours = new ArrayList<>(); - Imgproc.findContours(cls_diagram, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); + Imgproc.findContours(s_diagram, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); /* * 对每个轮廓contour,检测是否为矩形,并将contour和检测结果存在Rectangle中。所有轮廓的检测结果最终存在all_rect_areas中 */ @@ -74,8 +74,8 @@ public class ObjectDetector { // 这个rect_contours里只存那些检测结果为矩形的contour,用于后面边框涂白,防止矩形干扰关系符号和关系线识别 List rect_contours = new ArrayList<>(); // 根据图片像素计算轮廓面积阈值。如果轮廓面积太小或太大,则直接忽略 - long cd_area = cls_diagram.width() * cls_diagram.height(); - double min_cls_area = 60; + long cd_area = s_diagram.width() * s_diagram.height(); + double min_cls_area = 70; double max_cls_area = cd_area * 0.5; for (MatOfPoint contour : contours) { // 如果轮廓面积太小或太大,则直接忽略 @@ -89,172 +89,121 @@ public class ObjectDetector { Imgproc.approxPolyDP(curve, approx_curve, 0.01 * Imgproc.arcLength(curve, true), true); // 针对逼近结果approx_curve,若其共有4个顶点,则可认为是矩形。将检测结果绘制在原图中,便于后续处理 if (approx_curve.toArray().length == 4) { - // 将矩形存在all_rect_areas中用于类的整合和文字识别。存完后将其从原图中抹掉,防止其干扰关系符号和关系线识别 - all_rect_areas.add(new Rectangle(cls_diagram.clone(), contour, approx_curve)); + // 将矩形存在all_rect_areas中用于对象的整合和文字识别。存完后将其从原图中抹掉,防止其干扰关系符号和关系线识别 + all_rect_areas.add(new Rectangle(s_diagram.clone(), contour, approx_curve)); rect_contours.add(contour); // 存完后将矩形从图中抹掉(涂白)。后面还需对所有边框进行涂白 - Imgproc.fillConvexPoly(cls_diagram, contour, new Scalar(255, 255, 255)); + Imgproc.fillConvexPoly(s_diagram, contour, new Scalar(255, 255, 255)); } } // 对所有矩形边框进行涂白,防止其干扰关系符号和关系线识别 - Imgproc.drawContours(cls_diagram, rect_contours, -1, new Scalar(255, 255, 255), 5); - // 此时的cls_diagram中矩形已涂白 - return Pair.createPair(cls_diagram, all_rect_areas); + Imgproc.drawContours(s_diagram, rect_contours, -1, new Scalar(255, 255, 255), 5); + // 可以先不涂白,看下识别结果。最后要注释掉 + // Imgproc.drawContours(s_diagram, rect_contours, -1, new Scalar(0, 0, 0), 5); + // Imgcodecs.imwrite(temp_res_path, s_diagram); + // 此时的s_diagram中矩形已涂白 + return Pair.createPair(s_diagram, all_rect_areas); } - private List mergeIntoClass(Pair> all_rect_areas_in_cd) { - List result = new ArrayList<>(); - // Mat cls_diagram = all_rect_areas_in_cd.getLeft(); + private List mergeIntoObject(Pair> all_rect_areas_in_sd) { + List result = new ArrayList<>(); + // 临时保存所有矩形形成的新对象。后面要对比这些对象的横纵坐标、舍去纵坐标靠下的矩形 + List temp_result = new ArrayList<>(); + // Mat s_diagram = all_rect_areas_in_sd.getLeft(); // 获取所有矩形 - List rect_area_list = all_rect_areas_in_cd.getRight(); + List rect_area_list = all_rect_areas_in_sd.getRight(); /* * 拼接思路: * - * 首先针对当前矩形,将其赋给一个类。记录该类当前拥有的矩形列表。 + * 首先将矩形赋给一个对象。 * - * 然后针对该矩形的左上角,看是否与其他矩形的左上角横坐标相差不超过3~5个像素 + * 然后检测所有矩形横坐标。如果两个矩形横坐标一样,则舍去纵坐标靠下的矩形。 * - * 如果是,则再看两个左上角的纵坐标距离是否大致等于“上面矩形”的高度 - * - * 如果是,则纳入同一个类中 + * 留下的矩形包括:真正的对象(纵坐标可能不同)、生命线上的矩形。后者需在文字识别时剔除 */ for (int all_rect_index = 0; all_rect_index < rect_area_list.size(); all_rect_index++) { Rectangle current_rect = rect_area_list.get(all_rect_index); - // 如果当前rect已经属于某个类,则跳过它 - if (current_rect.within_class) { + // 如果当前rect已经属于某个对象,则跳过它 + if (current_rect.within_cls_obj) { continue; } - // 如果当前rect不属于任何类,则将其赋给一个新的类 - UMLClass uml_class = new UMLClass(); - uml_class.list.add(current_rect); - current_rect.within_class = true; - // 这个temp_rect用于每次拼接之后更新类的总大小。实际每次都是用temp_rect与其他未拼接的类作比较 - uml_class.temp_rect = current_rect.clone(); - // 对当前类的temp_rect,与all_rect_areas中其他所有矩形做比较 - for (int j = all_rect_index + 1; j < rect_area_list.size(); j++) { - // 获取all_rect_areas列表中位于current_rect后面的矩形 - Rectangle other_rect = rect_area_list.get(j); - // 针对该矩形的左上角,看是否与其他矩形的左上角横坐标相差不超过3~5个像素 - if (Math.abs(other_rect.tl().x - uml_class.temp_rect.tl().x) <= 5) { - // 如果是,则再看两个左上角的纵坐标距离是否大致等于“上面矩形”的高度 - // “上面矩形”是temp_rect - if (uml_class.temp_rect.tl().y < other_rect.tl().y) { - if (other_rect.tl().y - uml_class.temp_rect.tl().y - uml_class.temp_rect.height <= 5) { - uml_class.list.add(other_rect); - other_rect.within_class = true; - // 更新类的temp_rect - uml_class.temp_rect.height += other_rect.height; - } - } - // “上面矩形”是other_rect - else { - if (uml_class.temp_rect.tl().y - other_rect.tl().y - other_rect.height <= 5) { - uml_class.list.add(other_rect); - other_rect.within_class = true; - // 更新类的temp_rect - uml_class.temp_rect.x = other_rect.x; - uml_class.temp_rect.y = other_rect.y; - uml_class.temp_rect.height += other_rect.height; - } + // 如果当前rect不属于任何对象,则将其赋给一个新的对象 + UMLObject uml_obj = new UMLObject(); + current_rect.within_cls_obj = true; + uml_obj.whole = current_rect; + temp_result.add(uml_obj); + } + int temp_size = temp_result.size(); + // 检测temp_result中所有矩形横坐标。如果存在多个矩形横坐标一样,则只保留纵坐标最靠上的矩形、舍去其他所有矩形。 + for (int i = 0; i < temp_size; i++) { + UMLObject uml_obj = temp_result.get(i); + // 如果should_be_del,则说明uml_obj在一列横坐标相近的矩形中纵坐标靠下,且已被舍去,所以直接跳过 + if (uml_obj.should_be_del) { + continue; + } + // 临时保存所有横坐标相近的矩形在temp_result中的索引,用于后面比对纵坐标 + List similar_ys = new ArrayList<>(); + similar_ys.add(i); + // 针对当前矩形,查找是否有与其横坐标大致一样的其他矩形 + for (int j = 0; j < temp_size; j++) { + if (j != i) { + UMLObject other_uml_obj = temp_result.get(j); + if (Math.abs(uml_obj.whole.x - other_uml_obj.whole.x) <= 3) { + // 记录与当前矩形横坐标大致一样的其他矩形在temp_result中的索引 + similar_ys.add(j); } } } - // 将列表里的矩形按上、中、下排序 - uml_class.list.sort(new Comparator() { + // 对所有横坐标相近的矩形按纵坐标从小到大排序 + similar_ys.sort(new Comparator() { @Override - public int compare(Rectangle r1, Rectangle r2) { - return Integer.valueOf(r1.y).compareTo(r2.y); + public int compare(Integer o1, Integer o2) { + return Integer.valueOf(temp_result.get(o1).whole.y).compareTo(temp_result.get(o2).whole.y); } }); - for (int j = 0; j < uml_class.list.size(); j++) { - Rectangle r_in_l = uml_class.list.get(j); - if (r_in_l != null) { - switch (j) { - case 0: { - uml_class.top = r_in_l; - break; - } - case 1: { - uml_class.mid = r_in_l; - break; - } - case 2: { - uml_class.bottom = r_in_l; - break; - } - default: - break; - } + // 对所有横坐标相近的矩形,只保留纵坐标最靠上的矩形、舍去其他所有矩形。 + // 现在similar_ys的第一个元素,记录的是与当前矩形横坐标相近的矩形中,纵坐标最靠上的矩形在temp_result中的索引 + for (int j = 0; j < similar_ys.size(); j++) { + if (j == 0) { + // 只保留纵坐标最靠上的矩形 + result.add(temp_result.get(similar_ys.get(j))); } + // 舍去其他所有矩形。注意此时也应将本次添加的矩形舍去,防止重复记录 + temp_result.get(similar_ys.get(j)).should_be_del = true; } - /* - * 此时会出现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); + } - System.out.println("完成将" + cd_path + "中所有矩形整合为类的业务,共" + result.size() + "个类"); return result; } - private List detectText(Mat cls_diagram, List classes) { - System.out.println("开始识别" + cd_path + "中每个类的文字"); - for (UMLClass uc : classes) { + private List detectText(Mat s_diagram, List objects) { + List result = new ArrayList<>(); + System.out.println("开始识别" + sd_path + "中每个对象的文字"); + for (UMLObject uo : objects) { ITesseract instance = new Tesseract(); instance.setDatapath(UMLDiagramRecognizer.tessdata_path); try { - // 将uc中的每个区域写入临时文件,然后识别临时文件中的文字 - 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, ImgProcessor.cutImage(cls_diagram, uc.mid)); - uc.setAttrisStr(instance.doOCR(new File(temp_res_path))); - } - if (uc.bottom != null) { - Imgcodecs.imwrite(temp_res_path, ImgProcessor.cutImage(cls_diagram, uc.bottom)); - uc.setMethodsStr(instance.doOCR(new File(temp_res_path))); + // 将uo中的每个区域写入临时文件,然后识别临时文件中的文字 + if (uo.whole != null) { + Imgcodecs.imwrite(temp_res_path, ImgProcessor.cutImage(s_diagram, uo.whole)); + uo.setTitle(instance.doOCR(new File(temp_res_path))); } } catch (TesseractException e) { // TODO Auto-generated catch block e.printStackTrace(); } } - System.out.println("完成对" + cd_path + "中每个类的文字识别"); - return classes; + // 清除无文字的矩形,则剩下的就是对象 + for (UMLObject uo : objects) { + if (!uo.getTitle().isBlank()) { + result.add(uo); + } + } + System.out.println("完成对" + sd_path + "中对象的识别业务,共" + result.size() + "个对象"); + return result; } - public Pair> getResult() { + public Pair> getResult() { return result; } } diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/Message.java b/src/main/java/com/hy/java/uct/umlrecog/util/Message.java new file mode 100644 index 0000000000000000000000000000000000000000..90865620f7bb55a1fad7b0b782725ced87244724 --- /dev/null +++ b/src/main/java/com/hy/java/uct/umlrecog/util/Message.java @@ -0,0 +1,21 @@ +package com.hy.java.uct.umlrecog.util; + +public class Message { + + /** + * 标志其可能与其他msg重复 + */ + public boolean is_redu; + + public UMLObject source; + public UMLObject target; + public Line line; + public int source_pt_index; + public String msg; + + public PolygonalLine poly_line; + + public Message(Line line) { + this.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 f374ad620940da72aeacf55fdf1f6bd24c7fb995..ee787b34b321e9c1c4ce9f7ff69a58ab2df935e5 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 @@ -12,7 +12,7 @@ import org.opencv.core.Rect; public class Rectangle extends Rect { private Mat mat; private MatOfPoint contour; - public boolean within_class = false; + public boolean within_cls_obj = false; public Rectangle(Mat cls_diagram, MatOfPoint contour, MatOfPoint2f approx_rect) { super(new Point(approx_rect.get(0, 0)[0], approx_rect.get(0, 0)[1]), new Point(approx_rect.get(2, 0)[0], approx_rect.get(2, 0)[1])); diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/UMLObject.java b/src/main/java/com/hy/java/uct/umlrecog/util/UMLObject.java new file mode 100644 index 0000000000000000000000000000000000000000..530ac65ac514a5ea5d7f859d91da4004a004b2fb --- /dev/null +++ b/src/main/java/com/hy/java/uct/umlrecog/util/UMLObject.java @@ -0,0 +1,26 @@ +package com.hy.java.uct.umlrecog.util; + +import java.util.ArrayList; +import java.util.List; + +public class UMLObject { + /** + * 只在mergeIntoObject时使用 + */ + public boolean should_be_del = false; + /** + * 此UMLObject的信息 + */ + public Rectangle whole; + private String title; + public List out_msgs = new ArrayList<>(); + public List in_msgs = new ArrayList<>(); + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } +} diff --git a/src/main/resources/cd/C204-242-DJSMT_Assignment-1.txt b/src/main/resources/cd/C204-242-DJSMT_Assignment-1.txt new file mode 100644 index 0000000000000000000000000000000000000000..7340dafbb671c2cd9306e2979bd17f118eada256 --- /dev/null +++ b/src/main/resources/cd/C204-242-DJSMT_Assignment-1.txt @@ -0,0 +1,83 @@ +<> +ScanEvents +@+takeFromVehicle ++addToVehicle +@@Scan +%<> +ScanEvents +%依赖¥#<> +Employee Roles +@+sorter +@R +@Employeee +%<> +Employee Roles +%依赖¥#Employeee +@+role +@@Employeee +%<> +Employee Roles +%依赖¥SorterSession +%Employeee +%实现¥Scan +%Employeee +%依赖¥DataAdapter +%Employeee +%依赖¥#PackageBin +@+destinationCities ++scantistory ++scan +@+addPackage() ++ostring() ++lastScan() +@PackageBin +%Scan +%实现¥#Scan +@+event +@@Scan +%Employeee +%依赖¥Scan +%<> +ScanEvents +%依赖¥PackageBin +%Scan +%实现¥Package +%Scan +%实现¥#DataAdapter +@— =2 +@+getAllPackage() ++getallBin() + ++addBin() ++getundeliveredPackages() +@DataAdapter +%Employeee +%依赖¥DataAdapter +%SorterSession +%实现¥LoginForm +%DataAdapter +%实现¥Package +%DataAdapter +%实现¥#SorterSession +@w— +@@SorterSession +%Employeee +%实现¥DataAdapter +%SorterSession +%实现¥LoginForm +%SorterSession +%实现¥#Package +@“+destinationCity +“+scanHistory +@+tostring() +@Package +%Scan +%实现¥Package +%DataAdapter +%实现¥#LoginForm +@——— =~ +@@LoginForm +%SorterSession +%实现¥LoginForm +%DataAdapter +%实现¥#@null@null@# \ No newline at end of file diff --git a/src/main/resources/cd/Salaboy_smart-tasks.txt b/src/main/resources/cd/Salaboy_smart-tasks.txt index ee76cbe37ba0943720cababcb958d0044d349961..abc6e87fa71858700a5e045b442d22dd29eecf50 100644 --- a/src/main/resources/cd/Salaboy_smart-tasks.txt +++ b/src/main/resources/cd/Salaboy_smart-tasks.txt @@ -4,7 +4,7 @@ JBPMSMinaHumanTaskClientConfiguration %JBPMSHUmanTaskServiceOperations %实现¥JBPMSMinaHumanTaskClientConfiguration %JBPMSHUmanTaskClientConfiguration -%聚合¥#JBPMSHUmanTaskServiceOperations +%依赖¥#JBPMSHUmanTaskServiceOperations @taskClient : TaskClient configuration : JEPMSHumanTaskClientConfiguration @JBPMSHumanTaskServiceOperations(configuration : JBPMSHumanTaskClientConfiguration) @@ -21,7 +21,7 @@ HumanTaskservice %实现¥= HumanTaskServiceOperations %HumanTaskServicelmpl -%聚合¥#JBPMSHUmanTaskClientConfiguration +%依赖¥#JBPMSHUmanTaskClientConfiguration @port : String host : String @JBPMSHumanTaskClientConfiguration(port : String,host : String) @@ -30,14 +30,14 @@ host : String HumanTaskclientConfiguration %实现¥JBPMSMinaHumanTaskClientConfiguration %JBPMSHUmanTaskClientConfiguration -%聚合¥#==interface=: +%依赖¥#==interface=: HumanTaskservice @getTaskOperations() : Map @null@==interface=: HumanTaskservice %= HumanTaskServiceOperations -%聚合¥HumanTaskServicelmpl +%依赖¥HumanTaskServicelmpl %==interface=: HumanTaskservice %实现¥#< @@ -50,7 +50,7 @@ HumanTaskServiceOperations %实现¥< HumanTaskclientConfiguration %HumanTaskServiceConfiguration -%聚合¥JBPMSHUmanTaskClientConfiguration +%依赖¥JBPMSHUmanTaskClientConfiguration %< HumanTaskclientConfiguration %实现¥#= @@ -59,11 +59,11 @@ HumanTaskServiceOperations @null@= HumanTaskServiceOperations %HumanTaskServicelmpl -%聚合¥= +%依赖¥= HumanTaskServiceOperations %= TaskOperations -%聚合¥JBPMSHUmanTaskServiceOperations +%依赖¥JBPMSHUmanTaskServiceOperations %= HumanTaskServiceOperations %实现¥< @@ -74,14 +74,14 @@ HumanTaskServiceOperations HumanTaskservice %= HumanTaskServiceOperations -%聚合¥#HumanTaskServiceConfiguration +%依赖¥#HumanTaskServiceConfiguration @clientConfs : Map @addHumanTaskClientConfiguration(name : String,client : HumanTaskclientConfiguration) getHumarTaskClientConfigurations() : Map @< HumanTaskclientConfiguration %HumanTaskServiceConfiguration -%聚合¥#= +%依赖¥#= TaskOperations @getMyTaskabstracts(taskType : String) : TTaskAbstract geyMyTasks(taskType : String) : TTask @@ -97,4 +97,4 @@ complete(id : String,data : Object) HumanTaskServiceOperations %= TaskOperations -%聚合¥# \ No newline at end of file +%依赖¥# \ No newline at end of file diff --git a/src/main/resources/cd/abrden_StarCraft.txt b/src/main/resources/cd/abrden_StarCraft.txt new file mode 100644 index 0000000000000000000000000000000000000000..2fefe0f428b695c7562514b8e808776ff1260f6f --- /dev/null +++ b/src/main/resources/cd/abrden_StarCraft.txt @@ -0,0 +1,25 @@ +TerranBuilder +@@+TerranBuilder() ++getRace(): String +@#ProtossBuilder +@@+ProtossBuilder() ++getRace(): String +@#StructureTemplate +@@+create(position : Point) : Structure +@#Builder +@# dependsOn : Map = new HashMap() +@+ create(name : String, position : Point, resources : Resources, bl : terable, map : Map) : Construction +- getTemplateWithName(name : String) : StructureTemplate + +- structureRequiredExists(name : String, built - terable) : boolean + +#addTemplate(template : StructureTemplate) : void + +#addDependency(key : String, value : String) : void + +# dependensOn(key : String) : String + ++ getTemplateNames() : Stringl] + ++getRace() : String +@# \ No newline at end of file diff --git a/src/main/resources/cd/badqiu_rapid-framework.txt b/src/main/resources/cd/badqiu_rapid-framework.txt new file mode 100644 index 0000000000000000000000000000000000000000..f5acf042be6f4207d8f637a031413f239eb84814 --- /dev/null +++ b/src/main/resources/cd/badqiu_rapid-framework.txt @@ -0,0 +1,28 @@ +SqlFactory +@+ parsesal() : Sal +@null@GeneratorFacade +%SqlFactory +%实现¥#JavaClass +@@null@GeneratorFacade +%JavaClass +%实现¥#TableFactory +@+ getTable() : Table +@null@GeneratorFacade +%TableFactory +%实现¥#GeneratorFacade +@+ deleteByClass() : void ++ deleteBySql) : void + ++ deleteByTable() : void ++ generateByClass() : void ++ generateBySqll) : void ++ generateByTable() : void +@@GeneratorFacade +%JavaClass +%实现¥GeneratorFacade +%TableFactory +%实现¥GeneratorFacade +%SqlFactory +%实现¥#GeneratorProperties +@@null@#@null@null@#@null@null@#@null@null@#@null@null@#GeneratorControl +@null@null@# \ No newline at end of file diff --git a/src/main/resources/cd/cscfa_bartleby.txt b/src/main/resources/cd/cscfa_bartleby.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba694bdf2381eb958fcf39274d09ddcf33d043e7 --- /dev/null +++ b/src/main/resources/cd/cscfa_bartleby.txt @@ -0,0 +1,81 @@ +RollingFileAppender +@#setRolingPoiicy RollngPolicy): void ++setTriggeringPolcy (TriggeringPolicy ) +@null@#FileAppender +@+setFile(String): void. ++setPrudentlooolean): void ++setAppend(boolean): void +@@FileAppender +%‘OutputStreamAppender +%实现¥#‘ConsoleAppender +@+setTarget(String): void +@@‘ConsoleAppender +%‘OutputStreamAppender +%实现¥#Filter +@+decide(E event): FilterReply +@null@Filter +%‘OutputStreamAppender +%实现¥#<> +Encoder +@#init{OutputStream): void ++doEncode(E event): void ++cose(): void +@null@#‘OutputStreamAppender +@+setOutputStream(OutputStream): void +4setEncoder Encoder ): void +@null@FileAppender +%‘OutputStreamAppender +%实现¥‘ConsoleAppender +%‘OutputStreamAppender +%实现¥Filter +%‘OutputStreamAppender +%实现¥#UnsynchronizedAppenderBase +@+doAppend(E event): void + +#append(E event): void ++addFiter Fiter <> fiter): void ++getFierChainDeciion(E event): FiterReply +@@#<> +Appender +@“+doAppend(E event): void +@@#RollingFileAppender +@#setRolingPoiicy RollngPolicy): void ++setTriggeringPolcy (TriggeringPolicy ) +@null@#FileAppender +@+setFile(String): void. ++setPrudentlooolean): void ++setAppend(boolean): void +@@FileAppender +%‘OutputStreamAppender +%实现¥#‘ConsoleAppender +@+setTarget(String): void +@@‘ConsoleAppender +%‘OutputStreamAppender +%实现¥#Filter +@+decide(E event): FilterReply +@null@Filter +%‘OutputStreamAppender +%实现¥#<> +Encoder +@#init{OutputStream): void ++doEncode(E event): void ++cose(): void +@null@#‘OutputStreamAppender +@+setOutputStream(OutputStream): void +4setEncoder Encoder ): void +@null@FileAppender +%‘OutputStreamAppender +%实现¥‘ConsoleAppender +%‘OutputStreamAppender +%实现¥Filter +%‘OutputStreamAppender +%实现¥#UnsynchronizedAppenderBase +@+doAppend(E event): void + +#append(E event): void ++addFiter Fiter <> fiter): void ++getFierChainDeciion(E event): FiterReply +@@#<> +Appender +@“+doAppend(E event): void +@@# \ No newline at end of file diff --git a/src/main/resources/cd/fltt_jss7.txt b/src/main/resources/cd/fltt_jss7.txt new file mode 100644 index 0000000000000000000000000000000000000000..5412dcc4d8e4ee64f5299264f9d1b612f200a93e --- /dev/null +++ b/src/main/resources/cd/fltt_jss7.txt @@ -0,0 +1,97 @@ +< +MapServiceF actory +@createProcessUinstructuredS SRequest{ussdDataCodingS cheme : byte,ussdString : USSDString) : ProcessUnstructuradSSRequest +createProcessUnstructuredssR equestResponselinvokelD : rt, ussdDataCodingScharma : byke,ussdString :USSDString) - ProcesslnstructuredsSResponse +createlnstructuredsSRequest(ussdDataCodingSchama | byte,ussdString USSDString) - UnstructuredSSRaquest +createlnstructuradSsRequestResponselinokelD - nt,ussdDataCodingScherne - byte,ussdstring : USSDString) : UnstructuredssResponse +cretelISSDStringlussdString - String] : USSDString + +cretelISSDStringlusadstring : USSDString charSe : Charset] : USSDString + +cretelISSDStringlussdstring: ytell : LSSDString + +createl)SSDStringlussdstring: bytell charSat Charset) : USSDString + +crataddrassStringladdature : it urPlan : NurnberingPlan, adress : String) : Addressstring + +createMAP UserabortChaicel) MAPUsarabortChoice +@null@< +MAPProvider +%< +MapServiceF actory +%依赖¥#< +MAPServicelistener +@onProcessUnstructuredSSindication{procUnstrind : ProcessUnstructuredsSindication) : void +onlnstructuradssindication(unstrind : UnstructuredsSindicatio) void +@null@#< +MAPDislogListener +@onMAF OpeninfolmapOpeninfo : MAFOpeninfa) : void +onMAPAccaptinfolmapAccptinfo: MAPACcaptinfo) void +onMAPCloselnfolmapCloselnfo : MAP Clossinfo) : void + +OnMAPRefuseinfol mapRefuselnfo : MAPRefuselnfo) : void +nMAPUserabortinfolmapUserAbortinfo MAPUserAbortinfol : void +nMAPProvidarAbortinfolmapP roviderabortinfo : MAPP roviderabortinfo) : void +@null@< +MAPProvider +%< +MAPDislogListener +%实现¥#< +MAPProvider +@createNewDialoglappCritx : MAP ApplicationContext,orighddress : Sccpaddress,origReference : AddressString, destAddress : Sccpaddress, destReference : AddressString) : MAPDialog +dMAP Dialoglistener{mapDialogl istenr : MAPDiaioglistener) void + +removeAP DidlogListenrmapDislogLtener : MAP DidlogListenr) : void + +ddMAPServical istenarmapServicelstener MAPServiceL istener) : void + +removeMAP S arviceL tener{mapSariceL istenar : MAPSaruiceL tener] : void + +gethapServiceFactoryl) : MapSaruiceF actory + +GethAPDislogldialogld: Long] : MAPDislog +@null@< +MAPProvider +%< +MapServiceF actory +%依赖¥< +MAPProvider +%< +MAPDislogListener +%实现¥< +MAPDilog +%< +MAPProvider +%依赖¥< +MAPStack +%< +MAPProvider +%依赖¥#< +MAPDilog +@getDialogldl) : Long + +send() - void + +closelprearrangadEnd - boolsan) : void + +Short{rmaplserabortChoice : MAP UssrAbertChoice veid + +dProcessinstructuredSSFaquestlussdDataCodingScharne : byte,ussdString : USSDString msisdn: AddressString) veid +dProcassinstructurads SR esponsalinvakeld lang.lastResul : boslean ussdDstaCodingScheme : byte.ussdstring : UISSDString) : void +acdUnstructuredsSRequest{ussdDataCodingscheme - byte.ussdString : USSDString) : void + +Unstructurads s Rasponselinvokeld : longlastRasul : boolean,ussdD ataCodingScherns : byt ussdString : USSDStringl : void +@null@< +MAPDilog +%< +MAPProvider +%依赖¥#< +MAPStack +@stopl) : void +start() - void +GetMAPProvider() : MAPProvider +@null@< +MAPStack +%< +MAPProvider +%依赖¥# \ No newline at end of file diff --git a/src/main/resources/cd/marcellodesales_my-cs-research.txt b/src/main/resources/cd/marcellodesales_my-cs-research.txt new file mode 100644 index 0000000000000000000000000000000000000000..0b1ac1f0d053ed5b62d29c40951b83eb93d0b056 --- /dev/null +++ b/src/main/resources/cd/marcellodesales_my-cs-research.txt @@ -0,0 +1,87 @@ +GreatsrMancpsrator +@@@#LesscrEqualscpentor +@@@LesscrEqualscpentor +%Reazanaiopaacer +%实现¥#NotEquaICpsrator +@@null@NotEquaICpsrator +%Reazanaiopaacer +%实现¥#GreatirorBqualsCperator +@@null@Reazanaiopaacer +%GreatirorBqualsCperator +%实现¥#LessTancpsrator +@@@#Equacpsator +@@@Equacpsator +%Reazanaiopaacer +%依赖¥#@@null@#@@@#@@@#@@null@#@@null@#ToknEception +@@@#@@null@%%实现¥#“rCpsrator +@@@“rCpsrator +%Adcveopaer +%实现¥#Tidecperator +@@null@Tidecperator +%Idepieacveopaer +%依赖¥#andcperator +@@null@Idepieacveopaer +%andcperator +%实现¥#Timascpsrator +@@null@Idepieacveopaer +%Timascpsrator +%实现¥#Reazanaiopaacer +@@null@Reazanaiopaacer +%GreatirorBqualsCperator +%实现¥LesscrEqualscpentor +%Reazanaiopaacer +%实现¥NotEquaICpsrator +%Reazanaiopaacer +%实现¥Equacpsator +%Reazanaiopaacer +%依赖¥#@@null@#Minuscpsrator +@@null@#ntwrprotr +@@null@#@@null@#T~ +@null@null@Adcveopaer +%T~ +%实现¥#Adcveopaer +@@null@Adcveopaer +%T~ +%实现¥Adcveopaer +%Exprasson +%实现¥“rCpsrator +%Adcveopaer +%实现¥Puscpentor +%Adcveopaer +%实现¥#@@null@%Exprasson +%实现¥%%实现¥#Idepieacveopaer +@@null@Idepieacveopaer +%andcperator +%实现¥Idepieacveopaer +%Timascpsrator +%实现¥Tidecperator +%Idepieacveopaer +%依赖¥#@@null@%Exprasson +%实现¥#Puscpentor +@@null@Puscpentor +%Adcveopaer +%实现¥#@@null@%Exprasson +%实现¥#Concluslonsequencs. +@@null@Concluslonsequencs. +%Statamentssquence +%实现¥#@@null@#Asignmentstitament +@@null@#RoatTyps. +@@@#BokanType. +@@null@%BokanType. +%依赖¥#Statamentssquence +@@null@Concluslonsequencs. +%Statamentssquence +%实现¥#Exprasson +@@null@%Exprasson +%实现¥%Exprasson +%实现¥%Exprasson +%实现¥Adcveopaer +%Exprasson +%实现¥#@@null@%%实现¥#@@null@%%实现¥#@@null@%%实现¥#e +@@null@ntagerTyps. +%e +%实现¥#ntagerTyps. +@@null@ntagerTyps. +%e +%实现¥#@@null@%BokanType. +%依赖¥%%实现¥# \ No newline at end of file diff --git a/src/main/resources/cd/rodrigoazevedomartins_TrabalhoFinalLTPIII.txt b/src/main/resources/cd/rodrigoazevedomartins_TrabalhoFinalLTPIII.txt new file mode 100644 index 0000000000000000000000000000000000000000..2bedc5a35a49ef9344bbb3f328915809664bf289 --- /dev/null +++ b/src/main/resources/cd/rodrigoazevedomartins_TrabalhoFinalLTPIII.txt @@ -0,0 +1,286 @@ +ProfessorDisciplina +@-professor: Professor +—diacipiings Hasioinn +@setProfessor (professoriprofesser) : void ++getProfessor () : professor + ++setbisciplina (disciplina:disciplina) : void ++getDisciplina(): disciplina +@#Usuario +@-codusuario: int +-login: varchar +-senna: varchar +S Poenad g INCASDREAS +@+setCodfuncionario (codfuncionario:int) : void ++getCodfuncionario() : int ++setLogin(loginivarchar) : void + ++getlogin() : varchar + ++setSenna(senharvarchar) : void + ++gecSenha() : varchar + ++setFuncionario (funcicnario: funcionario) : void ++getFuncionario() : funcionario +@#Professor +@-codprofessor: int +—titulacao: varchar + +-nivel: int + +-pessoa: Pessoa + +S aetnlingd: TAiNCESEIHEIS +@+setCodprofessor (codprofessor:int) : void ++getCodprofessor() : int + ++setTitulacao (vitulacao:varchar): void ++getTitulacac(): varchar + ++setNivel (nivel:int) : void + ++gecNivel(): int + ++setPessoa (pessoa:pessoa) : void + ++gecPessca(): pessca + ++setbisciplinas (disciplinas:list) : void ++getDisciplinas(): lisc +@#Funcionario +@-codfuncionario: int +-siape: varchar +-cargo: varchar +s Diwak +@+setCodfuncionario (codfuncionario:int) : void ++getCodfuncionariol) : int + ++setSiape (siapervarchar) : void + ++gecSiape() : varchar ++setCargo(cargovarchar) : void + ++gecCargo() : varchar + ++setPessoa (pessoa:pessoa) : void ++getPessoa(): pessoa +@#Secao +@-codsecao: int +inicio: date +—final: date +RaREis DaaReis +@+setCodsecac (codsecaniint): void ++gecCodsecan() : int + ++setInicio (inicio:date) : void ++getInicio(): date + ++setFinal (finalidate) : void ++getFinal(): date + ++setUsuario (usuariorusuario) : void ++getUsuario () : usuario +@#Reserva +@-codreserva: int +-datainicio: date +—datafinal: date +-situacao: Situacao +—secao: Secao + +-pessoa: Pessoa +~recursoa: 1igt): void ++getRecursos() : list ++setPessoa (pessoa:pessoa) : void ++getPessoa(): pessoa +@#Disciplina +@-coddisciplina: int +-ementa: varchar +L e Bk +@+setCoddisciplina (coddisciplina:int): void ++getCoddisciplina(): int + ++setiome (nome:varchar) : void + ++getiome () : varchar + ++setEnenta (ementa:varchar): void ++getEnenta(): varchar ++setCurso(cursoscurso) : void + +FostCursol): Pares +@#Situacao +@-codsituacac: int +AEETORY: GREChAR +@+setCodsituacac (codsituacao:int) : void ++gecCodsituacac () : int + ++setbescricac (descricac:varchar): void ++getDescrican () : varchar +@#Tiporecurso +@-codtiporecurso: int +SRR h +@setCodtiporecurso (codtiporecursorint) ¢ void ++getCoduiporecurso () : int + ++setbescricac (descricao:varchar): void ++getDescricao () : varchar +@#Recurso +@-codrecurso: int +-nome: varchar +slemecapn: e +@+setCodrecurso (codrecursorint) : void ++getCodrecurso(): int + ++setiome (nome:varchar) : void + ++getiome () : varchar + ++setTiporecurso (ciporecurso: tiporecurso) : void ++getTiporecurso() : tiporecurso +@#Telefone +@-codtelefone: int +-pessoa: Pessoa +-area: int +vt e +@+setcodtelefone (codvelefone:int) ; void ++getcodtelefone () int + ++setarea (area:int) : void + ++getarea(): int + ++setnumero (numero:int) : void +sgetnmero(): inc + ++setpessoa (pessoa:pessoa) : void ++getpessoa(): pessoa +@#Curso +@-codcurso: int +-nome: varchar +—uracac: int +_disciplinas: list +@+setCodcurso (codeurso:int) : void + ++gecCodenrsa() : int + ++setiome (nome:varchar) : void + ++getome () : varchar + ++setburacao (duracao: int) : void + ++getDuracan(): int + ++setbisciplinas (disciplinas:list) : void ++getDisciplinas(): lisc +@#Endereco +@-codendereco: int +-pessoa: Pessoa +—rua: string +——— +-complemento: string +bairro: string +—cidade: string +-cep: int + +—estado: string +A e +@+setcodendereco (codendereco:int) ; void ++getcodendereca () : int + ++setrua (ruastring): void + ++getrua(): string + ++setnumero (numero:int) : void +tgetnmero(): inc + ++setcomplemento (complemento: string) : void ++getcamplementa () : string + ++setbairro (bairro:string) : void ++getbairro(): string + ++setcidade (cidade:string) : void ++getcidade (): string + ++setcep (cepiint): void + ++getcep(): int + ++setestads (estado:string) : void ++getestado(): string + ++setpais (pais:string) : void ++getpais () : string + ++setpessoa (pessoa:pessoa) : void ++getpessoa(): pessca +@#Pessoa +@~codpessoa: int + +-nome: string + +-rg: string + +~cpf:int + +~datanasc: date +-enderecos list +-emails: list +Aaloforves: e ): void ++getenderecos(): list ++setemails(emailsiist): void ++getemails): list ++setelefones(telefonesilist): void ++gettelefones(): list +@#ReservaRecurso +@-reserva: Reserva +Crerurass. Recarac +@+setReserva (reservaireserva) : void ++getResexrva(): reserva ++setRecurso (recurso: recurso) : void ++getRecurso () : recurso +@#Email +@~codemail: int +-endereco: string +i s +@+setcodemail(codemail:int): void ++getcodemail): int ++setendereco(endereco:string): void ++getendereco): string ++setpessoa(pessoa:pessoa): void +oo b pesce +@# \ No newline at end of file diff --git a/src/main/resources/cd/temp result.jpeg b/src/main/resources/cd/temp result.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..9a3ff1c6df94ff927d00f12edc792bd29c24b0a8 Binary files /dev/null and b/src/main/resources/cd/temp result.jpeg differ diff --git a/src/main/resources/cd/temp result.jpg b/src/main/resources/cd/temp result.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4d4fc04fa788bf90f477d39050370eb8fc861331 Binary files /dev/null and b/src/main/resources/cd/temp result.jpg differ diff --git a/src/main/resources/cd/temp result.png b/src/main/resources/cd/temp result.png index fede4d545fffed51ea3abb0bd5506741ba1ff7be..e38c3bb4341941f334afd2d0dc50d072cc0553b6 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/main/resources/sd/coinvent_coinvent.txt b/src/main/resources/sd/coinvent_coinvent.txt new file mode 100644 index 0000000000000000000000000000000000000000..1c27a377d9c17046ab418a5e095b456bc4301100 --- /dev/null +++ b/src/main/resources/sd/coinvent_coinvent.txt @@ -0,0 +1,4 @@ +Iquality +@Iquality +@Iquality +@ \ No newline at end of file diff --git a/src/main/resources/sd/temp result.png b/src/main/resources/sd/temp result.png new file mode 100644 index 0000000000000000000000000000000000000000..b870453967f8ecf877d45a7b69e97c561eadde17 Binary files /dev/null and b/src/main/resources/sd/temp result.png differ