[OpenCV 13] 영상의 기하학적 변환

Sinaenjuni·2023년 7월 9일
0

OpenCV

목록 보기
13/25

영상의 기하학적 변환(Geometric transformation)

영상을 구선하는 픽셀의 구조를 변경함으로써 전체 영상의 모양을 바꾸는 작업이다. 전처리, 영상 접합, 왜곡 제거 등이 이에 해당한다.

영상의 변환 방법은 크게 두 가지로 나눠진다. 첫 번째로 원본 영상의 직선 형태를 변환 후에도 직선으로 유지하는 형태의 변환이다. 두 번째는 원본 영상의 직선 형태를 곡선 형태로 변환하는 혹은 그 반대인 변환 방법들이 존재한다.

이동 변환(Translation trnaform)

이동 변환은 영상의 가로 또는 세로 방향으로 영상을 특정 크기만큼 이동시키는 변환 방법이다.

좌표를 이용하여 식으로 표현하면 다음과 같다.

{x=x+ay=y+b\begin{cases} x`=x+a\\ y`=y+b \end{cases} \\

원점이 (0,0)에서 (a,b)로 이동했기 때문에 영상 전체의 픽셀이 마찬가지로 원래 위치에서 (a, b)만큼 증가하는 형태가 되어야 한다.

[xy]=[1001][xy]+[ab]\begin{bmatrix} x`\\ y` \end{bmatrix} =\begin{bmatrix} 1 & 0\\ 0 & 1 \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix} +\begin{bmatrix} a\\ b \end{bmatrix} \\

위와 같이 행렬로 표현이 가능하며

[xy]=[10a01b][xy1]\begin{bmatrix} x`\\ y` \end{bmatrix} =\begin{bmatrix} 1 & 0 & a\\ 0 & 1 & b \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

행렬 연산 과정에서 덧셈이 포함되는 경우 식의 전개가 복잡해진다. 때문에 위 식처럼 덧셈 부분을 업애기 위해 곱셈 부분에 하나의 차원을 늘릴 수 있다. 이러한 좌표계를 동차좌표계(Homogeneous Coordinate)라고 한다.

void ex_translate(){
    Mat src = imread("../data/lenna.bmp", IMREAD_GRAYSCALE);

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

	Mat dst = Mat::zeros(src.size(), CV_8UC1);

	for (int y = 0; y < src.rows; y++) {
		for (int x = 0; x < src.cols; x++) {
			int x_ = x + 100;
			int y_ = y + 100;
			if (x_ < 0 || x_ >= dst.cols) continue;
			if (y_ < 0 || y_ >= dst.rows) continue;
			dst.at<uchar>(y_, x_) = src.at<uchar>(y, x);
		}
	}
	imshow("src", src);
	imshow("dst", dst);
	waitKey();

}
void ex_processing(){
    Mat src = imread("../data/lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return ;
	}
	float data[] = {1, 0, 100, 0, 1, 100};
	Mat affine = Mat(2,3, CV_32FC1, data);
	// or
	// Mat affine = (Mat_<float>(2, 3) << 1, 0, 100, 0, 1, 100);

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


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

}


전단 변환(Shear transform)

층밀림 변환이라고 하며, 직사각형 형태의 영상 데이터를 한쪽 방햫 혹은 양쪽 방향을 밀어서 평행사변형 모양으로 변환하는 방법이다.

[xy]=[1my0mx10][xy1]\begin{bmatrix} x`\\ y` \end{bmatrix} =\begin{bmatrix} 1 & m_{y} & 0\\ m_{x} & 1 & 0 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}
void ex_processing(){
    Mat src = imread("../data/lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return ;
	}
    
	Mat dst(src.rows * 3 / 2, src.cols, src.type(), Scalar(0));

	double m = 0.5;
	for (int y = 0; y < src.rows; y++) {
		for (int x = 0; x < src.cols; x++) {
			int nx = x;
			int ny = int(y + m*x);
			dst.at<uchar>(ny, nx) = src.at<uchar>(y, x);
		}
	}

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

	waitKey();

}
void ex_processing(){
    Mat src = imread("../data/lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return ;
	}
	float data[] = {1, 0.5, 0, 0, 1, 0};
	Mat affine = Mat(2,3, CV_32FC1, data);
	// or
	// Mat affine = (Mat_<float>(2, 3) << 1, 0.5, 0, 0, 1, 0);

	Mat dst;
	warpAffine(src, dst, affine, Size(src.cols * 3 / 2, src.rows));

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

	waitKey();

}


void ex_processing(){
    Mat src = imread("../data/lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return ;
	}
    
	float data[] = {1, 0.5, 0,
                    0.5, 1, 0};
	Mat affine = Mat(2,3, CV_32FC1, data);

	Mat dst;
	warpAffine(src, dst, affine, Size(src.cols * 1.5, src.rows * 1.5));

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

	waitKey();

}

크기 변환(Scale transform)

영상의 크기를 변환하는 방법이다. 스케일 비율에 따라 크기를 조절한다.

[xy]=[sx000sy0][xy1]\begin{bmatrix} x`\\ y` \end{bmatrix} =\begin{bmatrix} s_{x} & 0 & 0\\ 0 & s_{y} & 0 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

순방향 맵핑(Forward mapping)

for loop를 이용한 이미지 확대 방법을 이용해서 영상의 크기를 2배 확대하는 경우 빈 공간이 생기는것을 확인할 수 있다. 입력 영상의 좌표를 2배하여 출력 영상의 값으로 사용하기 때문이다.

{x=sxxy=syy\begin{cases} x`=s_{x} x\\ y`=s_{y} y \end{cases}
void ex_processing(){
    Mat src = imread("../data/lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return ;
	}
    
	Mat dst = Mat::zeros(src.rows * 2, src.cols * 2, CV_8UC1);

	for (int y = 0; y < src.rows; y++) {
		for (int x = 0; x < src.cols; x++) {
			int x_ = x * 2;
			int y_ = y * 2;

			dst.at<uchar>(y_, x_) = src.at<uchar>(y, x);
		}
	}

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

}


역방향 맵핑(Backward mapping)

역방향 맵핑을 통해 순방향 맵핑에서 나타나는 문제를 해결할 수 있다. 출력 영상의 좌표를 기준으로 스케일 팩터(s)를 나눈 원본 영상의 위치의 픽셀값을 가져온다. 하지만 역방향 맵핑의 경우 원본 영상에서 중복된 픽셀을 가져오기 때문에 계산 현상이 심한것을 확인할 수 있다.

{x=x/sxy=y/sy\begin{cases} x= x`/s_{x}\\ y=y`/s_{y} \end{cases}
void ex_processing(){
    Mat src = imread("../data/camera.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return ;
	}
    
	Mat dst = Mat::zeros(src.rows * 2, src.cols * 2, src.type());

	for (int y_ = 0; y_ < dst.rows; y_++) {
		for (int x_ = 0; x_ < dst.cols; x_++) {
			int x = x_ / 2;
			int y = y_ / 2;
			dst.at<uchar>(y_, x_) = src.at<uchar>(y, x);
		}
	}

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

보간법

\begin{align*} x & =( 1-p) a+pb\\ y & =( 1-p) d+pc\\ z & =( 1-p) y+px\\ z & = ( 1-q)[( 1-p) d+pc] +q[( 1-p) a+pb]\\ & = ( 1-p)( 1-q) d+( 1-q) pc+( 1-p) qa+pqb \end{align*}


void resizeBilinear(const Mat& src, Mat& dst, Size size)
{
	dst.create(size.height, size.width, CV_8U);

	int x1, y1, x2, y2;	double rx, ry, p, q, value;
	double sx = static_cast<double>(src.cols - 1) / (dst.cols - 1);
	double sy = static_cast<double>(src.rows - 1) / (dst.rows - 1);

	for (int y = 0; y < dst.rows; y++) {
		for (int x = 0; x < dst.cols; x++) {
			rx = sx * x;			ry = sy * y;
			x1 = cvFloor(rx);		y1 = cvFloor(ry);
			x2 = x1 + 1; if (x2 == src.cols) x2 = src.cols - 1;
			y2 = y1 + 1; if (y2 == src.rows) y2 = src.rows - 1;
			p = rx - x1;			q = ry - y1;

			value = (1. - p) * (1. - q) * src.at<uchar>(y1, x1)
				+ p * (1. - q) * src.at<uchar>(y1, x2)
				+ (1. - p) * q * src.at<uchar>(y2, x1)
				+ p * q * src.at<uchar>(y2, x2);

			dst.at<uchar>(y, x) = static_cast<uchar>(value + .5);
		}
	}
}
    Mat src = imread("../data/camera.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return ;
	}
    
	Mat dst;
	resizeBilinear(src, dst, Size(1200, 1200));

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

resize() 함수를 이용한 사이즈 변환

void ex_processing(){
    Mat src = imread("../data/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));
	resize(src, dst3, Size(1920, 1280), 0, 0, INTER_CUBIC);
	resize(src, dst4, Size(1920, 1280), 0, 0, INTER_LANCZOS4);

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

}

영상 데이터 축소 시 주의할 점

영상을 축소하는 것은 확대하는 것보다 단순하게 생각할 수 있지만 원본 영상을 급격하게 축소시키는 경우 열화 현상에 의해 화질이 나빠질 수 있다. 원본 영상에 blur 처리를 한 후 영상을 축소하거나, INTER_AREA 라는 옵션을 이용하면 된다.

회전 변환(Rotation transform)

영상을 특정 각도만큼 회전시키는 변환 방법이다. OpenCV에서는 기본적으로 반시계 방향을 정방향으로 정의한다.

정방향 맵핑(Forward mapping)

{x=cosθx+sinθyy=sinθx+cosθy\begin{cases} x`=cos\theta \cdotp x +sin\theta \cdot y\\ y`=-sin\theta \cdot x+cos\theta \cdot y \end{cases}
[xy]=[cosθsinθsinθcosθ][xy]+[00]\begin{bmatrix} x`\\ y` \end{bmatrix} =\begin{bmatrix} cos\theta & sin\theta \\ -sin\theta & cos\theta \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix} +\begin{bmatrix} 0\\ 0 \end{bmatrix}
[xy]=[cosθsinθ0sinθcosθ0][xy1]\begin{bmatrix} x`\\ y` \end{bmatrix} =\begin{bmatrix} cos\theta & sin\theta & 0\\ -sin\theta & cos\theta & 0 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

역방향 맵핑(Backward mapping)

\begin{align*} \begin{bmatrix} x`\\ y` \end{bmatrix} & =\begin{bmatrix} cos\theta & sin\theta \\ -sin\theta & cos\theta \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix}\\ \begin{bmatrix} x\\ y \end{bmatrix} & =\begin{bmatrix} cos\theta & sin\theta \\ -sin\theta & cos\theta \end{bmatrix}^{-1}\begin{bmatrix} x`\\ y` \end{bmatrix}\\ & =\begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix}\begin{bmatrix} x`\\ y` \end{bmatrix} \end{align*}

getRotationMatrix2D() 함수

2x3 크기의 affine matrix를 생성하는 함수이다. center는 회전에 중심이 되는 원본 영상의 중점을 의미한다(e.g. src.cols/2.f, src/rosw/2.f). angle은 회전할 각도를 입력하며 degree를 기준으로 한다.

영상을 그대로 회전 변환하면 중심축이 좌측 상단이기 때문에 기준점을 중심으로 옮겨 주어야 한다.

\begin{align*} \begin{bmatrix} x`\\ y`\\ 1 \end{bmatrix} & =\begin{bmatrix} 1 & 0 & c_{x}\\ 0 & 1 & c_{y}\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} \alpha & \beta & 0\\ -\beta & \alpha & 0\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} 1 & 0 & -c_{x}\\ 0 & 1 & -c_{y}\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} \ \begin{cases} \alpha =cos\theta \\ \beta =sin\theta \end{cases}\\ & =\begin{bmatrix} \alpha & \beta & c_{x}\\ -\beta & \alpha & c_{y}\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} 1 & 0 & -c_{x}\\ 0 & 1 & -c_{y}\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} \ \\ & =\begin{bmatrix} \alpha & \beta & -\alpha c_{x} -\beta c_{y} +c_{x}\\ -\beta & \alpha & \beta c_{x} -\alpha c_{y} +c_{y}\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} \ \end{align*}
center: 회전 중심 좌표
angle: 회전 각도(반시계 방향)
scale: 회전 후 확대 비율
Mat getRotationMatrix2D(Point2f center, double angle, double scale);

warpAffine() 함수

affine matrix를 원본 영상에 적용해서 반환하는 함수이다.

void warpAffine( InputArray src, OutputArray dst,
                              InputArray M, Size dsize,
                              int flags = INTER_LINEAR,
                              int borderMode = BORDER_CONSTANT,
                              const Scalar& borderValue = Scalar());
src: input image.
dst: output image that has the size dsize and the same type as src.
M: [2 x 3] transformation matrix. 
dsize: size of the output image.
flags: 보간법을 선택
//! interpolation algorithm
enum InterpolationFlags{
    /** nearest neighbor interpolation */
    INTER_NEAREST        = 0,
    /** bilinear interpolation */
    INTER_LINEAR         = 1,
    /** bicubic interpolation */
    INTER_CUBIC          = 2,
    /** resampling using pixel area relation. It may be a preferred method for image decimation, as
    it gives moire'-free results. But when the image is zoomed, it is similar to the INTER_NEAREST
    method. */
    INTER_AREA           = 3,
    /** Lanczos interpolation over 8x8 neighborhood */
    INTER_LANCZOS4       = 4,
    /** Bit exact bilinear interpolation */
    INTER_LINEAR_EXACT = 5,
    /** Bit exact nearest neighbor interpolation. This will produce same results as
    the nearest neighbor method in PIL, scikit-image or Matlab. */
    INTER_NEAREST_EXACT  = 6,
    /** mask for interpolation codes */
    INTER_MAX            = 7,
    /** flag, fills all of the destination image pixels. If some of them correspond to outliers in the
    source image, they are set to zero */
    WARP_FILL_OUTLIERS   = 8,
    /** flag, inverse transformation

    For example, #linearPolar or #logPolar transforms:
    - flag is __not__ set: \f$dst( \rho , \phi ) = src(x,y)\f$
    - flag is set: \f$dst(x,y) = src( \rho , \phi )\f$
    */
    WARP_INVERSE_MAP     = 16
};
borderMode: pixel extrapolation method (see #BorderTypes); when borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image corresponding to the "outliers" in the source image are not modified by the function.
//! Various border types, image boundaries are denoted with `|`
//! @see borderInterpolate, copyMakeBorder
enum BorderTypes {
    BORDER_CONSTANT    = 0, //!< `iiiiii|abcdefgh|iiiiiii`  with some specified `i`
    BORDER_REPLICATE   = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
    BORDER_REFLECT     = 2, //!< `fedcba|abcdefgh|hgfedcb`
    BORDER_WRAP        = 3, //!< `cdefgh|abcdefgh|abcdefg`
    BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
    BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno`

    BORDER_REFLECT101  = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
    BORDER_DEFAULT     = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
    BORDER_ISOLATED    = 16 //!< do not look outside of ROI
};
borderValue: value used in case of a constant border; by default, it is 0.

Example code


void on_rotate(int pos, void* data)
{
	Mat src = *(Mat*)data;

	float degree = (float)pos;
	Point2f pt(src.cols / 2.f, src.rows / 2.f);
	Mat rot = getRotationMatrix2D(pt, degree, 1.0);

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

	imshow("dst", dst);
}

void ex_processing(){
    Mat src = imread("../data/rose.bmp");

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

	namedWindow("dst");
	createTrackbar("angle", "dst", 0, 360, on_rotate, (void*)&src);
	on_rotate(0, (void*)&src);

	waitKey();

}


기하학적 변환

void flip(InputArray src, OutputArray dst, int flipCode);
src: input array.
dst: output array of the same size and type as src.
flipCode: a flag to specify how to flip the array; 0 means
flipping around the x-axis and positive value (for example, 1) means
flipping around y-axis. Negative value (for example, -1) means flipping around both axes.
1: 좌우 대칭
0: 상하 대칭
-1: 좌우 상하 대칭
void ex_processing(){
    Mat src = imread("../data/rose.bmp"), dst;

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

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

}


어파인 변환(Affine transform)

Affine transform 같은 경우 평행사변형 형태로 변환하기 때문에 3개의 점의 변환 정보만 알아도 나머지 1개는 3개의 점으로 구할 수 있다. 빨간색 점을 좌표 편면상의 한 점의 이동이라고 봤을 때, x와 y에 대한 두 식으로 표현이 가능하다. 나머지 두 점 또한 4개의 식으로 표현이 가능하기 때문에 총 6개의 식으로 위 변환을 정의할 수 있다.

투시 변환(Perspective tranform)

Perspectiva transform은 8개의 방정식으로 정의가 가능하며, 이를 통한 affine matrix를 구할 수 있다.

[wxwyw]=[abcdefghi][xy1]  \begin{aligned} \begin{bmatrix} wx`\\ wy`\\ w \end{bmatrix} & =\begin{bmatrix} a & b & c\\ d & e & f\\ g & h & i \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} \ \ \end{aligned}
{x=ax+by+cgx+hy+iy=dx+ey+fgx+hy+i\begin{cases} x` & =\frac{ax+by+c}{gx+hy+i}\\ y` & =\frac{dx+ey+f}{gx+hy+i} \end{cases}
{x=ax+by+cgxxhxyy=dx+ey+fgxyyy\begin{cases} x` & =ax+by+c-gxx`-hx`y\\ y` & =dx+ey+f-gxy`-yy` \end{cases}
{x1=ax1+by1+cgx1x1hx1y1y1=dx1+ey1+fgx1y1hy1y1\begin{cases} x_{1}^{`} & =ax_{1} +by_{1} +c & & -gx_{1} x_{1}^{`} -hx_{1} y_{1}^{`}\\ y_{1}^{`} & = & dx_{1} +ey_{1} +f & -gx_{1} y_{1}^{`} -hy_{1} y_{1}^{`} \end{cases}
[x1y1x2y2x3y3x4y4]=[x1y11000x1x1x1y1000x1y11x1y1y1y1x2y21000x2x2x2y2000x2y21x2y2y2y2x3y31000x3x3x3y3000x3y31x3y3y3y3x4y41000x4x4x4y4000x4y41x4y4y4y4][abcdefgh]\begin{bmatrix} x_{1}^{`}\\ y_{1}^{`}\\ x_{2}^{`}\\ y_{2}^{`}\\ x_{3}^{`}\\ y_{3}^{`}\\ x_{4}^{`}\\ y_{4}^{`} \end{bmatrix} =\begin{bmatrix} x_{1} & y_{1} & 1 & 0 & 0 & 0 & -x_{1} x_{1}^{`} & -x_{1}^{`} y_{1}\\ 0 & 0 & 0 & x_{1} & y_{1} & 1 & -x_{1} y_{1}^{`} & -y_{1} y_{1}^{`}\\ x_{2} & y_{2} & 1 & 0 & 0 & 0 & -x_{2} x_{2}^{`} & -x_{2}^{`} y_{2}\\ 0 & 0 & 0 & x_{2} & y_{2} & 1 & -x_{2} y_{2}^{`} & -y_{2} y_{2}^{`}\\ x_{3} & y_{3} & 1 & 0 & 0 & 0 & -x_{3} x_{3}^{`} & -x_{3}^{`} y_{3}\\ 0 & 0 & 0 & x_{3} & y_{3} & 1 & -x_{3} y_{3}^{`} & -y_{3} y_{3}^{`}\\ x_{4} & y_{4} & 1 & 0 & 0 & 0 & -x_{4} x_{4}^{`} & -x_{4}^{`} y_{4}\\ 0 & 0 & 0 & x_{4} & y_{4} & 1 & -x_{4} y_{4}^{`} & -y_{4} y_{4}^{`} \end{bmatrix}\begin{bmatrix} a\\ b\\ c\\ d\\ e\\ f\\ g\\ h \end{bmatrix}
Mat getPerspectiveTransform(InputArray src, InputArray dst, int solveMethod = DECOMP_LU);
src: Coordinates of quadrangle vertices in the source image.
dst: Coordinates of the corresponding quadrangle vertices in the destination image.
solveMethod: method passed to cv::solve (#DecompTypes)
void warpPerspective( InputArray src, OutputArray dst,
                                   InputArray M, Size dsize,
                                   int flags = INTER_LINEAR,
                                   int borderMode = BORDER_CONSTANT,
                                   const Scalar& borderValue = Scalar());
src: input image.
dst: output image that has the size dsize and the same type as src .
M: \f$3\times 3\f$ transformation matrix.
dsize: size of the output image.
flags: combination of interpolation methods (#INTER_LINEAR or #INTER_NEAREST) and the
optional flag #WARP_INVERSE_MAP, that sets M as the inverse transformation (
\f$\texttt{dst}\rightarrow\texttt{src}\f$ ).
borderMode: pixel extrapolation method (#BORDER_CONSTANT or #BORDER_REPLICATE).
borderValue: value used in case of a constant border; by default, it equals 0.
void ex_processing(){
    VideoCapture cap("../data/test_video.mp4");

	if (!cap.isOpened()) {
		cerr << "Video load failed!" << endl;
		return ;
	}

    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);

		Mat dst;
		warpPerspective(src, dst, per_mat, Size(w, h));

		vector<Point> pts;
		for (auto pt : src_pts) {
			pts.push_back(Point(pt.x, pt.y));
		}
		polylines(src, pts, true, Scalar(0, 0, 255), 2, LINE_AA);
        
        imshow("src", src);
        imshow("dst", dst);

        if (waitKey(1) == 27)
            break;

    }
}


리맵핑(Remapping)

영상의 특정 위치 픽셀을 다른 위치에 재배치하는 일반적인 방법이다.

{x: inputx: output{x=x+200y=y+100{x=x200y=y100\begin{cases} x & :\ input\\ x` & :\ output \end{cases} \Longrightarrow \begin{cases} x` & =x+200\\ y` & =y+100 \end{cases} \Longrightarrow \begin{cases} x & =x`-200\\ y & =y`-100 \end{cases}
이동 변환: {mapx(x,y)=x200mapy(x,y)=y100상하 대칭: {mapx(x,y)=xmapy(x,y)=h1y크기 변환: {mapx(x,y)=x/2mapy(x,y)=y/2\begin{array}{l} 이동\ 변환:\ \begin{cases} map_{x}( x,y) & =x-200\\ map_{y}( x,y) & =y-100 \end{cases}\\ \\ 상하\ 대칭:\ \begin{cases} map_{x}( x,y) & =x\\ map_{y}( x,y) & =h-1-y \end{cases}\\ \\ 크기\ 변환:\ \begin{cases} map_{x}( x,y) & =x/2\\ map_{y}( x,y) & =y/2 \end{cases} \end{array}
void ex_processing(){
    Mat src = imread("../data/lenna.bmp");

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

	int w = src.cols;
	int h = src.rows;

	Mat map1 = Mat::zeros(h*2, w*2, CV_32FC1);
	Mat map2 = Mat::zeros(h*2, w*2, CV_32FC1);

	for (int y = 0; y < h*2; y++) {
		for (int x = 0; x < w*2; x++) {
			map1.at<float>(y, x) = (float)x/2;
			map2.at<float>(y, x) = (float)y;
			//map2.at<float>(y, x) = (float)h - 1 - y;
			//map2.at<float>(y, x) = (float)y/2;
		}
	}

	Mat dst;
	remap(src, dst, map1, map2, INTER_LINEAR);
	//remap(src, dst, map1, map2, INTER_LINEAR, BORDER_DEFAULT);

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

0개의 댓글