어파인 변환
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;
}
}
'프로그래머스 > OPENCV' 카테고리의 다른 글
색 공간 (0) | 2022.12.06 |
---|---|
리매핑(remapping) (0) | 2022.12.05 |
회전변환과 기하학적 변환의 조합 [다시] (0) | 2022.12.05 |
영상의 크기 변환 (scale transform == resize) 과 보간법 [ 다시] (0) | 2022.12.05 |
영상의 기하학적 변환(geometric transformation) (0) | 2022.12.05 |