프로젝트

일반

사용자정보

개정판 2ffe1307

ID2ffe1307acfc8d0f34b0ba81ccebecfcae1df734
상위 aef56f4a
하위 a655d25c

백흠경이(가) 6년 이상 전에 추가함

fixed issue #503:
- fixed connection point error when resize area

차이점 보기:

DTI_PID/DTI_PID/ConfigurationAreaDialog.py
1 1
# coding: utf-8
2
"""
3
    This is area configuration module
4
"""
5

  
2 6
import os
3 7
import sys
4 8
from PyQt5.QtCore import *
DTI_PID/DTI_PID/DTI_PID.py
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 sys
24
from PyQt5.QtCore import *
25
from PyQt5.QtGui import *
26
from PyQt5.QtWidgets import *
27
from PyQt5.QtSvg import *
28
import QtImageViewer
29
from MainWindow import MainWindow
30
from AppDocData import AppDocData
31

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

  
36
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Shapes')
37
from EngineeringPolylineItem import QEngineeringPolylineItem
38
from EngineeringLineItem import QEngineeringLineItem
39
from SymbolSvgItem import SymbolSvgItem
40
from QGraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
41
from AppDocData import *
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
    return matchCount
280

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

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

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

  
329
        foundSymbolCount = 0
330

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

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

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

  
362
        roiItemSp = (0,0)
363
        roiItemEp = (srcWidth, srcHeight)
364
        roiItem = copiedBasePid
365

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

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

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

  
385
            ## Template Matching
386
            tmRes = cv2.matchTemplate(roiItem, symGray, cv2.TM_CCOEFF_NORMED)
387
            loc = np.where(tmRes >= symbolThreshold)
388

  
389
            for pt in zip(*loc[::-1]):
390
                mpCount = 0 # Match Point Count
391

  
392
                roi = roiItem[pt[1]:pt[1]+sh, pt[0]:pt[0]+sw]
393

  
394
                if symbolMinMatchCount > 0:
395
                    mpCount = getMatchPointCount(roi, symGray)
396
                    if not (mpCount >= symbolMinMatchCount):
397
                        continue
398

  
399
                searchedItemSp = (roiItemSp[0]+pt[0] + round(offsetDrawingArea[0]), roiItemSp[1]+pt[1] + round(offsetDrawingArea[1]))
400
                
401
                overlapArea = 0
402
                symbolIndex = -1
403
                for i in range(len(searchedSymbolList)):
404
                    '''
405
                    _pt = searchedSymbolList[i].getSp()
406
                    rect = QRectF(_pt[0], _pt[1], searchedSymbolList[i].getWidth(), searchedSymbolList[i].getHeight())
407
                    _rect = QRectF(searchedItemSp[0], searchedItemSp[1], sw, sh)
408
                    if rect.intersects(_rect):
409
                        intersect = rect.intersected(_rect)
410
                        overlapArea = intersect.width()*intersect.height()
411
                        if overlapArea > sw*sh*0.1:
412
                            symbolIndex = i
413
                            break
414
                    '''
415
                    overlapArea = contains(searchedSymbolList[i], searchedItemSp, sw, sh)
416
                    if overlapArea > ACCEPT_OVERLAY_AREA:
417
                        categories = [appDocData.isEquipmentType(symbolType), appDocData.isEquipmentType(searchedSymbolList[i].getType())]
418
                        if categories[0] == categories[1]:
419
                            symbolIndex = i
420
                            break
421
                        
422
                hitRate = tmRes[pt[1], pt[0]]
423

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

  
450
                                threadLock.release()
451
                        ## 현재 심볼과 검출된 심볼이 같지 않을 경우 (포함)
452
                        elif appDocData.isEquipmentType(searchedSymbol.getType()):
453
                            ## DEBUG
454
                            message = '{}->{}:{}-{}'.format(searchedSymbol.getName(), symbolName, searchedItemSp, hitRate)
455
                            worker.displayLog.emit(MessageType.Normal, message)
456
                            ## up to here
457

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

  
470
            if additionalSymbol is not None:
471
                additionalSymbol = getRotatedChildInfo(additionalSymbol)
472

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

  
477
        worker.updateProgress.emit(maxProgressValue)
478

  
479
        return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
480
    except Exception as ex:
481
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
482
        worker.displayLog.emit(MessageType.Error, message)
483
    
484
    return []
485

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

  
514
        foundSymbolCount = 0
515

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

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

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

  
541
        roiItemSp = (0,0)
542
        roiItemEp = (srcWidth, srcHeight)
543
        roiItem = img 
544

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

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

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

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

  
565
                roi = roiItem[pt[1]:pt[1]+sh, pt[0]:pt[0]+sw]
566

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

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

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

  
641
            if additionalSymbol is not None:
642
                additionalSymbol = getRotatedChildInfo(additionalSymbol)
643

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

  
648
        worker.updateProgress.emit(maxProgressValue)
649

  
650
        return [symbol for symbol in searchedSymbolList if symbol.getName() == symbolName]
651
    except Exception as ex:
652
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
653
        worker.displayLog(MessageType.Error, message)
654

  
655
    return []
656

  
657
'''
658
    @brief  detect equipment
659
    @author humkyung
660
    @date   2018.07.07
661
'''
662
def detectEquipmentOnPid(mainRes, targetSymbol, listWidget, worker):
663
    try:
664
        equipments = detectSymbolOnPid(mainRes, targetSymbol, listWidget, worker)
665
        for equipment in equipments:
666
            # detect nozzles around equimpent
667
            for nozzle in targetSymbolList[1]:
668
                #detectNozzleOnPid(equipment, nozzle, listWidget, worker)
669
                pass
670
            # up to here
671
    except Exception as ex:
672
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
673
        worker.displayLog(MessageType.Error, message)
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
    @history    2018.04.27  Jeongwoo    Remove Tesseract Log on listWidget
705
                2018.05.04  Jeongwoo    Change method to OCR with tesseract_ocr_module.py
706
                2018.05.09  Jeongwoo    Add global variable textInfoList, Remove text in symbol and Add tesseract result text
707
                2018.05.10  Jeongwoo    Remove not used if-statement
708
                2018.06.19  Jeongwoo    When detect text in symbol, use getTextAreaInfo() and Tesseract
709
                2018.06.21  Jeongwoo    Add if-statement for way to detect text by Type A
710
'''
711
def drawFoundSymbols(symbol, listWidget):
712
    global src
713
    global canvas
714
    global WHITE_LIST_CHARS
715
    global searchedSymbolList
716
    global textInfoList
717

  
718
    #symbolId = symbol.getId()
719
    symbolPath = symbol.getPath()
720
    symbolSp = symbol.getSp()
721
    symbolWidth = symbol.getWidth()
722
    symbolHeight = symbol.getHeight()
723
    symbolRotatedAngle = symbol.getRotatedAngle()
724
    symbolOcrOption = symbol.getOcrOption()
725

  
726
    symImg = cv2.cvtColor(cv2.imread(symbolPath, 1), cv2.COLOR_BGR2GRAY)
727
    for i in range(symbolRotatedAngle//90):
728
        symImg = cv2.rotate(symImg, cv2.ROTATE_90_COUNTERCLOCKWISE)
729

  
730
    w, h = symImg.shape[::-1]
731
    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)
732
    
733
'''
734
    @brief  draw found symbols and texts
735
    @author Jeongwoo
736
'''
737
def drawFoundSymbolsOnCanvas(drawingPath , textInfos , listWidget):
738
    global src
739
    global ocrCompletedSrc
740
    global searchedSymbolList
741
    global canvas
742

  
743
    appDocData = AppDocData.instance()
744
    canvas = np.zeros(appDocData.imgSrc.shape, np.uint8)
745
    canvas[::] = 255
746

  
747
    try:
748
        appDocData = AppDocData.instance()
749
        project = appDocData.getCurrentProject()
750

  
751
        for symbol in searchedSymbolList:
752
            drawFoundSymbols(symbol, listWidget)
753

  
754
        for text in textInfos:
755
            left = text.getX()
756
            top = text.getY()
757
            right = text.getX() + text.getW()
758
            bottom = text.getY() + text.getH()
759

  
760
            canvas[top:bottom, left:right] = appDocData.imgSrc[top:bottom, left:right]
761

  
762
        cv2.imwrite(os.path.join(project.getTempPath(), "FOUND_" + os.path.basename(drawingPath)), canvas)
763
    except Exception as ex:
764
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
765
        MainWindow.instance().displayMessage.emit(MessageType.Error, message)
766
        
767
'''
768
    @history    2018.04.24  Jeongwoo    Add isExceptDetect Field
769
                2018.05.09  Jeongwoo    Add targetSymbolList clear
770
                humkyung 2018.07.07 store symbols to list as like [equipments],[nozzles],[symbols]
771
'''
772
def initTargetSymbolDataList():
773
    global targetSymbolList
774

  
775
    targetSymbolList.clear()
776
    appDocData = AppDocData.instance()
777
    symbolList = appDocData.getTargetSymbolList()
778
    equipments = [item for item in symbolList if appDocData.getSymbolCategoryByType(item.getType()) == 'Equipment']
779
    nozzles = [item for item in symbolList if item.getType() == 'Nozzles']
780
    # [[equipments],[nozzles],[symbols]]
781
    targetSymbolList.append(equipments)
782
    targetSymbolList.append(nozzles)
783
    targetSymbolList.append([item for item in symbolList if item not in equipments and item not in nozzles])
784

  
785
    return targetSymbolList
786

  
787
'''
788
    @brief  remove small objects from given image
789
    @author humkyung
790
    @date   2018.04.26
791
    @history    2018.05.25  Jeongwoo    Moved from MainWindow
792
'''
793
def removeSmallObjects(image):
794
    try:
795
        appDocData = AppDocData.instance()
796
        configs = appDocData.getConfigs('Small Object Size', 'Min Area')
797
        minArea = int(configs[0].value) if 1 == len(configs) else 20
798
        configs = appDocData.getConfigs('Small Object Size', 'Max Area')
799
        maxArea = int(configs[0].value) if 1 == len(configs) else 50
800

  
801
        _,contours,_ = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
802
        selectedContours=[]
803
        for contour in contours:
804
            area = cv2.contourArea(contour)
805
            if area > minArea and area < maxArea: selectedContours.append(contour)
806
        contourImage = cv2.drawContours(image, selectedContours, -1, (255,255,255), -1); # draw contour with white color
807
    except Exception as ex:
808
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
809
        MainWindow.instance().displayMessage.emit(MessageType.Error, message)
810

  
811
    return contourImage
812

  
813
'''
814
    @history    2018.05.25  Jeongwoo    Moved from MainWindow
815
                2018.05.28  Jeongwoo    Add xmlPath Parameter and append LineInfo into xml
816
                2018.05.29  Jeongwoo    Change method to add item
817
                2018.05.30  Jeongwoo    Remove parameter (xmlPath)
818
                humkyung 2018.06.11 add drawing path to parameter and write recognized lines to image
819
                humkyung 2018.07.04 call arrangeLinePosition after creating line
820
'''
821
def recognizeLine(path, listWidget, graphicsView, worker):
822
    from shapely.geometry import Point, LineString
823
    from SymbolSvgItem import SymbolSvgItem
824
    from QEngineeringFlowArrowItem import QEngineeringFlowArrowItem
825
    from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
826
    from EngineeringTextItem import QEngineeringTextItem
827
    from EngineeringLineItem import QEngineeringLineItem
828
    from LineDetector import LineDetector
829

  
830
    try:
831
        listWidget.addItem('Starting line recognization')
832

  
833
        #remove already existing line and flow arrow item
834
        items = [item for item in graphicsView.scene.items() if (type(item) is QEngineeringLineItem) or (type(item) is QEngineeringFlowArrowItem)]
835
        for item in items:
836
            graphicsView.scene.removeItem(item)
837
        #up to here
838

  
839
        # detect line
840
        connectedLines = []
841

  
842
        area = AppDocData.instance().getArea('Drawing')
843
        area.img = removeSmallObjects(area.img)
844
        detector = LineDetector(area.img)
845

  
846
        symbols = []
847
        for item in graphicsView.scene.items():
848
            if issubclass(type(item), SymbolSvgItem):
849
                symbols.append(item)
850
                res = detector.detectConnectedLine(item, round(area.x), round(area.y))
851
                if res is not None:
852
                    connectedLines.extend(res)
853

  
854
        listWidget.addItem('Connecting lines')
855
        if len(connectedLines) > 1:
856
            detector.mergeLines(connectedLines, toler=5)
857
            # connect line to symbol
858
            try:
859
                for line in connectedLines:
860
                    matches = [symbol for symbol in symbols if symbol.isConnectable(line, (round(area.x), round(area.y)), toler=20)]
861
                    for symbol in matches:
862
                        detector.connectLineToSymbol(line, (round(area.x), round(area.y)), symbol)
863
            except Exception as ex:
864
                message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
865
                MainWindow.instance().displayMessage.emit(MessageType.Error, message)
866
            # up to here
867

  
868
            # connect line to line
869
            toler = 10
870
            try:
871
                for line in connectedLines:
872
                    matches = [it for it in connectedLines if (it is not line) and (not detector.isParallel(line, it))]
873

  
874
                    # get closest line
875
                    selected = []
876
                    shapelyLine = LineString(line)
877
                    for match in matches:
878
                        dist = [shapelyLine.distance(Point(match[0][0], match[0][1])),shapelyLine.distance(Point(match[1][0], match[1][1]))]
879
                        if dist[0] < toler or dist[1] < toler:
880
                            selected.append(match)
881
                    # up to here
882

  
883
                    for match in selected:
884
                        detector.connectLineToLine(match, line, toler)
885
            except Exception as ex:
886
                message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
887
                MainWindow.instance().displayMessage.emit(MessageType.Error, message)
888
            # up to here
889

  
890
        lines = []
891
        for pts in connectedLines:
892
            processLine = QEngineeringLineItem(vertices=[(area.x + param[0], area.y + param[1]) for param in pts])
893
            graphicsView.scene.addItem(processLine)
894
            lines.append(processLine)
895

  
896
            if processLine.length() > 100: # TODO: check critical length
897
                processLine.addFlowArrow()
898
        
899
        # re-order process line's start,end according to flow mark
900
        arrangeLinePosition(lines, symbols, listWidget, worker)
901
        # up to here
902
    except Exception as ex:
903
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
904
        worker.displayLog.emit(MessageType.Error, message)
905
    finally:
906
        listWidget.addItem('Finished line recognization')
907
    
908
'''
909
    @brief  arrange line's position
910
    @author humkyung
911
    @date   2018.07.04
912
'''
913
def arrangeLinePosition(lines, symbols, listWidget, worker):
914
    try:
915
        listWidget.addItem('Apply flow direction')
916
        pool = [line for line in lines if line.flowMark is not None]
917
        visited = []
918
        visited.extend(pool)
919
        while len(pool) > 0:
920
            line = pool.pop()
921
            message = '{} - ({})'.format(line, len(pool))
922
            worker.displayLog.emit(MessageType.Normal, message)
923
            rhs = [item for item in lines if item not in visited and item.isJointed(line)]
924
            if rhs:
925
                pool.extend(rhs)
926
                visited.extend(rhs)
927
                for item in rhs:
928
                    item.arrangeVertexOrder(line)
929
            
930
            # skip jointed symbols
931
            symbolPool = [item for item in symbols if item not in visited and item.isJointed(line)]
932
            if symbolPool:
933
                selected = []
934
                visited.extend(symbolPool)
935
                while len(symbolPool) > 0:
936
                    symbol = symbolPool.pop()
937

  
938
                    rhs = [item for item in symbols if item not in visited and item.isJointed(symbol)]
939
                    if rhs:
940
                        symbolPool.extend(rhs)
941
                        visited.extend(rhs)
942
                        selected.extend(rhs)
943
                    else:
944
                        selected.append(symbol)
945

  
946
                # find lines which are connected last symbol
947
                for symbol in selected:
948
                    rhs = [item for item in lines if item not in visited and item.isJointed(symbol)]
949
                    if rhs:
950
                        pool.extend(rhs)
951
                        visited.extend(rhs)
952
                        for item in rhs:
953
                            item.arrangeVertexOrder(line)
954
            # up to here
955
        # up to here
956
    except Exception as ex:
957
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
958
        MainWindow.instance().displayMessage.emit(MessageType.Error, message)
959

  
960
'''
961
    @brief      Main function
962
    @author     Jeongwoo
963
    @date
964
    @history    humkyung 2018.04.06 change error display from message box to print
965
                Jeongwoo 2018.04.25 Remove 'Current Symbol : ' QListItem
966
                Jeongwoo 2018.05.09 Make Comments OCR.removeTextFromNpArray block
967
                Jeongwoo 2018.05.25 Remove imgLineList variable and parameter on writeXml()
968
                humkyung 2018.05.26 add parameters(graphicsView, isSymbolTextChecked, isLineChecked)
969
                Jeongwoo 2018.05.28 Add/Remove Parameters(Add : signal / Remove : graphicsView, isLineChecked)
970
                Jeongwoo 2018.05.30 Remove return value
971
                humkyung 2018.06.08 add signal for progressbar to parameter
972
                humkyung 2018.06.11 get difference between original and recognized image
973
                Jeongwoo 2018.06.21 If noteTextInfoList is None, change from None to empty list
974
'''
975
def executeRecognition(signal, path, listWidget, isSymbolTextChecked, worker):
976
    import re
977
    from TextDetector import TextDetector
978

  
979
    global ocrCompletedSrc
980
    global searchedSymbolList
981
    global threadLock
982
    global textInfoList
983
    global noteTextInfoList
984
    global maxProgressValue
985
    
986
    try:
987
        appDocData = AppDocData.instance()
988
        project = appDocData.getCurrentProject()
989
        textDetector = TextDetector()
990

  
991
        srcList = []
992
        srcList.append(path)
993

  
994
        initTargetSymbolDataList()
995

  
996
        for mainRes in srcList:
997
            ocrCompletedSrc = []
998
            searchedSymbolList = []
999
            textInfoList = []
1000

  
1001
            if not os.path.isfile(mainRes):
1002
                item = QListWidgetItem('{} file is not found'.format(os.path.basename(mainRes)))
1003
                item.setBackground(Qt.red)
1004
                listWidget.addItem(item)
1005
                continue
1006

  
1007
            # remove equipment desc. area
1008
            configs = appDocData.getConfigs('{} Equipment Desc Area'.format(appDocData.imgName))
1009
            for config in configs:
1010
                found = re.findall('\d+', config.value)
1011
                if len(found) == 4:
1012
                    cv2.rectangle(appDocData.imgSrc, (int(found[0]), int(found[1])), (int(found[0])+int(found[2]), int(found[1])+int(found[3])), 255, -1)
1013
            # up to here
1014
            
1015
            # acquire note image and remove from imgSrc
1016
            noteArea = appDocData.getArea('Note')
1017
            if noteArea is not None:
1018
                noteArea.img = appDocData.imgSrc[round(noteArea.y):round(noteArea.y+noteArea.height), round(noteArea.x):round(noteArea.x+noteArea.width)].copy()
1019
                cv2.rectangle(appDocData.imgSrc, (round(noteArea.x), round(noteArea.y)), (round(noteArea.x + noteArea.width), round(noteArea.y + noteArea.height)), 255, -1)
1020
            # up to here
1021

  
1022
            area = appDocData.getArea('Drawing')
1023
            if area is not None:
1024
                area.img = appDocData.imgSrc[round(area.y):round(area.y+area.height), round(area.x):round(area.x+area.width)]
1025

  
1026
            listWidget.addItem("Start recognition : " + mainRes)
1027

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

  
1042
                # detect equipments
1043
                pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
1044
                for symbol in targetSymbolList[0]:
1045
                    pool.submit(detectEquipmentOnPid, mainRes, symbol, listWidget, worker)
1046
                pool.shutdown(wait = True)
1047
                # up to here
1048

  
1049
                pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
1050
                for symbol in targetSymbolList[2]:
1051
                    if type(symbol) is list:
1052
                        pool.submit(detectSymbolsOnPid, mainRes, symbol, listWidget, worker)
1053
                    else:
1054
                        pool.submit(detectSymbolOnPid, mainRes, symbol, listWidget, worker)
1055
                pool.shutdown(wait = True)
1056

  
1057
                ## DEBUG
1058
                for item in searchedSymbolList:
1059
                    _img = appDocData.imgSrc[round(item.getSp()[1]):round(item.getSp()[1]+item.getHeight()), round(item.getSp()[0]):round(item.getSp()[0]+item.getWidth())]
1060
                    cv2.imwrite(os.path.join(project.getTempPath(), 'Tile', item.getName()+'.png'), _img)
1061
                ## up to here
1062

  
1063
                textDetector.recognizeText(appDocData.imgSrc, offset, textAreas, searchedSymbolList, worker, listWidget, maxProgressValue)
1064
                textInfoList = textDetector.textInfoList.copy() if textDetector.textInfoList is not None else None
1065
                noteTextInfoList = textDetector.noteTextInfoList.copy() if textDetector.noteTextInfoList is not None else None
1066

  
1067
                appDocData.imgWidth, appDocData.imgHeight = appDocData.imgSrc.shape[::-1]
1068
                drawFoundSymbolsOnCanvas(mainRes, textInfoList, listWidget)
1069
 
1070
                # remove text from image
1071
                textDetector.removeTextFromImage(appDocData.imgSrc, offset)
1072
                # up to here
1073
               
1074
                appDocData.imgName = os.path.splitext(os.path.basename(mainRes))[0]
1075

  
1076
                pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
1077
                for sym in searchedSymbolList:
1078
                    pool.submit(removeDetectedSymbol, sym, appDocData.imgSrc)
1079
                pool.shutdown(wait = True)
1080

  
1081
                ## Remove Noise
1082
                kernel1 = np.ones((2, 2), np.uint8)
1083
                appDocData.imgSrc = cv2.dilate(appDocData.imgSrc, kernel1)
1084
                appDocData.imgSrc = cv2.erode(appDocData.imgSrc, kernel1)
1085

  
1086
                removedSymbolImgPath = os.path.join(project.getTempPath(), os.path.basename(path))
1087
                cv2.imwrite(removedSymbolImgPath, appDocData.imgSrc)
1088

  
1089
                area = AppDocData.instance().getArea('Drawing')
1090
                if area is not None:
1091
                    area.img = appDocData.imgSrc[round(area.y+1):round(area.y+area.height), round(area.x+1):round(area.x+area.width)]
1092
                cv2.imwrite(os.path.join(project.getTempPath(), "RECT_" + os.path.basename(path)), appDocData.imgSrc)
1093

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

  
1127
    global ocrCompletedSrc
1128
    global textInfoList
1129
    global noteTextInfoList
1130

  
1131
    try:
1132
        appDocData = AppDocData.instance()
1133
        if os.path.isfile(orgImagePath) and os.path.isfile(recImagePath):
1134
            imgOriginal = cv2.threshold(cvtGrayImage(cv2.imread(orgImagePath, 1)), 127, 255, cv2.THRESH_BINARY)[1]
1135
            # remove equipment desc. area
1136
            configs = appDocData.getConfigs('{} Equipment Desc Area'.format(appDocData.imgName))
1137
            for config in configs:
1138
                found = re.findall('\d+', config.value)
1139
                if len(found) == 4:
1140
                    cv2.rectangle(imgOriginal, (int(found[0]), int(found[1])), (int(found[0])+int(found[2]), int(found[1])+int(found[3])), 255, -1)
1141
            # up to here
1142

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

  
1145
            imgDiff = np.ones(imgOriginal.shape, np.uint8)*255
1146

  
1147
            area = AppDocData.instance().getArea('Drawing')
1148
            if area is not None:
1149
                x = round(area.x)
1150
                y = round(area.y)
1151
                width = round(area.width)
1152
                height = round(area.height)
1153
                imgNotOper = cv2.bitwise_not(imgRecognized[y:y+height, x:x+width])
1154
                imgDiff[y:y+height, x:x+width] = cv2.bitwise_xor(imgOriginal[y:y+height, x:x+width], imgNotOper)
1155
                
1156
            # remove noise
1157
            imgDiff = cv2.dilate(imgDiff, np.ones((2, 2), np.uint8))
1158

  
1159
            appDocData = AppDocData.instance()
1160
            project = appDocData.getCurrentProject()
1161
            cv2.imwrite(os.path.join(project.getTempPath(), "DIFF_" + os.path.basename(orgImagePath)), imgDiff)
1162
    except Exception as ex:
1163
        message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
1164
        MainWindow.instance().displayMessage.emit(MessageType.Error, message)
1165
   
1166
if __name__ == '__main__':
1167
    import DTI_PID_UI
1168
    from ProjectDialog import Ui_Dialog
1169
    import timeit
1170
    from PyQt5.QtCore import QRect
1171
    from operator import itemgetter, attrgetter
1172

  
1173
    start = timeit.default_timer()
1174
    img = cv2.imread('D:/Visual Studio Project/DTIPID/DTIPID/DTI_PID/DTI_PID/res/Result/PC-K/PC-K-2203_P1_800DPI.png', 1)
1175
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1176
    contourImg = np.ones(imgGray.shape, np.uint8)*255
1177
    contourOcrImg = contourImg.copy()
1178
    binaryImg,mask = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
1179
    imgFinal = cv2.bitwise_and(imgGray, imgGray, mask = mask)
1180
    ret, newImg = cv2.threshold(imgFinal, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
1181

  
1182
    image, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
1183
    #holes = [contours[i] for i in range(len(contours)) if hierarchy[i][3] >= 0]
1184
    for contour in contours:
1185
        area = cv2.contourArea(contour, True)
1186
        if area >= 0:
1187
            [x, y, w, h] = cv2.boundingRect(contour)
1188

  
1189
            # remove too big or small one
1190
            if (w > 100 or h > 100) or (w < 5 or h < 5): continue
1191

  
1192
            cv2.drawContours(contourImg, [contour], -1, (0,0,0), 1)
1193
            cv2.drawContours(contourOcrImg, [contour], -1, (0,0,0), -1)
1194
        else:
1195
            cv2.drawContours(contourOcrImg, [contour], -1, (255,255,255), -1)
1196

  
1197
    ''' contourImg = cv2.bitwise_not(contourImg) circles = cv2.HoughCircles(contourImg, cv2.HOUGH_GRADIENT, 1, 100) circles = np.uint16(np.around(circles))
1198
       for i in circles[0,:]:
1199
        cv2.circle(contourImg, (i[0], i[1]), i[2], (255,255,0), 1)
1200
    '''
1201

  
1202
    rects = []
1203
    kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (8, 8))
1204
    #kernel1 = cv2.getStructuringElement(cv2.MORPH_CROSS, (2,2))
1205
    #eroded = cv2.dilate(contourImg, kernel1)
1206
    #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)
1207
    eroded = cv2.erode(contourImg, kernel)
1208
    image, contours, hierarchy = cv2.findContours(eroded, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
1209
    for contour in contours:
1210
        area = cv2.contourArea(contour, True)
1211
        if area >= 0:
1212
            [x, y, w, h] = cv2.boundingRect(contour)
1213

  
1214
            # remove small one less than character size
1215
            if (w < 20 or h < 20): continue
1216
            #if w > h:
1217
            #    rects.append(QRect(x, y, w, h)) # expand rect
1218
            #elif w < h:
1219
            #    rects.append(QRect(x, y, w, h)) # expand rect
1220
            rects.append(QRect(x, y, w, h)) # expand rect
1221

  
1222
    intersected = True
1223
    while intersected:
1224
        intersected = False
1225
        for rect in rects[:]:
1226
            matches = [x for x in rects if rect.intersects(x)]
1227
            if len(matches) > 1:
1228
                united = matches[0]
1229
                for _rect in matches:
1230
                    united = united.united(_rect)
1231
                    if _rect in rects: rects.remove(_rect)
1232
                rects.append(united)
1233
                intersected = True
1234
                break
1235

  
1236
    for rect in rects:
1237
        cv2.rectangle(img, (rect.x(), rect.y()), (rect.x() + rect.width(), rect.y() + rect.height()), (255, 0, 255), 1)
1238

  
1239
    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)
1240
    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)
1241
    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)
1242

  
1243
    chan, imgW, imgH = img.shape[::-1]
1244
    index = 0
1245
    for rect in rects:
1246
        index = index + 1
1247
        isVertical = False
1248
        textInfoList = None
1249
        if rect.width() >= rect.height() or rect.height() < 50:
1250
            isVertical = False
1251
            textInfoList = TOCR.getTextInfo(contourOcrImg[rect.y():rect.y()+rect.height(), rect.x():rect.x()+rect.width()], (rect.x(), rect.y()))
1252
        else:
1253
            isVertical = True
1254
            transform = QTransform()
1255
            transform.translate(imgH*0.5, imgW*0.5)
1256
            transform.rotate(90)
1257
            transform.translate(-imgW*0.5, -imgH*0.5)
1258
            transRect = transform.mapRect(rect)
1259
            rotatedContourOcrImg = cv2.rotate(contourOcrImg, cv2.ROTATE_90_CLOCKWISE)
1260
            textInfoList = TOCR.getTextInfo(rotatedContourOcrImg[transRect.y():transRect.y()+transRect.height(), transRect.x():transRect.x()+transRect.width()], (transRect.x(), transRect.y()))
1261

  
1262
        if isVertical:
1263
            img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
1264
        if textInfoList is not None:
1265
            for textInfo in textInfoList:
1266
                cv2.putText(img, textInfo.getText(), (textInfo.getX(), textInfo.getY()+textInfo.getH()), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 3)
1267
        if isVertical:
1268
            img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
1269

  
1270
        print(str(index) + " / " + str(len(rects)) + " Finished")
1271
        #cv2.imshow('test', img[rect.y():rect.y()+rect.height(), rect.x():rect.x()+rect.width()])
1272
        #cv2.waitKey(0)
1273
        #cv2.destroyAllWindows()
1274
        
1275
    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)
1276
    stop = timeit.default_timer()
1277
    print('FINISHED : ' + str((stop-start)/60) + ' min')
1278
    
1279
    #app = QApplication(sys.argv)
1280

  
1281
    #try:
1282
    #    dlg = Ui_Dialog()
1283
    #    selectedProject = dlg.showDialog()
1284
    #    if selectedProject is not None:
1285
    #        form = ExampleApp()
1286
    #        form.show()
1287
    #except Exception as ex:
1288
    #    print('에러가 발생했습니다.\n', ex)
1289

  
1290
    #sys.exit(app.exec_())
DTI_PID/DTI_PID/Shapes/GraphicsBoundingBoxItem.py
117 117
    def getColor(self):
118 118
        return self.pen().color().name()
119 119

  
120
    """
121
        @brief      override setRect
122
        @author     humkyung
123
        @date       2018.09.12
124
    """
125
    def setRect(self, x, y, width, height):
126
        QGraphicsRectItem.setRect(self, x, y, width, height)
127
        
128
        minx, miny, maxx, maxy = x, y, x + width, y + height
129
        self.connectors[0].setPos((minx, miny))
130
        self.connectors[1].setPos((minx, maxy))
131
        self.connectors[2].setPos((maxx, maxy))
132
        self.connectors[3].setPos((maxx, miny))
133

  
120 134
    '''
121 135
        @brief  construct a bounding box item
122 136
    '''

내보내기 Unified diff

클립보드 이미지 추가 (최대 크기: 500 MB)