57 |
57 |
def getTextAreaInfo(self, imgGray, offsetX, offsetY):
|
58 |
58 |
from AppDocData import AppDocData
|
59 |
59 |
|
60 |
|
app_doc_data = AppDocData.instance()
|
61 |
|
project = app_doc_data.getCurrentProject()
|
62 |
|
|
63 |
|
configs = app_doc_data.getConfigs('Text Size', 'Max Text Size')
|
64 |
|
maxTextSize = int(configs[0].value) if 1 == len(configs) else 100
|
65 |
|
minSize = 5
|
66 |
|
|
67 |
|
ocr_image = np.ones(imgGray.shape, np.uint8) * 255
|
68 |
|
binaryImg, mask = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
69 |
|
|
70 |
|
contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
|
71 |
|
for contour in contours:
|
72 |
|
# remove too big one or horizontal/vertical line
|
73 |
|
[x, y, w, h] = cv2.boundingRect(contour)
|
74 |
|
area = cv2.contourArea(contour, True)
|
75 |
|
|
76 |
|
# skip one which size is greater than max size or less then minimum size
|
77 |
|
if area >= 0:
|
78 |
|
if (w > maxTextSize or h > maxTextSize) or (w <= minSize and h <= minSize):
|
79 |
|
continue
|
80 |
|
|
81 |
|
if area >= 0:
|
82 |
|
cv2.drawContours(ocr_image, [contour], -1, (0, 0, 0), -1)
|
83 |
|
cv2.drawContours(ocr_image, [contour], -1, (255, 255, 255), 1)
|
84 |
|
else:
|
85 |
|
cv2.drawContours(ocr_image, [contour], -1, (255, 255, 255), -1)
|
86 |
|
|
87 |
|
path = os.path.join(project.getTempPath(), 'OCR_{}.png'.format(app_doc_data.imgName))
|
88 |
|
cv2.imwrite(path, ocr_image)
|
89 |
|
|
90 |
|
rects = []
|
91 |
|
configs = app_doc_data.getConfigs('Text Recognition', 'Expand Size')
|
92 |
|
expandSize = int(configs[0].value) if 1 == len(configs) else 10
|
93 |
|
configs = app_doc_data.getConfigs('Text Recognition', 'Shrink Size')
|
94 |
|
shrinkSize = int(configs[0].value) if 1 == len(configs) else 0
|
95 |
|
|
96 |
|
eroded = cv2.erode(ocr_image, np.ones((expandSize, expandSize), np.uint8))
|
97 |
|
eroded = cv2.bitwise_not(eroded)
|
98 |
|
|
99 |
|
bboxes = []
|
100 |
|
contours, hierarchy = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
101 |
|
for contour in contours:
|
102 |
|
area = cv2.contourArea(contour, True)
|
103 |
|
if area < 0:
|
104 |
|
[x, y, w, h] = cv2.boundingRect(contour)
|
105 |
|
bboxes.append(QRect(x, y, w, h))
|
106 |
|
|
107 |
|
# exclude bounding boxes contains child bounding box
|
108 |
|
not_containing_bbox = []
|
109 |
|
for bbox in bboxes:
|
110 |
|
matches = [_bbox for _bbox in bboxes if bbox != _bbox and bbox.contains(_bbox)]
|
111 |
|
if not matches:
|
112 |
|
not_containing_bbox.append(bbox)
|
113 |
|
# up to here
|
|
60 |
list = []
|
|
61 |
ocr_image = None
|
|
62 |
try:
|
|
63 |
app_doc_data = AppDocData.instance()
|
|
64 |
project = app_doc_data.getCurrentProject()
|
|
65 |
|
|
66 |
configs = app_doc_data.getConfigs('Text Size', 'Max Text Size')
|
|
67 |
maxTextSize = int(configs[0].value) if 1 == len(configs) else 100
|
|
68 |
minSize = 5
|
114 |
69 |
|
115 |
|
for bbox in not_containing_bbox:
|
116 |
|
x, y = bbox.left(), bbox.top()
|
117 |
|
w, h = bbox.width(), bbox.height()
|
118 |
|
img = ocr_image[bbox.top():bbox.bottom(), bbox.left():bbox.right()]
|
119 |
|
img = cv2.bitwise_not(img)
|
|
70 |
ocr_image = np.ones(imgGray.shape, np.uint8) * 255
|
|
71 |
# binaryImg, mask = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
|
72 |
binaryImg, mask = cv2.threshold(imgGray, 200, 255, cv2.THRESH_BINARY)
|
120 |
73 |
|
121 |
|
horizontal, max_width = 0, 0
|
122 |
|
vertical, max_height = 0, 0
|
123 |
|
_contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
124 |
|
for xx in _contours:
|
125 |
|
[_x, _y, _w, _h] = cv2.boundingRect(xx)
|
|
74 |
contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
|
|
75 |
for contour in contours:
|
|
76 |
# remove too big one or horizontal/vertical line
|
|
77 |
[x, y, w, h] = cv2.boundingRect(contour)
|
|
78 |
area = cv2.contourArea(contour, True)
|
126 |
79 |
|
127 |
|
max_width = _x if _x > max_width else max_width
|
128 |
|
max_height = _y if _y > max_height else max_height
|
|
80 |
# skip one which size is greater than max size or less then minimum size
|
|
81 |
if area >= 0:
|
|
82 |
if (w > maxTextSize or h > maxTextSize) or (w <= minSize and h <= minSize):
|
|
83 |
continue
|
129 |
84 |
|
130 |
|
if (_w < _h) or (_w > maxTextSize > _h): # width is greater than height
|
131 |
|
horizontal += 1 + (_w * _h) / (w * h)
|
|
85 |
if area >= 0:
|
|
86 |
cv2.drawContours(ocr_image, [contour], -1, (0, 0, 0), -1)
|
|
87 |
cv2.drawContours(ocr_image, [contour], -1, (255, 255, 255), 1)
|
132 |
88 |
else:
|
133 |
|
vertical += 1 + (_w * _h) / (w * h)
|
134 |
|
|
135 |
|
if (w < minSize and h < minSize) or (max_width > maxTextSize and max_height > maxTextSize):
|
136 |
|
continue # skip too small or big one
|
137 |
|
|
138 |
|
rects.append([0 if horizontal > vertical else 90, QRect(x, y, w, h)])
|
139 |
|
|
140 |
|
configs = app_doc_data.getConfigs('Text Recognition', 'Merge Size')
|
141 |
|
mergeSize = int(configs[0].value) if 1 == len(configs) else 10
|
142 |
|
# merge rectangles
|
143 |
|
interestings = []
|
144 |
|
while rects:
|
145 |
|
rect = rects.pop()
|
146 |
|
|
147 |
|
if 0 == rect[0]: # x-direction text
|
148 |
|
rectExpand = rect[1].adjusted(-mergeSize, 0, mergeSize, 0)
|
149 |
|
matches = [x for x in rects if (x[0] == rect[0]) and
|
150 |
|
abs(x[1].height() - rect[1].height()) < (x[1].height() + rect[1].height())*0.5 and
|
151 |
|
abs(x[1].center().y() - rect[1].center().y()) < rect[1].height()*0.5 and
|
152 |
|
rectExpand.intersects(x[1])]
|
153 |
|
else: # y -direction text
|
154 |
|
rectExpand = rect[1].adjusted(0, -mergeSize, 0, mergeSize)
|
155 |
|
matches = [x for x in rects if (x[0] == rect[0]) and
|
156 |
|
abs(x[1].width() - rect[1].width()) < (x[1].width() + rect[1].width())*0.5 and
|
157 |
|
abs(x[1].center().x() - rect[1].center().x()) < rect[1].width()*0.5 and
|
158 |
|
rectExpand.intersects(x[1])]
|
159 |
|
|
160 |
|
if matches:
|
161 |
|
for _rect in matches:
|
162 |
|
rect[1] = rect[1].united(_rect[1])
|
163 |
|
if _rect in rects:
|
164 |
|
rects.remove(_rect)
|
165 |
|
rects.append(rect)
|
166 |
|
else:
|
167 |
|
interestings.append(rect)
|
|
89 |
cv2.drawContours(ocr_image, [contour], -1, (255, 255, 255), -1)
|
|
90 |
|
|
91 |
path = os.path.join(project.getTempPath(), 'OCR_{}.png'.format(app_doc_data.imgName))
|
|
92 |
cv2.imwrite(path, ocr_image)
|
|
93 |
|
|
94 |
rects = []
|
|
95 |
configs = app_doc_data.getConfigs('Text Recognition', 'Expand Size')
|
|
96 |
expandSize = int(configs[0].value) if 1 == len(configs) else 10
|
|
97 |
configs = app_doc_data.getConfigs('Text Recognition', 'Shrink Size')
|
|
98 |
shrinkSize = int(configs[0].value) if 1 == len(configs) else 0
|
|
99 |
|
|
100 |
eroded = cv2.erode(ocr_image, np.ones((expandSize, expandSize), np.uint8))
|
|
101 |
eroded = cv2.bitwise_not(eroded)
|
|
102 |
|
|
103 |
bboxes = []
|
|
104 |
contours, hierarchy = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
105 |
for contour in contours:
|
|
106 |
area = cv2.contourArea(contour, True)
|
|
107 |
if area < 0:
|
|
108 |
[x, y, w, h] = cv2.boundingRect(contour)
|
|
109 |
bboxes.append(QRect(x, y, w, h))
|
|
110 |
|
|
111 |
# exclude bounding boxes contains child bounding box
|
|
112 |
not_containing_bbox = []
|
|
113 |
for bbox in bboxes:
|
|
114 |
matches = [_bbox for _bbox in bboxes if bbox != _bbox and bbox.contains(_bbox)]
|
|
115 |
if not matches:
|
|
116 |
not_containing_bbox.append(bbox)
|
|
117 |
# up to here
|
168 |
118 |
|
169 |
|
list = []
|
170 |
|
for rect in interestings:
|
171 |
|
angle = rect[0]
|
172 |
|
list.append(ti.TextInfo('', round(offsetX) + rect[1].x(), round(offsetY) + rect[1].y(), rect[1].width(),
|
173 |
|
rect[1].height(), angle))
|
|
119 |
for bbox in not_containing_bbox:
|
|
120 |
x, y = bbox.left(), bbox.top()
|
|
121 |
w, h = bbox.width(), bbox.height()
|
|
122 |
img = ocr_image[bbox.top():bbox.bottom(), bbox.left():bbox.right()]
|
|
123 |
img = cv2.bitwise_not(img)
|
|
124 |
|
|
125 |
horizontal, max_width = 0, 0
|
|
126 |
vertical, max_height = 0, 0
|
|
127 |
_contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
128 |
for xx in _contours:
|
|
129 |
[_x, _y, _w, _h] = cv2.boundingRect(xx)
|
|
130 |
|
|
131 |
max_width = _x if _x > max_width else max_width
|
|
132 |
max_height = _y if _y > max_height else max_height
|
|
133 |
|
|
134 |
if (_w < _h) or (_w > maxTextSize > _h): # width is greater than height
|
|
135 |
horizontal += 1 + (_w * _h) / (w * h)
|
|
136 |
else:
|
|
137 |
vertical += 1 + (_w * _h) / (w * h)
|
|
138 |
|
|
139 |
if (w < minSize and h < minSize) or (max_width > maxTextSize and max_height > maxTextSize):
|
|
140 |
continue # skip too small or big one
|
|
141 |
|
|
142 |
rects.append([0 if horizontal > vertical else 90, QRect(x, y, w, h)])
|
|
143 |
|
|
144 |
configs = app_doc_data.getConfigs('Text Recognition', 'Merge Size')
|
|
145 |
mergeSize = int(configs[0].value) if 1 == len(configs) else 10
|
|
146 |
# merge rectangles
|
|
147 |
interestings = []
|
|
148 |
while rects:
|
|
149 |
rect = rects.pop()
|
|
150 |
|
|
151 |
if 0 == rect[0]: # x-direction text
|
|
152 |
rectExpand = rect[1].adjusted(-mergeSize, 0, mergeSize, 0)
|
|
153 |
matches = [x for x in rects if (x[0] == rect[0]) and
|
|
154 |
abs(x[1].height() - rect[1].height()) < (x[1].height() + rect[1].height())*0.5 and
|
|
155 |
abs(x[1].center().y() - rect[1].center().y()) < rect[1].height()*0.5 and
|
|
156 |
rectExpand.intersects(x[1])]
|
|
157 |
else: # y -direction text
|
|
158 |
rectExpand = rect[1].adjusted(0, -mergeSize, 0, mergeSize)
|
|
159 |
matches = [x for x in rects if (x[0] == rect[0]) and
|
|
160 |
abs(x[1].width() - rect[1].width()) < (x[1].width() + rect[1].width())*0.5 and
|
|
161 |
abs(x[1].center().x() - rect[1].center().x()) < rect[1].width()*0.5 and
|
|
162 |
rectExpand.intersects(x[1])]
|
|
163 |
|
|
164 |
if matches:
|
|
165 |
for _rect in matches:
|
|
166 |
rect[1] = rect[1].united(_rect[1])
|
|
167 |
if _rect in rects:
|
|
168 |
rects.remove(_rect)
|
|
169 |
rects.append(rect)
|
|
170 |
else:
|
|
171 |
interestings.append(rect)
|
|
172 |
|
|
173 |
for rect in interestings:
|
|
174 |
matches = [_rect for _rect in interestings if rect != _rect and _rect[1].contains(rect[1])]
|
|
175 |
# if there is no boxes which contains
|
|
176 |
if not matches:
|
|
177 |
angle = rect[0]
|
|
178 |
list.append(ti.TextInfo('', round(offsetX) + rect[1].x(), round(offsetY) + rect[1].y(), rect[1].width(),
|
|
179 |
rect[1].height(), angle))
|
|
180 |
except Exception as ex:
|
|
181 |
message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
|
182 |
sys.exc_info()[-1].tb_lineno)
|
|
183 |
print(message)
|
174 |
184 |
|
175 |
185 |
return list, ocr_image
|
176 |
186 |
|
... | ... | |
272 |
282 |
app_doc_data = AppDocData.instance()
|
273 |
283 |
project = app_doc_data.getCurrentProject()
|
274 |
284 |
|
275 |
|
text_info_array = np.array_split(tInfoList, THREAD_MAX_WORKER)
|
|
285 |
text_info_array = np.array_split(tInfoList, THREAD_MAX_WORKER if len(tInfoList) > THREAD_MAX_WORKER else \
|
|
286 |
len(tInfoList))
|
276 |
287 |
with futures.ThreadPoolExecutor(max_workers=THREAD_MAX_WORKER) as pool:
|
277 |
288 |
future_text = {pool.submit(TextDetector.recognizeTextFromImage, tInfo, imgSrc, offset,
|
278 |
289 |
searchedSymbolList, worker, listWidget, maxProgressValue):
|
279 |
290 |
tInfo for tInfo in text_info_array}
|
280 |
|
"""
|
281 |
|
future = pool.submit(TextDetector.recognizeTextFromImage, tInfo, imgSrc, offset,
|
282 |
|
searchedSymbolList, worker, listWidget, maxProgressValue)
|
283 |
|
"""
|
|
291 |
|
284 |
292 |
for future in futures.as_completed(future_text):
|
285 |
293 |
try:
|
286 |
294 |
data = future.result()
|
... | ... | |
340 |
348 |
|
341 |
349 |
if worker is not None: worker.updateProgress.emit(maxProgressValue, None)
|
342 |
350 |
|
343 |
|
"""
|
|
351 |
""""
|
344 |
352 |
for text_box in tInfoList:
|
345 |
353 |
x = text_box.getX()
|
346 |
354 |
y = text_box.getY()
|
... | ... | |
359 |
367 |
@date 2018.07.24
|
360 |
368 |
'''
|
361 |
369 |
|
362 |
|
def removeTextFromImage(self, imgSrc, offset):
|
363 |
|
appDocData = AppDocData.instance()
|
364 |
|
project = appDocData.getCurrentProject()
|
365 |
|
|
366 |
|
path = os.path.join(project.getTempPath(), 'OCR_{}.png'.format(appDocData.imgName))
|
367 |
|
if os.path.isfile(path):
|
368 |
|
imgOCR = cv2.imread(path)
|
369 |
|
imgOCR = \
|
370 |
|
cv2.threshold(cv2.cvtColor(imgOCR, cv2.COLOR_BGR2GRAY), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
|
371 |
|
|
372 |
|
# remove recognized text from image
|
373 |
|
for text in self.textInfoList:
|
374 |
|
x = round(text.getX() - offset[0])
|
375 |
|
y = round(text.getY() - offset[1])
|
376 |
|
width = round(text.getW())
|
377 |
|
height = round(text.getH())
|
378 |
|
self.removeText(imgSrc, (round(text.getX()), round(text.getY())), imgOCR[y:y + height, x:x + width])
|
379 |
|
# up to here
|
380 |
|
|
381 |
|
'''
|
382 |
|
@brief remove text from image by using ocr image
|
383 |
|
@author
|
384 |
|
'''
|
385 |
|
|
386 |
|
def removeText(self, img, pt, imgOCR):
|
387 |
|
try:
|
388 |
|
x = round(pt[0])
|
389 |
|
y = round(pt[1])
|
390 |
|
width, height = imgOCR.shape[::-1]
|
391 |
|
|
392 |
|
temp = img[y:y + height, x:x + width]
|
393 |
|
imgOCR = cv2.erode(imgOCR, np.ones((3, 3), np.uint8))
|
394 |
|
mask = cv2.bitwise_or(temp, imgOCR)
|
395 |
|
imgXOR = cv2.bitwise_xor(temp, mask)
|
396 |
|
img[y:y + height, x:x + width] = cv2.bitwise_not(imgXOR)
|
|
370 |
def remove_text_from_image(self, imgSrc, offset):
|
|
371 |
# remove recognized text from image
|
|
372 |
for text in self.textInfoList:
|
|
373 |
x = round(text.getX() - offset[0])
|
|
374 |
y = round(text.getY() - offset[1])
|
|
375 |
width = round(text.getW())
|
|
376 |
height = round(text.getH())
|
|
377 |
cv2.rectangle(imgSrc, (x, y), (x + width, y + height), 255, -1)
|
|
378 |
# up to here
|
397 |
379 |
|
398 |
|
except Exception as ex:
|
399 |
|
print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
|
400 |
|
sys.exc_info()[-1].tb_lineno))
|
|
380 |
# DEBUG
|
|
381 |
#cv2.imwrite("c:\\temp\\remove_texts.png", imgSrc)
|
401 |
382 |
|
402 |
|
return img
|