내맘대로 공부기록.

[ C++ ]

[ openCV | C++ ] ( 2 / 6 ) 차선검출. 이미지 전 처리. Perspective Transform (getPerspectiveTransform, warpPerspective 함수 사용법)

fwanggus 2021. 8. 13. 22:32

차선 검출을 위한 이미지 전 처리해보기 🛣


⚙️  기본 설명  ⚙️

 

  1. 왜곡 제거(카메라 보정) 👍
  2. Perspective Transform(원본 이미지 ⏩  2D)
  3. Color Filtering(HLS, LAB color space)
  4. 픽셀 값 정규화(feat. 최대값) 및 이미지 픽셀(HLS 1개, LAB 1개)  합치기.
  5. Window Search
  6. Show Detected Lines and Info.

개념  🧐

인풋으로 사용되는 이미지 형태(또는 영상 데이터)는 보통 기울어져 있거나, 카메라의 위치가 모두 다르기 때문에 차선검출에 사용하기 위한 소스로써 적합하지 않습니다. 그렇기 때문에 날 것(Raw)의 이미지 소스를 용도에 맞게 위치, 사이즈를 변환시키는 등 전처리 작업이 필요할 것 같습니다.

우리 주변에서 찾아볼 수 있는 예로, 명함을 카메라로 촬영하면 이쁜 사각형 틀에 맞춰서 저장해주는 어플이 있죠. 그리고 책 또는 A4용지를 촬영하는 것만으로도 마치 스캔한 것처럼 기울임과 왜곡을 보정해서 파일로 저장해주는 어플도 있는 걸로 알고 있습니다. 

이런 작업을 opencv 함수로도 구현해서 표현 할 수 있습니다.

 

https://arnab.org/blog/so-i-suck-24-automating-card-games-using-opencv-and-python/

 

방법 🛠

 

순서는 다음과 같으며, 각 단계에서 특정 함수를 사용합니다.

 

1. 소스(원본) 이미지와 최종(변화 후)데이터 간의 관계를 설명하는 행렬식을 계산한다.

2. 1에서 구한 행렬식을 이용하여(함수의 인수로 사용됨) 소스 이미지를 원하는 이미지의 크기와 위치로 변환(Warping)한다.


1. 소스(원본) 이미지와 최종(변환 후)데이터 간의 관계를 설명하는 행렬식을 계산

 

getPerspectiveTransform 함수를 사용합니다. 이 함수는 아래 헤더 파일에서 불러올 수 있습니다.

 

#include <opencv2/imgproc.hpp>

 

행렬식 계산에 사용되는 수식은 아래와 같은 관계를 같습니다. 좌변이 최종(변환 후)데이터 포맷이며, 우변이 소스 이미지의 포맷입니다.

 

(공홈)https://docs.opencv.org/master/da/d54/group__imgproc__transform.html#ga20f62aa3235d869c9956436c870893ae

 

🔗  함수와 매개변수 : getPerspectiveTransform(src, dst, solveMethod)

TYPE VARIABLE
InputArray src
InputArray dst
Int solveMethod(option)
Return map_matrix(3 X 3)

 

리턴 값을 변수에 잘 저장해놓고, 다음 단계에서 인수로 사용하게 됩니다. 여기서 소스데이터와 최종 데이터를 반대로 입력해서, 역방향 변환에 사용되는 행렬식을 구해놓을 필요가 있습니다. 최종적으로 전처리 작업을 하고, 이미지를 원래 형태로 되돌리는 과정에서 두 이미지 간의 관계가 필요하기 때문입니다. 

 

2. 1에서 구한 행렬식을 이용하여(함수의 인수로 사용됨) 소스 이미지를 원하는 이미지의 크기와 위치로 변환(Warping)한다.

 

warpPerspective 험수를 사용합니다. 이 함수는 아래 헤더 파일에서 불러올 수 있습니다.

 

#include <opencv2/imgproc.hpp>

 

인풋 이미지를 지정한 사이즈로 변환해주는 함수 입니다. 다음의 수식 관계로 최종 데이터를 생성합니다.

 

 

https://docs.opencv.org/master/da/d54/group__imgproc__transform.html#gaf73673a7e8e18ec6963e3774e6a94b87

 

🔗  함수와 매개변수 : warpPerspective(src, dst, M, dsize, flags, borderMode, borderValue)

TYPE VARIABLE
InputArray src
OutputArray(Return) dst
InputArray M
Size dsize
int flags(default : INTER_LINEAR)
int borderMode(option)
Const Scalar borderValue(option)
Return void(None)

 


차선 검출 이미지 전처리 과정에 사용(Demo)  🚀

1. getPerspectiveTransform 함수

함수의 인수 및 리턴 데이터로 사용할 변수들을 초기화 합니다.

 

	...
    
	Mat Matx;
	Mat imgWarp;
	vector<Point2f> srcRectCoord;
	vector<Point2f> dstRectCoord;

	...

 

다음 변수는 포인트 형식 데이터를 요소로 갖는 벡터 변수를 의미합니다. 

- srcRectCoord : 소스(원본) 이미지에서 함수를 적용할 사각형 좌표

- dstRectCoord : 변환 후 이미지의 크기를 사각형 좌표로 정의

 

소스 및 변환 후 이미지에 대해서, 변환될 부분의 크기를 다음과 같이 정의해주었습니다. 아래 코드에서 소스 이미지는 imgUndistort 입니다. 몇 차례 변환된 이미지를 띄어 보면서, 적당한 위치의 변환 영역을 지정할 필요가 있습니다. 해당 부분은 upX_diff, upY_diff, downX_diff, downY_diff 등의 변수를 추가해서 조정했습니다.

최종 적으로 srcRectCoord, dstRectCoord 벡터 변수에 포인트 데이터 4점씩 초기화해주었습니다.

 

	...
	int width = imgUndistort.cols;
	int height = imgUndistort.rows;

	Point p1s = Point2f(width / 2 - upX_diff, height - upY_diff);
	Point p2s = Point2f(width / 2 + upX_diff, height - upY_diff);
	Point p3s = Point2f(downX_diff, height - downY_diff);
	Point p4s = Point2f(width - downX_diff, height - downY_diff);

	Point p1d = Point2f(dstX, 0);
	Point p2d = Point2f(width - dstX, 0);
	Point p3d = Point2f(dstX, height);
	Point p4d = Point2f(width - dstX, height);

	srcRectCoord = {p1s, p2s, p3s, p4s};
	dstRectCoord = {p1d, p2d, p3d, p4d};
	...

 

변환할 이미지의 크기를  머릿속으로만 생각하면서 진행하기는 어려운 부분이 있어 원본 이미지에 사다리꼴 꼭짓점을 마킹해가면서 진행했습니다. 

 

원본이미지에 변환영역을 마킹

 

두 이미지 사이의 행렬식을 getPerspectiveTransform 함수로 계산할 수 있습니다. 사실 두 사각형(마킹된 사다리꼴 꼭짓점 좌표와 변환 후 사각형의 꼭짓점) 간의 관계가 맞는 말이겠네요. 아직 실제 이미지를 인수로 사용한 부분은 없거든요. 단지 이미지의 변환 영역 좌표만 사용하고 있습니다. 

 

    Matx = getPerspectiveTransform(srcRectCoord, dstRectCoord);

 

srcRectCoord 데이터는 원본 이미지에서 한 번 지정 후 변경할 일이 거의 없었습니다. 하지만, dstRectCoord 의 값은 처음에 대략적으로 잡아두고, warpPerspective 함수를 적용해서 이미지를 띄어보고(imshow함수) 필요에 따라 조정할 필요가 있습니다.

여기까지 해본 결과, 전처리는 전처리를 위한 준비작업이 꽤나 수고스럽다는 점이 느껴집니다.

 

2. warpPerspective 함수

 

warpPerspective 함수는 위에서 준비한 데이터를 적용시키기만 하면 됩니다. 하지만 위에서 언급한 대로, 변환 후 사각형 꼭짓점 데이터는 적절히 조정해줍시다.

 

    ...
    warpPerspective(imgUndistort, imgWarp, Matx, Size(width, height), INTER_LINEAR);
    ...

 

변환된 이미지는 imgWarp이며, 해당 변수는 가장 윗부분에서 초기화해주었습니다. 각 함수의 인수 내용은 공홈 및 함수와 매개변수 설명 부분을 참고해주세요.

 

좌 : 원본이미지, 우 : 변환 후 이미지(bird view)

 

위 오른쪽 그림처럼, bird view(top view) 형태로 변환된 이미지를 확인할 수 있습니다. 한 번에 이쁘게 찾아지진 않았습니다. srcRectCoord 포인트를 먼저 지정해 두고, dstRectCoord 좌표를 조율해서 차선이 평행한 형태로 보일 수 있게 하는 것이 핵심인 것 같습니다. 

 

깃헙 링크 🔗 

    Mat unWarpingImg(Mat imgUndistort, Mat **invMatx, bool showWarpZone)

 

반응형