openCv page Dewarp 그림책인지 텍스트 책인지 구분 방법

개요

현재의 이미지 처리 방식은 text을 기준으로 책을 피는 알고리즘을 사용하였습니다. 하지만 그림책의 경우에는 글자가 부족하고 그림의 특정부분을 윤곽선으로 인식하여 책을 제대로 펴지 못합니다.

그림 처리시 처리 결과

그렇기 때문에 텍스트 기반의 책이 이미지와 그림 기반의 책의 이미지의 처리방식은 달라져야 합니다.

일단 이 포스트에서는 이미지가 텍스트 기반인지 그림 기반인지 어떻게 구분할까에 대해서 살펴보겠습니다.

색 분포 분석

가장 처음으로 색에 대해서 분석해볼 가치가 있다고 생각했습니다.

보통 텍스트 기반의 책은 흰바탕에 검은색 텍스트를 가지고 있습니다. 그림 기반의 책은 어떤 색이 나올지 모릅니다.

그렇기 때문에 일정 이상의 흰색, 회색 계열의 색이 이미지에 많이 존재한다면 텍스트 기반의 책으로 볼수 있지 않을까 생각했습니다.

아래 코드는 k-means clustering 을 적용하여 이미지의 가장 대표적인 색상을 가진 팔레트를 만드는 예제입니다.

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
import matplotlib.pyplot as plt

average = img.mean(axis=0).mean(axis=0)

pixels = np.float32(img.reshape(-1, 3))

n_colors = 5
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
flags = cv2.KMEANS_RANDOM_CENTERS

_, labels, palette = cv2.kmeans(pixels, n_colors, None, criteria, 10, flags)
_, counts = np.unique(labels, return_counts=True)

avg_patch = np.ones(shape=img.shape, dtype=np.uint8)*np.uint8(average)

indices = np.argsort(counts)[::-1]
freqs = np.cumsum(np.hstack([[0], counts[indices]/counts.sum()]))
rows = np.int_(img.shape[0]*freqs)

dom_patch = np.zeros(shape=img.shape, dtype=np.uint8)
for i in range(len(rows) - 1):
dom_patch[rows[i]:rows[i + 1], :, :] += np.uint8(palette[indices[i]])

fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(12,6))
ax0.imshow(avg_patch)
ax0.set_title('Average color')
ax0.axis('off')
ax1.imshow(dom_patch)
ax1.set_title('Dominant colors')
ax1.axis('off')
plt.show(fig)

원본 그림

색상 팔레트

위 예제에서 팔레트를 분석하면 배경이 되는 검정색이 제일 많이 분포하는 것을 알 수 있습니다.

나머지 색상을 보면 회색빛으로 책의 색이 분포되고 있습니다.

이에 착안하여 2~4번째 색상이 회색빛인 (140,140,140) 이상이 되는 색으로 분포된다면 텍스트라고 판단할 수 있다고 생각했습니다.

1
2
3
4
5
img_paint = False

for i in range(3):
if np.all(palette[indices[i+1]] < 140):
img_paint = True

가로줄 윤곽선 분포

또하나의 기법으로는 책의 윤곽선의 갯수를 판별하는 것입니다.

위 소스에서 쓰인 함수를 바탕으로 아래와 같이 구성해 보았습니다.

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

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)

return len(spans_text)

위 코드 처럼 page dewarping에 쓰인 함수를 활용해서 몇개의 윤곽선을 탐지했는지 판단할 수 있습니다.

결론

저의 경우는 위에서 판별한 평균 색상과 윤곽선의 갯수 두가지를 혼합하여 판별을 해보았습니다.

1
2
3
4
5
6
if tmp_len < 15 and img_paint == True:
IMAGE_TYPE = 'paint'
elif tmp_len < 3:
IMAGE_TYPE = 'paint'
else:
IMAGE_TYPE = 'text'

윤곽선 갯수가 15개 미만이고 색상 판별에서 그림이라고 판별하면 그림이라고 판단했습니다.

색상에서 텍스트라고 판별했지만 윤곽선이 3개 미만이면 역시 그림이라고 판별했습니다.

두가지 모두 해당되지 않는다면 텍스트라고 판별했습니다.

아직까지는 샘플에서는 상당한 정확도를 보여주고 있습니다.

여러 테스트를 해보고 좀더 보완을 해볼 수 있는 방안을 생각해 봐야겠습니다.

공유하기