OpenCV | 어파인 변환

박나연·2021년 4월 4일
0

OpenCV

목록 보기
23/40
post-custom-banner

어파인 변환

어파인 변환은 영상의 평행 이동, 확대 및 축소, 회전 등의 조합으로 만들 수 있는 기하학적 변환을 나타낸다. 기하학적인 변환은 픽셀 값은 그대로 유지하면서 위치만을 변경시키는 작업이다. 기하학적 변환 중 어파인 변환은 영상을 평행이동시키거나 회전, 크기 변환 등을 통해 만들 수 있는 변환을 통칭한다.

어파인 변환은 모두 여섯개의 파라미터를 이용한 수식으로 정의할 수 있다. 결과 영상의 좌표 x' y' 를 입력 영상의 좌표를 파라미터로 넘겨 고유의 함수 f로 표현가능하고, 이를 각각 ax + by + c, dx + ey + f 와 같은 1차 다항식으로 표현한다.

x' = f1(x, y) = ax + by + c
y' = f2(x, y) = dx + ey +f

이를 행렬식으로 바꾸면

이와 같이 표현가능하고, 수학적 편의를 위해 하나의 아래와 같이 하나의 행렬 곱셈 형태로 바꿀 수 있다.

위의 수식에서 여섯개의 파라미터로 구성된 2x3행렬을 어파인 변환 행렬이라고 부른다.

https://jackyoon5737.tistory.com/84

입력영상과 어파인 변환 결과 영상으로부터 어파인 변환 행렬을 구하기 위해서는 최소 세점의 이동관계를 알아야 한다. (미지수가 각각 3개 이므로)
OpenCV에서 어파인 변환 행렬을 구하는 함수와 어파인 변환행렬을 이용하여 실제 영상을 어파인 변환하는 함수를 모두 제공한다.

getAffineTransform()

Mat getAffineTransform(const Point2f src[], const Point2f dst[]);
Mat getAffineTransform(InputArray src, InputArray dst);

src : 입력 영상에서 세점의 좌표
dst : 결과 영상에서 세점의 좌표
반환값 : 2x3 어파인 변환 행렬

2x3 어파인 변환 행렬을 가지고 있을때, 영상을 어파인 변환한 결과영상을 생성하려면 warpAffine() 함수를 사용한다.

warpAffine()

void warpAffine(InputArray src, OutputArray dst, 
InputArray M, Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());

M : 2x3 어파인 변환 행렬
dsize : 결과 영상 크기
flags : 보간법 알고리즘
borderMode : 가장자리 픽셀 확장 방식
borderValue : BORDER_CONSTANT일때 사용할 상수 값

void affine_transform() {
	Mat src = imread("tekapo.bmp");

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	Point2f srcPts[3], dstPts[3];//세점의 좌표를 저장할 배열
	srcPts[0] = Point2f(0, 0); // 좌측상단, 우측상단, 우측하단
	srcPts[1] = Point2f(src.cols-1, 0);
	srcPts[2] = Point2f(src.cols-1, src.rows -1);
	dstPts[0] = Point2f(50, 50); //이동할 좌표
	dstPts[1] = Point2f(src.cols-100, 100);
	dstPts[2] = Point2f(src.cols-50, src.rows-50);

	Mat M = getAffineTransform(srcPts, dstPts); //어파인 변환 행렬

	Mat dst;
	warpAffine(src, dst, M, Size()); //결과영상 생성

	imshow("src", src);
	imshow("dst", dst);

	waitKey();
	destroyAllWindows();

}


위의 어파인 변환행렬과 수식을 활용하여 아래와 같은 여러 변환을 구현할 수 있다.

이동 변환

영상을 가로 또는 세로 방향으로 일정 크기만큼 이동시키는 연산을 의미하며 시프트 연산이라고도 한다.

입력 영상의 모든 좌표를 x방향으로 a만큼, y방향으로 b만큼 이동하는 변환을 수식으로 나타내면 다음과 같다.

x' = x + a
y' = y + b

이를 행렬로 표현하면
[x', y'] = [1 0 / 0 1][x, y] + [a, b]
로 표현가능하며 어파인 변환 행렬은

다음과 같다.

void affine_translation() {
	Mat src = imread("tekapo.bmp");

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	Mat M = Mat_<double>({ 2,3 }, { 1,0,150,0,1,100 }); //가로방향 150, 세로방향 100 이동
	Mat dst;
	warpAffine(src, dst, M, Size());
	imshow("src", src);
	imshow("dst", dst);

	waitKey();
	destroyAllWindows();

}


전단 변환

전단 변환은 직사각형 형태의 영상을 한쪽 방향으로 밀어서 평행사변형 모양으로 변현되는 변환이며 층밀림 변환이라고도 한다.

x' = x + m*y
y' = y

위의 수식은 y좌표가 증가함에 따라 영상을 가로 방향으로 조금씩 밀어서 만드는 전단변환 수식이다.

x' = x
y' = m*x + y

위 수식은 x좌표가 증가함에 따라 영상을 세로 방향으로 조금씩 밀어 만드는 전단변환이다.

따라서 어파인 변환 행렬은 다음과 같이 나타난다.

void affine_shear() {
	Mat src = imread("tekapo.bmp");

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	double mx = 0.3;
	Mat M = Mat_<double>({ 2,3 }, { 1, mx, 0,0,1,0 });

	Mat dst;
	warpAffine(src, dst, M, Size(cvRound(src.cols + src.rows * mx), src.rows)); // 결과영상 크기 조정

	imshow("src", src);
	imshow("dst", dst);

	waitKey();
	destroyAllWindows();
}


크기 변환

크기변환은 영상의 전체적인 크기를 확대 또는 축소하는 변환이다.

x' = sx
y' = sy

어파인 변환 행렬은 다음과 같이 나타난다.

크기변환도 warpAffine() 함수를 이용해 수행할 수 있지만 OpenCV에서는 보다 단단하게 크기를 변경할 수 있는 resize()함수를 제공한다.

resize()

void resize(InputArray src, OutputArray dst, 
Size dsize, double fx = 0, double fy = 0,
int interpolation = INTER_LINEAR);

src : 입력영상
dst : 출력영상
dsize : 결과 영상 크기
fx : x축 방향으로의 크기 변환 비율
fy : y축 방향으로의 크기 변환 비율
interpolation : 보간법 지정

보간법 : 결과 영상의 픽셀 값을 결정하기 위해 입력 영상에서 주변 픽셀 값을 이용하는 방식

void affine_scale() {
	Mat src = imread("rose.bmp");

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	Mat dst1, dst2, dst3, dst4;
	resize(src, dst1, Size(), 4, 4, INTER_NEAREST); // 크기변환 비율을 지정한 경우
	resize(src, dst2, Size(1920, 1280)); // 픽셀 단위로 영상크기를 지정한 경우, 양선형 보간법


	imshow("src", src);
	imshow("dst1", dst1(Rect(400, 500, 400, 400)));
	imshow("dst2", dst2(Rect(400, 500, 400, 400))); 

	waitKey();
	destroyAllWindows();

}


회전변환

특정 좌표를 기준으로 영상을 원하는 각도만큼 회전하는 변환이다.

어파인 변환 행렬은 다음과 같다.

위는 x' 와 y'의 도출 과정

OpenCV에서 getRotationMatrix2D() 함수를 제공한다.

getRotationMatrix2D()

Mat getRotationMatrix2D(Point2f center, double angle, double scale);

center : 회전 중심 좌표
angle : 회전 각도
scale : 회전 후 추가적으로 확대 또는 축소할 비율
반환값 : 2x3 어파인 변환 행렬

void affine_rotation() {
	Mat src = imread("tekapo.bmp");
	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}
	
	Point2f cp(src.cols / 2.f, src.rows / 2.f);
	Mat M = getRotationMatrix2D(cp, 20, 1);

	Mat dst;
	warpAffine(src, dst, M, Size());


	imshow("src", src);
	imshow("dst", dst);

	waitKey();
	destroyAllWindows();

}


대칭 변환

영상의 상하를 뒤집은 것처럼 바꾸는 변환을 상하 대칭 또는 상하 반전이라고 한다. 입력 영상의 픽셀과 결과 영상의 픽셀이 일대일로 대응되므로 보간법이 필요하지 않다.

x' = w - 1 - x
y' = y


위 수식과 행렬은 좌우대칭 변환에 의한 좌표 변환이고 w는 입력 영상의 가로 크기이다.

위 수식과 행렬은 상하 대칭 변환을 나타낸 것이다.
수식에서 1을 빼준것은 인덱스 값이 0부터 시작하기 때문이다.

OpenCV에서 영상의 대칭 변환을 수행하는 flip()함수를 제공한다.

flip()

void flip(InputArray src, OutputArray dst, int flipCode);

src : 입력 영상
dst : 결과 영상
flipCode : 대칭 방법 지정 플래그, 양수이면 좌우 대칭, 0이면 상하대칭, 음수이면 상하대칭과 좌우 대칭 모두 수행

void affine_flip() {
	Mat src = imread("eastsea.bmp");
	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	imshow("src", src);

	Mat dst;
	int flipCode[] = { 1,0,-1 };
	for (int i = 0; i < 3; i++) {
		flip(src, dst, flipCode[i]);
		imshow("dst", dst);
		waitKey();
	}
	destroyAllWindows();
}

flipCode = 1
flipCode = 0flipCode = -1

profile
Data Science / Computer Vision
post-custom-banner

0개의 댓글