Geometric Transform 실습

Stella Kim·2021년 7월 17일
0

Image Processing

목록 보기
5/8
post-thumbnail

공간 변환에 따른 영상 크기 변경

공간 변환 수행 시 영상 일부가 잘리지 않도록 결과 영상의 크기를 변경한다.

해결 과정 및 주요 코드에 대한 설명

def Affine_transform(img, type):
	rows, cols, _ = img.shape
	result_x, result_y = cols, rows

	if type == 1:
		result_x, result_y, dx, dy = getImageSize(img, type, _)
		matrix = np.float32([[1, 0, dx], [0, 1, dy]])
	elif type == 2:
		result_x, result_y, r = getImageSize(img, type, _)
		matrix = cv2.getRotationMatrix2D((cols / 2, rows / 2), r, 1)
		matrix[0, 2] += (result_x - cols) / 2
		matrix[1, 2] += (result_y - rows) / 2
	elif type == 3:
		result_x, result_y, scale = getImageSize(img, type, _)
		matrix = np.float32([[scale, 0, 0], [0, scale, 0]])
	elif type == 4:
		u = math.tan(30*math.pi/180)
		matrix = np.float32([[1, u, 0], [0, 1, 0]])

		newpt = np.atleast_2d([cols - 1, rows - 1, 0])
		newpt = newpt.transpose()
		newpt = matrix.dot(newpt)
		result_x, result_y = getImageSize(img, type, newpt)
	elif type == 5:
		pts1 = np.float32([[50,50], [200,50], [50,200]])
		pts2 = np.float32([[10,100], [200,50], [100,250]])
		matrix = cv2.getAffineTransform(pts1, pts2)
		result_x, result_y, matrix = getImageSize(img, type, matrix)

	dst = cv2.warpAffine(img, matrix, (result_x, result_y), flags=cv2.INTER_LINEAR)
	return dst

getImageSize 함수를 불러와 결과 영상에서 이미지의 잘리는 부분이 없도록 사이즈 조정과 관련된 코드를 추가로 작성하였다. 이를 위해 입력 영상의 사이즈를 저장하는 기존 코드에도 선언되었던 변수 rows와 cols 외에도 결과 영상의 가로, 세로 사이즈를 저장할 수 있는 변수 result_x, result_y도 추가로 윗부분에 선언해주었다.

def getImageSize(img, type, newpt=[]):
    rows, cols, _ = img.shape

    if type == 1:
        dx = 100
        dy = 50
        result_x = cols + dx
        result_y = rows + dy
        return result_x, result_y, dx, dy

    elif type == 2:
        r = 45
        angle = math.radians(r)
        result_x = int(cols * math.cos(angle) + rows * math.sin(angle))
        result_y = int(cols * math.sin(angle) + rows * math.cos(angle))
        return result_x, result_y, r

    elif type == 3:
        scale = 1.5
        result_x = int(scale * cols)
        result_y = int(scale * rows)
        return result_x, result_y, scale

    elif type == 4:
        result_x = int(newpt[0])
        result_y = rows
        return result_x, result_y

    else:
        result_x = int((newpt.dot(np.array([cols, rows, 1]))-newpt.dot(np.array([0, 0, 1])))[0])
        result_y = int((newpt.dot(np.array([0, rows, 1]))-newpt.dot(np.array([cols, 0, 1])))[1])
        newpt[0, 2] -= int(newpt.dot(np.array([0, 0, 1]))[0])
        newpt[1, 2] -= int(newpt.dot(np.array([cols, 0, 1]))[1])
        return result_x, result_y, newpt

평행이동 변환은 간단하게 가로로 100, 세로로 50만큼 이동하도록 크기를 설정하여 그만큼을 결과 영상의 길이에 더하여 길이들과 100, 50에 해당하는 dx, dy를 반환하였다. 회전 변환은 45도로 회전 각도를 지정하고 삼각각 함수를 사용해 결과 영상에서 추가로 늘어나야 할 길이를 구하였다. 스케일링 변환은, 스케일링 값을 1.5로 설정한 후 그만큼을 기존의 입력 영상 값인 rows, cols에 곱하여 곱한 결과 값과 scale 값을 반환하였다. 비틀기 변환에 대해서는 어렵지 않게 작성할 수 있었다. 대응점 변환은 [cols, rows, 1] 행렬을 곱한 것에서 [0, 0, 1] 행렬을 곱한 것을 뺀 값의 인덱스 0 값을 결과 영상의 가로 길이로 설정하고, [0, rows, 1] 행렬을 곱한 것에서 [cols, 0, 1] 행렬을 곱한 것을 뺀 값의 인덱스 1 값을 결과 영상의 세로 길이로 설정하였다. 그리고 Affine_transform 함수에서의 type 1의 경우와 같이 결과 영상에서 일부가 잘리지 않도록 평행이동 시켜주었다.

ap = argparse.ArgumentParser()
ap.add_argument('-i', '--image', required = True, \
			help = 'Path to the input image')
args = vars(ap.parse_args())

filename = args['image']

i로 영상의 경로를 입력받는다. 받아온 값은 filename 변수에 저장한다.

실행 결과

  • python geometric_transform2.py --image ../images/nature.jpg 으로 명령을 주었을 경우

1) 평행 이동

2) 회전

3) 스케일링

4) 비틀기

5) 대응점에 의한 변환

코드

자세한 코드는 Github에서 확인할 수 있다.

문서 정렬하기

임의 모양 사각형 형태를 갖는 문서의 모양을 직사각형으로 변환한 후 이진화하여 표현한다.

해결 과정 및 주요 코드에 대한 설명

def transform_document(img):
    temp = img.copy()
    temp = cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY)
    temp = cv2.Canny(temp, 40, 200)
    contours, _ = cv2.findContours(temp, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    for i in range(0, len(contours)):
        cnt = sorted(contours, key=cv2.contourArea, reverse=True)[i]
        epsilon = 0.1 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)
        if len(approx) == 4:
            cv2.drawContours(img, [approx], 0, (0, 0, 255), 2)
            break

먼저 컬러 영상을 그레이 스케일 영상으로 변환한다. 이후 Canny 함수를 사용해 에지 검출을 수행하고, findContours 함수를 사용해 에지를 검출한 결과 영상에 대해 연결 요소를 검출한다. 크기순으로 정렬한 다음, 선택한 연결 요소의 모양을 근사화하기 위해 approxPolyDP 함수를 사용한다. 조건문에 len을 사용하여 근사화된 연결 요소가 사각형인지 확인한 후, 사각형일 경우에는 네 개의 좌표를 사용해서 투영 변환을 수행하기 위한 준비를 한다.

sum = np.array(
        [approx[0][0][0] + approx[0][0][1], approx[1][0][0] + approx[1][0][1], approx[2][0][0] + approx[2][0][1],
         approx[3][0][0] + approx[3][0][1]])
diff = np.array(
        [approx[0][0][0] - approx[0][0][1], approx[1][0][0] - approx[1][0][1], approx[2][0][0] - approx[2][0][1],
         approx[3][0][0] - approx[3][0][1]])

topLeft = approx[np.argmin(sum)]  # x+y가 가장 작은 값이 우하단 좌표
bottomRight = approx[np.argmax(sum)]  # x+y가 가장 큰 값이 우하단 좌표
topRight = approx[np.argmin(diff)]  # x-y가 가장 작은 것이 우상단 좌표
bottomLeft = approx[np.argmax(diff)]  # x-y가 가장 큰 값이 좌하단 좌표

rows, cols, _ = img.shape
pts1 = np.float32([topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]])
pts2 = np.float32([[0, 0], [0, rows], [cols, 0], [cols, rows]])
matrix = cv2.getPerspectiveTransform(pts1, pts2)
img = cv2.warpPerspective(img, matrix, (cols, rows))

img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 7)

return img

사각형에서 좌측 상단 좌표, 우측 상단 좌표, 좌측 하단 좌표, 우측 하단 좌표를 지정하기 위해 np를 사용해 sum 과 diff 배열을 만든다. 이후 이 중에서 sum의 값이 가장 큰 점은 우측 하단에, sum의 값이 가장 작은 점은 좌측 상단에 배정되고, 두 좌표의 차가 가장 작은 값은 우측 상단, 가장 큰 값은 좌측 하단에 배정된다. 이로써 순서에 주의하여 pts1, pts2 변수에 점의 좌표들을 입력하고 투영 변환을 수행해 직사각형으로 모양을 정렬한 다음, adaptive threshold를 적용하여 이진 영상을 생성해낸다.

ap = argparse.ArgumentParser()
ap.add_argument('-i', '--image', required=True, help='Path to the input image')
args = vars(ap.parse_args())

filename = args['image']

i로 이미지의 경로를 입력받는다. 받아온 값은 filename 변수에 저장한다.

실행 결과

  • python align_document.py --image ../imagess/receipt.jpg 으로 명령을 주었을 경우

    사선 방향으로 있던 영수증이 올바르게 직사각형 모양으로 검출되었다.

  • python align_document.py --image ../images/document.jpg 으로 명령을 주었을 경우

    마찬가지로 사선 방향으로 있던 문서가 직사각형 모양으로 올바르게 정렬되었다.

코드

자세한 코드는 Github에서 확인할 수 있다.

profile
취업 준비 용으로 사용했던 기술 블로그입니다. 이제는 업로드 거의 안 할지도..

0개의 댓글