프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / DTI_PID.py @ 0c4c6a44

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

1
#region import libs
2
import http.client
3
import urllib, base64, json
4
import cv2
5
import numpy as np
6
import matplotlib.pyplot as plt
7
import matplotlib.image as mpimg
8
import SymbolBase
9
import symbol
10
import TextInfo as ti
11
import azure_ocr_module as OCR
12
import azure_handwrite_ocr_module as HOCR
13
import imutils
14
from PIL import Image
15
from PIL import ImageTk
16
from PIL import ImageChops
17
from io import BytesIO
18
from functools import partial
19
from functools import reduce
20
import gc
21
import os
22
import glob
23
import timeit
24
import scipy
25
import math, operator
26
import threading
27
import concurrent.futures as futures
28
import XmlGenerator as xg
29
import sqlite3
30
import pytesseract
31
import tesseract_ocr_module as TOCR
32
import potrace
33
import sys
34
from PyQt5.QtCore import *
35
from PyQt5.QtGui import *
36
from PyQt5.QtWidgets import *
37
from PyQt5.QtSvg import *
38
import DTI_PID_UI
39
import QtImageViewer
40

    
41
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Commands')
42
import CreateCommand
43
import CropCommand
44

    
45
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes')
46
import QGraphicsPolylineItem
47
from QEngineeringLineItem import QEngineeringLineItem
48
from SymbolSvgItem import SymbolSvgItem
49
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
50
from AppDocData import AppDocData
51
#endregion
52

    
53
## Tesseract path
54
pytesseract.pytesseract.tesseract_cmd = os.environ['TESSERACT_HOME'] + '\\tesseract.exe'
55
tesseract_cmd = os.environ['TESSERACT_HOME'] + '\\tesseract.exe'
56

    
57
#region Symbol Image path List for test
58
targetSymbolList = []
59
#endregion
60

    
61
#region Global variables
62
searchedSymbolList = []
63
src = []
64
srcGray = []
65
ocrCompletedSrc = []
66
afterDenoising = []
67
canvas = []
68
textInfoList = []
69

    
70
WHITE_LIST_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"
71

    
72
#DB_NAME = "db/DTI_PID_test.db"
73
#DB_NAME = AppDocData.instance().getCurrentProject().getPath() + "/db/" + "DTI_PID.db"
74
#DB_NAME = os.path.dirname(os.path.realpath(__file__)) + "\\db\\DTI_PID.db"
75

    
76
THREAD_MAX_WORKER = 4
77
threadLock = threading.Lock()
78

    
79
ACCEPT_OVERLAY_AREA = 10
80
#endregion
81

    
82
def checkTextInSymbol(pt):
83
    global searchedSymbolList
84

    
85
    result = False
86
    for sym in searchedSymbolList:
87
        symId = sym.getId()
88
        symSp = sym.getSp()
89
        symWidth = sym.getWidth()
90
        symHeight = sym.getHeight()
91
        symOcrOption = sym.getOcrOption()
92

    
93
        categoryCode = symId // 100
94

    
95
        if symOcrOption != SymbolBase.OCR_OPTION_NOT_EXEC:
96
            if categoryCode != 99 and (pt[0] >= symSp[0] and pt[0] <= symSp[0] + symWidth) and (pt[1] >= symSp[1] and pt[1] <= symSp[1] + symHeight):
97
                result = True
98
                break
99

    
100
    return result
101

    
102
def removeText(img, text, x, y, width, height):
103
    x = int(x)
104
    y = int(y)
105
    width = int(width)
106
    height = int(height)
107
    roi = img[y:y+height, x:x+width]
108
    temp = roi.copy()
109
    tempBin = cv2.bitwise_not(temp)
110
    roi = cv2.bitwise_xor(roi, tempBin, roi)
111

    
112
#Convert into Grayscale image
113
def cvtGrayImage(img):
114
    return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
115

    
116
'''
117
    @brief      rotate (x,y) by given angle
118
    @author     Jeongwoo
119
    @date       2018.??.??
120
    @history    humkyung 2018.04.13 fixed code when angle is 90 or 270    
121
'''
122
def getCoordOnRotatedImage(rAngle, x, y, originImageWidth, originImageHeight):
123
    rx = None
124
    ry = None
125
    if rAngle == 0:
126
        rx = x
127
        ry = y
128
    elif rAngle == 90:
129
        rx = y
130
        ry = originImageWidth - x
131
    elif rAngle == 180:
132
        rx = originImageWidth - x
133
        ry = originImageHeight - y
134
    elif rAngle == 270:
135
        rx = originImageHeight - y
136
        ry = x
137
    else:   # general case
138
        pass
139
    return (rx, ry)
140

    
141
def convertDirectionCodeToValue(directionCode):
142
    if directionCode == "UP":
143
        return 0
144
    elif directionCode == "RIGHT":
145
        return 1
146
    elif directionCode == "DOWN":
147
        return 2
148
    elif directionCode == "LEFT":
149
        return 3
150
    else:
151
        return -1
152

    
153
def convertValueToDirectionCode(value):
154
    if value == 0:
155
        return "UP"
156
    elif value == 1:
157
        return "RIGHT"
158
    elif value == 2:
159
        return "DOWN"
160
    elif value == 3:
161
        return "LEFT"
162
    else:
163
        return "NONE"
164

    
165
def getRotatedChildInfo(additionalSymbol):
166
    tempChildInfo = ""
167
    if additionalSymbol:
168
        childList = additionalSymbol.split("/")
169
        for index in range(len(childList)):
170
            child = childList[index]
171
            direction = convertDirectionCodeToValue(child.split(",")[0])
172
            childName = child.split(",")[1]
173
            direction = (direction - 1) if direction > 0 else 3
174
            if index != 0:
175
                tempChildInfo = tempChildInfo + "/"
176
            tempChildInfo = tempChildInfo + convertValueToDirectionCode(direction) + "," + childName
177
    return tempChildInfo
178

    
179

    
180
#Check object contains pt
181
#obj is item in searchedSymbolList
182
def contains(obj, pt, tw, th):
183
    sp = obj.getSp()
184
    width = obj.getWidth()
185
    height = obj.getHeight()
186

    
187
    if sp[0] > pt[0]+tw:
188
        return 0
189
    if sp[0]+width < pt[0]: 
190
        return 0
191
    if sp[1] > pt[1]+th: 
192
        return 0
193
    if sp[1]+height < pt[1]: 
194
        return 0
195
    
196
    #shared area
197
    x = max(sp[0], pt[0]);
198
    y = max(sp[1], pt[1]);
199
    w = min(sp[0] + width, pt[0] + tw) - x;
200
    h = min(sp[1] + height, pt[1] + th) - y;
201

    
202
    return float((w * h)) / float((tw * th)) * 100
203

    
204
def getSplitSrcList(srcPid, splitCount, splitWidth, splitHeight):
205
    splitRoiList = []
206
    for hi in range(splitCount):
207
        for wi in range(splitCount):
208
            roiSp = (splitWidth*wi, splitHeight*hi)
209
            roiEp = (splitWidth*(wi+1), splitHeight*(hi+1))
210
            splitRoiList.append((roiSp, roiEp, srcPid[roiSp[1]:roiEp[1], roiSp[0]:roiEp[0]]))
211
    return splitRoiList
212

    
213
def getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, rotateSymbolWidth, rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight):
214
    originalPoint = ''
215
    if additionalSymbol is None and symbolOriginalPoint is None:
216
        originalPoint = str(rotateSymbolWidth//2)+','+str(rotateSymbolHeight//2)
217
    else:
218
        opx = int(symbolOriginalPoint.split(',')[0])
219
        opy = int(symbolOriginalPoint.split(',')[1])
220
        rPt = getCoordOnRotatedImage(symbolRotatedAngle, opx, opy, originalSymbolWidth, originalSymbolHeight)
221
        originalPoint = str(int(rPt[0])) + ',' + str(int(rPt[1]))
222
    return originalPoint
223

    
224
def getCalculatedConnectionPoint(symbolConnectionPointStr, symbolRotatedAngle, rotateSymbolWidth, rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight):
225
    convertedConnectionPoint = ""
226
    splitConnectionPointStr = symbolConnectionPointStr.split("/")
227
    for index in range(len(splitConnectionPointStr)):
228
        if index != 0:
229
            convertedConnectionPoint = convertedConnectionPoint + "/"
230
        item = splitConnectionPointStr[index]
231
        cpx = int(item.split(',')[0])
232
        cpy = int(item.split(',')[1])
233
        rPt = getCoordOnRotatedImage(symbolRotatedAngle, cpx, cpy, originalSymbolWidth, originalSymbolHeight)
234
        temp = str(int(rPt[0])) + ',' + str(int(rPt[1]))
235
        convertedConnectionPoint = convertedConnectionPoint + temp
236
    return convertedConnectionPoint
237
    
238
'''
239
    @brief  Add symbols
240
    @author jwkim
241
    @date
242
'''
243
def addSearchedSymbol(id, sName, sType
244
                      , sp, w, h, threshold, minMatchCount, mpCount, rotatedAngle
245
                      , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
246
                      , originalPoint, connectionPoint, baseSymbol, additionalSymbol):
247
    global searchedSymbolList
248
    newSym = symbol.Symbol(id, sName, sType
249
                           , sp, w, h, threshold, minMatchCount, mpCount, rotatedAngle
250
                           , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
251
                           , originalPoint, connectionPoint, baseSymbol, additionalSymbol)
252
##Add symbols
253
#def addSearchedSymbol(id, sName, sType, symbolPath
254
#                      , sp, w, h, threshold, minMatchCount, mpCount, rotatedAngle
255
#                      , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
256
#                      , originalPoint, connectionPoint, baseSymbol, additionalSymbol):
257
#    global searchedSymbolList
258
#    newSym = symbol.Symbol(id, sName, sType, symbolPath
259
#                           , sp, w, h, threshold, minMatchCount, mpCount, rotatedAngle
260
#                           , isDetectOnOrigin, rotateCount, ocrOption, isContainChild
261
#                           , originalPoint, connectionPoint, baseSymbol, additionalSymbol)
262
    searchedSymbolList.append(newSym)
263

    
264

    
265
#Calculate count of keypoint match result
266
def getMatchPointCount(src, cmp):
267
    orb = cv2.ORB_create(1000, 2.0, 2, 1)
268

    
269
    kp1, des1 = orb.detectAndCompute(src, None)
270
    kp2, des2 = orb.detectAndCompute(cmp, None)
271
    
272
    FLANN_INDEX_LSH = 6
273
    # table_number      : The number of hash tables use
274
    # key_size          : The length of the key in the hash tables
275
    # multi_probe_level : Number of levels to use in multi-probe (0 for standard LSH)
276
    #                     It controls how neighboring buckets are searched
277
    #                     Recommended value is 2
278
    # checks            : specifies the maximum leafs to visit when searching for neighbours.
279
    # LSH : Locality-Sensitive Hashing
280
    # ref : https://www.cs.ubc.ca/research/flann/uploads/FLANN/flann_manual-1.8.4.pdf
281
    index_params = dict(algorithm = FLANN_INDEX_LSH, table_number = 20, key_size = 10, multi_probe_level = 4)
282
    search_params = dict(checks = 100)
283
    
284
    flann = cv2.FlannBasedMatcher(index_params,search_params)
285
    
286
    matches = flann.knnMatch(des1, des2, k = 2)
287
    matchesMask = [[0, 0] for i in range(len(matches))] #Python 3.x
288
    
289
    count = 0
290
    # ratio test as per Lowe's paper
291
    for i in range(len(matches)):
292
        if len(matches[i]) == 2:
293
            m = matches[i][0]
294
            n = matches[i][1]
295
            if m.distance < 0.85 * n.distance:
296
                count = count + 1
297

    
298
    matchCount = count
299

    
300
    #print("match Count : " + str(matchCount))
301
    return matchCount
302

    
303

    
304
#detect symbols on PID
305
def detectSymbolsOnPid(mainRes, targetSymbols, listWidget):
306
    for detailTarget in targetSymbols:
307
        detectSymbolOnPid(mainRes, detailTarget, listWidget)
308

    
309
'''
310
    @brief      detect symbol on PID
311
    @author     jwkim
312
    @date   
313
    @history    humkyung 2018.04.06 check if symbol file exists
314
'''
315
def detectSymbolOnPid(mainRes, targetSymbol, listWidget):
316
    global src
317
    global srcGray
318
    global ocrCompletedSrc
319
    global afterDenoising
320
    global threadLock
321
    global searchedSymbolList
322
    
323
    symId = targetSymbol.getId()
324
    symbolName = targetSymbol.getName()
325
    symbolType = targetSymbol.getType()
326
    symbolPath = targetSymbol.getPath()
327
    symbolThreshold = targetSymbol.getThreshold()
328
    symbolMinMatchCount = targetSymbol.getMinMatchCount()
329
    isDetectOnOrigin = targetSymbol.getIsDetectOnOrigin()
330
    symbolRotateCount = targetSymbol.getRotationCount()
331
    symbolOcrOption = targetSymbol.getOcrOption()
332
    isContainChild = targetSymbol.getIsContainChild()
333
    symbolOriginalPoint = targetSymbol.getOriginalPoint()
334
    symbolConnectionPoint = targetSymbol.getConnectionPoint()
335
    baseSymbol = targetSymbol.getBaseSymbol()
336
    additionalSymbol = targetSymbol.getAdditionalSymbol()
337

    
338
    foundSymbolCount = 0
339

    
340
    # check if symbol file exists
341
    if not os.path.isfile(symbolPath):
342
        item = QListWidgetItem('{} file not found'.format(symbolPath))
343
        item.setBackground(QColor('red'))
344
        listWidget.addItem(item)
345
        return
346
    else:
347
        listWidget.addItem('current symbol : ' + symbolPath)
348
    # up to here
349

    
350
    sym = cv2.imread(symbolPath, 1)
351
    symGray = cvtGrayImage(sym)
352
    sow, soh = symGray.shape[::-1] # symbol original w, h
353

    
354
    offsetDrawingArea=[]
355
    area = AppDocData.instance().getArea('Drawing')
356
    if area is not None:
357
        copiedBasePid = area.img.copy()
358
        offsetDrawingArea.append(area.x)
359
        offsetDrawingArea.append(area.y)
360
    else:
361
        offsetDrawingArea.append(0)
362
        offsetDrawingArea.append(0)
363
        if isDetectOnOrigin == 1:
364
            copiedBasePid = srcGray.copy()
365
        else:
366
            copiedBasePid = ocrCompletedSrc.copy()
367
    srcWidth, srcHeight = copiedBasePid.shape[::-1]
368

    
369
    if srcWidth > 10000 or srcHeight > 10000:
370
        splitCount = 2 ## splitCount = 2^n
371
    else:
372
        splitCount = 1
373

    
374
    totalMpCount = 0
375
    while splitCount >= 1:
376
        splitWidth = srcWidth // splitCount
377
        splitHeight = srcHeight // splitCount
378
                
379
        ##Setting ROI List - [splitCount * splitCount] size
380
        splitRoiList = getSplitSrcList(copiedBasePid, splitCount, splitWidth, splitHeight)
381

    
382
        for roiIndex in range(len(splitRoiList)):
383
            roiItemSp = splitRoiList[roiIndex][0]
384
            roiItemEp = splitRoiList[roiIndex][1]
385
            roiItem = splitRoiList[roiIndex][2]
386

    
387
            symbolRotatedAngle = 0
388
            for rc in range(symbolRotateCount + 1): ## Rotation Count를 사용자 기준으로 받아서 1을 더한 후 사용
389
                ##Template Matching
390
                sw, sh = symGray.shape[::-1]
391
                roiw = (roiItemEp[0] - roiItemSp[0])
392
                roih = (roiItemEp[1] - roiItemSp[1])
393

    
394
                ## Case : Bigger Symbol than Split ROI
395
                if roiw < sw or roih < sh:
396
                    symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
397
                    symbolRotatedAngle = symbolRotatedAngle + 90
398

    
399
                    if baseSymbol is not None and additionalSymbol is not None:
400
                        additionalSymbol = getRotatedChildInfo(additionalSymbol)
401
                    continue
402
                
403
                ## get Rotated Original Point
404
                originalPoint = getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, sw, sh, sow, soh)
405
                connectionPoint = getCalculatedConnectionPoint(symbolConnectionPoint, symbolRotatedAngle, sw, sh, sow, soh)
406

    
407

    
408
                ## Template Matching
409
                tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
410
                loc = np.where(tmRes >= symbolThreshold)
411

    
412

    
413
                for pt in zip(*loc[::-1]):
414
                    overlapArea = 0
415
                    mpCount = 0 # Match Point Count
416
                    symbolIndex = -1
417

    
418
                    searchedItemSp = (roiItemSp[0]+pt[0] + int(offsetDrawingArea[0]), roiItemSp[1]+pt[1] + int(offsetDrawingArea[1]))
419

    
420
                    for i in range(len(searchedSymbolList)):
421
                        overlapArea = contains(searchedSymbolList[i], searchedItemSp, sw, sh)
422
                        if overlapArea > ACCEPT_OVERLAY_AREA:
423
                            symbolIndex = i
424
                            break
425
                    
426

    
427
                    roi = roiItem[pt[1]:pt[1]+sh, pt[0]:pt[0]+sw]
428
                    mpCount = getMatchPointCount(roi, symGray)
429

    
430

    
431
                    ## 겹치는 영역이 기준값보다 작을 경우
432
                    if overlapArea <= ACCEPT_OVERLAY_AREA:
433
                        if mpCount >= symbolMinMatchCount:
434
                            threadLock.acquire()
435
                            foundSymbolCount = foundSymbolCount + 1
436
                            totalMpCount = totalMpCount + mpCount
437
                            addSearchedSymbol(symId, symbolName, symbolType
438
                                              , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, mpCount, symbolRotatedAngle
439
                                              , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
440
                                              , originalPoint, connectionPoint, baseSymbol, additionalSymbol)
441
                            threadLock.release()
442
                    ## 겹치는 영역이 기준값보다 클 경우
443
                    else:
444
                        if symbolIndex != -1 and symbolIndex < len(searchedSymbolList):
445
                            searchedSymbol = searchedSymbolList[symbolIndex]
446
                            ## 현재 심볼과 검출된 심볼이 같을 경우 Match Point가 더 높은 정보로 교체
447
                            if symbolName == searchedSymbol.getName():
448
                                symbolMpCount = searchedSymbol.getMpCount()
449
                                if symbolMpCount < mpCount:
450
                                    threadLock.acquire()
451
                                    totalMpCount = totalMpCount - symbolMpCount + mpCount
452
                                    searchedSymbolList[symbolIndex] = symbol.Symbol(symId, symbolName, symbolType
453
                                                                                    , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, mpCount, symbolRotatedAngle
454
                                                                                    , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
455
                                                                                    , originalPoint, connectionPoint, baseSymbol, additionalSymbol)
456
                                    threadLock.release()
457
                            ## 현재 심볼과 검출된 심볼이 같지 않을 경우 (포함)
458
                            else:
459
                                ## 검출된 심볼 리스트 중에서 해당 영역에 같은 심볼이 있는지 여부 체크
460
                                matches = [sym for sym in searchedSymbolList if sym.getName() == symbolName and contains(sym, searchedItemSp, sw, sh) > ACCEPT_OVERLAY_AREA]
461

    
462
                                ## 현재 심볼과 검출된 심볼이 같을 경우 Match Point가 더 높은 정보로 교체
463
                                if len(matches) != 0:
464
                                    for s in matches:
465
                                        symbolMpCount = s.getMpCount()
466
                                        if symbolMpCount < mpCount:
467
                                            threadLock.acquire()
468
                                            totalMpCount = totalMpCount - symbolMpCount + mpCount
469
                                            searchedSymbolList[searchedSymbolList.index(s)] = symbol.Symbol(symId, symbolName, symbolType
470
                                                                                            , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, mpCount, symbolRotatedAngle
471
                                                                                            , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
472
                                                                                            , originalPoint, connectionPoint, baseSymbol, additionalSymbol)
473
                                            threadLock.release()
474
                                else:
475
                                    if mpCount >= symbolMinMatchCount:
476
                                        if searchedSymbol.getIsContainChild() == 1:
477
                                            ## 특정 카테고리 심볼을 걸러냄 (ex - 9900 대 Drum)
478
                                            if (searchedSymbol.getId() // 100) == (symId // 100):
479
                                                continue
480
                                            else:
481
                                                threadLock.acquire()
482
                                                foundSymbolCount = foundSymbolCount + 1
483
                                                totalMpCount = totalMpCount + mpCount
484
                                                addSearchedSymbol(symId, symbolName, symbolType
485
                                                                  , searchedItemSp, sw, sh, symbolThreshold, symbolMinMatchCount, mpCount, symbolRotatedAngle
486
                                                                  , isDetectOnOrigin, symbolRotateCount, symbolOcrOption, isContainChild
487
                                                                  , originalPoint, connectionPoint, baseSymbol, additionalSymbol)
488
                                                threadLock.release()
489
                                
490
                ## Rotate Symbol
491
                symGray = cv2.rotate(symGray, cv2.ROTATE_90_COUNTERCLOCKWISE)
492
                symbolRotatedAngle = symbolRotatedAngle + 90
493

    
494
                if additionalSymbol is not None:
495
                    additionalSymbol = getRotatedChildInfo(additionalSymbol)
496

    
497
        splitCount = splitCount // 2
498

    
499
    avgMpCount = 0
500
    if foundSymbolCount != 0:
501
        avgMpCount = totalMpCount / foundSymbolCount
502

    
503
    
504
    threadLock.acquire()
505
    srcName = os.path.splitext(os.path.basename(mainRes))[0]
506
    listWidget.addItem('finish symbol(count) / AvgMpCount : ' + symbolPath + '(' + str(foundSymbolCount) + ') / avgMpCount : ' + str(avgMpCount))
507
    f = open(os.path.dirname(os.path.realpath(__file__)) + "\\res\\Result\\result_"+srcName+"_600dpi.txt", 'a+')
508
    data = 'symbolName - (count) : ' + symbolPath + ' - (' + str(foundSymbolCount) + ') / avgMpCount : '+str(avgMpCount)+'\n'
509
    f.write(data)
510
    f.close()
511
    threadLock.release()
512

    
513
def removeDetectedSymbol(sym):
514
    global srcGray
515
    global ocrCompletedSrc
516
    global threadLock
517
    
518
    path = sym.getPath()
519
    sp = sym.getSp()
520
    sw = sym.getWidth()
521
    sh = sym.getHeight()
522
    sAngle = sym.getRotatedAngle()
523
    symImg = cv2.imread(path)
524
    symImg = cvtGrayImage(symImg)
525
    
526
    for i in range(sAngle//90):
527
        symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
528

    
529
    threadLock.acquire()
530
    temp = []
531
    temp = srcGray[sp[1]:sp[1]+sh, sp[0]:sp[0]+sw]
532
    tempBin = cv2.bitwise_not(temp)
533
    result = cv2.bitwise_xor(symImg, tempBin)
534
    kernel1 = np.ones((5, 5), np.uint8)
535
    result = cv2.dilate(result, kernel1)
536
    srcGray[sp[1]:sp[1]+sh, sp[0]:sp[0]+sw] = result
537
    ocrCompletedSrc[sp[1]:sp[1]+sh, sp[0]:sp[0]+sw] = result
538
    threadLock.release()
539

    
540
def drawRectOnSrc(sym):
541
    global src
542
    global srcGray
543
    global ocrCompletedSrc
544
    
545
    path = sym.getPath()
546
    sp = sym.getSp()
547
    sw = sym.getWidth()
548
    sh = sym.getHeight()
549
    sAngle = sym.getRotatedAngle()
550
    symImg = cv2.imread(path)
551
    symImg = cvtGrayImage(symImg)
552

    
553
    cv2.rectangle(src, sp, (sp[0]+sw, sp[1]+sh), (0, 0, 255), 2)
554

    
555
def drawFoundSymbols(symbol, listWidget):
556
    global src
557
    global canvas
558
    global WHITE_LIST_CHARS
559
    global searchedSymbolList
560

    
561
    symbolId = symbol.getId()
562
    symbolPath = symbol.getPath()
563
    symbolSp = symbol.getSp()
564
    symbolWidth = symbol.getWidth()
565
    symbolHeight = symbol.getHeight()
566
    symbolRotatedAngle = symbol.getRotatedAngle()
567
    symbolOcrOption = symbol.getOcrOption()
568

    
569
    symImg = cv2.imread(symbolPath, 1)
570
    for i in range(symbolRotatedAngle//90):
571
        symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
572

    
573
    chan, w, h = symImg.shape[::-1]
574
    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)
575

    
576
    if (symbolId // 100) == 99: ## Drum
577
        return    
578
    
579
    if symbolOcrOption == SymbolBase.OCR_OPTION_ALL_FIND or symbolOcrOption == SymbolBase.OCR_OPTION_HALF_AND_HALF:
580
        if symbolOcrOption == SymbolBase.OCR_OPTION_HALF_AND_HALF:
581
            inSqWidth = int(((w//2) * math.cos(round(math.radians(45), 2)))) * 2
582
            inSqHeight = int(((h//2) * math.sin(round(math.radians(45), 2)))) * 2
583
            inSqX = w//2 - (inSqWidth//2)
584
            inSqY = h//2 - (inSqHeight//2)
585
        else:
586
            inSqWidth = w
587
            inSqHeight = h
588
            inSqX = 0
589
            inSqY = 0
590
        
591
        roi = src[symbolSp[1]:symbolSp[1]+h, symbolSp[0]:symbolSp[0]+w]
592
        srcRoi = roi[inSqY:inSqY+inSqHeight, inSqX:inSqX+inSqWidth]
593
        symRev = cv2.bitwise_not(symImg[inSqY:inSqY+inSqHeight, inSqX:inSqX+inSqWidth])
594
        bitImg = cv2.bitwise_or(srcRoi, symRev)
595

    
596
        kernel1 = np.ones((2, 2), np.uint8)
597
        bitImg = cv2.dilate(bitImg, kernel1)
598
        #kernel2 = np.ones((1, 1), np.uint8)
599
        #bitImg = cv2.erode(bitImg, kernel2)
600

    
601
        bitImg = cv2.resize(bitImg, None, fx = 2.0, fy = 2.0)
602

    
603
        im = Image.fromarray(bitImg)
604

    
605
        ocrData = pytesseract.image_to_boxes(im, config='-c tessedit_char_whitelist="-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -psm 6')
606
        #ocrData = pytesseract.image_to_data(im)
607
        #ocrData = pytesseract.image_to_string(im, config = "hocr")
608
        listWidget.addItem("tesseract result : \n" + ocrData)
609

    
610
        ### For image_to_data()
611
        #if ocrData:
612
        #    splitOcrData = ocrData.split('\n')
613
        #    size = len(splitOcrData)
614
        #    i = 1
615
        #    while i < size:
616
        #        data = splitOcrData[i]
617
        #        threadLock.acquire()
618
        #        sData = data.split('\t')
619
        #        if len(sData) == 12:
620
        #            text = sData[11]
621
        #            tx = int(sData[6]) // 2
622
        #            ty = int(sData[7]) // 2
623
        #            tw = int(sData[8]) // 2
624
        #            th = int(sData[9]) // 2
625
        #            realSp = (symbolSp[0]+tx, symbolSp[1]+ty)
626
        #            cv2.putText(canvas, text, (realSp[0], realSp[1]+th), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
627
        #        threadLock.release()
628
        #        i = i+1
629

    
630
        ### For image_to_boxes()
631
        if ocrData:
632
            splitOcrData = ocrData.split('\n')
633
            tList = []
634
            lastCoord = (-1, -1) # Top-Right Coord
635
            tText = []
636
            ftSp = (-1, -1)
637
            threadLock.acquire()
638
            for data in splitOcrData:
639
                sData = data.split(' ')
640
                text = sData[0]
641
                tsx = int(sData[1]) // 2
642
                tsy = int(sData[2]) // 2
643
                tex = int(sData[3]) // 2
644
                tey = int(sData[4]) // 2
645
                tw = tex - tsx
646
                th = tey - tsy
647

    
648
                MIN_TEXT_SIZE = 10
649
                if WHITE_LIST_CHARS.find(text) >= 0:
650
                    if tw >= MIN_TEXT_SIZE or th >= MIN_TEXT_SIZE:
651
                        realTextSp = (-1, -1)
652
                        if symbolOcrOption == SymbolBase.OCR_OPTION_HALF_AND_HALF:
653
                            realTextSp = (symbolSp[0]+inSqX+tsx, symbolSp[1]+((h//2) - tsy + inSqY))
654
                        else:
655
                            realTextSp = (symbolSp[0]+inSqX+tsx, symbolSp[1]+tsy)
656
                        #cv2.rectangle(src, realTextSp, (realTextSp[0] + tw, realTextSp[1] + th), (0, 255, 255), 3)
657
                        removeText(srcGray, text, realTextSp[0], realTextSp[1], tw, th)
658
                        if lastCoord == (-1, -1):
659
                            tText.append(text)
660
                            ftSp = (tsx, tsy)
661
                        else:
662
                            COORD_ADJUSTMENT = 15
663
                            if (abs(lastCoord[1] - tsy) <= COORD_ADJUSTMENT and lastCoord[0] >= tsx - COORD_ADJUSTMENT and lastCoord[0] <= tsx + COORD_ADJUSTMENT) or (abs(lastCoord[0] - tsx) <= COORD_ADJUSTMENT and lastCoord[1] >= tsy - COORD_ADJUSTMENT and lastCoord[1] <= tsy + COORD_ADJUSTMENT):
664
                                tText.append(text)
665
                            else:
666
                                if symbolOcrOption == SymbolBase.OCR_OPTION_ALL_FIND or symbolOcrOption == SymbolBase.OCR_OPTION_HALF_AND_HALF:
667
                                    tText.append(',')
668
                                tText.append(text)
669
                        
670
                        lastCoord = (tex, tsy) # Top-Right Coord
671

    
672
            realSp = (symbolSp[0]+inSqX+ftSp[0], symbolSp[1]+inSqY+ftSp[1])
673
            realEp = (symbolSp[0]+inSqX+lastCoord[0], symbolSp[1]+inSqY+lastCoord[1])
674
            resultText = ''.join(tText)
675
            cv2.putText(canvas, resultText, (realSp[0], realSp[1]+th), 2, 1.0, (0, 0, 0)) # cv2.FONT_HERSHEY_SIMPLEX
676

    
677
            #textInfoList.append(ti.TextInfo(resultText, str(realSp[0]), str(realSp[1]), str(realEp[0]), str(realEp[1])))
678

    
679
            # text value in symbol object update
680
            index = [i for i, item in enumerate(searchedSymbolList) if item.getSp() == symbolSp]
681
            if len(index) > 0:
682
                searchedSymbolList[index[0]].setText(resultText)
683
            threadLock.release()
684

    
685

    
686
def drawFoundSymbolsOnCanvas(mainRes, width, height, listWidget):
687
    global src
688
    global srcGray
689
    global ocrCompletedSrc
690
    global searchedSymbolList
691
    global canvas
692
    global textInfoList
693

    
694
    canvas = np.zeros((height, width, 3), np.uint8)
695
    canvas[::] = (255, 255, 255)
696

    
697
    
698
    #pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
699
    for symbol in searchedSymbolList:
700
        #pool.submit(drawFoundSymbols, symbol)
701
        drawFoundSymbols(symbol, listWidget)
702
    #pool.shutdown(wait = True)
703

    
704
    for text in textInfoList:
705
        if not checkTextInSymbol((text.getX(), text.getY())):
706
            cv2.putText(canvas, text.getText(), (text.getX(), text.getY()+text.getH()), 2, 1.0, (0, 0, 0))
707

    
708
    srcName = os.path.splitext(os.path.basename(mainRes))[0]
709
    cv2.imwrite(os.path.dirname(os.path.realpath(__file__)) + '\\res\\Result\\result_moved_'+srcName+'_600dpi.png', canvas)
710

    
711

    
712
def drawTempLine(sym):
713
    ADJUST = 8
714
    symPath = sym.getPath()
715
    symSp = sym.getSp()
716
    symWidth = sym.getWidth()
717
    symHeight = sym.getHeight()
718
    rotatedAngle = sym.getRotatedAngle()
719

    
720
    symbol = cv2.imread(symPath, 1)
721
    _chan, sow, soh = symbol.shape[::-1]
722

    
723
    symCp = sym.getConnectionPoint()
724
    if symCp is not None:
725
        connectionPointList = symCp.split("/") # x,y string array
726
        if connectionPointList is not None and len(connectionPointList) > 0:
727
            symOpStr = sym.getOriginalPoint()
728
            symOp = (int(symOpStr.split(',')[0]), int(symOpStr.split(',')[1]))
729

    
730
            blank = np.zeros((symWidth, symHeight, 3), np.uint8)
731
            blank[::] = (255, 255, 255)
732
            blank = cvtGrayImage(blank)
733
            bw, bh = blank.shape[::-1]
734
            srcGray[symSp[1]:symSp[1]+bh, symSp[0]:symSp[0]+bw] = blank
735

    
736
            for cp in connectionPointList:
737
                pt = cp.split(",")
738
                cpx = int(pt[0])
739
                cpy = int(pt[1])
740
                convertedCp = getCoordOnRotatedImage(rotatedAngle, cpx, cpy, sow, soh)
741
                newSp = (symSp[0]+convertedCp[0], symSp[1]+convertedCp[1])
742
                newOp = (symSp[0]+symOp[0], symSp[1]+symOp[1])
743
                cv2.line(srcGray, newSp, newOp, (0, 0, 0), 3)
744

    
745

    
746
#Generate target symbol data list
747
def initTargetSymbolDataList():
748
    ############ region SQLite
749
    conn = sqlite3.connect(AppDocData.instance().getCurrentProject().getPath() + "/db/" + "ITI_PID.db")
750
    cursor = conn.cursor()
751
    sql = 'SELECT * FROM Symbol'
752
    cursor.execute(sql)
753
    rows = cursor.fetchall()
754
    dict = {}
755

    
756
    ## Init Symbol Data from SQLite
757
    for row in rows:
758
        symId = row[1] // 100 # symId
759
        sym = symbol.SymbolBase(row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[0]) ## uid is last item
760
        #sym = symbol.SymbolBase(row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[0]) ## uid is last item
761
        if symId in dict:
762
            dict[symId].append(sym)
763
        else:
764
            symGroup = []
765
            symGroup.append(sym)
766
            dict[symId] = symGroup
767
    conn.close()
768

    
769
    ## Sort each symbol list by symbol id
770
    for k, v in dict.items():
771
        dict[k] = sorted(v, key=lambda symbol:symbol.id)
772

    
773
    ## Insert each symbol list into targetSymbolList
774
    for sym in list(dict.values()):
775
        targetSymbolList.append(sym)
776
    ############ endregion SQLite
777

    
778
    return targetSymbolList
779

    
780
'''
781
    @brief      read image drawing and then remove text
782
    @author     jwkim
783
    @date       
784
    @history    humkyung 2018.04.06 check if file exists
785
'''
786
def initMainSrc(mainRes):
787
    global src
788
    global srcGray
789
    global ocrCompletedSrc
790
    global textInfoList
791

    
792
    try:
793
        if os.path.isfile(mainRes):
794
            #load original src & symbol
795
            src = cv2.imread(mainRes, 1)
796
                
797
            #gray scale
798
            if len(src.shape) == 3:
799
                srcGray = cvtGrayImage(src)
800
            else:
801
                srcGray = src.copy()
802

    
803
            (ocrCompletedSrc, textInfoList) = OCR.removeTextFromNpArray(srcGray)
804
            for textInfo in textInfoList:
805
                removeText(ocrCompletedSrc, textInfo.getText(), textInfo.getX(), textInfo.getY(), textInfo.getW(), textInfo.getH())
806
            
807
            area = AppDocData.instance().getArea('Drawing')
808
            if area is not None:
809
                area.img = srcGray[int(area.y):int(area.y+area.height), int(area.x):int(area.x+area.width)]
810
    except Exception as ex:
811
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
812

    
813
'''
814
    @brief      Main function
815
    @author     jwkim
816
    @date
817
    @history    humkyung 2018.04.06 change error display from message box to print
818
'''
819
def executeRecognition(path, listWidget):
820
    global src
821
    global srcGray
822
    global ocrCompletedSrc
823
    global searchedSymbolList
824
    global threadLock
825
    global textInfoList
826
    
827
    res = []
828
    try:
829
        project = AppDocData.instance().getCurrentProject()
830

    
831
        srcList = []
832
        srcList.append(path)
833

    
834
        initTargetSymbolDataList()
835

    
836
        for mainRes in srcList:
837
            #Init src
838
            src = []
839
            srcGray = []
840
            ocrCompletedSrc = []
841
            searchedSymbolList = []
842
            textInfoList = []
843

    
844
            start = timeit.default_timer()
845

    
846
            initMainSrc(mainRes)
847

    
848
            listWidget.addItem("Start recognition : " + mainRes)
849
            
850
            #threadLock = threading.Lock()
851
            pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
852
            for targetItem in targetSymbolList:
853
                if type(targetItem).__name__ == 'list':
854
                    #detectSymbolsOnPid(mainRes, target)
855
                    pool.submit(detectSymbolsOnPid, mainRes, targetItem, listWidget)
856
                else:
857
                    #detectSymbolOnPid(mainRes, target)
858
                    pool.submit(detectSymbolOnPid, mainRes, targetItem, listWidget)
859

    
860
            pool.shutdown(wait = True)
861

    
862
            chan, srcWidth, srcHeight = src.shape[::-1]
863
            drawFoundSymbolsOnCanvas(mainRes, srcWidth, srcHeight, listWidget)
864
            
865
            srcName = os.path.splitext(os.path.basename(mainRes))[0]
866

    
867
            pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
868
            for sym in searchedSymbolList:
869
                #threadLock.acquire()
870
                pool.submit(removeDetectedSymbol, sym)
871
                pool.submit(drawRectOnSrc, sym)
872
                #drawRectOnSrc()
873
                #threadLock.release()
874
            pool.shutdown(wait = True)
875
            
876
            (srcGray, tInfoList) = OCR.removeTextFromNpArray(srcGray)
877
            #srcGray = TOCR.removeTextFromNpArray(srcGray, TOCR.FLAG_IMAGE_TO_DATA)
878

    
879
            for textInfo in textInfoList:
880
                #if not checkTextInSymbol((textInfo.getX(), textInfo.getY())):
881
                removeText(srcGray, textInfo.getText(), textInfo.getX(), textInfo.getY(), textInfo.getW(), textInfo.getH())
882

    
883
            ## Remove Noise
884
            kernel1 = np.ones((2, 2), np.uint8)
885
            srcGray = cv2.dilate(srcGray, kernel1)
886
            #kernel2 = np.ones((4, 4), np.uint8)
887
            srcGray = cv2.erode(srcGray, kernel1)
888

    
889
            #for sym in searchedSymbolList:
890
            #    drawTempLine(sym)
891

    
892
            removedSymbolImgPath = project.getTempPath() + '/' + os.path.basename(path)
893
            cv2.imwrite(removedSymbolImgPath, srcGray)
894
            area = AppDocData.instance().getArea('Drawing')
895
            if area is not None:
896
                area.img = srcGray[int(area.y):int(area.y+area.height), int(area.x):int(area.x+area.width)]
897
            cv2.imwrite(project.getTempPath() + "/rect_" + os.path.basename(path), src)
898

    
899
            imgLineList = []
900

    
901
            xmlPath = xg.writeXml(srcName, srcWidth, srcHeight, searchedSymbolList, textInfoList, imgLineList)
902
            res.append(xmlPath)
903
            
904
            listWidget.addItem("Searched symbol count : " + str(len(searchedSymbolList)))
905

    
906
            stop = timeit.default_timer()    
907
            seconds = stop - start
908
            f = open(os.path.dirname(os.path.realpath(__file__)) + "\\res\\Result\\result_"+srcName+"_600dpi.txt", 'a+')
909
            data = "Running Time : " + str(seconds / 60) + "min\n"
910
            f.write(data)
911
            f.close()
912

    
913
            listWidget.addItem("\nRunning Time : " + str(seconds / 60) + "min\n")
914
    except Exception as ex:
915
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
916
    
917
    return res
918
    
919
if __name__ == '__main__':
920
    import DTI_PID_UI
921
    from ProjectDialog import Ui_Dialog
922

    
923
    app = QApplication(sys.argv)
924

    
925
    try:
926
        dlg = Ui_Dialog()
927
        dlg.show()
928
        dlg.exec_()
929
        if dlg.selectedProject is not None:
930
            form = ExampleApp()
931
            form.show()
932
    except Exception as ex:
933
        print('에러가 발생했습니다.\n', ex)
934

    
935
    sys.exit(app.exec_())