프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / DetectSymbolDialog.py @ d8ad8a2c

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

1
# coding: utf-8
2
"""
3
    This is fluid code dialog module
4
"""
5
import sys
6
import sqlite3
7
import os
8
import cv2
9
import math
10
import threading
11
import multiprocessing
12
from multiprocessing import Process, Queue
13

    
14
from shapely.geometry import Point
15
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree, parse
16
from PyQt5.QtCore import *
17
from PyQt5.QtGui import *
18
from PyQt5.QtWidgets import *
19

    
20
from AppDocData import AppDocData
21
import DetectSymbol_UI
22

    
23
class QDetectSymbolDialog(QDialog):
24

    
25
    def __init__(self, parent):
26
        QDialog.__init__(self)
27
        self.parent = parent
28
        self.ui = DetectSymbol_UI.Ui_DetectSymbolDialog()
29
        self.ui.setupUi(self)
30

    
31
        self.table = self.ui.tableWidgetSymbol
32
        self.currentRow = 0
33
        self.currentColumn = 0
34

    
35
        ## box dir setting
36
        self.boxDir = AppDocData.instance().getCurrentProject().path + '/box/'
37
        if not os.path.exists(self.boxDir):
38
            os.makedirs(self.boxDir)
39
        ## drawing file setting
40
        self.drawingDir = AppDocData.instance().getCurrentProject().path + '/drawings/'
41
        if os.path.exists(self.drawingDir):
42
            files = os.listdir(self.drawingDir)
43
            for file in files:
44
                listItem = QListWidgetItem()
45
                listItem.setData(32, file)
46
                listItem.setText(os.path.splitext(file)[0])
47
                self.ui.listWidgetDrawings.addItem(listItem)
48

    
49
        self.ui.listWidgetDrawings.currentTextChanged.connect(self.currentTextChangedEvent)
50
        self.ui.pushButtonDetectSymbol.clicked.connect(self.detectSymbol)
51

    
52
        if self.ui.listWidgetDrawings.count():
53
            self.ui.listWidgetDrawings.setCurrentRow(0)
54

    
55
        self.table.cellDoubleClicked.connect(self.cellDoubleClickedEvent)
56
        self.table.currentCellChanged.connect(self.currentCellChangedEvent)
57

    
58
        self.setAllSymbolsTable()
59

    
60
    '''
61
        @brief  current cell chage event
62
        @author kyouho
63
        @date   2018.10.01
64
    '''
65
    def currentCellChangedEvent(self, currentRow, currentColumn, previousRow, previousColumn):
66
        currentItem = self.table.cellWidget(currentRow, currentColumn)
67
        if currentItem is not None:
68
            self.currentRow = currentRow
69
            self.currentColumn = currentColumn
70
        else:
71
            self.currentRow = -1
72
            self.currentColumn = -1
73

    
74
    '''
75
        @brief  cell double click event
76
        @author kyouho
77
        @date   2018.10.01
78
    '''
79
    def cellDoubleClickedEvent(self, row, column):
80
        cell = self.table.cellWidget(row,column)
81
        if cell is not None:
82
            import SymbolEditorDialog
83
            symbolEditorDialog = SymbolEditorDialog.QSymbolEditorDialog(self, cell.pixmap(), AppDocData.instance().getCurrentProject())
84
            (isAccepted, isImmediateInsert, offsetX, offsetY, newSym) = symbolEditorDialog.showDialog()
85
            if isAccepted:
86
                self.table.removeCellWidget(row, column)
87
                self.parent.dirTreeWidget.initDirTreeWidget()
88

    
89
    '''
90
        @brief  text changed Event
91
        @author kyouho
92
        @date   2018.09.18
93
    '''
94
    def currentTextChangedEvent(self, text):
95
        self.imageName = text
96
        self.imgPath = self.drawingDir + self.ui.listWidgetDrawings.currentItem().data(32)
97
        self.tableSetting()
98

    
99
    '''
100
        @brief  text changed Event
101
        @author kyouho
102
        @date   2018.10.11
103
    '''
104
    def setAllSymbolsTable(self):
105
        table = self.ui.tableWidgetAllSymbols
106
        table.setRowCount(0)
107

    
108
        imageFiles = []
109
        imageDir = AppDocData.instance().getCurrentProject().path + '/image/'
110
        self.findFiles(imageDir, imageFiles)
111

    
112
        table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
113
        size = table.maximumWidth() - 40
114

    
115
        table.setRowCount(len(imageFiles))
116
        row = 0
117
        for path in imageFiles:
118
            cell = QLabel()
119
            pixmap = QPixmap(path)
120
            cell.setPixmap(pixmap.scaled(size, size, Qt.KeepAspectRatio))
121
            table.setCellWidget(row, 0, cell)
122
            row = row + 1
123

    
124
        table.resizeRowsToContents()
125

    
126
    '''
127
        @brief  find image files
128
        @author kyouho
129
        @date   2018.10.11
130
    '''
131
    def findFiles(self, dir, list):
132
        fileList = os.listdir(dir)
133
        for file in fileList:
134
            path = os.path.join(dir, file)
135
            if os.path.isdir(path):
136
                self.findFiles(path, list)
137
            elif os.path.splitext(path)[1] == '.png':
138
                list.append(path)
139

    
140

    
141
    
142
    '''
143
        @brief  text changed Event
144
        @author kyouho
145
        @date   2018.09.18
146
    '''
147
    def detectSymbol(self):
148
        if not self.ui.listWidgetDrawings.count():
149
            return
150

    
151
        self.progress = QProgressDialog("잠시만 기다려주세요", "Cancel", 0, 100, self)
152
        self.progress.setWindowModality(Qt.WindowModal)
153
        self.progress.setAutoReset(True)
154
        self.progress.setAutoClose(True)
155
        self.progress.setMinimum(0)
156
        self.progress.resize(600,100)
157
        self.progress.setWindowTitle("인식 중...")
158
        self.progress.show()
159
        self.progress.setMaximum(100)
160
        self.progress.setValue(0)
161
        QApplication.processEvents()
162

    
163
        from TextDetector import TextDetector
164
        import numpy as np
165

    
166
        appDocData = AppDocData.instance()
167
        ## textDetector에서 사용하기 때문에 설정
168
        appDocData.imgName = self.imageName
169

    
170
        ## 흑색 이미지로 변환
171
        img = cv2.imread(self.imgPath, 1)
172
        imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
173
        
174
        ## 프로젝트의 Drawing Area data
175
        area = appDocData.getArea('Drawing')
176

    
177
        ## area 영역 자른 offset
178
        offset = (area.x, area.y) if area is not None else (0,0)
179
        
180
        ## 이미지 이진화
181
        thresholdImg = cv2.threshold(imgGray , 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
182
        ## 프로젝트의 Area 영역만큼만 자른 이미지
183
        areaImg = thresholdImg[round(area.y):round(area.y+area.height), round(area.x):round(area.x+area.width)]
184

    
185
        ## 선제거 전에 먼저 작은 영역 제거
186
        ## contours 추출을 위한 색반전
187
        areaImg = cv2.bitwise_not(areaImg) 
188
        ## contours 추출
189
        image, contours, hierarchy = cv2.findContours(areaImg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
190
        for contour in contours:
191
            [x, y, w, h] = cv2.boundingRect(contour)
192

    
193
            if (w < 40 or h < 40): 
194
                areaImg[y:y+h,x:x+w] = 0
195
        ## 다시 색반전
196
        areaImg = cv2.bitwise_not(areaImg) 
197

    
198
        ## find lines
199
        verticalLineList = []
200
        horizontalLineList = []
201
        self.findLines(areaImg, verticalLineList, horizontalLineList)
202

    
203
        ## chage color
204
        for vLine in verticalLineList:
205
            p1 = vLine[0]
206
            p2 = vLine[1]
207
            x = p1[0]
208
            areaImg[p1[1]:p2[1], x] = 255
209
        ## chage color
210
        for vLine in horizontalLineList:
211
            p1 = vLine[0]
212
            p2 = vLine[1]
213
            y = p1[1]
214
            areaImg[y, p1[0]:p2[0]] = 255
215
        
216
        ## contours 추출을 위한 색반전
217
        areaImg = cv2.bitwise_not(areaImg) 
218
        ## contours 추출
219
        recList = self.getContours(areaImg)
220

    
221
        ## to xml
222
        self.toXml(recList, offset)
223

    
224
        ## table setting
225
        self.tableSetting()
226

    
227
        self.progress.setValue(self.progress.maximum())
228
    
229
    '''
230
        @brief  find lines
231
        @author kyouho
232
        @date   2018.10.01
233
    '''
234
    def findLines(self, areaImg, verticalLineList, horizontalLineList):
235

    
236
        ## for multiprocessing
237
        verticalProcessList = []
238
        horizontalProcessList = []
239

    
240
        ## Find VerticalLine using multiprocessing
241
        ## Set Vertical Line
242
        jumpValue = int(areaImg.shape[1] / os.cpu_count())
243
        value = 0
244
        for cpuIndex in range(os.cpu_count()):
245
            _queue = Queue()
246
            _range = value + jumpValue
247
            if os.cpu_count() -1 == cpuIndex:
248
                _range = areaImg.shape[1]
249

    
250
            _process = Process(target=isVerticalLineThread, args=(value, _range, areaImg, _queue))
251
            _process.daemon = True
252
            verticalProcessList.append((_process, _queue))
253
            value = value + jumpValue
254
        
255
        ## Set Horizontal Line
256
        jumpValue = int(areaImg.shape[0] / os.cpu_count())
257
        value = 0
258
        for cpuIndex in range(os.cpu_count()):
259
            _queue = Queue()
260
            _range = value + jumpValue
261
            if os.cpu_count() -1 == cpuIndex:
262
                _range = areaImg.shape[0]
263

    
264
            _process = Process(target=isHorizontalLineThread, args=(value, _range, areaImg, _queue))
265
            _process.daemon = True
266
            horizontalProcessList.append((_process, _queue))
267
            value = value + jumpValue
268
        
269
        ## set percent
270
        progressCount = len(verticalProcessList) + len(horizontalProcessList) + 1
271
        percentGage = int(100 / progressCount)
272
        percent = percentGage
273
        self.progress.setValue(percent)
274
        QApplication.processEvents()
275

    
276
        ## process start
277
        for process in verticalProcessList:
278
            process[0].start()
279

    
280
        ## Wait Vertical And Start Horizontal 
281
        for index in range(len(verticalProcessList)):
282
            verticalLineList.extend(verticalProcessList[index][1].get())
283
            verticalProcessList[index][0].join()
284
            verticalProcessList[index][1].close()
285

    
286
            percent = percent + percentGage
287
            self.progress.setValue(percent)
288
            QApplication.processEvents()
289

    
290
            horizontalProcessList[index][0].start()
291

    
292
        ## Wait Horizontal 
293
        for process in horizontalProcessList:
294
            horizontalLineList.extend(process[1].get())
295
            process[0].join()
296
            process[1].close()
297

    
298
            percent = percent + percentGage
299
            self.progress.setValue(percent)
300
            QApplication.processEvents()
301

    
302
    '''
303
        @brief  get contours
304
        @author kyouho
305
        @date   2018.10.01
306
    '''
307
    def getContours(self, areaImg):
308
        
309
        image, contours, hierarchy = cv2.findContours(areaImg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
310

    
311
        ## RecList 정리
312
        lineWidth = 5
313
        recList = []
314
        tempRecList = []
315
        for contour in contours:
316
            [x, y, w, h] = cv2.boundingRect(contour)
317
            tempRecList.append([x-lineWidth, y-lineWidth, x+w+lineWidth, y+h+lineWidth])
318

    
319
        ## Overlap Rec 합침
320
        while len(tempRecList):
321
            rec1 = tempRecList[0]
322
            _temp = []
323
            for index in range(1, len(tempRecList)):
324
                rec2 = tempRecList[index]
325
                if rec1[0] <= rec2[2] and rec1[2] >= rec2[0] and rec1[1] <= rec2[3] and rec1[3] >= rec2[1]:
326
                    _temp.append(rec2)
327
            
328
            if len(_temp):
329
                x1 = rec1[0]
330
                y1 = rec1[1]
331
                x2 = rec1[2]
332
                y2 = rec1[3]
333

    
334
                for rec in _temp:
335
                    x1 = min(x1, rec[0])
336
                    y1 = min(y1, rec[1])
337
                    x2 = max(x2, rec[2])
338
                    y2 = max(y2, rec[3])
339
                    tempRecList.remove(rec)
340
                tempRecList.append([x1, y1, x2, y2])
341

    
342
            else:
343
                recList.append(rec1)
344

    
345
            tempRecList.remove(rec1)
346

    
347
        return recList
348

    
349
    '''
350
        @brief  key press event
351
        @author kyouho
352
        @date   2018.10.01
353
    '''
354
    def keyPressEvent(self, event):
355
        if event.key() == Qt.Key_Delete and self.currentRow != -1 and self.currentColumn != -1:
356
            self.table.removeCellWidget(self.currentRow, self.currentColumn)
357
            
358

    
359
    '''
360
        @brief  box to xml
361
        @author kyouho
362
        @date   2018.10.01
363
    '''
364
    def toXml(self, recList, offset):
365
        xml = Element('BOXES')
366

    
367
        ## to xml
368
        for rec in recList:
369
            [x1, y1, x2, y2] = rec
370
            w = x2-x1
371
            h = y2-y1
372
            if w < 20 or h < 20: continue
373
            elif w*4<h or h*4<w: continue
374

    
375
            boxElement = Element('BOX')
376
            
377
            xElement = Element('X')
378
            xElement.text = str(x1 + offset[0])
379
            boxElement.append(xElement)
380

    
381
            yElement = Element('Y')
382
            yElement.text = str(y1 + offset[1])
383
            boxElement.append(yElement)
384

    
385
            widthElement = Element('WIDTH')
386
            widthElement.text = str(x2-x1)
387
            boxElement.append(widthElement)
388

    
389
            heightElement = Element('HEIGHT')
390
            heightElement.text = str(y2-y1)
391
            boxElement.append(heightElement)
392

    
393
            xml.append(boxElement)
394

    
395
        ElementTree(xml).write(self.boxDir + self.imageName + '.box')
396

    
397
    '''
398
        @brief  table setting
399
        @author kyouho
400
        @date   2018.09.18
401
    '''
402
    def tableSetting(self):
403
        columnCount = 3
404
        self.table.setColumnCount(columnCount)
405
        self.table.setRowCount(0)
406
        boxCount = 0
407

    
408
        xmlPath = self.boxDir + self.imageName + '.box'
409
        if os.path.exists(xmlPath):
410
            _pixmap = QPixmap(self.imgPath)
411

    
412
            xml = parse(xmlPath)
413
            root = xml.getroot()
414

    
415
            for box in root.iter('BOX'):
416
                rowIndex = int(boxCount / columnCount)
417
                self.table.setRowCount(rowIndex + 1)
418

    
419
                _x = int(box.find('X').text)
420
                _y = int(box.find('Y').text)
421
                _width = int(box.find('WIDTH').text)
422
                _height = int(box.find('HEIGHT').text)
423

    
424
                rect = QRect(_x, _y, _width, _height)
425
                boxImg = _pixmap.copy(rect)
426

    
427
                cell = QLabel()
428
                cell.setPixmap(boxImg)
429
                cell.rect = [_x, _y, _width, _height]
430
                self.table.setCellWidget(rowIndex, boxCount % columnCount, cell)
431

    
432
                boxCount = boxCount + 1
433
                
434

    
435
            self.table.resizeColumnsToContents()
436
            self.table.resizeRowsToContents()
437
        
438
    '''
439
        @brief  accept
440
        @author kyouho
441
        @date   2018.10.01
442
    '''
443
    def accept(self):
444
        columnCount = 3
445
        recList = []
446
        for rowIndex in range(self.table.rowCount()):
447
            for columnIndex in range(columnCount):
448
                cell = self.table.cellWidget(rowIndex, columnIndex)
449
                if cell is not None:
450
                    [x, y, w, h] = cell.rect
451
                    recList.append([x, y, x+w, y+h])
452

    
453
        self.toXml(recList, (0, 0))
454

    
455
        QDialog.accept(self)
456

    
457
'''
458
    @brief  Check Vertical Line using Multiprocessing
459
    @author kyouho
460
    @date   2018.09.27
461
'''
462
def isVerticalLineThread(start, end, img, _queue):
463
    minLineSize = 40
464
    
465
    ## Vertical
466
    find = False
467
    startY = 0
468
    lineList = []
469
    for x in range(start, end):
470
        for y in range(0, img.shape[0]):
471
            if img[y,x] == 0:
472
                if find:
473
                    continue
474
                else:
475
                    find = True
476
                    startY = y
477
            else:
478
                if find:
479
                    if y - startY > minLineSize:
480
                        lineList.append(((x,startY), (x,y)))
481
                    find = False
482
    _queue.put(lineList)
483

    
484
'''
485
    @brief  Check Horizontal Line using Multiprocessing
486
    @author kyouho
487
    @date   2018.09.27
488
'''
489
def isHorizontalLineThread(start, end, img, _queue):
490
    minLineSize = 40
491
    
492
    ## Horizontal
493
    find = False
494
    startX = 0
495
    lineList = []
496
    for y in range(start, end):
497
        for x in range(0, img.shape[1]):
498
            if img[y,x] == 0:
499
                if find:
500
                    continue
501
                else:
502
                    find = True
503
                    startX = x
504
            else:
505
                if find:
506
                    if x - startX > minLineSize:
507
                        lineList.append(((startX,y), (x,y)))
508

    
509
                    find = False
510
    _queue.put(lineList)
클립보드 이미지 추가 (최대 크기: 500 MB)