프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / RecognitionDialog.py @ f7987e0e

이력 | 보기 | 이력해설 | 다운로드 (125 KB)

1
# coding: utf-8
2
"""
3
    This is recognition dialog module
4
"""
5
import sys
6
import os
7
import threading
8
import cv2
9
import numpy as np
10
from PyQt5.QtCore import *
11
from PyQt5.QtGui import *
12
from PyQt5.QtWidgets import *
13
import Recognition_UI
14

    
15
import concurrent.futures as futures
16

    
17
from AppDocData import *
18
from SymbolSvgItem import SymbolSvgItem
19
from EngineeringTextItem import QEngineeringTextItem
20
from EngineeringUnknownItem import QEngineeringUnknownItem
21
from EngineeringErrorItem import QEngineeringErrorItem
22
from EngineeringEndBreakItem import QEngineeringEndBreakItem
23
from EngineeringLineItem import QEngineeringLineItem
24
from EngineeringTextItem import QEngineeringTextItem
25
from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
26
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
27
from QEngineeringOPCItem import QEngineeringOPCItem
28
from LineDetector import LineDetector
29
from symbol import Symbol
30

    
31
from MainWindow import MainWindow
32

    
33
# region Symbol Image path List for test
34
targetSymbolList = []
35
# endregion
36

    
37
# region Global variables
38
searchedSymbolList = []
39
textInfoList = []
40

    
41
src = []
42

    
43
ocrCompletedSrc = []
44
afterDenoising = []
45
canvas = []
46

    
47
#WHITE_LIST_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"
48

    
49
MIN_TEXT_SIZE = 10
50

    
51
THREAD_MAX_WORKER = os.cpu_count()
52
threadLock = threading.Lock()
53

    
54
ACCEPT_OVERLAY_AREA = 20
55
# endregion
56

    
57
'''
58
    @history    2018.05.25  Jeongwoo    Add pyqtSignal(recognizeLine, loadRecognitionResult)
59
'''
60

    
61

    
62
class Worker(QObject):
63
    from PyQt5.QtCore import QThread
64
    from PyQt5.QtCore import QTranslator
65
    from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QGridLayout, QListWidget
66
    from QtImageViewer import QtImageViewer
67
    import sys
68

    
69
    '''
70
        @history    2018.05.30  Jeongwoo    Remove parameter on recognizeLine signal / Change signal name (drawDetectedItems)
71
        @history    humkyung 2018.06.08 add signal for progressbar
72
        @history    euisung 2018.11.27 add signal for unknown items
73
    '''
74
    finished = pyqtSignal()
75
    intReady = pyqtSignal(int)
76
    displayTitle = pyqtSignal(str)
77
    displayMessage = pyqtSignal(QListWidgetItem)
78
    updateProgress = pyqtSignal(int, str)
79
    updateBatchProgress = pyqtSignal(int, int)
80
    displayLog = pyqtSignal(MessageType, str)
81
    add_detected_items_to_scene = pyqtSignal(QGraphicsScene)
82

    
83
    def __init__(self, mutex, cond):
84
        super(Worker, self).__init__()
85
        self.mutex = mutex
86
        self.cond = cond
87

    
88
    '''
89
        @history    2018.05.25  Jeongwoo    Add if-statements by isSymbolTextChecked and isLineChecked variable
90
                    2018.05.28  Jeongwoo    Add Parameter 'xmlPath[0]'
91
                    2018.05.30  Jeongwoo    Remove import recognizeLine and self.xmlPath and remove parameter on recognizeLine.emit() (xmlPath)
92
                                            Change signal name (drawDetectedItems)
93
    '''
94

    
95
    def procCounter(self):  # A slot takes no params
96
        try:
97
            self.mutex.lock()
98
            if self.isSymbolChecked or self.isTrainingChecked:
99
                Worker.executeRecognition(self.drawings, self.listWidget, self.isLineChecked, self)
100
        except Exception as ex:
101
            from App import App
102
            from AppDocData import MessageType
103

    
104
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
105
                      f"{sys.exc_info()[-1].tb_lineno}"
106
            App.mainWnd().addMessage.emit(MessageType.Error, message)
107
            self.displayLog.emit(MessageType.Error, message)
108
        except:
109
            (_type, value, traceback) = sys.exc_info()
110
            sys.excepthook(_type, value, traceback)
111
        finally:
112
            self.mutex.unlock()
113
            self.finished.emit()
114

    
115
    '''
116
        @brief  remove small objects from given image
117
        @author humkyung
118
        @date   2018.04.26
119
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
120
    '''
121

    
122
    @staticmethod
123
    def remove_small_objects(image):
124
        try:
125
            app_doc_data = AppDocData.instance()
126
            configs = app_doc_data.getConfigs('Small Object Size', 'Min Area')
127
            minArea = int(configs[0].value) if 1 == len(configs) else 20
128
            configs = app_doc_data.getConfigs('Small Object Size', 'Max Area')
129
            maxArea = int(configs[0].value) if 1 == len(configs) else 50
130

    
131
            # try to convert grayscale to binary
132
            image = cv2.threshold(image, 200, 255, cv2.THRESH_BINARY)[1]
133

    
134
            contours, _ = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
135
            selectedContours = []
136
            for contour in contours:
137
                area = cv2.contourArea(contour)
138
                if minArea < area < maxArea:
139
                    selectedContours.append(contour)
140

    
141
            # draw contour with white color
142
            cv2.drawContours(image, selectedContours, -1, (255, 255, 255), -1)
143
        except Exception as ex:
144
            from App import App
145

    
146
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
147
                      f"{sys.exc_info()[-1].tb_lineno}"
148
            App.mainWnd().addMessage.emit(MessageType.Error, message)
149

    
150
        return image
151

    
152
    '''
153
        @brief  arrange line's position
154
        @author humkyung
155
        @date   2018.07.04
156
    '''
157

    
158
    @staticmethod
159
    def arrangeLinePosition(lines, symbols, listWidget):
160
        try:
161
            listWidget.addItem('Apply flow direction')
162
            pool = [line for line in lines if line.flowMark is not None]
163
            visited = []
164
            visited.extend(pool)
165
            while len(pool) > 0:
166
                line = pool.pop()
167
                print('{} - ({})'.format(line, len(pool)))
168
                rhs = [item for item in lines if item not in visited and item.is_connected(line)]
169
                if rhs:
170
                    pool.extend(rhs)
171
                    visited.extend(rhs)
172
                    for item in rhs:
173
                        item.arrangeVertexOrder(line)
174

    
175
                # skip jointed symbols
176
                symbolPool = [item for item in symbols if item not in visited and item.is_connected(line)]
177
                if symbolPool:
178
                    selected = []
179
                    visited.extend(symbolPool)
180
                    while len(symbolPool) > 0:
181
                        symbol = symbolPool.pop()
182

    
183
                        rhs = [item for item in symbols if item not in visited and item.is_connected(symbol)]
184
                        if rhs:
185
                            symbolPool.extend(rhs)
186
                            visited.extend(rhs)
187
                            selected.extend(rhs)
188
                        else:
189
                            selected.append(symbol)
190

    
191
                    # find lines which are connected last symbol
192
                    for symbol in selected:
193
                        rhs = [item for item in lines if item not in visited and item.is_connected(symbol)]
194
                        if rhs:
195
                            pool.extend(rhs)
196
                            visited.extend(rhs)
197
                            for item in rhs:
198
                                item.arrangeVertexOrder(line)
199
                # up to here
200
            # up to here
201
        except Exception as ex:
202
            from App import App
203
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
204
                                                           sys.exc_info()[-1].tb_lineno)
205
            App.mainWnd().addMessage.emit(MessageType.Error, message)
206

    
207
    def create_detected_items(self, symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList):
208
        try:
209
            QApplication.processEvents()
210
            self.create_detected_symbol_item(symbolList)
211
            QApplication.processEvents()
212
            self.create_detected_text_item(textInfoList)
213
            QApplication.processEvents()
214
            self.create_detected_other_text_item(otherTextInfoList)
215
            QApplication.processEvents()
216
            self.create_detected_title_block_text_item(titleBlockTextInfoList)
217

    
218
            # update scene
219
            # self.graphicsView.scene().update(self.graphicsView.sceneRect())
220
        except Exception as ex:
221
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
222
                                                           sys.exc_info()[-1].tb_lineno)
223
            self.displayLog.emit(MessageType.Error, message)
224

    
225
    '''
226
            history     2018.06.09  humkyung    check length of original and connection point is 2 while parsing
227
                        2018.11.26  euisung     remove scene dependency
228
                        2018.11.29  euisung     change name drawDetectedSymbolItem() -> createDetectedSymbolItem
229
        '''
230

    
231
    def create_detected_symbol_item(self, symbolList):
232
        from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
233
        from SymbolSvgItem import SymbolSvgItem
234
        import math
235

    
236
        try:
237
            app_doc_data = AppDocData.instance()
238
            project = app_doc_data.getCurrentProject()
239

    
240
            searchedMap = []
241
            for symbol in symbolList:
242
                pt = [float(x) for x in symbol.getSp()]
243
                size = [symbol.getWidth(), symbol.getHeight()]
244
                name = symbol.getName()
245
                angle = round(math.radians(symbol.getRotatedAngle()), 2)
246
                _type = symbol.getType()
247
                flip = symbol.getDetectFlip()
248
                origin = [0, 0]
249
                if 2 == len(symbol.getOriginalPoint().split(',')):
250
                    tokens = symbol.getOriginalPoint().split(',')
251
                    origin = [pt[0] + float(tokens[0]), pt[1] + float(tokens[1])]
252
                connPts = []
253
                if symbol.getConnectionPoint() is not None and symbol.getConnectionPoint() != '':
254
                    for param in symbol.getConnectionPoint().split('/'):
255
                        tokens = param.split(',')
256
                        connPts.append(
257
                            ('AUTO', pt[0] + float(tokens[0]), pt[1] + float(tokens[1]), '0') if len(tokens) == 2 else \
258
                                (tokens[0], pt[0] + float(tokens[1]), pt[1] + float(tokens[2]), '0') if len(
259
                                    tokens) == 3 else \
260
                                    (tokens[0], pt[0] + float(tokens[1]), pt[1] + float(tokens[2]), tokens[3]) if len(
261
                                        tokens) == 4 else None)
262

    
263
                parentSymbol = symbol.getBaseSymbol()
264
                childSymbol = symbol.getAdditionalSymbol()
265
                hasInstrumentLabel = symbol.getHasInstrumentLabel()
266

    
267
                svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
268
                if os.path.isfile(svgFilePath):
269
                    svg = SymbolSvgItem.createItem(_type, None, svgFilePath, owner=None, flip=flip)
270
                    svg.hit_ratio = symbol.hitRate
271
                    svg.buildItem(name, _type, angle, pt, size, origin, connPts, parentSymbol, childSymbol,
272
                                  hasInstrumentLabel)
273
                    svg.area = 'Drawing'
274

    
275
                    # set owner - 2018.07.20 added by humkyung
276
                    matches = [searched for searched in searchedMap if searched[0] == symbol.owner]
277
                    if len(matches) == 1:
278
                        svg.owner = matches[0][1]
279
                    searchedMap.append((symbol, svg))
280
                    # up to here
281

    
282
                    # self.addSvgItemToScene(svg)
283
                    app_doc_data.symbols.append(svg)
284
                    app_doc_data.allItems.append(svg)
285
                else:
286
                    item = QGraphicsBoundingBoxItem(pt[0], pt[1], size[0], size[1])
287
                    item.isSymbol = True
288
                    item.angle = angle
289
                    item.setPen(QPen(Qt.red, 5, Qt.SolidLine))
290
                    # self.graphicsView.scene().addItem(item)
291
                    # appDocData.symbols.append(item)
292
                    app_doc_data.allItems.append(item)
293
            # up to here
294
        except Exception as ex:
295
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
296
                                                           sys.exc_info()[-1].tb_lineno)
297
            self.displayLog.emit(MessageType.Error, message)
298

    
299
    '''
300
            @history    2018.06.08  Jeongwoo    Add parameter on round method
301
            @history    2018.11.02  euisung     Add save note text item
302
            @history    2018.11.05  euisung     delete save note text item and move to drawDetectedItems()
303
                        2018.11.26  euisung     remove scene dependency
304
                        2018.11.29  euisung     change name drawDetectedTextItem() -> createDetectedTextItem
305
        '''
306

    
307
    def create_detected_text_item(self, textInfoList):
308
        from TextItemFactory import TextItemFactory
309
        import math
310

    
311
        try:
312
            app_doc_data = AppDocData.instance()
313

    
314
            # parse texts
315
            for textInfo in textInfoList:
316
                x = textInfo.getX()
317
                y = textInfo.getY()
318
                width = textInfo.getW()
319
                height = textInfo.getH()
320
                angle = round(math.radians(textInfo.getAngle()), 2)
321
                text = textInfo.getText()
322
                if not text: continue
323

    
324
                item = TextItemFactory.instance().createTextItem(textInfo)
325
                if item is not None:
326
                    item.loc = [x, y]
327
                    item.size = (width, height)
328
                    item.angle = angle
329
                    item.area = 'Drawing'
330
                    #item.transfer.onRemoved.connect(self.itemRemoved)
331
                    # self.addTextItemToScene(item)
332
                    app_doc_data.texts.append(item)
333
                    app_doc_data.allItems.append(item)
334
        except Exception as ex:
335
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
336
                                                           sys.exc_info()[-1].tb_lineno)
337
            self.displayLog.emit(MessageType.Error, message)
338

    
339
    '''
340
        @brief      draw detected texts except which in drawing area
341
        @history    2018.11.29  euisung     change name drawDetectedOtherTextItem() -> createDetectedOtherTextItem
342
    '''
343

    
344
    def create_detected_other_text_item(self, otherTextInfoList):
345
        from TextItemFactory import TextItemFactory
346
        import math
347

    
348
        try:
349
            app_doc_data = AppDocData.instance()
350

    
351
            # parse notes
352
            for textInfoMap in otherTextInfoList:
353
                if textInfoMap[0] == 'Note' or textInfoMap[1] is None:
354
                    pass
355

    
356
                for textInfo in textInfoMap[1]:
357
                    x = textInfo.getX()
358
                    y = textInfo.getY()
359
                    width = textInfo.getW()
360
                    height = textInfo.getH()
361
                    angle = round(math.radians(textInfo.getAngle()))
362
                    text = textInfo.getText()
363

    
364
                    item = TextItemFactory.instance().createTextItem(textInfo)
365

    
366
                    item.loc = [x, y]
367
                    item.size = (width, height)
368
                    item.angle = angle
369
                    item.area = textInfoMap[0]
370
                    #item.transfer.onRemoved.connect(self.itemRemoved)
371
                    app_doc_data.texts.append(item)
372
                    app_doc_data.allItems.append(item)
373
        except Exception as ex:
374
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
375
                                                           sys.exc_info()[-1].tb_lineno)
376
            self.displayLog.emit(MessageType.Error, message)
377

    
378
    def create_detected_title_block_text_item(self, textInfoList):
379
        """draw title block"""
380
        from TextItemFactory import TextItemFactory
381
        import math
382

    
383
        try:
384
            app_doc_data = AppDocData.instance()
385

    
386
            # parse texts
387
            for textInfos in textInfoList:
388
                if len(textInfos[1]) is 0:
389
                    continue
390

    
391
                for textInfo in textInfos[1]:
392
                    x = textInfo.getX()
393
                    y = textInfo.getY()
394
                    width = textInfo.getW()
395
                    height = textInfo.getH()
396
                    angle = round(math.radians(textInfo.getAngle()), 2)
397
                    text = textInfo.getText()
398
                    if not text: continue
399
                    item = TextItemFactory.instance().createTextItem(textInfo)
400

    
401
                    if item is not None:
402
                        item.loc = [x, y]
403
                        item.size = (width, height)
404
                        item.angle = angle
405
                        item.area = textInfos[0]
406
                        # self.addTextItemToScene(item)
407
                        app_doc_data.texts.append(item)
408
                        app_doc_data.allItems.append(item)
409
        except Exception as ex:
410
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
411
                                                           sys.exc_info()[-1].tb_lineno)
412
            self.displayLog.emit(MessageType.Error, message)
413

    
414
    '''
415
        @brief  draw unknown items 
416
        @author humkyung
417
        @date   2018.06.12
418
        @history    2018.06.14  Jeongwoo    Change method to add unknown item
419
                    2018.06.18  Jeongwoo    Add connect on unknown item
420
                                            Add [transfer] for using pyqtSignal
421
                    2018.11.26  euisung     remove scene dependency
422
                    2018.11.26  euisung     isolate scene adding part -> drawDetectedItemsToScene()
423
                    2018.11.27  euisung     add save to xml
424
                    2018.11.29  euisung     change name drawUnknownItems() -> createUnknownItems
425
    '''
426

    
427
    def create_unknown_items(self, path):
428
        from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
429
        from EngineeringLineItem import QEngineeringLineItem
430
        from EngineeringUnknownItem import QEngineeringUnknownItem
431

    
432
        try:
433
            app_doc_data = AppDocData.instance()
434
            project = app_doc_data.getCurrentProject()
435
            windowSize = app_doc_data.getSlidingWindowSize()
436

    
437
            thickness = int(windowSize[1] / 2)
438

    
439
            area = app_doc_data.getArea('Drawing')
440
            diffFilePath = os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(path))
441

    
442
            # remove line from image
443
            imgDiff = np.ones(app_doc_data.imgSrc.shape, np.uint8) * 255
444
            imgDiff[round(area.y + 1):round(area.y + area.height),
445
                           round(area.x + 1):round(area.x + area.width)] = \
446
                app_doc_data.imgSrc[round(area.y + 1):round(area.y + area.height),
447
                           round(area.x + 1):round(area.x + area.width)]
448

    
449
            lines = app_doc_data.lines
450
            for line in lines:
451
                line.drawToImage(imgDiff, 255, thickness) if line.thickness is None else \
452
                    line.drawToImage(imgDiff, 255, line.thickness)
453
            # up to here
454
            cv2.imwrite(diffFilePath, imgDiff)
455

    
456
            imgNot = np.ones(imgDiff.shape, np.uint8)
457
            cv2.bitwise_not(imgDiff, imgNot)
458
            configs = app_doc_data.getConfigs('Filter', 'ErodeSize')
459
            kernel = int(configs[0].value) if 1 == len(configs) else 3
460
            imgNot = cv2.erode(imgNot, np.ones((kernel, kernel), np.uint8))
461
            imgNot = cv2.dilate(imgNot, np.ones((8 + kernel, 8 + kernel), np.uint8))
462

    
463
            contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
464

    
465
            ##
466
            idx = 0
467
            ##
468
            smallContours = []
469
            minimumSize = app_doc_data.getConfigs('Filter', 'MinimumSize')
470
            for contour in contours:
471
                [x, y, w, h] = cv2.boundingRect(contour)
472

    
473
                # remove too small one
474
                if len(minimumSize) is 1:
475
                    if w * h < int(minimumSize[0].value) * int(minimumSize[0].value):
476
                        smallContours.append(contour)
477
                        idx += 1
478
                        continue
479

    
480
                # create unknown item
481
                epsilon = cv2.arcLength(contour, True) * 0.001
482
                approx = cv2.approxPolyDP(contour, epsilon, True)
483
                approx = [pt[0] for pt in approx]
484
                resultStr, resultList = self.determine_remain_object(idx, contours, imgNot)
485
                if resultStr == 'LineIndicator':
486
                    item = QEngineeringUnknownItem(approx, 'True', resultList[0], resultList[1])
487
                    app_doc_data.lineIndicators.append(item)
488
                elif resultStr == 'MissingLine':
489
                    idx += 1
490
                    continue
491
                elif resultStr == 'Unknown':
492
                    item = QEngineeringUnknownItem(approx, 'False')
493
                    app_doc_data.unknowns.append(item)
494
                item.area = 'Drawing'
495
                app_doc_data.allItems.append(item)
496
                idx += 1
497
                # up to here
498

    
499
            """
500
            if app_doc_data.needReOpening is not None:
501
                app_doc_data.needReOpening = True
502
            """
503

    
504
            """
505
            diffFilePath = os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(path))
506
            if os.path.isfile(diffFilePath):
507
                imgDiff = cv2.threshold(cv2.cvtColor(cv2.imread(diffFilePath, 1), cv2.COLOR_BGR2GRAY), 0, 255,
508
                                        cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
509

510
                # remove line from image
511
                lines = app_doc_data.lines
512
                for line in lines:
513
                    line.drawToImage(imgDiff, 255, thickness) if line.thickness is None else \
514
                        line.drawToImage(imgDiff, 255, line.thickness)
515
                cv2.imwrite(diffFilePath, imgDiff)
516
                # up to here
517

518
                imgNot = np.ones(imgDiff.shape, np.uint8)
519
                cv2.bitwise_not(imgDiff, imgNot)
520
                configs = app_doc_data.getConfigs('Filter', 'ErodeSize')
521
                kernel = int(configs[0].value) if 1 == len(configs) else 3
522
                imgNot = cv2.erode(imgNot, np.ones((kernel, kernel), np.uint8))
523
                imgNot = cv2.dilate(imgNot, np.ones((8 + kernel, 8 + kernel), np.uint8))
524

525
                contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
526

527
                ##
528
                idx = 0
529
                ##
530
                smallContours = []
531
                minimumSize = app_doc_data.getConfigs('Filter', 'MinimumSize')
532
                for contour in contours:
533
                    [x, y, w, h] = cv2.boundingRect(contour)
534

535
                    # remove too small one
536
                    if len(minimumSize) is 1:
537
                        if w * h < int(minimumSize[0].value) * int(minimumSize[0].value):
538
                            smallContours.append(contour)
539
                            idx += 1
540
                            continue
541

542
                    '''
543
                    rect = QRectF(x, y, w, h)
544
                    items = [item for item in diffItems if item.boundingRect().contains(rect)]
545
                    if len(items) > 0: continue
546

547
                    items = [item for item in diffItems if rect.contains(item.boundingRect())]
548
                    for item in items:
549
                        diffItems.remove(item)
550
                    '''
551

552
                    # create unknown item
553
                    epsilon = cv2.arcLength(contour, True) * 0.001
554
                    approx = cv2.approxPolyDP(contour, epsilon, True)
555
                    approx = [pt[0] for pt in approx]
556
                    resultStr, resultList = self.determine_remain_object(idx, contours, imgNot)
557
                    if resultStr == 'LineIndicator':
558
                        item = QEngineeringUnknownItem(approx, 'True', resultList[0], resultList[1])
559
                        app_doc_data.lineIndicators.append(item)
560
                    elif resultStr == 'MissingLine':
561
                        pass
562
                    elif resultStr == 'Unknown':
563
                        item = QEngineeringUnknownItem(approx, 'False')
564
                        app_doc_data.unknowns.append(item)
565
                    item.area = 'Drawing'
566
                    app_doc_data.allItems.append(item)
567
                    #item.transfer.onRemoved.connect(self.itemRemoved)
568
                    idx += 1
569
                    # up to here
570

571
                imgNotRemoveSmall = cv2.drawContours(imgNot, smallContours, -1, 0, -1)
572
                notFilePath = os.path.join(project.getTempPath(), "NOT_" + os.path.basename(path))
573
                cv2.imwrite(notFilePath, imgNotRemoveSmall)
574
            else:
575
                message = 'can\'t found {}'.format(diffFilePath)
576
                self.displayLog.emit(MessageType.Normal, message)
577
            """
578

    
579
        except Exception as ex:
580
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
581
                                                           sys.exc_info()[-1].tb_lineno)
582
            self.displayLog.emit(MessageType.Error, message)
583

    
584
    def determine_remain_object(self, idx, contours, imgNot):
585
        """determine remain objects -> line no indicator or unknown"""
586
        import math
587
        [x, y, w, h] = cv2.boundingRect(contours[idx])
588

    
589
        if w < 250 and h < 250:
590
            return ('Unknown', [])
591

    
592
        fLines = []
593
        maxDifAngle = 3
594
        mask = np.zeros_like(imgNot)
595
        cv2.drawContours(mask, contours, idx, 123, -1)  # Draw filled contour in mask
596
        out = np.zeros_like(imgNot)  # Extract out the object and place into output image
597
        out[mask == 123] = imgNot[mask == 123]
598

    
599
        # Now crop
600
        ##print(out)
601
        (x, y) = np.where(mask == 123)
602
        (topx, topy) = (np.min(x), np.min(y))
603
        (bottomx, bottomy) = (np.max(x), np.max(y))
604
        out = out[topx:bottomx + 1, topy:bottomy + 1]
605
        h, w = out.shape[0], out.shape[1]
606
        maxDifH, maxDifW = math.ceil(math.tan(4 * math.pi / 180) / 2 * w), math.ceil(
607
            math.tan(4 * math.pi / 180) / 2 * h)
608

    
609
        # detection lines
610
        edged2 = cv2.Canny(out, 100, 200)
611
        lines = cv2.HoughLinesP(image=edged2, rho=1, theta=np.pi / 180, threshold=25, minLineLength=30, maxLineGap=25)
612
        # lines = cv2.HoughLines(edged2, 1, np.pi/180, 60)
613
        if lines is None:
614
            return ('Unknown', [])
615
        for line in lines:
616
            # r, theta = line[0]
617
            # a, b = np.cos(theta), np.sin(theta)
618
            # x0, y0 = a * r, b * r
619
            # x1, y1 = int(x0 + 1000 * (-b)), int(y0 + 1000 * a)
620
            # x2, y2 = int(x0 - 1000 * (-b)), int(y0 - 1000 * a)
621
            # cv2.line(out, (x1, y1), (x2, y2), (0, 255, 0), 3)
622
            x1, y1, x2, y2 = line[0]
623
            degree = math.atan2(y2 - y1, x2 - x1) * 180 / math.pi
624
            fLine = [x1, y1, x2, y2, degree]
625
            # print(fLine)
626
            fLines.append(fLine)
627

    
628
        horLines = []
629
        verLines = []
630
        otherLines = []
631
        isVH = None
632
        for fLine in fLines:
633
            degree = math.fabs(fLine[4])
634
            if degree >= 90 - maxDifAngle:
635
                verLines.append(fLine)
636
            elif degree <= maxDifAngle:
637
                horLines.append(fLine)
638
            else:
639
                otherLines.append(fLine)
640

    
641
        baseLines = []
642
        baseDifV = 0
643
        if len(horLines):
644
            x, y = w / 2, 0
645
            baseDifV = maxDifH
646
            for horLine in horLines:
647
                x1, y1, x2, y2 = horLine[0], horLine[1], horLine[2], horLine[3]
648
                y = ((y2 - y1) / (x2 - x1)) * x + y1 - ((y2 - y1) / (x2 - x1)) * x1
649
                horLine.append(y)
650
            baseLines = horLines
651
            isVH = 'H'
652
        if len(verLines):
653
            x, y = 0, h / 2
654
            baseDifV = maxDifW
655
            for verLine in verLines:
656
                x1, y1, x2, y2 = verLine[0], verLine[1], verLine[2], verLine[3]
657
                x = ((x2 - x1) / (y2 - y1)) * y + x1 - ((x2 - x1) / (y2 - y1)) * y1
658
                verLine.append(x)
659
            baseLines = verLines
660
            isVH = 'V'
661

    
662
        for otherLine in otherLines:
663
            x, y = w / 2, 0
664
            x1, y1, x2, y2 = otherLine[0], otherLine[1], otherLine[2], otherLine[3]
665
            y = ((y2 - y1) / (x2 - x1)) * x + y1 - ((y2 - y1) / (x2 - x1)) * x1
666
            otherLine.append(y)
667

    
668
        # determine line no indicator
669
        if not ((len(horLines) > 0 and len(verLines) > 0) or len(otherLines) is 0 or (
670
                len(horLines) == 0 and len(verLines) == 0)):
671
            result, mergedOtherLine = self.is_line_no_indicator(w, h, maxDifAngle, baseDifV, baseLines, otherLines)
672
            if result:
673
                # print(fLines)
674
                return ('LineIndicator', [isVH, mergedOtherLine])
675

    
676
        return ('Unknown', [])
677

    
678
    def is_line_no_indicator(self, w, h, maxDifAngle, baseDifV, baseLines, otherLines):
679
        """determine line no indicator"""
680
        import math
681

    
682
        if w < 250 and h < 250:
683
            return (False, None)
684

    
685
        isSameLine = True
686
        i = 0
687
        for baseLine in baseLines:
688
            if not isSameLine: break
689
            j = 0
690
            for baseLinee in baseLines:
691
                if i == j:
692
                    j += 1
693
                    continue
694
                difV = math.fabs(baseLine[5] - baseLinee[5])
695
                if difV > baseDifV:
696
                    isSameLine = False
697
                    break
698
                j += 1
699
            i += 1
700
        if not isSameLine:
701
            return (False, None)
702

    
703
        isSameLine = True
704
        i = 0
705
        maxY = 0
706
        for otherLine in otherLines:
707
            y = otherLine[5]
708
            if math.fabs(y) > maxY:
709
                maxY = math.fabs(y)
710
            if not isSameLine: break
711
            j = 0
712
            for otherLinee in otherLines:
713
                if i == j:
714
                    j += 1
715
                    continue
716
                difV = math.fabs(otherLine[4] - otherLinee[4])
717
                if difV > maxDifAngle:
718
                    isSameLine = False
719
                    break
720
                j += 1
721
            i += 1
722
        if not isSameLine:
723
            return (False, None)
724

    
725
        isSameLine = True
726
        mergedOtherLine = [0, 0, 0, 0]
727
        i = 0
728
        maxDif = math.ceil(math.tan(4 * math.pi / 180) * maxY)
729
        for otherLine in otherLines:
730
            if not isSameLine: break
731
            j = 0
732
            for otherLinee in otherLines:
733
                if i == j:
734
                    j += 1
735
                    continue
736
                angle = math.fabs(otherLine[4] + otherLinee[4]) / 2
737
                difV = math.fabs(otherLine[5] - otherLinee[5])
738
                dist = math.sin((90 - angle) * math.pi / 180) * difV
739
                if dist > maxDif:
740
                    isSameLine = False
741
                    break
742
                j += 1
743
            i += 1
744
            mergedOtherLine[0] += otherLine[0]
745
            mergedOtherLine[1] += otherLine[1]
746
            mergedOtherLine[2] += otherLine[2]
747
            mergedOtherLine[3] += otherLine[3]
748
        if not isSameLine:
749
            (False, None)
750

    
751
        # Show the output image
752
        # print('line no indicator')
753
        mergedOtherLine[0] = round(mergedOtherLine[0] / len(otherLines))
754
        mergedOtherLine[1] = round(mergedOtherLine[1] / len(otherLines))
755
        mergedOtherLine[2] = round(mergedOtherLine[2] / len(otherLines))
756
        mergedOtherLine[3] = round(mergedOtherLine[3] / len(otherLines))
757
        # cv2.line(out, (mergedOtherLine[0], mergedOtherLine[1]), (mergedOtherLine[2], mergedOtherLine[3]), (255, 255, 255), 3)
758
        # cv2.imshow('Output', out)
759
        # cv2.waitKey(0)
760
        # cv2.destroyAllWindows()
761
        return (True, mergedOtherLine)
762

    
763
    def load_equipment_package(self, drawing):
764
        from LoadCommand import LoadCommand
765

    
766
        try:
767
            """load equipment package"""
768
            cmd = LoadCommand()
769
            cmd.execute((drawing, self.scene), symbol=False, text=False, line=False, unknown=False,
770
                        package=True ,update=False)
771
            """up to here"""
772
        except Exception as ex:
773
            from App import App
774
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
775
                                                           sys.exc_info()[-1].tb_lineno)
776
            App.mainWnd().addMessage.emit(MessageType.Error, message)
777

    
778
    @staticmethod
779
    def executeRecognition(drawings, listWidget, isLineChecked, worker):
780
        """recognize symbol, text, line from image p&id"""
781
        import re
782
        import timeit
783
        from TextDetector import TextDetector
784
        from Drawing import Drawing
785
        from datetime import datetime
786

    
787
        global ocrCompletedSrc
788
        global threadLock
789
        global searchedSymbolList
790
        global textInfoList
791
        global maxProgressValue
792

    
793
        try:
794
            app_doc_data = AppDocData.instance()
795
            #drawings = app_doc_data.getDrawings()
796
            project = app_doc_data.getCurrentProject()
797
            textDetector = TextDetector()
798

    
799
            worker.scene.clear()
800

    
801
            Worker.initTargetSymbolDataList()
802

    
803
            for drawing in drawings:
804
                ocrCompletedSrc = []
805
                searchedSymbolList = []
806
                textInfoList = []
807

    
808
                mainRes = drawing.file_path
809
                if not os.path.isfile(mainRes):
810
                    item = QListWidgetItem('{} file is not found'.format(os.path.basename(mainRes)))
811
                    item.setBackground(Qt.red)
812
                    listWidget.addItem(item)
813
                    continue
814

    
815
                app_doc_data.clearItemList(True)
816

    
817
                app_doc_data.setImgFilePath(mainRes)
818
                app_doc_data.imgSrc = None
819
                matches = [drawing for drawing in drawings if app_doc_data.imgName == os.path.splitext(drawing.name)[0]]
820
                app_doc_data.activeDrawing = drawing # matches[0] if matches else Drawing(None, app_doc_data.imgName, None)
821
                app_doc_data.activeDrawing.set_pid_source(Image.open(mainRes))
822

    
823
                # Load equipment package
824
                #worker.load_equipment_package(app_doc_data.activeDrawing)
825

    
826
                # remove not drawing area
827
                configs = app_doc_data.getConfigs('{} Equipment Desc Area'.format(app_doc_data.imgName))
828
                for config in configs:
829
                    found = re.findall('\\d+', config.value)
830
                    if len(found) == 4:
831
                        cv2.rectangle(app_doc_data.imgSrc, (int(found[0]), int(found[1])),
832
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
833

    
834
                configs = app_doc_data.getConfigs('{} Typical Area'.format(app_doc_data.imgName))
835
                for config in configs:
836
                    found = re.findall('\\d+', config.value)
837
                    if len(found) == 4:
838
                        cv2.rectangle(app_doc_data.imgSrc, (int(found[0]), int(found[1])),
839
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
840

    
841
                noteArea = app_doc_data.getArea('Note')
842
                if noteArea is not None:
843
                    noteArea.img = app_doc_data.imgSrc[round(noteArea.y):round(noteArea.y + noteArea.height),
844
                                   round(noteArea.x):round(noteArea.x + noteArea.width)].copy()
845
                    cv2.rectangle(app_doc_data.imgSrc, (round(noteArea.x), round(noteArea.y)),
846
                                  (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
847
                    #cv2.imwrite('c:\\temp\\note.png', noteArea.img)
848
                    #cv2.imwrite('c:\\temp\\removed_note.png', app_doc_data.imgSrc)
849
                # up to here
850

    
851
                area = app_doc_data.getArea('Drawing')
852
                if area is not None:
853
                    # copy image
854
                    area.img = app_doc_data.imgSrc[round(area.y):round(area.y + area.height),
855
                               round(area.x):round(area.x + area.width)]
856

    
857
                    _, area.mask = cv2.threshold(area.img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
858

    
859
                    # area.contours, area.hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
860
                    area.not_img = cv2.bitwise_not(area.img)
861
                    area.contours, area.hierachy = cv2.findContours(area.not_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
862

    
863
                maxProgressValue = 0
864
                start_time = timeit.default_timer()
865
                listWidget.addItem(f"Start recognition {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} : {mainRes}")
866
                threadLock.acquire()
867

    
868
                worker.updateBatchProgress.emit(len(drawings), 1)
869

    
870
                if worker.isSymbolChecked:
871
                    # calculate total count of symbol
872
                    for targetItem in targetSymbolList:
873
                        if type(targetItem) is list:
874
                            maxProgressValue += len(targetItem)
875
                        else:
876
                            maxProgressValue += 1
877
                    # up to here
878
                    maxProgressValue = maxProgressValue * 2
879
                threadLock.release()
880

    
881
                if worker.isSymbolChecked:
882
                    worker.displayTitle.emit(worker.tr('Detecting symbols...'))
883

    
884
                    # detect only equipments
885
                    with futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER) as pool:
886
                        future_equipment = {pool.submit(Worker.detectSymbolsOnPid, mainRes, symbol, listWidget, worker):
887
                                             symbol for symbol in targetSymbolList[0]}
888
                        futures.wait(future_equipment)
889
                    # up to here
890

    
891
                    # detect normal symbols
892
                    with futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER) as pool:
893
                        future_symbol = {pool.submit(Worker.detectSymbolsOnPid, mainRes, symbol, listWidget, worker):
894
                                           symbol for symbol in targetSymbolList[2]}
895
                        futures.wait(future_symbol)
896
                    # up to here
897

    
898
                    if worker.isTrainingChecked:
899
                        import uuid
900
                        worker.displayTitle.emit(worker.tr('Generating Data...'))
901
                        listWidget.addItem('Generating Data...')
902

    
903
                        for item in searchedSymbolList:
904
                            path = os.path.join(project.getTempPath(), 'Tile', item.getBaseSymbol())
905
                            if not os.path.exists(path): os.makedirs(path)
906

    
907
                            _img = app_doc_data.imgSrc[round(item.getSp()[1]):round(item.getSp()[1] + item.getHeight()),
908
                                   round(item.getSp()[0]):round(item.getSp()[0] + item.getWidth())]
909
                            cv2.imwrite(os.path.join(path, str(uuid.uuid4()) + '.png'), _img)
910
                        worker.updateBatchProgress.emit(len(srcList), 4)
911
                        continue
912

    
913
                    #cv2.imwrite('c:\\temp\\before-imgSrc.png', area.img)
914
                    # save original image before remove symbol
915
                    worker.copy_imgSrc = app_doc_data.imgSrc.copy()
916
                    searchedTextSymList = []
917
                    for sym in searchedSymbolList:
918
                        Worker.remove_detected_symbol_image(sym, app_doc_data.imgSrc)
919
                else:
920
                    '''
921
                    import math
922

923
                    symbolItems = [item for item in worker.scene.items() if issubclass(type(item), SymbolSvgItem)]
924
                    
925
                    for symbolItem in symbolItems:
926
                        symbolName = symbolItem.name
927
                        symbolType = symbolItem.type
928
                        searchedItemSp = [int(symbolItem.loc[0]), int(symbolItem.loc[1])]
929
                        sw = symbolItem.size[0] # check later
930
                        sh = symbolItem.size[1] # check later
931

932
                        symbolThreshold = 50 # not necessary
933
                        symbolMinMatchCount = 0 # not necessary
934
                        hitRate = 80 # not necessary
935
                        
936
                        allowed_error = 0.0001
937
                        #degree 0
938
                        if abs(symbolItem.angle - 0) <= allowed_error:
939
                            symbolRotatedAngle = 0
940
                        #degree 90
941
                        elif abs(symbolItem.angle - 1.57) <= allowed_error:
942
                            symbolRotatedAngle = 90
943
                        #degree 180
944
                        elif abs(symbolItem.angle - 3.14) <= allowed_error:
945
                            symbolRotatedAngle = 180
946
                        #degree 270
947
                        elif abs(symbolItem.angle - 4.71) <= allowed_error:
948
                            symbolRotatedAngle = 270
949
                        else:
950
                            symbolRotatedAngle = math.pi / 180 * symbolItem.angle
951

952
                        isDetectOnOrigin = 80 # not necessary
953
                        symbolRotateCount = 0 # not necessary
954
                        symbolOcrOption = 0 # not necessary
955
                        isContainChild = 0 # not necessary
956
                        originalPoint = [symbolItem.origin[0] - symbolItem.loc[0], symbolItem.origin[1] - symbolItem.loc[1]]
957
                        symbolConnectionPoint = []
958
                        baseSymbol = str(symbolItem.parentSymbol)
959
                        additionalSymbol = str(symbolItem.childSymbol)
960
                        isExceptDetect = 0 # not necessary
961
                        detectFlip = symbolItem.flip
962

963
                        searchedSymbolList.append(Symbol(symbolName, symbolType , 
964
                            searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, hitRate, symbolRotatedAngle , 
965
                            isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild , 
966
                            ','.join(str(x) for x in originalPoint), 
967
                            [], 
968
                            baseSymbol, additionalSymbol, isExceptDetect, detectFlip))
969
                        
970
                        worker.scene.removeItem(symbolItem)
971
                    
972
                    pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
973
                    '''
974
                    # symbolItems = [item for item in worker.scene.items() if issubclass(type(item), SymbolSvgItem)]
975
                    # appDocData.symbols.extend(symbolItems)
976
                    # appDocData.allItems.extend(symbolItems)
977

    
978
                    # for sym in searchedSymbolList:
979
                    #    pool.submit(Worker.remove_detected_symbol_image, sym, appDocData.imgSrc)
980
                    # pool.shutdown(wait = True)
981

    
982
                worker.updateBatchProgress.emit(len(drawings), 1)
983

    
984
                area = app_doc_data.getArea('Drawing')
985
                if area is not None:
986
                    # copy area image
987
                    area.img = app_doc_data.imgSrc[round(area.y):round(area.y + area.height),
988
                               round(area.x):round(area.x + area.width)]
989

    
990
                offset = (area.x, area.y) if area is not None else (0, 0)
991

    
992
                otherTextInfoList = None
993
                titleBlockTextInfoList = None
994
                textInfoList = []
995
                if worker.isTextChecked:
996
                    from TextInfo import TextInfo
997

    
998
                    textAreas, ocr_image = textDetector.detectTextAreas(area.img if area is not None else app_doc_data.imgSrc,
999
                                                             offset)
1000

    
1001
                    # get text area by using symbol setting
1002
                    text_area_symbols = []
1003
                    for symbol in [symbol for symbol in searchedSymbolList if symbol.text_area]:
1004
                        symbol_angle = symbol.getRotatedAngle()
1005
                        for text_area in symbol.text_area:
1006
                            so = [symbol.getWidth(), symbol.getHeight()] if symbol_angle is 0 or symbol_angle is 180\
1007
                                            else [symbol.getHeight(), symbol.getWidth()]
1008
                            text_area_origin = Worker.getCalculatedOriginalPoint(None, str(text_area.center()[0]) + ',' + str(text_area.center()[1]),\
1009
                                                                      symbol_angle, None, None, so[0], so[1], symbol.getDetectFlip())
1010
                            x = text_area_origin[0] - round(text_area.width / 2) if symbol_angle is 0 or symbol_angle is 180\
1011
                                else text_area_origin[0] - round(text_area.height / 2)
1012
                            x = x + symbol.sp[0]
1013
                            y = text_area_origin[1] - round(text_area.height / 2) if symbol_angle is 0 or symbol_angle is 180\
1014
                                else text_area_origin[1] - round(text_area.width / 2)
1015
                            y = y + symbol.sp[1]
1016
                            w = text_area.width if symbol_angle is 0 or symbol_angle is 180 else text_area.height
1017
                            h = text_area.height if symbol_angle is 0 or symbol_angle is 180 else text_area.width
1018

    
1019
                            if symbol_angle is 180 or symbol_angle is 0:
1020
                                text_angle = 0
1021
                            else:
1022
                                text_angle = 90
1023
                            text_area_symbols.append(TextInfo('', x, y, w, h, text_angle))
1024

    
1025
                    for textArea_index in reversed(range(len(textAreas))):
1026
                        is_pop = False
1027
                        for text_area_index in reversed(range(len(text_area_symbols))):
1028
                            if text_area_symbols[text_area_index].contains(textAreas[textArea_index].center) or \
1029
                                    textAreas[textArea_index].contains(text_area_symbols[text_area_index].center):
1030
                                if text_area_symbols[text_area_index].area > textAreas[textArea_index].area:
1031
                                    textAreas.pop(textArea_index)
1032
                                    is_pop = True
1033
                                    break
1034
                                else:
1035
                                    text_area_symbols.pop(text_area_index)
1036
                                    break
1037
                            if is_pop:
1038
                                break
1039
                    textAreas.extend(text_area_symbols)
1040
                    # up to here
1041

    
1042
                    if maxProgressValue < 2 * len(textAreas):
1043
                        for _ in range(len(textAreas) - int(maxProgressValue * 0.5)):
1044
                            worker.updateProgress.emit(2 * len(textAreas), None)
1045
                        maxProgressValue = 2 * len(textAreas)
1046
                    else:
1047
                        maxProgressValue = int(maxProgressValue * 0.5) + len(textAreas)
1048

    
1049
                    worker.displayTitle.emit(worker.tr('Detecting texts...'))
1050
                    textDetector.recognizeText(area.img, offset, textAreas, searchedSymbolList, worker,
1051
                                               listWidget, maxProgressValue)
1052
                    textInfoList = textDetector.textInfoList.copy() if textDetector.textInfoList is not None else None
1053
                    otherTextInfoList = textDetector.otherTextInfoList.copy() if textDetector.otherTextInfoList is not None else None
1054
                    titleBlockTextInfoList = textDetector.titleBlockTextInfoList.copy() if textDetector.titleBlockTextInfoList is not None else None
1055

    
1056
                    app_doc_data.activeDrawing.width, app_doc_data.activeDrawing.height = \
1057
                        app_doc_data.imgSrc.shape[::-1]
1058

    
1059
                    app_doc_data.imgName = os.path.splitext(os.path.basename(mainRes))[0]
1060
                else:
1061
                    from LoadCommand import LoadCommand
1062
                    import math
1063
                    from TextInfo import TextInfo
1064

    
1065
                    """load texts"""
1066
                    cmd = LoadCommand()
1067
                    cmd.execute((drawing, worker.scene), symbol=False, text=True, line=False, unknown=False,
1068
                                package=False, update=False)
1069
                    """up to here"""
1070
                    textItems = [item for item in worker.scene.items() if issubclass(type(item), QEngineeringTextItem)]
1071
                    app_doc_data.texts.extend(textItems)
1072
                    app_doc_data.allItems.extend(textItems)
1073

    
1074
                    lineNoTextItems = [item for item in textItems if type(item) is QEngineeringLineNoTextItem]
1075
                    for lineNoTextItem in lineNoTextItems:
1076
                        lineNoTextItem.set_property('Freeze', False)
1077
                        # lineNoTextItem.freeze_item.update_freeze(lineNoTextItem.prop('Freeze'))
1078
                        lineNoTextItem.explode()
1079

    
1080
                    for textItem in textItems:
1081
                        textInfoList.append(
1082
                            TextInfo(textItem.text(), textItem.loc[0], textItem.loc[1], textItem.size[0],
1083
                                     textItem.size[1], round(math.degrees(textItem.angle))))
1084

    
1085
                        textItem.owner = None
1086
                        worker.scene.removeItem(textItem)
1087

    
1088
                worker.updateBatchProgress.emit(len(drawings), 2)
1089

    
1090
                # check symbol validate
1091
                #valid_sym = []
1092
                for index in reversed(range(len(searchedTextSymList))):
1093
                    sym_xmin = searchedTextSymList[index].getSp()[0]
1094
                    sym_ymin = searchedTextSymList[index].getSp()[1]
1095
                    sym_xmax = searchedTextSymList[index].getSp()[0] + searchedTextSymList[index].getWidth()
1096
                    sym_ymax = searchedTextSymList[index].getSp()[1] + searchedTextSymList[index].getHeight()
1097
                    valid_count = searchedTextSymList[index].getHasInstrumentLabel()
1098
                    valid = 0
1099
                    for text_info in textInfoList:
1100
                        info_center = text_info.center
1101
                        if sym_xmin < info_center[0] < sym_xmax and sym_ymin < info_center[1] < sym_ymax:
1102
                            valid += 1
1103
                            break
1104
                            #if valid >= valid_count:
1105
                            #    valid_sym.append(searchedTextSymList[index])
1106
                            #    break
1107
                    if valid < valid_count:
1108
                        searchedSymbolList.pop(searchedSymbolList.index(searchedTextSymList[index]))
1109

    
1110
                # roll back because invalidated symbol was deleted for text detection
1111
                app_doc_data.imgSrc = worker.copy_imgSrc
1112

    
1113
                for sym in searchedSymbolList:
1114
                        Worker.remove_detected_symbol_image(sym, app_doc_data.imgSrc)
1115
                #pool = futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER)
1116
                #for sym in valid_sym:
1117
                #    pool.submit(Worker.remove_detected_symbol_image, sym, app_doc_data.imgSrc)
1118
                #pool.shutdown(wait=True)
1119
                # up to here
1120

    
1121
                # remove text from image
1122
                textDetector.remove_text_from_image(app_doc_data.imgSrc, [0, 0])
1123
                #textDetector.remove_text_from_image(area.img, offset)
1124
                if not worker.isTextChecked:
1125
                    textInfoList.clear()
1126
                # up to here
1127

    
1128
                removedSymbolImgPath = os.path.join(project.getTempPath(), os.path.basename(mainRes))
1129
                cv2.imwrite(removedSymbolImgPath, app_doc_data.imgSrc)
1130

    
1131
                listWidget.addItem("Recognized symbol count : " + str(len(searchedSymbolList)))
1132

    
1133
                listWidget.addItem(worker.tr('Creating detected infos...'))
1134
                worker.displayTitle.emit(worker.tr('Creating detected infos...'))
1135
                worker.create_detected_items(searchedSymbolList, textInfoList,
1136
                                    otherTextInfoList if otherTextInfoList is not None else [],
1137
                                    titleBlockTextInfoList if titleBlockTextInfoList is not None else [])
1138

    
1139
                if isLineChecked:
1140
                    Worker.recognizeLine(mainRes, listWidget, worker.scene, worker)
1141
                else:
1142
                    lineItems = [item for item in worker.scene.items()
1143
                                 if issubclass(type(item), QEngineeringLineItem)]
1144
                    app_doc_data.lines.extend(lineItems)
1145
                    app_doc_data.allItems.extend(lineItems)
1146

    
1147
                    for lineItem in lineItems:
1148
                        lineItem.owner = None
1149
                        for conn in lineItem.connectors:
1150
                            conn.connectedItem = None
1151
                        worker.scene.removeItem(lineItem)
1152

    
1153
                # try to detect nozzle from image drawing
1154
                if worker.isSymbolChecked:
1155
                    worker.displayTitle.emit(worker.tr('Detecting nozzle and flange...'))
1156

    
1157
                    nozzles = []
1158
                    with futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER) as pool:
1159
                        future_nozzle = {pool.submit(Worker.detect_nozzles, mainRes, symbol, listWidget, worker):
1160
                                           symbol for symbol in targetSymbolList[1]}
1161
                        for future in futures.as_completed(future_nozzle):
1162
                            data = future.result()
1163
                            if data:
1164
                                nozzles.extend(data)
1165

    
1166
                    worker.create_detected_items(nozzles, [], [], [])
1167

    
1168
                    for nozzle in nozzles:
1169
                        Worker.remove_detected_symbol_image(nozzle, app_doc_data.imgSrc)
1170
                # up to here
1171

    
1172
                area = app_doc_data.getArea('Drawing')
1173
                if area is not None:
1174
                    area.img = app_doc_data.imgSrc[round(area.y + 1):round(area.y + area.height),
1175
                               round(area.x + 1):round(area.x + area.width)]
1176
                cv2.imwrite(os.path.join(project.getTempPath(), "RECT_" + os.path.basename(mainRes)),
1177
                            app_doc_data.imgSrc)
1178

    
1179
                Worker.drawFoundSymbolsOnCanvas(mainRes, searchedSymbolList, textInfoList, listWidget)
1180

    
1181
                # get difference between original and recognized image
1182
                foundFilePath = os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(mainRes))
1183
                Worker.getDifference(mainRes, foundFilePath)
1184
                # up to here
1185

    
1186
                # connect symbol to symbol
1187
                try:
1188
                    configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1189
                    toler = int(configs[0].value) if configs else 20
1190
                    for symbol in app_doc_data.symbols:
1191
                        matches = [it for it in app_doc_data.symbols if
1192
                                   it is not symbol and symbol.is_connectable(it, toler=toler)]
1193
                        for match in matches:
1194
                            symbol.connect_if_possible(match)
1195
                except Exception as ex:
1196
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1197
                                                                   sys.exc_info()[-1].tb_lineno)
1198
                    worker.displayLog.emit(MessageType.Error, message)
1199
                # up to here
1200

    
1201
                listWidget.addItem('Connecting lines')
1202
                #area = app_doc_data.getArea('Drawing')
1203
                detector = LineDetector(app_doc_data.imgSrc)
1204
                symbols = app_doc_data.symbols
1205
                configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1206
                toler = int(configs[0].value) if configs else 20
1207
                if app_doc_data.lines:
1208
                    # connect line to symbol
1209
                    try:
1210
                        for line in app_doc_data.lines:
1211
                            matches = [_symbol for _symbol in symbols if _symbol.is_connectable(line, toler=toler)]
1212
                            for _symbol in matches:
1213
                                detector.connectLineToSymbol(line, _symbol, toler=toler)
1214
                    except Exception as ex:
1215
                        message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[
1216
                            -1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1217
                        worker.displayLog.emit(MessageType.Error, message)
1218
                    # up to here
1219

    
1220
                    # connect line to line
1221
                    try:
1222
                        for line in app_doc_data.lines:
1223
                            matches = [it for it in app_doc_data.lines if
1224
                                       (it is not line) and (not line.isParallel(it))]
1225

    
1226
                            for match in matches:
1227
                                detector.connectLineToLine(match, line, toler)
1228

    
1229
                        # change line type using symbol connection type(info)
1230
                        for sym in symbols:
1231
                            if sym.conn_type:
1232
                                for index in range(len(sym.conn_type)):
1233
                                    item = sym.connectors[index].connectedItem
1234
                                    if item and type(item) is QEngineeringLineItem:
1235
                                        Worker.changeConnectedLineType(item, sym.conn_type[index])
1236
                    except Exception as ex:
1237
                        message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1238
                                  f"{sys.exc_info()[-1].tb_lineno}"
1239
                        worker.displayLog.emit(MessageType.Error, message)
1240
                    # up to here
1241

    
1242
                    # remove line has not connected item
1243
                    try:
1244
                        count = 1
1245
                        while count > 0:
1246
                            count = 0
1247
                            for line in reversed(app_doc_data.lines):
1248
                                if not line.has_connection:
1249
                                    app_doc_data.lines.remove(line)
1250
                                    app_doc_data.allItems.remove(line)
1251
                                    count += 1
1252
                                    for item in app_doc_data.lines + symbols:
1253
                                        for conn in item.connectors:
1254
                                            if conn.connectedItem is line:
1255
                                                conn.connectedItem = None
1256
                    except Exception as ex:
1257
                        message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1258
                                  f"{sys.exc_info()[-1].tb_lineno}"
1259
                        worker.displayLog.emit(MessageType.Error, message)
1260
                    # up to here
1261

    
1262
                worker.create_unknown_items(mainRes)
1263
                worker.add_detected_items_to_scene.emit(worker.scene)
1264
                worker.cond.wait(worker.mutex)
1265

    
1266
                worker.scene._end = False
1267
                # clear drawing
1268
                app_doc_data.activeDrawing.image = None
1269

    
1270
                worker.updateBatchProgress.emit(len(drawings), 1)
1271
        except Exception as ex:
1272
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
1273
                      f"{sys.exc_info()[-1].tb_lineno}"
1274
            worker.displayLog.emit(MessageType.Error, message)
1275
        finally:
1276
            pass
1277

    
1278
    @staticmethod
1279
    def changeConnectedLineType(line, lineType):
1280
        line.lineType = lineType
1281
        if type(line.connectors[0].connectedItem) is QEngineeringLineItem and \
1282
                (line.connectors[0].connectedItem.connectors[0].connectedItem is line or
1283
                 line.connectors[0].connectedItem.connectors[1].connectedItem is line) and \
1284
                line.connectors[0].connectedItem.lineType is not lineType:
1285
            Worker.changeConnectedLineType(line.connectors[0].connectedItem, lineType)
1286
        if type(line.connectors[1].connectedItem) is QEngineeringLineItem and \
1287
                (line.connectors[1].connectedItem.connectors[0].connectedItem is line or
1288
                 line.connectors[1].connectedItem.connectors[1].connectedItem is line) and \
1289
                line.connectors[1].connectedItem.lineType is not lineType:
1290
            Worker.changeConnectedLineType(line.connectors[1].connectedItem, lineType)
1291

    
1292
    '''
1293
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
1294
                    2018.05.28  Jeongwoo    Add xmlPath Parameter and append LineInfo into xml
1295
                    2018.05.29  Jeongwoo    Change method to add item
1296
                    2018.05.30  Jeongwoo    Remove parameter (xmlPath)
1297
                    humkyung 2018.06.11 add drawing path to parameter and write recognized lines to image
1298
                    humkyung 2018.07.04 call arrangeLinePosition after creating line
1299
    '''
1300

    
1301
    @staticmethod
1302
    def recognizeLine(path, listWidget, graphicsView, worker):
1303
        from shapely.geometry import Point, LineString
1304
        from SymbolSvgItem import SymbolSvgItem
1305
        from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
1306
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
1307
        from EngineeringTextItem import QEngineeringTextItem
1308
        from EngineeringLineItem import QEngineeringLineItem
1309
        from LineDetector import LineDetector
1310
        from EngineeringVendorItem import QEngineeringVendorItem
1311

    
1312
        try:
1313
            listWidget.addItem('Starting line recognition')
1314
            worker.displayTitle.emit(worker.tr('Detecting lines...'))
1315

    
1316
            app_doc_data = AppDocData.instance()
1317
            project = app_doc_data.getCurrentProject()
1318

    
1319
            # detect line
1320
            connectedLines = []
1321

    
1322
            area = app_doc_data.getArea('Drawing')
1323
            if area is not None:
1324
                area.img = app_doc_data.imgSrc[round(area.y + 1):round(area.y + area.height),
1325
                            round(area.x + 1):round(area.x + area.width)]
1326

    
1327
                for item in worker.scene.items():
1328
                    if issubclass(type(item), QEngineeringVendorItem) and item.pack_type =='Equipment Package':
1329
                        points = []
1330
                        for conn in item.connectors:
1331
                            points.append([conn.center()[0] - area.y, conn.center()[1] - area.y])
1332
                        
1333
                        points = np.array(points, np.int32)
1334
                        cv2.fillConvexPoly(area.img, points, 255)
1335
                #cv2.imshow('aa', area.img)
1336
                #cv2.waitKey(0)
1337
                #cv2.destroyAllWindows()
1338

    
1339
            area.img = worker.remove_small_objects(area.img)
1340
            detector = LineDetector(area.img)
1341

    
1342
            symbols = []
1343
            for item in app_doc_data.symbols:
1344
                if issubclass(type(item), SymbolSvgItem):
1345
                    symbols.append(item)
1346
                    res = detector.detectConnectedLine(item, round(area.x), round(area.y))
1347
                    if res is not None:
1348
                        connectedLines.extend(res)
1349

    
1350
            # line detection without symbol connection point info
1351
            configs = app_doc_data.getConfigs('Line', 'Gap')
1352
            if configs and int(configs[0].value) is 1:
1353
                remainLines = detector.detect_line_without_symbol()
1354
                windowSize = app_doc_data.getSlidingWindowSize()
1355
                thickness = int(windowSize[1] / 2)
1356

    
1357
                for line in remainLines:
1358
                    line.append(thickness)
1359
                connectedLines.extend(remainLines)
1360

    
1361
            configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
1362
            toler = int(configs[0].value) if configs else 20
1363

    
1364
            symbol_areas = []
1365
            for symbol in symbols:
1366
                symbol_areas.append(QRect(symbol.loc[0] - area.x, symbol.loc[1] - area.y, symbol.size[0], symbol.size[1]))
1367

    
1368
            detector.mergeLines(connectedLines, toler=toler, symbol_areas=symbol_areas)
1369

    
1370
            for pts in connectedLines:
1371
                line = QEngineeringLineItem(
1372
                    vertices=[(area.x + param[0], area.y + param[1]) for param in pts[:-1]], thickness=pts[2])
1373
                line.area = 'Drawing'
1374

    
1375
                app_doc_data.lines.append(line)
1376
                app_doc_data.allItems.append(line)
1377
        except Exception as ex:
1378
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1379
                                                           sys.exc_info()[-1].tb_lineno)
1380
            worker.displayLog.emit(MessageType.Error, message)
1381
        finally:
1382
            listWidget.addItem('Finishing line recognition')
1383
            worker.finished.emit()
1384

    
1385
    '''
1386
        @history    2018.04.24  Jeongwoo    Add isExceptDetect Field
1387
                    2018.05.09  Jeongwoo    Add targetSymbolList clear
1388
                    humkyung 2018.07.07 store symbols to list as like [equipments],[nozzles],[symbols]
1389
    '''
1390

    
1391
    @staticmethod
1392
    def initTargetSymbolDataList():
1393
        global targetSymbolList
1394

    
1395
        targetSymbolList.clear()
1396
        app_doc_data = AppDocData.instance()
1397
        symbolList = app_doc_data.getTargetSymbolList()
1398
        equipments = [item for item in symbolList if app_doc_data.getSymbolCategoryByType(item.getType()) == 'Equipment']
1399
        nozzles = [item for item in symbolList if item.getType() == 'Nozzles']
1400
        # [[equipments],[nozzles],[symbols]]
1401
        targetSymbolList.append(equipments)
1402
        targetSymbolList.append(nozzles)
1403
        targetSymbolList.append([item for item in symbolList if item not in equipments and item not in nozzles])
1404

    
1405
        return targetSymbolList
1406

    
1407
    '''
1408
        @brief  detect equipment
1409
        @author humkyung
1410
        @date   2018.07.07
1411
    '''
1412

    
1413
    @staticmethod
1414
    def detect_nozzles(mainRes, targetSymbol, listWidget, worker):
1415
        res = []
1416
        try:
1417
            app_doc_data = AppDocData.instance()
1418

    
1419
            nozzles = Worker.detectSymbolOnPid(mainRes, targetSymbol, listWidget, worker)
1420
            equipments = [symbol for symbol in searchedSymbolList if app_doc_data.isEquipmentType(symbol.getType())]
1421
            for equipment in equipments:
1422
                rect = QRectF(equipment.sp[0], equipment.sp[1], equipment.width, equipment.height)
1423
                rect.adjust(-equipment.width*0.1, -equipment.height*0.1, equipment.width*0.1, equipment.height*0.1)
1424
                matches = [nozzle for nozzle in nozzles if rect.contains(nozzle.rect)]
1425
                res.extend(matches)
1426
                for match in matches:
1427
                    nozzles.remove(match)
1428
        except Exception as ex:
1429
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1430
                                                           sys.exc_info()[-1].tb_lineno)
1431
            worker.displayLog(MessageType.Error, message)
1432

    
1433
        return res
1434

    
1435
    '''
1436
        @brief      detect symbol on PID
1437
        @author     jwkim
1438
        @date   
1439
        @history    humkyung 2018.04.06 check if symbol file exists
1440
                    Jeongwoo 2018.05.29 Change method to adjust detail symbol location with hit-rate. Not feature point count
1441
                                        Change parameter on add symbol part (mpCount → hitRate)
1442
                                        Remove unusing calculation (avg)
1443
                    Jeongwoo 2018.06.27 Remove part to split P&ID image and for loop
1444
                    humkyung 2018.07.07 return searched symbols
1445
    '''
1446

    
1447
    @staticmethod
1448
    def detectSymbolOnPid(mainRes, targetSymbol, listWidget, worker):
1449
        import copy
1450
        global ocrCompletedSrc
1451
        global threadLock
1452
        global maxProgressValue
1453

    
1454
        try:
1455
            forTraining = worker.isTrainingChecked
1456
            symbolName = targetSymbol.getName()
1457
            symbolType = targetSymbol.getType()
1458
            symbolPath = targetSymbol.getPath()
1459
            symbolThreshold = targetSymbol.getThreshold()  # if not forTraining else targetSymbol.getThreshold() / 3 * 2
1460
            symbolMinMatchCount = targetSymbol.getMinMatchCount()
1461
            isDetectOnOrigin = targetSymbol.getIsDetectOnOrigin()
1462
            symbolRotateCount = targetSymbol.getRotationCount()
1463
            symbolOcrOption = targetSymbol.getOcrOption()
1464
            isContainChild = targetSymbol.getIsContainChild()
1465
            symbolOriginalPoint = targetSymbol.getOriginalPoint()
1466
            symbolConnectionPoint = targetSymbol.getConnectionPoint()
1467
            baseSymbol = targetSymbol.getBaseSymbol()
1468
            additionalSymbol = targetSymbol.getAdditionalSymbol()
1469
            isExceptDetect = targetSymbol.getIsExceptDetect()
1470
            detectFlip = targetSymbol.getDetectFlip()
1471
            hasInstrumentLabel = targetSymbol.getHasInstrumentLabel()
1472
            text_area = targetSymbol.getText_area()
1473

    
1474
            # check if symbol file is target or not
1475
            if isExceptDetect == 1:
1476
                item = QListWidgetItem('{} file is not target'.format(symbolName))
1477
                item.setBackground(QColor('green'))
1478
                listWidget.addItem(item)
1479
                return
1480

    
1481
            foundSymbolCount = 0
1482

    
1483
            # check if symbol file exists
1484
            if not os.path.isfile(symbolPath):
1485
                item = QListWidgetItem('{} file not found'.format(symbolName))
1486
                item.setBackground(QColor('red'))
1487
                listWidget.addItem(item)
1488
                return
1489
            # up to here
1490

    
1491
            sym = cv2.imread(symbolPath, 1)
1492
            symGray = Worker.cvtGrayImage(sym)
1493
            symGrayOri = copy.copy(symGray)
1494
            ## TODO: 이진화 시켰을때 심볼이 검출되지 않음
1495
            ## symGray = cv2.threshold(cvtGrayImage(sym), 127, 255, cv2.THRESH_BINARY)[1]
1496
            ## cv2.imshow('symbol', symGray)
1497
            ## cv2.waitKey(0)
1498
            sow, soh = symGray.shape[::-1]  # symbol original w, h
1499

    
1500
            offsetDrawingArea = []
1501
            app_doc_data = AppDocData.instance()
1502
            area = app_doc_data.getArea('Drawing')
1503
            if area is not None:
1504
                roiItem = area.img.copy() if hasInstrumentLabel else area.img
1505
                offsetDrawingArea.append(area.x)
1506
                offsetDrawingArea.append(area.y)
1507
            else:
1508
                offsetDrawingArea.append(0)
1509
                offsetDrawingArea.append(0)
1510
                if isDetectOnOrigin == 1:
1511
                    roiItem = app_doc_data.imgSrc
1512
                else:
1513
                    roiItem = ocrCompletedSrc
1514
            srcWidth, srcHeight = roiItem.shape[::-1]
1515

    
1516
            roiItemSp = (0, 0)
1517
            roiItemEp = (srcWidth, srcHeight)
1518

    
1519
            # when text is located inside of symbol
1520
            if area is not None and hasInstrumentLabel:
1521
                # remove objects smaller than symbol
1522
                selected_contours = []
1523
                contours, hierarchy = cv2.findContours(area.mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
1524
                index = 0
1525
                for contour in contours:
1526
                    child, parent = hierarchy[0][index][2], hierarchy[0][index][3]
1527
                    [x, y, w, h] = cv2.boundingRect(contour)
1528
                    if w * h < sow * soh * 0.5:
1529
                        contour_area = cv2.contourArea(contour, True)
1530
                        if contour_area > 0:
1531
                            selected_contours.append(contour)
1532
                    index += 1
1533
                # up to here
1534

    
1535
                # draw contour with white color
1536
                roiItem = cv2.drawContours(roiItem, selected_contours, -1, (255, 255, 255), -1)
1537
                # cv2.imwrite("c:\\temp\\roiItem.png", roiItem)
1538

    
1539
            # try to recognize symbol twice(first one is normal, second on is flipped)
1540
            steps = [False, True] if detectFlip else [False]
1541
            for flipped in steps:
1542
                if flipped:
1543
                    symGray = symGrayOri
1544
                    symGray = cv2.flip(symGray, 1)
1545

    
1546
                symbolRotatedAngle = 0
1547
                for rc in range(symbolRotateCount + 1):  # Rotation count를 사용자 기준으로 받아서 1을 더한 후 사용
1548
                    sw, sh = symGray.shape[::-1]
1549
                    roiw = (roiItemEp[0] - roiItemSp[0])
1550
                    roih = (roiItemEp[1] - roiItemSp[1])
1551

    
1552
                    # Case : symbol is bigger than roi
1553
                    if roiw < sw or roih < sh:
1554
                        symGray = cv2.rotate(symGray, cv2.ROTATE_90_CLOCKWISE)
1555
                        symbolRotatedAngle = (symbolRotatedAngle + 90) % 360
1556

    
1557
                        if baseSymbol is not None and additionalSymbol is not None:
1558
                            additionalSymbol = Worker.getRotatedChildInfo(additionalSymbol)
1559
                        continue
1560

    
1561
                    # get Rotated Original Point
1562
                    originalPoint = Worker.getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint,
1563
                                                                      symbolRotatedAngle, sw, sh, sow, soh, flipped)
1564
                    connectionPoint = Worker.getCalculatedConnectionPoint(symbolConnectionPoint, symbolRotatedAngle, sw,
1565
                                                                          sh, sow, soh, flipped)
1566

    
1567
                    # For OPC
1568
                    drawing_area = app_doc_data.getArea('Drawing')
1569
                    if drawing_area is not None and (symbolType == "Piping OPC\'s" or symbolType == "Instrument OPC\'s"):
1570
                        customMatch = worker.detectOPCOnPid(drawing_area, symGray)
1571
                        if customMatch and len(customMatch) > 0:
1572
                            for custom in customMatch:
1573
                                hitRate = custom[0]
1574
                                searchedItemSp = (custom[1][0] + round(offsetDrawingArea[0]), custom[1][1] + round(offsetDrawingArea[1]))
1575

    
1576
                                is_add = True
1577
                                for searched in searchedSymbolList:
1578
                                    if Worker.IsOverlap((searchedItemSp[0], searchedItemSp[1], sw, sh), (
1579
                                            searched.getSp()[0], searched.getSp()[1], searched.getWidth(),
1580
                                            searched.getHeight())):
1581
                                        if searched.getHitRate() > hitRate:
1582
                                            is_add = False
1583
                                        else:
1584
                                            searchedSymbolList.remove(searched)
1585
                                            break
1586

    
1587
                                if is_add:
1588
                                    threadLock.acquire()
1589
                                    foundSymbolCount = foundSymbolCount + 1
1590
                                    Worker.addSearchedSymbol(symbolName, symbolType,
1591
                                                             searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount,
1592
                                                             hitRate, symbolRotatedAngle,
1593
                                                             isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1594
                                                             isContainChild,
1595
                                                             originalPoint, connectionPoint, baseSymbol, additionalSymbol,
1596
                                                             isExceptDetect, detectFlip=1 if flipped else 0,
1597
                                                             hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1598
                                    threadLock.release()
1599

    
1600
                    # Template Matching
1601
                    tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
1602
                    loc = np.where(tmRes >= symbolThreshold)
1603

    
1604
                    for pt in zip(*loc[::-1]):
1605
                        mpCount = 0  # Match Point Count
1606

    
1607
                        roi = roiItem[pt[1]:pt[1] + sh, pt[0]:pt[0] + sw]
1608

    
1609
                        if symbolMinMatchCount > 0:
1610
                            mpCount = Worker.getMatchPointCount(roi, symGray)
1611
                            if not (mpCount >= symbolMinMatchCount):
1612
                                continue
1613

    
1614
                        searchedItemSp = (roiItemSp[0] + pt[0] + round(offsetDrawingArea[0]),
1615
                                          roiItemSp[1] + pt[1] + round(offsetDrawingArea[1]))
1616
                        # print(searchedItemSp)
1617

    
1618
                        overlapArea = 0
1619
                        symbolIndex = -1
1620
                        for i in range(len(searchedSymbolList) - 1, -1, -1):
1621
                            area = Worker.contains(searchedSymbolList[i], searchedItemSp, sw, sh)
1622
                            if area > ACCEPT_OVERLAY_AREA:
1623
                                # if area > overlapArea:
1624
                                #    overlapArea = area
1625
                                #    symbolIndex = i
1626
                                overlapArea = area
1627
                                symbolIndex = i
1628
                                break
1629
                                """
1630
                                categories = [appDocData.isEquipmentType(symbolType), appDocData.isEquipmentType(searchedSymbolList[i].getType())]
1631
                                if categories[0] == categories[1]:
1632
                                    symbolIndex = i
1633
                                    break
1634
                                """
1635

    
1636
                        hitRate = tmRes[pt[1], pt[0]]
1637

    
1638
                        # 겹치는 영역이 기준값보다 작을 경우
1639
                        if overlapArea <= ACCEPT_OVERLAY_AREA:
1640
                            threadLock.acquire()
1641
                            foundSymbolCount = foundSymbolCount + 1
1642
                            Worker.addSearchedSymbol(symbolName, symbolType,
1643
                                                     searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount,
1644
                                                     hitRate, symbolRotatedAngle,
1645
                                                     isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1646
                                                     isContainChild,
1647
                                                     originalPoint, connectionPoint, baseSymbol, additionalSymbol,
1648
                                                     isExceptDetect,
1649
                                                     detectFlip=1 if flipped else 0,
1650
                                                     hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1651
                            threadLock.release()
1652
                        else:  # 겹치는 영역이 기준값보다 클 경우
1653
                            if symbolIndex != -1 and symbolIndex < len(searchedSymbolList):
1654
                                searchedSymbol = searchedSymbolList[symbolIndex]
1655
                                # 현재 심볼과 검출된 심볼이 같을 경우 Match Point가 더 높은 정보로 교체
1656
                                if symbolName == searchedSymbol.getName():
1657
                                    symbolHitRate = searchedSymbol.getHitRate()
1658
                                    if symbolHitRate - searchedSymbol.getThreshold() < hitRate - symbolThreshold:
1659
                                        threadLock.acquire()
1660
                                        # replace existing symbol with new symbol has high accuracy
1661
                                        searchedSymbolList[symbolIndex] = symbol.Symbol(symbolName, symbolType,
1662
                                                                                        searchedItemSp, sw, sh,
1663
                                                                                        symbolThreshold,
1664
                                                                                        symbolMinMatchCount, hitRate,
1665
                                                                                        symbolRotatedAngle,
1666
                                                                                        isDetectOnOrigin,
1667
                                                                                        symbolRotateCount,
1668
                                                                                        symbolOcrOption, isContainChild,
1669
                                                                                        ','.join(str(x) for x in
1670
                                                                                                 originalPoint),
1671
                                                                                        '/'.join('{},{},{},{}'.format(
1672
                                                                                            param[0], param[1],
1673
                                                                                            param[2], param[3]) for
1674
                                                                                                 param in
1675
                                                                                                 connectionPoint),
1676
                                                                                        baseSymbol, additionalSymbol,
1677
                                                                                        isExceptDetect,
1678
                                                                                        detectFlip=1 if flipped else 0,
1679
                                                                                        hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1680
                                        threadLock.release()
1681
                                # 현재 심볼과 검출된 심볼이 같지 않을 경우 (포함)
1682
                                elif app_doc_data.isEquipmentType(searchedSymbol.getType()) and not app_doc_data.isEquipmentType(symbolType):
1683
                                    if searchedSymbol.area > sw * sh * 10:  # searched equipment area is greather than 10 times of symbol's area
1684
                                        threadLock.acquire()
1685
                                        foundSymbolCount = foundSymbolCount + 1
1686
                                        Worker.addSearchedSymbol(symbolName, symbolType,
1687
                                                                 searchedItemSp, sw, sh, symbolThreshold, hitRate,
1688
                                                                 hitRate, symbolRotatedAngle,
1689
                                                                 isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1690
                                                                 isContainChild,
1691
                                                                 originalPoint, connectionPoint, baseSymbol,
1692
                                                                 additionalSymbol, isExceptDetect,
1693
                                                                 detectFlip=1 if flipped else 0,
1694
                                                                 hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1695
                                        threadLock.release()
1696
                                # 현재 심볼과 검출된 심볼이 같지 않을 경우 (교체)
1697
                                elif not forTraining:
1698
                                    searchedSymbol = searchedSymbolList[symbolIndex]
1699
                                    symbolHitRate = searchedSymbol.getHitRate()
1700
                                    if symbolHitRate - searchedSymbol.getThreshold() < hitRate - symbolThreshold:
1701
                                        threadLock.acquire()
1702
                                        searchedSymbolList[symbolIndex] = symbol.Symbol(symbolName, symbolType,
1703
                                                                                        searchedItemSp, sw, sh,
1704
                                                                                        symbolThreshold,
1705
                                                                                        symbolMinMatchCount, hitRate,
1706
                                                                                        symbolRotatedAngle,
1707
                                                                                        isDetectOnOrigin,
1708
                                                                                        symbolRotateCount,
1709
                                                                                        symbolOcrOption, isContainChild,
1710
                                                                                        ','.join(str(x) for x in
1711
                                                                                                 originalPoint),
1712
                                                                                        '/'.join('{},{},{},{}'.format(
1713
                                                                                            param[0], param[1],
1714
                                                                                            param[2], param[3]) for
1715
                                                                                                 param in
1716
                                                                                                 connectionPoint),
1717
                                                                                        baseSymbol, additionalSymbol,
1718
                                                                                        isExceptDetect,
1719
                                                                                        detectFlip=1 if flipped else 0,
1720
                                                                                        hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1721
                                        threadLock.release()
1722
                                # 학습용 데이터 생성을 위해 교체하지 않고 추가함
1723
                                elif forTraining:
1724
                                    threadLock.acquire()
1725
                                    foundSymbolCount = foundSymbolCount + 1
1726
                                    Worker.addSearchedSymbol(symbolName, symbolType,
1727
                                                             searchedItemSp, sw, sh, symbolThreshold,
1728
                                                             symbolMinMatchCount, hitRate, symbolRotatedAngle,
1729
                                                             isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1730
                                                             isContainChild,
1731
                                                             originalPoint, connectionPoint, baseSymbol,
1732
                                                             additionalSymbol, isExceptDetect,
1733
                                                             detectFlip=1 if flipped else 0,
1734
                                                             hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1735
                                    threadLock.release()
1736

    
1737
                    # rotate symbol
1738
                    symGray = cv2.rotate(symGray, cv2.ROTATE_90_CLOCKWISE)
1739
                    symbolRotatedAngle = (symbolRotatedAngle + 90) % 360
1740

    
1741
                    if additionalSymbol is not None:
1742
                        additionalSymbol = Worker.getRotatedChildInfo(additionalSymbol)
1743

    
1744
            threadLock.acquire()
1745
            listWidget.addItem('Found Symbol   : ' + os.path.splitext(os.path.basename(symbolPath))[0] + ' - (' + str(
1746
                foundSymbolCount) + ')')
1747
            threadLock.release()
1748

    
1749
            """
1750
            if area is not None and hasInstrumentLabel:
1751
                # restore objects smaller than symbol
1752
                roiItem = cv2.drawContours(roiItem, outside_contours, -1, (0, 0, 0), -1)
1753
                roiItem = cv2.drawContours(roiItem, hole_contours, -1, (255, 255, 255), -1)
1754
                # up to here
1755
                cv2.imwrite('c:\\Temp\\contour2.png', roiItem)
1756
            """
1757

    
1758
            worker.updateProgress.emit(maxProgressValue, symbolPath)
1759

    
1760
            return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
1761
        except Exception as ex:
1762
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1763
                                                           sys.exc_info()[-1].tb_lineno)
1764
            worker.displayLog.emit(MessageType.Error, message)
1765

    
1766
        return []
1767

    
1768
    @staticmethod
1769
    def IsOverlap(range1, range2):
1770
        if range1[0] <= range2[0] + range2[2] and range1[0] + range1[2] >= range2[0] and range1[1] <= range2[1] + \
1771
                range2[3] and range1[1] + range1[3] >= range2[1]:
1772
            range = (min(range1[0] + range1[2], range2[0] + range2[2]) - max(range1[0], range2[0])) * (
1773
                    min(range1[1] + range1[3], range2[1] + range2[3]) - max(range1[1], range2[1]))
1774
            if range >= range1[2] * range1[3] * 0.4 and range >= range2[2] * range2[3] * 0.4:
1775
                return True
1776
            else:
1777
                return False
1778
        else:
1779
            return False
1780

    
1781
    @staticmethod
1782
    def detectOPCOnPid(area, symGray):
1783
        results = []
1784

    
1785
        try:
1786
            symbol = cv2.copyMakeBorder(symGray, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=255)
1787
            not_symbol = cv2.bitwise_not(symbol)
1788
            symbol_contours, symbol_hierachy = cv2.findContours(not_symbol, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
1789
            if symbol_hierachy[0][0][0] != -1:
1790
                return results
1791

    
1792
            contour_count = len(symbol_contours)
1793
            if contour_count != 2:
1794
                return results
1795

    
1796
            for i in range(1, contour_count):
1797
                # contour size
1798
                symbol_area = cv2.contourArea(symbol_contours[i])
1799
                # moments mass center
1800
                symbol_moments = cv2.moments(symbol_contours[i])
1801
                symbol_x = int(symbol_moments['m10'] / (symbol_moments['m00'] + 1e-5))
1802
                symbol_y = int(symbol_moments['m01'] / (symbol_moments['m00'] + 1e-5))
1803
                rect_x, rect_y, rect_w, rect_h = cv2.boundingRect(symbol_contours[i])
1804
                symbol_x = symbol_x - rect_x
1805
                symbol_y = symbol_y - rect_y
1806
                # percent x, y
1807
                percent_x = symbol_x / rect_w
1808
                percent_y = symbol_y / rect_h
1809

    
1810
                for contour in area.contours:
1811
                    area_area = cv2.contourArea(contour)
1812
                    if area_area * 1.2 >= symbol_area >= area_area * 0.8:
1813
                        I1 = cv2.matchShapes(symbol_contours[i], contour, 1, 0)
1814
                        I2 = cv2.matchShapes(symbol_contours[i], contour, 2, 0)
1815
                        I3 = cv2.matchShapes(symbol_contours[i], contour, 3, 0)
1816
                        if I1 < 1 and I2 < 1 and I3 < 0.1:
1817
                            rect_x2, rect_y2, rect_w2, rect_h2 = cv2.boundingRect(contour)
1818
                            if rect_w * 1.2 >= rect_w2 >= rect_w * 0.8 and rect_h * 1.2 >= rect_h2 >= rect_h * 0.8:
1819
                                # moments mass center
1820
                                moments = cv2.moments(contour)
1821
                                x = int(moments['m10'] / (moments['m00'] + 1e-5))
1822
                                y = int(moments['m01'] / (moments['m00'] + 1e-5))
1823

    
1824
                                x = x - rect_x2
1825
                                y = y - rect_y2
1826
                                percent_x2 = x / rect_w2
1827
                                percent_y2 = y / rect_h2
1828

    
1829
                                value_x = abs(percent_x - percent_x2)
1830
                                value_y = abs(percent_y - percent_y2)
1831

    
1832
                                results.append([1 - (value_x + value_y), [rect_x2, rect_y2]])
1833
                break
1834
        except Exception as ex:
1835
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1836
                                                           sys.exc_info()[-1].tb_lineno)
1837
        return results
1838

    
1839
    # Convert into Grayscale image
1840
    @staticmethod
1841
    def cvtGrayImage(img):
1842
        return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1843

    
1844
    '''
1845
        @history    2018.06.12  Jeongwoo    Type changed (int → float)
1846
                    humkyung 2018.07.07 change return type as like [x,y]
1847
    '''
1848

    
1849
    @staticmethod
1850
    def getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, rotateSymbolWidth,
1851
                                   rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight, flipped=False):
1852
        res = []
1853

    
1854
        if additionalSymbol is None and symbolOriginalPoint is None:
1855
            res.append(rotateSymbolWidth // 2)
1856
            res.append(rotateSymbolHeight // 2)
1857
        else:
1858
            if flipped:
1859
                opx = originalSymbolWidth - float(symbolOriginalPoint.split(',')[0])
1860
                opy = float(symbolOriginalPoint.split(',')[1])
1861
            else:
1862
                opx = float(symbolOriginalPoint.split(',')[0])
1863
                opy = float(symbolOriginalPoint.split(',')[1])
1864

    
1865
            rPt = Worker.getCoordOnRotatedImage(symbolRotatedAngle, ('AUTO', opx, opy, '0'), originalSymbolWidth,
1866
                                                originalSymbolHeight)
1867

    
1868
            res.append(rPt[1])
1869
            res.append(rPt[2])
1870

    
1871
        return res
1872

    
1873
    '''
1874
        @history    2018.06.12  Jeongwoo    Type changed (int → float)
1875
                    humkyung 2018.07.07 change return type as like [[x,y],...]
1876
                    humkyung 2019.01.04 get symbol index
1877
    '''
1878

    
1879
    @staticmethod
1880
    def getCalculatedConnectionPoint(symbolConnectionPointStr, symbolRotatedAngle, rotateSymbolWidth,
1881
                                     rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight, flipped=0):
1882
        res = []
1883

    
1884
        if symbolConnectionPointStr is not None and symbolConnectionPointStr != '':
1885
            splitConnectionPointStr = symbolConnectionPointStr.split("/")
1886
            for strConnPt in splitConnectionPointStr:
1887
                tokens = strConnPt.split(',')
1888

    
1889
                direction = 'AUTO'
1890
                symbol_idx = '0'
1891
                if flipped:
1892
                    converted = {'AUTO': 'AUTO', 'LEFT': 'RIGHT', 'RIGHT': 'LEFT', 'UP': 'UP', 'DOWN': 'DOWN'}
1893

    
1894
                    if len(tokens) == 2:
1895
                        cpx = originalSymbolWidth - float(tokens[0])
1896
                        cpy = float(tokens[1])
1897
                    elif len(tokens) == 3:
1898
                        direction = converted[tokens[0]]
1899
                        cpx = originalSymbolWidth - float(tokens[1])
1900
                        cpy = float(tokens[2])
1901
                    elif len(tokens) >= 4:
1902
                        direction = converted[tokens[0]]
1903
                        cpx = originalSymbolWidth - float(tokens[1])
1904
                        cpy = float(tokens[2])
1905
                        symbol_idx = tokens[3]
1906
                else:
1907
                    if len(tokens) == 2:
1908
                        cpx = float(tokens[0])
1909
                        cpy = float(tokens[1])
1910
                    elif len(tokens) == 3:
1911
                        direction = tokens[0]
1912
                        cpx = float(tokens[1])
1913
                        cpy = float(tokens[2])
1914
                    elif len(tokens) >= 4:
1915
                        direction = tokens[0]
1916
                        cpx = float(tokens[1])
1917
                        cpy = float(tokens[2])
1918
                        symbol_idx = tokens[3]
1919

    
1920
                res.append(Worker.getCoordOnRotatedImage(symbolRotatedAngle, (direction, cpx, cpy, symbol_idx),
1921
                                                         originalSymbolWidth, originalSymbolHeight))
1922

    
1923
        return res
1924

    
1925
    '''
1926
        @brief      rotate (x,y) by given angle
1927
        @author     Jeongwoo
1928
        @date       2018.??.??
1929
        @history    humkyung 2018.04.13 fixed code when angle is 90 or 270    
1930
                    Jeongwoo 2018.04.27 Change calculation method with QTransform
1931
                    humkyung 2018.09.01 calculate rotated direction
1932
    '''
1933

    
1934
    @staticmethod
1935
    def getCoordOnRotatedImage(angle, connPt, originImageWidth, originImageHeight):
1936
        rx = None
1937
        ry = None
1938

    
1939
        # calculate rotated direction
1940
        direction = connPt[0]
1941
        if direction == 'LEFT':
1942
            direction = 'DOWN' if angle == 90 else 'RIGHT' if angle == 180 else 'UP' if angle == 270 else direction
1943
        elif direction == 'RIGHT':
1944
            direction = 'UP' if angle == 90 else 'LEFT' if angle == 180 else 'DOWN' if angle == 270 else direction
1945
        elif direction == 'UP':
1946
            direction = 'LEFT' if angle == 90 else 'DOWN' if angle == 180 else 'RIGHT' if angle == 270 else direction
1947
        elif direction == 'DOWN':
1948
            direction = 'RIGHT' if angle == 90 else 'UP' if angle == 180 else 'LEFT' if angle == 270 else direction
1949
        # up to here
1950

    
1951
        transform = QTransform()
1952
        if angle == 90 or angle == 270:
1953
            transform.translate(originImageHeight * 0.5, originImageWidth * 0.5)
1954
        elif angle == 0 or angle == 180:
1955
            transform.translate(originImageWidth * 0.5, originImageHeight * 0.5)
1956
        transform.rotate(abs(angle))
1957
        transform.translate(-originImageWidth * 0.5, -originImageHeight * 0.5)
1958
        point = QPoint(connPt[1], connPt[2])
1959
        point = transform.map(point)
1960
        rx = point.x()
1961
        ry = point.y()
1962

    
1963
        symbol_idx = connPt[3]
1964

    
1965
        return (direction, rx, ry, symbol_idx)
1966

    
1967
    '''
1968
        @brief      Add symbols
1969
        @author     jwkim
1970
        @date
1971
        @history    Change parameter (mpCount → hitRate)
1972
                    Yecheol 2018.07.04 Delete Symbol Id 
1973
    '''
1974

    
1975
    @staticmethod
1976
    def addSearchedSymbol(sName, sType
1977
                          , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle
1978
                          , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
1979
                          , originalPoint, connectionPoint, baseSymbol, additionalSymbol, isExceptDetect, detectFlip
1980
                          , hasInstrumentLabel, text_area):
1981
        global searchedSymbolList
1982

    
1983
        newSym = None
1984
        try:
1985
            newSym = symbol.Symbol(sName, sType
1986
                                   , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle,
1987
                                   isDetectOnOrigin, rotateCount, ocrOption, isContainChild,
1988
                                   ','.join(str(x) for x in originalPoint),
1989
                                   '/'.join('{},{},{},{}'.format(param[0], param[1], param[2], param[3]) for param in
1990
                                            connectionPoint),
1991
                                   baseSymbol, additionalSymbol, isExceptDetect, detectFlip=1 if detectFlip else 0,
1992
                                   hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1993

    
1994
            searchedSymbolList.append(newSym)
1995
        except Exception as ex:
1996
            from App import App
1997
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1998
                                                           sys.exc_info()[-1].tb_lineno)
1999
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2000

    
2001
        return newSym
2002

    
2003
    '''
2004
        @brief      Check object contains pt
2005
        @param      obj is item in searchedSymbolList
2006
    '''
2007

    
2008
    @staticmethod
2009
    def contains(obj, pt, tw, th):
2010
        sp = obj.getSp()
2011
        width = obj.getWidth()
2012
        height = obj.getHeight()
2013

    
2014
        if sp[0] > pt[0] + tw:
2015
            return 0
2016
        if sp[0] + width < pt[0]:
2017
            return 0
2018
        if sp[1] > pt[1] + th:
2019
            return 0
2020
        if sp[1] + height < pt[1]:
2021
            return 0
2022

    
2023
        # shared area
2024
        x = max(sp[0], pt[0])
2025
        y = max(sp[1], pt[1])
2026
        w = min(sp[0] + width, pt[0] + tw) - x
2027
        h = min(sp[1] + height, pt[1] + th) - y
2028

    
2029
        return float((w * h)) / float((tw * th)) * 100
2030

    
2031
    # Calculate count of keypoint match result
2032
    @staticmethod
2033
    def getMatchPointCount(src, cmp):
2034
        matchCount = 0
2035

    
2036
        try:
2037
            orb = cv2.ORB_create(1000, 2.0, 2, 1)
2038

    
2039
            kp1, des1 = orb.detectAndCompute(src, None)
2040
            kp2, des2 = orb.detectAndCompute(cmp, None)
2041

    
2042
            FLANN_INDEX_LSH = 6
2043
            # table_number      : The number of hash tables use
2044
            # key_size          : The length of the key in the hash tables
2045
            # multi_probe_level : Number of levels to use in multi-probe (0 for standard LSH)
2046
            #                     It controls how neighboring buckets are searched
2047
            #                     Recommended value is 2
2048
            # checks            : specifies the maximum leafs to visit when searching for neighbours.
2049
            # LSH : Locality-Sensitive Hashing
2050
            # ref : https://www.cs.ubc.ca/research/flann/uploads/FLANN/flann_manual-1.8.4.pdf
2051
            index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=20, key_size=10, multi_probe_level=4)
2052
            search_params = dict(checks=100)
2053

    
2054
            flann = cv2.FlannBasedMatcher(index_params, search_params)
2055

    
2056
            matches = flann.knnMatch(des1, des2, k=2)
2057
            matchesMask = [[0, 0] for i in range(len(matches))]  # Python 3.x
2058

    
2059
            count = 0
2060
            # ratio test as per Lowe's paper
2061
            for i in range(len(matches)):
2062
                if len(matches[i]) == 2:
2063
                    m = matches[i][0]
2064
                    n = matches[i][1]
2065
                    if m.distance < 0.85 * n.distance:
2066
                        count = count + 1
2067

    
2068
            matchCount = count
2069
        except Exception as ex:
2070
            from App import App
2071
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2072
                                                           sys.exc_info()[-1].tb_lineno)
2073
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2074

    
2075
        return matchCount
2076

    
2077
    '''
2078
        @brief      Remake rotated child symbol info
2079
    '''
2080

    
2081
    @staticmethod
2082
    def getRotatedChildInfo(additionalSymbol):
2083
        tempChildInfo = ""
2084
        if additionalSymbol:
2085
            childList = additionalSymbol.split("/")
2086
            for index in range(len(childList)):
2087
                child = childList[index]
2088
                direction = Worker.convertDirectionCodeToValue(child.split(",")[0])
2089
                childName = child.split(",")[1]
2090
                direction = (direction - 1) if direction > 0 else 3
2091
                if index != 0:
2092
                    tempChildInfo = tempChildInfo + "/"
2093
                tempChildInfo = tempChildInfo + Worker.convertValueToDirectionCode(direction) + "," + childName
2094
        return tempChildInfo
2095

    
2096
    '''
2097
        @brief   detect symbols on PID
2098
        @history humkyung 2018.06.08 add parameteres for signal
2099
    '''
2100

    
2101
    @staticmethod
2102
    def detectSymbolsOnPid(mainRes, targetSymbols, listWidget, updateProgressSignal):
2103
        res = []
2104

    
2105
        if type(targetSymbols) is list:
2106
            for detailTarget in targetSymbols:
2107
                res.extend(Worker.detectSymbolOnPid(mainRes, detailTarget, listWidget, updateProgressSignal))
2108
        else:
2109
            res = Worker.detectSymbolOnPid(mainRes, targetSymbols, listWidget, updateProgressSignal)
2110

    
2111
        return res
2112

    
2113
    @staticmethod
2114
    def convertDirectionCodeToValue(directionCode):
2115
        if directionCode == "UP":
2116
            return 0
2117
        elif directionCode == "RIGHT":
2118
            return 1
2119
        elif directionCode == "DOWN":
2120
            return 2
2121
        elif directionCode == "LEFT":
2122
            return 3
2123
        else:
2124
            return -1
2125

    
2126
    @staticmethod
2127
    def convertValueToDirectionCode(value):
2128
        if value == 0:
2129
            return "UP"
2130
        elif value == 1:
2131
            return "RIGHT"
2132
        elif value == 2:
2133
            return "DOWN"
2134
        elif value == 3:
2135
            return "LEFT"
2136
        else:
2137
            return "NONE"
2138

    
2139
    @staticmethod
2140
    def drawFoundSymbolsOnCanvas(drawingPath, symbols, textInfos, listWidget):
2141
        """draw found symbols and texts to image"""
2142

    
2143
        global src
2144
        global ocrCompletedSrc
2145
        global canvas
2146

    
2147
        app_doc_data = AppDocData.instance()
2148
        canvas = np.zeros(app_doc_data.imgSrc.shape, np.uint8)
2149
        canvas[::] = 255
2150

    
2151
        try:
2152
            project = app_doc_data.getCurrentProject()
2153

    
2154
            for symbol in symbols:
2155
                Worker.drawFoundSymbols(symbol, listWidget)
2156

    
2157
            for text in textInfos:
2158
                left = text.getX()
2159
                top = text.getY()
2160
                right = text.getX() + text.getW()
2161
                bottom = text.getY() + text.getH()
2162

    
2163
                canvas[top:bottom, left:right] = app_doc_data.imgSrc[top:bottom, left:right]
2164

    
2165
            cv2.imwrite(os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(drawingPath)), canvas)
2166
        except Exception as ex:
2167
            from App import App
2168
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2169
                                                           sys.exc_info()[-1].tb_lineno)
2170
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2171

    
2172
    '''
2173
        @history    2018.04.27  Jeongwoo    Remove Tesseract Log on listWidget
2174
                    2018.05.04  Jeongwoo    Change method to OCR with tesseract_ocr_module.py
2175
                    2018.05.09  Jeongwoo    Add global variable textInfoList, Remove text in symbol and Add tesseract result text
2176
                    2018.05.10  Jeongwoo    Remove not used if-statement
2177
                    2018.06.19  Jeongwoo    When detect text in symbol, use getTextAreaInfo() and Tesseract
2178
                    2018.06.21  Jeongwoo    Add if-statement for way to detect text by Type A
2179
    '''
2180

    
2181
    @staticmethod
2182
    def drawFoundSymbols(symbol, listWidget):
2183
        global src
2184
        global canvas
2185
        #global WHITE_LIST_CHARS
2186
        global searchedSymbolList
2187
        global textInfoList
2188

    
2189
        # symbolId = symbol.getId()
2190
        symbolPath = symbol.getPath()
2191
        symbolSp = symbol.getSp()
2192
        symbolRotatedAngle = symbol.getRotatedAngle()
2193

    
2194
        symImg = cv2.cvtColor(cv2.imread(symbolPath, 1), cv2.COLOR_BGR2GRAY)
2195
        if symbol.getDetectFlip() is 1:
2196
            symImg = cv2.flip(symImg, 1)
2197
        for i in range(symbolRotatedAngle // 90):
2198
            symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
2199

    
2200
        w, h = symImg.shape[::-1]
2201
        canvas[symbolSp[1]:symbolSp[1] + h, symbolSp[0]:symbolSp[0] + w] = cv2.bitwise_and(
2202
            canvas[symbolSp[1]:symbolSp[1] + h, symbolSp[0]:symbolSp[0] + w], symImg)
2203

    
2204
    @staticmethod
2205
    def remove_detected_symbol_image(sym, imgSrc):
2206
        """remove detected symbol image from drawing image"""
2207
        global threadLock
2208

    
2209
        path = sym.getPath()
2210
        sp = sym.getSp()
2211
        sw = sym.getWidth()
2212
        sh = sym.getHeight()
2213
        angle = sym.getRotatedAngle()
2214
        # get symbol image
2215
        sym_img = cv2.imread(path)
2216
        sym_img = cv2.cvtColor(sym_img, cv2.COLOR_BGR2GRAY)
2217
        # symImg = cv2.threshold(Worker.cvtGrayImage(symImg), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
2218
        if sym.getDetectFlip() is 1:
2219
            sym_img = cv2.flip(sym_img, 1)
2220

    
2221
        for i in range(angle // 90):
2222
            sym_img = cv2.rotate(sym_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
2223
        # up to here
2224

    
2225
        threadLock.acquire()
2226
        temp = imgSrc[sp[1]:sp[1] + sh, sp[0]:sp[0] + sw]
2227
        sym_img = cv2.erode(sym_img, np.ones((5, 5), np.uint8))
2228
        mask = cv2.bitwise_or(temp, sym_img)
2229
        imgXOR = cv2.bitwise_xor(temp, mask)
2230
        imgSrc[sp[1]:sp[1] + sh, sp[0]:sp[0] + sw] = cv2.bitwise_not(imgXOR)
2231
        threadLock.release()
2232

    
2233
    '''
2234
        @brief  get difference between given original and recognized image
2235
        @author humkyung
2236
        @date   2018.06.11
2237
    '''
2238

    
2239
    @staticmethod
2240
    def getDifference(orgImagePath, recImagePath):
2241
        import re
2242

    
2243
        global ocrCompletedSrc
2244
        global textInfoList
2245

    
2246
        try:
2247
            app_doc_data = AppDocData.instance()
2248
            imgOriginal = app_doc_data.imgSrc
2249

    
2250
            # remove not drawing area
2251
            configs = app_doc_data.getConfigs('{} Equipment Desc Area'.format(app_doc_data.imgName))
2252
            for config in configs:
2253
                found = re.findall('\\d+', config.value)
2254
                if len(found) == 4:
2255
                    cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2256
                                    (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2257

    
2258
            configs = app_doc_data.getConfigs('{} Typical Area'.format(app_doc_data.imgName))
2259
            for config in configs:
2260
                found = re.findall('\\d+', config.value)
2261
                if len(found) == 4:
2262
                    cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2263
                                    (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2264

    
2265
            noteArea = app_doc_data.getArea('Note')
2266
            if noteArea is not None:
2267
                noteArea.img = app_doc_data.imgSrc[round(noteArea.y):round(noteArea.y + noteArea.height),
2268
                                round(noteArea.x):round(noteArea.x + noteArea.width)].copy()
2269
                cv2.rectangle(imgOriginal, (round(noteArea.x), round(noteArea.y)),
2270
                                (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
2271
            # up to here
2272

    
2273
            """
2274
            if os.path.isfile(orgImagePath) and os.path.isfile(recImagePath):
2275
                imgOriginal = \
2276
                    cv2.threshold(Worker.cvtGrayImage(cv2.imread(orgImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
2277

2278
                configs = app_doc_data.getConfigs('Filter', 'DilateSize')
2279
                if 1 == len(configs) and int(configs[0].value) is not 0:
2280
                    size = int(configs[0].value)
2281
                    kernel = np.ones((size, size), np.uint8)
2282
                    imgOriginal = cv2.erode(imgOriginal, kernel, iterations=1)
2283

2284
                # remove not drawing area
2285
                configs = app_doc_data.getConfigs('{} Equipment Desc Area'.format(app_doc_data.imgName))
2286
                for config in configs:
2287
                    found = re.findall('\\d+', config.value)
2288
                    if len(found) == 4:
2289
                        cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2290
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2291

2292
                configs = app_doc_data.getConfigs('{} Typical Area'.format(app_doc_data.imgName))
2293
                for config in configs:
2294
                    found = re.findall('\\d+', config.value)
2295
                    if len(found) == 4:
2296
                        cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2297
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2298

2299
                noteArea = app_doc_data.getArea('Note')
2300
                if noteArea is not None:
2301
                    noteArea.img = app_doc_data.imgSrc[round(noteArea.y):round(noteArea.y + noteArea.height),
2302
                                   round(noteArea.x):round(noteArea.x + noteArea.width)].copy()
2303
                    cv2.rectangle(imgOriginal, (round(noteArea.x), round(noteArea.y)),
2304
                                  (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
2305
                # up to here
2306

2307
                imgRecognized = \
2308
                    cv2.threshold(Worker.cvtGrayImage(cv2.imread(recImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
2309

2310
                imgDiff = np.ones(imgOriginal.shape, np.uint8) * 255
2311

2312
                area = app_doc_data.getArea('Drawing')
2313
                if area is not None:
2314
                    x = round(area.x)
2315
                    y = round(area.y)
2316
                    width = round(area.width)
2317
                    height = round(area.height)
2318
                    imgNotOper = cv2.bitwise_not(imgRecognized[y:y + height, x:x + width])
2319
                    imgDiff[y:y + height, x:x + width] = cv2.bitwise_xor(imgOriginal[y:y + height, x:x + width],
2320
                                                                         imgNotOper)
2321

2322
                # remove noise
2323
                imgDiff = cv2.dilate(imgDiff, np.ones((2, 2), np.uint8))
2324

2325
                project = app_doc_data.getCurrentProject()
2326
                cv2.imwrite(os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(orgImagePath)), imgDiff)
2327
            """
2328
        except Exception as ex:
2329
            from App import App
2330
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2331
                                                           sys.exc_info()[-1].tb_lineno)
2332
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2333

    
2334

    
2335
'''
2336
    @history    2018.05.25  Jeongwoo    Add pyqtSignal(svgItemClicked, itemRemoved)
2337
'''
2338

    
2339

    
2340
class QRecognitionDialog(QDialog):
2341
    svgItemClicked = pyqtSignal(SymbolSvgItem)
2342
    itemRemoved = pyqtSignal(QGraphicsItem)
2343
    unBlockEvent = pyqtSignal()
2344

    
2345
    '''
2346
        @history    2018.05.25  Jeongwoo    Add parameter and initialize / Connect recognizeButton signal and slot
2347
                    2018.05.29  Jeongwoo    Chnage parameter(graphicsView → parent) and Get graphicsView from parent
2348
    '''
2349

    
2350
    def __init__(self, parent, drawings):  # Parent is MainWindow
2351
        from AppDocData import AppDocData
2352

    
2353
        QDialog.__init__(self, parent)
2354

    
2355
        self.parent = parent
2356
        self._scene = QGraphicsScene()
2357
        self.drawings = drawings
2358
        self.xmlPath = None
2359
        self.ui = Recognition_UI.Ui_Recognition()
2360
        self.ui.setupUi(self)
2361
        self.isTreated = False
2362

    
2363
        self.ui.buttonBox.setEnabled(True)
2364
        self.ui.listWidget.model().rowsInserted.connect(self.rowInserted)
2365
        self.ui.recognizeButton.clicked.connect(self.recognizeButtonClicked)
2366
        self.ui.lineCheckBox.stateChanged.connect(self.checkBoxChanged)
2367
        self.ui.checkBoxSymbol.stateChanged.connect(self.checkBoxChanged)
2368
        self.ui.checkBoxText.stateChanged.connect(self.checkBoxChanged)
2369
        self.ui.checkBoxTraining.stateChanged.connect(self.checkBoxChanged)
2370
        self.isAccepted = False
2371

    
2372
        appDocData = AppDocData.instance()
2373
        configs = appDocData.getAppConfigs('app', 'mode')
2374
        if configs and 1 == len(configs) and 'advanced' == configs[0].value:
2375
            pass
2376
        else:
2377
            self.ui.checkBoxTraining.setVisible(False)
2378

    
2379
        if False:#len(self.drawings) == 1 and appDocData.activeDrawing and appDocData.activeDrawing == self.drawings[0]:
2380
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2381
            self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
2382
            self.ui.checkBoxText.setCheckState(Qt.Unchecked)
2383
        else:
2384
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2385
            self.ui.lineCheckBox.setCheckState(Qt.Checked)
2386
            self.ui.checkBoxText.setCheckState(Qt.Checked)
2387
            #self.ui.checkBoxSymbol.setEnabled(False)
2388
            #self.ui.lineCheckBox.setEnabled(False)
2389
            #self.ui.checkBoxText.setEnabled(False)
2390

    
2391
        self.ui.progressBarBatch.setFormat('{}/{}'.format('0', str(len(self.drawings))))
2392

    
2393
    def checkBoxChanged(self, checkState):
2394
        '''
2395
        @brief      line and text cannot be reocognized alone
2396
        @author     euisung
2397
        @date       2019.05.14
2398
        '''
2399
        if self.ui.checkBoxTraining.isChecked():
2400
            self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
2401
            self.ui.checkBoxText.setCheckState(Qt.Unchecked)
2402
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2403
            self.ui.lineCheckBox.setEnabled(False)
2404
            self.ui.checkBoxText.setEnabled(False)
2405
            self.ui.checkBoxSymbol.setEnabled(False)
2406
            self.ui.recognizeButton.setText('Extract')
2407
            return
2408
        else:
2409
            self.ui.lineCheckBox.setEnabled(True)
2410
            self.ui.checkBoxText.setEnabled(True)
2411
            self.ui.checkBoxSymbol.setEnabled(True)
2412
            self.ui.recognizeButton.setText('Recognize')
2413

    
2414
        if checkState is int(Qt.Checked):
2415
            if self.ui.lineCheckBox.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2416
                self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2417
            elif self.ui.checkBoxText.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2418
                self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2419
        elif checkState is int(Qt.Unchecked):
2420
            if self.ui.lineCheckBox.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2421
                self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
2422
            elif self.ui.checkBoxText.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2423
                self.ui.checkBoxText.setCheckState(Qt.Unchecked)
2424

    
2425
    '''
2426
        @brief      QListWidget Row Inserted Listener
2427
                    Whenever row inserted, scroll to bottom
2428
        @author     Jeongwoo
2429
        @date       18.04.12
2430
    '''
2431

    
2432
    def rowInserted(self, item):
2433
        self.ui.listWidget.scrollToBottom()
2434

    
2435
    def accept(self):
2436
        self.isAccepted = True
2437
        QDialog.accept(self)
2438

    
2439
    def recognizeButtonClicked(self, event):
2440
        """
2441
        @brief      start recognization
2442
        @author     humkyung
2443
        @history    humkyung 2018.10.05 clear imgSrc before recognizing
2444
        """
2445
        if self.ui.checkBoxSymbol.isChecked():  # or self.ui.checkBoxText.isChecked() or self.ui.lineCheckBox.isChecked():
2446
            appDocData = AppDocData.instance()
2447
            appDocData.imgSrc = None
2448
            area = appDocData.getArea('Drawing')
2449
            if area is None:
2450
                QMessageBox.about(self, self.tr("Notice"), self.tr('Please select drawing area.'))
2451
                return
2452

    
2453
            self.ui.recognizeButton.setEnabled(False)
2454
            self.ui.buttonBox.setEnabled(False)
2455
            self.ui.progressBar.setValue(0)
2456
            self.ui.listWidget.addItem("Initializing...")
2457
            self.startThread()
2458

    
2459
            self.isTreated = True
2460

    
2461
    '''
2462
        @brief  add item to list widget
2463
        @author humkyung
2464
        @date   2018.06.19
2465
    '''
2466

    
2467
    def addListItem(self, item):
2468
        self.ui.listWidget.addItem(item)
2469

    
2470
    '''
2471
        @brief  update progressbar with given value
2472
        @author humkyung
2473
        @date   2018.06.08
2474
    '''
2475

    
2476
    def updateProgress(self, maxValue, image_path):
2477
        self.ui.progressBar.setMaximum(maxValue)
2478
        self.ui.progressBar.setValue(self.ui.progressBar.value() + 1)
2479

    
2480
        if image_path is not None and os.path.isfile(image_path):
2481
            self.ui.labelImage.setPixmap(QPixmap(image_path))
2482
        elif image_path is not None:
2483
            self.ui.labelImage.setText(image_path)
2484

    
2485
    def updateBatchProgress(self, maxValue, weight):
2486
        """update batch progressbar"""
2487
        self.ui.progressBarBatch.setMaximum(maxValue * 5)
2488
        value = self.ui.progressBarBatch.value() + weight
2489
        self.ui.progressBarBatch.setValue(value)
2490
        self.ui.progressBarBatch.setFormat('{}/{}'.format(str(int(value / 5)), str(maxValue)))
2491

    
2492
    '''
2493
        @brief      display title
2494
        @author     humkyung
2495
        @date       2018.08.20
2496
    '''
2497

    
2498
    def displayTitle(self, title):
2499
        self.ui.labelTitle.setText(title)
2500

    
2501
    def startThread(self):
2502
        """start thread"""
2503
        import timeit
2504
        from PyQt5 import QtWidgets
2505
        from App import App
2506

    
2507
        self.ui.buttonBox.setDisabled(True)
2508

    
2509
        self.mutex = QMutex()
2510
        self.cond = QWaitCondition()
2511

    
2512
        # 1 - create Worker and Thread inside the Form
2513
        self.obj = Worker(self.mutex, self.cond)  # no parent!
2514
        self.obj.drawings = self.drawings
2515
        self.obj.listWidget = self.ui.listWidget
2516
        self.obj.scene = self._scene
2517
        self.obj.scene._end = False  # for waiting each drawing finished
2518
        self.obj.isSymbolChecked = self.ui.checkBoxSymbol.isChecked()
2519
        self.obj.isTextChecked = self.ui.checkBoxText.isChecked()
2520
        self.obj.isLineChecked = self.ui.lineCheckBox.isChecked()
2521
        self.obj.isTrainingChecked = self.ui.checkBoxTraining.isChecked()
2522
        self.thread = QThread()  # no parent!
2523

    
2524
        # 2 - Move the Worker object to the Thread object
2525
        self.obj.moveToThread(self.thread)
2526

    
2527
        # 3 - Connect Worker Signals to the Thread slots
2528
        self.obj.finished.connect(self.thread.quit)
2529
        self.obj.displayMessage.connect(self.addListItem)
2530
        self.obj.updateProgress.connect(self.updateProgress)
2531
        self.obj.updateBatchProgress.connect(self.updateBatchProgress)
2532
        self.obj.displayLog.connect(App.mainWnd().addMessage)
2533
        self.obj.displayTitle.connect(self.displayTitle)
2534
        self.obj.add_detected_items_to_scene.connect(self.add_detected_items_to_scene)
2535

    
2536
        # 4 - Connect Thread started signal to Worker operational slot method
2537
        self.thread.started.connect(self.obj.procCounter)
2538

    
2539
        # 5 - Thread finished signal will close the app if you want!
2540
        self.thread.finished.connect(self.dlgExit)
2541

    
2542
        # 6 - Start the thread
2543
        self.thread.start()
2544

    
2545
        self.tmStart = timeit.default_timer()
2546

    
2547
    '''
2548
        @brief set buttonbox's enabled flag
2549
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
2550
                    2018.06.14  Jeongwoo    Change sentence order
2551
                    2018.11.26  euisung     add drawing part
2552
                    2018.11.26  euising     move save and unknown part into executerecognition
2553
    '''
2554

    
2555
    def dlgExit(self):
2556
        import timeit
2557

    
2558
        try:
2559
            self.ui.buttonBox.setEnabled(True)
2560

    
2561
            self.ui.progressBar.setValue(self.ui.progressBar.maximum())
2562
        except Exception as ex:
2563
            from App import App
2564
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2565
                                                           sys.exc_info()[-1].tb_lineno)
2566
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2567
        finally:
2568
            self.tmStop = timeit.default_timer()
2569
            seconds = self.tmStop - self.tmStart
2570
            self.ui.listWidget.addItem("\nRunning Time : {} min".format(str(round(seconds / 60, 1))) + "\n")
2571

    
2572
    '''
2573
        @history    2018.05.29  Jeongwoo    Call parent's method
2574
                    2018.05.30  Jeongwoo    Change method name
2575
                    2018.06.09  humkyung    set progressbar value to maximum
2576
                    2018.11.12  euisung     add title block properties
2577
                    2018.11.29  euisung     no more used
2578
    '''
2579

    
2580
    def drawDetectedItems(self, symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList, loop):
2581
        try:
2582
            self.ui.progressBar.setValue(self.ui.progressBar.maximum())
2583
            self.parent.drawDetectedItems(symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList)
2584
        finally:
2585
            loop.quit()
2586

    
2587
    '''
2588
        @brief      draw detected lines
2589
        @author     humkyung
2590
        @date       2018.08.23
2591
        @history    2018.11.27  euisung     no more used
2592
    '''
2593

    
2594
    def drawDetectedLines(self, lineList, loop):
2595
        try:
2596
            self.parent.drawDetectedLines(lineList, self.obj)
2597
        finally:
2598
            loop.quit()
2599

    
2600
    '''
2601
        @brief      draw detected lines
2602
        @author     euisung
2603
        @date       2018.11.27
2604
        @history    2018.11.27  euisung     no more used
2605
    '''
2606

    
2607
    def drawUnknownItems(self, path, loop):
2608
        try:
2609
            self.parent.drawUnknownItems(path)
2610
        finally:
2611
            loop.quit()
2612

    
2613
    def add_detected_items_to_scene(self, scene) -> None:
2614
        """add detected items to scene"""
2615
        from SaveWorkCommand import SaveWorkCommand
2616
        from EngineeringVendorItem import QEngineeringVendorItem
2617

    
2618
        app_doc_data = AppDocData.instance()
2619

    
2620
        try:
2621
            for item in scene.items():
2622
                if issubclass(type(item), QEngineeringVendorItem):
2623
                    app_doc_data.symbols.append(item)
2624
                    app_doc_data.allItems.append(item)
2625

    
2626
            # symbol need to be attached for scene position
2627
            for symbol in app_doc_data.symbols:
2628
                if issubclass(type(symbol), SymbolSvgItem):
2629
                    symbol.addSvgItemToScene(scene)
2630
                else:
2631
                    scene.addItem(symbol)
2632

    
2633
            # text no need to be attached
2634
            '''
2635
            for text in app_doc_data.texts:
2636
                text.addTextItemToScene(scene)
2637

2638
            for lineNo in app_doc_data.tracerLineNos:
2639
                lineNo.addTextItemToScene(scene)
2640
            '''
2641

    
2642
            # remove lines which is located inside symbol
2643
            for symbol in app_doc_data.symbols:
2644
                rect = symbol.sceneBoundingRect()
2645
                rect.adjust(-10, -10, 10, 10)
2646
                matches = [line for line in app_doc_data.lines if rect.contains(line.line().p1()) and
2647
                           rect.contains(line.line().p2())]# and not line.has_connection]
2648
                for line in matches:
2649
                    app_doc_data.allItems.remove(line)
2650
                    app_doc_data.lines.remove(line)
2651
            # up to here
2652

    
2653
            for line in app_doc_data.lines:
2654
                scene.addItem(line)
2655
                # line.transfer.onRemoved.connect(self.itemRemoved)
2656
                for conn in line.connectors:
2657
                    conn.transfer.onPosChanged.connect(line.onConnectorPosChaned)
2658

    
2659
            for unknown in app_doc_data.unknowns + app_doc_data.lineIndicators:
2660
                scene.addItem(unknown)
2661

    
2662
            # save scene
2663
            SaveWorkCommand.save_to_database()
2664
            SaveWorkCommand.save_to_xml()
2665
        except Exception as ex:
2666
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2667
                                                           sys.exc_info()[-1].tb_lineno)
2668
            self.displayLog.emit(MessageType.Error, message)
2669
        finally:
2670
            self.cond.wakeAll()
2671
            scene._end = True
클립보드 이미지 추가 (최대 크기: 500 MB)