어파인 변환

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

Mat getAffineTransform(InputArray src, InputArray dst);

-src, dst: 3개의 원본 좌표점/결과점 ( Point2f src[3] 또는 vector<Point2f> src) dst의 경우 dst로 바꿔줌

-변환값: 2x3 크기의 변환행렬(CV_64F)

 

 

투시변환

Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[], int solveMethod = DECOMP_LU);

Mat getPerspectiveTransform(InputArray src, InputArray dst, int solveMethod = DECOMP_LU);

-src: 4개의 원본좌표점 ( Point2f src[4]; 또는 vector<Point2f>src;)
-dst: 4개의 결과 좌표점( Point2f dst[4]; 또는 vector<Point2f> dst;)
-반환값: 3x3 크기의 변환행렬 (CV_64F)

 

 

2x3  크기의 어파인 변환행렬을 알고 있거나  3x3 투시변환행렬을 알고 있다면, 주어진 영상을 실제로 어파인 변환 또는 투시 변환해주는 warpAffine , warpPerspective 함수를 사용할 수 있다.

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

**
M: 2x3 어파인 변환 행렬, CV_32F 또는 CV64F.
dsize: 결과 영상의 크기
flags: 보간법 선택
borderMode: 가장자리 픽셀처리 방식
borderValue: BORDER_CONSTANT 모드 사용 시 사용할 픽셀 값
void warpPerspective(InputArray src, OutputArray dst, InputArray M, size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar());

**
M: 3x3  투시변환 행렬. CV_32F 또는 CV_64F
dsize: 결과 영상의 크기
flags: 보간법 선택
borderMode: 가장자리 픽셀처리 방식
borderValue: BORDER_CONSTANT 모드 사용 시 사용할 픽셀값.

이동변환이나 전담변환은 직접구현해야함.(Opencv에선 해당 함수 없음) 또는 Affine transform 을 직접 구성해서 warpAffine 호출해서 이동 변환 가능

  크기변환은 resize 함수로 크기 변환 가능.

 

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;
#define TC 3

void translation_transform(Mat src, Mat& dst) {
	/*이동변환 표현*/
	for (int y = 0; y < src.rows; ++y) {
		for (int x = 0; x < src.cols; ++x) {
			int x_ = x + 200;  // 가로 200
			int y_ = y + 100;  // 세로 100   offset 줘서 원점으로 만듦.
			if (x_ < 0 || y_ < 0 || x_ >= dst.cols || y_ >= dst.rows)continue; // width, heigh  범위 넘어가는 경우를 방지.
			dst.at<uchar>(y_, x_) = src.at<uchar>(y, x);

		}
	}
}

void shear_transform(Mat src, Mat& dst) {
	// 영상의 전단 변환(shear transformation):  직사각형 형태의 영상을 한쪽 방향으로 밀어서 평행사변형 모양으로 변환시킴. (==층밀림 변환).
	// 가로 방향 또는 세로 방향으로 따로 정의  됨.
	double m = 0.5;
	for (int y = 0; y < src.rows; y++) {
		for (int x = 0; x < src.cols; x++) {

			//x축 방향, 가로방향으로 전단변환
			//int nx = int(x + (m * y)); // 출력영상 좌표 설정.  원 x좌표에 0.5 를 곱한 y좌표 ㅍ값을 더함.
			//int ny = y;

			//y축방향, 세로방향으로 전단 변환
			int nx = x;
			int ny = int(y + (m * x));
			dst.at<uchar>(ny, nx) = src.at<uchar>(y, x);
		}
	}
}

/******************************상기 함수들은 이전에 다뤘던 transform 방식**********************************************************************/

void warpAffine_func(Mat src, Mat & dst) {
	//Mat trans = (Mat_<float>(2, 3) << 1, 0, 100, 0, 1, 100);   //    [1,0,100] , [0,1,100]  으로 가로방향 , 세로방향 각각 100픽셀 이동 하는 형태의 affine transformation matrix 임.
	Mat trans = (Mat_<float>(2, 3) << 0.5, 0, 100, 0, 0.5, 100); // 가로 세로 각각 0.5 로 줄 시 가로/세로가 각각 1/2 축소된 형태


	//warpAffine(src, dst, trans, src.size());
	warpAffine(src, dst, trans, Size(700,700));
}


int main()
{

	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

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

#if TC == 1
	Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC1); // src와 동일한 행렬(0으로 채워짐),
	translation_transform(src, dst);

#elif TC == 2
	//x축 방향, 가로방향으로 전단변환
	//Mat dst(src.rows, src.cols * 3 / 2, src.type(), Scalar(0));  // 특이사항: 가로크기를 입력영상의 1.5 배로 설정.

	//y축방향, 세로방향으로 전단 변환
	Mat dst(src.rows * 3 / 2, src.cols, src.type(), Scalar(0)); //특이사항: 세로크기를 입력영상의 1.5 배로 설정.
	shear_transform(src, dst);

/********************* TC 3 부터 진행 할 예정. 그 이전은 이전에 배웠던 내용************************/
#elif TC == 3
	Mat dst;
	warpAffine_func(src, dst);
#endif

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

warpAffine_func 만 보면 된다.

 

 

투시변환을 바탕으로 버드아이뷰 (bird's-eye view) 를 다뤄보자

버드 아이뷰란 새가 하늘에 내려다 보듯, 매우 높은 곳에 위치한 카메라가 아래의 피사체를 찍은 화면이다.

투시 변환을 이용 할 시 전면에서 촬영된 영상을 버드 아이뷰 처럼 변환 할 수 있다.

 

bird eyeview code

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

int main()
{
	VideoCapture cap("../data/test_video.mp4");
	if (!cap.isOpened()) {
		cerr << "Video open failed!" << endl;
		return -1;
	} 
	Mat src;
	while (true) {
		cap >> src;
		if (src.empty())break;
		
		int w = 500, h = 260;  // 임의 지정

		vector<Point2f> src_pts(4);
		vector<Point2f> dst_pts(4);

		//앞쪽 사다리꼴 좌표들
		src_pts[0] = Point2f(474, 400); 
		src_pts[1] = Point2f(710, 400);
		//뒷쪽 사다리꼴 좌표들
		src_pts[2] = Point2f(866, 530);
		src_pts[3] = Point2f(366, 530);

		//출력영상 좌표들
		dst_pts[0] = Point2f(0, 0);
		dst_pts[1] = Point2f(w - 1, 0);

		dst_pts[2] = Point2f(w - 1, h - 1);
		dst_pts[3] = Point2f(0, h - 1);

		Mat per_mat = getPerspectiveTransform(src_pts, dst_pts);  // 입력점들의 좌표와 출력점들의 좌표를 인자로 넣고  3x3 투시변환행렬을 얻는다

		Mat dst;
		warpPerspective(src, dst, per_mat, Size(w, h));  //입력영상이 dst 영상으로 변환됨  

#if 1
		// src_pts에 들어있는 점들을 원본영상에 사다리꼴 다각형 형태로 표현.    
		vector<Point>pts;
		for (auto pt : src_pts) {
			pts.push_back(Point(pt.x, pt.y));  // float -> int로 바뀜
		}
		polylines(src, pts, true, Scalar(0, 0, 255), 2, LINE_AA);
#endif
		imshow("src", src);
		imshow("dst", dst);
		if(waitKey(10)==27)break;
	}

}

 

 

+ Recent posts