프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / RecognitionDialog.py @ f3f9d47e

이력 | 보기 | 이력해설 | 다운로드 (133 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
from Drawing import Drawing
31

    
32
from MainWindow import MainWindow
33

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

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

    
42
src = []
43

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

    
48
#WHITE_LIST_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"
49

    
50
MIN_TEXT_SIZE = 10
51

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

    
55
ACCEPT_OVERLAY_AREA = 20
56
# endregion
57

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

    
62

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

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

    
86
    def __init__(self, mutex, cond):
87
        super(Worker, self).__init__()
88
        self.mutex = mutex
89
        self.cond = cond
90

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

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

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

    
118
    '''
119
        @brief  remove small objects from given image
120
        @author humkyung
121
        @date   2018.04.26
122
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
123
    '''
124

    
125
    @staticmethod
126
    def remove_small_objects(img, minArea=None, maxArea=None):
127
        try:
128
            image = img.copy()
129

    
130
            if not minArea or not maxArea:
131
                app_doc_data = AppDocData.instance()
132
                configs = app_doc_data.getConfigs('Small Object Size', 'Min Area')
133
                minArea = int(configs[0].value) if 1 == len(configs) else 20
134
                configs = app_doc_data.getConfigs('Small Object Size', 'Max Area')
135
                maxArea = int(configs[0].value) if 1 == len(configs) else 50
136

    
137
            ## try to convert grayscale to binary
138
            #image = cv2.threshold(image, 200, 255, cv2.THRESH_BINARY)[1]
139

    
140
            contours, _ = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
141
            selectedContours = []
142
            for contour in contours:
143
                area = cv2.contourArea(contour)
144
                if minArea < area < maxArea:
145
                    selectedContours.append(contour)
146

    
147
            # draw contour with white color
148
            cv2.drawContours(image, selectedContours, -1, (255, 255, 255), -1)
149
        except Exception as ex:
150
            from App import App
151

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

    
156
        return image
157

    
158
    '''
159
        @brief  arrange line's position
160
        @author humkyung
161
        @date   2018.07.04
162
    '''
163

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

    
181
                # skip jointed symbols
182
                symbolPool = [item for item in symbols if item not in visited and item.is_connected(line)]
183
                if symbolPool:
184
                    selected = []
185
                    visited.extend(symbolPool)
186
                    while len(symbolPool) > 0:
187
                        symbol = symbolPool.pop()
188

    
189
                        rhs = [item for item in symbols if item not in visited and item.is_connected(symbol)]
190
                        if rhs:
191
                            symbolPool.extend(rhs)
192
                            visited.extend(rhs)
193
                            selected.extend(rhs)
194
                        else:
195
                            selected.append(symbol)
196

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

    
213
    def create_detected_items(self, symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList):
214
        try:
215
            QApplication.processEvents()
216
            self.create_detected_symbol_item(symbolList)
217
            QApplication.processEvents()
218
            self.create_detected_text_item(textInfoList)
219
            QApplication.processEvents()
220
            self.create_detected_other_text_item(otherTextInfoList)
221
            QApplication.processEvents()
222
            self.create_detected_title_block_text_item(titleBlockTextInfoList)
223

    
224
            # update scene
225
            # self.graphicsView.scene().update(self.graphicsView.sceneRect())
226
        except Exception as ex:
227
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
228
                                                           sys.exc_info()[-1].tb_lineno)
229
            self.displayLog.emit(MessageType.Error, message)
230

    
231
    '''
232
            history     2018.06.09  humkyung    check length of original and connection point is 2 while parsing
233
                        2018.11.26  euisung     remove scene dependency
234
                        2018.11.29  euisung     change name drawDetectedSymbolItem() -> createDetectedSymbolItem
235
        '''
236

    
237
    def create_detected_symbol_item(self, symbolList):
238
        from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
239
        from SymbolSvgItem import SymbolSvgItem
240
        import math
241

    
242
        try:
243
            app_doc_data = AppDocData.instance()
244
            project = app_doc_data.getCurrentProject()
245

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

    
269
                parentSymbol = symbol.getBaseSymbol()
270
                childSymbol = symbol.getAdditionalSymbol()
271
                hasInstrumentLabel = symbol.getHasInstrumentLabel()
272

    
273
                svgFilePath = os.path.join(project.getSvgFilePath(), _type, name + '.svg')
274
                if os.path.isfile(svgFilePath):
275
                    svg = SymbolSvgItem.createItem(_type, None, svgFilePath, owner=None, flip=flip)
276
                    svg.hit_ratio = symbol.hitRate
277
                    svg.buildItem(name, _type, angle, pt, size, origin, connPts, parentSymbol, childSymbol,
278
                                  hasInstrumentLabel)
279
                    svg.area = 'Drawing'
280

    
281
                    # set owner - 2018.07.20 added by humkyung
282
                    matches = [searched for searched in searchedMap if searched[0] == symbol.owner]
283
                    if len(matches) == 1:
284
                        svg.owner = matches[0][1]
285
                    searchedMap.append((symbol, svg))
286
                    # up to here
287

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

    
305
    '''
306
            @history    2018.06.08  Jeongwoo    Add parameter on round method
307
            @history    2018.11.02  euisung     Add save note text item
308
            @history    2018.11.05  euisung     delete save note text item and move to drawDetectedItems()
309
                        2018.11.26  euisung     remove scene dependency
310
                        2018.11.29  euisung     change name drawDetectedTextItem() -> createDetectedTextItem
311
        '''
312

    
313
    def create_detected_text_item(self, textInfoList):
314
        from TextItemFactory import TextItemFactory
315
        import math
316

    
317
        try:
318
            app_doc_data = AppDocData.instance()
319

    
320
            # parse texts
321
            for textInfo in textInfoList:
322
                x = textInfo.getX()
323
                y = textInfo.getY()
324
                width = textInfo.getW()
325
                height = textInfo.getH()
326
                angle = round(math.radians(textInfo.getAngle()), 2)
327
                text = textInfo.getText()
328
                if not text: continue
329

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

    
345
    '''
346
        @brief      draw detected texts except which in drawing area
347
        @history    2018.11.29  euisung     change name drawDetectedOtherTextItem() -> createDetectedOtherTextItem
348
    '''
349

    
350
    def create_detected_other_text_item(self, otherTextInfoList):
351
        from TextItemFactory import TextItemFactory
352
        import math
353

    
354
        try:
355
            app_doc_data = AppDocData.instance()
356

    
357
            # parse notes
358
            for textInfoMap in otherTextInfoList:
359
                if textInfoMap[0] == 'Note' or textInfoMap[1] is None:
360
                    pass
361

    
362
                for textInfo in textInfoMap[1]:
363
                    x = textInfo.getX()
364
                    y = textInfo.getY()
365
                    width = textInfo.getW()
366
                    height = textInfo.getH()
367
                    angle = round(math.radians(textInfo.getAngle()))
368
                    text = textInfo.getText()
369

    
370
                    item = TextItemFactory.instance().createTextItem(textInfo)
371

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

    
384
    def create_detected_title_block_text_item(self, textInfoList):
385
        """draw title block"""
386
        from TextItemFactory import TextItemFactory
387
        import math
388

    
389
        try:
390
            app_doc_data = AppDocData.instance()
391

    
392
            # parse texts
393
            for textInfos in textInfoList:
394
                if len(textInfos[1]) is 0:
395
                    continue
396

    
397
                for textInfo in textInfos[1]:
398
                    x = textInfo.getX()
399
                    y = textInfo.getY()
400
                    width = textInfo.getW()
401
                    height = textInfo.getH()
402
                    angle = round(math.radians(textInfo.getAngle()), 2)
403
                    text = textInfo.getText()
404
                    if not text: continue
405
                    item = TextItemFactory.instance().createTextItem(textInfo)
406

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

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

    
433
    def create_unknown_items(self, path):
434
        from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
435
        from EngineeringLineItem import QEngineeringLineItem
436
        from EngineeringUnknownItem import QEngineeringUnknownItem
437

    
438
        try:
439
            app_doc_data = AppDocData.instance()
440
            project = app_doc_data.getCurrentProject()
441
            windowSize = app_doc_data.getSlidingWindowSize()
442

    
443
            thickness = int(windowSize[1] / 2)
444

    
445
            area = app_doc_data.getArea('Drawing')
446
            diffFilePath = os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(path))
447

    
448
            # remove line from image
449
            imgDiff = np.ones(app_doc_data.imgSrc.shape, np.uint8) * 255
450
            imgDiff[round(area.y + 1):round(area.y + area.height),
451
                           round(area.x + 1):round(area.x + area.width)] = \
452
                app_doc_data.imgSrc[round(area.y + 1):round(area.y + area.height),
453
                           round(area.x + 1):round(area.x + area.width)]
454

    
455
            lines = app_doc_data.lines
456
            for line in lines:
457
                line.drawToImage(imgDiff, 255, thickness) if line.thickness is None else \
458
                    line.drawToImage(imgDiff, 255, line.thickness)
459
            # up to here
460
            cv2.imwrite(diffFilePath, imgDiff)
461

    
462
            imgNot = np.ones(imgDiff.shape, np.uint8)
463
            cv2.bitwise_not(imgDiff, imgNot)
464
            configs = app_doc_data.getConfigs('Filter', 'ErodeSize')
465
            kernel = int(configs[0].value) if 1 == len(configs) else 3
466
            imgNot = cv2.erode(imgNot, np.ones((kernel, kernel), np.uint8))
467
            imgNot = cv2.dilate(imgNot, np.ones((8 + kernel, 8 + kernel), np.uint8))
468

    
469
            contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
470

    
471
            ##
472
            idx = 0
473
            ##
474
            smallContours = []
475
            minimumSize = app_doc_data.getConfigs('Filter', 'MinimumSize')
476
            for contour in contours:
477
                [x, y, w, h] = cv2.boundingRect(contour)
478

    
479
                # remove too small one
480
                if len(minimumSize) is 1:
481
                    if w * h < int(minimumSize[0].value) * int(minimumSize[0].value):
482
                        smallContours.append(contour)
483
                        idx += 1
484
                        continue
485

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

    
505
            """
506
            if app_doc_data.needReOpening is not None:
507
                app_doc_data.needReOpening = True
508
            """
509

    
510
            """
511
            diffFilePath = os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(path))
512
            if os.path.isfile(diffFilePath):
513
                imgDiff = cv2.threshold(cv2.cvtColor(cv2.imread(diffFilePath, 1), cv2.COLOR_BGR2GRAY), 0, 255,
514
                                        cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
515

516
                # remove line from image
517
                lines = app_doc_data.lines
518
                for line in lines:
519
                    line.drawToImage(imgDiff, 255, thickness) if line.thickness is None else \
520
                        line.drawToImage(imgDiff, 255, line.thickness)
521
                cv2.imwrite(diffFilePath, imgDiff)
522
                # up to here
523

524
                imgNot = np.ones(imgDiff.shape, np.uint8)
525
                cv2.bitwise_not(imgDiff, imgNot)
526
                configs = app_doc_data.getConfigs('Filter', 'ErodeSize')
527
                kernel = int(configs[0].value) if 1 == len(configs) else 3
528
                imgNot = cv2.erode(imgNot, np.ones((kernel, kernel), np.uint8))
529
                imgNot = cv2.dilate(imgNot, np.ones((8 + kernel, 8 + kernel), np.uint8))
530

531
                contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
532

533
                ##
534
                idx = 0
535
                ##
536
                smallContours = []
537
                minimumSize = app_doc_data.getConfigs('Filter', 'MinimumSize')
538
                for contour in contours:
539
                    [x, y, w, h] = cv2.boundingRect(contour)
540

541
                    # remove too small one
542
                    if len(minimumSize) is 1:
543
                        if w * h < int(minimumSize[0].value) * int(minimumSize[0].value):
544
                            smallContours.append(contour)
545
                            idx += 1
546
                            continue
547

548
                    '''
549
                    rect = QRectF(x, y, w, h)
550
                    items = [item for item in diffItems if item.boundingRect().contains(rect)]
551
                    if len(items) > 0: continue
552

553
                    items = [item for item in diffItems if rect.contains(item.boundingRect())]
554
                    for item in items:
555
                        diffItems.remove(item)
556
                    '''
557

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

577
                imgNotRemoveSmall = cv2.drawContours(imgNot, smallContours, -1, 0, -1)
578
                notFilePath = os.path.join(project.getTempPath(), "NOT_" + os.path.basename(path))
579
                cv2.imwrite(notFilePath, imgNotRemoveSmall)
580
            else:
581
                message = 'can\'t found {}'.format(diffFilePath)
582
                self.displayLog.emit(MessageType.Normal, message)
583
            """
584

    
585
        except Exception as ex:
586
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
587
                                                           sys.exc_info()[-1].tb_lineno)
588
            self.displayLog.emit(MessageType.Error, message)
589

    
590
    def determine_remain_object(self, idx, contours, imgNot):
591
        """determine remain objects -> line no indicator or unknown"""
592
        import math
593
        [x, y, w, h] = cv2.boundingRect(contours[idx])
594

    
595
        if w < 250 and h < 250:
596
            return ('Unknown', [])
597

    
598
        fLines = []
599
        maxDifAngle = 3
600
        mask = np.zeros_like(imgNot)
601
        cv2.drawContours(mask, contours, idx, 123, -1)  # Draw filled contour in mask
602
        out = np.zeros_like(imgNot)  # Extract out the object and place into output image
603
        out[mask == 123] = imgNot[mask == 123]
604

    
605
        # Now crop
606
        ##print(out)
607
        (x, y) = np.where(mask == 123)
608
        (topx, topy) = (np.min(x), np.min(y))
609
        (bottomx, bottomy) = (np.max(x), np.max(y))
610
        out = out[topx:bottomx + 1, topy:bottomy + 1]
611
        h, w = out.shape[0], out.shape[1]
612
        maxDifH, maxDifW = math.ceil(math.tan(4 * math.pi / 180) / 2 * w), math.ceil(
613
            math.tan(4 * math.pi / 180) / 2 * h)
614

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

    
634
        horLines = []
635
        verLines = []
636
        otherLines = []
637
        isVH = None
638
        for fLine in fLines:
639
            degree = math.fabs(fLine[4])
640
            if degree >= 90 - maxDifAngle:
641
                verLines.append(fLine)
642
            elif degree <= maxDifAngle:
643
                horLines.append(fLine)
644
            else:
645
                otherLines.append(fLine)
646

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

    
668
        for otherLine in otherLines:
669
            x, y = w / 2, 0
670
            x1, y1, x2, y2 = otherLine[0], otherLine[1], otherLine[2], otherLine[3]
671
            y = ((y2 - y1) / (x2 - x1)) * x + y1 - ((y2 - y1) / (x2 - x1)) * x1
672
            otherLine.append(y)
673

    
674
        # determine line no indicator
675
        if not ((len(horLines) > 0 and len(verLines) > 0) or len(otherLines) is 0 or (
676
                len(horLines) == 0 and len(verLines) == 0)):
677
            result, mergedOtherLine = self.is_line_no_indicator(w, h, maxDifAngle, baseDifV, baseLines, otherLines)
678
            if result:
679
                # print(fLines)
680
                return ('LineIndicator', [isVH, mergedOtherLine])
681

    
682
        return ('Unknown', [])
683

    
684
    def is_line_no_indicator(self, w, h, maxDifAngle, baseDifV, baseLines, otherLines):
685
        """determine line no indicator"""
686
        import math
687

    
688
        if w < 250 and h < 250:
689
            return (False, None)
690

    
691
        isSameLine = True
692
        i = 0
693
        for baseLine in baseLines:
694
            if not isSameLine: break
695
            j = 0
696
            for baseLinee in baseLines:
697
                if i == j:
698
                    j += 1
699
                    continue
700
                difV = math.fabs(baseLine[5] - baseLinee[5])
701
                if difV > baseDifV:
702
                    isSameLine = False
703
                    break
704
                j += 1
705
            i += 1
706
        if not isSameLine:
707
            return (False, None)
708

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

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

    
757
        # Show the output image
758
        # print('line no indicator')
759
        mergedOtherLine[0] = round(mergedOtherLine[0] / len(otherLines))
760
        mergedOtherLine[1] = round(mergedOtherLine[1] / len(otherLines))
761
        mergedOtherLine[2] = round(mergedOtherLine[2] / len(otherLines))
762
        mergedOtherLine[3] = round(mergedOtherLine[3] / len(otherLines))
763
        # cv2.line(out, (mergedOtherLine[0], mergedOtherLine[1]), (mergedOtherLine[2], mergedOtherLine[3]), (255, 255, 255), 3)
764
        # cv2.imshow('Output', out)
765
        # cv2.waitKey(0)
766
        # cv2.destroyAllWindows()
767
        return (True, mergedOtherLine)
768

    
769
    @staticmethod
770
    def executeRecognition(drawings, listWidget, isLineChecked, worker):
771
        """recognize symbol, text, line from image p&id"""
772
        import re
773
        import timeit
774
        from TextDetector import TextDetector
775
        from datetime import datetime
776

    
777
        global ocrCompletedSrc
778
        global threadLock
779
        global searchedSymbolList
780
        global textInfoList
781
        global maxProgressValue
782

    
783
        try:
784
            app_doc_data = AppDocData.instance()
785
            #drawings = app_doc_data.getDrawings()
786
            project = app_doc_data.getCurrentProject()
787
            textDetector = TextDetector()
788

    
789
            Worker.initTargetSymbolDataList()
790

    
791
            for drawing in drawings:
792
                ocrCompletedSrc = []
793
                searchedSymbolList = []
794
                textInfoList = []
795

    
796
                mainRes = drawing.file_path
797
                if not os.path.isfile(mainRes):
798
                    item = QListWidgetItem('{} file is not found'.format(os.path.basename(mainRes)))
799
                    item.setBackground(Qt.red)
800
                    listWidget.addItem(item)
801
                    continue
802

    
803
                #worker.scene.clear()
804
                #worker.text_scene.clear()
805
                #worker.line_scene.clear()
806
                worker.clear_scene.emit([worker.scene, worker.text_scene, worker.line_scene])
807
                worker.cond.wait(worker.mutex)
808

    
809
                app_doc_data.clearItemList(True)
810

    
811
                app_doc_data.setImgFilePath(mainRes)
812
                app_doc_data.imgSrc = None
813
                matches = [drawing for drawing in drawings if app_doc_data.imgName == os.path.splitext(drawing.name)[0]]
814
                app_doc_data.activeDrawing = drawing # matches[0] if matches else Drawing(None, app_doc_data.imgName, None)
815
                app_doc_data.activeDrawing.set_pid_source(Image.open(mainRes))
816

    
817
                # Load equipment package
818
                worker.add_predata_to_scene.emit(app_doc_data.activeDrawing, worker.scene, False, False, False, False, True)
819
                worker.cond.wait(worker.mutex)
820

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

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

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

    
846
                area = app_doc_data.getArea('Drawing')
847
                if area is not None:
848
                    # copy image
849
                    area.img = app_doc_data.imgSrc[round(area.y):round(area.y + area.height),
850
                               round(area.x):round(area.x + area.width)]
851

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

    
854
                    # area.contours, area.hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
855
                    area.not_img = cv2.bitwise_not(area.img)
856
                    area.contours, area.hierachy = cv2.findContours(area.not_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
857

    
858
                maxProgressValue = 0
859
                start_time = timeit.default_timer()
860
                listWidget.addItem(f"Start recognition {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} : {mainRes}")
861
                threadLock.acquire()
862

    
863
                worker.updateBatchProgress.emit(len(drawings), 1)
864

    
865
                if worker.isSymbolChecked:
866
                    # calculate total count of symbol
867
                    for targetItem in targetSymbolList:
868
                        if type(targetItem) is list:
869
                            maxProgressValue += len(targetItem)
870
                        else:
871
                            maxProgressValue += 1
872
                    # up to here
873
                    maxProgressValue = maxProgressValue * 2
874
                threadLock.release()
875

    
876
                if worker.isSymbolChecked:
877
                    worker.displayTitle.emit(worker.tr('Detecting symbols...'))
878
                 
879
                    configs = app_doc_data.getConfigs('Engine', 'Symbol')
880
                    if (configs and int(configs[0].value) is 1) or not configs:
881
                    # get symbol original way
882
            
883
                        # detect only equipments
884
                        with futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER) as pool:
885
                            future_equipment = {pool.submit(Worker.detectSymbolsOnPid, mainRes, symbol, listWidget, worker):
886
                                                symbol for symbol in targetSymbolList[0]}
887
                            futures.wait(future_equipment)
888
                        # up to here
889

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

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

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

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

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

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

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

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

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

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

    
984
                worker.updateBatchProgress.emit(len(drawings), 1)
985

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

    
992
                offset = (area.x, area.y) if area is not None else (0, 0)
993

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

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

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

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

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

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

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

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

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

    
1066
                    """load texts"""
1067
                    worker.add_predata_to_scene.emit(app_doc_data.activeDrawing, worker.text_scene,  False, True, False, False, False)
1068
                    worker.cond.wait(worker.mutex)
1069
                    """up to here"""
1070
                    textItems = [item for item in worker.text_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
                    """load lines"""
1143
                    worker.add_predata_to_scene.emit(app_doc_data.activeDrawing, worker.line_scene,  False, False, True, False, False)
1144
                    worker.cond.wait(worker.mutex)
1145
                    """up to here"""
1146
                    lineItems = [item for item in worker.line_scene.items()
1147
                                 if issubclass(type(item), QEngineeringLineItem)]
1148
                    app_doc_data.lines.extend(lineItems)
1149
                    app_doc_data.allItems.extend(lineItems)
1150

    
1151
                    for lineItem in lineItems:
1152
                        lineItem.owner = None
1153
                        for conn in lineItem.connectors:
1154
                            conn.connectedItem = None
1155
                        #worker.scene.removeItem(lineItem)
1156

    
1157
                # try to detect nozzle from image drawing
1158
                if worker.isSymbolChecked:
1159
                    worker.displayTitle.emit(worker.tr('Detecting nozzle and flange...'))
1160

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

    
1170
                    worker.create_detected_items(nozzles, [], [], [])
1171

    
1172
                    for nozzle in nozzles:
1173
                        Worker.remove_detected_symbol_image(nozzle, app_doc_data.imgSrc)
1174
                # up to here
1175

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

    
1183
                Worker.drawFoundSymbolsOnCanvas(mainRes, searchedSymbolList, textInfoList, listWidget)
1184

    
1185
                # get difference between original and recognized image
1186
                foundFilePath = os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(mainRes))
1187
                Worker.getDifference(mainRes, foundFilePath)
1188
                # up to here
1189

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

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

    
1224
                    # connect line to line
1225
                    try:
1226
                        for line in app_doc_data.lines:
1227
                            matches = [it for it in app_doc_data.lines if
1228
                                       (it is not line) and (not line.isParallel(it))]
1229

    
1230
                            for match in matches:
1231
                                detector.connectLineToLine(match, line, toler)
1232

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

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

    
1266
                worker.create_unknown_items(mainRes)
1267
                worker.add_detected_items_to_scene.emit(worker.scene)
1268
                worker.cond.wait(worker.mutex)
1269

    
1270
                worker.scene._end = False
1271
                # clear drawing
1272
                app_doc_data.activeDrawing.image = None
1273

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

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

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

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

    
1316
        try:
1317
            listWidget.addItem('Starting line recognition')
1318
            worker.displayTitle.emit(worker.tr('Detecting lines...'))
1319

    
1320
            app_doc_data = AppDocData.instance()
1321
            project = app_doc_data.getCurrentProject()
1322

    
1323
            # detect line
1324
            connectedLines = []
1325

    
1326
            area = app_doc_data.getArea('Drawing')
1327
            if area is not None:
1328
                area.img = app_doc_data.imgSrc[round(area.y + 1):round(area.y + area.height),
1329
                            round(area.x + 1):round(area.x + area.width)]
1330

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1406
        return targetSymbolList
1407

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

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

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

    
1434
        return res
1435

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

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

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

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

    
1482
            foundSymbolCount = 0
1483

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

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

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

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

    
1520
            # try to recognize symbol twice(first one is normal, second one is flipped)
1521
            steps = [False, True] if detectFlip else [False]
1522
            for flipped in steps:
1523
                if flipped:
1524
                    symGray = symGrayOri
1525
                    symGray = cv2.flip(symGray, 1)
1526

    
1527
                symbolRotatedAngle = 0
1528
                for rc in range(symbolRotateCount + 1):  # Rotation count를 사용자 기준으로 받아서 1을 더한 후 사용
1529
                    sw, sh = symGray.shape[::-1]
1530
                    roiw = (roiItemEp[0] - roiItemSp[0])
1531
                    roih = (roiItemEp[1] - roiItemSp[1])
1532

    
1533
                    # Case : symbol is bigger than roi
1534
                    if roiw < sw or roih < sh:
1535
                        symGray = cv2.rotate(symGray, cv2.ROTATE_90_CLOCKWISE)
1536
                        symbolRotatedAngle = (symbolRotatedAngle + 90) % 360
1537

    
1538
                        if baseSymbol is not None and additionalSymbol is not None:
1539
                            additionalSymbol = Worker.getRotatedChildInfo(additionalSymbol)
1540
                        continue
1541

    
1542
                    # get Rotated Original Point
1543
                    originalPoint = Worker.getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint,
1544
                                                                      symbolRotatedAngle, sw, sh, sow, soh, flipped)
1545
                    connectionPoint = Worker.getCalculatedConnectionPoint(symbolConnectionPoint, symbolRotatedAngle, sw,
1546
                                                                          sh, sow, soh, flipped)
1547

    
1548
                    # For OPC
1549
                    drawing_area = app_doc_data.getArea('Drawing')
1550
                    if drawing_area is not None and (symbolType == "Piping OPC\'s" or symbolType == "Instrument OPC\'s"):
1551
                        customMatch = worker.detectOPCOnPid(drawing_area, symGray)
1552
                        if customMatch and len(customMatch) > 0:
1553
                            for custom in customMatch:
1554
                                hitRate = custom[0]
1555
                                searchedItemSp = (custom[1][0] + round(offsetDrawingArea[0]), custom[1][1] + round(offsetDrawingArea[1]))
1556

    
1557
                                is_add = True
1558
                                for searched in searchedSymbolList:
1559
                                    if Worker.IsOverlap((searchedItemSp[0], searchedItemSp[1], sw, sh), (
1560
                                            searched.getSp()[0], searched.getSp()[1], searched.getWidth(),
1561
                                            searched.getHeight())):
1562
                                        if searched.getHitRate() > hitRate:
1563
                                            is_add = False
1564
                                        else:
1565
                                            searchedSymbolList.remove(searched)
1566
                                            break
1567

    
1568
                                if is_add:
1569
                                    threadLock.acquire()
1570
                                    foundSymbolCount = foundSymbolCount + 1
1571
                                    Worker.addSearchedSymbol(symbolName, symbolType,
1572
                                                             searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount,
1573
                                                             hitRate, symbolRotatedAngle,
1574
                                                             isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1575
                                                             isContainChild,
1576
                                                             originalPoint, connectionPoint, baseSymbol, additionalSymbol,
1577
                                                             isExceptDetect, detectFlip=1 if flipped else 0,
1578
                                                             hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1579
                                    threadLock.release()
1580

    
1581
                    # Template Matching
1582
                    tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
1583
                    loc = np.where(tmRes >= symbolThreshold)
1584

    
1585
                    for pt in zip(*loc[::-1]):
1586
                        '''
1587
                        # no more used 
1588
                        mpCount = 0  # Match Point Count
1589
                        roi = roiItem[pt[1]:pt[1] + sh, pt[0]:pt[0] + sw]
1590

1591
                        if symbolMinMatchCount > 0:
1592
                            mpCount = Worker.getMatchPointCount(roi, symGray)
1593
                            if not (mpCount >= symbolMinMatchCount):
1594
                                continue
1595
                        '''
1596

    
1597
                        searchedItemSp = (roiItemSp[0] + pt[0] + round(offsetDrawingArea[0]),
1598
                                          roiItemSp[1] + pt[1] + round(offsetDrawingArea[1]))
1599
                        # print(searchedItemSp)
1600

    
1601
                        overlapArea = 0
1602
                        symbolIndex = -1
1603
                        for i in range(len(searchedSymbolList) - 1, -1, -1):
1604
                            area = Worker.contains(searchedSymbolList[i], searchedItemSp, sw, sh)
1605
                            if area > ACCEPT_OVERLAY_AREA:
1606
                                # if area > overlapArea:
1607
                                #    overlapArea = area
1608
                                #    symbolIndex = i
1609
                                overlapArea = area
1610
                                symbolIndex = i
1611
                                break
1612
                                """
1613
                                categories = [appDocData.isEquipmentType(symbolType), appDocData.isEquipmentType(searchedSymbolList[i].getType())]
1614
                                if categories[0] == categories[1]:
1615
                                    symbolIndex = i
1616
                                    break
1617
                                """
1618

    
1619
                        hitRate = tmRes[pt[1], pt[0]]
1620

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

    
1720
                    # rotate symbol
1721
                    symGray = cv2.rotate(symGray, cv2.ROTATE_90_CLOCKWISE)
1722
                    symbolRotatedAngle = (symbolRotatedAngle + 90) % 360
1723

    
1724
                    if additionalSymbol is not None:
1725
                        additionalSymbol = Worker.getRotatedChildInfo(additionalSymbol)
1726

    
1727
            threadLock.acquire()
1728
            listWidget.addItem('Found Symbol   : ' + os.path.splitext(os.path.basename(symbolPath))[0] + ' - (' + str(
1729
                foundSymbolCount) + ')')
1730
            threadLock.release()
1731

    
1732
            """
1733
            if area is not None and hasInstrumentLabel:
1734
                # restore objects smaller than symbol
1735
                roiItem = cv2.drawContours(roiItem, outside_contours, -1, (0, 0, 0), -1)
1736
                roiItem = cv2.drawContours(roiItem, hole_contours, -1, (255, 255, 255), -1)
1737
                # up to here
1738
                cv2.imwrite('c:\\Temp\\contour2.png', roiItem)
1739
            """
1740

    
1741
            worker.updateProgress.emit(maxProgressValue, symbolPath)
1742

    
1743
            return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
1744
        except Exception as ex:
1745
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1746
                                                           sys.exc_info()[-1].tb_lineno)
1747
            worker.displayLog.emit(MessageType.Error, message)
1748

    
1749
        return []
1750

    
1751
    @staticmethod
1752
    def IsOverlap(range1, range2):
1753
        if range1[0] <= range2[0] + range2[2] and range1[0] + range1[2] >= range2[0] and range1[1] <= range2[1] + \
1754
                range2[3] and range1[1] + range1[3] >= range2[1]:
1755
            range = (min(range1[0] + range1[2], range2[0] + range2[2]) - max(range1[0], range2[0])) * (
1756
                    min(range1[1] + range1[3], range2[1] + range2[3]) - max(range1[1], range2[1]))
1757
            if range >= range1[2] * range1[3] * 0.4 and range >= range2[2] * range2[3] * 0.4:
1758
                return True
1759
            else:
1760
                return False
1761
        else:
1762
            return False
1763

    
1764
    @staticmethod
1765
    def detectOPCOnPid(area, symGray):
1766
        results = []
1767

    
1768
        try:
1769
            symbol = cv2.copyMakeBorder(symGray, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=255)
1770
            not_symbol = cv2.bitwise_not(symbol)
1771
            symbol_contours, symbol_hierachy = cv2.findContours(not_symbol, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
1772
            if symbol_hierachy[0][0][0] != -1:
1773
                return results
1774

    
1775
            contour_count = len(symbol_contours)
1776
            if contour_count != 2:
1777
                return results
1778

    
1779
            for i in range(1, contour_count):
1780
                # contour size
1781
                symbol_area = cv2.contourArea(symbol_contours[i])
1782
                # moments mass center
1783
                symbol_moments = cv2.moments(symbol_contours[i])
1784
                symbol_x = int(symbol_moments['m10'] / (symbol_moments['m00'] + 1e-5))
1785
                symbol_y = int(symbol_moments['m01'] / (symbol_moments['m00'] + 1e-5))
1786
                rect_x, rect_y, rect_w, rect_h = cv2.boundingRect(symbol_contours[i])
1787
                symbol_x = symbol_x - rect_x
1788
                symbol_y = symbol_y - rect_y
1789
                # percent x, y
1790
                percent_x = symbol_x / rect_w
1791
                percent_y = symbol_y / rect_h
1792

    
1793
                for contour in area.contours:
1794
                    area_area = cv2.contourArea(contour)
1795
                    if area_area * 1.2 >= symbol_area >= area_area * 0.8:
1796
                        I1 = cv2.matchShapes(symbol_contours[i], contour, 1, 0)
1797
                        I2 = cv2.matchShapes(symbol_contours[i], contour, 2, 0)
1798
                        I3 = cv2.matchShapes(symbol_contours[i], contour, 3, 0)
1799
                        if I1 < 1 and I2 < 1 and I3 < 0.1:
1800
                            rect_x2, rect_y2, rect_w2, rect_h2 = cv2.boundingRect(contour)
1801
                            if rect_w * 1.2 >= rect_w2 >= rect_w * 0.8 and rect_h * 1.2 >= rect_h2 >= rect_h * 0.8:
1802
                                # moments mass center
1803
                                moments = cv2.moments(contour)
1804
                                x = int(moments['m10'] / (moments['m00'] + 1e-5))
1805
                                y = int(moments['m01'] / (moments['m00'] + 1e-5))
1806

    
1807
                                x = x - rect_x2
1808
                                y = y - rect_y2
1809
                                percent_x2 = x / rect_w2
1810
                                percent_y2 = y / rect_h2
1811

    
1812
                                value_x = abs(percent_x - percent_x2)
1813
                                value_y = abs(percent_y - percent_y2)
1814

    
1815
                                results.append([1 - (value_x + value_y), [rect_x2, rect_y2]])
1816
                break
1817
        except Exception as ex:
1818
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1819
                                                           sys.exc_info()[-1].tb_lineno)
1820
        return results
1821

    
1822
    # Convert into Grayscale image
1823
    @staticmethod
1824
    def cvtGrayImage(img):
1825
        return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1826

    
1827
    '''
1828
        @history    2018.06.12  Jeongwoo    Type changed (int → float)
1829
                    humkyung 2018.07.07 change return type as like [x,y]
1830
    '''
1831

    
1832
    @staticmethod
1833
    def getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, rotateSymbolWidth,
1834
                                   rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight, flipped=False):
1835
        res = []
1836

    
1837
        if additionalSymbol is None and symbolOriginalPoint is None:
1838
            res.append(rotateSymbolWidth // 2)
1839
            res.append(rotateSymbolHeight // 2)
1840
        else:
1841
            if flipped:
1842
                opx = originalSymbolWidth - float(symbolOriginalPoint.split(',')[0])
1843
                opy = float(symbolOriginalPoint.split(',')[1])
1844
            else:
1845
                opx = float(symbolOriginalPoint.split(',')[0])
1846
                opy = float(symbolOriginalPoint.split(',')[1])
1847

    
1848
            rPt = Worker.getCoordOnRotatedImage(symbolRotatedAngle, ('AUTO', opx, opy, '0'), originalSymbolWidth,
1849
                                                originalSymbolHeight)
1850

    
1851
            res.append(rPt[1])
1852
            res.append(rPt[2])
1853

    
1854
        return res
1855

    
1856
    '''
1857
        @history    2018.06.12  Jeongwoo    Type changed (int → float)
1858
                    humkyung 2018.07.07 change return type as like [[x,y],...]
1859
                    humkyung 2019.01.04 get symbol index
1860
    '''
1861

    
1862
    @staticmethod
1863
    def getCalculatedConnectionPoint(symbolConnectionPointStr, symbolRotatedAngle, rotateSymbolWidth,
1864
                                     rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight, flipped=0):
1865
        res = []
1866

    
1867
        if symbolConnectionPointStr is not None and symbolConnectionPointStr != '':
1868
            splitConnectionPointStr = symbolConnectionPointStr.split("/")
1869
            for strConnPt in splitConnectionPointStr:
1870
                tokens = strConnPt.split(',')
1871

    
1872
                direction = 'AUTO'
1873
                symbol_idx = '0'
1874
                if flipped:
1875
                    converted = {'AUTO': 'AUTO', 'LEFT': 'RIGHT', 'RIGHT': 'LEFT', 'UP': 'UP', 'DOWN': 'DOWN'}
1876

    
1877
                    if len(tokens) == 2:
1878
                        cpx = originalSymbolWidth - float(tokens[0])
1879
                        cpy = float(tokens[1])
1880
                    elif len(tokens) == 3:
1881
                        direction = converted[tokens[0]]
1882
                        cpx = originalSymbolWidth - float(tokens[1])
1883
                        cpy = float(tokens[2])
1884
                    elif len(tokens) >= 4:
1885
                        direction = converted[tokens[0]]
1886
                        cpx = originalSymbolWidth - float(tokens[1])
1887
                        cpy = float(tokens[2])
1888
                        symbol_idx = tokens[3]
1889
                else:
1890
                    if len(tokens) == 2:
1891
                        cpx = float(tokens[0])
1892
                        cpy = float(tokens[1])
1893
                    elif len(tokens) == 3:
1894
                        direction = tokens[0]
1895
                        cpx = float(tokens[1])
1896
                        cpy = float(tokens[2])
1897
                    elif len(tokens) >= 4:
1898
                        direction = tokens[0]
1899
                        cpx = float(tokens[1])
1900
                        cpy = float(tokens[2])
1901
                        symbol_idx = tokens[3]
1902

    
1903
                res.append(Worker.getCoordOnRotatedImage(symbolRotatedAngle, (direction, cpx, cpy, symbol_idx),
1904
                                                         originalSymbolWidth, originalSymbolHeight))
1905

    
1906
        return res
1907

    
1908
    '''
1909
        @brief      rotate (x,y) by given angle
1910
        @author     Jeongwoo
1911
        @date       2018.??.??
1912
        @history    humkyung 2018.04.13 fixed code when angle is 90 or 270    
1913
                    Jeongwoo 2018.04.27 Change calculation method with QTransform
1914
                    humkyung 2018.09.01 calculate rotated direction
1915
    '''
1916

    
1917
    @staticmethod
1918
    def getCoordOnRotatedImage(angle, connPt, originImageWidth, originImageHeight):
1919
        rx = None
1920
        ry = None
1921

    
1922
        # calculate rotated direction
1923
        direction = connPt[0]
1924
        if direction == 'LEFT':
1925
            direction = 'DOWN' if angle == 90 else 'RIGHT' if angle == 180 else 'UP' if angle == 270 else direction
1926
        elif direction == 'RIGHT':
1927
            direction = 'UP' if angle == 90 else 'LEFT' if angle == 180 else 'DOWN' if angle == 270 else direction
1928
        elif direction == 'UP':
1929
            direction = 'LEFT' if angle == 90 else 'DOWN' if angle == 180 else 'RIGHT' if angle == 270 else direction
1930
        elif direction == 'DOWN':
1931
            direction = 'RIGHT' if angle == 90 else 'UP' if angle == 180 else 'LEFT' if angle == 270 else direction
1932
        # up to here
1933

    
1934
        transform = QTransform()
1935
        if angle == 90 or angle == 270:
1936
            transform.translate(originImageHeight * 0.5, originImageWidth * 0.5)
1937
        elif angle == 0 or angle == 180:
1938
            transform.translate(originImageWidth * 0.5, originImageHeight * 0.5)
1939
        transform.rotate(abs(angle))
1940
        transform.translate(-originImageWidth * 0.5, -originImageHeight * 0.5)
1941
        point = QPoint(connPt[1], connPt[2])
1942
        point = transform.map(point)
1943
        rx = point.x()
1944
        ry = point.y()
1945

    
1946
        symbol_idx = connPt[3]
1947

    
1948
        return (direction, rx, ry, symbol_idx)
1949

    
1950
    '''
1951
        @brief      Add symbols
1952
        @author     jwkim
1953
        @date
1954
        @history    Change parameter (mpCount → hitRate)
1955
                    Yecheol 2018.07.04 Delete Symbol Id 
1956
    '''
1957

    
1958
    @staticmethod
1959
    def addSearchedSymbol(sName, sType
1960
                          , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle
1961
                          , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
1962
                          , originalPoint, connectionPoint, baseSymbol, additionalSymbol, isExceptDetect, detectFlip
1963
                          , hasInstrumentLabel, text_area):
1964
        global searchedSymbolList
1965

    
1966
        newSym = None
1967
        try:
1968
            newSym = symbol.Symbol(sName, sType
1969
                                   , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle,
1970
                                   isDetectOnOrigin, rotateCount, ocrOption, isContainChild,
1971
                                   ','.join(str(x) for x in originalPoint),
1972
                                   '/'.join('{},{},{},{}'.format(param[0], param[1], param[2], param[3]) for param in
1973
                                            connectionPoint),
1974
                                   baseSymbol, additionalSymbol, isExceptDetect, detectFlip=1 if detectFlip else 0,
1975
                                   hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1976

    
1977
            searchedSymbolList.append(newSym)
1978
        except Exception as ex:
1979
            from App import App
1980
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1981
                                                           sys.exc_info()[-1].tb_lineno)
1982
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1983

    
1984
        return newSym
1985

    
1986
    '''
1987
        @brief      Check object contains pt
1988
        @param      obj is item in searchedSymbolList
1989
    '''
1990

    
1991
    @staticmethod
1992
    def contains(obj, pt, tw, th):
1993
        sp = obj.getSp()
1994
        width = obj.getWidth()
1995
        height = obj.getHeight()
1996

    
1997
        if sp[0] > pt[0] + tw:
1998
            return 0
1999
        if sp[0] + width < pt[0]:
2000
            return 0
2001
        if sp[1] > pt[1] + th:
2002
            return 0
2003
        if sp[1] + height < pt[1]:
2004
            return 0
2005

    
2006
        # shared area
2007
        x = max(sp[0], pt[0])
2008
        y = max(sp[1], pt[1])
2009
        w = min(sp[0] + width, pt[0] + tw) - x
2010
        h = min(sp[1] + height, pt[1] + th) - y
2011

    
2012
        return float((w * h)) / float((tw * th)) * 100
2013

    
2014
    # Calculate count of keypoint match result
2015
    @staticmethod
2016
    def getMatchPointCount(src, cmp):
2017
        matchCount = 0
2018

    
2019
        try:
2020
            orb = cv2.ORB_create(1000, 2.0, 2, 1)
2021

    
2022
            kp1, des1 = orb.detectAndCompute(src, None)
2023
            kp2, des2 = orb.detectAndCompute(cmp, None)
2024

    
2025
            FLANN_INDEX_LSH = 6
2026
            # table_number      : The number of hash tables use
2027
            # key_size          : The length of the key in the hash tables
2028
            # multi_probe_level : Number of levels to use in multi-probe (0 for standard LSH)
2029
            #                     It controls how neighboring buckets are searched
2030
            #                     Recommended value is 2
2031
            # checks            : specifies the maximum leafs to visit when searching for neighbours.
2032
            # LSH : Locality-Sensitive Hashing
2033
            # ref : https://www.cs.ubc.ca/research/flann/uploads/FLANN/flann_manual-1.8.4.pdf
2034
            index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=20, key_size=10, multi_probe_level=4)
2035
            search_params = dict(checks=100)
2036

    
2037
            flann = cv2.FlannBasedMatcher(index_params, search_params)
2038

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

    
2042
            count = 0
2043
            # ratio test as per Lowe's paper
2044
            for i in range(len(matches)):
2045
                if len(matches[i]) == 2:
2046
                    m = matches[i][0]
2047
                    n = matches[i][1]
2048
                    if m.distance < 0.85 * n.distance:
2049
                        count = count + 1
2050

    
2051
            matchCount = count
2052
        except Exception as ex:
2053
            from App import App
2054
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2055
                                                           sys.exc_info()[-1].tb_lineno)
2056
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2057

    
2058
        return matchCount
2059

    
2060
    '''
2061
        @brief      Remake rotated child symbol info
2062
    '''
2063

    
2064
    @staticmethod
2065
    def getRotatedChildInfo(additionalSymbol):
2066
        tempChildInfo = ""
2067
        if additionalSymbol:
2068
            childList = additionalSymbol.split("/")
2069
            for index in range(len(childList)):
2070
                child = childList[index]
2071
                direction = Worker.convertDirectionCodeToValue(child.split(",")[0])
2072
                childName = child.split(",")[1]
2073
                direction = (direction - 1) if direction > 0 else 3
2074
                if index != 0:
2075
                    tempChildInfo = tempChildInfo + "/"
2076
                tempChildInfo = tempChildInfo + Worker.convertValueToDirectionCode(direction) + "," + childName
2077
        return tempChildInfo
2078

    
2079
    @staticmethod
2080
    def calculate_exact_position(area, symGray, symbol, rect, worker):
2081
        import copy
2082

    
2083
        try:
2084
            symbolThreshold = symbol.getThreshold()
2085
            symbolRotateCount = symbol.getRotationCount()
2086
            detectFlip = symbol.getDetectFlip()
2087
            hasInstrumentLabel = symbol.getHasInstrumentLabel()
2088
            symbolOriginalPoint = symbol.getOriginalPoint()
2089
            symbolConnectionPoint = symbol.getConnectionPoint()
2090
            additionalSymbol = symbol.getAdditionalSymbol()
2091

    
2092
            sow, soh = symGray.shape[::-1]
2093

    
2094
            # symbol is bigger than roi -> detected symbol area is too big
2095
            if rect[3] * rect[4] > sow * soh * 1.5:
2096
                return (None, None, None, None, None, None, None)
2097
            # detected symbol area is too small
2098
            elif rect[3] * rect[4] * 1.5 < sow * soh:
2099
                return (None, None, None, None, None, None, None)
2100

    
2101
            # get Rotated Original Point
2102
            sow, soh = symGray.shape[::-1]
2103
            offset = max(sow, soh)
2104

    
2105
            roiItem = Worker.remove_small_objects(area.img, 0, sow * soh * 0.5) if hasInstrumentLabel else area.img.copy()
2106
            x_start = round(rect[1]) - round(offset) if round(rect[1]) - round(offset) > 0 else 0
2107
            y_start = round(rect[2]) - round(offset) if round(rect[2]) - round(offset) > 0 else 0
2108
            x_max = round(rect[1]) + round(rect[3]) + round(offset) if round(rect[1]) + round(rect[3]) + round(offset) < len(roiItem[0]) else len(roiItem[0]) - 1
2109
            y_max = round(rect[2]) + round(rect[4]) + round(offset) if round(rect[2]) + round(rect[4]) + round(offset) < len(roiItem) else len(roiItem) - 1
2110
            roiItem = roiItem[y_start:y_max, x_start:x_max]
2111

    
2112
            symGrayOri = copy.copy(symGray)
2113

    
2114
            searchedInfos = [] # score, x, y, angle, flip, originalPoint, connectionPoint, sw, sh
2115

    
2116
            steps = [False, True] if detectFlip else [False]
2117
            for flipped in steps:
2118
                if flipped:
2119
                    symGray = symGrayOri
2120
                    symGray = cv2.flip(symGray, 1)
2121

    
2122
                symbolRotatedAngle = 0
2123
                for rc in range(symbolRotateCount + 1):
2124
                    sw, sh = symGray.shape[::-1]
2125

    
2126
                    originalPoint = Worker.getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint,
2127
                                                    symbolRotatedAngle, sw, sh, sow, soh, flipped)
2128
                    connectionPoint = Worker.getCalculatedConnectionPoint(symbolConnectionPoint, symbolRotatedAngle, sw,
2129
                                                    sh, sow, soh, 0)
2130

    
2131
                    tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
2132
                    _, max_val, __, max_loc = cv2.minMaxLoc(tmRes)
2133
                    #maxIndex = tmRes.argmax()
2134
                    #colCount = len(tmRes[0])
2135
                    #col, row = divmod(maxIndex, colCount)
2136

    
2137
                    if max_val > symbolThreshold * 0.8:
2138
                        searchedInfos.append([max_val, max_loc[0] + x_start, max_loc[1] + y_start, symbolRotatedAngle, flipped, originalPoint, connectionPoint, sw, sh])
2139

    
2140
                    symGray = cv2.rotate(symGray, cv2.ROTATE_90_CLOCKWISE)
2141
                    symbolRotatedAngle = (symbolRotatedAngle + 90) % 360
2142

    
2143
            if searchedInfos:
2144
                searchedInfos = sorted(searchedInfos, key=lambda param: param[0], reverse=True)
2145
                searchedInfo = searchedInfos[0]
2146
                return ((searchedInfo[1] + area.x, searchedInfo[2] + area.y), searchedInfo[3], searchedInfo[4], \
2147
                        searchedInfo[5], searchedInfo[6], searchedInfo[7], searchedInfo[8])
2148
            else:
2149
                return (None, None, None, None, None, None, None)
2150

    
2151
        except Exception as ex:
2152
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2153
                                                            sys.exc_info()[-1].tb_lineno)
2154
            worker.displayLog.emit(MessageType.Error, message)
2155

    
2156
            return (None, None, None, None, None, None, None)
2157

    
2158
    @staticmethod
2159
    def detect_symbol_using_server(targetSymbols, listWidget, worker):
2160
        from AppWebService import AppWebService
2161

    
2162
        res = []
2163

    
2164
        app_doc_data = AppDocData.instance()
2165
        area = app_doc_data.getArea('Drawing')
2166
        
2167
        app_web_service = AppWebService()
2168
        symbols = app_web_service.request_symbol_box(area.img)
2169

    
2170
        for targetSymbol in targetSymbols[2]:
2171
            symbolName = targetSymbol.getName()
2172
            symbolType = targetSymbol.getType()
2173
            symbolPath = targetSymbol.getPath()
2174
            symbolThreshold = targetSymbol.getThreshold()
2175
            symbolMinMatchCount = targetSymbol.getMinMatchCount()
2176
            isDetectOnOrigin = targetSymbol.getIsDetectOnOrigin()
2177
            symbolRotateCount = targetSymbol.getRotationCount()
2178
            symbolOcrOption = targetSymbol.getOcrOption()
2179
            isContainChild = targetSymbol.getIsContainChild()
2180
            baseSymbol = targetSymbol.getBaseSymbol()
2181
            additionalSymbol = targetSymbol.getAdditionalSymbol()
2182
            isExceptDetect = targetSymbol.getIsExceptDetect()
2183
            hasInstrumentLabel = targetSymbol.getHasInstrumentLabel()
2184
            text_area = targetSymbol.getText_area()
2185

    
2186
            # check if symbol file is target or not
2187
            if isExceptDetect == 1:
2188
                item = QListWidgetItem('{} file is not target'.format(symbolName))
2189
                item.setBackground(QColor('green'))
2190
                listWidget.addItem(item)
2191
                continue
2192

    
2193
            foundSymbolCount = 0
2194

    
2195
            # check if symbol file exists
2196
            if not os.path.isfile(symbolPath):
2197
                item = QListWidgetItem('{} file not found'.format(symbolName))
2198
                item.setBackground(QColor('red'))
2199
                listWidget.addItem(item)
2200
                continue
2201
            # up to here
2202

    
2203
            sym = cv2.imread(symbolPath, 1)
2204
            symGray = Worker.cvtGrayImage(sym)
2205

    
2206
            for symbol in symbols:
2207
                if symbol[0] == symbolName:
2208
                    searchedItemSp, symbolRotatedAngle, flipped, originalPoint, connectionPoint, sw, sh = \
2209
                                Worker.calculate_exact_position(area, symGray, targetSymbol, symbol, worker)
2210
                    if not searchedItemSp:
2211
                        continue
2212

    
2213
                    hitRate = symbol[5]
2214

    
2215
                    Worker.addSearchedSymbol(symbolName, symbolType,
2216
                                    searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount,
2217
                                    hitRate, symbolRotatedAngle,
2218
                                    isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
2219
                                    isContainChild,
2220
                                    originalPoint, connectionPoint, baseSymbol, additionalSymbol,
2221
                                    isExceptDetect,
2222
                                    detectFlip=1 if flipped else 0,
2223
                                    hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
2224

    
2225
        return symbols
2226
    '''
2227
        @brief   detect symbols on PID
2228
        @history humkyung 2018.06.08 add parameteres for signal
2229
    '''
2230
    @staticmethod
2231
    def detectSymbolsOnPid(mainRes, targetSymbols, listWidget, updateProgressSignal):
2232
        res = []
2233

    
2234
        if type(targetSymbols) is list:
2235
            for detailTarget in targetSymbols:
2236
                res.extend(Worker.detectSymbolOnPid(mainRes, detailTarget, listWidget, updateProgressSignal))
2237
        else:
2238
            res = Worker.detectSymbolOnPid(mainRes, targetSymbols, listWidget, updateProgressSignal)
2239

    
2240
        return res
2241

    
2242
    @staticmethod
2243
    def convertDirectionCodeToValue(directionCode):
2244
        if directionCode == "UP":
2245
            return 0
2246
        elif directionCode == "RIGHT":
2247
            return 1
2248
        elif directionCode == "DOWN":
2249
            return 2
2250
        elif directionCode == "LEFT":
2251
            return 3
2252
        else:
2253
            return -1
2254

    
2255
    @staticmethod
2256
    def convertValueToDirectionCode(value):
2257
        if value == 0:
2258
            return "UP"
2259
        elif value == 1:
2260
            return "RIGHT"
2261
        elif value == 2:
2262
            return "DOWN"
2263
        elif value == 3:
2264
            return "LEFT"
2265
        else:
2266
            return "NONE"
2267

    
2268
    @staticmethod
2269
    def drawFoundSymbolsOnCanvas(drawingPath, symbols, textInfos, listWidget):
2270
        """draw found symbols and texts to image"""
2271

    
2272
        global src
2273
        global ocrCompletedSrc
2274
        global canvas
2275

    
2276
        app_doc_data = AppDocData.instance()
2277
        canvas = np.zeros(app_doc_data.imgSrc.shape, np.uint8)
2278
        canvas[::] = 255
2279

    
2280
        try:
2281
            project = app_doc_data.getCurrentProject()
2282

    
2283
            for symbol in symbols:
2284
                Worker.drawFoundSymbols(symbol, listWidget)
2285

    
2286
            for text in textInfos:
2287
                left = text.getX()
2288
                top = text.getY()
2289
                right = text.getX() + text.getW()
2290
                bottom = text.getY() + text.getH()
2291

    
2292
                canvas[top:bottom, left:right] = app_doc_data.imgSrc[top:bottom, left:right]
2293

    
2294
            cv2.imwrite(os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(drawingPath)), canvas)
2295
        except Exception as ex:
2296
            from App import App
2297
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2298
                                                           sys.exc_info()[-1].tb_lineno)
2299
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2300

    
2301
    '''
2302
        @history    2018.04.27  Jeongwoo    Remove Tesseract Log on listWidget
2303
                    2018.05.04  Jeongwoo    Change method to OCR with tesseract_ocr_module.py
2304
                    2018.05.09  Jeongwoo    Add global variable textInfoList, Remove text in symbol and Add tesseract result text
2305
                    2018.05.10  Jeongwoo    Remove not used if-statement
2306
                    2018.06.19  Jeongwoo    When detect text in symbol, use getTextAreaInfo() and Tesseract
2307
                    2018.06.21  Jeongwoo    Add if-statement for way to detect text by Type A
2308
    '''
2309

    
2310
    @staticmethod
2311
    def drawFoundSymbols(symbol, listWidget):
2312
        global src
2313
        global canvas
2314
        #global WHITE_LIST_CHARS
2315
        global searchedSymbolList
2316
        global textInfoList
2317

    
2318
        # symbolId = symbol.getId()
2319
        symbolPath = symbol.getPath()
2320
        symbolSp = symbol.getSp()
2321
        symbolRotatedAngle = symbol.getRotatedAngle()
2322

    
2323
        symImg = cv2.cvtColor(cv2.imread(symbolPath, 1), cv2.COLOR_BGR2GRAY)
2324
        if symbol.getDetectFlip() is 1:
2325
            symImg = cv2.flip(symImg, 1)
2326
        for i in range(symbolRotatedAngle // 90):
2327
            symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
2328

    
2329
        w, h = symImg.shape[::-1]
2330
        canvas[symbolSp[1]:symbolSp[1] + h, symbolSp[0]:symbolSp[0] + w] = cv2.bitwise_and(
2331
            canvas[symbolSp[1]:symbolSp[1] + h, symbolSp[0]:symbolSp[0] + w], symImg)
2332

    
2333
    @staticmethod
2334
    def remove_detected_symbol_image(sym, imgSrc, lock=True):
2335
        """remove detected symbol image from drawing image"""
2336
        if lock:
2337
            global threadLock
2338

    
2339
        try:
2340
            path = sym.getPath()
2341
            sp = (int(sym.getSp()[0]), int(sym.getSp()[1]))
2342
            sw = int(sym.getWidth())
2343
            sh = int(sym.getHeight())
2344
            angle = int(sym.getRotatedAngle())
2345
            # get symbol image
2346
            sym_img = cv2.imread(path)
2347
            sym_img = cv2.cvtColor(sym_img, cv2.COLOR_BGR2GRAY)
2348
            # symImg = cv2.threshold(Worker.cvtGrayImage(symImg), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
2349
            if sym.getDetectFlip() is 1:
2350
                sym_img = cv2.flip(sym_img, 1)
2351

    
2352
            for i in range(angle // 90):
2353
                sym_img = cv2.rotate(sym_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
2354
            # up to here
2355

    
2356
            if lock:
2357
                threadLock.acquire()
2358
            temp = imgSrc[sp[1]:sp[1] + sh, sp[0]:sp[0] + sw]
2359
            sym_img = cv2.erode(sym_img, np.ones((5, 5), np.uint8))
2360
            mask = cv2.bitwise_or(temp, sym_img)
2361
            imgXOR = cv2.bitwise_xor(temp, mask)
2362
            imgSrc[sp[1]:sp[1] + sh, sp[0]:sp[0] + sw] = cv2.bitwise_not(imgXOR)
2363
        except Exception as ex:
2364
            from App import App
2365
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2366
                                                           sys.exc_info()[-1].tb_lineno)
2367
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2368
        finally:
2369
            if lock:
2370
                threadLock.release()
2371

    
2372
    '''
2373
        @brief  get difference between given original and recognized image
2374
        @author humkyung
2375
        @date   2018.06.11
2376
    '''
2377

    
2378
    @staticmethod
2379
    def getDifference(orgImagePath, recImagePath):
2380
        import re
2381

    
2382
        global ocrCompletedSrc
2383
        global textInfoList
2384

    
2385
        try:
2386
            app_doc_data = AppDocData.instance()
2387
            imgOriginal = app_doc_data.imgSrc
2388

    
2389
            # remove not drawing area
2390
            configs = app_doc_data.getConfigs('{} Equipment Desc Area'.format(app_doc_data.imgName))
2391
            for config in configs:
2392
                found = re.findall('\\d+', config.value)
2393
                if len(found) == 4:
2394
                    cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2395
                                    (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2396

    
2397
            configs = app_doc_data.getConfigs('{} Typical Area'.format(app_doc_data.imgName))
2398
            for config in configs:
2399
                found = re.findall('\\d+', config.value)
2400
                if len(found) == 4:
2401
                    cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2402
                                    (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2403

    
2404
            noteArea = app_doc_data.getArea('Note')
2405
            if noteArea is not None:
2406
                noteArea.img = app_doc_data.imgSrc[round(noteArea.y):round(noteArea.y + noteArea.height),
2407
                                round(noteArea.x):round(noteArea.x + noteArea.width)].copy()
2408
                cv2.rectangle(imgOriginal, (round(noteArea.x), round(noteArea.y)),
2409
                                (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
2410
            # up to here
2411

    
2412
            """
2413
            if os.path.isfile(orgImagePath) and os.path.isfile(recImagePath):
2414
                imgOriginal = \
2415
                    cv2.threshold(Worker.cvtGrayImage(cv2.imread(orgImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
2416

2417
                configs = app_doc_data.getConfigs('Filter', 'DilateSize')
2418
                if 1 == len(configs) and int(configs[0].value) is not 0:
2419
                    size = int(configs[0].value)
2420
                    kernel = np.ones((size, size), np.uint8)
2421
                    imgOriginal = cv2.erode(imgOriginal, kernel, iterations=1)
2422

2423
                # remove not drawing area
2424
                configs = app_doc_data.getConfigs('{} Equipment Desc Area'.format(app_doc_data.imgName))
2425
                for config in configs:
2426
                    found = re.findall('\\d+', config.value)
2427
                    if len(found) == 4:
2428
                        cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2429
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2430

2431
                configs = app_doc_data.getConfigs('{} Typical Area'.format(app_doc_data.imgName))
2432
                for config in configs:
2433
                    found = re.findall('\\d+', config.value)
2434
                    if len(found) == 4:
2435
                        cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
2436
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
2437

2438
                noteArea = app_doc_data.getArea('Note')
2439
                if noteArea is not None:
2440
                    noteArea.img = app_doc_data.imgSrc[round(noteArea.y):round(noteArea.y + noteArea.height),
2441
                                   round(noteArea.x):round(noteArea.x + noteArea.width)].copy()
2442
                    cv2.rectangle(imgOriginal, (round(noteArea.x), round(noteArea.y)),
2443
                                  (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
2444
                # up to here
2445

2446
                imgRecognized = \
2447
                    cv2.threshold(Worker.cvtGrayImage(cv2.imread(recImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
2448

2449
                imgDiff = np.ones(imgOriginal.shape, np.uint8) * 255
2450

2451
                area = app_doc_data.getArea('Drawing')
2452
                if area is not None:
2453
                    x = round(area.x)
2454
                    y = round(area.y)
2455
                    width = round(area.width)
2456
                    height = round(area.height)
2457
                    imgNotOper = cv2.bitwise_not(imgRecognized[y:y + height, x:x + width])
2458
                    imgDiff[y:y + height, x:x + width] = cv2.bitwise_xor(imgOriginal[y:y + height, x:x + width],
2459
                                                                         imgNotOper)
2460

2461
                # remove noise
2462
                imgDiff = cv2.dilate(imgDiff, np.ones((2, 2), np.uint8))
2463

2464
                project = app_doc_data.getCurrentProject()
2465
                cv2.imwrite(os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(orgImagePath)), imgDiff)
2466
            """
2467
        except Exception as ex:
2468
            from App import App
2469
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2470
                                                           sys.exc_info()[-1].tb_lineno)
2471
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2472

    
2473

    
2474
'''
2475
    @history    2018.05.25  Jeongwoo    Add pyqtSignal(svgItemClicked, itemRemoved)
2476
'''
2477

    
2478

    
2479
class QRecognitionDialog(QDialog):
2480
    svgItemClicked = pyqtSignal(SymbolSvgItem)
2481
    itemRemoved = pyqtSignal(QGraphicsItem)
2482
    unBlockEvent = pyqtSignal()
2483

    
2484
    '''
2485
        @history    2018.05.25  Jeongwoo    Add parameter and initialize / Connect recognizeButton signal and slot
2486
                    2018.05.29  Jeongwoo    Chnage parameter(graphicsView → parent) and Get graphicsView from parent
2487
    '''
2488

    
2489
    def __init__(self, parent, drawings):  # Parent is MainWindow
2490
        from AppDocData import AppDocData
2491

    
2492
        QDialog.__init__(self, parent)
2493

    
2494
        self.parent = parent
2495
        self._scene = QGraphicsScene()
2496
        self._text_scene = QGraphicsScene()
2497
        self._line_scene = QGraphicsScene()
2498
        self.drawings = drawings
2499
        self.xmlPath = None
2500
        self.ui = Recognition_UI.Ui_Recognition()
2501
        self.ui.setupUi(self)
2502
        self.isTreated = False
2503

    
2504
        self.ui.buttonBox.setEnabled(True)
2505
        self.ui.listWidget.model().rowsInserted.connect(self.rowInserted)
2506
        self.ui.recognizeButton.clicked.connect(self.recognizeButtonClicked)
2507
        self.ui.lineCheckBox.stateChanged.connect(self.checkBoxChanged)
2508
        self.ui.checkBoxSymbol.stateChanged.connect(self.checkBoxChanged)
2509
        self.ui.checkBoxText.stateChanged.connect(self.checkBoxChanged)
2510
        self.ui.checkBoxTraining.stateChanged.connect(self.checkBoxChanged)
2511
        self.isAccepted = False
2512

    
2513
        appDocData = AppDocData.instance()
2514
        configs = appDocData.getAppConfigs('app', 'mode')
2515
        if configs and 1 == len(configs) and 'advanced' == configs[0].value:
2516
            pass
2517
        else:
2518
            self.ui.checkBoxTraining.setVisible(False)
2519

    
2520
        if False:#len(self.drawings) == 1 and appDocData.activeDrawing and appDocData.activeDrawing == self.drawings[0]:
2521
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2522
            self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
2523
            self.ui.checkBoxText.setCheckState(Qt.Unchecked)
2524
        else:
2525
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2526
            self.ui.lineCheckBox.setCheckState(Qt.Checked)
2527
            self.ui.checkBoxText.setCheckState(Qt.Checked)
2528
            #self.ui.checkBoxSymbol.setEnabled(False)
2529
            #self.ui.lineCheckBox.setEnabled(False)
2530
            #self.ui.checkBoxText.setEnabled(False)
2531

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

    
2534
    def checkBoxChanged(self, checkState):
2535
        '''
2536
        @brief      line and text cannot be reocognized alone
2537
        @author     euisung
2538
        @date       2019.05.14
2539
        '''
2540
        if self.ui.checkBoxTraining.isChecked():
2541
            self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
2542
            self.ui.checkBoxText.setCheckState(Qt.Unchecked)
2543
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2544
            self.ui.lineCheckBox.setEnabled(False)
2545
            self.ui.checkBoxText.setEnabled(False)
2546
            self.ui.checkBoxSymbol.setEnabled(False)
2547
            self.ui.recognizeButton.setText('Extract')
2548
            return
2549
        else:
2550
            self.ui.lineCheckBox.setEnabled(True)
2551
            self.ui.checkBoxText.setEnabled(True)
2552
            self.ui.checkBoxSymbol.setEnabled(True)
2553
            self.ui.recognizeButton.setText('Recognize')
2554

    
2555
        if checkState is int(Qt.Checked):
2556
            if self.ui.lineCheckBox.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2557
                self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2558
            elif self.ui.checkBoxText.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2559
                self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
2560
        elif checkState is int(Qt.Unchecked):
2561
            if self.ui.lineCheckBox.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2562
                self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
2563
            elif self.ui.checkBoxText.isChecked() and not self.ui.checkBoxSymbol.isChecked():
2564
                self.ui.checkBoxText.setCheckState(Qt.Unchecked)
2565

    
2566
    '''
2567
        @brief      QListWidget Row Inserted Listener
2568
                    Whenever row inserted, scroll to bottom
2569
        @author     Jeongwoo
2570
        @date       18.04.12
2571
    '''
2572

    
2573
    def rowInserted(self, item):
2574
        self.ui.listWidget.scrollToBottom()
2575

    
2576
    def accept(self):
2577
        self.isAccepted = True
2578
        QDialog.accept(self)
2579

    
2580
    def recognizeButtonClicked(self, event):
2581
        """
2582
        @brief      start recognization
2583
        @author     humkyung
2584
        @history    humkyung 2018.10.05 clear imgSrc before recognizing
2585
        """
2586
        if self.ui.checkBoxSymbol.isChecked():  # or self.ui.checkBoxText.isChecked() or self.ui.lineCheckBox.isChecked():
2587
            appDocData = AppDocData.instance()
2588
            appDocData.imgSrc = None
2589
            area = appDocData.getArea('Drawing')
2590
            if area is None:
2591
                QMessageBox.about(self, self.tr("Notice"), self.tr('Please select drawing area.'))
2592
                return
2593

    
2594
            self.ui.recognizeButton.setEnabled(False)
2595
            self.ui.buttonBox.setEnabled(False)
2596
            self.ui.progressBar.setValue(0)
2597
            self.ui.listWidget.addItem("Initializing...")
2598
            self.startThread()
2599

    
2600
            self.isTreated = True
2601

    
2602
    '''
2603
        @brief  add item to list widget
2604
        @author humkyung
2605
        @date   2018.06.19
2606
    '''
2607

    
2608
    def addListItem(self, item):
2609
        self.ui.listWidget.addItem(item)
2610

    
2611
    '''
2612
        @brief  update progressbar with given value
2613
        @author humkyung
2614
        @date   2018.06.08
2615
    '''
2616

    
2617
    def updateProgress(self, maxValue, image_path):
2618
        self.ui.progressBar.setMaximum(maxValue)
2619
        self.ui.progressBar.setValue(self.ui.progressBar.value() + 1)
2620

    
2621
        if image_path is not None and os.path.isfile(image_path):
2622
            self.ui.labelImage.setPixmap(QPixmap(image_path))
2623
        elif image_path is not None:
2624
            self.ui.labelImage.setText(image_path)
2625

    
2626
    def updateBatchProgress(self, maxValue, weight):
2627
        """update batch progressbar"""
2628
        self.ui.progressBarBatch.setMaximum(maxValue * 5)
2629
        value = self.ui.progressBarBatch.value() + weight
2630
        self.ui.progressBarBatch.setValue(value)
2631
        self.ui.progressBarBatch.setFormat('{}/{}'.format(str(int(value / 5)), str(maxValue)))
2632

    
2633
    '''
2634
        @brief      display title
2635
        @author     humkyung
2636
        @date       2018.08.20
2637
    '''
2638

    
2639
    def displayTitle(self, title):
2640
        self.ui.labelTitle.setText(title)
2641

    
2642
    def startThread(self):
2643
        """start thread"""
2644
        import timeit
2645
        from PyQt5 import QtWidgets
2646
        from App import App
2647

    
2648
        self.ui.buttonBox.setDisabled(True)
2649

    
2650
        self.mutex = QMutex()
2651
        self.cond = QWaitCondition()
2652

    
2653
        # 1 - create Worker and Thread inside the Form
2654
        self.obj = Worker(self.mutex, self.cond)  # no parent!
2655
        self.obj.drawings = self.drawings
2656
        self.obj.listWidget = self.ui.listWidget
2657
        self.obj.scene = self._scene
2658
        self.obj.text_scene = self._text_scene
2659
        self.obj.line_scene = self._line_scene
2660
        self.obj.scene._end = False  # for waiting each drawing finished
2661
        self.obj.isSymbolChecked = self.ui.checkBoxSymbol.isChecked()
2662
        self.obj.isTextChecked = self.ui.checkBoxText.isChecked()
2663
        self.obj.isLineChecked = self.ui.lineCheckBox.isChecked()
2664
        self.obj.isTrainingChecked = self.ui.checkBoxTraining.isChecked()
2665
        self.thread = QThread()  # no parent!
2666

    
2667
        # 2 - Move the Worker object to the Thread object
2668
        self.obj.moveToThread(self.thread)
2669

    
2670
        # 3 - Connect Worker Signals to the Thread slots
2671
        self.obj.finished.connect(self.thread.quit)
2672
        self.obj.displayMessage.connect(self.addListItem)
2673
        self.obj.updateProgress.connect(self.updateProgress)
2674
        self.obj.updateBatchProgress.connect(self.updateBatchProgress)
2675
        self.obj.displayLog.connect(App.mainWnd().addMessage)
2676
        self.obj.displayTitle.connect(self.displayTitle)
2677
        self.obj.add_detected_items_to_scene.connect(self.add_detected_items_to_scene)
2678
        self.obj.add_predata_to_scene.connect(self.add_predata_to_scene)
2679
        self.obj.clear_scene.connect(self.clear_scene)
2680

    
2681
        # 4 - Connect Thread started signal to Worker operational slot method
2682
        self.thread.started.connect(self.obj.procCounter)
2683

    
2684
        # 5 - Thread finished signal will close the app if you want!
2685
        self.thread.finished.connect(self.dlgExit)
2686

    
2687
        # 6 - Start the thread
2688
        self.thread.start()
2689

    
2690
        self.tmStart = timeit.default_timer()
2691

    
2692
    '''
2693
        @brief set buttonbox's enabled flag
2694
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
2695
                    2018.06.14  Jeongwoo    Change sentence order
2696
                    2018.11.26  euisung     add drawing part
2697
                    2018.11.26  euising     move save and unknown part into executerecognition
2698
    '''
2699

    
2700
    def dlgExit(self):
2701
        import timeit
2702

    
2703
        try:
2704
            self.ui.buttonBox.setEnabled(True)
2705

    
2706
            self.ui.progressBar.setValue(self.ui.progressBar.maximum())
2707
        except Exception as ex:
2708
            from App import App
2709
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2710
                                                           sys.exc_info()[-1].tb_lineno)
2711
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2712
        finally:
2713
            self.tmStop = timeit.default_timer()
2714
            seconds = self.tmStop - self.tmStart
2715
            self.ui.listWidget.addItem("\nRunning Time : {} min".format(str(round(seconds / 60, 1))) + "\n")
2716

    
2717
    '''
2718
        @history    2018.05.29  Jeongwoo    Call parent's method
2719
                    2018.05.30  Jeongwoo    Change method name
2720
                    2018.06.09  humkyung    set progressbar value to maximum
2721
                    2018.11.12  euisung     add title block properties
2722
                    2018.11.29  euisung     no more used
2723
    '''
2724

    
2725
    def drawDetectedItems(self, symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList, loop):
2726
        try:
2727
            self.ui.progressBar.setValue(self.ui.progressBar.maximum())
2728
            self.parent.drawDetectedItems(symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList)
2729
        finally:
2730
            loop.quit()
2731

    
2732
    '''
2733
        @brief      draw detected lines
2734
        @author     humkyung
2735
        @date       2018.08.23
2736
        @history    2018.11.27  euisung     no more used
2737
    '''
2738

    
2739
    def drawDetectedLines(self, lineList, loop):
2740
        try:
2741
            self.parent.drawDetectedLines(lineList, self.obj)
2742
        finally:
2743
            loop.quit()
2744

    
2745
    '''
2746
        @brief      draw detected lines
2747
        @author     euisung
2748
        @date       2018.11.27
2749
        @history    2018.11.27  euisung     no more used
2750
    '''
2751

    
2752
    def drawUnknownItems(self, path, loop):
2753
        try:
2754
            self.parent.drawUnknownItems(path)
2755
        finally:
2756
            loop.quit()
2757

    
2758
    def add_predata_to_scene(self, drawing, scene, symbol, text, line, unknown, package) -> None:
2759
        """ add predata to scene """
2760
        from LoadCommand import LoadCommand
2761

    
2762
        try:
2763
            cmd = LoadCommand()
2764
            cmd.execute((drawing, scene), symbol=symbol, text=text, line=line, unknown=unknown,
2765
                        package=package, update=False)
2766
        except Exception as ex:
2767
            from App import App
2768
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2769
                                                           sys.exc_info()[-1].tb_lineno)
2770
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2771
        finally:
2772
            self.cond.wakeAll()
2773

    
2774
    def clear_scene(self, scenes) -> None:
2775
        """ clear scenes """
2776

    
2777
        try:
2778
            for scene in scenes:
2779
                scene.clear()
2780

    
2781
        except Exception as ex:
2782
            from App import App
2783
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2784
                                                           sys.exc_info()[-1].tb_lineno)
2785
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2786
        finally:
2787
            self.cond.wakeAll()
2788

    
2789
    def add_detected_items_to_scene(self, scene) -> None:
2790
        """add detected items to scene"""
2791
        from SaveWorkCommand import SaveWorkCommand
2792
        from EngineeringVendorItem import QEngineeringVendorItem
2793

    
2794
        app_doc_data = AppDocData.instance()
2795

    
2796
        try:
2797
            for item in scene.items():
2798
                if issubclass(type(item), QEngineeringVendorItem):
2799
                    app_doc_data.allItems.append(item)
2800

    
2801
            # symbol
2802
            for symbol in app_doc_data.symbols:
2803
                if issubclass(type(symbol), SymbolSvgItem):
2804
                    symbol.addSvgItemToScene(scene)
2805
                else:
2806
                    scene.addItem(symbol)
2807

    
2808
            # text
2809
            for text in app_doc_data.texts:
2810
                text.addTextItemToScene(scene)
2811

    
2812
            #for lineNo in app_doc_data.tracerLineNos:
2813
            #    lineNo.addTextItemToScene(scene)
2814

    
2815
            # remove lines which is located inside symbol
2816
            for symbol in app_doc_data.symbols:
2817
                rect = symbol.sceneBoundingRect()
2818
                rect.adjust(-10, -10, 10, 10)
2819
                matches = [line for line in app_doc_data.lines if rect.contains(line.line().p1()) and
2820
                           rect.contains(line.line().p2())]# and not line.has_connection]
2821
                for line in matches:
2822
                    app_doc_data.allItems.remove(line)
2823
                    app_doc_data.lines.remove(line)
2824
            # up to here
2825

    
2826
            for line in app_doc_data.lines:
2827
                scene.addItem(line)
2828
                # line.transfer.onRemoved.connect(self.itemRemoved)
2829
                for conn in line.connectors:
2830
                    conn.transfer.onPosChanged.connect(line.onConnectorPosChaned)
2831

    
2832
            for unknown in app_doc_data.unknowns + app_doc_data.lineIndicators:
2833
                scene.addItem(unknown)
2834

    
2835
            # save scene
2836
            save = SaveWorkCommand(scene)
2837
            save.run()
2838
            #SaveWorkCommand.save_to_database()
2839
            #SaveWorkCommand.save_to_xml()
2840
        except Exception as ex:
2841
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2842
                                                           sys.exc_info()[-1].tb_lineno)
2843
            self.displayLog.emit(MessageType.Error, message)
2844
        finally:
2845
            self.cond.wakeAll()
2846
            scene._end = True