프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / DTI_PID.py @ 47bd6b4e

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

1
#region import libs
2
import http.client
3
import urllib, base64, json
4
import cv2
5
import numpy as np
6
import SymbolBase
7
import symbol
8
import TextInfo as ti
9
import azure_ocr_module as OCR
10
from PIL import Image
11
from io import BytesIO
12
import gc
13
import os
14
import glob
15
import timeit
16
import math, operator
17
import threading
18
import concurrent.futures as futures
19
import XmlGenerator as xg
20
import pytesseract
21
import tesseract_ocr_module as TOCR
22
import potrace
23
import sys
24
from PyQt5.QtCore import *
25
from PyQt5.QtGui import *
26
from PyQt5.QtWidgets import *
27
from PyQt5.QtSvg import *
28
import DTI_PID_UI
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.environ['TESSERACT_HOME'] + '\\tesseract.exe'
45
tesseract_cmd = 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
#DB_NAME = "db/DTI_PID_test.db"
65
#DB_NAME = AppDocData.instance().getCurrentProject().getPath() + "/db/" + "DTI_PID.db"
66
#DB_NAME = os.path.dirname(os.path.realpath(__file__)) + "\\db\\DTI_PID.db"
67

    
68
THREAD_MAX_WORKER = 4
69
threadLock = threading.Lock()
70

    
71
ACCEPT_OVERLAY_AREA = 10
72
#endregion
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 categoryCode != 99 and (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
def removeText(img, text, x, y, width, height):
95
    x = int(x)
96
    y = int(y)
97
    width = int(width)
98
    height = int(height)
99
    roi = img[y:y+height, x:x+width]
100
    temp = roi.copy()
101
    tempBin = cv2.bitwise_not(temp)
102
    img[y:y+height, x:x+width] = cv2.bitwise_xor(roi, tempBin)
103
    return img
104

    
105
#Convert into Grayscale image
106
def cvtGrayImage(img):
107
    return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
108

    
109
'''
110
    @brief      rotate (x,y) by given angle
111
    @author     Jeongwoo
112
    @date       2018.??.??
113
    @history    humkyung 2018.04.13 fixed code when angle is 90 or 270    
114
                Jeongwoo 2018.04.27 Change calculation method with QTransform
115
'''
116
def getCoordOnRotatedImage(rAngle, x, y, originImageWidth, originImageHeight):
117
    rx = None
118
    ry = None
119
    transform = QTransform()
120
    if rAngle == 90 or rAngle == 270:
121
        transform.translate(originImageHeight*0.5, originImageWidth*0.5)
122
    elif rAngle == 0 or rAngle == 180:
123
        transform.translate(originImageWidth*0.5, originImageHeight*0.5)
124
    transform.rotate(-abs(rAngle))
125
    transform.translate(-originImageWidth*0.5, -originImageHeight*0.5)
126
    point = QPoint(x, y)
127
    point = transform.map(point)
128
    rx = point.x()
129
    ry = point.y()
130
    return (rx, ry)
131

    
132
def convertDirectionCodeToValue(directionCode):
133
    if directionCode == "UP":
134
        return 0
135
    elif directionCode == "RIGHT":
136
        return 1
137
    elif directionCode == "DOWN":
138
        return 2
139
    elif directionCode == "LEFT":
140
        return 3
141
    else:
142
        return -1
143

    
144
def convertValueToDirectionCode(value):
145
    if value == 0:
146
        return "UP"
147
    elif value == 1:
148
        return "RIGHT"
149
    elif value == 2:
150
        return "DOWN"
151
    elif value == 3:
152
        return "LEFT"
153
    else:
154
        return "NONE"
155

    
156
def getRotatedChildInfo(additionalSymbol):
157
    tempChildInfo = ""
158
    if additionalSymbol:
159
        childList = additionalSymbol.split("/")
160
        for index in range(len(childList)):
161
            child = childList[index]
162
            direction = convertDirectionCodeToValue(child.split(",")[0])
163
            childName = child.split(",")[1]
164
            direction = (direction - 1) if direction > 0 else 3
165
            if index != 0:
166
                tempChildInfo = tempChildInfo + "/"
167
            tempChildInfo = tempChildInfo + convertValueToDirectionCode(direction) + "," + childName
168
    return tempChildInfo
169

    
170

    
171
#Check object contains pt
172
#obj is item in searchedSymbolList
173
def contains(obj, pt, tw, th):
174
    sp = obj.getSp()
175
    width = obj.getWidth()
176
    height = obj.getHeight()
177

    
178
    if sp[0] > pt[0]+tw:
179
        return 0
180
    if sp[0]+width < pt[0]: 
181
        return 0
182
    if sp[1] > pt[1]+th: 
183
        return 0
184
    if sp[1]+height < pt[1]: 
185
        return 0
186
    
187
    #shared area
188
    x = max(sp[0], pt[0]);
189
    y = max(sp[1], pt[1]);
190
    w = min(sp[0] + width, pt[0] + tw) - x;
191
    h = min(sp[1] + height, pt[1] + th) - y;
192

    
193
    return float((w * h)) / float((tw * th)) * 100
194

    
195
def getSplitSrcList(srcPid, splitCount, splitWidth, splitHeight):
196
    splitRoiList = []
197
    for hi in range(splitCount):
198
        for wi in range(splitCount):
199
            roiSp = (splitWidth*wi, splitHeight*hi)
200
            roiEp = (splitWidth*(wi+1), splitHeight*(hi+1))
201
            splitRoiList.append((roiSp, roiEp, srcPid[roiSp[1]:roiEp[1], roiSp[0]:roiEp[0]]))
202
    return splitRoiList
203

    
204
def getCalculatedOriginalPoint(additionalSymbol, symbolOriginalPoint, symbolRotatedAngle, rotateSymbolWidth, rotateSymbolHeight, originalSymbolWidth, originalSymbolHeight):
205
    originalPoint = ''
206
    if additionalSymbol is None and symbolOriginalPoint is None:
207
        originalPoint = str(rotateSymbolWidth//2)+','+str(rotateSymbolHeight//2)
208
    else:
209
        opx = int(symbolOriginalPoint.split(',')[0])
210
        opy = int(symbolOriginalPoint.split(',')[1])
211
        rPt = getCoordOnRotatedImage(symbolRotatedAngle, opx, opy, originalSymbolWidth, originalSymbolHeight)
212
        originalPoint = str(int(rPt[0])) + ',' + str(int(rPt[1]))
213
    return originalPoint
214

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

    
256

    
257
#Calculate count of keypoint match result
258
def getMatchPointCount(src, cmp):
259
    orb = cv2.ORB_create(1000, 2.0, 2, 1)
260

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

    
290
    matchCount = count
291

    
292
    #print("match Count : " + str(matchCount))
293
    return matchCount
294

    
295

    
296
#detect symbols on PID
297
def detectSymbolsOnPid(mainRes, targetSymbols, listWidget):
298
    for detailTarget in targetSymbols:
299
        detectSymbolOnPid(mainRes, detailTarget, listWidget)
300

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

    
331
    if isExceptDetect == 1:
332
        item = QListWidgetItem('{} file is not target'.format(os.path.basename(symbolPath.replace('.png', ''))))
333
        item.setBackground(QColor('green'))
334
        listWidget.addItem(item)
335
        return
336

    
337
    foundSymbolCount = 0
338

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

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

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

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

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

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

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

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

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

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

    
410

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

    
416
                    searchedItemSp = (roiItemSp[0]+pt[0] + round(offsetDrawingArea[0]) + 1, roiItemSp[1]+pt[1] + round(offsetDrawingArea[1]) + 1)
417

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

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

    
428

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

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

    
492
                if additionalSymbol is not None:
493
                    additionalSymbol = getRotatedChildInfo(additionalSymbol)
494

    
495
        splitCount = splitCount // 2
496

    
497
    avgMpCount = 0
498
    if foundSymbolCount != 0:
499
        avgMpCount = totalMpCount / foundSymbolCount
500

    
501
    
502
    threadLock.acquire()
503
    srcName = os.path.splitext(os.path.basename(mainRes))[0]    
504
    listWidget.addItem('Finish Symbol   : ' + os.path.basename(symbolPath.replace('.png', '')) + ' - (' + str(foundSymbolCount) + ')')
505
    threadLock.release()
506

    
507
'''
508
    @history    2018.05.17  Jeongwoo    Bitwise_not target changed (Original Image → Symbol Image)
509
'''
510
def removeDetectedSymbol(sym):
511
    global srcGray
512
    global ocrCompletedSrc
513
    global threadLock
514
    
515
    path = sym.getPath()
516
    sp = sym.getSp()
517
    sw = sym.getWidth()
518
    sh = sym.getHeight()
519
    sAngle = sym.getRotatedAngle()
520
    symImg = cv2.imread(path)
521
    symImg = cvtGrayImage(symImg)
522
    
523
    for i in range(sAngle//90):
524
        symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
525

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

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

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

    
552
'''
553
    @history    2018.04.27  Jeongwoo    Remove Tesseract Log on listWidget
554
                2018.05.04  Jeongwoo    Change method to OCR with tesseract_ocr_module.py
555
                2018.05.09  Jeongwoo    Add global variable textInfoList, Remove text in symbol and Add tesseract result text
556
                2018.05.10  Jeongwoo    Remove not used if-statement
557
'''
558
def drawFoundSymbols(symbol, listWidget):
559
    global src
560
    global canvas
561
    global WHITE_LIST_CHARS
562
    global searchedSymbolList
563
    global textInfoList
564

    
565
    symbolId = symbol.getId()
566
    symbolPath = symbol.getPath()
567
    symbolSp = symbol.getSp()
568
    symbolWidth = symbol.getWidth()
569
    symbolHeight = symbol.getHeight()
570
    symbolRotatedAngle = symbol.getRotatedAngle()
571
    symbolOcrOption = symbol.getOcrOption()
572

    
573
    symImg = cv2.imread(symbolPath, 1)
574
    for i in range(symbolRotatedAngle//90):
575
        symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
576

    
577
    chan, w, h = symImg.shape[::-1]
578
    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)
579
    
580
    if symbolOcrOption == SymbolBase.OCR_OPTION_ALL_FIND or symbolOcrOption == SymbolBase.OCR_OPTION_HALF_AND_HALF:
581
        if symbolOcrOption == SymbolBase.OCR_OPTION_HALF_AND_HALF:
582
            inSqWidth = int(((w//2) * math.cos(round(math.radians(45), 2)))) * 2
583
            inSqHeight = int(((h//2) * math.sin(round(math.radians(45), 2)))) * 2
584
            inSqX = w//2 - (inSqWidth//2)
585
            inSqY = h//2 - (inSqHeight//2)
586
        else:
587
            inSqWidth = w
588
            inSqHeight = h
589
            inSqX = 0
590
            inSqY = 0
591
        
592
        roi = src[symbolSp[1]:symbolSp[1]+h, symbolSp[0]:symbolSp[0]+w]
593
        srcRoi = roi[inSqY:inSqY+inSqHeight, inSqX:inSqX+inSqWidth]
594
        symRev = cv2.bitwise_not(symImg[inSqY:inSqY+inSqHeight, inSqX:inSqX+inSqWidth])
595
        bitImg = cv2.bitwise_or(srcRoi, symRev)
596

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

    
602
        try:
603
            threadLock.acquire()
604
            im = Image.fromarray(bitImg)
605
            sp = (0, 0)
606
            if symbolOcrOption == SymbolBase.OCR_OPTION_HALF_AND_HALF:
607
                sp = (symbolSp[0]+inSqX, symbolSp[1]+ inSqY)
608
            else:
609
                sp = (symbolSp[0]+inSqX, symbolSp[1] + inSqY)
610
            tList = TOCR.getTextInfoInSymbol(bitImg, sp)
611

    
612
            resultText = ''
613
            if tList is not None:
614
                for index in range(len(tList)):
615
                    textInfo = tList[index]
616
                    if index != 0:
617
                        resultText = resultText + ","
618
                    resultText = resultText + textInfo.getText()
619
                    cv2.putText(canvas, textInfo.getText(), (textInfo.getX(), textInfo.getY()), 2, 1.0, (0, 0, 0)) # cv2.FONT_HERSHEY_SIMPLEX
620

    
621
                # Sub List textInfo in symbol
622
                tempList = []
623
                for textInfo in textInfoList:
624
                    tx = textInfo.getX()
625
                    ty = textInfo.getY()
626
                    if (tx >= symbolSp[0] and tx <= symbolSp[0] + symbolWidth) and (ty >= symbolSp[1] and ty <= symbolSp[1] + symbolHeight):
627
                        tempList.append(textInfo)
628

    
629
                # Remove textInfo
630
                for textInfo in tempList:
631
                    textInfoList.remove(textInfo)
632

    
633
                # Append Tesseract result
634
                textInfoList.extend(tList)
635
                
636
                # text value in symbol object update
637
                index = [i for i, item in enumerate(searchedSymbolList) if item.getSp() == symbolSp]
638
                if len(index) > 0:
639
                    searchedSymbolList[index[0]].setText(resultText)
640
        except Exception as ex:
641
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
642
        finally:
643
            threadLock.release()
644

    
645

    
646
def drawFoundSymbolsOnCanvas(mainRes, width, height, listWidget):
647
    global src
648
    global srcGray
649
    global ocrCompletedSrc
650
    global searchedSymbolList
651
    global canvas
652
    global textInfoList
653

    
654
    canvas = np.zeros((height, width, 3), np.uint8)
655
    canvas[::] = (255, 255, 255)
656

    
657
    try:
658
        #pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
659
        for symbol in searchedSymbolList:
660
            #pool.submit(drawFoundSymbols, symbol)
661
            drawFoundSymbols(symbol, listWidget)
662
        #pool.shutdown(wait = True)
663

    
664
        for text in textInfoList:
665
            if not checkTextInSymbol((text.getX(), text.getY())):
666
                cv2.putText(canvas, text.getText(), (text.getX(), text.getY()+text.getH()), 2, 1.0, (0, 0, 0))
667

    
668
        srcName = os.path.splitext(os.path.basename(mainRes))[0]
669
        cv2.imwrite(os.path.dirname(os.path.realpath(__file__)) + '\\res\\Result\\result_moved_'+srcName+'_600dpi.png', canvas)
670
    
671
    except Exception as ex:
672
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
673
        
674
#Generate target symbol data list
675
'''
676
    @history    2018.04.24  Jeongwoo    Add isExceptDetect Field
677
                2018.05.09  Jeongwoo    Add targetSymbolList clear
678
'''
679
def initTargetSymbolDataList():
680
    ############ region SQLite
681
    global targetSymbolList
682
    targetSymbolList.clear()
683
    dict = {}
684
    tempTargetList = AppDocData.instance().getTargetSymbolList()
685

    
686
    ## Init Symbol Data from SQLite
687
    for target in tempTargetList:
688
        symId = target.getId() // 100 # symId
689
        if symId in dict:
690
            dict[symId].append(target)
691
        else:
692
            symGroup = []
693
            symGroup.append(target)
694
            dict[symId] = symGroup
695

    
696
    ## Sort each symbol list by symbol id
697
    for k, v in dict.items():
698
        dict[k] = sorted(v, key=lambda symbol:symbol.id)
699

    
700
    ## Insert each symbol list into targetSymbolList
701
    for sym in list(dict.values()):
702
        targetSymbolList.append(sym)
703
    ############ endregion SQLite
704

    
705
    return targetSymbolList
706

    
707
'''
708
    @brief      read image drawing and then remove text
709
    @author     jwkim
710
    @date       
711
    @history    humkyung 2018.04.06 check if file exists
712
                Jeongwoo 2018.05.09 Use Tesseract OCR after Azure OCR (Azure OCR : Getting text area)
713
                Jeongwoo 2018.05.25 Add condition on if-statemen
714
'''
715
def initMainSrc(mainRes):
716
    global src
717
    global srcGray
718
    global ocrCompletedSrc
719
    global textInfoList
720
    global noteTextInfoList
721

    
722
    try:
723
        if os.path.isfile(mainRes):
724
            #load original src & symbol
725
            src = cv2.imread(mainRes, 1)
726
                
727
            #gray scale
728
            if len(src.shape) == 3:
729
                srcGray = cvtGrayImage(src)
730
            else:
731
                srcGray = src.copy()
732
            srcGray = cv2.threshold(srcGray, 127, 255, cv2.THRESH_BINARY)[1]
733
            
734
            ocrCompletedSrc = srcGray.copy()
735

    
736
            area = AppDocData.instance().getArea('Drawing')
737
            if area is not None:
738
                #TODO: 영역을 설정한 값과 이미지 좌표계를 차이를 보정
739
                area.img = srcGray[round(area.y+1):round(area.y+area.height), round(area.x+1):round(area.x+area.width)]
740
                
741
            #(_tempOcrSrc, textInfoList) = OCR.removeTextFromNpArray(area.img if area is not None else srcGray, area.x if area is not None else 0, area.y if area is not None else 0)
742
            (_tempOcrSrc, tInfoList) = OCR.removeTextFromNpArray(area.img if area is not None else srcGray, area.x if area is not None else 0, area.y if area is not None else 0)
743
            
744
            global MIN_TEXT_SIZE
745
            for tInfo in tInfoList:
746
                if tInfo.getW() >= MIN_TEXT_SIZE or tInfo.getH() >= MIN_TEXT_SIZE:
747
                    resultTextInfo = TOCR.getTextInfo(ocrCompletedSrc[tInfo.getY():tInfo.getY()+tInfo.getH(),tInfo.getX():tInfo.getX()+tInfo.getW()], (tInfo.getX(), tInfo.getY()))
748
                    if resultTextInfo is not None and len(resultTextInfo) > 0:
749
                        textInfoList.extend(resultTextInfo)
750
                        ocrCompletedSrc = removeText(ocrCompletedSrc, resultTextInfo[0].getText(), resultTextInfo[0].getX(), resultTextInfo[0].getY(), resultTextInfo[0].getW(), resultTextInfo[0].getH())
751
                    else:
752
                        print(tInfo.getText())
753
            #global MIN_TEXT_SIZE
754
            #for textInfo in textInfoList:
755
            #    if textInfo.getW() >= MIN_TEXT_SIZE or textInfo.getH() >= MIN_TEXT_SIZE:
756
            #        ocrCompletedSrc = removeText(ocrCompletedSrc, textInfo.getText(), textInfo.getX(), textInfo.getY(), textInfo.getW(), textInfo.getH())
757

    
758
            noteArea = AppDocData.instance().getArea('Note')
759
            if noteArea is not None:
760
                noteArea.img = srcGray[round(noteArea.y-1):round(noteArea.y+noteArea.height-1), round(noteArea.x-1):round(noteArea.x+noteArea.width-1)]
761
                #(_tempNoteOcrSrc, noteTextInfoList) = OCR.removeTextFromNpArray(noteArea.img, noteArea.x, noteArea.y)
762
                noteTextInfoList = TOCR.getTextInfo(noteArea.img, (noteArea.x, noteArea.y))
763
    except Exception as ex:
764
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
765

    
766
'''
767
    @brief      Main function
768
    @author     jwkim
769
    @date
770
    @history    humkyung 2018.04.06 change error display from message box to print
771
                Jeongwoo 2018.04.25 Remove 'Current Symbol : ' QListItem
772
                Jeongwoo 2018.05.09 Make Comments OCR.removeTextFromNpArray block
773
                Jeongwoo 2018.05.25 Remove imgLineList variable and parameter on writeXml()
774
'''
775
def executeRecognition(path, listWidget):
776
    global src
777
    global srcGray
778
    global ocrCompletedSrc
779
    global searchedSymbolList
780
    global threadLock
781
    global textInfoList
782
    global noteTextInfoList
783
    
784
    res = []
785
    try:
786
        docData = AppDocData.instance()
787
        project = docData.getCurrentProject()
788

    
789
        srcList = []
790
        srcList.append(path)
791

    
792
        initTargetSymbolDataList()
793

    
794
        for mainRes in srcList:
795
            #Init src
796
            src = []
797
            srcGray = []
798
            ocrCompletedSrc = []
799
            searchedSymbolList = []
800
            textInfoList = []
801

    
802
            #start = timeit.default_timer()
803

    
804
            initMainSrc(mainRes)
805

    
806
            listWidget.addItem("Start recognition : " + mainRes)
807
            
808
            #threadLock = threading.Lock()
809
            pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
810
            for targetItem in targetSymbolList:
811
                if type(targetItem).__name__ == 'list':
812
                    #detectSymbolsOnPid(mainRes, target)
813
                    pool.submit(detectSymbolsOnPid, mainRes, targetItem, listWidget)
814
                else:
815
                    #detectSymbolOnPid(mainRes, target)
816
                    pool.submit(detectSymbolOnPid, mainRes, targetItem, listWidget)
817

    
818
            pool.shutdown(wait = True)
819

    
820
            chan, docData.imgWidth, docData.imgHeight = src.shape[::-1]
821
            drawFoundSymbolsOnCanvas(mainRes, docData.imgWidth, docData.imgHeight, listWidget)
822
            
823
            docData.imgName = os.path.splitext(os.path.basename(mainRes))[0]
824

    
825
            pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
826
            for sym in searchedSymbolList:
827
                #threadLock.acquire()
828
                pool.submit(removeDetectedSymbol, sym)
829
                pool.submit(drawRectOnSrc, sym)
830
                #drawRectOnSrc()
831
                #threadLock.release()
832
            pool.shutdown(wait = True)
833
            
834
            ####area = AppDocData.instance().getArea('Drawing')
835
            ####(_tempOcrSrc, tInfoList) = OCR.removeTextFromNpArray(area.img if area is not None else srcGray, area.x if area is not None else 0, area.y if area is not None else 0)
836
            #####(srcGray, tInfoList) = OCR.removeTextFromNpArray(area.img if area is not None else srcGray)
837
            ####if area is not None:
838
            ####    srcGray[int(area.y):int(area.y+area.height), int(area.x):int(area.x+area.width)] = _tempOcrSrc
839
            ####else:
840
            ####    srcGray = _tempOcrSrc
841
            ####srcGray = TOCR.removeTextFromNpArray(srcGray, TOCR.FLAG_IMAGE_TO_DATA)
842
            global MIN_TEXT_SIZE
843
            for textInfo in textInfoList:
844
                #if not checkTextInSymbol((textInfo.getX(), textInfo.getY())):
845
                if textInfo.getW() >= MIN_TEXT_SIZE or textInfo.getH() >= MIN_TEXT_SIZE:
846
                    removeText(srcGray, textInfo.getText(), textInfo.getX(), textInfo.getY(), textInfo.getW(), textInfo.getH())
847

    
848
            ## Remove Noise
849
            kernel1 = np.ones((2, 2), np.uint8)
850
            srcGray = cv2.dilate(srcGray, kernel1)
851
            #kernel2 = np.ones((4, 4), np.uint8)
852
            srcGray = cv2.erode(srcGray, kernel1)
853

    
854
            removedSymbolImgPath = os.path.join(project.getTempPath(), os.path.basename(path))
855
            cv2.imwrite(removedSymbolImgPath, srcGray)
856
            area = AppDocData.instance().getArea('Drawing')
857
            if area is not None:
858
                area.img = srcGray[round(area.y+1):round(area.y+area.height), round(area.x+1):round(area.x+area.width)]
859
            cv2.imwrite(os.path.join(project.getTempPath(), "rect_" + os.path.basename(path)), src)
860
            
861
            xmlPath = xg.writeXml(docData.imgName, docData.imgWidth, docData.imgHeight, searchedSymbolList, textInfoList, noteTextInfoList)
862
            res.append(xmlPath)
863
            
864
            listWidget.addItem("Searched symbol count : " + str(len(searchedSymbolList)))
865

    
866
            #stop = timeit.default_timer()    
867
            #seconds = stop - start
868

    
869
            #listWidget.addItem("\nRunning Time : " + str(seconds / 60) + "min\n")
870
    except Exception as ex:
871
        print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
872
    
873
    return res
874
    
875
if __name__ == '__main__':
876
    import DTI_PID_UI
877
    from ProjectDialog import Ui_Dialog
878

    
879
    app = QApplication(sys.argv)
880

    
881
    try:
882
        dlg = Ui_Dialog()
883
        selectedProject = dlg.showDialog()
884
        if selectedProject is not None:
885
            form = ExampleApp()
886
            form.show()
887
    except Exception as ex:
888
        print('에러가 발생했습니다.\n', ex)
889

    
890
    sys.exit(app.exec_())
클립보드 이미지 추가 (최대 크기: 500 MB)