1
|
|
2
|
"""
|
3
|
This is ocr result dialog module
|
4
|
"""
|
5
|
from PIL import Image
|
6
|
import io, sys
|
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
|
|
19
|
class QOcrResultDialog(QDialog):
|
20
|
def __init__(self, parent, qimage, boundingBox, isModify=False, text=None):
|
21
|
QDialog.__init__(self, parent)
|
22
|
self.textInfoList = []
|
23
|
|
24
|
self.isModify = isModify
|
25
|
self.image = qimage
|
26
|
self.originImageWidth = qimage.width()
|
27
|
self.originImageHeight = qimage.height()
|
28
|
self.boundingBox = boundingBox
|
29
|
|
30
|
self.angle = 0
|
31
|
|
32
|
self.ui = OcrResultDialog_UI.Ui_Dialog()
|
33
|
self.ui.setupUi(self)
|
34
|
|
35
|
appDocData = AppDocData.instance()
|
36
|
configs = appDocData.getAppConfigs('app', 'mode')
|
37
|
if configs and 1 == len(configs) and 'advanced' == configs[0].value:
|
38
|
pass
|
39
|
else:
|
40
|
self.ui.pushButtonMakeTrainingImage.setVisible(False)
|
41
|
|
42
|
self.imgW = qimage.width()
|
43
|
self.imgH = qimage.height()
|
44
|
self.image = self.image.scaled(self.imgW, self.imgH)
|
45
|
self.graphicsView = QtImageViewer.QtImageViewer(App.mainWnd())
|
46
|
self.graphicsView.useDefaultCommand()
|
47
|
self.graphicsView.setImage(self.image)
|
48
|
self.ui.horizontalLayoutGraphicsView.addWidget(self.graphicsView)
|
49
|
|
50
|
self.ui.counterClockPushButton_2.clicked.connect(lambda: self.rotateImage(True))
|
51
|
self.ui.clockPushButton_2.clicked.connect(lambda: self.rotateImage(False))
|
52
|
self.ui.redetectPushButton_2.clicked.connect(self.detectText)
|
53
|
self.ui.pushButtonMakeTrainingImage.clicked.connect(self.pushButtonMakeTrainingImageClicked)
|
54
|
|
55
|
self.ui.comboBoxOCRData.addItem('eng')
|
56
|
tessdata_path = os.path.join(os.getenv('ALLUSERSPROFILE'), 'Digital PID', 'Tesseract-OCR', 'tessdata')
|
57
|
if os.path.isfile(os.path.join(tessdata_path, appDocData.getCurrentProject().getName() + '.traineddata')):
|
58
|
self.ui.comboBoxOCRData.addItem(appDocData.getCurrentProject().getName())
|
59
|
|
60
|
configs = appDocData.getConfigs('Text Recognition', 'OCR Data')
|
61
|
value = configs[0].value if 1 == len(configs) else ''
|
62
|
if value:
|
63
|
at = self.ui.comboBoxOCRData.findText(value)
|
64
|
self.ui.comboBoxOCRData.setCurrentIndex(at)
|
65
|
else:
|
66
|
self.ui.comboBoxOCRData.selectedIndex = 0
|
67
|
|
68
|
if not self.isModify:
|
69
|
self.detectText()
|
70
|
else:
|
71
|
self.ui.detectResultTextEdit.setPlainText(text)
|
72
|
|
73
|
self.isAccepted = False
|
74
|
|
75
|
'''
|
76
|
@brief Make OCR Training Image
|
77
|
@author euisung
|
78
|
@date 2018.10.16
|
79
|
@history euisung 2018.11.02 add notice push
|
80
|
'''
|
81
|
|
82
|
def pushButtonMakeTrainingImageClicked(self):
|
83
|
import uuid
|
84
|
uid = str(uuid.uuid4()) + '.png'
|
85
|
appDocData = AppDocData.instance()
|
86
|
project = appDocData.getCurrentProject()
|
87
|
trainingImgPath = os.path.join(project.getTrainingFilePath(), uid)
|
88
|
|
89
|
self.image.save(trainingImgPath)
|
90
|
QMessageBox.about(self, self.tr("INFO"), self.tr('Successfully saved.'))
|
91
|
QDialog.reject(self)
|
92
|
|
93
|
def rotateImage(self, isCounterClock):
|
94
|
for item in self.graphicsView.scene.items():
|
95
|
self.graphicsView.scene.removeItem(item)
|
96
|
self.graphicsView.clearImage()
|
97
|
transform = QTransform()
|
98
|
if isCounterClock:
|
99
|
'''CounterClock'''
|
100
|
self.angle = (self.angle - 90) % 360
|
101
|
transform.rotate(-90)
|
102
|
else:
|
103
|
'''Clock'''
|
104
|
self.angle = (self.angle - 270) % 360
|
105
|
transform.rotate(90)
|
106
|
|
107
|
self.image = self.image.transformed(transform)
|
108
|
self.graphicsView.setImage(self.image)
|
109
|
self.textInfoList = []
|
110
|
|
111
|
'''
|
112
|
@history 2018.04.26 Jeongwoo Add Rectangle with modified Coords
|
113
|
2018.06.20 Jeongwoo Remove test code
|
114
|
2018.11.08 euisung add white char list check process on db
|
115
|
2018.11.22 euisung OCR lang apply fixed
|
116
|
'''
|
117
|
|
118
|
def detectText(self):
|
119
|
try:
|
120
|
buffer = QBuffer()
|
121
|
buffer.open(QBuffer.ReadWrite)
|
122
|
self.image.save(buffer, "PNG")
|
123
|
pyImage = Image.open(io.BytesIO(buffer.data()))
|
124
|
img = np.array(pyImage)
|
125
|
|
126
|
app_doc_data = AppDocData.instance()
|
127
|
|
128
|
ocr_data = self.ui.comboBoxOCRData.currentText()
|
129
|
|
130
|
whiteCharList = app_doc_data.getConfigs('Text Recognition', 'White Character List')
|
131
|
if len(whiteCharList) is 0:
|
132
|
self.textInfoList = TOCR.getTextInfo(img, (round(self.boundingBox.x()), round(self.boundingBox.y())), 0,
|
133
|
language=ocr_data)
|
134
|
else:
|
135
|
self.textInfoList = TOCR.getTextInfo(img, (round(self.boundingBox.x()), round(self.boundingBox.y())), 0,
|
136
|
language=ocr_data, conf=whiteCharList[0].value)
|
137
|
|
138
|
if self.textInfoList is not None and len(self.textInfoList) > 0:
|
139
|
self.ui.detectResultTextEdit.setText(self.getPlainText(self.textInfoList))
|
140
|
for textInfo in self.textInfoList:
|
141
|
self.graphicsView.scene.addRect(textInfo.getX() - int(self.boundingBox.x()),
|
142
|
textInfo.getY() - int(self.boundingBox.y()), textInfo.getW(),
|
143
|
textInfo.getH(), QPen(Qt.red, 1, Qt.SolidLine))
|
144
|
else:
|
145
|
self.ui.detectResultTextEdit.setText("Not Found")
|
146
|
except Exception as ex:
|
147
|
from App import App
|
148
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
149
|
sys.exc_info()[-1].tb_lineno)
|
150
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
151
|
|
152
|
def getPlainText(self, textInfoList):
|
153
|
text = ''
|
154
|
for index in range(len(textInfoList)):
|
155
|
textInfo = textInfoList[index]
|
156
|
if index != 0:
|
157
|
text = text + '\n'
|
158
|
text = text + textInfo.getText()
|
159
|
return text
|
160
|
|
161
|
'''
|
162
|
@brief OK Button Clicked. Remake TextInfo object
|
163
|
@author Jeongwoo
|
164
|
@date 18.04.19
|
165
|
@history 18.04.20 Jeongwoo Calculate Start Point Coordinates by rotated angle
|
166
|
18.04.26 Jeongwoo Scene.itemAt(textX - boundBox.x(), textY - boundBox.y())
|
167
|
'''
|
168
|
|
169
|
def accept(self):
|
170
|
from TextInfo import TextInfo
|
171
|
self.isAccepted = True
|
172
|
|
173
|
try:
|
174
|
text = self.ui.detectResultTextEdit.toPlainText()
|
175
|
if text == '' or text == 'Not Found':
|
176
|
QMessageBox.about(self.ui.ocrDialogButtonBox, 'Notice', 'Please try again after recognition or type.')
|
177
|
return
|
178
|
|
179
|
isSplit = self.ui.checkBoxSeperate.isChecked()
|
180
|
if isSplit:
|
181
|
splitText = text.split('\n')
|
182
|
else:
|
183
|
splitText = [text]
|
184
|
|
185
|
if not len(self.textInfoList) > 0:
|
186
|
import cv2
|
187
|
|
188
|
buffer = QBuffer()
|
189
|
buffer.open(QBuffer.ReadWrite)
|
190
|
self.image.save(buffer, "PNG")
|
191
|
pyImage = Image.open(io.BytesIO(buffer.data()))
|
192
|
img = np.array(pyImage)
|
193
|
|
194
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
195
|
imgNot = np.ones(img.shape, np.uint8)
|
196
|
cv2.bitwise_not(img, imgNot)
|
197
|
imgNot = cv2.dilate(imgNot, np.ones((8, 8), np.uint8))
|
198
|
|
199
|
contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
200
|
minX, minY, maxX, maxY = sys.maxsize, sys.maxsize, 0, 0
|
201
|
if len(contours) is 0:
|
202
|
minX, minY, maxX, maxY = self.boundingBox.x(), self.boundingBox.y(), self.boundingBox.x() + self.image.width(), self.boundingBox.y() + self.image.height()
|
203
|
else:
|
204
|
minX, minY, maxX, maxY = sys.maxsize, sys.maxsize, 0, 0
|
205
|
for cnt in contours:
|
206
|
x, y, w, h = cv2.boundingRect(cnt)
|
207
|
minX = min(x, minX)
|
208
|
minY = min(y, minY)
|
209
|
maxX = max(x + w, maxX)
|
210
|
maxY = max(y + h, maxY)
|
211
|
minX, minY, maxX, maxY = minX + self.boundingBox.x(), minY + self.boundingBox.y(), maxX + self.boundingBox.x(), maxY + self.boundingBox.y()
|
212
|
|
213
|
self.textInfoList.append(TextInfo(text, minX, minY, maxX - minX, maxY - minY, 0))
|
214
|
|
215
|
if not isSplit:
|
216
|
minX, minY, maxX, maxY = sys.maxsize, sys.maxsize, 0, 0
|
217
|
for textInfo in self.textInfoList:
|
218
|
x, y, w, h = textInfo.getX(), textInfo.getY(), textInfo.getW(), textInfo.getH()
|
219
|
minX = min(x, minX)
|
220
|
minY = min(y, minY)
|
221
|
maxX = max(x + w, maxX)
|
222
|
maxY = max(y + h, maxY)
|
223
|
|
224
|
self.textInfoList = [TextInfo(text, minX, minY, maxX - minX, maxY - minY, 0)]
|
225
|
|
226
|
if len(self.textInfoList) > 0:
|
227
|
for index in range(len(splitText)):
|
228
|
textInfo = self.textInfoList[index]
|
229
|
item = self.graphicsView.scene.itemAt(QPointF(float(textInfo.getX() - int(self.boundingBox.x())),
|
230
|
float(textInfo.getY() - int(self.boundingBox.y()))),
|
231
|
QTransform())
|
232
|
if item is not None:
|
233
|
|
234
|
imgTransform = QTransform()
|
235
|
if self.angle == 90 or self.angle == 270:
|
236
|
imgTransform.translate(self.image.height() * 0.5, self.image.width() * 0.5)
|
237
|
elif self.angle == 0 or self.angle == 360:
|
238
|
imgTransform.translate(self.image.width() * 0.5, self.image.height() * 0.5)
|
239
|
imgTransform.rotate(-abs(self.angle))
|
240
|
imgTransform.translate(-self.image.width() * 0.5, -self.image.height() * 0.5)
|
241
|
rect = QRect(textInfo.getX() - int(self.boundingBox.x()),
|
242
|
textInfo.getY() - int(self.boundingBox.y()), textInfo.getW(), textInfo.getH())
|
243
|
rect = imgTransform.mapRect(rect)
|
244
|
|
245
|
textInfo.setX(rect.x() + int(self.boundingBox.x()))
|
246
|
textInfo.setY(rect.y() + int(self.boundingBox.y()))
|
247
|
textInfo.setText(splitText[index])
|
248
|
radian = round(math.radians(abs(self.angle)), 2)
|
249
|
textInfo.setAngle(radian)
|
250
|
if radian == 1.57 or radian == 4.71:
|
251
|
width = textInfo.getW()
|
252
|
height = textInfo.getH()
|
253
|
textInfo.setW(height)
|
254
|
textInfo.setH(width)
|
255
|
self.textInfoList = self.textInfoList[:len(splitText)]
|
256
|
|
257
|
QDialog.accept(self)
|
258
|
|
259
|
except Exception as ex:
|
260
|
from App import App
|
261
|
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
262
|
sys.exc_info()[-1].tb_lineno)
|
263
|
App.mainWnd().addMessage.emit(MessageType.Error, message)
|
264
|
|
265
|
def reject(self):
|
266
|
self.isAccepted = False
|
267
|
self.textInfoList = None
|
268
|
QDialog.reject(self)
|
269
|
|
270
|
'''
|
271
|
@brief Display this QDialog
|
272
|
'''
|
273
|
|
274
|
def showDialog(self):
|
275
|
|
276
|
self.exec_()
|
277
|
return (self.isAccepted, self.textInfoList)
|