내맘대로 공부기록.

[ C++ ]

[ openCV | C++ ] ( 6 / 6 ) 차선검출. 데이터를 시각화 하기.

fwanggus 2021. 9. 19. 23:11
반응형

차선 검출 데이터를 시각화 📺


⚙️  기본 설명  ⚙️

 

  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.

개념  🧐

1~5번에서 이미지 속에 있는 차선을 찾아내기 위해 전처리 작업을 진행했습니다. 작업을 통해서 획득한 데이터를, 이미지 프레임에 얹어 주겠습니다. 사실 이 작업은 우리가 보고 있는 영상 또는 카메라 입력 데이터에 대해서 전처리된 데이터를 뿌려주는 개념으로, "이미지 전처리"라는 타이틀을 가져가야 하는 게 맞는지 고민을 했었습니다. 하지만, 이미지 소스로부터 무언가를 획득하거나 계산하는 작업이 아니었고, Visualiztion( 시각화 )의 개념이기 때문에 시각화라는 작업으로 인식하고 작성했습니다.

방법  + DEMO. 🛠🚀

순서는 다음과 같습니다.

  1. 좌, 우 차선 표시(polylines)
  2. 주행 차선을 영역으로 표시(fillPoly)

 

아래 내용은 다음 내용을 완료 후 진행된 부분입니다.

1️⃣  Window Search 로 차선 픽셀 검색 및 획득

2️⃣  획득한 픽셀을 표현하는 2차 방정식(근사식) 계수 획득 후, 방정식에 의한 차선의 픽셀 위치 재정의(벡터 변수)

👉  재정의한 픽셀 위치 데이터를 알고 있는 상태. 

1. 좌, 우 차선 표시(polylines)

차선을 표시할 픽셀 입력값을 벡터 변수로 준비합니다. 작성한 함수는 다음과 같은 구성으로 준비했습니다.

	...
	drawDataSet = calcImg(imgBinary);
	imgShow = drawAll(img, imgBinary, invMatx, drawDataSet);
   	...

이미지 소스를 인풋으로 하는 함수를 작성해서, 이미지에 표시할 데이터를 작성합니다. 위의 drawDataSet은 구조체 변수이며, 멤버 변수로써 왼쪽, 오른쪽 차선 픽셀 데이터를 가지도록 했습니다.(아래 변수 점의 참고)

// drawDataInfo 구조체를 정의.
typedef struct drawData
{
    vector<Point> leftLanePts;
    vector<Point> rightLanePts;
    double leftRadius;
    double rightRadius;
    double centerOffset;

} drawDataInfo;

 

polylines 함수의 사용법은 다음의 공홈을 참고해주세요.

 

OpenCV: Drawing Functions

enum  cv::HersheyFonts {   cv::FONT_HERSHEY_SIMPLEX = 0,   cv::FONT_HERSHEY_PLAIN = 1,   cv::FONT_HERSHEY_DUPLEX = 2,   cv::FONT_HERSHEY_COMPLEX = 3,   cv::FONT_HERSHEY_TRIPLEX = 4,   cv::FONT_HERSHEY_COMPLEX_SMALL = 5,   cv::FONT_HERSHEY_SC

docs.opencv.org

 

원본 이미지와 크기가 같은 zero 이미지를 생성하고, 그 위에 차선 데이터를 뿌려줍니다. 데이터만 있다면 매우 간단합니다. 😎

	// Drawing Detected Lane information on Zero img.
	Mat windowImg = Mat::zeros(binary.size(), CV_8UC3);
	...
	polylines(windowImg, drawDataSet.leftLanePts, false, Scalar(255, 0, 255), 15, LINE_AA);
	polylines(windowImg, drawDataSet.rightLanePts, false, Scalar(0, 255, 255), 15, LINE_AA);
	...

 

2. 주행 차선을 영역으로 표시(fillPoly)

1. 에서 정의하고, 획득했던 각 차선의 벡터 변수를 활용해서 시계방향의 순서로(왼쪽, 오른쪽) 픽셀 데이터를 새로운 변수에 할당해줍니다. 다시 말해, fillPoly 함수를 사용하여 주행 차선의 영역을 표시하기 때문에 이 함수에 필요한 인수로써 변수를 정리해줍니다.

 

왼쪽 차선 아래 ⏩ 왼쪽 차선 위 ⏩ 오른쪽 차선 위 ⏩ 오른쪽 차선 아래, 순서로 픽셀데이터를 정렬해서 새로운 변수로 정의합니다.(아래 그림을 참고.)

 

주행 차선 영역을 표시하기 위한 픽셀 데이터 입력 순서.

    ...
    // for drawing lane area with polygon, push each lane pixel back on one vector variable.
    vector<Point> lanePolyPts;
    int margin = 10;

    for (int i = binary.rows; i >= 0; i--)
    {
        lanePolyPts.push_back(Point(drawDataSet.leftLanePts[i].x - margin, i));
    }

    for (int i = 0; i < binary.rows; i++)
    {
        lanePolyPts.push_back(Point(drawDataSet.rightLanePts[i].x + margin, i));
    }
    ...

  

이렇게 차선을 감싸는 픽셀 데이터를 정리했다면, fillPoly 함수를 호출해서 이미지 위에 뿌려주면 됩니다. fillPoly 함수에 대한 설명은 아래 글을 참고해주세요.

 

[ openCV | C++ ] fillPoly 함수 사용 방법.(픽셀 필터링 활용 용도.)

어떤 함수 ❓ 🤔 "그리는" 함수입니다. 다각형 형상을 이미지에 표현합니다. 이때 다각형 내면을 Fill(채우기) 함으로써, 다각형 내부를 특정 픽셀 데이터로 채우게 됩니다. fillPoly( 타겟이미지,

fwanggu-lee.tistory.com

 

fillPoly함수 호출 🛣

    ...
    fillPoly(windowImg, lanePolyPts, Scalar(0, 255, 0), LINE_AA);
    ...

G : 255 (녹색) 으로 차선 영역을 표시함.

 

zero 이미지(차선 영역 표시 완료)를 unWarping 해주기.

이미지 전처리 과정의 Perspective Transform 과정에서 원본 이미지와 bird's view 이미지 관계를 설명하는 매트릭스를 계산했었습니다. 또한, bird's view에서 원본 이미지로 형태를 변환하는 inverse matrix 도 계산할 수 있구요. 여기서는 이 inverse matrix를 적용해서 원본 이미지로 복원해주었습니다.

	...
	warpPerspective(
		windowImg, 
		unWarpedImg, 
		invMatx, 
		Size(original.cols, original.rows), 
		INTER_LINEAR
		);
	...

획득한 차선 데이터를 표시 후 원본 데이터로 변형.(unwarping)

 

초기 이미지 프레임에 데이터를 렌더링 해주기.

zero 이미지 에는 순수한 작업 데이터가 포함되어 있습니다. 작업한 픽셀의 위치는 초기 데이터 소스로부터 왔기 때문에, 초기 이미지에 렌더링? 맵핑해주는 작업은 의외로 간단합니다. 원본 이미지와 같은 위치에 렌더링을 해야 하기 때문에 추가적인 픽셀 위치(인덱스)에 대한 스트레스는 받지 않아도 될 것 같습니다.

여기서 우리는 addWeighted라는 함수를 사용하게 됩니다.

 

 

OpenCV: Operations on arrays

Divides a multi-channel array into several single-channel arrays. The function cv::split splits a multi-channel array into separate single-channel arrays: If you need to extract a single channel or do some other sophisticated channel permutation, use mixCh

docs.opencv.org

	...
	// Combination with addweighted fuctino for two img.
	Mat imgShow;
	addWeighted(original, 1.0, unWarpedImg, 0.5, 0, imgShow);
	...

 

최종 출력된 이미지 프레임은 imgShow 변수가 되며 결과 내용은 다음과 같습니다.

 

 

깃헙 링크 🔗 

	Mat drawAll(Mat original, Mat binary, Mat invMatx, drawDataInfo drawDataSet)

 

반응형