본문 바로가기
코딩/OpenCV

[C++ opencv] 허프변환을 이용하여 직선 검출하기, HoughLines

by DIYver 2020. 7. 29.

본문 목표

영상처리에 있어서 직선 검출이 필요한 상황이 있다.

이런 경우에 허프변환을 통해서 직선을 검출할 수 있는데,

OpenCV 에서는 어떻게 사용하는지 알아보자.

 

 

 

키워드 : Hough transform Line

 

 

 

 

 

알아볼 함수 원형

- 허프변환 직선 검출 ( HoughLines )

	Mat img = imread("lane.png");

	Mat img_gray;
	cvtColor(img, img_gray, COLOR_BGR2GRAY);

	Mat img_canny;
	Canny(img_gray, img_canny, 150, 255);

	vector<Vec2f> lines;
	HoughLines(img_canny, lines, 1, CV_PI / 180, 150);

	Mat img_hough;
	img.copyTo(img_hough);
	
	Mat img_lane;
	threshold(img_canny, img_lane, 150, 255, THRESH_MASK);

	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0], theta = lines[i][1];
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a * rho, y0 = b * rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(img_hough, pt1, pt2, Scalar(0,0,255), 2, 8);
		line(img_lane, pt1, pt2, Scalar::all(255), 1, 8);
	}

 

HoughLines( src, dst, rho, thetathreshold)

 

  ○ src : 입력할 이미지 변수, Edge detect 된 이미지를 입력해야 함

 

  ○ dst : 허프변환 직선 검출 정보를 저장할 Array 

 

  ○ rho : 계산할 픽셀(매개 변수)의 해상도, 그냥 1을 사용하면 됨

- 변환된 그래프에서, 선에서 원점까지의 수직 거리

 

  ○ theta : 계산할 각도(라디안, 매개변수)의 해상도, 선 회전 각도

- 모든 방향에서 직선을 검출하려면 PI/180 을 사용하면 된다.

 

  ○ threshold : 변환된 그래프에서 라인을 검출하기 위한 최소 교차 수

 

 

 

 

 

 

허프 변환에 대해서 개념을 살짝 짚고 넘어가야 이해가 쉽게 된다.

 

 

우리는 위와같은 x y 좌표계의 한 점 (1,1) 이 있을 때,

(1,1) 을 지나가는 직선의 방정식은

y = ax + b 라는 방정식에 (1,1) 을 입력한 것과 같다고 할 수 있다.

그러면 식은 1 = a + b 가 된다.

이 때, b = -a + 1 의 그래프를 그려보자.

 

이 그래프는 변환된 그래프로, 

이 그래프는 원본 이미지에서 (1,1) 을 지나는 직선의 방정식의 그래프이다.

좌표계가 x y 에서 a b 로 바뀌었다.

 

좌표계 변환의 기본은 위와 같을 때, 아래 이미지를 살펴보자.

 

이미지가 위의 픽셀들의 좌표가 위와 같을 때,

방정식에 대입하면 위와 같은 식을 얻을 수 있다.

 

그래프에 그려본다면,

이렇게 그려낼 수 있다.

 

위의 그래프에서 3번 이상 교차하는 지점을 빨간 원으로 표시하였다.

빨간 원의 a 와 b 값을 보면

(0,1), (1,-1) 이 된다.

이걸 다시 원래의 x y 좌표계에 입력해서

식을 만들어보면

y = 1

y = x - 1 이 된다.

 

그래프에 표시해보면 아래와 같다.

원래 이미지에 픽셀들을 연결한 직선이 되는 모습을 볼 수 있다.

 

허프 변환 직선 검출은 위와 비슷한 방식으로 직선을 검출해낸다.

 

너무 자세한 내용은 다루지 않도록하고,

마지막으로 설명을 하자면 허프변환은 극좌표계에서 이런 교차점을 찾는 과정을 거치게 된다.

따라서 rho 값과 theta 값이 필요한데,

이는 사용자가 정해주어야 하는 값이 된다.

모든 방향에서 검출을 하려면 rho 는 1을 사용하고,

theta 는 PI / 180 을 사용하면 된다.

 

자세히 알아보려면 opencv 홈페이지를 참고하길 바란다.

https://docs.opencv.org/3.4/d9/db0/tutorial_hough_lines.html

 

OpenCV: Hough Line Transform

Prev Tutorial: Canny Edge Detector Next Tutorial: Hough Circle Transform Goal In this tutorial you will learn how to: Theory NoteThe explanation below belongs to the book Learning OpenCV by Bradski and Kaehler. Hough Line Transform The Hough Line Transform

docs.opencv.org

 

사실 그냥 원리를 이해하고,

코드를 복붙해서 사용하는 것이 이롭다.

원리를 아예 모르고 사용하는 것은 문제가 있지만,

이정도만 알고 사용해도 충분하다.

 

 

 

 

 

 

 

 

 

 

코드 테스트 결과

- CODE

#include <opencv2/opencv.hpp>


using namespace cv;
using namespace std;

int main(int ac, char** av)
{
	Mat img = imread("lane.png");

	Mat img_gray;
	cvtColor(img, img_gray, COLOR_BGR2GRAY);

	Mat img_canny;
	Canny(img_gray, img_canny, 150, 255);

	vector<Vec2f> lines;
	HoughLines(img_canny, lines, 1, CV_PI / 180, 150);

	Mat img_hough;
	img.copyTo(img_hough);
	
	Mat img_lane;
	threshold(img_canny, img_lane, 150, 255, THRESH_MASK);

	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0], theta = lines[i][1];
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a * rho, y0 = b * rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(img_hough, pt1, pt2, Scalar(0,0,255), 2, 8);
		line(img_lane, pt1, pt2, Scalar::all(255), 1, 8);
	}

	imshow("img_canny", img_hough);
	imshow("img_lane", img_lane);

	waitKey(0);
	return 0;
}

 

- RESULT

lane.png
0.36MB

원본 이미지는 위와 같은 차선 이미지를 사용하였다.

아무래도 직선검출은 자율주행 시스템에서 많이 사용하기 때문에,

적합한 이미지를 찾다가 이 사진을 이용하기로 하였다.

 

이번에는 cvtColor( ) 함수를 이용해서 grayscale 로 변환해 주었다.

grayscale 로 변환해주는 이유는 Edge detection 이 필요하기 때문이다.

 

Canny( ) 함수를 이용해서 Edge detection 을 하였다.

허프변환을 함에 있어서 Edge detection이 된 이미지를 사용하여야 하기 때문이다.

 

허프 변환을 통해서 검출된 직선을 저장할 Array를 생성해준다.

vector<Vec2f> 라는 벡터 자료형을 이용하는데, 데이터가 2개로 평면임을 알 수 있다.

HoughLines( ) 함수를 이용하여 img_canny 이미지로부터 직선을 검출하고,

해당 직선들을 lines 라는 Array 에 저장한다.

이 때, 마지막에 쓰이 150은 threshold 값으로 최소 교차 횟수를 뜻한다.

사용자가 여러번 값을 조정해보면서 찾아야하는 값이다.

 

검은색 이미지에 직선만 검출한 정보를 띄우기 위해

black mask 를 하나 만들어 주었다.

 

이미지에 찾은 직선들을 그리는 작업이다.

굳이 이해하기보단 외워서 쓰는게 더 좋을 수 있다.

위와 같이 했을 때 정상적으로 안 된다면, 이해가 필요한 순간인 것이다.

 

 

원본이미지에 찾은 직선들을 표시하면 위와 같이 나온다.

 

 

 

직선들만 따로 표시한 경우는 위와 같고,

차선이 직선으로 검출된 것을 확인할 수 있다.

 

 

 

 

 

결론.

OpenCV 에서도 허프변환을 이용하여 직선을 검출할 수 있다.

 

직선은 영상 끝과 끝을 이어준다.

 

교차점 수를 뜻하는 threshold 값을 조절하여 직선 검출량을 조절할 수 있다.

 

 

 

 

 

도움이 되었거나, 문제가 있는 경우 댓글로 알려주세요~!

감사의 댓글은 작성자에게 큰 힘이 됩니다 ^^

댓글