프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / DTI_PID.py @ cf1f887b

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

1
# coding: utf-8
2

    
3
#region import libs
4
import http.client
5
import urllib, base64, json
6
import cv2
7
import numpy as np
8
import SymbolBase
9
import symbol
10
import TextInfo as ti
11
import azure_ocr_module as OCR
12
from PIL import Image
13
from io import BytesIO
14
import gc
15
import os
16
import glob
17
import math, operator
18
import threading
19
import concurrent.futures as futures
20
import XmlGenerator as xg
21
import pytesseract
22
import tesseract_ocr_module as TOCR
23
import potrace
24
import sys
25
from PyQt5.QtCore import *
26
from PyQt5.QtGui import *
27
from PyQt5.QtWidgets import *
28
from PyQt5.QtSvg import *
29
import QtImageViewer
30

    
31
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Commands')
32
import CreateCommand
33
import CropCommand
34

    
35
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes')
36
import QGraphicsPolylineItem
37
from QEngineeringLineItem import QEngineeringLineItem
38
from SymbolSvgItem import SymbolSvgItem
39
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
40
from AppDocData import AppDocData
41
#endregion
42

    
43
## Tesseract path
44
pytesseract.pytesseract.tesseract_cmd = os.path.join(os.environ['TESSERACT_HOME'], 'tesseract.exe')
45
tesseract_cmd = os.path.join(os.environ['TESSERACT_HOME'], 'tesseract.exe')
46

    
47
#region Symbol Image path List for test
48
targetSymbolList = []
49
#endregion
50

    
51
#region Global variables
52
searchedSymbolList = []
53
src = []
54
#srcGray = []
55
ocrCompletedSrc = []
56
afterDenoising = []
57
canvas = []
58
textInfoList = []
59
noteTextInfoList = []
60

    
61
WHITE_LIST_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"
62

    
63
MIN_TEXT_SIZE = 10
64

    
65
THREAD_MAX_WORKER = os.cpu_count()
66
threadLock = threading.Lock()
67

    
68
ACCEPT_OVERLAY_AREA = 10
69
#endregion
70

    
71
'''
72
    @history    2018.06.28  Jeongwoo    Remove useless condition
73
'''
74
def checkTextInSymbol(pt):
75
    global searchedSymbolList
76

    
77
    result = False
78
    for sym in searchedSymbolList:
79
        #symId = sym.getId()
80
        symSp = sym.getSp()
81
        symWidth = sym.getWidth()
82
        symHeight = sym.getHeight()
83
        symOcrOption = sym.getOcrOption()
84

    
85
        categoryCode = symId // 100
86

    
87
        if symOcrOption != SymbolBase.OCR_OPTION_NOT_EXEC:
88
            if (pt[0] >= symSp[0] and pt[0] <= symSp[0] + symWidth) and (pt[1] >= symSp[1] and pt[1] <= symSp[1] + symHeight):
89
                result = True
90
                break
91

    
92
    return result
93

    
94
#Convert into Grayscale image
95
def cvtGrayImage(img):
96
    return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
97

    
98
'''
99
    @brief      rotate (x,y) by given angle
100
    @author     Jeongwoo
101
    @date       2018.??.??
102
    @history    humkyung 2018.04.13 fixed code when angle is 90 or 270    
103
                Jeongwoo 2018.04.27 Change calculation method with QTransform
104
'''
105
def getCoordOnRotatedImage(rAngle, x, y, originImageWidth, originImageHeight):
106
    rx = None
107
    ry = None
108
    transform = QTransform()
109
    if rAngle == 90 or rAngle == 270:
110
        transform.translate(originImageHeight*0.5, originImageWidth*0.5)
111
    elif rAngle == 0 or rAngle == 180:
112
        transform.translate(originImageWidth*0.5, originImageHeight*0.5)
113
    transform.rotate(-abs(rAngle))
114
    transform.translate(-originImageWidth*0.5, -originImageHeight*0.5)
115
    point = QPoint(x, y)
116
    point = transform.map(point)
117
    rx = point.x()
118
    ry = point.y()
119
    return (rx, ry)
120

    
121
def convertDirectionCodeToValue(directionCode):
122
    if directionCode == "UP":
123
        return 0
124
    elif directionCode == "RIGHT":
125
        return 1
126
    elif directionCode == "DOWN":
127
        return 2
128
    elif directionCode == "LEFT":
129
        return 3
130
    else:
131
        return -1
132

    
133
def convertValueToDirectionCode(value):
134
    if value == 0:
135
        return "UP"
136
    elif value == 1:
137
        return "RIGHT"
138
    elif value == 2:
139
        return "DOWN"
140
    elif value == 3:
141
        return "LEFT"
142
    else:
143
        return "NONE"
144

    
145
'''
146
    @brief      Remake rotated child symbol info
147
'''
148
def getRotatedChildInfo(additionalSymbol):
149
    tempChildInfo = ""
150
    if additionalSymbol:
151
        childList = additionalSymbol.split("/")
152
        for index in range(len(childList)):
153
            child = childList[index]
154
            direction = convertDirectionCodeToValue(child.split(",")[0])
155
            childName = child.split(",")[1]
156
            direction = (direction - 1) if direction > 0 else 3
157
            if index != 0:
158
                tempChildInfo = tempChildInfo + "/"
159
            tempChildInfo = tempChildInfo + convertValueToDirectionCode(direction) + "," + childName
160
    return tempChildInfo
161

    
162

    
163
#Check object contains pt
164
#obj is item in searchedSymbolList
165
def contains(obj, pt, tw, th):
166
    sp = obj.getSp()
167
    width = obj.getWidth()
168
    height = obj.getHeight()
169

    
170
    if sp[0] > pt[0]+tw:
171
        return 0
172
    if sp[0]+width < pt[0]: 
173
        return 0
174
    if sp[1] > pt[1]+th: 
175
        return 0
176
    if sp[1]+height < pt[1]: 
177
        return 0
178
    
179
    #shared area
180
    x = max(sp[0], pt[0]);
181
    y = max(sp[1], pt[1]);
182
    w = min(sp[0] + width, pt[0] + tw) - x;
183
    h = min(sp[1] + height, pt[1] + th) - y;
184

    
185
    return float((w * h)) / float((tw * th)) * 100
186

    
187
'''
188
    @history    2018.06.12  Jeongwoo    Type changed (int → float)
189
                humkyung 2018.07.07 change return type as like [x,y]
190
'''
191
def getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, rotateSymbolWidth, rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight):
192
    res = []
193

    
194
    if additionalSymbol is None and symbolOriginalPoint is None:
195
        res.append(rotateSymbolWidth//2)
196
        res.append(rotateSymbolHeight//2)
197
    else:
198
        opx = float(symbolOriginalPoint.split(',')[0])
199
        opy = float(symbolOriginalPoint.split(',')[1])
200
        rPt = getCoordOnRotatedImage(symbolRotatedAngle, opx, opy, originalSymbolWidth, originalSymbolHeight)
201
        res.append(float(rPt[0]))
202
        res.append(float(rPt[1]))
203

    
204
    return res 
205

    
206
'''
207
    @history    2018.06.12  Jeongwoo    Type changed (int → float)
208
                humkyung 2018.07.07 change return type as like [[x,y],...]
209
'''
210
def getCalculatedConnectionPoint(symbolConnectionPointStr, symbolRotatedAngle, rotateSymbolWidth, rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight):
211
    res = []
212
    if symbolConnectionPointStr is not None:
213
        splitConnectionPointStr = symbolConnectionPointStr.split("/")
214
        for index in range(len(splitConnectionPointStr)):
215
            item = splitConnectionPointStr[index]
216
            cpx = float(item.split(',')[0])
217
            cpy = float(item.split(',')[1])
218
            rPt = getCoordOnRotatedImage(symbolRotatedAngle, cpx, cpy, originalSymbolWidth, originalSymbolHeight)
219
            res.append([float(rPt[0]), float(rPt[1])])
220

    
221
    return res
222
    
223
'''
224
    @brief      Add symbols
225
    @author     jwkim
226
    @date
227
    @history    Change parameter (mpCount → hitRate)
228
                Yecheol 2018.07.04 Delete Symbol Id 
229
'''
230
def addSearchedSymbol(sName, sType
231
                      , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle
232
                      , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
233
                      , originalPoint, connectionPoint, baseSymbol, additionalSymbol, isExceptDetect):
234
    global searchedSymbolList
235
    newSym = symbol.Symbol(sName, sType
236
                           , sp, w, h, threshold, minMatchCount, hitRate, rotatedAngle
237
                           , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
238
                           , ','.join(str(x) for x in originalPoint), '/'.join('{},{}'.format(x[0],x[1]) for x in connectionPoint), baseSymbol, additionalSymbol, isExceptDetect)
239

    
240
    searchedSymbolList.append(newSym)
241

    
242
    return newSym
243

    
244
#Calculate count of keypoint match result
245
def getMatchPointCount(src, cmp):
246
    orb = cv2.ORB_create(1000, 2.0, 2, 1)
247

    
248
    kp1, des1 = orb.detectAndCompute(src, None)
249
    kp2, des2 = orb.detectAndCompute(cmp, None)
250
    
251
    FLANN_INDEX_LSH = 6
252
    # table_number      : The number of hash tables use
253
    # key_size          : The length of the key in the hash tables
254
    # multi_probe_level : Number of levels to use in multi-probe (0 for standard LSH)
255
    #                     It controls how neighboring buckets are searched
256
    #                     Recommended value is 2
257
    # checks            : specifies the maximum leafs to visit when searching for neighbours.
258
    # LSH : Locality-Sensitive Hashing
259
    # ref : https://www.cs.ubc.ca/research/flann/uploads/FLANN/flann_manual-1.8.4.pdf
260
    index_params = dict(algorithm = FLANN_INDEX_LSH, table_number = 20, key_size = 10, multi_probe_level = 4)
261
    search_params = dict(checks = 100)
262
    
263
    flann = cv2.FlannBasedMatcher(index_params,search_params)
264
    
265
    matches = flann.knnMatch(des1, des2, k = 2)
266
    matchesMask = [[0, 0] for i in range(len(matches))] #Python 3.x
267
    
268
    count = 0
269
    # ratio test as per Lowe's paper
270
    for i in range(len(matches)):
271
        if len(matches[i]) == 2:
272
            m = matches[i][0]
273
            n = matches[i][1]
274
            if m.distance < 0.85 * n.distance:
275
                count = count + 1
276

    
277
    matchCount = count
278

    
279
    #print("match Count : " + str(matchCount))
280
    return matchCount
281

    
282
'''
283
    @brief   detect symbols on PID
284
    @history humkyung 2018.06.08 add parameteres for signal
285
'''
286
def detectSymbolsOnPid(mainRes, targetSymbols, listWidget, updateProgressSignal):
287
    for detailTarget in targetSymbols:
288
        detectSymbolOnPid(mainRes, detailTarget, listWidget, updateProgressSignal)
289

    
290
'''
291
    @brief      detect symbol on PID
292
    @author     jwkim
293
    @date   
294
    @history    humkyung 2018.04.06 check if symbol file exists
295
                Jeongwoo 2018.05.29 Change method to adjust detail symbol location with hit-rate. Not feature point count
296
                                    Change parameter on add symbol part (mpCount → hitRate)
297
                                    Remove unusing calculation (avg)
298
                Jeongwoo 2018.06.27 Remove part to split P&ID image and for loop
299
                humkyung 2018.07.07 return searched symbols
300
'''
301
def detectSymbolOnPid(mainRes, targetSymbol, listWidget, updateProgressSignal):
302
    global ocrCompletedSrc
303
    global afterDenoising
304
    global threadLock
305
    global searchedSymbolList
306
    global maxProgressValue
307
    
308
    try:
309
        symbolName = targetSymbol.getName()
310
        symbolType = targetSymbol.getType()
311
        symbolPath = targetSymbol.getPath()
312
        symbolThreshold = targetSymbol.getThreshold()
313
        symbolMinMatchCount = targetSymbol.getMinMatchCount()
314
        isDetectOnOrigin = targetSymbol.getIsDetectOnOrigin()
315
        symbolRotateCount = targetSymbol.getRotationCount()
316
        symbolOcrOption = targetSymbol.getOcrOption()
317
        isContainChild = targetSymbol.getIsContainChild()
318
        symbolOriginalPoint = targetSymbol.getOriginalPoint()
319
        symbolConnectionPoint = targetSymbol.getConnectionPoint()
320
        baseSymbol = targetSymbol.getBaseSymbol()
321
        additionalSymbol = targetSymbol.getAdditionalSymbol()
322
        isExceptDetect = targetSymbol.getIsExceptDetect()
323

    
324
        # check if symbol file is target or not
325
        if isExceptDetect == 1:
326
            item = QListWidgetItem('{} file is not target'.format(os.path.split(os.path.basename(symbolPath))[0]))
327
            item.setBackground(QColor('green'))
328
            listWidget.addItem(item)
329
            return
330

    
331
        foundSymbolCount = 0
332

    
333
        # check if symbol file exists
334
        if not os.path.isfile(symbolPath):
335
            item = QListWidgetItem('{} file not found'.format(os.path.split(os.path.basename(symbolPath))[0]))
336
            item.setBackground(QColor('red'))
337
            listWidget.addItem(item)
338
            return
339
        # up to here
340

    
341
        sym = cv2.imread(symbolPath, 1)
342
        symGray = cvtGrayImage(sym)
343
        ## TODO: 이진화 시켰을때 심볼이 검출되지 않음
344
        ## symGray = cv2.threshold(cvtGrayImage(sym), 127, 255, cv2.THRESH_BINARY)[1]
345
        ## cv2.imshow('symbol', symGray)
346
        ## cv2.waitKey(0)
347
        sow, soh = symGray.shape[::-1] # symbol original w, h
348

    
349
        offsetDrawingArea=[]
350
        appDocData = AppDocData.instance()
351
        area = appDocData.getArea('Drawing')
352
        if area is not None:
353
            copiedBasePid = area.img.copy()
354
            offsetDrawingArea.append(area.x)
355
            offsetDrawingArea.append(area.y)
356
        else:
357
            offsetDrawingArea.append(0)
358
            offsetDrawingArea.append(0)
359
            if isDetectOnOrigin == 1:
360
                copiedBasePid = appDocData.imgSrc.copy()
361
            else:
362
                copiedBasePid = ocrCompletedSrc.copy()
363
        srcWidth, srcHeight = copiedBasePid.shape[::-1]
364

    
365
        roiItemSp = (0,0)
366
        roiItemEp = (srcWidth, srcHeight)
367
        roiItem = copiedBasePid
368

    
369
        symbolRotatedAngle = 0
370
        for rc in range(symbolRotateCount + 1): ## Rotation Count를 사용자 기준으로 받아서 1을 더한 후 사용
371
            sw, sh = symGray.shape[::-1]
372
            roiw = (roiItemEp[0] - roiItemSp[0])
373
            roih = (roiItemEp[1] - roiItemSp[1])
374

    
375
            ## Case : Bigger Symbol than Split ROI
376
            if roiw < sw or roih < sh:
377
                symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
378
                symbolRotatedAngle = symbolRotatedAngle + 90
379

    
380
                if baseSymbol is not None and additionalSymbol is not None:
381
                    additionalSymbol = getRotatedChildInfo(additionalSymbol)
382
                continue
383
                    
384
            ## get Rotated Original Point
385
            originalPoint = getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, sw, sh, sow, soh)
386
            connectionPoint = getCalculatedConnectionPoint(symbolConnectionPoint, symbolRotatedAngle, sw, sh, sow, soh)
387

    
388
            ## Template Matching
389
            tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
390
            loc = np.where(tmRes >= symbolThreshold)
391

    
392
            for pt in zip(*loc[::-1]):
393
                overlapArea = 0
394
                mpCount = 0 # Match Point Count
395
                symbolIndex = -1
396

    
397
                roi = roiItem[pt[1]:pt[1]+sh, pt[0]:pt[0]+sw]
398

    
399
                if symbolMinMatchCount > 0:
400
                    mpCount = getMatchPointCount(roi, symGray)
401
                    if not (mpCount >= symbolMinMatchCount):
402
                        continue
403

    
404
                searchedItemSp = (roiItemSp[0]+pt[0] + round(offsetDrawingArea[0]), roiItemSp[1]+pt[1] + round(offsetDrawingArea[1]))
405

    
406
                for i in range(len(searchedSymbolList)):
407
                    '''
408
                    _pt = searchedSymbolList[i].getSp()
409
                    rect = QRectF(_pt[0], _pt[1], searchedSymbolList[i].getWidth(), searchedSymbolList[i].getHeight())
410
                    _rect = QRectF(searchedItemSp[0], searchedItemSp[1], sw, sh)
411
                    if rect.intersects(_rect):
412
                        intersect = rect.intersected(_rect)
413
                        overlapArea = intersect.width()*intersect.height()
414
                        if overlapArea > sw*sh*0.1:
415
                            symbolIndex = i
416
                            break
417
                    '''
418
                    overlapArea = contains(searchedSymbolList[i], searchedItemSp, sw, sh)
419
                    if overlapArea > ACCEPT_OVERLAY_AREA:
420
                        symbolIndex = i
421
                        break
422
                        
423
                hitRate = tmRes[pt[1], pt[0]]
424
                ## DEBUG
425
                #print('{}:{}-{}'.format(symbolName, searchedItemSp, hitRate))
426
                ## up to here
427

    
428
                ## 겹치는 영역이 기준값보다 작을 경우
429
                if overlapArea <= ACCEPT_OVERLAY_AREA:
430
                    threadLock.acquire()
431
                    foundSymbolCount = foundSymbolCount + 1
432
                    addSearchedSymbol(symbolName, symbolType
433
                        , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, hitRate, symbolRotatedAngle
434
                        , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
435
                        , originalPoint, connectionPoint, baseSymbol, additionalSymbol,isExceptDetect)
436
                    threadLock.release()
437
                ## 겹치는 영역이 기준값보다 클 경우
438
                else:
439
                    if symbolIndex != -1 and symbolIndex < len(searchedSymbolList):
440
                        searchedSymbol = searchedSymbolList[symbolIndex]
441
                        ## 현재 심볼과 검출된 심볼이 같을 경우 Match Point가 더 높은 정보로 교체
442
                        if symbolName == searchedSymbol.getName():
443
                            symbolHitRate = searchedSymbol.getHitRate()
444
                            if symbolHitRate < hitRate:
445
                                threadLock.acquire()
446
                                searchedSymbolList[symbolIndex] = symbol.Symbol(symbolName, symbolType
447
                                                                    , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, hitRate, symbolRotatedAngle
448
                                                                    , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
449
                                                                    , ','.join(str(x) for x in originalPoint), '/'.join('{},{}'.format(x[0],x[1]) for x in connectionPoint), baseSymbol, additionalSymbol,isExceptDetect)
450
                                ## DEBUG
451
                                #print('//// {}:{}-{} ////'.format(symbolName, searchedItemSp, hitRate))
452
                                ## up to here
453

    
454
                                threadLock.release()
455
                        ## 현재 심볼과 검출된 심볼이 같지 않을 경우 (포함)
456
                        elif appDocData.isEquipmentType(searchedSymbol.getType()):
457
                            ## DEBUG
458
                            print('{}->{}:{}-{}'.format(searchedSymbol.getName(), symbolName, searchedItemSp, hitRate))
459
                            ## up to here
460

    
461
                            threadLock.acquire()
462
                            foundSymbolCount = foundSymbolCount + 1
463
                            addSearchedSymbol(symbolName, symbolType
464
                                , searchedItemSp, sw, sh, symbolThreshold, hitRate, hitRate, symbolRotatedAngle
465
                                , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
466
                                , originalPoint, connectionPoint, baseSymbol, additionalSymbol,isExceptDetect)
467
                            threadLock.release()
468
                                    
469
            ## Rotate Symbol
470
            symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
471
            symbolRotatedAngle = symbolRotatedAngle + 90
472

    
473
            if additionalSymbol is not None:
474
                additionalSymbol = getRotatedChildInfo(additionalSymbol)
475

    
476
        threadLock.acquire()
477
        listWidget.addItem('Found Symbol   : ' + os.path.splitext(os.path.basename(symbolPath))[0] + ' - (' + str(foundSymbolCount) + ')')
478
        threadLock.release()
479

    
480
        updateProgressSignal.emit(maxProgressValue)
481

    
482
        return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
483
    except Exception as ex:
484
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
485
    
486
    return []
487

    
488
'''
489
    @brief      detect nozzle
490
    @author     humkyung
491
    @date       2018.07.07
492
    @history    humkyhung 2018.07.17 pass equpment as parameter instead of image
493
'''
494
def detectNozzleOnPid(equipment, nozzle, listWidget, updateProgressSignal):
495
    global src
496
    global threadLock
497
    global searchedSymbolList
498
    global maxProgressValue
499
    
500
    try:
501
        symbolName = nozzle.getName()
502
        symbolType = nozzle.getType()
503
        symbolPath = nozzle.getPath()
504
        symbolThreshold = nozzle.getThreshold()
505
        symbolMinMatchCount = nozzle.getMinMatchCount()
506
        isDetectOnOrigin = nozzle.getIsDetectOnOrigin()
507
        symbolRotateCount = nozzle.getRotationCount()
508
        symbolOcrOption = nozzle.getOcrOption()
509
        isContainChild = nozzle.getIsContainChild()
510
        symbolOriginalPoint = nozzle.getOriginalPoint()
511
        symbolConnectionPoint = nozzle.getConnectionPoint()
512
        baseSymbol = nozzle.getBaseSymbol()
513
        additionalSymbol = nozzle.getAdditionalSymbol()
514
        isExceptDetect = nozzle.getIsExceptDetect()
515

    
516
        foundSymbolCount = 0
517

    
518
        # check if symbol file exists
519
        if not os.path.isfile(symbolPath):
520
            item = QListWidgetItem('{} file not found'.format(os.path.split(os.path.basename(symbolPath))[0]))
521
            item.setBackground(QColor('red'))
522
            listWidget.addItem(item)
523
            return
524
        # up to here
525

    
526
        symGray = cvtGrayImage(cv2.imread(symbolPath, 1))
527
        sow, soh = symGray.shape[::-1] # symbol original w, h
528

    
529
        # get image of equipment with offset of nozzle size
530
        appDocData = AppDocData.instance()
531
        pt = equipment.getSp()
532
        nozzleSize = max(sow, soh)
533
        sx = round(pt[0]) - nozzleSize
534
        sy = round(pt[1]) - nozzleSize
535
        ex = round(pt[0] + equipment.getWidth()) + nozzleSize
536
        ey = round(pt[1] + equipment.getHeight()) + nozzleSize
537
        offset = (sx, sy)
538
        eqpSize = (pt[0], pt[1], equipment.getWidth(), equipment.getHeight())
539
        img = appDocData.imgSrc[sy:ey, sx:ex]
540
        srcWidth, srcHeight = img.shape[::-1]
541
        # up to here
542

    
543
        roiItemSp = (0,0)
544
        roiItemEp = (srcWidth, srcHeight)
545
        roiItem = img 
546

    
547
        symbolAngle = 0
548
        for rc in range(symbolRotateCount + 1): ## Rotation Count를 사용자 기준으로 받아서 1을 더한 후 사용
549
            sw, sh = symGray.shape[::-1]
550
            roiw = (roiItemEp[0] - roiItemSp[0])
551
            roih = (roiItemEp[1] - roiItemSp[1])
552

    
553
            ## get Rotated Original Point
554
            originalPoint = getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolAngle, sw, sh, sow, soh)
555
            connectionPoint = getCalculatedConnectionPoint(symbolConnectionPoint, symbolAngle, sw, sh, sow, soh)
556
            dx = connectionPoint[0][0] - originalPoint[0]
557
            dy = connectionPoint[0][1] - originalPoint[1]
558

    
559
            ## Template Matching
560
            tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
561
            loc = np.where(tmRes >= symbolThreshold)
562

    
563
            for pt in zip(*loc[::-1]):
564
                mpCount = 0 # Match Point Count
565
                symbolIndex = -1
566

    
567
                roi = roiItem[pt[1]:pt[1]+sh, pt[0]:pt[0]+sw]
568

    
569
                if symbolMinMatchCount > 0:
570
                    mpCount = getMatchPointCount(roi, symGray)
571
                    if not (mpCount >= symbolMinMatchCount):
572
                        continue
573
                
574
                mid = (offset[0] + pt[0] + (originalPoint[0] + connectionPoint[0][0])*0.5, offset[1] + pt[1] + (originalPoint[1] + connectionPoint[0][1])*0.5) 
575
                searchedItemSp = (roiItemSp[0]+pt[0]+offset[0], roiItemSp[1]+pt[1]+offset[1])
576
                print('---{}---{}---{}---'.format(eqpSize,searchedItemSp,mid))
577
                # check searched nozzle location
578
                if abs(dx) > abs(dy):
579
                    if dx > 0:
580
                        if mid[0] < eqpSize[0] + eqpSize[2]*0.5: continue
581
                    else:
582
                        if mid[0] > eqpSize[0] + eqpSize[2]*0.5: continue
583
                else:
584
                    if dy > 0:
585
                        if mid[1] < eqpSize[1] + eqpSize[3]*0.5: continue
586
                    else:
587
                        if mid[1] > eqpSize[1] + eqpSize[3]*0.5: continue
588
                # up to here
589

    
590
                overlapArea = 0
591
                nozzles = [symbol for symbol in searchedSymbolList if symbol.getType() == 'Nozzles']
592
                for i in range(len(nozzles)):
593
                    _pt = nozzles[i].getSp()
594
                    rect = QRectF(_pt[0], _pt[1], nozzles[i].getWidth(), nozzles[i].getHeight())
595
                    _rect = QRectF(searchedItemSp[0], searchedItemSp[1], sw, sh)
596
                    if rect.intersects(_rect):
597
                        intersect = rect.intersected(_rect)
598
                        overlapArea = intersect.width()*intersect.height()
599
                        if overlapArea > ACCEPT_OVERLAY_AREA:
600
                            symbolIndex = i
601
                            break
602
                        
603
                hitRate = tmRes[pt[1], pt[0]]
604

    
605
                ## 겹치는 영역이 기준값보다 작을 경우
606
                if overlapArea <= ACCEPT_OVERLAY_AREA:
607
                    threadLock.acquire()
608
                    foundSymbolCount = foundSymbolCount + 1
609
                    searched = addSearchedSymbol(symbolName, symbolType
610
                        , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, hitRate, symbolAngle
611
                        , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
612
                        , originalPoint, connectionPoint, baseSymbol, additionalSymbol,isExceptDetect)
613
                    searched.owner = equipment
614
                    threadLock.release()
615
                ## 겹치는 영역이 기준값보다 클 경우
616
                else:
617
                    if symbolIndex != -1 and symbolIndex < len(nozzles):
618
                        searchedSymbol = nozzles[symbolIndex]
619
                        ## 현재 심볼과 검출된 심볼이 같을 경우 Match Point가 더 높은 정보로 교체
620
                        if symbolName == searchedSymbol.getName():
621
                            symbolHitRate = searchedSymbol.getHitRate()
622
                            if symbolHitRate < hitRate:
623
                                threadLock.acquire()
624
                                nozzles[symbolIndex] = symbol.Symbol(symbolName, symbolType
625
                                                                    , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, hitRate, symbolAngle
626
                                                                    , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
627
                                                                    , ','.join(str(x) for x in originalPoint), '/'.join('{},{}'.format(x[0],x[1]) for x in connectionPoint), baseSymbol, additionalSymbol,isExceptDetect)
628
                                threadLock.release()
629
                        ## 현재 심볼과 검출된 심볼이 같지 않을 경우 (포함)
630
                        elif appDocData.isEquipmentType(searchedSymbol.getType()):
631
                            threadLock.acquire()
632
                            foundSymbolCount = foundSymbolCount + 1
633
                            searched = addSearchedSymbol(symbolName, symbolType
634
                                , searchedItemSp, sw, sh, symbolThreshold, hitRate, hitRate, symbolAngle
635
                                , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
636
                                , originalPoint, connectionPoint, baseSymbol, additionalSymbol,isExceptDetect)
637
                            searched.owner = equipment
638
                            threadLock.release()
639
                                    
640
            ## Rotate Symbol
641
            symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
642
            symbolAngle = symbolAngle + 90
643

    
644
            if additionalSymbol is not None:
645
                additionalSymbol = getRotatedChildInfo(additionalSymbol)
646

    
647
        threadLock.acquire()
648
        listWidget.addItem('Found Symbol   : ' + os.path.splitext(os.path.basename(symbolPath))[0] + ' - (' + str(foundSymbolCount) + ')')
649
        threadLock.release()
650

    
651
        updateProgressSignal.emit(maxProgressValue)
652

    
653
        return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
654
    except Exception as ex:
655
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
656

    
657
    return []
658

    
659
'''
660
    @brief  detect equipment
661
    @author humkyung
662
    @date   2018.07.07
663
'''
664
def detectEquipmentOnPid(mainRes, targetSymbol, listWidget, updateProgressSignal):
665
    try:
666
        equipments = detectSymbolOnPid(mainRes, targetSymbol, listWidget, updateProgressSignal)
667
        for equipment in equipments:
668
            # detect nozzles around equimpent
669
            for nozzle in targetSymbolList[1]:
670
                detectNozzleOnPid(equipment, nozzle, listWidget, updateProgressSignal)
671
            # up to here
672
    except Exception as ex:
673
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
674
            
675
'''
676
    @history    2018.05.17  Jeongwoo    Bitwise_not target changed (Original Image → Symbol Image)
677
                humkyung 2018.07.11 add parameter for image
678
'''
679
def removeDetectedSymbol(sym, imgSrc):
680
    global ocrCompletedSrc
681
    global threadLock
682
    
683
    path = sym.getPath()
684
    sp = sym.getSp()
685
    sw = sym.getWidth()
686
    sh = sym.getHeight()
687
    angle = sym.getRotatedAngle()
688
    symImg = cv2.imread(path)
689
    symImg = cv2.threshold(cvtGrayImage(symImg), 127, 255, cv2.THRESH_BINARY)[1]
690
    
691
    for i in range(angle//90):
692
        symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
693

    
694
    threadLock.acquire()
695
    temp = []
696
    temp = imgSrc[sp[1]:sp[1]+sh, sp[0]:sp[0]+sw]
697
    symImgBin = cv2.bitwise_not(symImg)
698
    result = cv2.bitwise_xor(symImgBin, temp)
699
    result = cv2.dilate(result, np.ones((5, 5), np.uint8))
700
    imgSrc[sp[1]:sp[1]+sh, sp[0]:sp[0]+sw] = result
701
    threadLock.release()
702

    
703
'''
704
def drawRectOnSrc(sym):
705
    global src
706
    global srcGray
707
    global ocrCompletedSrc
708
    
709
    path = sym.getPath()
710
    sp = sym.getSp()
711
    sw = sym.getWidth()
712
    sh = sym.getHeight()
713
    sAngle = sym.getRotatedAngle()
714
    symImg = cv2.imread(path)
715
    symImg = cvtGrayImage(symImg)
716

717
    cv2.rectangle(src, sp, (sp[0]+sw, sp[1]+sh), (0, 0, 255), 2)
718
'''
719

    
720
'''
721
    @history    2018.04.27  Jeongwoo    Remove Tesseract Log on listWidget
722
                2018.05.04  Jeongwoo    Change method to OCR with tesseract_ocr_module.py
723
                2018.05.09  Jeongwoo    Add global variable textInfoList, Remove text in symbol and Add tesseract result text
724
                2018.05.10  Jeongwoo    Remove not used if-statement
725
                2018.06.19  Jeongwoo    When detect text in symbol, use getTextAreaInfo() and Tesseract
726
                2018.06.21  Jeongwoo    Add if-statement for way to detect text by Type A
727
'''
728
def drawFoundSymbols(symbol, listWidget):
729
    global src
730
    global canvas
731
    global WHITE_LIST_CHARS
732
    global searchedSymbolList
733
    global textInfoList
734

    
735
    #symbolId = symbol.getId()
736
    symbolPath = symbol.getPath()
737
    symbolSp = symbol.getSp()
738
    symbolWidth = symbol.getWidth()
739
    symbolHeight = symbol.getHeight()
740
    symbolRotatedAngle = symbol.getRotatedAngle()
741
    symbolOcrOption = symbol.getOcrOption()
742

    
743
    symImg = cv2.cvtColor(cv2.imread(symbolPath, 1), cv2.COLOR_BGR2GRAY)
744
    for i in range(symbolRotatedAngle//90):
745
        symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
746

    
747
    w, h = symImg.shape[::-1]
748
    canvas[symbolSp[1]:symbolSp[1]+h, symbolSp[0]:symbolSp[0]+w] = cv2.bitwise_and(canvas[symbolSp[1]:symbolSp[1]+h, symbolSp[0]:symbolSp[0]+w], symImg)
749
    
750
'''
751
    @brief  draw found symbols and texts
752
    @author Jeongwoo
753
'''
754
def drawFoundSymbolsOnCanvas(drawingPath , textInfos , listWidget):
755
    global src
756
    global ocrCompletedSrc
757
    global searchedSymbolList
758
    global canvas
759

    
760
    appDocData = AppDocData.instance()
761
    canvas = np.zeros(appDocData.imgSrc.shape, np.uint8)
762
    canvas[::] = 255
763

    
764
    try:
765
        appDocData = AppDocData.instance()
766
        project = appDocData.getCurrentProject()
767

    
768
        for symbol in searchedSymbolList:
769
            drawFoundSymbols(symbol, listWidget)
770

    
771
        for text in textInfos:
772
            #if not checkTextInSymbol((text.getX(), text.getY())):
773
            left = text.getX()
774
            top = text.getY()
775
            right = text.getX() + text.getW()
776
            bottom = text.getY() + text.getH()
777

    
778
            canvas[top:bottom, left:right] = appDocData.imgSrc[top:bottom, left:right]
779

    
780
        cv2.imwrite(os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(drawingPath)), canvas)
781
    except Exception as ex:
782
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
783
        
784
'''
785
    @history    2018.04.24  Jeongwoo    Add isExceptDetect Field
786
                2018.05.09  Jeongwoo    Add targetSymbolList clear
787
                humkyung 2018.07.07 store symbols to list as like [equipments],[nozzles],[symbols]
788
'''
789
def initTargetSymbolDataList():
790
    global targetSymbolList
791

    
792
    targetSymbolList.clear()
793
    appDocData = AppDocData.instance()
794
    symbolList = appDocData.getTargetSymbolList()
795
    equipments = [item for item in symbolList if appDocData.getSymbolCategoryByType(item.getType()) == 'Equipment']
796
    nozzles = [item for item in symbolList if item.getType() == 'Nozzles']
797
    # [[equipments],[nozzles],[symbols]]
798
    targetSymbolList.append(equipments)
799
    targetSymbolList.append(nozzles)
800
    targetSymbolList.append([item for item in symbolList if item not in equipments and item not in nozzles])
801

    
802
    return targetSymbolList
803

    
804
'''
805
    @brief  remove small objects from given image
806
    @author humkyung
807
    @date   2018.04.26
808
    @history    2018.05.25  Jeongwoo    Moved from MainWindow
809
'''
810
def removeSmallObjects(image):
811
    try:
812
        appDocData = AppDocData.instance()
813
        configs = appDocData.getConfigs('Small Object Size', 'Min Area')
814
        minArea = int(configs[0].value) if 1 == len(configs) else 20
815
        configs = appDocData.getConfigs('Small Object Size', 'Max Area')
816
        maxArea = int(configs[0].value) if 1 == len(configs) else 50
817

    
818
        _,contours,_ = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
819
        selectedContours=[]
820
        for contour in contours:
821
            area = cv2.contourArea(contour)
822
            if area > minArea and area < maxArea: selectedContours.append(contour)
823
        contourImage = cv2.drawContours(image, selectedContours, -1, (255,255,255), -1); # draw contour with white color
824
    except Exception as ex:
825
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
826

    
827
    return contourImage
828

    
829
'''
830
    @history    2018.05.25  Jeongwoo    Moved from MainWindow
831
                2018.05.28  Jeongwoo    Add xmlPath Parameter and append LineInfo into xml
832
                2018.05.29  Jeongwoo    Change method to add item
833
                2018.05.30  Jeongwoo    Remove parameter (xmlPath)
834
                humkyung 2018.06.11 add drawing path to parameter and write recognized lines to image
835
                humkyung 2018.07.04 call arrangeLinePosition after creating line
836
'''
837
def recognizeLine(path, listWidget, graphicsView):
838
    from shapely.geometry import Point, LineString
839
    from SymbolSvgItem import SymbolSvgItem
840
    from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
841
    from QEngineeringLineNoTextItem import QEngineeringLineNoTextItem
842
    from QEngineeringTextItem import QEngineeringTextItem
843
    from QEngineeringLineItem import QEngineeringLineItem
844
    from LineDetector import LineDetector
845

    
846
    try:
847
        listWidget.addItem('Starting line recognization')
848

    
849
        #remove already existing line and flow arrow item
850
        items = [item for item in graphicsView.scene.items() if (type(item) is QEngineeringLineItem) or (type(item) is QEngineeringFlowArrowItem)]
851
        for item in items:
852
            graphicsView.scene.removeItem(item)
853
        #up to here
854

    
855
        # detect line
856
        connectedLines = []
857

    
858
        area = AppDocData.instance().getArea('Drawing')
859
        area.img = removeSmallObjects(area.img)
860
        detector = LineDetector(area.img)
861

    
862
        symbols = []
863
        for item in graphicsView.scene.items():
864
            if issubclass(type(item), SymbolSvgItem):
865
                symbols.append(item)
866
                res = detector.detectConnectedLine(item, round(area.x), round(area.y))
867
                if res is not None:
868
                    connectedLines.extend(res)
869

    
870
        listWidget.addItem('Connecting lines')
871
        if len(connectedLines) > 1:
872
            detector.mergeLines(connectedLines, toler=5)
873
            # connect line to symbol
874
            try:
875
                for line in connectedLines:
876
                    matches = [symbol for symbol in symbols if symbol.isConnectable(line, (round(area.x), round(area.y)), toler=20)]
877
                    for symbol in matches:
878
                        detector.connectLineToSymbol(line, (round(area.x), round(area.y)), symbol)
879
            except Exception as ex:
880
                print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
881
            # up to here
882

    
883
            # connect line to line
884
            toler = 10
885
            try:
886
                for line in connectedLines:
887
                    matches = [it for it in connectedLines if (it is not line) and (not detector.isParallel(line, it))]
888

    
889
                    # get closest line
890
                    selected = []
891
                    shapelyLine = LineString(line)
892
                    for match in matches:
893
                        dist = [shapelyLine.distance(Point(match[0][0], match[0][1])),shapelyLine.distance(Point(match[1][0], match[1][1]))]
894
                        if dist[0] < toler or dist[1] < toler:
895
                            selected.append(match)
896
                    # up to here
897

    
898
                    for match in selected:
899
                        detector.connectLineToLine(match, line, toler)
900
            except Exception as ex:
901
                print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
902
            # up to here
903

    
904
        lines = []
905
        for pts in connectedLines:
906
            processLine = QEngineeringLineItem(vertices=[(area.x + param[0], area.y + param[1]) for param in pts])
907
            processLine.buildItem()
908
            processLine.addLineItemToScene(graphicsView.scene)
909
            lines.append(processLine)
910

    
911
            if processLine.length() > 100: # TODO: check critical length
912
                processLine.addFlowArrow()
913
        
914
        # re-order process line's start,end according to flow mark
915
        arrangeLinePosition(lines, symbols, listWidget)
916
        # up to here
917
    except Exception as ex:
918
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
919
    finally:
920
        listWidget.addItem('Finished line recognization')
921
    
922
'''
923
    @brief  arrange line's position
924
    @author humkyung
925
    @date   2018.07.04
926
'''
927
def arrangeLinePosition(lines, symbols, listWidget):
928
    try:
929
        listWidget.addItem('Apply flow direction')
930
        pool = [line for line in lines if line.flowMark is not None]
931
        visited = []
932
        visited.extend(pool)
933
        while len(pool) > 0:
934
            line = pool.pop()
935
            print('{} - ({})'.format(line, len(pool)))
936
            rhs = [item for item in lines if item not in visited and item.isJointed(line)]
937
            if rhs:
938
                pool.extend(rhs)
939
                visited.extend(rhs)
940
                for item in rhs:
941
                    item.arrangeVertexOrder(line)
942
            
943
            # skip jointed symbols
944
            symbolPool = [item for item in symbols if item not in visited and item.isJointed(line)]
945
            if symbolPool:
946
                selected = []
947
                visited.extend(symbolPool)
948
                while len(symbolPool) > 0:
949
                    symbol = symbolPool.pop()
950

    
951
                    rhs = [item for item in symbols if item not in visited and item.isJointed(symbol)]
952
                    if rhs:
953
                        symbolPool.extend(rhs)
954
                        visited.extend(rhs)
955
                        selected.extend(rhs)
956
                    else:
957
                        selected.append(symbol)
958

    
959
                # find lines which are connected last symbol
960
                for symbol in selected:
961
                    rhs = [item for item in lines if item not in visited and item.isJointed(symbol)]
962
                    if rhs:
963
                        pool.extend(rhs)
964
                        visited.extend(rhs)
965
                        for item in rhs:
966
                            item.arrangeVertexOrder(line)
967
            # up to here
968
        # up to here
969
    except Exception as ex:
970
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
971

    
972
'''
973
    @brief      Main function
974
    @author     Jeongwoo
975
    @date
976
    @history    humkyung 2018.04.06 change error display from message box to print
977
                Jeongwoo 2018.04.25 Remove 'Current Symbol : ' QListItem
978
                Jeongwoo 2018.05.09 Make Comments OCR.removeTextFromNpArray block
979
                Jeongwoo 2018.05.25 Remove imgLineList variable and parameter on writeXml()
980
                humkyung 2018.05.26 add parameters(graphicsView, isSymbolTextChecked, isLineChecked)
981
                Jeongwoo 2018.05.28 Add/Remove Parameters(Add : signal / Remove : graphicsView, isLineChecked)
982
                Jeongwoo 2018.05.30 Remove return value
983
                humkyung 2018.06.08 add signal for progressbar to parameter
984
                humkyung 2018.06.11 get difference between original and recognized image
985
                Jeongwoo 2018.06.21 If noteTextInfoList is None, change from None to empty list
986
'''
987
def executeRecognition(signal, updateProgressSignal, path, listWidget, isSymbolTextChecked):
988
    import re
989
    from TextDetector import TextDetector
990

    
991
    global ocrCompletedSrc
992
    global searchedSymbolList
993
    global threadLock
994
    global textInfoList
995
    global noteTextInfoList
996
    global maxProgressValue
997
    
998
    try:
999
        appDocData = AppDocData.instance()
1000
        project = appDocData.getCurrentProject()
1001
        textDetector = TextDetector()
1002

    
1003
        srcList = []
1004
        srcList.append(path)
1005

    
1006
        initTargetSymbolDataList()
1007

    
1008
        for mainRes in srcList:
1009
            ocrCompletedSrc = []
1010
            searchedSymbolList = []
1011
            textInfoList = []
1012

    
1013
            if not os.path.isfile(mainRes):
1014
                item = QListWidgetItem('{} file is not found'.format(os.path.basename(mainRes)))
1015
                item.setBackground(Qt.red)
1016
                listWidget.addItem(item)
1017
                continue
1018

    
1019
            # remove equipment desc. area
1020
            configs = appDocData.getConfigs('{} Equipment Desc Area'.format(appDocData.imgName))
1021
            for config in configs:
1022
                found = re.findall('\d+', config.value)
1023
                if len(found) == 4:
1024
                    cv2.rectangle(appDocData.imgSrc, (int(found[0]), int(found[1])), (int(found[0])+int(found[2]), int(found[1])+int(found[3])), 255, -1)
1025
            # up to here
1026
            
1027
            area = appDocData.getArea('Drawing')
1028
            if area is not None:
1029
                area.img = appDocData.imgSrc[round(area.y):round(area.y+area.height), round(area.x):round(area.x+area.width)]
1030

    
1031
            listWidget.addItem("Start recognition : " + mainRes)
1032

    
1033
            if isSymbolTextChecked:
1034
                threadLock.acquire()
1035
                offset = (area.x, area.y) if area is not None else (0,0)
1036
                textAreas = textDetector.detectTextAreas(area.img if area is not None else appDocData.imgSrc, offset)
1037
                ### calculate total count of symbol
1038
                maxProgressValue = len(textAreas) + 1 
1039
                for targetItem in targetSymbolList:
1040
                    if type(targetItem) is list:
1041
                        maxProgressValue += len(targetItem)
1042
                    else:
1043
                        maxProgressValue += 1
1044
                ### up to here
1045
                threadLock.release()
1046

    
1047
                # detect equipments
1048
                pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
1049
                for symbol in targetSymbolList[0]:
1050
                    pool.submit(detectEquipmentOnPid, mainRes, symbol, listWidget, updateProgressSignal)
1051
                pool.shutdown(wait = True)
1052
                # up to here
1053

    
1054
                pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
1055
                for symbol in targetSymbolList[2]:
1056
                    if type(symbol) is list:
1057
                        pool.submit(detectSymbolsOnPid, mainRes, symbol, listWidget, updateProgressSignal)
1058
                    else:
1059
                        pool.submit(detectSymbolOnPid, mainRes, symbol, listWidget, updateProgressSignal)
1060
                pool.shutdown(wait = True)
1061

    
1062
                ## DEBUG
1063
                print('----------')
1064
                for item in searchedSymbolList:
1065
                    print('{}:{}-{}'.format(item.getName(), item.getSp(), item.hitRate))
1066
                    _img = appDocData.imgSrc[round(item.getSp()[1]):round(item.getSp()[1]+item.getHeight()), round(item.getSp()[0]):round(item.getSp()[0]+item.getWidth())]
1067
                    cv2.imwrite(os.path.join(project.getTempPath(), 'Tile', item.getName()+'.png'), _img)
1068
                ## up to here
1069

    
1070
                textDetector.recognizeText(appDocData.imgSrc, offset, textAreas, searchedSymbolList, updateProgressSignal, listWidget, maxProgressValue)
1071
                textInfoList = textDetector.textInfoList.copy()
1072
                noteTextInfoList = textDetector.noteTextInfoList.copy()
1073

    
1074
                appDocData.imgWidth, appDocData.imgHeight = appDocData.imgSrc.shape[::-1]
1075
                drawFoundSymbolsOnCanvas(mainRes, textInfoList, listWidget)
1076
                
1077
                appDocData.imgName = os.path.splitext(os.path.basename(mainRes))[0]
1078

    
1079
                pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
1080
                for sym in searchedSymbolList:
1081
                    pool.submit(removeDetectedSymbol, sym, appDocData.imgSrc)
1082
                    #pool.submit(drawRectOnSrc, sym)
1083
                pool.shutdown(wait = True)
1084

    
1085
                '''
1086
                global MIN_TEXT_SIZE
1087
                for textInfo in textInfoList:
1088
                    if textInfo.getW() >= MIN_TEXT_SIZE or textInfo.getH() >= MIN_TEXT_SIZE:
1089
                        removeText(srcGray, (textInfo.getX(), textInfo.getY()), srcGray[textInfo.getY():textInfo.getY()+textInfo.getH(), textInfo.getX():textInfo.getX()+textInfo.getW()])
1090
                '''
1091

    
1092
                ## Remove Noise
1093
                kernel1 = np.ones((2, 2), np.uint8)
1094
                appDocData.imgSrc = cv2.dilate(appDocData.imgSrc, kernel1)
1095
                appDocData.imgSrc = cv2.erode(appDocData.imgSrc, kernel1)
1096

    
1097
                removedSymbolImgPath = os.path.join(project.getTempPath(), os.path.basename(path))
1098
                cv2.imwrite(removedSymbolImgPath, appDocData.imgSrc)
1099

    
1100
                area = AppDocData.instance().getArea('Drawing')
1101
                if area is not None:
1102
                    area.img = appDocData.imgSrc[round(area.y+1):round(area.y+area.height), round(area.x+1):round(area.x+area.width)]
1103
                cv2.imwrite(os.path.join(project.getTempPath(), "RECT_" + os.path.basename(path)), appDocData.imgSrc)
1104

    
1105
                listWidget.addItem("Recognized symbol count : " + str(len(searchedSymbolList)))
1106
                
1107
                # get difference between original and recognized image
1108
                foundFilePath = os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(path))
1109
                getDifference(path, foundFilePath)
1110
                # up to here
1111
                
1112
                signal.emit(searchedSymbolList, textInfoList, noteTextInfoList if noteTextInfoList is not None else [])
1113
    except Exception as ex:
1114
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
1115
 
1116
'''
1117
    @brief      draw contour to image
1118
    @author     humkyung
1119
    @date       2018.06.18
1120
'''
1121
def drawContour(img, contour):
1122
    area = cv2.contourArea(contour, True)
1123
    if area >= 0:
1124
        cv2.drawContours(img, [contour], -1, (0,0,0), -1)
1125
        cv2.drawContours(img, [contour], -1, (255,255,255), 1)
1126
    else:
1127
        cv2.drawContours(img, [contour], -1, (255,255,255), -1)
1128
    
1129
'''
1130
    @brief  get difference between given original and recognized image
1131
    @author humkyung
1132
    @date   2018.06.11
1133
'''
1134
def getDifference(orgImagePath, recImagePath):
1135
    import re
1136

    
1137
    global ocrCompletedSrc
1138
    global textInfoList
1139
    global noteTextInfoList
1140

    
1141
    try:
1142
        appDocData = AppDocData.instance()
1143
        if os.path.isfile(orgImagePath) and os.path.isfile(recImagePath):
1144
            imgOriginal = cv2.threshold(cvtGrayImage(cv2.imread(orgImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
1145
            # remove equipment desc. area
1146
            configs = appDocData.getConfigs('{} Equipment Desc Area'.format(appDocData.imgName))
1147
            for config in configs:
1148
                found = re.findall('\d+', config.value)
1149
                if len(found) == 4:
1150
                    cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])), (int(found[0])+int(found[2]), int(found[1])+int(found[3])), 255, -1)
1151
            # up to here
1152

    
1153
            imgRecognized = cv2.threshold(cvtGrayImage(cv2.imread(recImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
1154

    
1155
            imgDiff = np.ones(imgOriginal.shape, np.uint8)*255
1156

    
1157
            area = AppDocData.instance().getArea('Drawing')
1158
            if area is not None:
1159
                x = round(area.x)
1160
                y = round(area.y)
1161
                width = round(area.width)
1162
                height = round(area.height)
1163
                imgNotOper = cv2.bitwise_not(imgRecognized[y:y+height, x:x+width])
1164
                imgDiff[y:y+height, x:x+width] = cv2.bitwise_xor(imgOriginal[y:y+height, x:x+width], imgNotOper)
1165
                
1166
            # remove noise
1167
            imgDiff = cv2.dilate(imgDiff, np.ones((2, 2), np.uint8))
1168

    
1169
            appDocData = AppDocData.instance()
1170
            project = appDocData.getCurrentProject()
1171
            cv2.imwrite(os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(orgImagePath)), imgDiff)
1172
    except Exception as ex:
1173
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
1174
   
1175
if __name__ == '__main__':
1176
    import DTI_PID_UI
1177
    from ProjectDialog import Ui_Dialog
1178
    import timeit
1179
    from PyQt5.QtCore import QRect
1180
    from operator import itemgetter, attrgetter
1181

    
1182
    start = timeit.default_timer()
1183
    img = cv2.imread('D:/Visual Studio Project/DTIPID/DTIPID/DTI_PID/DTI_PID/res/Result/PC-K/PC-K-2203_P1_800DPI.png', 1)
1184
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1185
    contourImg = np.ones(imgGray.shape, np.uint8)*255
1186
    contourOcrImg = contourImg.copy()
1187
    binaryImg,mask = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
1188
    imgFinal = cv2.bitwise_and(imgGray, imgGray, mask = mask)
1189
    ret, newImg = cv2.threshold(imgFinal, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
1190

    
1191
    image, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
1192
    #holes = [contours[i] for i in range(len(contours)) if hierarchy[i][3] >= 0]
1193
    for contour in contours:
1194
        area = cv2.contourArea(contour, True)
1195
        if area >= 0:
1196
            [x, y, w, h] = cv2.boundingRect(contour)
1197

    
1198
            # remove too big or small one
1199
            if (w > 100 or h > 100) or (w < 5 or h < 5): continue
1200

    
1201
            cv2.drawContours(contourImg, [contour], -1, (0,0,0), 1)
1202
            cv2.drawContours(contourOcrImg, [contour], -1, (0,0,0), -1)
1203
        else:
1204
            cv2.drawContours(contourOcrImg, [contour], -1, (255,255,255), -1)
1205

    
1206
    ''' contourImg = cv2.bitwise_not(contourImg) circles = cv2.HoughCircles(contourImg, cv2.HOUGH_GRADIENT, 1, 100) circles = np.uint16(np.around(circles))
1207
       for i in circles[0,:]:
1208
        cv2.circle(contourImg, (i[0], i[1]), i[2], (255,255,0), 1)
1209
    '''
1210

    
1211
    rects = []
1212
    kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (8, 8))
1213
    #kernel1 = cv2.getStructuringElement(cv2.MORPH_CROSS, (2,2))
1214
    #eroded = cv2.dilate(contourImg, kernel1)
1215
    #cv2.imwrite('D:/Visual Studio Project/DTIPID/DTIPID/DTI_PID/DTI_PID/res/Result/PC-K/Temp/Dilate_PC-K-2203_P1_800DPI___partial.png', eroded)
1216
    eroded = cv2.erode(contourImg, kernel)
1217
    image, contours, hierarchy = cv2.findContours(eroded, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
1218
    for contour in contours:
1219
        area = cv2.contourArea(contour, True)
1220
        if area >= 0:
1221
            [x, y, w, h] = cv2.boundingRect(contour)
1222

    
1223
            # remove small one less than character size
1224
            if (w < 20 or h < 20): continue
1225
            #if w > h:
1226
            #    rects.append(QRect(x, y, w, h)) # expand rect
1227
            #elif w < h:
1228
            #    rects.append(QRect(x, y, w, h)) # expand rect
1229
            rects.append(QRect(x, y, w, h)) # expand rect
1230

    
1231
    intersected = True
1232
    while intersected:
1233
        intersected = False
1234
        for rect in rects[:]:
1235
            matches = [x for x in rects if rect.intersects(x)]
1236
            if len(matches) > 1:
1237
                united = matches[0]
1238
                for _rect in matches:
1239
                    united = united.united(_rect)
1240
                    if _rect in rects: rects.remove(_rect)
1241
                rects.append(united)
1242
                intersected = True
1243
                break
1244

    
1245
    for rect in rects:
1246
        cv2.rectangle(img, (rect.x(), rect.y()), (rect.x() + rect.width(), rect.y() + rect.height()), (255, 0, 255), 1)
1247

    
1248
    cv2.imwrite('D:/Visual Studio Project/DTIPID/DTIPID/DTI_PID/DTI_PID/res/Result/PC-K/Temp/PC-K-2203_P1_800DPI___partial.png', img)
1249
    cv2.imwrite('D:/Visual Studio Project/DTIPID/DTIPID/DTI_PID/DTI_PID/res/Result/PC-K/Temp/Contour_PC-K-2203_P1_800DPI___partial.png', contourOcrImg)
1250
    cv2.imwrite('D:/Visual Studio Project/DTIPID/DTIPID/DTI_PID/DTI_PID/res/Result/PC-K/Temp/Erode_PC-K-2203_P1_800DPI___partial.png', eroded)
1251

    
1252
    chan, imgW, imgH = img.shape[::-1]
1253
    index = 0
1254
    for rect in rects:
1255
        index = index + 1
1256
        isVertical = False
1257
        textInfoList = None
1258
        if rect.width() >= rect.height() or rect.height() < 50:
1259
            isVertical = False
1260
            textInfoList = TOCR.getTextInfo(contourOcrImg[rect.y():rect.y()+rect.height(), rect.x():rect.x()+rect.width()], (rect.x(), rect.y()))
1261
        else:
1262
            isVertical = True
1263
            transform = QTransform()
1264
            transform.translate(imgH*0.5, imgW*0.5)
1265
            transform.rotate(90)
1266
            transform.translate(-imgW*0.5, -imgH*0.5)
1267
            transRect = transform.mapRect(rect)
1268
            rotatedContourOcrImg = cv2.rotate(contourOcrImg, cv2.ROTATE_90_CLOCKWISE)
1269
            textInfoList = TOCR.getTextInfo(rotatedContourOcrImg[transRect.y():transRect.y()+transRect.height(), transRect.x():transRect.x()+transRect.width()], (transRect.x(), transRect.y()))
1270

    
1271
        if isVertical:
1272
            img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
1273
        if textInfoList is not None:
1274
            for textInfo in textInfoList:
1275
                cv2.putText(img, textInfo.getText(), (textInfo.getX(), textInfo.getY()+textInfo.getH()), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 3)
1276
        if isVertical:
1277
            img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
1278

    
1279
        print(str(index) + " / " + str(len(rects)) + " Finished")
1280
        #cv2.imshow('test', img[rect.y():rect.y()+rect.height(), rect.x():rect.x()+rect.width()])
1281
        #cv2.waitKey(0)
1282
        #cv2.destroyAllWindows()
1283
        
1284
    cv2.imwrite('D:/Visual Studio Project/DTIPID/DTIPID/DTI_PID/DTI_PID/res/Result/PC-K/Temp/OCR_PC-K-2203_P1_800DPI___partial.png', img)
1285
    stop = timeit.default_timer()
1286
    print('FINISHED : ' + str((stop-start)/60) + ' min')
1287
    
1288
    #app = QApplication(sys.argv)
1289

    
1290
    #try:
1291
    #    dlg = Ui_Dialog()
1292
    #    selectedProject = dlg.showDialog()
1293
    #    if selectedProject is not None:
1294
    #        form = ExampleApp()
1295
    #        form.show()
1296
    #except Exception as ex:
1297
    #    print('에러가 발생했습니다.\n', ex)
1298

    
1299
    #sys.exit(app.exec_())