개정판 f5b9097a
issue #478: 텍스트 인식 시 패딩 크기 증가(10)
- 다른 텍스트 영역에 포함되는 텍스트 영역 제거
Change-Id: I157577f0f98c8f362ac68854fb304de58a37ebcf
DTI_PID/DTI_PID/TextDetector.py | ||
---|---|---|
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 |
내보내기 Unified diff