id card를 식별하기 위해 text detection을 진행한다. 이때 input image가 삐뚤어지면 text detection 성능이 떨어지게 된다. 따라서 text detection에서 skew correction은 필수적이다.
우선 skew correction의 성능향상을 위해 다음의 전처리 과정을 거쳤다.
구글링을 통해 다른 방법들을 많이 시도해봤지만 위의 순서로 진행했을 때 특히나 직선검출(뒤에서 다룰 Hough Transform)이 잘 이루어졌다 .ㅎㅎ
그러면 본격적으로 skew correction의 과정을 살펴보기로 한다.
허프 변환은 수학적인 방법으로 기울어진 직선의 각도를 구하는 방법이다.
open cv2 에서는 HoughLines와 HoughLinesP 두가지 함수를 제공한다. 직선 검출 자체로는 HoughLines가 성능이 좋으나 연산이 느리다. 이에 비해 HoughLinesP는 확률적으로 직선을 검출하기 때문에 빠르다는 강점이 있다.
( HoughLines로 시도해 보았는데 원하는 직선과 다른 방향으로 많이 뽑혀서 프로젝트에 사용하지 않기로 했다. 따라서 이후 과정에서는 HoughLinesP 방법을 사용하기로 한다.)
두 함수의 return이 다르므로 사용방법에 대해 공식문서를 참고 바란다.
결국 기울어진 각도(theta)를 구하는게 skew correction의 핵심이다. 사실 위의 방법으로 theta를 구하여 ratation을 진행해도 되지만 실제 프로젝트에선 아크탄젠트를 구하여 rotation을 진행하였다. 개념은 똑같기 때문에 사진첨부로 설명을 마무리하고자 한다.
위의 hough transform으로 구한 theta만큼 rotation을 진행하였다.
이때 theta의 방향이 중요하다. cv2의 rotation은 시계반대방향으로 진행된다. 따라서 theta가 음수인지 양수인지 잘 파악하고 rotation의 방향을 결정할 필요가 있다.
cv2에서 getRotaionMatrix2D 함수를 제공한다. 사용법은 공식문서에 예시와 함께 잘 설명되어 있으니 참고바란다.
# 1. Compute Skew
def compute_skew(file_name):
#load in grayscale:
src = cv2.imread(file_name,0) # original_image
img = src.copy()
h, w = img.shape[0:2]
_,th1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)
cv2.bitwise_not(th1, th1)
canny = cv2.Canny(th1, 100, 200)
# Hough transform:
minLineLength = w/2.0
maxLineGap = 20
lines = cv2.HoughLinesP(canny,1,np.pi/180,100,minLineLength,maxLineGap)
if lines is not None:
for i in range(lines.shape[0]):
pt1 = (lines[i][0][0], lines[i][0][1]) # 시작점 좌표
pt2 = (lines[i][0][2], lines[i][0][3]) # 끝점 좌표
cv2.line(img, pt1, pt2, (0,0,255), 2, cv2.LINE_AA)
cv2.circle(img,pt1,3,(255,0,0),-1)
cv2.putText(img,f"{i+1}",pt1,cv2.FONT_HERSHEY_COMPLEX, fontScale=0.5, color=(255,0,0), thickness=1)
# calculate the angle between two points
angle = 0.0
angle_list = []
for line in lines:
angle = math.atan2(line[0][3]*1.0 - line[0][1]*1.0,line[0][2]*1.0 - line[0][0]*1.0)
angle_list.append(angle)
theta = angle_list[0]* 180.0 / np.pi
return (theta)
# 2. Deskew
def deskew(original_img, theta):
h,w = original_img.shape[:2]
# 이미지의 중심점을 기준으로 theta도 회전 하면서 1.0배 Scale
M= cv2.getRotationMatrix2D((w/2, h/2), theta, 1.0) # 변환행렬
dst = cv2.warpAffine(original_img, M,(w, h))
return dst
[참고자료]