openCv page Dewarp 동적 마스킹

개요

동적 마스킹이란 기존 소스에서 하드 코딩값으로 가장자리의 부분을 잘라냈던 것을 텍스트 인지를 통해 동적으로 자르는 것을 목표로 합니다.

결과는 다음과 같습니다.

원본과 결과 비교

위의 알고리즘은 기존소스의 mask를 구하기 전에 먼저 spans를 구하고 거기에 따른 x,y 좌표의 최대 최소값을 구해 적용하는 것입니다.

구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def text_mask(small):
global DEBUG_LEVEL
global TEXT_MIN_WIDTH
global TEXT_MIN_HEIGHT
global TEXT_MAX_THICKNESS

tmp_debug_level = DEBUG_LEVEL
DEBUG_LEVEL = 0
tmp_text_min_width = TEXT_MIN_WIDTH
TEXT_MIN_WIDTH = 5
tmp_text_min_height = TEXT_MIN_HEIGHT
TEXT_MIN_HEIGHT = 1
tmp_text_max_thickness = TEXT_MAX_THICKNESS
TEXT_MAX_THICKNESS = 20

name = 'mask_temp'
spans = []
height, width = small.shape[:2]
pagemask = np.zeros((height, width), dtype=np.uint8)
cv2.rectangle(pagemask, (0, 0), (width, height), (255, 255, 255), -1)

# 윤곽선 정보 검출
cinfo_list = get_contours(name, small, pagemask, 'text')
# 윤곽선을 길게 합침
spans_text = assemble_spans(name, small, pagemask, cinfo_list)

# 텍스트가 없어서 span 갯수가 부족시 line을 디텍팅 하도록 설계
if len(spans_text) < 3:
print (' detecting lines because only', len(spans_text), 'text spans')
cinfo_list = get_contours(name, small, pagemask, 'line')
spans2 = assemble_spans(name, small, pagemask, cinfo_list)
if len(spans2) > len(spans_text):
spans_text = spans2

# 윤곽선 각각의 넓이와 y축 위치 구하기.
span_with_min = []
span_with_max = []
span_height_min = []
span_height_max = []
for i, span in enumerate(spans_text):
# 한줄 빼오기
contours = [cinfo.contour for cinfo in span]
# 빼온거에서 젤 왼쪽과 오른쪽 을 빼서 길이 재고 y축 값 구하기.
contours_min = np.argmin(contours[0], axis = 0)
contours_max = np.argmax(contours[0], axis = 0)
span_with_min.append(contours[0][contours_min[0][0]][0][0])
span_with_max.append(contours[0][contours_max[0][0]][0][0])
span_height_min.append(contours[0][contours_min[0][1]][0][1])
span_height_max.append(contours[0][contours_max[0][1]][0][1])

DEBUG_LEVEL = tmp_debug_level
TEXT_MIN_WIDTH = tmp_text_min_width
TEXT_MIN_HEIGHT = tmp_text_min_height
TEXT_MAX_THICKNESS = tmp_text_max_thickness

# 리턴 값은 x축 최소값, x축 최대값, y축 최소값, y축 최대값
return np.min(span_with_min), np.max(span_with_max), np.min(span_height_min), np.max(span_height_max)

윤곽선 구하기

1
2
3
4
5
6
7
8
9
10
11
12
13
global DEBUG_LEVEL
global TEXT_MIN_WIDTH
global TEXT_MIN_HEIGHT
global TEXT_MAX_THICKNESS

tmp_debug_level = DEBUG_LEVEL
DEBUG_LEVEL = 0
tmp_text_min_width = TEXT_MIN_WIDTH
TEXT_MIN_WIDTH = 5
tmp_text_min_height = TEXT_MIN_HEIGHT
TEXT_MIN_HEIGHT = 1
tmp_text_max_thickness = TEXT_MAX_THICKNESS
TEXT_MAX_THICKNESS = 20

기존에 정의되었던 TEXT 관련 검색 기준을 낮추어 더 많은 것을 찾도록 구현하였습니다. 디버그는 기존 소스의 디버그만 볼 수 있도록 0으로 지정하였습니다.

기존 값들을 저장하기 위해 tmp_xxx로 지정하여 보존하였습니다.

1
2
3
4
5
6
7
8
9
10
name = 'mask_temp'
spans = []
height, width = small.shape[:2]
pagemask = np.zeros((height, width), dtype=np.uint8)
cv2.rectangle(pagemask, (0, 0), (width, height), (255, 255, 255), -1)

# 윤곽선 정보 검출
cinfo_list = get_contours(name, small, pagemask, 'text')
# 윤곽선을 길게 합침
spans_text = assemble_spans(name, small, pagemask, cinfo_list)

당장 필요없는 파라미터를 더미값으로 주고 최종 윤곽선을 구하는 assemble_spans까지 진입하도록 하였습니다.

유효한 부분의 위치 구하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 윤곽선 각각의 넓이와 y축 위치 구하기.
span_with_min = []
span_with_max = []
span_height_min = []
span_height_max = []
for i, span in enumerate(spans_text):
# 한줄 빼오기
contours = [cinfo.contour for cinfo in span]
# 빼온거에서 젤 왼쪽과 오른쪽 을 빼서 길이 재고 y축 값 구하기.
contours_min = np.argmin(contours[0], axis = 0)
contours_max = np.argmax(contours[0], axis = 0)
span_with_min.append(contours[0][contours_min[0][0]][0][0])
span_with_max.append(contours[0][contours_max[0][0]][0][0])
span_height_min.append(contours[0][contours_min[0][1]][0][1])
span_height_max.append(contours[0][contours_max[0][1]][0][1])

DEBUG_LEVEL = tmp_debug_level
TEXT_MIN_WIDTH = tmp_text_min_width
TEXT_MIN_HEIGHT = tmp_text_min_height
TEXT_MAX_THICKNESS = tmp_text_max_thickness

# 리턴 값은 x축 최소값, x축 최대값, y축 최소값, y축 최대값
return np.min(span_with_min), np.max(span_with_max), np.min(span_height_min), np.max(span_height_max)

해당 부분은 기존에 포스트 했던 opencv 윤곽선(Contours)값의 의미(내용) 최대값 최소값 구하기를 응용한 것입니다.

기존 spans에서 윤곽선을 하나씩 빼옵니다.

그 후 윤곽선의 최대 최소가 되는 좌표를 각각에 리스트에 넣어 줍니다.

그 후 최종적으로 리턴을 넣어준 리스트의 최소, 최대값으로 리턴 한다면 각각의 그림에서 유효한 영역의 부분을 구할 수 있습니다.

masking

1
2
3
4
5
6
7
8
9
10
11
def get_page_extents(small):

# 이미지의 높이, 넓이 취득
height, width = small.shape[:2]

if IMAGE_TYPE == 'text':
xmin, xmax, ymin, ymax= text_mask(small)
xmin = xmin - PAGE_MARGIN_X
ymin = ymin - PAGE_MARGIN_Y
xmax = xmax + PAGE_MARGIN_X
ymax = ymax + PAGE_MARGIN_Y

기존 소스에서 고정값으로 되있던 부분을 위에서 구한 값으로 대체하였습니다.

다만 기존에 구한 값으로 자르면 마진 없이 자르게 되어서 이상한 모습을 나타나게 됩니다.

저같은 경우는 10의 값을 주어서 output 이미지가 좀더 자연스럽게 나올 수 있도록 하였습니다.

공유하기