크기 변환 ( 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 를 영상 축소에 사용시 특정 부분들 사라지는 경우 발생
*/
}
'프로그래머스 > OPENCV' 카테고리의 다른 글
어파인 변환과 투시 변환 ( with bird eyeview) (0) | 2022.12.05 |
---|---|
회전변환과 기하학적 변환의 조합 [다시] (0) | 2022.12.05 |
영상의 기하학적 변환(geometric transformation) (0) | 2022.12.05 |
1주차 과제) 히스토그램 개선해서 명암비가 좋은 결과 이미지 만들기 (0) | 2022.12.03 |
잡음제거 필터 (GaussianBlur vs BilateralFilter) (0) | 2022.12.02 |