본문 바로가기

IT/OPENCV

[OPENCV] 7. Line Detection

반응형

☞ 메인보드 : Jetson Nano Developer Kit
운영 체제 : Ubuntu 18.04 - JetPack 4.4.1
☞ OpenCV 버전 : 4.2.0
☞ IDE : Visual Studio Code
☞ 언어 : C++




목차

1. 작업 순서
2. 코드 작성
3. 이미지 프로세싱







 

[OPENCV] 5. 색 공간 모델 HSV

☞ 메인보드 : Jetson Nano Developer Kit ☞ 이미지 파일 버전 : JetPack 4.4.1 ☞ IDE : Visual Studio Code ☞ 언어 : C++ 목차 ○ 1. HSV란? ○ 2. OPENCV의 HSV함수 ○ 3. 코드 작성 ○ 4. 심화 구글에 HSV를..

95mkr.tistory.com


지난 포스트에서는 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에 대해서 더 알아보고 싶다면 아래에 있는 링크에서 좋은 설명이 있으니 읽어보기 바란다.



☞ 민초맛 젤리님의 네이버 블로그

 

OpenCV - 허프 변환 직선 검출 (Hough Transform)

자율주행차에서 도로의 차선을 검출한다고 할때, 센서로 받은 영상에서 차선에 해당하는 에지를 검출했다면...

blog.naver.com

 





반응형