Circle Detection & Line Detection - OpenCV(C++)
Updated:
Hough 변환을 이용한 Circle & Line Detection을 해보기
Visual Studio 2017을 사용하였습니다.
- HoughLinesP, line, HoughCircles, circle, threshold
Hough 변환 이론에 대한 내용보다는 코딩 관련에 집중하여 포스팅하겠습니다.
1. HoughLinesP(확률을 적용한 Hough line detection )
함수 원형
HoughLinesP(src, dst, rho, theta, threshold, min_line_length, max_line_gap)
- src : 입력할 이미지 변수, Edge detect 된 이미지를 입력해야 함
- dst : Hough 변환 line detection 정보를 저장할 Array
- rho : 계산할 픽셀(매개 변수)의 해상도
- theta : 계산할 각도(라디안, 매개변수)의 해상도, 선 회전 각도
- 모든 방향에서 직선을 검출하려면 PI/180 을 사용하면 된다.
- threshold : 변환된 그래프에서 라인을 검출하기 위한 최소 교차 수
- min_line_length : 검출할 직선의 최소 길이, 단위는 픽셀
- max_line_gap : 검출할 선위의 점들 사이의 최대 거리, 점 사이의 거리가 이 값보다 크면 다른 선으로 간주
2. HoughCircles(Hough Circle detection)
함수 원형
HoughCircles( src, dst, method, dp, min_dist, parameter1, parameter2, min_Radius, max_Radius)
- Src : 입력할 Grayscale 이미지
- Dst : 검출 정보를 저장할 Array, 원의 중심 정보를 저장
- method : 원을 검출하는 방법, HOUGH_GRADIENT
- dp : 이미지 해상도(1 : 원본 해상도, 2 : 절반 해상도)
- min_dist : 검출할 원의 최소 거리
- parameter1 : Canny edge detection 에서의 높은 threshold 값
- parameter2 : 원 검출을 위한 정보
- Min_Radius : 검출될 원의 최소 반지름(모든 원 검출하려면 0)
- Max_Radius : 검출될 원의 최대 반지름(모든 원 검출하려면 0, 원의 중심만 반환하려면 음수 값)
3. line(선 그리기)
함수 원형
void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, int lineType = 8, int shift = 0)
- Mat& img : 그림 대상 행렬
- Point pt1, pt2 : 시작 좌표와 종료 좌표
- Rect rect : 그릴 영역을 나타내는 사각형 자료형
- Scalar color : 선의 색상
- int thickness : 선의 두께, FILLED(-1)일 경우 지정된 색으로 내부를 채움
- int lineType :
- LINE_4 : 값 → 4 : 4-방향 연결선(4-connected line)
- LINE_8 : 값 → 8 : 8-방향 연결선(8-connected line)
- LINE_AA : 값→16 : 계단현상 감소(Anti-aliasing)
- int shift : 입력 좌표(pt1, pt2)에 대해서 오른쪽 비트 시프트(») 연산한 결과를 좌표로 지정해서 직선을 그림
4. circle(원 그리기)
함수 원형
void circle(Mat& img, Point center, int radius, const Scalar& color, int thickness = 1, int lineType = 8, int shift = 0)
- Mat& img : 원을 그릴 대상 행렬
- Point center : 원의 중심 좌표
- int radius : 원의 반지름
- Scalar& color : 선의 색상
- int thickness : 선의 두께
- int lineType : 선의 형태, cv::line() 함수의 인자와 동일
- int shift : 좌표에 대한 비트 시프트 연산
5. Threshold(Threshold 처리하기)
함수 원형
cv::threshold( src, dst, threshold_value, Max_value, threshold_type)
- src : 입력할 이미지 변수 (grayscale 이미지)
- dst : 필터가 적용되어 저장될 이미지 변수
- threshold_value : 임계 값 (0~255), 이진화 시킬 기준 값을 입력
- Max_value : 임계 값 이상의 픽셀들에 적용할 값
- threshold_type : 이진화 하는 방법
Threshold_type | Threshold 값 이상 | Threshold 값 이하 |
---|---|---|
THRESH_BINARY | Max_value | 0 |
THRESH_BINARY_INV | 0 | Max_value |
THRESH_MASK | 흑색 이미지로 변환 | |
THRESH_OTSU | Otsu 알고리즘 사용 | |
THRESH_TOZERO | 원본 값 | 0 |
THRESH_TOZERO_INV | 0 | 원본 값 |
THRESH_TRIANGLE | Triangle 알고리즘 사용 | |
THRESH_TRUNC | Threshold 값 | 원본 값 |
6. 코드
#include <opencv2/opencv.hpp>
#include <iostream>
// using namespace cv;
using namespace std;
int main()
{
cv::Mat frame;
cv::VideoCapture cap(0); // camera 연결
if (!cap.isOpened())
{
cerr << "카메라 열 수 없음." << endl;
return -1;
}
cout << "\n\n프로그램 종료는 esc key를 누르세요.\n";
cout << "\n ===================================================================== \n";
cout << "웹캠 사용은 1, 동영상 재생은 2를 누르세요.\n\n\n";
int num;
cin >> num;
if (num == 1) // 웹캠 선택
{
while (true) // 종료 시까지 영상 출력
{
cap.read(frame);
if (frame.empty())
{
cerr << "캡쳐 실패." << endl;
break;
}
imshow("Live", frame);
cv::Mat gray_image;
cv::cvtColor(frame, gray_image, cv::COLOR_BGR2GRAY); // HoughCircles 함수는 grayscale 필요
cv::Mat result_frame; // 결과를 그릴 frame
frame.copyTo(result_frame);
/*==============================HoughCircle 해보기===========================================*/
vector<cv::Vec3f> circles;
cv::HoughCircles(gray_image, circles, cv::HOUGH_GRADIENT, 1, 100, 100, 35, 10, 150);
// HoughCircles(입력, 결과출력, 검출 방법, 이미지 해상도, 검출할 원 최소거리, threshold, 원 검출을 위한 정보, 최소 반지름, 최대 반지름)
for (size_t i = 0; i < circles.size(); i++) // 원 그리기
{
cv::Vec3i c = circles[i];
cv::Point center(c[0], c[1]); // 원 중심의 x, y 좌표
int radius = c[2]; // 원 반지름
cv::circle(result_frame, center, radius, cv::Scalar(0, 0, 255), 2);
}
/*=================================HoughLinesP 해보기=============================================*/
cv::Mat frame_canny;
cv::Canny(line_result, frame_canny, 150, 250); // HoughLinesP 함수는 Edge detection을 한 결과를 입력으로 받음
vector<cv::Vec4i> linesP;
cv::HoughLinesP(frame_canny, linesP, 1, (CV_PI / 180), 100, 50, 10);
cv::Mat frame_lane; // 마스크에 그려볼 용도
cv::threshold(frame_canny, frame_lane, 150, 255, cv::THRESH_MASK); // 흑백으로 바꾸기
for (size_t i = 0; i < linesP.size(); i++) // 선 그리기
{
cv::Vec4i l = linesP[i];
cv::line(result_frame, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 255, 0), 2, 8);
cv::line(frame_lane, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar::all(255), 1, 8);
}
imshow("Result", result_frame);
imshow("Result_lane", frame_lane);
int key = cv::waitKey(10);
if (key == 27) // esc key 누르면 종료
break;
}
}
else if (num == 2) // 동영상 출력
{
cv::Mat frame;
cv::VideoCapture cap("BlackBOX.mp4");
if (!cap.isOpened())
{
cerr << "파일을 열 수 없습니다." << endl;
return -1;
}
while (cap.isOpened())
{
cap.read(frame);
if (frame.empty())
{
cerr << "\n\n파일 읽기 실패 or 동영상 재생 완료" << endl;
return -1;
}
imshow("BLACK", frame);
cv::Mat gray_image;
cv::cvtColor(frame, gray_image, cv::COLOR_BGR2GRAY);
cv::Mat result_frame; // 결과를 그릴 frame
frame.copyTo(circle_result);
/*==============================HoughCircle 해보기===========================================*/
vector<cv::Vec3f> circles;
cv::HoughCircles(gray_image, circles, cv::HOUGH_GRADIENT, 1, 100, 100, 35, 10, 150);
// HoughCircles(입력, 결과출력, 검출 방법, 이미지 해상도, 검출할 원 최소거리, threshold, 원 검출을 위한 정보, 최소 반지름, 최대 반지름)
for (size_t i = 0; i < circles.size(); i++)
{
cv::Vec3i c = circles[i];
cv::Point center(c[0], c[1]);
int radius = c[2];
cv::circle(result_frame, center, radius, cv::Scalar(0, 0, 255), 2); // 원 테두리 그리기
}
/*===========================HoughLinesP 해보기============================================*/
cv::Mat frame_canny;
cv::Canny(line_result, frame_canny, 150, 250);
vector<cv::Vec4i> linesP;
cv::HoughLinesP(frame_canny, linesP, 1, (CV_PI / 180), 100, 50, 10);
cv::Mat frame_lane;
cv::threshold(frame_canny, frame_lane, 150, 255, cv::THRESH_MASK);
for (size_t i = 0; i < linesP.size(); i++)
{
cv::Vec4i l = linesP[i];
cv::line(result_frame, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 255, 0), 2, 8);
cv::line(frame_lane, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar::all(255), 1, 8);
}
imshow("Result_Line", result_frame);
imshow("Result_lane", frame_lane);
int key = cv::waitKey(10);
if (key == 27)
break;
}
}
return 0;
}
Leave a comment