차선 검출을 위한 이미지 전 처리해보기 🛣
- 왜곡 제거(카메라 보정) 👍
- Perspective Transform(원본 이미지 ⏩ 2D)
- Color Filtering(HLS, LAB color space)
- 픽셀 값 정규화(feat. 최대값) 및 이미지 픽셀(HLS 1개, LAB 1개) 합치기.
- Window Search
- Show Detected Lines and Info.
개념 🧐
차선을 잡아주기 위해서, 원본 이미지의 색 채널을 조작합니다. 우리가 통상 보고 있는 화면의 색은 BGR채널(Blue, Green, Red)의 조합으로 표현할 수 있습니다. 고맙게도 opencv 에서는 BGR 채널을 변환시켜서 빛의 요소 및 색의 명암, 채도, 포화도 등등의 섬세한 요소로 분리하여 표현할 수 있게 해 줍니다. 차선 검출 전처리 작업에서는 이런 변환 과정을 통해서, 노란색 차선 또는 흰색 차선만 픽셀 요소로 남겨두고(필터링), 남겨진 픽셀 데이터의 인덱스(위치) 값을 활용하여 차선 검출에 좋은 소스로서 활용할 수 있게 됩니다.
방법 + DEMO. 🛠🚀
순서는 다음과 같습니다.
1. 컬러 채널 변경.
2. 남겨둘 채널의 픽셀값만 가져오기.(전체 3개 중 1개의 채널 값만 입력함. 예) L, A, B 중 B 채널 값만 가져오기)
1. 컬러 채널 변경.
cvtColor 함수를 사용합니다. 함수의 사용법에 대해서는 다음 글의 "이미지 이진화 하기" 부분을 참고하시면 좋을 것 같네요.
컬러 채널 중 HLS, LAB 채널을 선택해서 변환한 이유는 흰색 라인, 노란색 라인(중앙선)을 구별할 수 있는 가장 좋은 컬러 채널로 판단되어 사용했습니다. 그 외 채널에 대해서 검증한 내용은 다음 글을 참고해서 적용했습니다.
BGR ⏩ HLS(Hue, Lightness, Saturation) 의 경우.
cvtColor(imgUnwarp, imgConverted, COLOR_BGR2HLS);
BGR ⏩ LAB(Lightness, a, b) 의 경우.
cvtColor(imgUnwarp, imgConverted, COLOR_BGR2Lab);
2. 남겨둘 채널의 픽셀값만 가져오기. (전체 3개중 1개의 채널 값만 정의하기. 예) L, A, B 중 B 채널 값만 가져오기)
1️⃣ zero Mat 변수 정의
컬러 채널 필터링 후 사용하게 될 Mat 변수를 정의합니다. 이때, 1개 채널만 핸들링하기 때문에 Mat 타입은 CV_8UC1(8비트 Unsigned, Channel 1개)로 정의합니다.
Mat imgOUT = Mat::zeros(img.rows, img.cols, CV_8UC1);
2️⃣ 이미지의 특정 채널 픽셀값을 zero Mat에 할당(포인터 개념으로 할당)
이미지의 컬러 채널을 변경 후, 변경된 이미지의 픽셀 데이터를 포인터 개념으로 접근합니다. 이미지의 포인터 데이터는 아래와 같이 초기화 할 수 있습니다. 우리가 알고 있듯이 포인터는 어레이와 같은 개념이므로, 인덱스를 부여해서 해당 인덱스의 요소(픽셀 값) 에 접근할 수 있습니다.
[ 준비 : 포인터 변수 정의 ]
cvtColor(imgUnwarp, imgConverted, COLOR_BGR2Lab);
uint8_t *pixelPtr = (uint8_t *)imgConverted.data;
다음으로 원본 이미지에서 B채널의 픽셀 값만(필터링 B채널) zero Mat 변수(모든 픽셀 값 0 인 이미지이며, row column 크기는 원본과 같다.) 를 타겟으로 할당해줍니다. 이때, 원본 이미지의 컬러 채널은 픽셀의 위치 별로 [L, A, B]의 어레이 형태와 순서로 픽셀 값을 갖게 됩니다. 그렇기 때문에 B 채널의 픽셀 값인 인덱스 2 값을 이용해서 B 채널의 픽셀 값을 할당해줄 수 있습니다.
[ 할당 : 필터링 컬러 채널 값 ]
// 이미지의 채널 수
int cn = imgConverted.channels();
// L,A,B 채널 중 B 채널 값만 imgOUT 이미지에 각 픽셀에 할당
for (int i = 0; i < imgConverted.rows; i++)
{
for (int j = 0; j < imgConverted.cols; j++)
{
imgOUT.at<uint8_t>(i, j) = pixelPtr[i * imgConverted.cols * cn + j * cn + 2];
}
}
함수를 사용해서 구현해주었기 때문에 imgOUT 변수를 리턴 해주는 것으로 마무리 할 수 있었습니다. 끝.
깃헙 링크 🔗
Mat filterImg(Mat imgUnwarp, int toColorChannel, int mode)