2018-03-21 16:53:17

▶ 영상 재매핑 처리


오늘은 영상의 화소를 옮겨 영상의 모습을 변경하는 방법에 대해 공부해보자. 화소의 값을 바꾸지 않고 화소의 위치를 새로운 위치에 재매핑 할 것이다. 두 가지를 해볼 것인데 하나는 영상에 물결 효과를 넣는 것이고, 또 다른 하나는 영상을 좌우반전시키는 것이다. 


1. 원본 영상 읽기 및 띄우기

2. 원본 영상에 물결 효과 넣기 - cv::remap 함수

3. 원본 영상을 좌우반전시키기 - cv::remap 함수


코드는 아래와 같다. 


#include <iostream>

#include <opencv2\core.hpp>

#include <opencv2\highgui.hpp>

#include <opencv2\imgproc.hpp> //cv::remap 함수 사용을 위해 필요


void wave(const cv::Mat &image, cv::Mat &result); // 물결 효과 함수

void flip(const cv::Mat &image, cv::Mat &result); // 좌우반전 함수


int main()

{

cv::Mat before = cv::imread("cathedral.jpg");

cv::imshow("before", before);

cv::Mat after1;

after1.create(before.rows, before.cols, before.type());


wave(before, after1); // 물결 효과

cv::imshow("after1", after1);


cv::Mat after2;

after2.create(before.rows, before.cols, before.type());


flip(before, after2); // 좌우반전

cv::imshow("after2", after2);


cv::waitKey(0);

    return 0;

}


void wave(const cv::Mat &image, cv::Mat &result) // 물결 효과 함수

{

// 맵 역할

cv::Mat srcX(image.rows, image.cols, CV_32F);

cv::Mat srcY(image.rows, image.cols, CV_32F);


  // 매핑 생성

for (int i = 0; i < image.rows; i++)

{

for (int j = 0; j < image.cols; j++)

{

// 화소 (i, j)의 새로운 위치

srcX.at<float>(i, j) = j; // 열은 그대로 유지

srcY.at<float>(i, j) = i + 10 * sin(j / 10.0); // 원래 있는 i행의 화소를 바로 사인 곡선에 따라 옮김

}

}


// 매핑 적용

cv::remap(image, result, srcX, srcY, cv::INTER_LINEAR);


}


void flip(const cv::Mat &image, cv::Mat &result) // 좌우 반전 함수

{

// 맵 역할

cv::Mat srcX(image.rows, image.cols, CV_32F);

cv::Mat srcY(image.rows, image.cols, CV_32F);


  // 매핑 생성

for (int i = 0; i < image.rows; i++)

{

for (int j = 0; j < image.cols; j++)

{

// 화소 (i, j)의 새로운 위치

srcX.at<float>(i, j) = image.cols-j-1; // 열은 좌우가 바뀜 ex) 1번째 열은 마지막 열로, 2번째 열은 (마지막-1)열로,..., 마지막 열은 1번째 열로.

srcY.at<float>(i, j) = i; // 행은 그대로 유지

}

}


// 매핑 적용

cv::remap(image, result, srcX, srcY, cv::INTER_LINEAR);

}



원본 이미지와 결과 이미지들을 살펴보자. 원본 이미지, 물결 효과가 첨가된 이미지, 좌우반전된 이미지 순이다. 




▶ 좀 더 알고 넘어갈 것들


1) cv::remap 함수


cv::remap 함수를 사용하기 위해서는 재매핑처리를 위한 x맵과 y맵을 먼저 정의해야 한다. (참고로 cv::remap 함수를 사용하기 위해서는 


#include <opencv2\imgproc.hpp>


가 헤더 부분에 선언되어 있어야 한다.) 물결 효과보다는 좌우반전의 경우가 좀 더 간단하니 좌우반전의 코드 부분으로 재매핑처리에 대해서 이해해보자. 


// 맵 역할

cv::Mat srcX(image.rows, image.cols, CV_32F);

cv::Mat srcY(image.rows, image.cols, CV_32F);


// 매핑 생성

for (int i = 0; i < image.rows; i++)

{

for (int j = 0; j < image.cols; j++)

{

// 화소 (i, j)의 새로운 위치

srcX.at<float>(i, j) = image.cols-j-1; // 열은 좌우가 바뀜.

srcY.at<float>(i, j) = i; // 행은 그대로 유지

}

}


좌우반전을 위해 행들은 그대로 유지시키고 열들만 변경시켜준다. 첫번째 열은 마지막 열로, 두번째 열은 마지막 하나 전 열로,..., 마지막 열은 첫번째 열로. 


x맵과 y맵을 정의한 후에는 매핑을 적용해준다. 


// 매핑 적용

cv::remap(image, result, srcX, srcY, cv::INTER_LINEAR);


이전 포스팅 http://bskyvision.com/291에서도 영상의 좌우반전을 소개했었다. 이때는 opencv 라이브러리 내의 cv::flip 함수를 사용했었다. 



2) 물결 효과


이미지에 물결 효과를 어떻게 줬는지 좀 더 살펴보자. 


// 매핑 생성

for (int i = 0; i < image.rows; i++)

{

for (int j = 0; j < image.cols; j++)

{

// 화소 (i, j)의 새로운 위치

srcX.at<float>(i, j) = j; // 열은 그대로 유지

srcY.at<float>(i, j) = i + 10 * sin(j / 10.0); // 원래 있는 i행의 화소를 바로 사인 곡선에 따라 옮김

}

}


우선 열들은 그대로 유지한다. 그리고 행들은 사인 함수를 이용해서 j의 값에 따라 위 아래로 변동이 있게 만들어 준다. 현재 행의 위치에서 사인 함수 앞에 곱해져 있는 10픽셀 만큼 위 아래로 왔다갔다 하게 될 것이다. 



<참고자료>

[1] 로버트 라가니에 지음, 이문호 옮김, "OpenCV를 활용한 컴퓨터 비전 프로그래밍 3/e", 에이콘