프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

hytos / DTI_PID / DTI_PID / RecognitionDialog.py @ f9068b2a

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

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

    
15
import concurrent.futures as futures
16

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

    
31
from MainWindow import MainWindow
32

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

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

    
41
src = []
42

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

    
47
WHITE_LIST_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"
48

    
49
MIN_TEXT_SIZE = 10
50

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

    
54
ACCEPT_OVERLAY_AREA = 20
55
# endregion
56

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

    
61

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

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

    
82
    def __init__(self):
83
        super(Worker, self).__init__()
84

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

    
92
    def procCounter(self):  # A slot takes no params
93
        try:
94
            if self.isSymbolChecked or self.isTrainingChecked:
95
                Worker.executeRecognition(self.createDetectedItems, self.path, self.listWidget, self.isLineChecked,
96
                                          self, self.batch, self.createUnknownItems)
97
        except Exception as ex:
98
            from App import App
99
            from AppDocData import MessageType
100

    
101
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
102
                                                           sys.exc_info()[-1].tb_lineno)
103
            App.mainWnd().addMessage.emit(MessageType.Error, message)
104
            self.displayLog.emit(MessageType.Error, message)
105
        except:
106
            (_type, value, traceback) = sys.exc_info()
107
            sys.excepthook(_type, value, traceback)
108
        finally:
109
            self.finished.emit()
110

    
111
    '''
112
        @brief  remove small objects from given image
113
        @author humkyung
114
        @date   2018.04.26
115
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
116
    '''
117

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

    
127
            # try to convert grayscale to binary
128
            image = cv2.threshold(image, 200, 255, cv2.THRESH_BINARY)[1]
129

    
130
            contours, _ = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
131
            selectedContours = []
132
            for contour in contours:
133
                area = cv2.contourArea(contour)
134
                if minArea < area < maxArea:
135
                    selectedContours.append(contour)
136

    
137
            # draw contour with white color
138
            cv2.drawContours(image, selectedContours, -1, (255, 255, 255), -1)
139
        except Exception as ex:
140
            from App import App
141

    
142
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
143
                                                           sys.exc_info()[-1].tb_lineno)
144
            App.mainWnd().addMessage.emit(MessageType.Error, message)
145

    
146
        return image
147

    
148
    '''
149
        @brief  arrange line's position
150
        @author humkyung
151
        @date   2018.07.04
152
    '''
153

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

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

    
179
                        rhs = [item for item in symbols if item not in visited and item.is_connected(symbol)]
180
                        if rhs:
181
                            symbolPool.extend(rhs)
182
                            visited.extend(rhs)
183
                            selected.extend(rhs)
184
                        else:
185
                            selected.append(symbol)
186

    
187
                    # find lines which are connected last symbol
188
                    for symbol in selected:
189
                        rhs = [item for item in lines if item not in visited and item.is_connected(symbol)]
190
                        if rhs:
191
                            pool.extend(rhs)
192
                            visited.extend(rhs)
193
                            for item in rhs:
194
                                item.arrangeVertexOrder(line)
195
                # up to here
196
            # up to here
197
        except Exception as ex:
198
            from App import App
199

    
200
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
201
                                                           sys.exc_info()[-1].tb_lineno)
202
            App.mainWnd().addMessage.emit(MessageType.Error, message)
203

    
204
    @staticmethod
205
    def executeRecognition(createDetectedItems, path, listWidget, isLineChecked, worker, batch, createUnknownItems):
206
        """
207
        @brief      recognize symbol, text, line from image p&id
208
        @author     Jeongwoo
209
        @date
210
        @history    humkyung 2018.04.06 change error display from message box to print
211
                    Jeongwoo 2018.04.25 Remove 'Current Symbol : ' QListItem
212
                    Jeongwoo 2018.05.09 Make Comments OCR.removeTextFromNpArray block
213
                    Jeongwoo 2018.05.25 Remove imgLineList variable and parameter on writeXml()
214
                    humkyung 2018.05.26 add parameters(graphicsView, isSymbolTextChecked, isLineChecked)
215
                    Jeongwoo 2018.05.28 Add/Remove Parameters(Add : signal / Remove : graphicsView, isLineChecked)
216
                    Jeongwoo 2018.05.30 Remove return value
217
                    humkyung 2018.06.08 add signal for progressbar to parameter
218
                    humkyung 2018.06.11 get difference between original and recognized image
219
                    Jeongwoo 2018.06.21 If noteTextInfoList is None, change from None to empty list
220
                    humkyung 2018.08.20 remove alreay existing symbol and text item before recognizing
221
                                        block until drawing reconized objects
222
                    euisung  2018.11.12 add title block properties
223
        """
224
        import re
225
        import timeit
226
        from TextDetector import TextDetector
227
        from Drawing import Drawing
228
        from datetime import datetime
229

    
230
        global ocrCompletedSrc
231
        global threadLock
232
        global searchedSymbolList
233
        global textInfoList
234
        global maxProgressValue
235

    
236
        try:
237
            app_doc_data = AppDocData.instance()
238
            drawings = app_doc_data.getDrawings()
239
            project = app_doc_data.getCurrentProject()
240
            textDetector = TextDetector()
241

    
242
            # remove already existing symbol and text
243
            if not batch:
244
                listWidget.addItem('Deleting existing items...')
245
                items = [item for item in worker.graphicsView.scene().items() if
246
                         type(item) is QEngineeringUnknownItem or type(item) is QEngineeringEndBreakItem or
247
                         type(item) is QEngineeringErrorItem or type(item) is QGraphicsBoundingBoxItem]
248

    
249
                if worker.isSymbolChecked:
250
                    items.extend(
251
                        [item for item in worker.graphicsView.scene().items() if issubclass(type(item), SymbolSvgItem)])
252
                if worker.isTextChecked:
253
                    items.extend([item for item in worker.graphicsView.scene().items() if
254
                                  issubclass(type(item), QEngineeringTextItem)])
255
                for item in items:
256
                    item.transfer.onRemoved.emit(item)
257
            # up to here
258

    
259
            srcList = path
260

    
261
            Worker.initTargetSymbolDataList()
262

    
263
            for mainRes in srcList:
264
                ocrCompletedSrc = []
265
                searchedSymbolList = []
266
                textInfoList = []
267

    
268
                if not os.path.isfile(mainRes):
269
                    item = QListWidgetItem('{} file is not found'.format(os.path.basename(mainRes)))
270
                    item.setBackground(Qt.red)
271
                    listWidget.addItem(item)
272
                    continue
273

    
274
                app_doc_data.clearItemList(True)
275

    
276
                app_doc_data.setImgFilePath(mainRes)
277
                app_doc_data.imgSrc = None
278
                matches = [drawing for drawing in drawings if app_doc_data.imgName == os.path.splitext(drawing.name)[0]]
279
                app_doc_data.activeDrawing = matches[0] if matches else Drawing(None, app_doc_data.imgName, None)
280
                app_doc_data.activeDrawing.set_pid_source(Image.open(mainRes))
281

    
282
                # remove not drawing area
283
                configs = app_doc_data.getConfigs('{} Equipment Desc Area'.format(app_doc_data.imgName))
284
                for config in configs:
285
                    found = re.findall('\\d+', config.value)
286
                    if len(found) == 4:
287
                        cv2.rectangle(app_doc_data.imgSrc, (int(found[0]), int(found[1])),
288
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
289

    
290
                configs = app_doc_data.getConfigs('{} Typical Area'.format(app_doc_data.imgName))
291
                for config in configs:
292
                    found = re.findall('\\d+', config.value)
293
                    if len(found) == 4:
294
                        cv2.rectangle(app_doc_data.imgSrc, (int(found[0]), int(found[1])),
295
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
296

    
297
                noteArea = app_doc_data.getArea('Note')
298
                if noteArea is not None:
299
                    noteArea.img = app_doc_data.imgSrc[round(noteArea.y):round(noteArea.y + noteArea.height),
300
                                   round(noteArea.x):round(noteArea.x + noteArea.width)].copy()
301
                    cv2.rectangle(app_doc_data.imgSrc, (round(noteArea.x), round(noteArea.y)),
302
                                  (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
303
                    #cv2.imwrite('c:\\temp\\note.png', noteArea.img)
304
                    #cv2.imwrite('c:\\temp\\removed_note.png', app_doc_data.imgSrc)
305
                # up to here
306

    
307
                area = app_doc_data.getArea('Drawing')
308
                if area is not None:
309
                    # copy image
310
                    area.img = app_doc_data.imgSrc[round(area.y):round(area.y + area.height),
311
                               round(area.x):round(area.x + area.width)]
312

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

    
315
                    # area.contours, area.hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
316
                    area.not_img = cv2.bitwise_not(area.img)
317
                    area.contours, area.hierachy = cv2.findContours(area.not_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
318

    
319
                maxProgressValue = 0
320
                start_time = timeit.default_timer()
321
                listWidget.addItem(f"Start recognition {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} : {mainRes}")
322
                threadLock.acquire()
323

    
324
                worker.updateBatchProgress.emit(len(srcList), 1)
325

    
326
                if worker.isSymbolChecked:
327
                    # calculate total count of symbol
328
                    for targetItem in targetSymbolList:
329
                        if type(targetItem) is list:
330
                            maxProgressValue += len(targetItem)
331
                        else:
332
                            maxProgressValue += 1
333
                    # up to here
334
                    maxProgressValue = maxProgressValue * 2
335
                threadLock.release()
336

    
337
                if worker.isSymbolChecked:
338
                    worker.displayTitle.emit(worker.tr('Detecting symbols...'))
339

    
340
                    # detect equipments
341
                    pool = futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER)
342
                    for symbol in targetSymbolList[0]:
343
                        pool.submit(Worker.detectEquipmentOnPid, mainRes, symbol, listWidget, worker)
344
                    pool.shutdown(wait=True)
345
                    # up to here
346

    
347
                    with futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER) as pool:
348
                        future_symbol = {pool.submit(Worker.detectSymbolsOnPid, mainRes, symbol, listWidget, worker):
349
                                           symbol for symbol in targetSymbolList[2]}
350
                        futures.wait(future_symbol)
351

    
352
                    if worker.isTrainingChecked:
353
                        import uuid
354
                        worker.displayTitle.emit(worker.tr('Generating Data...'))
355
                        listWidget.addItem('Generating Data...')
356

    
357
                        for item in searchedSymbolList:
358
                            path = os.path.join(project.getTempPath(), 'Tile', item.getBaseSymbol())
359
                            if not os.path.exists(path): os.makedirs(path)
360

    
361
                            _img = app_doc_data.imgSrc[round(item.getSp()[1]):round(item.getSp()[1] + item.getHeight()),
362
                                   round(item.getSp()[0]):round(item.getSp()[0] + item.getWidth())]
363
                            cv2.imwrite(os.path.join(path, str(uuid.uuid4()) + '.png'), _img)
364
                        worker.updateBatchProgress.emit(len(srcList), 4)
365
                        continue
366

    
367
                    #cv2.imwrite('c:\\temp\\before-imgSrc.png', area.img)
368
                    #pool = futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER)
369
                    searchedTextSymList = []
370
                    for sym in searchedSymbolList:
371
                        Worker.remove_detected_symbol_image(sym, app_doc_data.imgSrc)
372
                        #pool.submit(Worker.remove_detected_symbol_image, sym, app_doc_data.imgSrc)
373
                    #pool.shutdown(wait=True)
374
                    #cv2.imwrite('c:\\temp\\imgSrc.png', area.img)
375
                else:
376
                    '''
377
                    import math
378

379
                    symbolItems = [item for item in worker.graphicsView.scene().items() if issubclass(type(item), SymbolSvgItem)]
380
                    
381
                    for symbolItem in symbolItems:
382
                        symbolName = symbolItem.name
383
                        symbolType = symbolItem.type
384
                        searchedItemSp = [int(symbolItem.loc[0]), int(symbolItem.loc[1])]
385
                        sw = symbolItem.size[0] # check later
386
                        sh = symbolItem.size[1] # check later
387

388
                        symbolThreshold = 50 # not necessary
389
                        symbolMinMatchCount = 0 # not necessary
390
                        hitRate = 80 # not necessary
391
                        
392
                        allowed_error = 0.0001
393
                        #degree 0
394
                        if abs(symbolItem.angle - 0) <= allowed_error:
395
                            symbolRotatedAngle = 0
396
                        #degree 90
397
                        elif abs(symbolItem.angle - 1.57) <= allowed_error:
398
                            symbolRotatedAngle = 90
399
                        #degree 180
400
                        elif abs(symbolItem.angle - 3.14) <= allowed_error:
401
                            symbolRotatedAngle = 180
402
                        #degree 270
403
                        elif abs(symbolItem.angle - 4.71) <= allowed_error:
404
                            symbolRotatedAngle = 270
405
                        else:
406
                            symbolRotatedAngle = math.pi / 180 * symbolItem.angle
407

408
                        isDetectOnOrigin = 80 # not necessary
409
                        symbolRotateCount = 0 # not necessary
410
                        symbolOcrOption = 0 # not necessary
411
                        isContainChild = 0 # not necessary
412
                        originalPoint = [symbolItem.origin[0] - symbolItem.loc[0], symbolItem.origin[1] - symbolItem.loc[1]]
413
                        symbolConnectionPoint = []
414
                        baseSymbol = str(symbolItem.parentSymbol)
415
                        additionalSymbol = str(symbolItem.childSymbol)
416
                        isExceptDetect = 0 # not necessary
417
                        detectFlip = symbolItem.flip
418

419
                        searchedSymbolList.append(Symbol(symbolName, symbolType , 
420
                            searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, hitRate, symbolRotatedAngle , 
421
                            isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild , 
422
                            ','.join(str(x) for x in originalPoint), 
423
                            [], 
424
                            baseSymbol, additionalSymbol, isExceptDetect, detectFlip))
425
                        
426
                        worker.graphicsView.scene().removeItem(symbolItem)
427
                    
428
                    pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
429
                    '''
430
                    # symbolItems = [item for item in worker.graphicsView.scene().items() if issubclass(type(item), SymbolSvgItem)]
431
                    # appDocData.symbols.extend(symbolItems)
432
                    # appDocData.allItems.extend(symbolItems)
433

    
434
                    # for sym in searchedSymbolList:
435
                    #    pool.submit(Worker.remove_detected_symbol_image, sym, appDocData.imgSrc)
436
                    # pool.shutdown(wait = True)
437

    
438
                worker.updateBatchProgress.emit(len(srcList), 1)
439

    
440
                area = app_doc_data.getArea('Drawing')
441
                if area is not None:
442
                    # copy area image
443
                    area.img = app_doc_data.imgSrc[round(area.y):round(area.y + area.height),
444
                               round(area.x):round(area.x + area.width)]
445

    
446
                offset = (area.x, area.y) if area is not None else (0, 0)
447

    
448
                otherTextInfoList = None
449
                titleBlockTextInfoList = None
450
                textInfoList = []
451
                if worker.isTextChecked:
452
                    from TextInfo import TextInfo
453

    
454
                    textAreas, ocr_image = textDetector.detectTextAreas(area.img if area is not None else app_doc_data.imgSrc,
455
                                                             offset)
456

    
457
                    # get text area by using symbol setting
458
                    text_area_symbols = []
459
                    for symbol in [symbol for symbol in searchedSymbolList if symbol.text_area]:
460
                        for text_area in symbol.text_area:
461
                            so = [symbol.getWidth(), symbol.getHeight()] if symbol.getRotatedAngle() is 0 or symbol.getRotatedAngle() is 180\
462
                                            else [symbol.getHeight(), symbol.getWidth()]
463
                            text_area_origin = Worker.getCalculatedOriginalPoint(None, str(text_area.center()[0]) + ',' + str(text_area.center()[1]),\
464
                                                                      symbol.getRotatedAngle(), None, None, so[0], so[1], symbol.getDetectFlip())
465
                            x = text_area_origin[0] - round(text_area.width / 2) if symbol.getRotatedAngle() is 0 or symbol.getRotatedAngle() is 180\
466
                                else text_area_origin[0] - round(text_area.height / 2)
467
                            x = x + symbol.sp[0]
468
                            y = text_area_origin[1] - round(text_area.height / 2) if symbol.getRotatedAngle() is 0 or symbol.getRotatedAngle() is 180\
469
                                else text_area_origin[1] - round(text_area.width / 2)
470
                            y = y + symbol.sp[1]
471
                            w = text_area.width if symbol.getRotatedAngle() is 0 or symbol.getRotatedAngle() is 180 else text_area.height
472
                            h = text_area.height if symbol.getRotatedAngle() is 0 or symbol.getRotatedAngle() is 180 else text_area.width
473
                            text_area_symbols.append(TextInfo('', x, y, w, h, symbol.getRotatedAngle()))
474

    
475
                    for textArea_index in reversed(range(len(textAreas))):
476
                        for text_area_index in reversed(range(len(text_area_symbols))):
477
                            if text_area_symbols[text_area_index].contains(textAreas[textArea_index].center) or \
478
                                    textAreas[textArea_index].contains(text_area_symbols[text_area_index].center):
479
                                if text_area_symbols[text_area_index].area > textAreas[textArea_index].area:
480
                                    textAreas.pop(textArea_index)
481
                                else:
482
                                    text_area_symbols.pop(text_area_index)
483
                    textAreas.extend(text_area_symbols)
484
                    # up to here
485

    
486
                    if maxProgressValue < 2 * len(textAreas):
487
                        for _ in range(len(textAreas) - int(maxProgressValue * 0.5)):
488
                            worker.updateProgress.emit(2 * len(textAreas), None)
489
                        maxProgressValue = 2 * len(textAreas)
490
                    else:
491
                        maxProgressValue = int(maxProgressValue * 0.5) + len(textAreas)
492

    
493
                    worker.displayTitle.emit(worker.tr('Detecting texts...'))
494
                    textDetector.recognizeText(area.img, offset, textAreas, searchedSymbolList, worker,
495
                                               listWidget, maxProgressValue)
496
                    textInfoList = textDetector.textInfoList.copy() if textDetector.textInfoList is not None else None
497
                    otherTextInfoList = textDetector.otherTextInfoList.copy() if textDetector.otherTextInfoList is not None else None
498
                    titleBlockTextInfoList = textDetector.titleBlockTextInfoList.copy() if textDetector.titleBlockTextInfoList is not None else None
499

    
500
                    app_doc_data.activeDrawing.width, app_doc_data.activeDrawing.height = app_doc_data.imgSrc.shape[
501
                                                                                          ::-1]
502

    
503
                    app_doc_data.imgName = os.path.splitext(os.path.basename(mainRes))[0]
504
                else:
505
                    import math
506
                    from TextInfo import TextInfo
507

    
508
                    textItems = [item for item in worker.graphicsView.scene().items() if
509
                                 issubclass(type(item), QEngineeringTextItem)]
510
                    app_doc_data.texts.extend(textItems)
511
                    app_doc_data.allItems.extend(textItems)
512

    
513
                    lineNoTextItems = [item for item in textItems if type(item) is QEngineeringLineNoTextItem]
514
                    for lineNoTextItem in lineNoTextItems:
515
                        lineNoTextItem.set_property('Freeze', False)
516
                        # lineNoTextItem.freeze_item.update_freeze(lineNoTextItem.prop('Freeze'))
517
                        lineNoTextItem.explode()
518

    
519
                    for textItem in textItems:
520
                        textInfoList.append(
521
                            TextInfo(textItem.text(), textItem.loc[0], textItem.loc[1], textItem.size[0],
522
                                     textItem.size[1], \
523
                                     round(math.degrees(textItem.angle))))
524

    
525
                        textItem.owner = None
526
                        worker.graphicsView.scene().removeItem(textItem)
527

    
528
                worker.updateBatchProgress.emit(len(srcList), 2)
529

    
530
                # check symbol validate
531
                valid_sym = []
532
                for index in reversed(range(len(searchedTextSymList))):
533
                    sym_xmin = searchedTextSymList[index].getSp()[0]
534
                    sym_ymin = searchedTextSymList[index].getSp()[1]
535
                    sym_xmax = searchedTextSymList[index].getSp()[0] + searchedTextSymList[index].getWidth()
536
                    sym_ymax = searchedTextSymList[index].getSp()[1] + searchedTextSymList[index].getHeight()
537
                    valid_count = searchedTextSymList[index].getHasInstrumentLabel()
538
                    valid = 0
539
                    for text_info in textInfoList:
540
                        info_center = text_info.center
541
                        if info_center[0] > sym_xmin and info_center[0] < sym_xmax and info_center[1] > sym_ymin and \
542
                                info_center[1] < sym_ymax:
543
                            valid += 1
544
                            if valid >= valid_count:
545
                                valid_sym.append(searchedTextSymList[index])
546
                                break
547
                    if valid < valid_count:
548
                        searchedSymbolList.pop(searchedSymbolList.index(searchedTextSymList[index]))
549

    
550
                pool = futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER)
551

    
552
                for sym in valid_sym:
553
                    pool.submit(Worker.remove_detected_symbol_image, sym, app_doc_data.imgSrc)
554
                pool.shutdown(wait=True)
555
                # up to here
556

    
557
                # remove text from image
558
                Worker.drawFoundSymbolsOnCanvas(mainRes, textInfoList, listWidget)
559
                textDetector.remove_text_from_image(area.img, offset)
560
                if not worker.isTextChecked:
561
                    textInfoList.clear()
562
                # up to here
563

    
564
                removedSymbolImgPath = os.path.join(project.getTempPath(), os.path.basename(mainRes))
565
                cv2.imwrite(removedSymbolImgPath, app_doc_data.imgSrc)
566

    
567
                area = AppDocData.instance().getArea('Drawing')
568
                if area is not None:
569
                    area.img = app_doc_data.imgSrc[round(area.y + 1):round(area.y + area.height),
570
                               round(area.x + 1):round(area.x + area.width)]
571
                cv2.imwrite(os.path.join(project.getTempPath(), "RECT_" + os.path.basename(mainRes)),
572
                            app_doc_data.imgSrc)
573

    
574
                listWidget.addItem("Recognized symbol count : " + str(len(searchedSymbolList)))
575

    
576
                # get difference between original and recognized image
577
                foundFilePath = os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(mainRes))
578
                Worker.getDifference(mainRes, foundFilePath)
579
                # up to here
580

    
581
                listWidget.addItem(worker.tr('Creating detected infos...'))
582
                worker.displayTitle.emit(worker.tr('Creating detected infos...'))
583
                createDetectedItems(searchedSymbolList, textInfoList,
584
                                    otherTextInfoList if otherTextInfoList is not None else [],
585
                                    titleBlockTextInfoList if titleBlockTextInfoList is not None else [])
586

    
587
                if isLineChecked:
588
                    Worker.recognizeLine(mainRes, listWidget, worker.graphicsView, worker, batch)
589
                else:
590
                    lineItems = [item for item in worker.graphicsView.scene().items()
591
                                 if issubclass(type(item), QEngineeringLineItem)]
592
                    app_doc_data.lines.extend(lineItems)
593
                    app_doc_data.allItems.extend(lineItems)
594

    
595
                    for lineItem in lineItems:
596
                        lineItem.owner = None
597
                        for conn in lineItem.connectors:
598
                            conn.connectedItem = None
599
                        worker.graphicsView.scene().removeItem(lineItem)
600

    
601
                # connect symbol to symbol
602
                try:
603
                    configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
604
                    toler = int(configs[0].value) if configs else 20
605
                    for symbol in app_doc_data.symbols:
606
                        matches = [it for it in app_doc_data.symbols if
607
                                   it is not symbol and symbol.is_connectable(it, toler=toler)]
608
                        for match in matches:
609
                            symbol.connect_if_possible(match)
610
                except Exception as ex:
611
                    message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
612
                                                                   sys.exc_info()[-1].tb_lineno)
613
                    worker.displayLog.emit(MessageType.Error, message)
614
                # up to here
615

    
616
                listWidget.addItem('Connecting lines')
617
                area = app_doc_data.getArea('Drawing')
618
                detector = LineDetector(area.img)
619
                symbols = app_doc_data.symbols
620
                configs = app_doc_data.getConfigs('Line Detector', 'Length to connect line')
621
                toler = int(configs[0].value) if configs else 20
622
                if app_doc_data.lines:
623
                    # connect line to symbol
624
                    try:
625
                        for line in app_doc_data.lines:
626
                            matches = [symbol for symbol in symbols if symbol.is_connectable(line, toler=toler)]
627
                            for symbol in matches:
628
                                detector.connectLineToSymbol(line, symbol, toler=toler)
629
                    except Exception as ex:
630
                        message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[
631
                            -1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
632
                        worker.displayLog.emit(MessageType.Error, message)
633
                    # up to here
634

    
635
                    # connect line to line
636
                    try:
637
                        for line in app_doc_data.lines:
638
                            matches = [it for it in app_doc_data.lines if
639
                                       (it is not line) and (not line.isParallel(it))]
640

    
641
                            for match in matches:
642
                                detector.connectLineToLine(match, line, toler)
643

    
644
                        # change line type using symbol connection type(info)
645
                        for sym in symbols:
646
                            if sym.conn_type:
647
                                for index in range(len(sym.conn_type)):
648
                                    item = sym.connectors[index].connectedItem
649
                                    if item and type(item) is QEngineeringLineItem:
650
                                        Worker.changeConnectedLineType(item, sym.conn_type[index])
651
                    except Exception as ex:
652
                        message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[
653
                            -1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
654
                        worker.displayLog.emit(MessageType.Error, message)
655
                    # up to here
656

    
657
                    # remove line has not connected item
658
                    try:
659
                        count = 1
660
                        while count > 0:
661
                            count = 0
662
                            for line in reversed(app_doc_data.lines):
663
                                if not line.has_connection:
664
                                    app_doc_data.lines.remove(line)
665
                                    app_doc_data.allItems.remove(line)
666
                                    count += 1
667
                                    for item in app_doc_data.lines + symbols:
668
                                        for conn in item.connectors:
669
                                            if conn.connectedItem is line:
670
                                                conn.connectedItem = None
671

    
672
                    except Exception as ex:
673
                        message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[
674
                            -1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
675
                        worker.displayLog.emit(MessageType.Error, message)
676
                    # up to here
677

    
678
                createUnknownItems(mainRes)
679

    
680
                worker.updateBatchProgress.emit(len(srcList), 1)
681
        except Exception as ex:
682
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
683
                                                           sys.exc_info()[-1].tb_lineno)
684
            worker.displayLog.emit(MessageType.Error, message)
685
        finally:
686
            pass
687

    
688
    @staticmethod
689
    def changeConnectedLineType(line, lineType):
690
        line.lineType = lineType
691
        if type(line.connectors[0].connectedItem) is QEngineeringLineItem and \
692
                (line.connectors[0].connectedItem.connectors[0].connectedItem is line or
693
                 line.connectors[0].connectedItem.connectors[1].connectedItem is line) and \
694
                line.connectors[0].connectedItem.lineType is not lineType:
695
            Worker.changeConnectedLineType(line.connectors[0].connectedItem, lineType)
696
        if type(line.connectors[1].connectedItem) is QEngineeringLineItem and \
697
                (line.connectors[1].connectedItem.connectors[0].connectedItem is line or
698
                 line.connectors[1].connectedItem.connectors[1].connectedItem is line) and \
699
                line.connectors[1].connectedItem.lineType is not lineType:
700
            Worker.changeConnectedLineType(line.connectors[1].connectedItem, lineType)
701

    
702
    '''
703
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
704
                    2018.05.28  Jeongwoo    Add xmlPath Parameter and append LineInfo into xml
705
                    2018.05.29  Jeongwoo    Change method to add item
706
                    2018.05.30  Jeongwoo    Remove parameter (xmlPath)
707
                    humkyung 2018.06.11 add drawing path to parameter and write recognized lines to image
708
                    humkyung 2018.07.04 call arrangeLinePosition after creating line
709
    '''
710

    
711
    @staticmethod
712
    def recognizeLine(path, listWidget, graphicsView, worker, batch):
713
        from shapely.geometry import Point, LineString
714
        from SymbolSvgItem import SymbolSvgItem
715
        from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
716
        from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
717
        from EngineeringTextItem import QEngineeringTextItem
718
        from EngineeringLineItem import QEngineeringLineItem
719
        from LineDetector import LineDetector
720
        from HoughBundler import HoughBundler
721

    
722
        try:
723
            listWidget.addItem('Starting line recognition')
724
            worker.displayTitle.emit(worker.tr('Detecting lines...'))
725

    
726
            app_doc_data = AppDocData.instance()
727
            project = app_doc_data.getCurrentProject()
728
            # remove already existing line and flow arrow item
729
            if not batch:
730
                items = [item for item in worker.graphicsView.scene().items() if (type(item) is QEngineeringLineItem)]
731
                for item in items:
732
                    item.transfer.onRemoved.emit(item)
733
            # up to here
734

    
735
            # detect line
736
            connectedLines = []
737

    
738
            area = app_doc_data.getArea('Drawing')
739
            area.img = worker.remove_small_objects(area.img)
740
            detector = LineDetector(area.img)
741

    
742
            symbols = []
743
            for item in app_doc_data.symbols:
744
                if issubclass(type(item), SymbolSvgItem):
745
                    symbols.append(item)
746
                    res = detector.detectConnectedLine(item, round(area.x), round(area.y))
747
                    if res is not None:
748
                        connectedLines.extend(res)
749

    
750
            # line detection without symbol connection point info
751
            remainLines = detector.detect_line_without_symbol()
752
            windowSize = app_doc_data.getSlidingWindowSize()
753
            thickness = int(windowSize[1])
754

    
755
            configs = app_doc_data.getConfigs('Line', 'Diagonal')
756
            if configs and int(configs[0].value) is not 1:
757
                diagonal = HoughBundler()
758
                for index in reversed(range(len(remainLines))):
759
                    angle = diagonal.get_orientation([remainLines[index][0][0], remainLines[index][0][1], remainLines[index][1][0], remainLines[index][1][1]])
760
                    if not (angle < 3 or angle > 87):
761
                        remainLines.pop(index)
762

    
763
            for line in remainLines:
764
                line.append(thickness)
765
            connectedLines.extend(remainLines)
766

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

    
770
            symbol_areas = []
771
            for symbol in symbols:
772
                symbol_areas.append(QRect(symbol.loc[0] - area.x, symbol.loc[1] - area.y, symbol.size[0], symbol.size[1]))
773

    
774
            detector.mergeLines(connectedLines, toler=toler, symbol_areas=symbol_areas)
775

    
776
            for pts in connectedLines:
777
                line = QEngineeringLineItem(
778
                    vertices=[(area.x + param[0], area.y + param[1]) for param in pts[:-1]], thickness=pts[2])
779
                line.area = 'Drawing'
780

    
781
                app_doc_data.lines.append(line)
782
                app_doc_data.allItems.append(line)
783
        except Exception as ex:
784
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
785
                                                           sys.exc_info()[-1].tb_lineno)
786
            worker.displayLog.emit(MessageType.Error, message)
787
        finally:
788
            listWidget.addItem('Finishing line recognition')
789
            worker.finished.emit()
790

    
791
    '''
792
        @history    2018.04.24  Jeongwoo    Add isExceptDetect Field
793
                    2018.05.09  Jeongwoo    Add targetSymbolList clear
794
                    humkyung 2018.07.07 store symbols to list as like [equipments],[nozzles],[symbols]
795
    '''
796

    
797
    @staticmethod
798
    def initTargetSymbolDataList():
799
        global targetSymbolList
800

    
801
        targetSymbolList.clear()
802
        appDocData = AppDocData.instance()
803
        symbolList = appDocData.getTargetSymbolList()
804
        equipments = [item for item in symbolList if appDocData.getSymbolCategoryByType(item.getType()) == 'Equipment']
805
        nozzles = [item for item in symbolList if item.getType() == 'Nozzles']
806
        # [[equipments],[nozzles],[symbols]]
807
        targetSymbolList.append(equipments)
808
        targetSymbolList.append(nozzles)
809
        targetSymbolList.append([item for item in symbolList if item not in equipments and item not in nozzles])
810

    
811
        return targetSymbolList
812

    
813
    '''
814
        @brief  detect equipment
815
        @author humkyung
816
        @date   2018.07.07
817
    '''
818

    
819
    @staticmethod
820
    def detectEquipmentOnPid(mainRes, targetSymbol, listWidget, worker):
821
        try:
822
            equipments = Worker.detectSymbolOnPid(mainRes, targetSymbol, listWidget, worker)
823
            for equipment in equipments:
824
                # detect nozzles around equimpent
825
                for nozzle in targetSymbolList[1]:
826
                    Worker.detectNozzleOnPid(equipment, nozzle, listWidget, worker)
827
                # up to here
828
        except Exception as ex:
829
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
830
                                                           sys.exc_info()[-1].tb_lineno)
831
            worker.displayLog(MessageType.Error, message)
832

    
833
    '''
834
        @brief      detect symbol on PID
835
        @author     jwkim
836
        @date   
837
        @history    humkyung 2018.04.06 check if symbol file exists
838
                    Jeongwoo 2018.05.29 Change method to adjust detail symbol location with hit-rate. Not feature point count
839
                                        Change parameter on add symbol part (mpCount → hitRate)
840
                                        Remove unusing calculation (avg)
841
                    Jeongwoo 2018.06.27 Remove part to split P&ID image and for loop
842
                    humkyung 2018.07.07 return searched symbols
843
    '''
844

    
845
    @staticmethod
846
    def detectSymbolOnPid(mainRes, targetSymbol, listWidget, worker):
847
        import copy
848
        global ocrCompletedSrc
849
        global threadLock
850
        global maxProgressValue
851

    
852
        try:
853
            forTraining = worker.isTrainingChecked
854
            symbolName = targetSymbol.getName()
855
            symbolType = targetSymbol.getType()
856
            symbolPath = targetSymbol.getPath()
857
            symbolThreshold = targetSymbol.getThreshold()  # if not forTraining else targetSymbol.getThreshold() / 3 * 2
858
            symbolMinMatchCount = targetSymbol.getMinMatchCount()
859
            isDetectOnOrigin = targetSymbol.getIsDetectOnOrigin()
860
            symbolRotateCount = targetSymbol.getRotationCount()
861
            symbolOcrOption = targetSymbol.getOcrOption()
862
            isContainChild = targetSymbol.getIsContainChild()
863
            symbolOriginalPoint = targetSymbol.getOriginalPoint()
864
            symbolConnectionPoint = targetSymbol.getConnectionPoint()
865
            baseSymbol = targetSymbol.getBaseSymbol()
866
            additionalSymbol = targetSymbol.getAdditionalSymbol()
867
            isExceptDetect = targetSymbol.getIsExceptDetect()
868
            detectFlip = targetSymbol.getDetectFlip()
869
            hasInstrumentLabel = targetSymbol.getHasInstrumentLabel()
870
            text_area = targetSymbol.getText_area()
871

    
872
            # check if symbol file is target or not
873
            if isExceptDetect == 1:
874
                item = QListWidgetItem('{} file is not target'.format(symbolName))
875
                item.setBackground(QColor('green'))
876
                listWidget.addItem(item)
877
                return
878

    
879
            foundSymbolCount = 0
880

    
881
            # check if symbol file exists
882
            if not os.path.isfile(symbolPath):
883
                item = QListWidgetItem('{} file not found'.format(symbolName))
884
                item.setBackground(QColor('red'))
885
                listWidget.addItem(item)
886
                return
887
            # up to here
888

    
889
            sym = cv2.imread(symbolPath, 1)
890
            symGray = Worker.cvtGrayImage(sym)
891
            symGrayOri = copy.copy(symGray)
892
            ## TODO: 이진화 시켰을때 심볼이 검출되지 않음
893
            ## symGray = cv2.threshold(cvtGrayImage(sym), 127, 255, cv2.THRESH_BINARY)[1]
894
            ## cv2.imshow('symbol', symGray)
895
            ## cv2.waitKey(0)
896
            sow, soh = symGray.shape[::-1]  # symbol original w, h
897

    
898
            offsetDrawingArea = []
899
            app_doc_data = AppDocData.instance()
900
            area = app_doc_data.getArea('Drawing')
901
            if area is not None:
902
                roiItem = area.img.copy() if hasInstrumentLabel else area.img
903
                offsetDrawingArea.append(area.x)
904
                offsetDrawingArea.append(area.y)
905
            else:
906
                offsetDrawingArea.append(0)
907
                offsetDrawingArea.append(0)
908
                if isDetectOnOrigin == 1:
909
                    roiItem = app_doc_data.imgSrc
910
                else:
911
                    roiItem = ocrCompletedSrc
912
            srcWidth, srcHeight = roiItem.shape[::-1]
913

    
914
            roiItemSp = (0, 0)
915
            roiItemEp = (srcWidth, srcHeight)
916

    
917
            # when text is located inside of symbol
918
            if area is not None and hasInstrumentLabel:
919
                # remove objects smaller than symbol
920
                selected_contours = []
921
                contours, hierarchy = cv2.findContours(area.mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
922
                index = 0
923
                for contour in contours:
924
                    child, parent = hierarchy[0][index][2], hierarchy[0][index][3]
925
                    [x, y, w, h] = cv2.boundingRect(contour)
926
                    if w * h < sow * soh * 0.5:
927
                        contour_area = cv2.contourArea(contour, True)
928
                        if contour_area > 0:
929
                            selected_contours.append(contour)
930
                    index += 1
931
                # up to here
932

    
933
                # draw contour with white color
934
                roiItem = cv2.drawContours(roiItem, selected_contours, -1, (255, 255, 255), -1)
935
                # cv2.imwrite("c:\\temp\\roiItem.png", roiItem)
936

    
937
            # try to recognize symbol twice(first one is normal, second on is flipped)
938
            steps = [False, True] if detectFlip else [False]
939
            for flipped in steps:
940
                if flipped:
941
                    symGray = symGrayOri
942
                    symGray = cv2.flip(symGray, 1)
943

    
944
                symbolRotatedAngle = 0
945
                for rc in range(symbolRotateCount + 1):  # Rotation count를 사용자 기준으로 받아서 1을 더한 후 사용
946
                    sw, sh = symGray.shape[::-1]
947
                    roiw = (roiItemEp[0] - roiItemSp[0])
948
                    roih = (roiItemEp[1] - roiItemSp[1])
949

    
950
                    # Case : Bigger Symbol than Split ROI
951
                    if roiw < sw or roih < sh:
952
                        symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
953
                        symbolRotatedAngle = symbolRotatedAngle + 90
954

    
955
                        if baseSymbol is not None and additionalSymbol is not None:
956
                            additionalSymbol = Worker.getRotatedChildInfo(additionalSymbol)
957
                        continue
958

    
959
                    # get Rotated Original Point
960
                    originalPoint = Worker.getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint,
961
                                                                      symbolRotatedAngle, sw, sh, sow, soh, flipped)
962
                    connectionPoint = Worker.getCalculatedConnectionPoint(symbolConnectionPoint, symbolRotatedAngle, sw,
963
                                                                          sh, sow, soh, flipped)
964

    
965
                    # # For OPC
966
                    # drawing_area = app_doc_data.getArea('Drawing')
967
                    # if drawing_area is not None and (symbolType == "Piping OPC\'s" or symbolType == "Instrument OPC\'s"):
968
                    #     customMatch = worker.detectOPCOnPid(drawing_area, symGray)
969
                    #     if customMatch and len(customMatch) > 0:
970
                    #         for custom in customMatch:
971
                    #             hitRate = custom[0]
972
                    #             searchedItemSp = (custom[1][0] + round(offsetDrawingArea[0]), custom[1][1] + round(offsetDrawingArea[1]))
973
                    #
974
                    #             is_add = True
975
                    #             for searched in searchedSymbolList:
976
                    #                 if Worker.IsOverlap((searchedItemSp[0], searchedItemSp[1], sw, sh), (
977
                    #                         searched.getSp()[0], searched.getSp()[1], searched.getWidth(),
978
                    #                         searched.getHeight())):
979
                    #                     if searched.getHitRate() > hitRate:
980
                    #                         is_add = False
981
                    #                     else:
982
                    #                         searchedSymbolList.remove(searched)
983
                    #                         break
984
                    #
985
                    #             if is_add:
986
                    #                 threadLock.acquire()
987
                    #                 foundSymbolCount = foundSymbolCount + 1
988
                    #                 Worker.addSearchedSymbol(symbolName, symbolType,
989
                    #                                          searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount,
990
                    #                                          hitRate, symbolRotatedAngle,
991
                    #                                          isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
992
                    #                                          isContainChild,
993
                    #                                          originalPoint, connectionPoint, baseSymbol, additionalSymbol,
994
                    #                                          isExceptDetect, detectFlip=1 if flipped else 0,
995
                    #                                          hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
996
                    #                 threadLock.release()
997

    
998
                    # Template Matching
999
                    tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
1000
                    loc = np.where(tmRes >= symbolThreshold)
1001

    
1002
                    for pt in zip(*loc[::-1]):
1003
                        mpCount = 0  # Match Point Count
1004

    
1005
                        roi = roiItem[pt[1]:pt[1] + sh, pt[0]:pt[0] + sw]
1006

    
1007
                        if symbolMinMatchCount > 0:
1008
                            mpCount = Worker.getMatchPointCount(roi, symGray)
1009
                            if not (mpCount >= symbolMinMatchCount):
1010
                                continue
1011

    
1012
                        searchedItemSp = (roiItemSp[0] + pt[0] + round(offsetDrawingArea[0]),
1013
                                          roiItemSp[1] + pt[1] + round(offsetDrawingArea[1]))
1014
                        # print(searchedItemSp)
1015

    
1016
                        overlapArea = 0
1017
                        symbolIndex = -1
1018
                        for i in range(len(searchedSymbolList) - 1, -1, -1):
1019
                            area = Worker.contains(searchedSymbolList[i], searchedItemSp, sw, sh)
1020
                            if area > ACCEPT_OVERLAY_AREA:
1021
                                # if area > overlapArea:
1022
                                #    overlapArea = area
1023
                                #    symbolIndex = i
1024
                                overlapArea = area
1025
                                symbolIndex = i
1026
                                break
1027
                                """
1028
                                categories = [appDocData.isEquipmentType(symbolType), appDocData.isEquipmentType(searchedSymbolList[i].getType())]
1029
                                if categories[0] == categories[1]:
1030
                                    symbolIndex = i
1031
                                    break
1032
                                """
1033

    
1034
                        hitRate = tmRes[pt[1], pt[0]]
1035

    
1036
                        # 겹치는 영역이 기준값보다 작을 경우
1037
                        if overlapArea <= ACCEPT_OVERLAY_AREA:
1038
                            threadLock.acquire()
1039
                            foundSymbolCount = foundSymbolCount + 1
1040
                            Worker.addSearchedSymbol(symbolName, symbolType,
1041
                                                     searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount,
1042
                                                     hitRate, symbolRotatedAngle,
1043
                                                     isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1044
                                                     isContainChild,
1045
                                                     originalPoint, connectionPoint, baseSymbol, additionalSymbol,
1046
                                                     isExceptDetect,
1047
                                                     detectFlip=1 if flipped else 0,
1048
                                                     hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1049
                            threadLock.release()
1050
                        else:  # 겹치는 영역이 기준값보다 클 경우
1051
                            if symbolIndex != -1 and symbolIndex < len(searchedSymbolList):
1052
                                searchedSymbol = searchedSymbolList[symbolIndex]
1053
                                # 현재 심볼과 검출된 심볼이 같을 경우 Match Point가 더 높은 정보로 교체
1054
                                if symbolName == searchedSymbol.getName():
1055
                                    symbolHitRate = searchedSymbol.getHitRate()
1056
                                    if symbolHitRate - searchedSymbol.getThreshold() < hitRate - symbolThreshold:
1057
                                        threadLock.acquire()
1058
                                        # replace existing symbol with new symbol has high accuracy
1059
                                        searchedSymbolList[symbolIndex] = symbol.Symbol(symbolName, symbolType,
1060
                                                                                        searchedItemSp, sw, sh,
1061
                                                                                        symbolThreshold,
1062
                                                                                        symbolMinMatchCount, hitRate,
1063
                                                                                        symbolRotatedAngle,
1064
                                                                                        isDetectOnOrigin,
1065
                                                                                        symbolRotateCount,
1066
                                                                                        symbolOcrOption, isContainChild,
1067
                                                                                        ','.join(str(x) for x in
1068
                                                                                                 originalPoint),
1069
                                                                                        '/'.join('{},{},{},{}'.format(
1070
                                                                                            param[0], param[1],
1071
                                                                                            param[2], param[3]) for
1072
                                                                                                 param in
1073
                                                                                                 connectionPoint),
1074
                                                                                        baseSymbol, additionalSymbol,
1075
                                                                                        isExceptDetect,
1076
                                                                                        detectFlip=1 if flipped else 0,
1077
                                                                                        hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1078
                                        threadLock.release()
1079
                                # 현재 심볼과 검출된 심볼이 같지 않을 경우 (포함)
1080
                                elif app_doc_data.isEquipmentType(searchedSymbol.getType()):
1081
                                    if searchedSymbol.area > sw * sh * 10:  # searched equipment area is greather than 10 times of symbol's area
1082
                                        threadLock.acquire()
1083
                                        foundSymbolCount = foundSymbolCount + 1
1084
                                        Worker.addSearchedSymbol(symbolName, symbolType,
1085
                                                                 searchedItemSp, sw, sh, symbolThreshold, hitRate,
1086
                                                                 hitRate, symbolRotatedAngle,
1087
                                                                 isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1088
                                                                 isContainChild,
1089
                                                                 originalPoint, connectionPoint, baseSymbol,
1090
                                                                 additionalSymbol, isExceptDetect,
1091
                                                                 detectFlip=1 if flipped else 0,
1092
                                                                 hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1093
                                        threadLock.release()
1094
                                # 현재 심볼과 검출된 심볼이 같지 않을 경우 (교체)
1095
                                elif not forTraining:
1096
                                    searchedSymbol = searchedSymbolList[symbolIndex]
1097
                                    symbolHitRate = searchedSymbol.getHitRate()
1098
                                    if symbolHitRate - searchedSymbol.getThreshold() < hitRate - symbolThreshold:
1099
                                        threadLock.acquire()
1100
                                        searchedSymbolList[symbolIndex] = symbol.Symbol(symbolName, symbolType,
1101
                                                                                        searchedItemSp, sw, sh,
1102
                                                                                        symbolThreshold,
1103
                                                                                        symbolMinMatchCount, hitRate,
1104
                                                                                        symbolRotatedAngle,
1105
                                                                                        isDetectOnOrigin,
1106
                                                                                        symbolRotateCount,
1107
                                                                                        symbolOcrOption, isContainChild,
1108
                                                                                        ','.join(str(x) for x in
1109
                                                                                                 originalPoint),
1110
                                                                                        '/'.join('{},{},{},{}'.format(
1111
                                                                                            param[0], param[1],
1112
                                                                                            param[2], param[3]) for
1113
                                                                                                 param in
1114
                                                                                                 connectionPoint),
1115
                                                                                        baseSymbol, additionalSymbol,
1116
                                                                                        isExceptDetect,
1117
                                                                                        detectFlip=1 if flipped else 0,
1118
                                                                                        hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1119
                                        threadLock.release()
1120
                                # 학습용 데이터 생성을 위해 교체하지 않고 추가함
1121
                                elif forTraining:
1122
                                    threadLock.acquire()
1123
                                    foundSymbolCount = foundSymbolCount + 1
1124
                                    Worker.addSearchedSymbol(symbolName, symbolType,
1125
                                                             searchedItemSp, sw, sh, symbolThreshold,
1126
                                                             symbolMinMatchCount, hitRate, symbolRotatedAngle,
1127
                                                             isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1128
                                                             isContainChild,
1129
                                                             originalPoint, connectionPoint, baseSymbol,
1130
                                                             additionalSymbol, isExceptDetect,
1131
                                                             detectFlip=1 if flipped else 0,
1132
                                                             hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1133
                                    threadLock.release()
1134

    
1135
                    # Rotate Symbol
1136
                    symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
1137
                    symbolRotatedAngle = symbolRotatedAngle + 90
1138

    
1139
                    if additionalSymbol is not None:
1140
                        additionalSymbol = Worker.getRotatedChildInfo(additionalSymbol)
1141

    
1142
            threadLock.acquire()
1143
            listWidget.addItem('Found Symbol   : ' + os.path.splitext(os.path.basename(symbolPath))[0] + ' - (' + str(
1144
                foundSymbolCount) + ')')
1145
            threadLock.release()
1146

    
1147
            """
1148
            if area is not None and hasInstrumentLabel:
1149
                # restore objects smaller than symbol
1150
                roiItem = cv2.drawContours(roiItem, outside_contours, -1, (0, 0, 0), -1)
1151
                roiItem = cv2.drawContours(roiItem, hole_contours, -1, (255, 255, 255), -1)
1152
                # up to here
1153
                cv2.imwrite('c:\\Temp\\contour2.png', roiItem)
1154
            """
1155

    
1156
            worker.updateProgress.emit(maxProgressValue, symbolPath)
1157

    
1158
            return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
1159
        except Exception as ex:
1160
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1161
                                                           sys.exc_info()[-1].tb_lineno)
1162
            worker.displayLog.emit(MessageType.Error, message)
1163

    
1164
        return []
1165

    
1166
    @staticmethod
1167
    def IsOverlap(range1, range2):
1168
        if range1[0] <= range2[0] + range2[2] and range1[0] + range1[2] >= range2[0] and range1[1] <= range2[1] + \
1169
                range2[3] and range1[1] + range1[3] >= range2[1]:
1170
            range = (min(range1[0] + range1[2], range2[0] + range2[2]) - max(range1[0], range2[0])) * (
1171
                    min(range1[1] + range1[3], range2[1] + range2[3]) - max(range1[1], range2[1]))
1172
            if range >= range1[2] * range1[3] * 0.4 and range >= range2[2] * range2[3] * 0.4:
1173
                return True
1174
            else:
1175
                return False
1176
        else:
1177
            return False
1178

    
1179
    @staticmethod
1180
    def detectOPCOnPid(area, symGray):
1181
        results = []
1182

    
1183
        try:
1184
            symbol = cv2.copyMakeBorder(symGray, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=255)
1185
            not_symbol = cv2.bitwise_not(symbol)
1186
            symbol_contours, symbol_hierachy = cv2.findContours(not_symbol, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
1187
            if symbol_hierachy[0][0][0] != -1:
1188
                return results
1189

    
1190
            contour_count = len(symbol_contours)
1191
            for i in range(1, contour_count):
1192
                symbol_area = cv2.contourArea(symbol_contours[i])
1193

    
1194
                for contour in area.contours:
1195
                    area_area = cv2.contourArea(contour)
1196
                    if area_area * 1.2 >= symbol_area >= area_area * 0.8:
1197
                        I1 = cv2.matchShapes(symbol_contours[i], contour, 1, 0)
1198
                        I2 = cv2.matchShapes(symbol_contours[i], contour, 2, 0)
1199
                        I3 = cv2.matchShapes(symbol_contours[i], contour, 3, 0)
1200
                        if I1 < 1 and I2 < 1 and I3 < 0.1:
1201
                            # need shape check
1202

    
1203
                            rect = cv2.boundingRect(contour)
1204
                            results.append([1 - I3, [rect[0], rect[1]]])
1205
                break
1206
        except Exception as ex:
1207
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1208
                                                           sys.exc_info()[-1].tb_lineno)
1209
        return results
1210

    
1211
    '''
1212
        @brief      detect nozzlez
1213
        @author     humkyung
1214
        @date       2018.07.07
1215
        @history    humkyhung 2018.07.17 pass equpment as parameter instead of image
1216
    '''
1217
    @staticmethod
1218
    def detectNozzleOnPid(equipment, nozzle, listWidget, worker):
1219
        global src
1220
        global threadLock
1221
        global maxProgressValue
1222

    
1223
        try:
1224
            symbolName = nozzle.getName()
1225
            symbolType = nozzle.getType()
1226
            symbolPath = nozzle.getPath()
1227
            symbolThreshold = nozzle.getThreshold()
1228
            symbolMinMatchCount = nozzle.getMinMatchCount()
1229
            isDetectOnOrigin = nozzle.getIsDetectOnOrigin()
1230
            symbolRotateCount = nozzle.getRotationCount()
1231
            symbolOcrOption = nozzle.getOcrOption()
1232
            isContainChild = nozzle.getIsContainChild()
1233
            symbolOriginalPoint = nozzle.getOriginalPoint()
1234
            symbolConnectionPoint = nozzle.getConnectionPoint()
1235
            baseSymbol = nozzle.getBaseSymbol()
1236
            additionalSymbol = nozzle.getAdditionalSymbol()
1237
            isExceptDetect = nozzle.getIsExceptDetect()
1238
            text_area = text_area
1239

    
1240
            foundSymbolCount = 0
1241

    
1242
            # check if symbol file exists
1243
            if not os.path.isfile(symbolPath):
1244
                item = QListWidgetItem('{} file not found'.format(os.path.split(os.path.basename(symbolPath))[0]))
1245
                item.setBackground(QColor('red'))
1246
                listWidget.addItem(item)
1247
                return
1248
            # up to here
1249

    
1250
            symGray = Worker.cvtGrayImage(cv2.imread(symbolPath, 1))
1251
            sow, soh = symGray.shape[::-1]  # symbol original w, h
1252

    
1253
            # get image of equipment with offset of nozzle size
1254
            appDocData = AppDocData.instance()
1255
            pt = equipment.getSp()
1256
            nozzleSize = max(sow, soh)
1257
            sx = round(pt[0]) - nozzleSize
1258
            sy = round(pt[1]) - nozzleSize
1259
            ex = round(pt[0] + equipment.getWidth()) + nozzleSize
1260
            ey = round(pt[1] + equipment.getHeight()) + nozzleSize
1261
            offset = (sx, sy)
1262
            eqpSize = (pt[0], pt[1], equipment.getWidth(), equipment.getHeight())
1263
            img = appDocData.imgSrc[sy:ey, sx:ex]
1264
            srcWidth, srcHeight = img.shape[::-1]
1265
            # up to here
1266

    
1267
            roiItemSp = (0, 0)
1268
            roiItemEp = (srcWidth, srcHeight)
1269
            roiItem = img
1270

    
1271
            symbolAngle = 0
1272
            for rc in range(symbolRotateCount + 1):  # Rotation Count를 사용자 기준으로 받아서 1을 더한 후 사용
1273
                sw, sh = symGray.shape[::-1]
1274
                roiw = (roiItemEp[0] - roiItemSp[0])
1275
                roih = (roiItemEp[1] - roiItemSp[1])
1276

    
1277
                # get Rotated Original Point
1278
                originalPoint = Worker.getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolAngle,
1279
                                                                  sw, sh, sow, soh)
1280
                connectionPoint = Worker.getCalculatedConnectionPoint(symbolConnectionPoint, symbolAngle, sw, sh, sow,
1281
                                                                      soh)
1282
                dx = connectionPoint[0][0] - originalPoint[0]
1283
                dy = connectionPoint[0][1] - originalPoint[1]
1284

    
1285
                # Template Matching
1286
                tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
1287
                loc = np.where(tmRes >= symbolThreshold)
1288

    
1289
                for pt in zip(*loc[::-1]):
1290
                    mpCount = 0  # Match Point Count
1291
                    symbolIndex = -1
1292

    
1293
                    roi = roiItem[pt[1]:pt[1] + sh, pt[0]:pt[0] + sw]
1294

    
1295
                    if symbolMinMatchCount > 0:
1296
                        mpCount = Worker.getMatchPointCount(roi, symGray)
1297
                        if not (mpCount >= symbolMinMatchCount):
1298
                            continue
1299

    
1300
                    mid = (offset[0] + pt[0] + (originalPoint[0] + connectionPoint[0][0]) * 0.5,
1301
                           offset[1] + pt[1] + (originalPoint[1] + connectionPoint[0][1]) * 0.5)
1302
                    searchedItemSp = (roiItemSp[0] + pt[0] + offset[0], roiItemSp[1] + pt[1] + offset[1])
1303
                    # check searched nozzle location
1304
                    if abs(dx) > abs(dy):
1305
                        if dx > 0:
1306
                            if mid[0] < eqpSize[0] + eqpSize[2] * 0.5: continue
1307
                        else:
1308
                            if mid[0] > eqpSize[0] + eqpSize[2] * 0.5: continue
1309
                    else:
1310
                        if dy > 0:
1311
                            if mid[1] < eqpSize[1] + eqpSize[3] * 0.5: continue
1312
                        else:
1313
                            if mid[1] > eqpSize[1] + eqpSize[3] * 0.5: continue
1314
                    # up to here
1315

    
1316
                    overlapArea = 0
1317
                    nozzles = [symbol for symbol in searchedSymbolList if symbol.getType() == 'Nozzles']
1318
                    for i in range(len(nozzles)):
1319
                        _pt = nozzles[i].getSp()
1320
                        rect = QRectF(_pt[0], _pt[1], nozzles[i].getWidth(), nozzles[i].getHeight())
1321
                        _rect = QRectF(searchedItemSp[0], searchedItemSp[1], sw, sh)
1322
                        if rect.intersects(_rect):
1323
                            intersect = rect.intersected(_rect)
1324
                            overlapArea = intersect.width() * intersect.height()
1325
                            if overlapArea > ACCEPT_OVERLAY_AREA:
1326
                                symbolIndex = i
1327
                                break
1328

    
1329
                    hitRate = tmRes[pt[1], pt[0]]
1330

    
1331
                    ## 겹치는 영역이 기준값보다 작을 경우
1332
                    if overlapArea <= ACCEPT_OVERLAY_AREA:
1333
                        threadLock.acquire()
1334
                        foundSymbolCount = foundSymbolCount + 1
1335
                        searched = Worker.addSearchedSymbol(symbolName, symbolType
1336
                                                            , searchedItemSp, sw, sh, symbolThreshold,
1337
                                                            symbolMinMatchCount, hitRate, symbolAngle
1338
                                                            , isDetectOnOrigin, symbolRotateCount, symbolOcrOption,
1339
                                                            isContainChild
1340
                                                            , originalPoint, connectionPoint, baseSymbol,
1341
                                                            additionalSymbol, isExceptDetect, text_area)
1342
                        searched.owner = equipment
1343
                        threadLock.release()
1344
                    ## 겹치는 영역이 기준값보다 클 경우
1345
                    else:
1346
                        if symbolIndex != -1 and symbolIndex < len(nozzles):
1347
                            searchedSymbol = nozzles[symbolIndex]
1348
                            ## 현재 심볼과 검출된 심볼이 같을 경우 Match Point가 더 높은 정보로 교체
1349
                            if symbolName == searchedSymbol.getName():
1350
                                symbolHitRate = searchedSymbol.getHitRate()
1351
                                if symbolHitRate < hitRate:
1352
                                    threadLock.acquire()
1353
                                    nozzles[symbolIndex] = symbol.Symbol(symbolName, symbolType,
1354
                                                                         searchedItemSp, sw, sh, symbolThreshold,
1355
                                                                         symbolMinMatchCount, hitRate, symbolAngle,
1356
                                                                         isDetectOnOrigin, symbolRotateCount,
1357
                                                                         symbolOcrOption, isContainChild,
1358
                                                                         ','.join(str(x) for x in originalPoint),
1359
                                                                         '/'.join(
1360
                                                                             '{},{},{},{}'.format(param[0], param[1],
1361
                                                                                                  param[2], param[3])
1362
                                                                             for param in connectionPoint),
1363
                                                                         baseSymbol, additionalSymbol, isExceptDetect, text_area)
1364
                                    threadLock.release()
1365
                            ## 현재 심볼과 검출된 심볼이 같지 않을 경우 (포함)
1366
                            elif appDocData.isEquipmentType(searchedSymbol.getType()):
1367
                                threadLock.acquire()
1368
                                foundSymbolCount = foundSymbolCount + 1
1369
                                searched = Worker.addSearchedSymbol(symbolName, symbolType
1370
                                                                    , searchedItemSp, sw, sh, symbolThreshold, hitRate,
1371
                                                                    hitRate, symbolAngle
1372
                                                                    , isDetectOnOrigin, symbolRotateCount,
1373
                                                                    symbolOcrOption, isContainChild
1374
                                                                    , originalPoint, connectionPoint, baseSymbol,
1375
                                                                    additionalSymbol, isExceptDetect, text_area)
1376
                                searched.owner = equipment
1377
                                threadLock.release()
1378

    
1379
                ## Rotate Symbol
1380
                symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
1381
                symbolAngle = symbolAngle + 90
1382

    
1383
                if additionalSymbol is not None:
1384
                    additionalSymbol = Worker.getRotatedChildInfo(additionalSymbol)
1385

    
1386
            threadLock.acquire()
1387
            listWidget.addItem('Found Symbol   : ' + os.path.splitext(os.path.basename(symbolPath))[0] + ' - (' + str(
1388
                foundSymbolCount) + ')')
1389
            threadLock.release()
1390

    
1391
            worker.updateProgress.emit(maxProgressValue, symbolPath)
1392

    
1393
            return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
1394
        except Exception as ex:
1395
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1396
                                                           sys.exc_info()[-1].tb_lineno)
1397
            worker.displayLog(MessageType.Error, message)
1398

    
1399
        return []
1400

    
1401
    # Convert into Grayscale image
1402
    @staticmethod
1403
    def cvtGrayImage(img):
1404
        return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1405

    
1406
    '''
1407
        @history    2018.06.12  Jeongwoo    Type changed (int → float)
1408
                    humkyung 2018.07.07 change return type as like [x,y]
1409
    '''
1410

    
1411
    @staticmethod
1412
    def getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, rotateSymbolWidth,
1413
                                   rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight, flipped=False):
1414
        res = []
1415

    
1416
        if additionalSymbol is None and symbolOriginalPoint is None:
1417
            res.append(rotateSymbolWidth // 2)
1418
            res.append(rotateSymbolHeight // 2)
1419
        else:
1420
            if flipped:
1421
                opx = originalSymbolWidth - float(symbolOriginalPoint.split(',')[0])
1422
                opy = float(symbolOriginalPoint.split(',')[1])
1423
            else:
1424
                opx = float(symbolOriginalPoint.split(',')[0])
1425
                opy = float(symbolOriginalPoint.split(',')[1])
1426

    
1427
            rPt = Worker.getCoordOnRotatedImage(symbolRotatedAngle, ('AUTO', opx, opy, '0'), originalSymbolWidth,
1428
                                                originalSymbolHeight)
1429

    
1430
            res.append(rPt[1])
1431
            res.append(rPt[2])
1432

    
1433
        return res
1434

    
1435
    '''
1436
        @history    2018.06.12  Jeongwoo    Type changed (int → float)
1437
                    humkyung 2018.07.07 change return type as like [[x,y],...]
1438
                    humkyung 2019.01.04 get symbol index
1439
    '''
1440

    
1441
    @staticmethod
1442
    def getCalculatedConnectionPoint(symbolConnectionPointStr, symbolRotatedAngle, rotateSymbolWidth,
1443
                                     rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight, flipped=0):
1444
        res = []
1445

    
1446
        if symbolConnectionPointStr is not None and symbolConnectionPointStr != '':
1447
            splitConnectionPointStr = symbolConnectionPointStr.split("/")
1448
            for strConnPt in splitConnectionPointStr:
1449
                tokens = strConnPt.split(',')
1450

    
1451
                direction = 'AUTO'
1452
                symbol_idx = '0'
1453
                if flipped:
1454
                    converted = {'AUTO': 'AUTO', 'LEFT': 'RIGHT', 'RIGHT': 'LEFT', 'UP': 'UP', 'DOWN': 'DOWN'}
1455

    
1456
                    if len(tokens) == 2:
1457
                        cpx = originalSymbolWidth - float(tokens[0])
1458
                        cpy = float(tokens[1])
1459
                    elif len(tokens) == 3:
1460
                        direction = converted[tokens[0]]
1461
                        cpx = originalSymbolWidth - float(tokens[1])
1462
                        cpy = float(tokens[2])
1463
                    elif len(tokens) >= 4:
1464
                        direction = converted[tokens[0]]
1465
                        cpx = originalSymbolWidth - float(tokens[1])
1466
                        cpy = float(tokens[2])
1467
                        symbol_idx = tokens[3]
1468
                else:
1469
                    if len(tokens) == 2:
1470
                        cpx = float(tokens[0])
1471
                        cpy = float(tokens[1])
1472
                    elif len(tokens) == 3:
1473
                        direction = tokens[0]
1474
                        cpx = float(tokens[1])
1475
                        cpy = float(tokens[2])
1476
                    elif len(tokens) >= 4:
1477
                        direction = tokens[0]
1478
                        cpx = float(tokens[1])
1479
                        cpy = float(tokens[2])
1480
                        symbol_idx = tokens[3]
1481

    
1482
                res.append(Worker.getCoordOnRotatedImage(symbolRotatedAngle, (direction, cpx, cpy, symbol_idx),
1483
                                                         originalSymbolWidth, originalSymbolHeight))
1484

    
1485
        return res
1486

    
1487
    '''
1488
        @brief      rotate (x,y) by given angle
1489
        @author     Jeongwoo
1490
        @date       2018.??.??
1491
        @history    humkyung 2018.04.13 fixed code when angle is 90 or 270    
1492
                    Jeongwoo 2018.04.27 Change calculation method with QTransform
1493
                    humkyung 2018.09.01 calculate rotated direction
1494
    '''
1495

    
1496
    @staticmethod
1497
    def getCoordOnRotatedImage(angle, connPt, originImageWidth, originImageHeight):
1498
        rx = None
1499
        ry = None
1500

    
1501
        # calculate rotated direction
1502
        direction = connPt[0]
1503
        if direction == 'LEFT':
1504
            direction = 'DOWN' if angle == 90 else 'RIGHT' if angle == 180 else 'UP' if angle == 270 else direction
1505
        elif direction == 'RIGHT':
1506
            direction = 'UP' if angle == 90 else 'LEFT' if angle == 180 else 'DOWN' if angle == 270 else direction
1507
        elif direction == 'UP':
1508
            direction = 'LEFT' if angle == 90 else 'DOWN' if angle == 180 else 'RIGHT' if angle == 270 else direction
1509
        elif direction == 'DOWN':
1510
            direction = 'RIGHT' if angle == 90 else 'UP' if angle == 180 else 'LEFT' if angle == 270 else direction
1511
        # up to here
1512

    
1513
        transform = QTransform()
1514
        if angle == 90 or angle == 270:
1515
            transform.translate(originImageHeight * 0.5, originImageWidth * 0.5)
1516
        elif angle == 0 or angle == 180:
1517
            transform.translate(originImageWidth * 0.5, originImageHeight * 0.5)
1518
        transform.rotate(-abs(angle))
1519
        transform.translate(-originImageWidth * 0.5, -originImageHeight * 0.5)
1520
        point = QPoint(connPt[1], connPt[2])
1521
        point = transform.map(point)
1522
        rx = point.x()
1523
        ry = point.y()
1524

    
1525
        symbol_idx = connPt[3]
1526

    
1527
        return (direction, rx, ry, symbol_idx)
1528

    
1529
    '''
1530
        @brief      Add symbols
1531
        @author     jwkim
1532
        @date
1533
        @history    Change parameter (mpCount → hitRate)
1534
                    Yecheol 2018.07.04 Delete Symbol Id 
1535
    '''
1536

    
1537
    @staticmethod
1538
    def addSearchedSymbol(sName, sType
1539
                          , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle
1540
                          , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
1541
                          , originalPoint, connectionPoint, baseSymbol, additionalSymbol, isExceptDetect, detectFlip
1542
                          , hasInstrumentLabel, text_area):
1543
        global searchedSymbolList
1544

    
1545
        newSym = None
1546
        try:
1547
            newSym = symbol.Symbol(sName, sType
1548
                                   , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle,
1549
                                   isDetectOnOrigin, rotateCount, ocrOption, isContainChild,
1550
                                   ','.join(str(x) for x in originalPoint),
1551
                                   '/'.join('{},{},{},{}'.format(param[0], param[1], param[2], param[3]) for param in
1552
                                            connectionPoint),
1553
                                   baseSymbol, additionalSymbol, isExceptDetect, detectFlip=1 if detectFlip else 0,
1554
                                   hasInstrumentLabel=hasInstrumentLabel, text_area=text_area)
1555

    
1556
            searchedSymbolList.append(newSym)
1557
        except Exception as ex:
1558
            from App import App
1559

    
1560
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1561
                                                           sys.exc_info()[-1].tb_lineno)
1562
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1563

    
1564
        return newSym
1565

    
1566
    '''
1567
        @brief      Check object contains pt
1568
        @param      obj is item in searchedSymbolList
1569
    '''
1570

    
1571
    @staticmethod
1572
    def contains(obj, pt, tw, th):
1573
        sp = obj.getSp()
1574
        width = obj.getWidth()
1575
        height = obj.getHeight()
1576

    
1577
        if sp[0] > pt[0] + tw:
1578
            return 0
1579
        if sp[0] + width < pt[0]:
1580
            return 0
1581
        if sp[1] > pt[1] + th:
1582
            return 0
1583
        if sp[1] + height < pt[1]:
1584
            return 0
1585

    
1586
        # shared area
1587
        x = max(sp[0], pt[0])
1588
        y = max(sp[1], pt[1])
1589
        w = min(sp[0] + width, pt[0] + tw) - x
1590
        h = min(sp[1] + height, pt[1] + th) - y
1591

    
1592
        return float((w * h)) / float((tw * th)) * 100
1593

    
1594
    # Calculate count of keypoint match result
1595
    @staticmethod
1596
    def getMatchPointCount(src, cmp):
1597
        matchCount = 0
1598

    
1599
        try:
1600
            orb = cv2.ORB_create(1000, 2.0, 2, 1)
1601

    
1602
            kp1, des1 = orb.detectAndCompute(src, None)
1603
            kp2, des2 = orb.detectAndCompute(cmp, None)
1604

    
1605
            FLANN_INDEX_LSH = 6
1606
            # table_number      : The number of hash tables use
1607
            # key_size          : The length of the key in the hash tables
1608
            # multi_probe_level : Number of levels to use in multi-probe (0 for standard LSH)
1609
            #                     It controls how neighboring buckets are searched
1610
            #                     Recommended value is 2
1611
            # checks            : specifies the maximum leafs to visit when searching for neighbours.
1612
            # LSH : Locality-Sensitive Hashing
1613
            # ref : https://www.cs.ubc.ca/research/flann/uploads/FLANN/flann_manual-1.8.4.pdf
1614
            index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=20, key_size=10, multi_probe_level=4)
1615
            search_params = dict(checks=100)
1616

    
1617
            flann = cv2.FlannBasedMatcher(index_params, search_params)
1618

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

    
1622
            count = 0
1623
            # ratio test as per Lowe's paper
1624
            for i in range(len(matches)):
1625
                if len(matches[i]) == 2:
1626
                    m = matches[i][0]
1627
                    n = matches[i][1]
1628
                    if m.distance < 0.85 * n.distance:
1629
                        count = count + 1
1630

    
1631
            matchCount = count
1632
        except Exception as ex:
1633
            from App import App
1634

    
1635
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1636
                                                           sys.exc_info()[-1].tb_lineno)
1637
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1638

    
1639
        return matchCount
1640

    
1641
    '''
1642
        @brief      Remake rotated child symbol info
1643
    '''
1644

    
1645
    @staticmethod
1646
    def getRotatedChildInfo(additionalSymbol):
1647
        tempChildInfo = ""
1648
        if additionalSymbol:
1649
            childList = additionalSymbol.split("/")
1650
            for index in range(len(childList)):
1651
                child = childList[index]
1652
                direction = Worker.convertDirectionCodeToValue(child.split(",")[0])
1653
                childName = child.split(",")[1]
1654
                direction = (direction - 1) if direction > 0 else 3
1655
                if index != 0:
1656
                    tempChildInfo = tempChildInfo + "/"
1657
                tempChildInfo = tempChildInfo + Worker.convertValueToDirectionCode(direction) + "," + childName
1658
        return tempChildInfo
1659

    
1660
    '''
1661
        @brief   detect symbols on PID
1662
        @history humkyung 2018.06.08 add parameteres for signal
1663
    '''
1664

    
1665
    @staticmethod
1666
    def detectSymbolsOnPid(mainRes, targetSymbols, listWidget, updateProgressSignal):
1667
        if type(targetSymbols) is list:
1668
            for detailTarget in targetSymbols:
1669
                Worker.detectSymbolOnPid(mainRes, detailTarget, listWidget, updateProgressSignal)
1670
        else:
1671
            Worker.detectSymbolOnPid(mainRes, targetSymbols, listWidget, updateProgressSignal)
1672

    
1673
    @staticmethod
1674
    def convertDirectionCodeToValue(directionCode):
1675
        if directionCode == "UP":
1676
            return 0
1677
        elif directionCode == "RIGHT":
1678
            return 1
1679
        elif directionCode == "DOWN":
1680
            return 2
1681
        elif directionCode == "LEFT":
1682
            return 3
1683
        else:
1684
            return -1
1685

    
1686
    @staticmethod
1687
    def convertValueToDirectionCode(value):
1688
        if value == 0:
1689
            return "UP"
1690
        elif value == 1:
1691
            return "RIGHT"
1692
        elif value == 2:
1693
            return "DOWN"
1694
        elif value == 3:
1695
            return "LEFT"
1696
        else:
1697
            return "NONE"
1698

    
1699
    '''
1700
        @brief  draw found symbols and texts
1701
        @author Jeongwoo
1702
    '''
1703

    
1704
    @staticmethod
1705
    def drawFoundSymbolsOnCanvas(drawingPath, textInfos, listWidget):
1706
        global src
1707
        global ocrCompletedSrc
1708
        global canvas
1709

    
1710
        appDocData = AppDocData.instance()
1711
        canvas = np.zeros(appDocData.imgSrc.shape, np.uint8)
1712
        canvas[::] = 255
1713

    
1714
        try:
1715
            appDocData = AppDocData.instance()
1716
            project = appDocData.getCurrentProject()
1717

    
1718
            for symbol in searchedSymbolList:
1719
                Worker.drawFoundSymbols(symbol, listWidget)
1720

    
1721
            for text in textInfos:
1722
                left = text.getX()
1723
                top = text.getY()
1724
                right = text.getX() + text.getW()
1725
                bottom = text.getY() + text.getH()
1726

    
1727
                canvas[top:bottom, left:right] = appDocData.imgSrc[top:bottom, left:right]
1728

    
1729
            cv2.imwrite(os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(drawingPath)), canvas)
1730
        except Exception as ex:
1731
            from App import App
1732

    
1733
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1734
                                                           sys.exc_info()[-1].tb_lineno)
1735
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1736

    
1737
    '''
1738
        @history    2018.04.27  Jeongwoo    Remove Tesseract Log on listWidget
1739
                    2018.05.04  Jeongwoo    Change method to OCR with tesseract_ocr_module.py
1740
                    2018.05.09  Jeongwoo    Add global variable textInfoList, Remove text in symbol and Add tesseract result text
1741
                    2018.05.10  Jeongwoo    Remove not used if-statement
1742
                    2018.06.19  Jeongwoo    When detect text in symbol, use getTextAreaInfo() and Tesseract
1743
                    2018.06.21  Jeongwoo    Add if-statement for way to detect text by Type A
1744
    '''
1745

    
1746
    @staticmethod
1747
    def drawFoundSymbols(symbol, listWidget):
1748
        global src
1749
        global canvas
1750
        global WHITE_LIST_CHARS
1751
        global searchedSymbolList
1752
        global textInfoList
1753

    
1754
        # symbolId = symbol.getId()
1755
        symbolPath = symbol.getPath()
1756
        symbolSp = symbol.getSp()
1757
        symbolRotatedAngle = symbol.getRotatedAngle()
1758

    
1759
        symImg = cv2.cvtColor(cv2.imread(symbolPath, 1), cv2.COLOR_BGR2GRAY)
1760
        if symbol.getDetectFlip() is 1:
1761
            symImg = cv2.flip(symImg, 1)
1762
        for i in range(symbolRotatedAngle // 90):
1763
            symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
1764

    
1765
        w, h = symImg.shape[::-1]
1766
        canvas[symbolSp[1]:symbolSp[1] + h, symbolSp[0]:symbolSp[0] + w] = cv2.bitwise_and(
1767
            canvas[symbolSp[1]:symbolSp[1] + h, symbolSp[0]:symbolSp[0] + w], symImg)
1768

    
1769
    @staticmethod
1770
    def remove_detected_symbol_image(sym, imgSrc):
1771
        """remove detected symbol image from drawing image"""
1772
        global threadLock
1773

    
1774
        path = sym.getPath()
1775
        sp = sym.getSp()
1776
        sw = sym.getWidth()
1777
        sh = sym.getHeight()
1778
        angle = sym.getRotatedAngle()
1779
        # get symbol image
1780
        sym_img = cv2.imread(path)
1781
        sym_img = cv2.cvtColor(sym_img, cv2.COLOR_BGR2GRAY)
1782
        # symImg = cv2.threshold(Worker.cvtGrayImage(symImg), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
1783
        if sym.getDetectFlip() is 1:
1784
            sym_img = cv2.flip(sym_img, 1)
1785

    
1786
        for i in range(angle // 90):
1787
            sym_img = cv2.rotate(sym_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
1788
        # up to here
1789

    
1790
        threadLock.acquire()
1791
        temp = imgSrc[sp[1]:sp[1] + sh, sp[0]:sp[0] + sw]
1792
        sym_img = cv2.erode(sym_img, np.ones((5, 5), np.uint8))
1793
        mask = cv2.bitwise_or(temp, sym_img)
1794
        imgXOR = cv2.bitwise_xor(temp, mask)
1795
        imgSrc[sp[1]:sp[1] + sh, sp[0]:sp[0] + sw] = cv2.bitwise_not(imgXOR)
1796
        threadLock.release()
1797

    
1798
    '''
1799
        @brief  get difference between given original and recognized image
1800
        @author humkyung
1801
        @date   2018.06.11
1802
    '''
1803

    
1804
    @staticmethod
1805
    def getDifference(orgImagePath, recImagePath):
1806
        import re
1807

    
1808
        global ocrCompletedSrc
1809
        global textInfoList
1810

    
1811
        try:
1812
            appDocData = AppDocData.instance()
1813
            if os.path.isfile(orgImagePath) and os.path.isfile(recImagePath):
1814
                imgOriginal = \
1815
                    cv2.threshold(Worker.cvtGrayImage(cv2.imread(orgImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
1816

    
1817
                configs = appDocData.getConfigs('Filter', 'DilateSize')
1818
                if 1 == len(configs) and int(configs[0].value) is not 0:
1819
                    size = int(configs[0].value)
1820
                    kernel = np.ones((size, size), np.uint8)
1821
                    imgOriginal = cv2.erode(imgOriginal, kernel, iterations=1)
1822

    
1823
                # remove not drawing area
1824
                configs = appDocData.getConfigs('{} Equipment Desc Area'.format(appDocData.imgName))
1825
                for config in configs:
1826
                    found = re.findall('\\d+', config.value)
1827
                    if len(found) == 4:
1828
                        cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
1829
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
1830

    
1831
                configs = appDocData.getConfigs('{} Typical Area'.format(appDocData.imgName))
1832
                for config in configs:
1833
                    found = re.findall('\\d+', config.value)
1834
                    if len(found) == 4:
1835
                        cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])),
1836
                                      (int(found[0]) + int(found[2]), int(found[1]) + int(found[3])), 255, -1)
1837

    
1838
                noteArea = appDocData.getArea('Note')
1839
                if noteArea is not None:
1840
                    noteArea.img = appDocData.imgSrc[round(noteArea.y):round(noteArea.y + noteArea.height),
1841
                                   round(noteArea.x):round(noteArea.x + noteArea.width)].copy()
1842
                    cv2.rectangle(imgOriginal, (round(noteArea.x), round(noteArea.y)),
1843
                                  (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
1844
                # up to here
1845

    
1846
                imgRecognized = \
1847
                    cv2.threshold(Worker.cvtGrayImage(cv2.imread(recImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
1848

    
1849
                imgDiff = np.ones(imgOriginal.shape, np.uint8) * 255
1850

    
1851
                area = AppDocData.instance().getArea('Drawing')
1852
                if area is not None:
1853
                    x = round(area.x)
1854
                    y = round(area.y)
1855
                    width = round(area.width)
1856
                    height = round(area.height)
1857
                    imgNotOper = cv2.bitwise_not(imgRecognized[y:y + height, x:x + width])
1858
                    imgDiff[y:y + height, x:x + width] = cv2.bitwise_xor(imgOriginal[y:y + height, x:x + width],
1859
                                                                         imgNotOper)
1860

    
1861
                # remove noise
1862
                imgDiff = cv2.dilate(imgDiff, np.ones((2, 2), np.uint8))
1863

    
1864
                appDocData = AppDocData.instance()
1865
                project = appDocData.getCurrentProject()
1866
                cv2.imwrite(os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(orgImagePath)), imgDiff)
1867
        except Exception as ex:
1868
            from App import App
1869

    
1870
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
1871
                                                           sys.exc_info()[-1].tb_lineno)
1872
            App.mainWnd().addMessage.emit(MessageType.Error, message)
1873

    
1874

    
1875
'''
1876
    @history    2018.05.25  Jeongwoo    Add pyqtSignal(svgItemClicked, itemRemoved)
1877
'''
1878

    
1879

    
1880
class QRecognitionDialog(QDialog):
1881
    svgItemClicked = pyqtSignal(SymbolSvgItem)
1882
    itemRemoved = pyqtSignal(QGraphicsItem)
1883
    unBlockEvent = pyqtSignal()
1884

    
1885
    '''
1886
        @history    2018.05.25  Jeongwoo    Add parameter and initialize / Connect recognizeButton signal and slot
1887
                    2018.05.29  Jeongwoo    Chnage parameter(graphicsView → parent) and Get graphicsView from parent
1888
    '''
1889

    
1890
    def __init__(self, parent, path, batch):  # Parent is MainWindow
1891
        from AppDocData import AppDocData
1892

    
1893
        QDialog.__init__(self, parent)
1894

    
1895
        self.parent = parent
1896
        self.graphicsView = parent.graphicsView
1897
        self.path = path
1898
        self.xmlPath = None
1899
        self.ui = Recognition_UI.Ui_Recognition()
1900
        self.ui.setupUi(self)
1901

    
1902
        self.ui.buttonBox.setEnabled(True)
1903
        self.ui.listWidget.model().rowsInserted.connect(self.rowInserted)
1904
        self.ui.recognizeButton.clicked.connect(self.recognizeButtonClicked)
1905
        self.ui.lineCheckBox.stateChanged.connect(self.checkBoxChanged)
1906
        self.ui.checkBoxSymbol.stateChanged.connect(self.checkBoxChanged)
1907
        self.ui.checkBoxText.stateChanged.connect(self.checkBoxChanged)
1908
        self.ui.checkBoxTraining.stateChanged.connect(self.checkBoxChanged)
1909
        self.isAccepted = False
1910
        self.batch = batch
1911

    
1912
        appDocData = AppDocData.instance()
1913
        configs = appDocData.getAppConfigs('app', 'mode')
1914
        if configs and 1 == len(configs) and 'advanced' == configs[0].value:
1915
            pass
1916
        else:
1917
            self.ui.checkBoxTraining.setVisible(False)
1918

    
1919
        '''
1920
        if self.batch:
1921
            self.ui.lineCheckBox.setCheckState(Qt.Checked)
1922
            self.ui.lineCheckBox.setEnabled(False)
1923
            self.ui.checkBoxSymbol.setEnabled(False)
1924
            self.ui.checkBoxText.setEnabled(False)
1925
        else:
1926
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
1927
            self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
1928
            self.ui.checkBoxText.setCheckState(Qt.Unchecked)
1929
        '''
1930
        self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
1931
        self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
1932
        self.ui.checkBoxText.setCheckState(Qt.Unchecked)
1933

    
1934
        self.ui.progressBarBatch.setFormat('{}/{}'.format('0', str(len(self.path))))
1935

    
1936
    def checkBoxChanged(self, checkState):
1937
        '''
1938
        @brief      line and text cannot be reocognized alone
1939
        @author     euisung
1940
        @date       2019.05.14
1941
        '''
1942
        if self.ui.checkBoxTraining.isChecked():
1943
            self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
1944
            self.ui.checkBoxText.setCheckState(Qt.Unchecked)
1945
            self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
1946
            self.ui.lineCheckBox.setEnabled(False)
1947
            self.ui.checkBoxText.setEnabled(False)
1948
            self.ui.checkBoxSymbol.setEnabled(False)
1949
            self.ui.recognizeButton.setText('Extract')
1950
            return
1951
        else:
1952
            self.ui.lineCheckBox.setEnabled(True)
1953
            self.ui.checkBoxText.setEnabled(True)
1954
            self.ui.checkBoxSymbol.setEnabled(True)
1955
            self.ui.recognizeButton.setText('Recognize')
1956

    
1957
        if checkState is int(Qt.Checked):
1958
            if self.ui.lineCheckBox.isChecked() and not self.ui.checkBoxSymbol.isChecked():
1959
                self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
1960
            elif self.ui.checkBoxText.isChecked() and not self.ui.checkBoxSymbol.isChecked():
1961
                self.ui.checkBoxSymbol.setCheckState(Qt.Checked)
1962
        elif checkState is int(Qt.Unchecked):
1963
            if self.ui.lineCheckBox.isChecked() and not self.ui.checkBoxSymbol.isChecked():
1964
                self.ui.lineCheckBox.setCheckState(Qt.Unchecked)
1965
            elif self.ui.checkBoxText.isChecked() and not self.ui.checkBoxSymbol.isChecked():
1966
                self.ui.checkBoxText.setCheckState(Qt.Unchecked)
1967

    
1968
    '''
1969
        @brief      QListWidget Row Inserted Listener
1970
                    Whenever row inserted, scroll to bottom
1971
        @author     Jeongwoo
1972
        @date       18.04.12
1973
    '''
1974

    
1975
    def rowInserted(self, item):
1976
        self.ui.listWidget.scrollToBottom()
1977

    
1978
    def accept(self):
1979
        self.isAccepted = True
1980
        QDialog.accept(self)
1981

    
1982
    def recognizeButtonClicked(self, event):
1983
        """
1984
        @brief      start recognization
1985
        @author     humkyung
1986
        @history    humkyung 2018.10.05 clear imgSrc before recognizing
1987
        """
1988
        if self.ui.checkBoxSymbol.isChecked():  # or self.ui.checkBoxText.isChecked() or self.ui.lineCheckBox.isChecked():
1989
            appDocData = AppDocData.instance()
1990
            appDocData.imgSrc = None
1991
            area = appDocData.getArea('Drawing')
1992
            if area is None:
1993
                QMessageBox.about(self, self.tr("Notice"), self.tr('Please select drawing area.'))
1994
                return
1995

    
1996
            self.ui.recognizeButton.setEnabled(False)
1997
            self.ui.buttonBox.setEnabled(False)
1998
            self.ui.progressBar.setValue(0)
1999
            self.ui.listWidget.addItem("Initializing...")
2000
            self.startThread()
2001

    
2002
    '''
2003
        @brief  add item to list widget
2004
        @author humkyung
2005
        @date   2018.06.19
2006
    '''
2007

    
2008
    def addListItem(self, item):
2009
        self.ui.listWidget.addItem(item)
2010

    
2011
    '''
2012
        @brief  update progressbar with given value
2013
        @author humkyung
2014
        @date   2018.06.08
2015
    '''
2016

    
2017
    def updateProgress(self, maxValue, image_path):
2018
        self.ui.progressBar.setMaximum(maxValue)
2019
        self.ui.progressBar.setValue(self.ui.progressBar.value() + 1)
2020

    
2021
        if image_path is not None and os.path.isfile(image_path):
2022
            self.ui.labelImage.setPixmap(QPixmap(image_path))
2023
        elif image_path is not None:
2024
            self.ui.labelImage.setText(image_path)
2025

    
2026
    def updateBatchProgress(self, maxValue, weight):
2027
        '''
2028
            @brief  update batch progressbar
2029
            @author euisung
2030
            @date   2018.11.28
2031
        '''
2032
        self.ui.progressBarBatch.setMaximum(maxValue * 5)
2033
        value = self.ui.progressBarBatch.value() + weight
2034
        self.ui.progressBarBatch.setValue(value)
2035
        self.ui.progressBarBatch.setFormat('{}/{}'.format(str(int(value / 5)), str(maxValue)))
2036

    
2037
    '''
2038
        @brief      display title
2039
        @author     humkyung
2040
        @date       2018.08.20
2041
    '''
2042

    
2043
    def displayTitle(self, title):
2044
        self.ui.labelTitle.setText(title)
2045

    
2046
    def startThread(self):
2047
        """
2048
        @brief  start thread
2049
        @author humkyung
2050
        @date   2018.04.??
2051
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
2052
                    2018.05.28  Jeongwoo    Add connects (self.loadRecognitionResult, recognizeLine)
2053
                    2018.05.30  Jeongwoo    Change signal name (drawDetectedItems)
2054
                    humkyung 2018.06.08 connect signal to self.updateProgress
2055
        """
2056
        import timeit
2057
        from PyQt5 import QtWidgets
2058
        from App import App
2059

    
2060
        self.ui.buttonBox.setDisabled(True)
2061

    
2062
        # 1 - create Worker and Thread inside the Form
2063
        self.obj = Worker()  # no parent!
2064
        self.obj.path = self.path
2065
        self.obj.listWidget = self.ui.listWidget
2066
        self.obj.graphicsView = self.graphicsView
2067
        self.obj.isSymbolChecked = self.ui.checkBoxSymbol.isChecked()
2068
        self.obj.isTextChecked = self.ui.checkBoxText.isChecked()
2069
        self.obj.isLineChecked = self.ui.lineCheckBox.isChecked()
2070
        self.obj.isTrainingChecked = self.ui.checkBoxTraining.isChecked()
2071
        self.obj.batch = self.batch
2072
        self.obj.createDetectedItems = self.parent.createDetectedItems
2073
        self.obj.createUnknownItems = self.parent.createUnknownItems
2074
        self.thread = QThread()  # no parent!
2075

    
2076
        # 2 - Move the Worker object to the Thread object
2077
        self.obj.moveToThread(self.thread)
2078

    
2079
        # 3 - Connect Worker Signals to the Thread slots
2080
        self.obj.finished.connect(self.thread.quit)
2081
        # self.obj.drawDetectedItems.connect(self.drawDetectedItems)
2082
        # self.obj.drawDetectedLines.connect(self.drawDetectedLines)
2083
        # self.obj.drawUnknownItems.connect(self.drawUnknownItems)
2084
        self.obj.displayMessage.connect(self.addListItem)
2085
        self.obj.updateProgress.connect(self.updateProgress)
2086
        self.obj.updateBatchProgress.connect(self.updateBatchProgress)
2087
        self.obj.displayLog.connect(App.mainWnd().addMessage)
2088
        self.obj.displayTitle.connect(self.displayTitle)
2089

    
2090
        # 4 - Connect Thread started signal to Worker operational slot method
2091
        self.thread.started.connect(self.obj.procCounter)
2092

    
2093
        # 5 - Thread finished signal will close the app if you want!
2094
        self.thread.finished.connect(self.dlgExit)
2095

    
2096
        # 6 - Start the thread
2097
        self.thread.start()
2098

    
2099
        self.tmStart = timeit.default_timer()
2100

    
2101
    '''
2102
        @brief set buttonbox's enabled flag
2103
        @history    2018.05.25  Jeongwoo    Moved from MainWindow
2104
                    2018.06.14  Jeongwoo    Change sentence order
2105
                    2018.11.26  euisung     add drawing part
2106
                    2018.11.26  euising     move save and unknown part into executerecognition
2107
    '''
2108

    
2109
    def dlgExit(self):
2110
        import timeit
2111
        import XmlGenerator as xg
2112

    
2113
        try:
2114
            self.ui.buttonBox.setEnabled(True)
2115

    
2116
            self.ui.progressBar.setValue(self.ui.progressBar.maximum())
2117
            # if not self.batch:
2118
            #    self.parent.drawDetectedItemsToScene()
2119
        except Exception as ex:
2120
            from App import App
2121

    
2122
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
2123
                                                           sys.exc_info()[-1].tb_lineno)
2124
            App.mainWnd().addMessage.emit(MessageType.Error, message)
2125
        finally:
2126
            self.tmStop = timeit.default_timer()
2127
            seconds = self.tmStop - self.tmStart
2128
            self.ui.listWidget.addItem("\nRunning Time : {} min".format(str(round(seconds / 60, 1))) + "\n")
2129

    
2130
    '''
2131
        @history    2018.05.29  Jeongwoo    Call parent's method
2132
                    2018.05.30  Jeongwoo    Change method name
2133
                    2018.06.09  humkyung    set progressbar value to maximum
2134
                    2018.11.12  euisung     add title block properties
2135
                    2018.11.29  euisung     no more used
2136
    '''
2137

    
2138
    def drawDetectedItems(self, symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList, loop):
2139
        try:
2140
            self.ui.progressBar.setValue(self.ui.progressBar.maximum())
2141
            self.parent.drawDetectedItems(symbolList, textInfoList, otherTextInfoList, titleBlockTextInfoList)
2142
        finally:
2143
            loop.quit()
2144

    
2145
    '''
2146
        @brief      draw detected lines
2147
        @author     humkyung
2148
        @date       2018.08.23
2149
        @history    2018.11.27  euisung     no more used
2150
    '''
2151

    
2152
    def drawDetectedLines(self, lineList, loop):
2153
        try:
2154
            self.parent.drawDetectedLines(lineList, self.obj)
2155
        finally:
2156
            loop.quit()
2157

    
2158
    '''
2159
        @brief      draw detected lines
2160
        @author     euisung
2161
        @date       2018.11.27
2162
        @history    2018.11.27  euisung     no more used
2163
    '''
2164

    
2165
    def drawUnknownItems(self, path, loop):
2166
        try:
2167
            self.parent.drawUnknownItems(path)
2168
        finally:
2169
            loop.quit()
클립보드 이미지 추가 (최대 크기: 500 MB)