프로젝트

일반

사용자정보

개정판 596f227d

ID596f227d82b0d67034cd331ec541e9b89fccd866
상위 0a4292cc
하위 3a3c2ea7, 1fcf20b4

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

setup HYTOS environment

Change-Id: I9805b21caba91b08775b4da53427cf2324a9cb86

차이점 보기:

DTI_PID/DTI_PID/App.py
11 11
from PyQt5 import QtWidgets
12 12

  
13 13
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
14
from AppDocData import AppDocData
15 14

  
16 15
class App(QApplication):
17 16
    """
18 17
    This is App class inherits from QApplication
19 18
    """
19

  
20
    NAME = 'HYTOS'  ### program name
21

  
20 22
    def __init__(self, args):
21 23
        import locale
24
        from AppDocData import AppDocData
22 25

  
23 26
        super(App, self).__init__(args)
24 27
        app_doc_data = AppDocData.instance()
......
96 99
    @history    18.04.23    Jeongwoo    Change method to execute ProjectDialog(dlg.exec_()→dlg.showDialog())
97 100
'''
98 101
if __name__ == '__main__':
99
    import cv2
100 102
    from License import QLicenseDialog
101 103
    from ProjectDialog import Ui_Dialog
102 104
    from MainWindow import MainWindow
DTI_PID/DTI_PID/AppDocData.py
6 6
import sqlite3
7 7
import datetime
8 8
from enum import Enum
9
from PIL import PngImagePlugin, JpegImagePlugin
10
from PIL import Image
11
from PIL.ImageQt import ImageQt
12 9

  
13 10
try:
14 11
    from PyQt5.QtCore import *
......
19 16
    from PyQt4.QtGui import *
20 17
import numpy as np
21 18

  
19
from App import App
22 20
from SingletonInstance import SingletonInstane
23 21
import symbol
24 22
from NominalPipeSize import NominalPipeSize
......
203 201
        @history    2018.06.29  Jeongwoo    Change method to get template db path
204 202
    '''
205 203
    def getTemplateDbPath(self):
206
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID')
204
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME)
207 205
        templateDbPath = os.path.join(path, 'Template.db')
208 206
        return templateDbPath
209 207

  
......
214 212
        @date       2018.10.01
215 213
        """
216 214

  
217
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID')
215
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME)
218 216
        app_database = os.path.join(path, 'App.db')
219 217
        return app_database 
220 218

  
......
565 563
    '''
566 564
    def buildAppDatabase(self):
567 565
        try:
568
            path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID')
566
            path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME)
569 567
            appDatabaseFilePath = os.path.join(path, 'App.db')
570 568

  
571 569
            # Creates or opens a file called mydb with a SQLite3 DB
......
602 600
    def loadAppStyle(self):
603 601
        style = 'Fusion'
604 602

  
605
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID')
603
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME)
606 604
        if not os.path.exists(path): os.makedirs(path)
607 605

  
608 606
        self.buildAppDatabase()
......
639 637
        try:
640 638
            self.buildAppDatabase()
641 639

  
642
            path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID')
640
            path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME)
643 641
            appDatabaseFilePath = os.path.join(path, 'App.db')
644 642

  
645 643
            # Creates or opens a file called mydb with a SQLite3 DB
......
754 752
        @history    humkyung 2018.04.19 return Project.db in Program Data folder instead of PROJECT_DB_PATH variable
755 753
    '''
756 754
    def getPrjDatabasePath(self):
757
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID')
755
        path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME)
758 756
        if not os.path.exists(path): os.makedirs(path)
759 757

  
760 758
        prjDatabaseFilePath = os.path.join(path, 'Project.db')
......
791 789
            @author euisung
792 790
            @date   2019.04.02
793 791
        '''
794
        return os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID', 'Explode.svg')
792
        return os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, 'Explode.svg')
795 793

  
796 794
    def updateTitleBlockProperties(self, titleBlockProps):
797 795
        '''
DTI_PID/DTI_PID/Commands/AreaOcrCommand.py
1
import os.path
2
import AbstractCommand
3
try:
4
    from PyQt5.QtCore import *
5
    from PyQt5.QtGui import *
6
    from PyQt5.QtWidgets import *
7
except ImportError:
8
    try:
9
        from PyQt4.QtCore import Qt, QPoint, QPointF, QRectF, pyqtSignal, QT_VERSION_STR, QBuffer, QRect, QRegExp
10
        from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QPainter, QColor, QPen, QBrush, QCursor, QTransform, QFont, QRegExpValidator, QValidator
11
    except ImportError:
12
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
13
import OcrResultDialog
14
import sys
15
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '\\Shapes')
16
from EngineeringTextItem import QEngineeringTextItem
17
from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
18
from EngineeringNoteItem import QEngineeringNoteItem
19
from TextItemFactory import TextItemFactory
20
from AppDocData import AppDocData
21
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
22

  
23
class AreaOcrCommand(AbstractCommand.AbstractCommand):
24
    onSuccess = pyqtSignal(float, float, float, float)
25
    onRejected = pyqtSignal(AbstractCommand.AbstractCommand)
26

  
27
    def __init__(self, imageViewer):
28
        super(AreaOcrCommand, self).__init__(imageViewer)
29
        self.name = 'AreaOcr' 
30
        self.imageViewer.setCursor(QCursor(Qt.CrossCursor))
31
        self._rubberBand = QRubberBand(QRubberBand.Rectangle, self.imageViewer)
32
        self._origin = QPoint()
33
        self.isLeftClicked = False
34
    
35
    '''
36
        @brief      pan image by left click and drag
37
        @author     Jeongwoo
38
        @date       18.04.19
39
        @history    18.04.20    Jeongwoo    Transform text box
40
                    humkyung 2018.04.20 set item's location with selection box
41
                    Jeongwoo 2018.04.25 Add if state with QEngineeringNoteItem
42
                    Jeongwoo 2018.04.26 Change method to create TextItem with TextItemFactory
43
                    Jeongwoo 2018.05.02 Add Checking imageViewer has image if-statement, when mouseReleaseEvent happen
44
                    humkyung 2018.08.29 use rubberband for selection text area
45
    '''
46
    def execute(self, param):
47
        event = param[1]
48
        scenePos = param[2]
49
        if 'mousePressEvent' == param[0]:
50
            if event.button() == Qt.LeftButton:
51
                self.isLeftClicked = True
52
                self._origin = event.pos()
53
                self._rubberBand.setGeometry(QRect(self._origin, QSize()))
54
                self._rubberBand.show()
55

  
56
            self.imageViewer.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y())
57
        elif 'mouseMoveEvent' == param[0] and event.buttons() == Qt.LeftButton:
58
            if self._rubberBand.isVisible():
59
                self._rubberBand.setGeometry(QRect(self._origin, event.pos()).normalized())
60
        elif 'mouseReleaseEvent' == param[0]:
61
            QGraphicsView.mouseReleaseEvent(self.imageViewer, event)
62
            try:
63
                if event.button() == Qt.LeftButton:
64
                    self.isLeftClicked = False
65
                if self.imageViewer.canZoom and event.button() == Qt.LeftButton:
66
                    self._rubberBand.hide()
67
                    topLeft = self.imageViewer.mapToScene(self._rubberBand.geometry().topLeft())
68
                    bottomRight = self.imageViewer.mapToScene(self._rubberBand.geometry().bottomRight())
69
                    rect = QRectF(topLeft, bottomRight)
70
                    if rect.isValid():
71
                        if self.imageViewer.hasImage():
72
                            rect = rect.toAlignedRect()
73
                            self.onSuccess.emit(rect.left(), rect.top(), rect.width(), rect.height())
74
                        else:
75
                            QMessageBox.about(self.imageViewer, self.tr('Notice'), self.tr('Please check the image.'))
76
                elif event.button() == Qt.RightButton:
77
                    if self.isLeftClicked == False:
78
                        self.onRejected.emit(self)
79
            finally:
80
                pass
81

  
82
        self.isTreated = False
83

  
84
    def undo(self):
85
        pass
86

  
87
    def redo(self):
88
        pass
DTI_PID/DTI_PID/Commands/RemoveTextCommand.py
1
import os.path
2
import AbstractCommand
3
try:
4
    from PyQt5.QtCore import Qt, QPoint, QPointF, QRectF, pyqtSignal, QT_VERSION_STR, QBuffer, QRect, QRegExp
5
    from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QPainter, QColor, QPen, QBrush, QCursor, QTransform, QFont, QRegExpValidator, QValidator
6
    from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QMessageBox
7
except ImportError:
8
    try:
9
        from PyQt4.QtCore import Qt, QPoint, QPointF, QRectF, pyqtSignal, QT_VERSION_STR, QBuffer, QRect, QRegExp
10
        from PyQt4.QtGui import QGraphicsView, QGraphicsScene, QImage, QPixmap, QPainterPath, QFileDialog, QPainter, QColor, QPen, QBrush, QCursor, QTransform, QFont, QRegExpValidator, QValidator
11
    except ImportError:
12
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
13
import sys
14
from AppDocData import AppDocData
15
from TextDetector import TextDetector
16
import numpy as np
17
from PIL import Image
18
import io
19
import cv2
20

  
21
class RemoveTextCommand(AbstractCommand.AbstractCommand):
22
    '''
23
        @history    2018.05.09  Jeongwoo    Draw Rect on ±1 area
24
                    2018.06.11  Jeongwoo    Change method to set image (setImage → setPixmap/updateViewer)
25
    '''
26
    def __init__(self, imageViewer):
27
        super(RemoveTextCommand, self).__init__(imageViewer)
28
        self.name = 'RemoveText' 
29
        #self.imageViewer.setCursor(QCursor(Qt.ArrowCursor))
30
        image = self.imageViewer.image()
31
        buffer = QBuffer()
32
        buffer.open(QBuffer.ReadWrite)
33
        image.save(buffer, "PNG")
34
        pyImage = Image.open(io.BytesIO(buffer.data()))
35
        dst = cv2.cvtColor(np.array(pyImage), cv2.COLOR_BGR2GRAY)
36
        textDetector = TextDetector()
37
        self.textInfoList = textDetector.detectTextAreas(dst, (0, 0))
38
        pixmap = self.imageViewer.pixmap()
39
        ADJUST = 1
40
        for textInfo in self.textInfoList:
41
            painter = QPainter()
42
            painter.begin(pixmap)
43
            painter.setPen(QColor(255, 255, 255))
44
            painter.setBrush(QColor(255, 255, 255))
45
            painter.drawRect(QRect(textInfo.getX()-ADJUST, textInfo.getY()-ADJUST, textInfo.getW()+ADJUST, textInfo.getH()+ADJUST))
46
            painter.end()
47
        pixmapHandle = self.imageViewer.getPixmapHandle()
48
        if pixmapHandle is not None:
49
            pixmapHandle.setPixmap(pixmap)
50
            self.imageViewer.setSceneRect(QRectF(pixmap.rect()))
51
            self.imageViewer.updateViewer()
52
    
53
    def execute(self, param):
54
        event = param[1]
55
        scenePos = param[2]
56
        self.isTreated = False
57

  
58
    def undo(self):
59
        pass
60

  
61
    def redo(self):
62
        pass
DTI_PID/DTI_PID/ConfigurationDialog.py
7 7
from PyQt5.QtGui import *
8 8
from PyQt5.QtWidgets import *
9 9
import sqlite3
10
from App import App
10 11
from AppDocData import AppDocData
11 12
from AppDocData import Config
12 13
from AppDocData import Color
......
46 47

  
47 48
        docData = AppDocData.instance()
48 49
        self.ui.comboBoxOCRData.addItem('eng')
49
        tessdata_path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID', 'Tesseract-OCR', 'tessdata')
50
        tessdata_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, 'Tesseract-OCR', 'tessdata')
50 51
        if os.path.isfile(os.path.join(tessdata_path, docData.getCurrentProject().getName() + '.traineddata')):
51 52
            self.ui.comboBoxOCRData.addItem(docData.getCurrentProject().getName())
52 53

  
DTI_PID/DTI_PID/ExceptionHandler.py
11 11
from PyQt5.QtSvg import *
12 12
from PyQt5 import QtWidgets
13 13
import logging
14
from datetime import datetime
14
from App import App
15 15

  
16 16
class QExceptionHandler(QObject):
17 17
    """ This is exception handler class """
......
21 21
    def __init__(self):
22 22
        super(QExceptionHandler, self).__init__()
23 23

  
24
        self.log_path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID', 'id2.log')
24
        self.log_path = os.path.join(os.getenv('ALLUSERSPROFILE'), App.NAME, App.NAME + '.log')
25 25
        self.logger = logging.getLogger(__name__)
26
        logging.basicConfig(filename=self.log_path , filemode='a', level=logging.CRITICAL)
27
        self.logger.critical(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
26
        logging.basicConfig(filename=self.log_path , filemode='w', level=logging.CRITICAL)
28 27

  
29 28
    def handler(self, exctype, value, traceback):
30 29
        """ log exception, file namd and line number """
DTI_PID/DTI_PID/MainWindow.py
10 10
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Commands')
11 11
import CreateCommand
12 12
import CropCommand
13
import AreaOcrCommand
14 13
import CreateSymbolCommand
15 14
import AreaZoomCommand
16 15
import FenceCommand
......
55 54
import ItemPropertyTableWidget
56 55
from UserInputAttribute import UserInputAttribute
57 56
from TextItemFactory import TextItemFactory
58
from TrainingImageListDialog import QTrainingImageListDialog
59 57
from TextDataListDialog import QTextDataListDialog
60 58
from DisplayColors import DisplayColors
61 59
from DisplayColors import DisplayOptions
DTI_PID/DTI_PID/OcrResultDialog.py
1
# coding: utf-8
2
"""
3
    This is ocr result dialog module
4
"""
5
from PIL import Image
6
import io
7
import numpy as np
8
import math
9
from PyQt5.QtCore import *
10
from PyQt5.QtGui import *
11
from PyQt5.QtWidgets import *
12
import OcrResultDialog_UI
13
import QtImageViewer
14
import tesseract_ocr_module as TOCR
15
from App import App
16
from AppDocData import *
17

  
18
class QOcrResultDialog(QDialog):
19
    def __init__(self, parent, qimage, boundingBox, isModify = False, text = None):
20
        QDialog.__init__(self, parent)
21
        self.textInfoList = []
22

  
23
        self.isModify = isModify
24
        self.image = qimage
25
        self.originImageWidth = qimage.width()
26
        self.originImageHeight = qimage.height()
27
        self.boundingBox = boundingBox
28

  
29
        self.angle = 0 # angle is degree
30

  
31
        self.ui = OcrResultDialog_UI.Ui_Dialog()
32
        self.ui.setupUi(self)
33
        
34
        appDocData = AppDocData.instance()
35
        configs = appDocData.getAppConfigs('app', 'mode')
36
        if configs and 1 == len(configs) and 'advanced' == configs[0].value:
37
            pass
38
        else:
39
            self.ui.pushButtonMakeTrainingImage.setVisible(False)
40

  
41
        self.imgW = qimage.width()
42
        self.imgH = qimage.height()
43
        self.image = self.image.scaled(self.imgW, self.imgH)
44
        self.graphicsView = QtImageViewer.QtImageViewer(App.mainWnd())
45
        self.graphicsView.useDefaultCommand() ##### USE DEFAULT COMMAND
46
        self.graphicsView.setImage(self.image)
47
        self.ui.horizontalLayoutGraphicsView.addWidget(self.graphicsView)
48
        
49
        self.ui.counterClockPushButton_2.clicked.connect(lambda : self.rotateImage(True))
50
        self.ui.clockPushButton_2.clicked.connect(lambda : self.rotateImage(False))
51
        self.ui.redetectPushButton_2.clicked.connect(self.detectText)
52
        self.ui.pushButtonMakeTrainingImage.clicked.connect(self.pushButtonMakeTrainingImageClicked)
53

  
54
        if self.isModify == False:
55
            self.detectText()
56
        else:
57
            self.ui.detectResultTextEdit.setPlainText(text)
58

  
59
        self.isAccepted = False
60

  
61
    '''
62
        @brief      Make OCR Training Image
63
        @author     euisung
64
        @date       2018.10.16
65
        @history    euisung     2018.11.02       add notice push
66
    '''
67
    def pushButtonMakeTrainingImageClicked(self):
68
        import uuid
69
        uid = str(uuid.uuid4()) + '.png'
70
        appDocData = AppDocData.instance()
71
        project = appDocData.getCurrentProject()
72
        trainingImgPath = os.path.join(project.getTrainingFilePath(), uid)
73

  
74
        self.image.save(trainingImgPath)
75
        QMessageBox.about(self, self.tr("INFO"), self.tr('Successfully saved.'))
76
        QDialog.reject(self)
77
        
78
    def rotateImage(self, isCounterClock):
79
        for item in self.graphicsView.scene.items():
80
            self.graphicsView.scene.removeItem(item)
81
        self.graphicsView.clearImage()
82
        transform = QTransform()
83
        if isCounterClock:
84
            '''CounterClock'''
85
            self.angle = (self.angle - 90) % 360
86
            transform.rotate(-90)
87
        else:
88
            '''Clock'''
89
            self.angle = (self.angle - 270) % 360
90
            transform.rotate(90)
91
        #print(str(360 - self.angle))
92
        self.image = self.image.transformed(transform)
93
        self.graphicsView.setImage(self.image)
94
        self.textInfoList = []
95

  
96
    '''
97
        @history 2018.04.26 Jeongwoo    Add Rectangle with modified Coords
98
                 2018.06.20 Jeongwoo    Remove test code
99
                 2018.11.08 euisung     add white char list check process on db
100
                 2018.11.22 euisung     OCR lang apply fixed
101
    '''
102
    def detectText(self):
103
        try:
104
            buffer = QBuffer()
105
            buffer.open(QBuffer.ReadWrite)
106
            self.image.save(buffer, "PNG")
107
            pyImage = Image.open(io.BytesIO(buffer.data()))
108
            img = np.array(pyImage)
109
            
110
            #self.image.save('c:\\temp\\a.png')
111

  
112
            docData = AppDocData.instance()
113
            
114
            # get ocr data of area which has the text
115
            pt = self.boundingBox.center()
116
            areas = [area for area in docData.getAreaList() if area.contains((pt.x(), pt.y()))]
117
            ocr_data = sorted(areas, key=lambda attr: attr.area)[0].OCRData if areas else 'eng'
118
            # up to here
119

  
120
            whiteCharList = docData.getConfigs('Text Recognition', 'White Character List')
121
            if len(whiteCharList) is 0:
122
                self.textInfoList = TOCR.getTextInfo(img, (round(self.boundingBox.x()), round(self.boundingBox.y())), 0, language=ocr_data)
123
            else:
124
                self.textInfoList = TOCR.getTextInfo(img, (round(self.boundingBox.x()), round(self.boundingBox.y())), 0, language=ocr_data, conf = whiteCharList[0].value)
125

  
126
            if self.textInfoList is not None and len(self.textInfoList) > 0:
127
                self.ui.detectResultTextEdit.setText(self.getPlainText(self.textInfoList))
128
                for textInfo in self.textInfoList:
129
                    self.graphicsView.scene.addRect(textInfo.getX()-int(self.boundingBox.x()), textInfo.getY()-int(self.boundingBox.y()), textInfo.getW(), textInfo.getH(), QPen(Qt.red, 1, Qt.SolidLine))
130
            else:
131
                self.ui.detectResultTextEdit.setText("Not Found")
132
        except Exception as ex:
133
            from App import App 
134
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
135
            App.mainWnd().addMessage.emit(MessageType.Error, message)
136

  
137
    def getPlainText(self, textInfoList):
138
        text = ''
139
        for index in range(len(textInfoList)):
140
            textInfo = textInfoList[index]
141
            if index != 0:
142
                text = text + '\n'
143
            text = text + textInfo.getText()
144
        return text
145

  
146
    '''
147
        @brief      OK Button Clicked. Remake TextInfo object
148
        @author     Jeongwoo
149
        @date       18.04.19
150
        @history    18.04.20    Jeongwoo    Calculate Start Point Coordinates by rotated angle
151
                    18.04.26    Jeongwoo    Scene.itemAt(textX - boundBox.x(), textY - boundBox.y())
152
    '''
153
    def accept(self):
154
        self.isAccepted = True
155

  
156
        try:
157
            text = self.ui.detectResultTextEdit.toPlainText()
158
            if text == '' or text == 'Not Found':
159
                QMessageBox.about(self.ui.ocrDialogButtonBox, 'Notice', 'Please try again after recognition or type.')
160
                return
161

  
162
            splitText = text.split('\n')
163

  
164
            if not len(self.textInfoList) > 0:
165
                import cv2, sys
166
                from TextInfo import TextInfo
167
                #QMessageBox.about(self.ui.ocrDialogButtonBox, "알림", "텍스트 검출을 하신 후 다시 시도해주세요.")
168
                
169
                buffer = QBuffer()
170
                buffer.open(QBuffer.ReadWrite)
171
                self.image.save(buffer, "PNG")
172
                pyImage = Image.open(io.BytesIO(buffer.data()))
173
                img = np.array(pyImage)
174

  
175
                img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
176
                imgNot = np.ones(img.shape, np.uint8)
177
                cv2.bitwise_not(img, imgNot)
178
                imgNot = cv2.dilate(imgNot, np.ones((8,8), np.uint8))
179

  
180
                image, contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
181
                minX, minY, maxX, maxY = sys.maxsize, sys.maxsize, 0 ,0
182
                if len(contours) is 0:
183
                    minX, minY, maxX, maxY = self.boundingBox.x(), self.boundingBox.y(), self.boundingBox.x() + self.image.width(), self.boundingBox.y() + self.image.height()
184
                else:
185
                    minX, minY, maxX, maxY = sys.maxsize, sys.maxsize, 0, 0
186
                    for cnt in contours:
187
                        x, y, w, h = cv2.boundingRect(cnt)
188
                        minX = min(x ,minX)
189
                        minY = min(y, minY)
190
                        maxX = max(x + w, maxX)
191
                        maxY = max(y + h, maxY)
192
                    minX, minY, maxX, maxY = minX + self.boundingBox.x(), minY + self.boundingBox.y(), maxX + self.boundingBox.x(), maxY + self.boundingBox.y()
193
                
194
                self.textInfoList.append(TextInfo(text, minX, minY, maxX - minX, maxY - minY, 0))
195

  
196
            if len(self.textInfoList) > 0:
197
                for index in range(len(splitText)):
198
                    textInfo = self.textInfoList[index]
199
                    item = self.graphicsView.scene.itemAt(QPointF(float(textInfo.getX() - int(self.boundingBox.x())), float(textInfo.getY()-int(self.boundingBox.y()))), QTransform())
200
                    if item is not None:
201
                        ## Transform rectangle for calculate start point
202
                        imgTransform = QTransform()
203
                        if self.angle == 90 or self.angle == 270:
204
                            imgTransform.translate(self.image.height()*0.5, self.image.width()*0.5)
205
                        elif self.angle == 0 or self.angle == 360:
206
                            imgTransform.translate(self.image.width()*0.5, self.image.height()*0.5)
207
                        imgTransform.rotate(-abs(self.angle))
208
                        imgTransform.translate(-self.image.width()*0.5, -self.image.height()*0.5)
209
                        rect = QRect(textInfo.getX() - int(self.boundingBox.x()), textInfo.getY() - int(self.boundingBox.y()), textInfo.getW(), textInfo.getH())
210
                        rect = imgTransform.mapRect(rect)
211
                        ## up to here
212
                        textInfo.setX(rect.x() + int(self.boundingBox.x()))
213
                        textInfo.setY(rect.y() + int(self.boundingBox.y()))
214
                        textInfo.setText(splitText[index])
215
                        radian = round(math.radians(abs(self.angle)), 2)
216
                        textInfo.setAngle(radian) # 360 degree == 6.28319 radian
217
                        if radian == 1.57 or radian == 4.71:
218
                            width = textInfo.getW()
219
                            height = textInfo.getH()
220
                            textInfo.setW(height) ## SWAP
221
                            textInfo.setH(width) ## SWAP
222
                self.textInfoList = self.textInfoList[:len(splitText)]
223

  
224
                QDialog.accept(self)
225

  
226
        except Exception as ex:
227
            from App import App 
228
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
229
            App.mainWnd().addMessage.emit(MessageType.Error, message)
230

  
231
    def reject(self):
232
        self.isAccepted = False
233
        self.textInfoList = None
234
        QDialog.reject(self)
235

  
236
    '''
237
        @brief  Display this QDialog
238
    '''
239
    def showDialog(self):
240
        #self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
241
        self.exec_()
242
        return (self.isAccepted, self.textInfoList)
DTI_PID/DTI_PID/OcrResultDialog_UI.py
1
# -*- coding: utf-8 -*-
2

  
3
# Form implementation generated from reading ui file '.\UI\OcrResultDialog.ui'
4
#
5
# Created by: PyQt5 UI code generator 5.11.3
6
#
7
# WARNING! All changes made in this file will be lost!
8

  
9
from PyQt5 import QtCore, QtGui, QtWidgets
10

  
11
class Ui_Dialog(object):
12
    def setupUi(self, Dialog):
13
        Dialog.setObjectName("Dialog")
14
        Dialog.resize(1080, 650)
15
        Dialog.setMinimumSize(QtCore.QSize(1080, 650))
16
        font = QtGui.QFont()
17
        font.setFamily("맑은 고딕")
18
        font.setBold(True)
19
        font.setWeight(75)
20
        Dialog.setFont(font)
21
        self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
22
        self.gridLayout_2.setObjectName("gridLayout_2")
23
        self.splitter = QtWidgets.QSplitter(Dialog)
24
        self.splitter.setOrientation(QtCore.Qt.Vertical)
25
        self.splitter.setObjectName("splitter")
26
        self.topWidget = QtWidgets.QWidget(self.splitter)
27
        self.topWidget.setMinimumSize(QtCore.QSize(0, 150))
28
        self.topWidget.setObjectName("topWidget")
29
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.topWidget)
30
        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
31
        self.verticalLayout_2.setObjectName("verticalLayout_2")
32
        self.tVerticalLayout_2 = QtWidgets.QVBoxLayout()
33
        self.tVerticalLayout_2.setObjectName("tVerticalLayout_2")
34
        self.horizontalLayout = QtWidgets.QHBoxLayout()
35
        self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
36
        self.horizontalLayout.setContentsMargins(8, 8, 8, 8)
37
        self.horizontalLayout.setObjectName("horizontalLayout")
38
        self.targetTextLabel_2 = QtWidgets.QLabel(self.topWidget)
39
        self.targetTextLabel_2.setMinimumSize(QtCore.QSize(0, 60))
40
        self.targetTextLabel_2.setMaximumSize(QtCore.QSize(16777215, 60))
41
        font = QtGui.QFont()
42
        font.setBold(True)
43
        font.setWeight(75)
44
        self.targetTextLabel_2.setFont(font)
45
        self.targetTextLabel_2.setObjectName("targetTextLabel_2")
46
        self.horizontalLayout.addWidget(self.targetTextLabel_2)
47
        self.counterClockPushButton_2 = QtWidgets.QPushButton(self.topWidget)
48
        self.counterClockPushButton_2.setMinimumSize(QtCore.QSize(0, 60))
49
        self.counterClockPushButton_2.setMaximumSize(QtCore.QSize(60, 60))
50
        self.counterClockPushButton_2.setText("")
51
        icon = QtGui.QIcon()
52
        icon.addPixmap(QtGui.QPixmap(":/newPrefix/Rotate_Minus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
53
        self.counterClockPushButton_2.setIcon(icon)
54
        self.counterClockPushButton_2.setIconSize(QtCore.QSize(32, 32))
55
        self.counterClockPushButton_2.setObjectName("counterClockPushButton_2")
56
        self.horizontalLayout.addWidget(self.counterClockPushButton_2)
57
        self.clockPushButton_2 = QtWidgets.QPushButton(self.topWidget)
58
        self.clockPushButton_2.setMinimumSize(QtCore.QSize(0, 60))
59
        self.clockPushButton_2.setMaximumSize(QtCore.QSize(60, 60))
60
        self.clockPushButton_2.setText("")
61
        icon1 = QtGui.QIcon()
62
        icon1.addPixmap(QtGui.QPixmap(":/newPrefix/Rotate_Plus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
63
        self.clockPushButton_2.setIcon(icon1)
64
        self.clockPushButton_2.setIconSize(QtCore.QSize(32, 32))
65
        self.clockPushButton_2.setObjectName("clockPushButton_2")
66
        self.horizontalLayout.addWidget(self.clockPushButton_2)
67
        self.redetectPushButton_2 = QtWidgets.QPushButton(self.topWidget)
68
        self.redetectPushButton_2.setMinimumSize(QtCore.QSize(0, 60))
69
        self.redetectPushButton_2.setMaximumSize(QtCore.QSize(60, 60))
70
        self.redetectPushButton_2.setText("")
71
        icon2 = QtGui.QIcon()
72
        icon2.addPixmap(QtGui.QPixmap(":/newPrefix/OCR.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
73
        self.redetectPushButton_2.setIcon(icon2)
74
        self.redetectPushButton_2.setIconSize(QtCore.QSize(32, 32))
75
        self.redetectPushButton_2.setObjectName("redetectPushButton_2")
76
        self.horizontalLayout.addWidget(self.redetectPushButton_2)
77
        self.tVerticalLayout_2.addLayout(self.horizontalLayout)
78
        self.horizontalLayoutGraphicsView = QtWidgets.QHBoxLayout()
79
        self.horizontalLayoutGraphicsView.setObjectName("horizontalLayoutGraphicsView")
80
        self.tVerticalLayout_2.addLayout(self.horizontalLayoutGraphicsView)
81
        self.verticalLayout_2.addLayout(self.tVerticalLayout_2)
82
        self.bottomWidget = QtWidgets.QWidget(self.splitter)
83
        self.bottomWidget.setMinimumSize(QtCore.QSize(0, 150))
84
        self.bottomWidget.setObjectName("bottomWidget")
85
        self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.bottomWidget)
86
        self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
87
        self.verticalLayout_4.setObjectName("verticalLayout_4")
88
        self.detectResultVerticalLayout_2 = QtWidgets.QVBoxLayout()
89
        self.detectResultVerticalLayout_2.setContentsMargins(8, 8, 8, 8)
90
        self.detectResultVerticalLayout_2.setObjectName("detectResultVerticalLayout_2")
91
        self.detectResultLabel_2 = QtWidgets.QLabel(self.bottomWidget)
92
        font = QtGui.QFont()
93
        font.setBold(True)
94
        font.setWeight(75)
95
        self.detectResultLabel_2.setFont(font)
96
        self.detectResultLabel_2.setObjectName("detectResultLabel_2")
97
        self.detectResultVerticalLayout_2.addWidget(self.detectResultLabel_2)
98
        self.detectResultTextEdit = QtWidgets.QTextEdit(self.bottomWidget)
99
        font = QtGui.QFont()
100
        font.setFamily("Consolas")
101
        font.setPointSize(15)
102
        self.detectResultTextEdit.setFont(font)
103
        self.detectResultTextEdit.setObjectName("detectResultTextEdit")
104
        self.detectResultVerticalLayout_2.addWidget(self.detectResultTextEdit)
105
        self.verticalLayout_4.addLayout(self.detectResultVerticalLayout_2)
106
        self.gridLayout_2.addWidget(self.splitter, 1, 0, 1, 1)
107
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
108
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
109
        self.pushButtonMakeTrainingImage = QtWidgets.QPushButton(Dialog)
110
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
111
        sizePolicy.setHorizontalStretch(0)
112
        sizePolicy.setVerticalStretch(0)
113
        sizePolicy.setHeightForWidth(self.pushButtonMakeTrainingImage.sizePolicy().hasHeightForWidth())
114
        self.pushButtonMakeTrainingImage.setSizePolicy(sizePolicy)
115
        self.pushButtonMakeTrainingImage.setObjectName("pushButtonMakeTrainingImage")
116
        self.horizontalLayout_2.addWidget(self.pushButtonMakeTrainingImage)
117
        self.ocrDialogButtonBox = QtWidgets.QDialogButtonBox(Dialog)
118
        self.ocrDialogButtonBox.setOrientation(QtCore.Qt.Horizontal)
119
        self.ocrDialogButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
120
        self.ocrDialogButtonBox.setObjectName("ocrDialogButtonBox")
121
        self.horizontalLayout_2.addWidget(self.ocrDialogButtonBox)
122
        self.gridLayout_2.addLayout(self.horizontalLayout_2, 3, 0, 1, 1)
123

  
124
        self.retranslateUi(Dialog)
125
        self.ocrDialogButtonBox.accepted.connect(Dialog.accept)
126
        self.ocrDialogButtonBox.rejected.connect(Dialog.reject)
127
        QtCore.QMetaObject.connectSlotsByName(Dialog)
128

  
129
    def retranslateUi(self, Dialog):
130
        _translate = QtCore.QCoreApplication.translate
131
        Dialog.setWindowTitle(_translate("Dialog", "OCR"))
132
        self.targetTextLabel_2.setText(_translate("Dialog", "Recognition Object"))
133
        self.counterClockPushButton_2.setToolTip(_translate("Dialog", "반시계 방향 회전"))
134
        self.clockPushButton_2.setToolTip(_translate("Dialog", "시계 방향 회전"))
135
        self.detectResultLabel_2.setText(_translate("Dialog", "Recognition Result"))
136
        self.pushButtonMakeTrainingImage.setText(_translate("Dialog", "Save OCR Training Image"))
137

  
138
import MainWindow_rc
139

  
140
if __name__ == "__main__":
141
    import sys
142
    app = QtWidgets.QApplication(sys.argv)
143
    Dialog = QtWidgets.QDialog()
144
    ui = Ui_Dialog()
145
    ui.setupUi(Dialog)
146
    Dialog.show()
147
    sys.exit(app.exec_())
148

  
DTI_PID/DTI_PID/Shapes/EngineeringLineItem.py
1 1
# coding: utf-8
2 2
""" This is engineering line item module """
3 3
import sys
4
import cv2
5 4
import os
6 5

  
7 6
try:
DTI_PID/DTI_PID/Shapes/EngineeringLineNoTextItem.py
18 18
from EngineeringPolylineItem import QEngineeringPolylineItem
19 19
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
20 20
from UserInputAttribute import UserInputAttribute
21
from OcrResultDialog import QOcrResultDialog
22 21
from AppDocData import AppDocData
23 22
from EngineeringTextItem import QEngineeringTextItem
24 23
from TextInfo import TextInfo
DTI_PID/DTI_PID/Shapes/EngineeringNoteItem.py
15 15

  
16 16
from EngineeringPolylineItem import QEngineeringPolylineItem
17 17
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
18
import OcrResultDialog
19 18
from AppDocData import AppDocData
20 19
from EngineeringTextItem import QEngineeringTextItem
21 20
import re
DTI_PID/DTI_PID/Shapes/EngineeringValveOperCodeTextItem.py
18 18

  
19 19
from EngineeringPolylineItem import QEngineeringPolylineItem
20 20
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
21
import OcrResultDialog
22 21
from AppDocData import AppDocData, MessageType
23 22
from EngineeringTextItem import QEngineeringTextItem
24 23
from SymbolSvgItem import SymbolSvgItem
DTI_PID/DTI_PID/Shapes/QEngineeringSizeTextItem.py
15 15

  
16 16
from EngineeringPolylineItem import QEngineeringPolylineItem
17 17
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
18
import OcrResultDialog
19 18
from AppDocData import AppDocData, MessageType
20 19
from EngineeringTextItem import QEngineeringTextItem
21 20
from SymbolSvgItem import SymbolSvgItem
DTI_PID/DTI_PID/Shapes/QEngineeringTagNoTextItem.py
17 17

  
18 18
from EngineeringPolylineItem import QEngineeringPolylineItem
19 19
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
20
import OcrResultDialog
21 20
from AppDocData import AppDocData,MessageType
22 21
from EngineeringTextItem import QEngineeringTextItem
23 22
from EngineeringEquipmentItem import QEngineeringEquipmentItem
DTI_PID/DTI_PID/Shapes/QEngineeringTrimLineNoTextItem.py
19 19

  
20 20
from EngineeringPolylineItem import QEngineeringPolylineItem
21 21
from GraphicsBoundingBoxItem import QGraphicsBoundingBoxItem
22
import OcrResultDialog
23 22
from AppDocData import AppDocData
24 23
from EngineeringLineNoTextItem import QEngineeringLineNoTextItem
25 24

  
DTI_PID/DTI_PID/SymbolEditorDialog.py
17 17
from AppDocData import * 
18 18

  
19 19
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '\\Commands')
20
import CropCommand, HandCommand, ZoomCommand, PenCommand, EraserCommand, AreaEraserCommand, OriginalPointCommand, ConnectionPointCommand, AreaZoomCommand, FitImageCommand, RemoveTextCommand, RotateImageCommand, FlipImageCommand
20
import CropCommand, HandCommand, ZoomCommand, PenCommand, EraserCommand, AreaEraserCommand, OriginalPointCommand, ConnectionPointCommand, AreaZoomCommand, FitImageCommand, RotateImageCommand, FlipImageCommand
21 21

  
22 22

  
23 23
class QSymbolEditorDialog(QDialog):
DTI_PID/DTI_PID/TextDetector.py
1
# coding: utf-8
2
"""
3
    This is text detector module
4
"""
5
import sys
6
import os
7
import cv2
8
import numpy as np
9
from PyQt5.QtCore import *
10
from PyQt5.QtGui import *
11
from PyQt5.QtWidgets import *
12
from PyQt5.QtSvg import *
13

  
14
from AppDocData import *
15
import TextInfo as ti
16
import tesseract_ocr_module as TOCR
17

  
18
MIN_TEXT_SIZE = 10
19
THREAD_MAX_WORKER = os.cpu_count()
20

  
21
class TextDetector:
22
    '''
23
        @brief  constructor
24
        @author humkyung
25
        @date   2018.07.11
26
    '''
27
    def __init__(self):
28
        self.textInfoList = []
29
        self.otherTextInfoList = []
30
        self.titleBlockTextInfoList = []
31

  
32
    '''
33
        @brief  detect text areas
34
        @author humkyung
35
        @date   2018.06.16
36
    '''
37
    def detectTextAreas(self, img, offset):
38
        tInfoList = []
39
        try:
40
            tInfoList = self.getTextAreaInfo(img, offset[0], offset[1])
41
        except Exception as ex:
42
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
43

  
44
        return tInfoList
45

  
46
    '''
47
        @brief      Get Text Area info by contour
48
        @author     Jeongwoo
49
        @date       2018.06.05
50
        @history    2018.06.08  Jeongwoo    Add angle
51
                    humkyung 2018.06.18 fixed logic to detect text area
52
    '''
53
    def getTextAreaInfo(self, imgGray, offsetX, offsetY):
54
        from AppDocData import AppDocData
55

  
56
        appDocData = AppDocData.instance()
57
        project = appDocData.getCurrentProject()
58

  
59
        configs = appDocData.getConfigs('Text Size', 'Max Text Size')
60
        maxTextSize = int(configs[0].value) if 1 == len(configs) else 100
61
        minSize = 5
62

  
63
        contourImg = np.ones(imgGray.shape, np.uint8) * 255
64
        binaryImg,mask = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
65

  
66
        image, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
67
        for contour in contours:
68
            # remove too big one or horizontal/vertical line
69
            [x, y, w, h] = cv2.boundingRect(contour)
70
            area = cv2.contourArea(contour, True)
71

  
72
            # skip one which size is greater than max size or less then minimum size
73
            if area >= 0:
74
                if (w > maxTextSize or h > maxTextSize) or (w <= minSize and h <= minSize): continue
75

  
76
            if area >= 0:
77
                cv2.drawContours(contourImg, [contour], -1, (0,0,0), -1)
78
                cv2.drawContours(contourImg, [contour], -1, (255,255,255), 1)
79
            else:
80
                cv2.drawContours(contourImg, [contour], -1, (255,255,255), -1)
81
                
82
        path = os.path.join(project.getTempPath(), 'OCR_{}.png'.format(appDocData.imgName))
83
        cv2.imwrite(path, contourImg)
84

  
85
        rects = []
86
        configs = appDocData.getConfigs('Text Recognition', 'Expand Size')
87
        expandSize = int(configs[0].value) if 1 == len(configs) else 10
88
        configs = appDocData.getConfigs('Text Recognition', 'Shrink Size')
89
        shrinkSize = int(configs[0].value) if 1 == len(configs) else 0
90

  
91
        eroded = cv2.erode(contourImg, np.ones((expandSize,expandSize), np.uint8))
92
        #path = os.path.join(project.getTempPath(), 'ERODED_OCR_{}.png'.format(appDocData.imgName))
93
        #cv2.imwrite(path, eroded)
94

  
95
        eroded = cv2.bitwise_not(eroded)
96
        
97
        image, contours, hierarchy = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
98
        for contour in contours:
99
            area = cv2.contourArea(contour, True)
100
            if area < 0:
101
                [x, y, w, h] = cv2.boundingRect(contour)
102
                
103
                img = contourImg[y:y+h, x:x+w]
104
                img = cv2.bitwise_not(img)
105

  
106
                horizontal,max_width = 0,0
107
                vertical,max_height = 0,0
108
                _, _contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
109
                for xx in _contours:
110
                    [_x, _y, _w, _h] = cv2.boundingRect(xx)
111
                    cv2.rectangle(img, (_x, _y), (_x+_w, _y+_h), 255, 1)
112

  
113
                    max_width = _x if _x > max_width else max_width
114
                    max_height = _y if _y > max_height else max_height
115

  
116
                    if (_w < _h) or (_w > maxTextSize and _h < maxTextSize): # width is greater than height
117
                        horizontal += 1 + (_w*_h)/(w*h)
118
                    else:
119
                        vertical += 1 + (_w*_h)/(w*h)
120

  
121
                if (w < 10 and h < 10) or (max_width > maxTextSize and max_height > maxTextSize): continue; # skip too small or big one
122
                
123
                """
124
                if w > maxTextSize:
125
                    horizontal = 1
126
                elif h > maxTextSize:
127
                    vertical = 1
128
                else:
129
                    if shrinkSize > 0:
130
                        img = cv2.erode(img, np.ones((shrinkSize,shrinkSize), np.uint8))
131

  
132
                    _, _contours, _ = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
133
                    for xx in _contours:
134
                        [_x, _y, _w, _h] = cv2.boundingRect(xx)
135
                        cv2.rectangle(img, (_x, _y), (_x+_w, _y+_h), 255, 1)
136

  
137
                        if (_w < _h) or (_w > maxTextSize and _h < maxTextSize): # width is greater than height
138
                            horizontal += 1 + (_w*_h)/(w*h)
139
                        else:
140
                            vertical += 1 + (_w*_h)/(w*h)
141
                """
142

  
143
                """
144
                if horizontal > vertical:
145
                    filePath = os.path.join(project.getTempPath(), "Tile", "H-{}-{}-{}-{}.png".format(x,y,w,h))
146
                else:
147
                    filePath = os.path.join(project.getTempPath(), "Tile", "V-{}-{}-{}-{}.png".format(x,y,w,h))
148
                cv2.imwrite(filePath, img)
149
                """
150

  
151
                rects.append([0 if horizontal > vertical else 90, QRect(x, y, w, h)])
152

  
153
        configs = appDocData.getConfigs('Text Recognition', 'Merge Size')
154
        mergeSize = int(configs[0].value) if 1 == len(configs) else 10
155
        # merge rectangles
156
        intersected = True
157
        while intersected:
158
            intersected = False
159
            for rect in rects[:]:   # clone rects
160
                if 0 == rect[0]:
161
                    rectExpand = rect[1].adjusted(-mergeSize, 0, mergeSize, 0)
162
                else:
163
                    rectExpand = rect[1].adjusted(0, -mergeSize, 0, mergeSize)
164

  
165
                matches = [x for x in rects if (x[0] == rect[0]) and rectExpand.intersects(x[1])]
166
                if len(matches) > 1:
167
                    united = matches[0]
168
                    for _rect in matches:
169
                        united[1] = united[1].united(_rect[1])
170
                        if _rect in rects: rects.remove(_rect)
171
                    rects.append(united)
172
                    intersected = True
173
                    break
174

  
175
        list = []
176
        for rect in rects:
177
            angle = rect[0]
178
            list.append(ti.TextInfo('', round(offsetX) + rect[1].x(), round(offsetY) + rect[1].y(), rect[1].width(), rect[1].height(), angle))
179

  
180
            x = rect[1].x()
181
            y = rect[1].y()
182
            w = rect[1].width()
183
            h = rect[1].height()
184
            img = contourImg[y:y+h, x:x+w]
185
            ## DEBUG
186
            #if angle == 0:
187
            #    filePath = os.path.join(project.getTempPath(), "Tile", "H-{}-{}-{}-{}.png".format(x,y,w,h))
188
            #else:
189
            #    filePath = os.path.join(project.getTempPath(), "Tile", "V-{}-{}-{}-{}.png".format(x,y,w,h))
190
            #cv2.imwrite(filePath, img)
191
            ## up to here
192

  
193
        return list
194

  
195
    '''
196
        @brief      recognize text of given text info
197
        @author     humkyung
198
        @date       2018.07.24
199
        @history    change parameter updateProgressSignal to worker
200
                    2018.11.08 euisung     add white char list check process on db
201
    '''
202
    @staticmethod
203
    def recognizeTextFromImage(tInfo, imgOCR, offset, searchedSymbolList, worker, listWidget, maxProgressValue):
204
        import re
205
        res = []
206

  
207
        appDocData = AppDocData.instance()
208

  
209
        try:
210
            x = tInfo.getX() - round(offset[0])
211
            y = tInfo.getY() - round(offset[1])
212
            img = imgOCR[y:y+tInfo.getH(), x:x+tInfo.getW()]
213

  
214
            # set angle 0 if symbol contains the text area is instrumentation
215
            category = None
216
            contains = [symbol for symbol in searchedSymbolList if symbol.contains(tInfo)]
217
            if contains:
218
                _type = contains[0].getType()
219
                category = appDocData.getSymbolCategoryByType(_type)
220
                if 'Instrumentation' == category: tInfo.setAngle(0)
221
            # up to here
222

  
223
            whiteCharList = appDocData.getConfigs('Text Recognition', 'White Character List')
224
            if len(whiteCharList) is 0:
225
                resultTextInfo = TOCR.getTextInfo(img, (x, y), tInfo.getAngle(), language=appDocData.OCRData)
226
            else:
227
                resultTextInfo = TOCR.getTextInfo(img, (x, y), tInfo.getAngle(), language=appDocData.OCRData, conf = whiteCharList[0].value)
228

  
229
            if resultTextInfo is not None and len(resultTextInfo) > 0:
230
                for result in resultTextInfo:
231
                    result.setX(result.getX() + round(offset[0]))
232
                    result.setY(result.getY() + round(offset[1]))
233
                    if 'Instrumentation' == category:
234
                        text = re.sub('[^a-zA-Z0-9]+', '', result.getText())
235
                        result.setText(text)
236
                res.extend(resultTextInfo)
237
                
238
                if listWidget is not None:
239
                    item = QListWidgetItem('{},{},{} is recognized'.format(resultTextInfo[0].getX(), resultTextInfo[0].getY(), resultTextInfo[0].getText()))
240
                    listWidget.addItem(item)
241
            else:
242
                pass
243

  
244
            if worker is not None: worker.updateProgress.emit(maxProgressValue, resultTextInfo[0].getText() if resultTextInfo is not None and 1 == len(resultTextInfo) else None)
245
        except Exception as ex:
246
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
247
            worker.displayLog.emit(MessageType.Error, message)
248

  
249
        return res
250

  
251
    '''
252
        @brief      read image drawing and then remove text
253
        @author     jwkim
254
        @date       
255
        @history    humkyung 2018.04.06 check if file exists
256
                    Jeongwoo 2018.05.09 Use Tesseract OCR after Azure OCR (Azure OCR : Getting text area)
257
                    Jeongwoo 2018.05.25 Add condition on if-statement
258
                    Jeongwoo 2018.06.05 Get text area data list by config.type
259
                    Jeongwoo 2018.06.08 Add angle Parameter on TOCR.getTextInfo
260
                    humkyung 2018.06.16 update proessbar while recognizing text
261
                    humkyung 2018.07.03 remove white space and replace given oldStr with newStr
262
                    humkyung 2018.07.07 change method name to recognizeText
263
                    euisung  2018.11.08 add white char list check process on db
264
                    euisung  2018.11.12 add title block properties
265
    '''
266
    def recognizeText(self, imgSrc, offset, tInfoList, searchedSymbolList, worker, listWidget, maxProgressValue, onlyTextArea = False):
267
        import concurrent.futures as futures
268
        from Area import Area
269

  
270
        try:
271
            self.otherTextInfoList = []
272
            self.titleBlockTextInfoList = []
273
            self.textInfoList = []
274
            
275
            appDocData = AppDocData.instance()
276
            project = appDocData.getCurrentProject()
277

  
278
            path = os.path.join(project.getTempPath(), 'OCR_{}.png'.format(appDocData.imgName))
279
            if os.path.isfile(path):
280
                imgOCR = cv2.imread(path, 1)
281
                imgOCR = cv2.threshold(cv2.cvtColor(imgOCR, cv2.COLOR_BGR2GRAY), 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
282

  
283
                pool = futures.ThreadPoolExecutor(max_workers = THREAD_MAX_WORKER)
284
                for tInfo in tInfoList:
285
                    future = pool.submit(TextDetector.recognizeTextFromImage, tInfo, imgOCR, offset, searchedSymbolList, worker, listWidget, maxProgressValue)
286
                    data = future.result()
287
                    if data: self.textInfoList.extend(data)
288
                pool.shutdown(wait = True)
289

  
290
                if onlyTextArea:
291
                    return
292
                # parse texts in area except Drawing area
293
                whiteCharList = appDocData.getConfigs('Text Recognition', 'White Character List')
294
                for area in appDocData.getAreaList():
295
                    if area.name == 'Drawing': continue
296

  
297
                    if area.name == 'Unit':
298
                        img = imgSrc[round(area.y):round(area.y+area.height), round(area.x):round(area.x+area.width)]
299
                        if len(whiteCharList) is 0:
300
                            texts = TOCR.getTextInfo(img, (area.x, area.y), 0, language='eng')
301
                        else:
302
                            texts = TOCR.getTextInfo(img, (area.x, area.y), 0, language='eng', conf = whiteCharList[0].value)
303
                        if texts is not None and len(texts) > 0:
304
                            appDocData.activeDrawing.setAttr('Unit', texts[0].getText())
305
                            self.otherTextInfoList.append([area.name, texts])
306
                    else:
307
                        if area is not None and hasattr(area, 'img') and area.img is not None:
308
                            if len(whiteCharList) is 0:
309
                                texts = TOCR.getTextInfo(area.img, (area.x, area.y), 0, language='eng')
310
                            else:
311
                                texts = TOCR.getTextInfo(area.img, (area.x, area.y), 0, language='eng', conf=whiteCharList[0].value)
312
                            self.otherTextInfoList.append([area.name, texts])
313

  
314
                titleBlockProps = appDocData.getTitleBlockProperties()
315
                for titleBlockProp in titleBlockProps:
316
                    area = Area(titleBlockProp[0])
317
                    area.parse(titleBlockProp[2])
318
                    img = imgSrc[round(area.y):round(area.y+area.height), round(area.x):round(area.x+area.width)]
319
                    if len(whiteCharList) is 0:
320
                        texts = TOCR.getTextInfo(img, (area.x, area.y), 0, language=appDocData.OCRData)
321
                    else:
322
                        texts = TOCR.getTextInfo(img, (area.x, area.y), 0, language='eng', conf=whiteCharList[0].value)
323
                    self.titleBlockTextInfoList.append([area.name, texts])
324

  
325
                if worker is not None: worker.updateProgress.emit(maxProgressValue, None)
326
        except Exception as ex:
327
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
328
            worker.displayLog.emit(MessageType.Error, message)
329

  
330
    '''
331
        @brief      remove text from image
332
        @author     humkyung
333
        @date       2018.07.24
334
    '''
335
    def removeTextFromImage(self, imgSrc, offset):
336
        appDocData = AppDocData.instance()
337
        project = appDocData.getCurrentProject()
338

  
339
        path = os.path.join(project.getTempPath(), 'OCR_{}.png'.format(appDocData.imgName))
340
        if os.path.isfile(path):
341
            imgOCR = cv2.imread(path)
342
            imgOCR = cv2.threshold(cv2.cvtColor(imgOCR, cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
343

  
344
            # remove recognized text from image
345
            for text in self.textInfoList:
346
                x = round(text.getX() - offset[0])
347
                y = round(text.getY() - offset[1])
348
                width = round(text.getW())
349
                height = round(text.getH())
350
                self.removeText(imgSrc, (round(text.getX()), round(text.getY())), imgOCR[y:y+height, x:x+width])
351
            # up to here
352

  
353
    '''
354
        @brief  remove text from image by using ocr image
355
        @author
356
    '''
357
    def removeText(self, img, pt, imgOCR):
358
        try:
359
            x = round(pt[0])
360
            y = round(pt[1])
361
            width, height = imgOCR.shape[::-1]
362
            
363
            temp = img[y:y+height, x:x+width]
364
            imgOCR = cv2.erode(imgOCR, np.ones((3,3), np.uint8))
365
            mask = cv2.bitwise_or(temp, imgOCR)
366
            imgXOR = cv2.bitwise_xor(temp, mask)
367
            img[y:y+height, x:x+width] = cv2.bitwise_not(imgXOR)
368

  
369
        except Exception as ex:
370
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
371

  
372
        return img
DTI_PID/DTI_PID/TrainingImageListDialog.py
1
import sys
2
import os, time, subprocess, ctypes, datetime, copy
3
from ctypes import wintypes
4
_GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW
5
_GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD]
6
_GetShortPathNameW.restype = wintypes.DWORD
7
from PyQt5.QtCore import *
8
from PyQt5.QtGui import *
9
from PyQt5.QtWidgets import *
10
#from PyQt5.QtChart import *
11
from AppDocData import AppDocData, Source
12
import pytesseract
13
import TrainingImageList_UI
14
from TrainingEditorDialog import QTrainingEditorDialog
15
import tesseract_ocr_module as TOCR
16
import numpy as np
17
import pyqtgraph as pg
18

  
19

  
20
dataPath = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID')
21
tesseractPath = os.path.join(dataPath, 'Tesseract-OCR', 'tessdata')
22
pytesseract.pytesseract.tesseract_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'tesseract.exe')
23
tesseract_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'tesseract.exe')
24
unicharset_extractor_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'unicharset_extractor.exe')
25
set_unicharset_properties_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'set_unicharset_properties.exe')
26
shapeclustering_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'shapeclustering.exe')
27
mftraining_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'mftraining.exe')
28
cntraining_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'cntraining.exe')
29
combine_tessdata_cmd = os.path.join(dataPath, 'Tesseract-OCR', 'combine_tessdata.exe')
30

  
31
# for reset chart
32
defaultCharList = [['0',0],['1',0],['2',0],['3',0],['4',0],['5',0],['6',0],['7',0],['8',0],['9',0],['a',0],['b',0],['c',0],['d',0],['e',0],['f',0],['g',0],['h',0],['i',0],['j',0],['k',0],['l',0],['m',0],['n',0],['o',0],['p',0],['q',0],['r',0],['s',0],['t',0],['u',0],['w',0],['x',0],['y',0],['z',0],['A',0],['B',0],['C',0],['D',0],['E',0],['F',0],['G',0],['H',0],['I',0],['J',0],['K',0],['L',0],['M',0],['N',0],['O',0],['P',0],['Q',0],['R',0],['S',0],['T',0],['U',0],['V',0],['W',0],['X',0],['Y',0],['Z',0]]
33
#// for remove noise
34
#noisePassList = ['.', '\"', '\'', ',', '`', '-', '+']
35

  
36
class EmittingStream(QObject):
37
    """
38
    This is EmittingStream class
39
    """
40
    textWritten = pyqtSignal(str)
41

  
42
    def write(self, text):
43
        self.textWritten.emit(str(text))
44

  
45
class QTrainingImageListDialog(QDialog):
46
    """
47
    This is training image list dialog class
48
    """
49
    TRAINING_DATA_COUNT = 5
50

  
51
    def __init__(self, parent):
52
        QDialog.__init__(self, parent)
53
        self.setWindowFlag(Qt.WindowMinMaxButtonsHint)
54

  
55
        self.ui = TrainingImageList_UI.Ui_TraingingImageListDialog()
56
        self.ui.setupUi(self)
57
        self.ui.progressBar.setValue(0)
58
        self.charList = []
59

  
60
        # for List 
61
        self.ui.tableWidgetList.setSelectionMode(QAbstractItemView.SingleSelection) 
62
        self.ui.tableWidgetList.setColumnCount(4)
63

  
64
        ## column header 명 설정
65
        self.ui.tableWidgetList.setHorizontalHeaderLabels([self.tr('No.'), self.tr('Image List'), self.tr('Modified Date'), self.tr('Box Contents')])
66
        self.ui.tableWidgetList.horizontalHeaderItem(1).setToolTip(self.tr('Image Name')) # header tooltip
67
        self.ui.tableWidgetList.horizontalHeaderItem(2).setToolTip(self.tr('Edit Status')) # header tooltip
68
        self.ui.tableWidgetList.horizontalHeaderItem(3).setToolTip(self.tr('Box Contents')) # header tooltip
69
        self.ui.tableWidgetList.horizontalHeaderItem(1).setSizeHint(QSize(30, 30))
70
        self.ui.tableWidgetList.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
71
        self.ui.tableWidgetList.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
72
        self.ui.tableWidgetList.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
73
        self.ui.tableWidgetList.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
74
        self.ui.tableWidgetList.resizeColumnsToContents()
75
        self.ui.tableWidgetList.setEditTriggers(QAbstractItemView.NoEditTriggers)
76
        
77
        self.listUpdate()
78

  
79
        # connect signals and slots
80
        self.ui.tableWidgetList.cellDoubleClicked.connect(self.listCellDoubleClicked)
81
        self.ui.pushButtonBox.clicked.connect(self.pushButtonBoxClicked)
82
        self.ui.pushButtonMakeTrainingData.clicked.connect(self.makeTrainingDataClicked)
83
        self.ui.pushButtonImageDelete.clicked.connect(self.pushButtonImageDeleteClicked)
84
        self.ui.pushButtonBoxDelete.clicked.connect(self.pushButtonBoxDeleteClicked)
85
        self.ui.pushButtonClose.clicked.connect(self.pushButtonCloseClicked)
86
        self.ui.pushButtonDeleteBatchChar.clicked.connect(self.pushButtonDeleteBatchCharClicked)
87

  
88
        # delete character in box
89
        self.ui.pushButtonDeleteBatchChar.setEnabled(False)
90
        self.ui.pushButtonDeleteBatchChar.setHidden(True)
91
        self.ui.lineEditTargetChar.setEnabled(False)
92
        self.ui.lineEditTargetChar.setHidden(True)
93

  
94
        # Install the custom output stream
95
        #sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
96

  
97
        os.environ['TESSDATA_PREFIX'] = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID', 'Tesseract-OCR', 'tessdata')
98

  
99
        appDocData = AppDocData.instance()
100
        project = appDocData.getCurrentProject()
101
        dataList = appDocData.getTrainingFileList()
102
        self.charList = []
103
        for charCounts in defaultCharList:
104
            charCount = [charCounts[0], charCounts[1]]
105
            self.charList.append(charCount)
106
        for data in dataList:
107
            if data.find('.boxS') is not -1:
108
                boxPath = os.path.join(project.getTrainingFilePath(), data)
109
                fw = open(boxPath, 'r', encoding='utf8')
110
                boxContent = fw.read()
111
                lines = boxContent.split('\n')
112
                for line in lines:
113
                    if not line: continue
114
                    char,min_x,min_y,max_x,max_y = line.split(' ')
115
                    
116
                    matches = [_char for _char in self.charList if _char[0] == char]
117
                    if matches:
118
                        matches[0][1] = matches[0][1] + 1
119
                    else:
120
                        self.charList.append([char, 1])
121
                fw.close()
122
        self.makeChart()
123

  
124
    def __del__(self):
125
        # Restore sys.stdout
126
        #sys.stdout = sys.__stdout__
127
        pass
128

  
129
    def normalOutputWritten(self, text):
130
        """Append text to the QTextEdit."""
131
        # Maybe QTextEdit.append() works as well, but this is how I do it:
132
        cursor = self.ui.textEditConsole.textCursor()
133
        cursor.movePosition(QTextCursor.End)
134
        cursor.insertText(text)
135
        self.ui.textEditConsole.setTextCursor(cursor)
136
        self.ui.textEditConsole.ensureCursorVisible()
137

  
138
    '''
139
        @brief      close dialog by button click
140
        @author     euisung
141
        @date       2018.10.18
142
    '''
143
    def pushButtonCloseClicked(self):
144
        QDialog.reject(self)
145

  
146
    '''
147
        @brief      update image list
148
        @author     euisung
149
        @date       2018.10.18
150
    '''
151
    def listUpdate(self):
152
        appDocData = AppDocData.instance()
153
        project = appDocData.getCurrentProject()
154
        dataList = appDocData.getTrainingFileList()
155
        imgCount = 0
156
        for data in dataList:
157
            if data.find('.png') is not -1:
158
                imgCount += 1
159
        self.ui.tableWidgetList.setRowCount(imgCount)
160
        
161
        row = 0
162
        for data in dataList:
163
            if data.find('.png') is not -1:
164
                self.ui.tableWidgetList.setItem(row, 0, QTableWidgetItem(str(row + 1)))
165
                self.ui.tableWidgetList.setItem(row, 1, QTableWidgetItem(data))
166
                allDataList = appDocData.getTrainingFileList()
167
                for adata in allDataList:
168
                    boxName = data.replace('.png', '.boxS')
169
                    if adata.find(boxName) is not -1:
170
                        boxPath = os.path.join(project.getTrainingFilePath(), boxName)
171
                        modifiedTime = str(datetime.datetime.strptime(time.ctime(os.path.getmtime(boxPath)), "%a %b %d %H:%M:%S %Y"))
172
                        self.ui.tableWidgetList.setItem(row, 2, QTableWidgetItem(modifiedTime))
173

  
174
                        fw = open(boxPath, 'r', encoding='utf8')
175
                        boxContent = fw.read()
176
                        fw.close()
177
                        boxContent = boxContent.split('\n')
178
                        boxContent = boxContent[:-1]
179
                        boxchars = ''
180
                        for char in boxContent:
181
                            boxchars += char[0]
182
                        boxContent = QTableWidgetItem(boxchars)
183
                        boxContent.setFont(QFont('Consolas', 9, QFont.Bold))
184
                        self.ui.tableWidgetList.setItem(row, 3, QTableWidgetItem(boxContent))
185
                        break
186
                    else:
187
                        self.ui.tableWidgetList.setItem(row, 2, QTableWidgetItem(''))
188
                        self.ui.tableWidgetList.setItem(row, 3, QTableWidgetItem(''))
189
                row += 1
190

  
191
    '''
192
        @brief      delete training box only by button click
193
        @author     euisung
194
        @date       2018.10.18
195
    '''
196
    def pushButtonBoxDeleteClicked(self):
197
        try:
198
            row = self.ui.tableWidgetList.selectedIndexes()[0].row()
199
            col = self.ui.tableWidgetList.selectedIndexes()[0].column()
200
            drawingName = self.ui.tableWidgetList.item(row, 1).text()
201
            boxDate = self.ui.tableWidgetList.item(row, 2).text()
202
            if boxDate == '':
203
                return
204
        except Exception as ex:
205
            pass
206
        try:
207
            reply = QMessageBox.question(self, self.tr('Continue?'), self.tr('Are you sure you want to delete the box job? '), QMessageBox.Yes, QMessageBox.Cancel)
208
            if reply == QMessageBox.Yes:
209
                appDocData = AppDocData.instance()
210
                project = appDocData.getCurrentProject()
211

  
212
                drawingPath = os.path.join(project.getTrainingFilePath(), drawingName)
213
                boxName = drawingPath.replace('.png', '.boxS')
214
                if os.path.isfile(boxName):
215
                    os.remove(boxName)
216
                self.ui.tableWidgetList.item(row, 2).setText('')
217
                self.ui.tableWidgetList.item(row, 3).setText('')
218
                #self.listUpdate()
219
        except Exception as ex:
220
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
221

  
222
    '''
223
        @brief      delete training image and box by button click
224
        @author     euisung
225
        @date       2018.10.18
226
    '''
227
    def pushButtonImageDeleteClicked(self):
228
        try:
229
            row = self.ui.tableWidgetList.selectedIndexes()[0].row()
230
            col = self.ui.tableWidgetList.selectedIndexes()[0].column()
231
            drawingName = self.ui.tableWidgetList.item(row, 1).text()
232
        except Exception as ex:
233
            pass
234
        try:
235
            reply = QMessageBox.question(self, self.tr('Continue?'), self.tr('Are you sure you want to delete selected image? '), QMessageBox.Yes, QMessageBox.Cancel)
236
            if reply == QMessageBox.Yes:
237
                appDocData = AppDocData.instance()
238
                project = appDocData.getCurrentProject()
239

  
240
                drawingPath = os.path.join(project.getTrainingFilePath(), drawingName)
241
                if os.path.isfile(drawingPath):
242
                    os.remove(drawingPath)
243
                boxName = drawingPath.replace('.png', '.boxS')
244
                if os.path.isfile(boxName):
245
                    os.remove(boxName)
246
                self.ui.tableWidgetList.removeRow(row)
247
                #self.listUpdate()
248
        except Exception as ex:
249
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
250

  
251
    def dataReady(self):
252
        cursor = self.ui.textEditConsole.textCursor()
253
        cursor.movePosition(cursor.End)
254
        cursor.insertText(str(self.process.readAll()))
255
        self.ui.textEditConsole.ensureCursorVisible()
... 이 차이점은 표시할 수 있는 최대 줄수를 초과해서 이 차이점은 잘렸습니다.

내보내기 Unified diff

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