본문 바로가기

IT/OPENCV

[OPENCV] 9. 이미지 프로세싱(4) - Warp

반응형

☞ 메인보드 : Jetson Nano Developer Kit

운영 체제 : Ubuntu 18.04 - JetPack 4.4.1

☞ IDE : Visual Studio Code

☞ 언어 : C++

 

 

 

 


목차

1. Warp

2. 코드 작성

3. 실행 결과

 

 

 


 

 

 

 

① Warp

 

영상을 구성하는 직사각형의 꼭짓점 4개를 이용하여 평행 이동, 대칭 이동, 회전 등 모양에 변화를 주는 것을 Geometric Transform(기하학적 변환) 이라 한다. 대표적인 기하학적 변환으로는 Affine Transform(아핀 또는 어파인 변환)과 Perspective Transform(원근 변환)이 있다. 차선을 검출하기 위해 임의의 사각형의 결과 영상을 얻을 수 있는 원근 변환을 사용해보려 한다.

 

getPerspectiveTransform 함수는 2가지 종류로 구성되어 있는데 이는 매개변수 타입을 보면 알 수 있다. 하나는 직교 좌표계의 좌표 값이고 나머지는 행과 열로 이루어진 이미지이다. 직사각형이나 사다리꼴의 형태를 구성할 좌표나 이미지를 입력하면 함수 실행 후 반환되는 원근 변환 행렬 M을 얻을 수 있는데 이 행렬을 warpPerspective 함수에 사용하면 좌표를 위로 들어 올리는 듯한 이미지를 얻을 수 있다.

 

 

 

※ getPerspectiveTransform (원근 변환 행렬 M)

 

// 4개의 점 좌표
Mat cv::getPerspectiveTransform	( const Point2f src[], const Point2f dst[] , int solveMethod = DECOMP_LU )

 

  • const Point2f src[] : 초기 좌표 배열
  • const Point2f dst[] : 결과 좌표 배열

 

 

 

// 이미지
Mat cv::getPerspectiveTransform	( InputArray src, InputArray dst, int solveMethod = DECOMP_LU )

 

  • InputArray src : 원본 이미지
  • InputArray dst : 결과 이미지

 

 

 

 

※ warpPerspective

 

void cv::warpPerspective( InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar & borderValue = Scalar() )

 

  • InputArray M : getPerspectiveTransform 함수로부터 반환된 변환 행렬
  • flags : 보간 방법(기본 값 - INTER_LINEAR)
  • borderMode : 픽셀 외삽 방법(기본 값 - BORDER_CONSTANT)

 

 

곡선을 갖고 있는 차선을 Warp로 들어 올리면 아무런 조치도 취하지 않은 상황보단 선을 검출해내기 쉬워지지만, 이번 포스트에 사용된 이미지처럼 급격한 곡선으로 그리는 경우에는 사용하기 어렵다고 생각한다. 그리고 로봇이 자율주행을 한다고 했을 때 카메라의 위치나 각도 등을 고려해보면 이러한 이미지처럼 보이지는 않는다.

 

실제로 이미지 안의 도로를 자율주행 중인 (목이 긴) 로봇이 30-40의 시야각을 갖고 있는 카메라를 장착하여 아래 방향으로 비추고 얻은 프레임이라고 가정하고, Warp로 비틀리는 효과를 주기 전에 당장 인식을 해야 하는 도로 앞부분을 ROI로써 초기 좌표로 설정하고 시작한다.

 

7번 포스트 Line Detection의 과정에서 필터링과 가장자리 검출 사이에 Warp 과정을 삽입하여 진행한다. 곡선의 형태로 그려질 것을 예상하여 확률적 허프 변환 함수 HoughLinesP와 함께 차선 검출 효과를 나타낼 것이다.

 

 

 

 


② 코드 작성

※ Warp Point 설정

 

cv::Point2f srcPt[4];

// 왼쪽 위
srcPt[0] = cv::Point2f(0,400);

// 왼쪽 아래
srcPt[1] = cv::Point2f(0,586);

// 오른쪽 위
srcPt[2] = cv::Point2f(880,400);

// 오른쪽 아래
srcPt[3] = cv::Point2f(880,586);

 

왼쪽 위 - 왼쪽 아래 - 오른쪽 위 - 오른쪽 아래

 

 

Warp 초기, 결과 좌표를 설정할 때 위와 같은 순서로 해야 한다.  srcPt와 마찬가지로 dstPt 또한 같은 순서로 설정한다.

 

 

※ 소스 코드

 

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>

int main(){
    cv::Mat src;
    src = cv::imread("curve2.jpg", cv::IMREAD_COLOR);
    if(src.empty()){
        std::cout << "Can't Open Image" << std::endl;
        return -1;
    }

    cv::Mat ROI, HSV;
    ROI = src(cv::Rect(0,400,880,186));
    cv::cvtColor(src,HSV,cv::COLOR_BGR2HSV);
    cv::Scalar lower_white = cv::Scalar(0,0,200);
    cv::Scalar upper_white = cv::Scalar(180,50,255);
    cv::inRange(HSV, lower_white, upper_white, HSV);

    cv::Mat sharp, closing;
    cv::bilateralFilter(HSV, sharp, 5, 100, 100);
    cv::morphologyEx(sharp, closing, cv::MORPH_CLOSE, cv::Mat());
    
    cv::Point2f srcPt[4], dstPt[4];
    srcPt[0] = cv::Point2f(0,400);
    srcPt[1] = cv::Point2f(0,586);
    srcPt[2] = cv::Point2f(880,400);
    srcPt[3] = cv::Point2f(880,586);

    dstPt[0] = cv::Point2f(0,0);
    dstPt[1] = cv::Point2f(0,480);
    dstPt[2] = cv::Point2f(600,0);
    dstPt[3] = cv::Point2f(600,480);

    cv::Mat M = cv::getPerspectiveTransform(srcPt, dstPt);

    cv::Mat dst;
    cv::warpPerspective(closing, dst, M, cv::Size(600,480));

    cv::imshow("ROI", ROI);
    cv::imshow("RESULT", dst);
    cv::waitKey(0);

    return 0;

}

 

 


③ 실행 결과

 

 

 

원본 이미지
ROI = srcPt(초기 좌표)
Warp 결과

 

 

Warp 결과에서 오른쪽의 이미지는 기본 소스 코드에서 아래와 같이 dstPt와 warPerspective의 Size 매개변수 값을 변화했을 때 반환되는 이미지이다.

 

dstPt[0] = cv::Point2f(0,0);
dstPt[1] = cv::Point2f(0,600);
dstPt[2] = cv::Point2f(400,0);
dstPt[3] = cv::Point2f(400,600);

cv::warpPerspective(dilate, dst, M, cv::Size(400,600));

 

 

왼쪽 위에 사포로 긁어놓은것 같은 픽셀은 차선과 함께 흰색으로 인식된 픽셀들이다. 흰색 픽셀을 팽창하는 dilate 필터 이전에는 더 적었지만 왼쪽 차선을 살리면서 생겨난 노이즈이다. 그러나 검은 비닐봉지에 구멍 뚫은듯한 픽셀들은 결코 차선으로 인식될 수 없다. 위 결과에서 흰색 픽셀로 검출된 것들은 Hough Transform에 의해 직선인지 아닌지 시험받게 되기 때문이다. 다음 포스트에서는 Hough Transform을 이용하여 Warp로 얻어낸 이미지의 직선을 검출해볼 것이다.

 

 

 

 

 

 

 

 

 

 

반응형