다음의 순서로 차선 검출을 구현해봤습니다. 매우 기본적인 부분을 이용했다고 생각합니다.
1. 이미지 준비 🖼
2. 이미지 가공 🛠
- 이진화 작업
- 블러
- 캐니엣지
3. 차선 검출 기능(사용한 함수) 🛣
① HoughLines
② HoughLinesP
1. 이미지 준비 🖼
차선 검출 이미지는 BDD100K(Berkeley DeepDrive) 소스를 이용했습니다. 데이터 발행기관의 자세한 설명은 여기에서 확인할 수 있습니다. BDD100K 데이터는 웹사이트 회원가입만 하면 데이터를 다운 받을 수 있습니다. 제공하는 데이터 수가 말 그대로 100K(100,000개 : 10만개) 갯수이며, 각 데이터는 40초 영상으로 구성되어 있습니다. 또한, 영상에서 추출한 이미지 데이터도 제공하고 있어서, 그 데이터를 사용했습니다.
BDD100K 데이터셋(다운로드 가능)
Berkeley DeepDrive
Whitepaper on the dataset is on arXiv! Please download at User portal!
bdd-data.berkeley.edu
실제로 이미지 데이터만 받아보니 약 7GB 정도이네요. 이 정도는 로컬에 여유가 있어서 직접 받아서 진행했네요. 그리고 기본적으로 머신러닝 용도로 제공되고, 사용된 데이터 이기 때문에, 각각 train, test, valid데이터로 나누어서 제공하고 있습니다.
캐글에서도 데이터셋(이미지 데이터)을 제공하고 있었네요. 관심 있는 분들은 아래를 참고 부탁드립니다.
bdd100k
100k Labeled Road Images | Day, Night
www.kaggle.com
사용한 이미지 원본. 📸
2. 이미지 가공 🛠
아래와 같은 순서로 이미지를 가공했습니다.
① 그레이 스케일
② 블러 효과
③ 캐니엣지 검출
이전 작성했던 openCV 기본기 공부 글에서 다뤘던 내용을 그대로 적용했습니다. 상세한 내용은 생략하겠습니다.
① 그레이 스케일(이미지)
② 블러 효과(이미지)
③ 캐니엣지 검출 1(이미지) : 최초 실행 시 아래와 같았습니다.
타겟으로 하는 차선 외의 픽셀들은 노이즈가 되겠죠. 그 노이즈를 나름 줄이는 게 좋다고 생각돼서 low, high Threshold 값을 조절해서 아래와 같은 결과를 최종 데이터 값으로 사용했습니다.
③ 캐니엣지 검출 2(이미지) : low:100, high:120으로 셋팅하니 노이즈가 조금 적어진 것 같네요. 도로 부분과 특히 나무(양쪽)가 있는 부분에서 많이 줄어들었습니다. 또 하나 노이즈를 줄일 수 있는 방법으로 생각한 점은 블러 효과를 더 강하게 주는 것이었는데 적용하진 않았습니다.
3. 차선 검출 기능(사용한 함수) 🛣
아래의 두 가지 openCV 함수를 이용해서 차선 검출을 시도했습니다. 결과적으로 얘기하면, HoughLinesP를 사용하는 편이 더욱 효과 적이었던 것 같습니다. 각 함수에 대한 자세한 설명은 아래 글을 참고하시면 좋을 것 같습니다.
① HoughLines 함수 설명
[ openCV | C++ ] HoughLines 함수 사용 방법. (차선 검출 준비단계)
어떤 함수 ❓ 🤔 직선을 "검출하는" 함수입니다. 직선을 검출하는 방법으로 Hough Line Transform 알고리즘이 사용되었습니다. 이진화된 이미지(엣지 검출 데이터)를 기반으로, 픽셀 포인트가 같은
fwanggu-lee.tistory.com
② HoughLinesP 함수 설명
[ openCV | C++ ] HoughLinesP 함수 사용 방법. (+HoughLines함수와 비교)
어떤 함수 ❓ 🤔 직선을 추출하는 개념으로는 HoughLines과 같습니다. 하지만 이 함수에서는 확률적인 방법으로 접근하는데요. 구체적으로 말하자면 직선 속성을 추출하기 위해 픽셀을 랜덤 하
fwanggu-lee.tistory.com
① HoughLines함수를 사용한 결과.
파라미터 값을 열심히 조절한다고 해봤지만, 어떤 조합으로도 예쁘게 검출하기는 어려웠습니다.(제 능력의 문제일 수도..)어쨌든 이때 사용한 HoughLines의 (주요) 각 파라미터 값은 다음과 같습니다.
- Rho threshold = 1.25 px
- Angle threshold = 4 degree
- # votes = 110
이때 검출된 라인은 아래와 같이 무자비하게 많이 작성되었습니다. 이걸로 차선 검출이 됐다고 얘기할 순 없습니다.
위 그림에서 보면, 양 끝의 나무에서 이진화된 불필요한 픽셀들이 직선을 생성해버린 것 같습니다. 그러면 인풋 파일인 캐니엣지 검출 데이터를 잘 보정하면 될 것 같은데요. 가장 기본적인 가공 처리로는 어느 정도 한계가 있는 것 같았습니다. 그렇기 때문에 검출된 여러 직선에 대해서 특정 컨디션을 부여해서 원하는 직선만 이미지에 표현하면 될것 같습니다.
구체적으로 말하자면, (횡방향 수평선을 0도라고 했을 경우.) 대략 50~130도? 사이의 직선만 골라내서 이미지에 표현하는 겁니다. 그 외의 각도 범위에 있는 직선을 필터링하는 거죠.
Filtering Angle. 🚀
왼쪽, 오른쪽 차선을 검출한 직선이 각각 1줄씩 나오도록 각도 범위를 if 문으로 작성했습니다. 아래 코드의 theta는 HoughLines 리턴 값으로 획득한 직선의 속성 값(각도)을 그대로 활용한 부분입니다. 그리고, 이미지의 종방향(y방향) 위치를 사용자가 지정하고, 직선의 아래 방향 점 데이터는 무한대로 뻗어 가도록 직선 데이터를 잡아 줬습니다.
왼쪽, 오른쪽 차선 모두 필터링한 코드. 👨🏻💻
// angle criteria.
double temp1 = double(90 - leftSide_Angle) * toRadian();
double temp2 = double(90 + rightSide_Angle) * toRadian();
...
if (theta < temp1 && theta > 0)
// rightside line...
{
pt1.x = x0 + lineLen * b;
pt1.y = yFixed;
pt2.x = cvRound(x0 + 10000 * (-b));
pt2.y = cvRound(y0 + 10000 * (a));
}
else if (theta < CV_PI && theta > temp2)
// leftside line...
{
pt1.x = x0 + lineLen * b;
pt1.y = yFixed;
pt2.x = cvRound(x0 - 10000 * (-b));
pt2.y = cvRound(y0 - 10000 * (a));
}
...
먼저 왼쪽 차선만 적용했을 때 결과 값입니다. 이 이미지는 y방향 위치를 구속하지 않은 결과입니다.(양쪽 점 데이터 무한대인 상황)
다음은 y방향 위치(상단 포인트 데이터)를 고정해서 양쪽 차선을 모두 검출한 경우입니다. yFixed라는 변수를 부여해서, y방향 고정 점 처리를 해주었습니다.
HoughLines 함수는 Hough 속성의 rho, theta 값만 반환하기 때문에 일반적으론 무한 직선의 형태로 표현하는 게 편리합니다. 하지만, 직선의 한 점을 직선의 끝점으로 지정할 수도 있습니다. 무한 직선의 경우는, 기준이 되는 x0, y0 좌표를 참조하여 직선 길이 L을 적용하여 직선의 양끝 점을 구하게 됩니다. 이때 L 값을 조절하면, 유저가 원하는 y방향 위치를 끝점으로 지정할 수 있을 것 같습니다. 하지만 직선 위를 타고 가는 변위 값 L이기 때문에 약간의 삼각함수 조작이 필요합니다.
먼저 사진의 row, column 데이터를 출력해서 사이즈를 확인하고, 고정하고자 하는 y 위치 값을 지정합니다. 고정된 y 위치를 지나는 L값을 구하면 x 값은 쉽게 구할 수 있습니다. 코드로 직접 구현해보면 그 개념을 더 잘 학습하실 수 있을 것 같습니다. 자세한 부분은 drawHoughLines함수를 참고 해주세요.
② HoughLinesP함수를 사용한 결과.
HoughLines로 구현한 내용을, 똑같이 HoughLinesP함수로 구현해봅니다. 기본 함수와 가장 큰 차이점은 기본 함수와는 다르게 점 데이터를 리턴한다는 점일 것 같습니다.
HoughLines 함수와 같은 파라미터 값(공통 파라미터일 경우)으로 실행한 경우.
완벽하진 않지만, 기본 함수 때보다 확실히 스트레스가 없네요. 기본적으로 직선을 표현하는 방식이 다르기 때문에 더 깔끔하게 보이는 것 같기도 합니다. 이 함수에서는 두 점만 가지고 직선을 표현하고 있어서 짤막짤막하게 표현되고 있네요. 여기서 더 파라미터를 조절해서 라인만 잡히도록 해봤는데요. 오히려 짤막짤막한 픽셀 데이터들이 넓게 분포하고 있다는 특징이 있어서, 더 제어하기가 어려워지지 않았나 싶었습니다. 하지만, 아래와 같은 아이디어로 접근하면 명확하게 처리할 수 있을 것 같습니다.
기본 함수에서 검출된 직선을 각도 범위로 필터링을 해준 것처럼 여기서는 가상의 박스를 만들어서 해당 박스를 기준으로 필터링을 적용해 줍니다. 아래 그림을 참고해주세요.
노란색 박스에 있는 픽셀만 나타나게 점 데이터를 필터링하는 함수를 만들어 봅니다.
그다음, 필터링된 점 데이터를 이용해서 직선 데이터로 가공 후 이미지에 표시하는 방법으로 진행했습니다.
filterBtmCtrPt 함수 작성 👨🏻💻 :
top, left, right 경계값을 이미지의 전체 너비, 높이 값을 기준으로 지정할 수 있게 설정.
...
// top, left, right Boundary ...
if (pt1.y > imgHeight * (1 - topPos) &&
pt1.x > imgWidth * leftPos &&
pt1.x < imgWidth * (1 - rightPos))
{
linePointsRtn[cnt][0] = pt1.x;
linePointsRtn[cnt][1] = pt1.y;
linePointsRtn[cnt][2] = pt2.x;
linePointsRtn[cnt][3] = pt2.y;
cnt++;
}
}
return linePointsRtn;
}
가장 중요하게 작용한 부분은 votes 경계값을 줄여서 인지하는 픽셀 대상 수를 늘린 후, 필터링 구역을 지정해주는 부분이었습니다. 운전자 입장에서는 바로 앞 차선에 대한 정보가 가장 중요하기 때문에, 이미지의 하단 중앙에 잡히는 픽셀만 표현하도록 구성했습니다.
(실제 사용하기 위해서는 한계점이 있다는 것 인정합니다. 하지만 개념을 익히기 위해서는 이미 충분하다고 생각합니다.)
그렇다면, 위의 파라미터 값을 초기값으로 설정하고, 다른 이미지에는 어떻게 데이터를 표시해주는지 적용해 보겠습니다.
필터링하고자 했던 부분(하단 중앙)에 대해서는 잘 처리를 해주는 모습입니다. 하지만, 기본적으로 검출된 포인트 데이터의 분포 정도가 다른 것 같습니다. 데이터가 너무 적은 것 같습니다.
이런 점으로 봤을 때 기본 함수 또는 HoughLinesP 함수중 어느 방법으로 접근해도, 같은 설정의 파라미터 값으로는 실시간 차선 검출에 적용할 수는 없을 것 같습니다. 다음은 위의 이미지에 가장 알맞게 HoughLinesP 파라미터 값을 설정한 결과입니다.
이때 사용한 파라미터의 조합은 아래와 같습니다.
- # votes:16
- Rho threshold : 0.5
- Theta threshold : 3
- minLength:1.5
- maxGap:5.5
- topPos:0.3
- leftPos:0.1
- rightPos:0.2
여기서 비율은 이미지의 전체 높이, 넓이에 대한 비율을 의미합니다. 불필요한 차량 본넷 테두리까지 잡혀버리네요. 이런 부분도 필터링 라인을 하단에 추가로 넣어준다면 제거할 수 있겠네요.
마무리 🙋🏻♂️
가장 간단하고 기본적인 이미지 준비작업만 사용해서 차선 검출을 구현해봤습니다. 이 내용을 진행하면서 차선 검출에 필요한 이미지 가공 및 검출 작업에 사용되는 수학적인 개념에 대해서 공부할 수 있는 기회가 되었던 것 같습니다. 다음은 더욱 심화적인 내용으로 접근해서, 실제 세계? 차량에서 사용될 수 있는 정도(머신러닝 개념 도입)의 구현 방법에 대해서 진행해보도록 하겠습니다.
'[ C++ ]' 카테고리의 다른 글
[ openCV | C++ ] 차선 검출 이미지 전처리 작업. 왜곡 현상 보정 하는 방법. (2) | 2021.05.08 |
---|---|
[ openCV | C++ ] fillPoly 함수 사용 방법.(픽셀 필터링 활용 용도.) (0) | 2021.04.21 |
[ openCV | C++ ] HoughLinesP 함수 사용 방법. (+HoughLines함수와 비교) (0) | 2021.03.28 |
[ openCV | C++ ] HoughLines 함수 사용 방법. (차선 검출 준비단계) (0) | 2021.03.26 |
[ openCV | C++ ] Hough Line Transform 이론편.개념정리. (2) | 2021.03.23 |