☞ 메인보드 : Jetson Nano Developer Kit
☞ 운영 체제 : Ubuntu 18.04 - JetPack 4.4.1
☞ OpenCV 버전 : 4.2.0
☞ IDE : Visual Studio Code
☞ 언어 : C++
목차
○ 1. 작업 순서
○ 2. 코드 작성
○ 3. 이미지 프로세싱
지난 포스트에서는 HSV 색공간 모델을 이용하여 도로에 있는 흰색의 차선을 검출해낼 수 있었다. 이번 포스트에서는 단순히 흰색을 검출함이 아닌 차선으로 인식되는 픽셀을 임의의 선으로 검출해내는 작업을 하려고한다.
① 작업 순서
첫째 ☞ 차선을 쉽게 검출할 수 있도록 밑 작업을 한다. 불러온 이미지 픽셀 상에서 차선이 있는 도로 부분을 ROI(관심 영역)으로 지정하여 잘라낸다.
둘째 ☞ 도로에서 흰색의 차선을 검출하기 위해 HSV 색공간 모델을 이용하여 ROI로 잡은 픽셀 중 흰색에 가까운 픽셀을 검출한다.
셋째 ☞ BilateralFilter를 이용하여 이미지에 샤프 효과를 준다. 이 작업으로 근접한 픽셀의 값들이 변화한다. 가장자리는 유지하되 노이즈가 줄어들어 선을 검출하기가 수월해진다.
넷째 ☞ dilate를 이용하여 기존 흰색 픽셀에 근접한 픽셀이 흰색으로 바뀌게 된다. 이로 인해 차선으로 추측되는 객체가 팽창되어 가장자리로 인식하기 수월해진다.
다섯째 ☞ 허프 변환(Hough Transform)을 적용하여 Canny에 의해 가장자리로 검출된 것을 선으로 잇는다.
mkdir -p ~/workspace/lineDetection/src
위 작업을 진행하는데 사용하는 이미지 파일은 이전 포스트와 같다.
작업 공간을 생성하여 이제부터 만들 프로젝트 디렉토리 안에 넣어주어야 한다.
② 코드 작성
※ main.cpp
파일 위치 ☞ ~/workspace/lineDetection/src/main.cpp
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
int main(){
cv::Mat src;
// road2.jpg - width : 960, height : 720
src = cv::imread("road2.jpg", cv::IMREAD_COLOR);
cv::resize(src, src, cv::Size(src.cols/2, src.rows/2));
if(src.empty()){
std::cout<<"Can't Open Image"<<std::endl;
return -1;
}
cv::Mat ROI, HSV;
ROI = src(cv::Rect(0,200,480,160));
cv::cvtColor(ROI,HSV,cv::COLOR_BGR2HSV);
cv::Scalar lower_white = cv::Scalar(0,0,200);
cv::Scalar upper_white = cv::Scalar(180,255,255);
cv::inRange(HSV, lower_white, upper_white, HSV);
cv::Mat filter, dilate, canny;
cv::bilateralFilter(HSV, filter, 5, 100, 100);
cv::dilate(HSV, dilate, cv::Mat());
cv::Canny(dilate, canny, 150, 270);
cv::Mat lineResult;
cv::cvtColor(canny, lineResult, cv::COLOR_GRAY2BGR);
std::vector<cv::Vec4i> lines;
cv::HoughLinesP(canny, lines, 1, CV_PI/180, 50, 50, 10);
for(size_t i = 0; i < lines.size(); i++){
cv::Vec4i l = lines[i];
cv::line(lineResult, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0,0,255), 2, cv::LINE_AA);
}
cv::imshow("HSV", HSV);
cv::imshow("dilate", dilate);
cv::imshow("canny", canny);
cv::imshow("Line Detection", lineResult);
cv::waitKey(0);
return 0;
}
※ CMakeLists.txt
파일 위치 ☞ ~/workspace/lineDetection/CMakeLists.txt
cmake_minimum_required(VERSION 2.9)
project(lineDetection)
find_package(OpenCV REQUIRED)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
코드와 CMakeLists.txt 작성을 완료했다면 프로젝트 경로에서 cmake ☞ make 과정을 진행해준다.
③ 이미지 프로세싱
※ 실행
※ 이미지 축소
cv::Mat src;
// road2.jpg - width : 960, height : 720
src = cv::imread("road2.jpg", cv::IMREAD_COLOR);
cv::resize(src, src, cv::Size(src.cols/2, src.rows/2));
resize 함수는 사용자가 설정한 double 타입의 fx, fy 매개변수 값에 맞게 InputArray에 입력된 이미지 크기를 재지정할 수 있고, 재지정된 출력이미지는 OutputArray의 dst 매개변수로 반환한다. 이미지로 작업을 할 때는 크게 상관이 없지만 픽셀 수가 많은 이미지를 처리하다보면 연산 시간이 꽤 오래걸리기 때문에 메모리를 많이 잡아먹게 된다. 즉, 렉이 먹을 수도 있다. 특히 카메라를 이용하여 실시간으로 영상을 출력하는 경우에는 실행 프로그램이 메모리를 많이 차지할 확률이 높으니 영상 크기를 작게하면 이를 해결할 수 있을 것이다.
※ ROI
cv::Mat ROI, HSV;
ROI = src(cv::Rect(0,200,480,160));
이미지에는 사용자가 원하는 물체나 사물 등이 있을 것이다. 모든 픽셀이 중요하지는 않기 때문에 그 부분만 잘라내어 관심 영역으로 지정하는 것을 Region Of Interest(ROI) 라고 한다. 이 이미지는 하늘보다는 도로의 흰색 가장자리를 검출하기 위한 작업이니 도로 부분만 관심영역으로 지정했다. [원래는 그냥 잘라서 사용하지 않고 mask와 fillPoly 함수를 이용하여 원본 이미지 중 ROI로 정한 부분 이외에는 전부 검정색으로 처리하고 사용한다.]
※ HSV
cv::cvtColor(ROI,HSV,cv::COLOR_BGR2HSV);
cv::Scalar lower_white = cv::Scalar(0,0,200);
cv::Scalar upper_white = cv::Scalar(180,255,255);
cv::inRange(HSV, lower_white, upper_white, HSV);
HSV의 범위를 설정하여 이미지 픽셀 중 흰색에 가까운 픽셀값을 찾아낸다. lower_white(최소), upper_white(최대)를 설정하여 inRange 함수로 해당 범위 만큼의 흰색 픽셀을 검출할 수 있도록 했다.
Hue(색상) : 0 ~ 180 / Saturation(채도) : 0 ~ 255 / Value(명도) : 200 ~ 255
※ Blur + Dilate
cv::Mat filter, dilate, canny;
cv::bilateralFilter(HSV, filter, 5, 100, 100);
cv::dilate(filter, dilate, cv::Mat());
검출된 흰색 픽셀을 더 뚜렷하게 나타내기 위해서 bilateral 효과를 준다. 이 작업으로 흰색 픽셀에 인접한 픽셀들이 계산에 의해 비슷하게 변한다. 사용자가 설정한 편차치에 맞게 노이즈는 제거하고 윤곽선은 비교적 살려주기 때문에 dilate와 함께 사용하여 흰색 픽셀을 팽창시켰다. 가장자리 검출이 더욱 쉬워졌다.
※ Canny + Hough Transform
cv::Canny(dilate, canny, 150, 270);
cv::Mat lineResult;
cv::cvtColor(canny, lineResult, cv::COLOR_GRAY2BGR);
std::vector<cv::Vec4i> lines;
cv::HoughLinesP(canny, lines, 1, CV_PI/180, 50, 50, 10);
for(size_t i = 0; i < lines.size(); i++){
cv::Vec4i l = lines[i];
cv::line(lineResult, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0,0,255), 2, cv::LINE_AA);
}
dilate로 팽창 시켜놓은 흰색 픽셀의 가장자리를 검출하기 위해 Canny를 사용한다.
이미지의 타입을 통일해야하기 때문에 BGR 포맷으로 바꾸는 과정을 넣었다.
HoughLinesP로 가장자리를 검출할때 중간에 있는 선과 양 차선 밖에 있는 흰색 물체는 무시할 수 있도록 최소 검출 길이를 길게 정해놓았다. 이로써 양쪽에 있는 차선만을 정확하게 검출할 수 있었다.
Hough Transform에 대해서 더 알아보고 싶다면 아래에 있는 링크에서 좋은 설명이 있으니 읽어보기 바란다.
☞ 민초맛 젤리님의 네이버 블로그
'IT > OPENCV' 카테고리의 다른 글
[OPENCV] 9. 이미지 프로세싱(4) - Warp (3) | 2021.06.12 |
---|---|
[OPENCV] 8. 이미지 프로세싱(3) - 모폴로지 연산 (0) | 2021.05.12 |
[OPENCV] 6. 이미지 프로세싱(2) - 샤프닝 & 가장자리 검출 (0) | 2021.05.08 |
[OPENCV] 5. 색 공간 모델 HSV (3) | 2021.03.14 |
[OPENCV] 4. 이미지 프로세싱(1) - 블러링 (0) | 2020.12.13 |