크기 변환 ( scale transform) : 영상 크기를 원본 영상보다 크거나 작게 만드는 변환

x축과 y축 방향으로의 스케일 비율(scale factor) 지정

 

x,y : 원래 x,y 좌표

x': 이동된 x좌표

 y' : 이동된 y좌표

x' = s_x   * x   ( s_x ==  w' / w )

y' = s_y  *  y    ( s_y == h' / h )    

 

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

using namespace std;
using namespace cv;

void resize1() {
	Mat src = imread("camera.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image laod failed!" << endl;
		return;
	}
	Mat dst = Mat::zeros(src.rows * 2, src.cols * 2, CV_8UC1); // ==  	Mat dst(src.rows * 2, src.cols * 2, CV_8UC1, Scalar(0));  // 2배 증가

	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); //순방향 매핑(forward mapping)  ,  입력영상 좌표값을 하나하나씩  출력영상에 설정. 그러나 문제는 입력영상만 채워지고  나머지 부분(크기)들이 채워지지 못할 수 있다.
		}
	}
	imshow("src", src);
	imshow("dst", dst);
	waitKey();
}

//resize1 문제 보완
void resize2() {
	Mat src = imread("camera.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image laod failed!" << endl;
		return;
	}
	//Mat dst = Mat::zeros(src.rows * 2, src.cols * 2, CV_8UC1); // ==  	Mat dst(src.rows * 2, src.cols * 2, CV_8UC1, Scalar(0));  // 2배 증가
	Mat dst = Mat::zeros(src.rows * 4, src.cols * 4, CV_8UC1); 
	//영상4배 확대 시 결과를 보면 픽셀값들이 계단 형태로 보인다. 픽셀 하나의 크기가 정사각형 크기로 확대되어 영상이 각진 영상으로 나온다. (저화질)
	// x_  , y_ 가 4의 배수가 될 때까진  x, y는 그 전의 4의 배수값으로 통일 된다.     x=  x_ / 4     y  =  y_/4  
	//이에 보간법을 통해 이 문제를 해결 할 수 있다.
	
	for (int y_ = 0; y_ < dst.rows; ++y_) {
		for (int x_ = 0; x_ < dst.cols; ++x_) {
			//int x = x_ / 2;
			//int y = y_ / 2;
			int x = x_ / 4;
			int y = y_ / 4;  
			
			//dst.at<uchar>(y_, x_) = src.at<uchar>(y, x); //순방향 매핑(forward mapping)  ,  입력영상 좌표값을 하나하나씩  출력영상에 설정. 그러나 문제는 입력영상만 채워지고  나머지 부분(크기)들이 채워지지 못할 수 있다.

			// 순방향 매핑 보단 역방향 매핑(backward mapping) 을 활용해 계산해야.
			/*
				x' = s_x * x
				y' = s_Y * y  가 아닌

				x = x'/s_x
				y = y'/s_y   결과 영상 좌표( x' , y') 에서 원본 영상의 좌표(x,y)를 참조하는 걸로 설정한다.

				dst.at<uchar>(y_, x_) = <src 영상의 픽셀 값 참조>
			*/
			dst.at<uchar>(y_, x_) = src.at<uchar>(y, x);

		}
	}

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


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 ,ry 보다 작은 부분 x1, y1 으로   큰 부분은 x2,y2 로 설정
			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;

			// z= (1-p)(1-q)a + p(1-q)b+ (1-p)qc + pqd 
			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);  //실수값을 반올림 하여  지정
		}
	}

}
void resize3() {  //보간법
	/*
	보간법 (interpolation): 역방향 매핑에 의한 크기 변환 시 , 참조해야할 입력영상의 (x,y)  좌표가 실수 일 경우 사용. 실수 좌표 상에서의 픽셀 값을 결정하기 위해 주변 픽셀 값을 이용해 값을 추정하는 방법이다.
	
	*/
	Mat src = imread("camera.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image laod failed!" << endl;
		return;
	}
	/* 
	1, 최근방 이웃 보간법( nearest neighbor interpolation) 
	1)  가장 가까운 위치에 있는 픽셀 값 참조
	2) 소수점 슬라이싱  (50.2, 32.8) -> (50, 33)
	장점 빠르고 구현이 쉬우나 단점은 계단현상(블록현상)
	
	2, 양선형 보간법(bilinear interpolation)
	 1) 실수 좌표를 둘러싸고 있는 네 개의 픽셀값에 가중치를 곱한 값들의 선형 합으로 결과 영상의 픽셀 값 구하는 방법.
	 2) 최근방 이웃보간법에 비해 느린편이나, 비교적 빠르고 계단 현상이 크게 감소,.
	*/
	Mat dst;
	//resizeBilinear(src,dst,Size(600,300)); 
	resizeBilinear(src, dst, Size(1024,1024));  //계단현상 완화됨.
	imshow("src", src);
	imshow("dst", dst);
	waitKey();
}

void resize4() {
	Mat src = imread("rose.bmp");  // 480 x 320 pix

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

	Mat dst1, dst2, dst3, dst4;
	resize(src, dst1, Size(), 4, 4, INTER_NEAREST); //결과 영상 단위를 픽셀 단위로 지정한게 아닌 , 가로 4배 세로 4배 만큼 확대
	resize(src, dst2, Size(1920, 1280));   // (480x320) * 4  == 1920x1280   로 dst1과  dst2의 사이즈는 같다. 4번째 인자는 default로 자동으로  INTER_LINEAR방식(양선형 보간법) 사용함.
	resize(src, dst3, Size(1920, 1280), 0, 0, INTER_CUBIC);
	resize(src, dst4, Size(1920, 1280), 0, 0, INTER_LANCZOS4);  // 입력영상에서 실수좌표에 주변 64개의 픽셀값 이용

	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();
} 
int main()
{
	//resize1();
	//resize2();
	//resize3();
	resize4();

	/*
	입력영상 축소 시 고려할 사항
	- 영상 확대 시 역방향 맵핑을 하거나 어떤 보간법을 사용할지 고민해야 할 것이다.
	 축소는 단순 형태로 생각할 수 있다. 한 픽셀로 구성된 선분들은 영상을 축소할 때 사라지는 경우가 발생할 수 있기에 이를 방지하고자 입력영상을 부드럽게 필터링 한 후 축소하거나 다단계축소를 하는게 좋다.
	 OpenCV의 resize() 함수에서 INTER_AREA 플래그를 사용한다,.   INTER_LINEAR 를 영상 축소에 사용시 특정 부분들 사라지는 경우 발생
	*/
}

+ Recent posts