使用OpenCV python

时间:2019-01-23 11:08:20

标签: python opencv image-processing

输入手绘逻辑门图 enter image description here

我用YOLO训练和识别带有标签(字母)的7种不同的逻辑门。 检测输入图像中的逻辑门和标签。 enter image description here

在这里,我得到了一个包含每个矩形的数组列表(框)。每个列表包含以下每个详细信息 依次按矩形 •矩形标签 •矩形左上角的x,y坐标 •矩形右下角的x,y坐标

矩形框的数组。

boxes = [['AND',(614,98),(1146,429)],['NOT',(525,1765),(1007,1983)],['NAND',(762, 1188),(1209, 1528)],['NOR',(1323、272),(1884、682)],['OR',(575、599),(1225、985)],['XOR',(1393、1368) ,(2177, 1842)],['XNOR',(2136、859),(2762、1231)],['A',(34、50),(321、224)],['B',(12、305) ,(344,487)],['C',(3, 581),(391、779)],['D',(0、828),(400、1060)],['E',(0、1143),(354、1351)],['F' ,(0、1418),(313、1615)],['G', (0,1753),(301,1985)],['OUTPUT',(2810,940),(3069,1184)]] p

之后,我使用了概率霍夫线变换来检测标签和逻辑门之间的线。 为此,我引用了此链接[How to merge lines after HoughLinesP?。通过使用此链接,我减少到最少的行数,最后只得到35行。 检测绿色的35条线

enter image description here

然后,我将这35条线分类,并将彼此靠近的线分组。最终,我得到了14行。 最后14行图像。

enter image description here

14行数组。

final_line_points = [[[((87,1864),(625,1869)]],[[((623,1815),(1354,1855)],[(1343,1660),(1770,1655)] , [(1348,1656),(1348,1869)]],[[(102,971),(531,945)],[(518,835),(892,825)],[(521,830) ,(526,949)]], [[(105,1260),(494,1254)],[(487,1351),(891,1340)],[(489,1252),(491,1356)]],[[((107,1533) ),(526, 1510)],[((516,1432),(892,1410)],[(521,1433),(520,1514)]],[[((111,432),(519,396)]],[( 499,313),(820, 299)],[(503、310),(506、402)]],[[(123、157),(496、150)],[(493、144),(498、247)],[( 495,242),(815,234)]], [[(170,692),(509,687)],[(504,771),(888,764)],[(505,685),(508,775)]],[[((936,264) ),(1229,261)],[(1227, 257),(1240、485)],[(1234、481),(1535、458)]],[[(985、1361),(1343、1347)],[(1341、1344),(1348, 1578)], [(1345,1575),(1773,1571)]],[[((991,796),(1264,778)]],[(1240,535),(1544,520)],[(1247,532) , (1254,783)]],[[(1546,582),(2156,489)],[(2154,488),(2148,1021)]],[[(2153,1087),(2164,1581) )]], [[((2444,1139),(3017,1055)]]]

那么如何通过使用以上两个数组(框,final_line_points)获得以下输出?

enter image description here

2 个答案:

答案 0 :(得分:2)

您的项目似乎很酷,所以我花了一些时间寻找解决方案。我在下面的代码。代码的结果是:

bootstrap.min.css

我假设,如果一个元素比另一个元素最左,则它是前一个块。我还假设在您的那组线中,第一个是正确的...这使我可以将您的14条几条线简化为14条线。

OUTPUT[XNOR[NOR[AND[B, A], OR[D, C]], XOR[NOT[G], NAND[E, F]]]]

如果需要的话,我可以再解释一天,或者您可以从这里开始。无论如何,请与您的项目一起玩乐。

编辑:

我的目标是建立一个图形,其中节点是盒子,边是线。问题是这些行仅被定义为一组封闭的行。他们也很混乱,但首先。因此,第一步是将每组线变成一条直线。那就是我所说的boxes = [['AND', (614, 98), (1146, 429)], ['NOT', (525, 1765), (1007, 1983)], ['NAND', (762, 1188), (1209, 1528)], ['NOR', (1323, 272), (1884, 682)], ['OR', (575, 599), (1225, 985)], ['XOR', (1393, 1368), (2177, 1842)], ['XNOR', (2136, 859), (2762, 1231)], ['A', (34, 50), (321, 224)], ['B', (12, 305), (344, 487)], ['C', (3, 581), (391, 779)], ['D', (0, 828), (400, 1060)], ['E', (0, 1143), (354, 1351)], ['F', (0, 1418), (313, 1615)], ['G', (0, 1753), (301, 1985)], ['OUTPUT', (2810, 940), (3069, 1184)]] final_line_points = [[[(87, 1864), (625, 1869)]], [[(623, 1815), (1354, 1855)], [(1343, 1660), (1770, 1655)], [(1348, 1656), (1348, 1869)]], [[(102, 971), (531, 945)], [(518, 835), (892, 825)], [(521, 830), (526, 949)]], [[(105, 1260), (494, 1254)], [(487, 1351), (891, 1340)], [(489, 1252), (491, 1356)]], [[(107, 1533), (526, 1510)], [(516, 1432), (892, 1410)], [(521, 1433), (520, 1514)]], [[(111, 432), (519, 396)], [(499, 313), (820, 299)], [(503, 310), (506, 402)]], [[(123, 157), (496, 150)], [(493, 144), (498, 247)], [(495, 242), (815, 234)]], [[(170, 692), (509, 687)], [(504, 771), (888, 764)], [(505, 685), (508, 775)]], [[(936, 264), (1229, 261)], [(1227, 257), (1240, 485)], [(1234, 481), (1535, 458)]], [[(985, 1361), (1343, 1347)], [(1341, 1344), (1348, 1578)], [(1345, 1575), (1773, 1571)]], [[(991, 796), (1264, 778)], [(1240, 535), (1544, 520)], [(1247, 532), (1254, 783)]], [[(1546, 582), (2156, 489)], [(2154, 488), (2148, 1021)]], [[(2153, 1087), (2164, 1581)]], [[(2444, 1139), (3017, 1055)]]] def dist(pt1, pt2): return (pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2 def seg_dist(seg1, seg2): distances = [dist(seg1[i], seg2[j]) for i in range(2) for j in range(2)] return min(enumerate(distances), key=lambda x: x[1]) sorted_lines = [] for lines in final_line_points: connected_part = lines[0] non_connected = lines[1:] while non_connected: mat_dist = [seg_dist(connected_part, non_connected[i])[1] for i in range(len(non_connected))] i, min_dist = min(enumerate(mat_dist), key=lambda x: x[1]) seg_to_connect = non_connected.pop(i) idx, real_dist = seg_dist(connected_part, seg_to_connect) if idx == 0: print("error: this case is not handled") exit() elif idx == 1: print("error: this case is not handled") exit() elif idx == 2: connected_part[1] = seg_to_connect[1] elif idx == 3: connected_part[1] = seg_to_connect[0] sorted_lines.append(connected_part) class node(): def __init__(self, name, box) -> None: super().__init__() self.name = name self.box = [(min(box[0][0], box[1][0]), min(box[0][1], box[1][1])), (max(box[0][0], box[1][0]), max(box[0][1], box[1][1]))] self.args = [] self.outputs = [] def __contains__(self, item): return self.box[0][0] <= item[0] <= self.box[1][0] and self.box[0][1] <= item[1] <= self.box[1][1] def __str__(self) -> str: if self.args: return f"{self.name}{self.args}" else: return f"{self.name}" def __repr__(self) -> str: return self.__str__() def center(self): return (self.box[0][0] + self.box[1][0]) / 2, (self.box[0][1] + self.box[1][1]) / 2 nodes = [node(box[0], box[1:]) for box in boxes] for line in sorted_lines: start_point = line[0] end_point = line[1] try: gate1 = next(node for node in nodes if start_point in node) gate2 = next(node for node in nodes if end_point in node) if gate1.center() < gate2.center(): source_gate = gate1 dest_gate = gate2 else: source_gate = gate2 dest_gate = gate1 source_gate.outputs.append(dest_gate) dest_gate.args.append(source_gate) except StopIteration: print(f"{start_point} or {end_point} not in any of the boxes") print(next(node for node in nodes if node.name == "OUTPUT"))

要构建此列表,我使用了以下逻辑:

  1. 对于每组线,将其分为一个连接部分和一个非连接部分
  2. 连接部分的初始化是集合的第一行。正如我所说,在这里我假设第一行是正确的。尝试改善这一点,因为这种假设在其他情况下可能是错误的。
  3. 当有未连接的线路时,请执行以下操作:

    • 找到最接近连接零件的线段
    • 将其从非连接部分中移除
    • 检查段的哪一端离连接的零件最近
    • 如果它是线段的第一个点,则连接零件的最​​后一个点将成为线段的第二个点,否则,第一个点将成为最后的点。

在检查中,未处理的情况是要连接的线段关闭到连接零件的第一个点而不是最后一个点。因为我认为第一行是正确的,所以没有处理。再一次可以改善这一点。

现在,您已经对行进行了排序,对于其中的每一行,都找到包含每个端点的节点。选择最少的作为源门,最右边的作为目标门。由于边缘未定向,因此我不得不假定方向。更新目标门的输入和源门的输出。

最后打印图形的最后一个门。

答案 1 :(得分:0)

首先,您应该按照公式定义如何绘制/输出每个门。例如,XNOR是一行,只要其下方的文本长即可,并带有x个参数和介于两者之间的带圈号+。

现在,您可以反向浏览架构。从最右边的框开始,即您的输出Q。在输出中查找结束的行(=最右边的点/元组的最高x),然后选择该行开始的框。那是一个XNOR。如上所述,XNOR被定义为具有自变量/输入。因此,在XNOR框中寻找最右边的线。接下来,选择这些行开始的框。首先是NOR,然后是XOR。重复该循环,NOR的参数为AND门和OR门。再次重复,“与”门的参数是输入A和B。这些没有输入,也没有结尾的行,因此这些是要打印的实际值。

可视化:
(这不是不是代码,但是我不能提交答案)

Q = XNOR(A,B)  
A = NOR(C,D)  
So, Q = XNOR(NOR(C,D),B)  
C = AND(E,F)  
So Q = XNOR(NOR(AND(E,F),D),B)  
E = input A  
F = input B  
So Q = XNOR(NOR(AND("A","B"),D),B)  
D = OR(G,H)  
So Q = XNOR(NOR(AND("A","B"),OR(G,H)),B)  
ect.

要考虑公式的大小,您可以使大小取决于  “层”号。为不同类型的门创建类将使整个过程变得更加容易。

此伪代码显示了以上概念:

# to draw an XNOR gate:
def draw():
    arguments = get_start_box_of_lines_ending_in_this_box(this.boundingBox, lines)
    for gate in arguments:
        gate.draw()
        # TODO: draw encircled + (except after the last argument)

    # TODO: draw a line over the output generated by the code above