반응형




깃허브에 있는 차선인식 파이썬 코드를 C++로 변환후 분석해 본 결과물입니다.


원본 코드  https://github.com/georgesung/road_lane_line_detection/blob/master/lane_lines.py



최초 작성 - 2018. 9. 17

                  2019. 3. 21 Visual Studio 2017에 gls 라이브러리를 추가하는 방법, OpenCV C API 부분 수정




리눅스에서 작업한 결과입니다. 리눅스에서 gls 라이브러리 추가하는 방법은  소스코드 상단에 있습니다.

Visual Studio 2017에서는 아래 링크를 참고하여 진행하세요.


Visual Studio 2017에서 GLS(GNU Scientific Library) 사용하기

https://webnautes.tistory.com/1323



filter_colors 함수


BGR 영상에서 흰색 차선 후보, HSV 영상에서 노란색 차선 후보를 검출합니다.




노란색 차선 후보와 흰색 차선 후보를 합쳐서 차선 후보 영상을 만듭니다.





region_of_interest 함수


차선 후보 영상으로 부터 검출된 에지 영상에서 차선이 검출되는 영역을 차선 진행방향의 일정영역 바닥으로 제한합니다.




최종 차선 후보만 에지로 검출됩니다.




HoughLinesP 함수, draw_line 함수

HoughLinesP로 검출한 선분으로부터 좌우 차선에 있을 가능성있는 직선들만 따로 뽑아서 좌우 각각 하나의 직선으로 변환합니다. (Linear Least-Squares Fitting)





최종 결과

원본 영상과 좌우 두개의 직선을 추출한 영상을 합쳐서 결과 영상을 만듭니다.






전체 소스 코드


현재 코드에는 main 함수에서 challenge.mp4 동영상을 가지고 테스트하도록 되어 있습니다.


VideoCapture videoCapture("challenge.mp4");



카메라에서 영상을 캡처하도록 바꾸어 테스트 가능합니다. 실제 도로에서 테스트하게 되면 테스트에 사용한 영상과 조명 환경이 달라서 노란색 차선과 흰색 차선시 사용한 파라미터 값 조정이 필요할 수 있습니다.


VideoCapture videoCapture(0);


// 원본 코드  https://github.com/georgesung/road_lane_line_detection/blob/master/lane_lines.py
// 수정 - webnautes
//
// 필요한 라이브러리
// OpenCV 3.x  http://opencv.org/releases.html

// 설치 방법 http://webnautes.tistory.com/1186

//
// GSL - GNU Scientific Library https://www.gnu.org/software/gsl/
// 설치 방법 sudo apt-get install libgsl-dev
//
// 컴파일

// g++ main.cpp -o main $(pkg-config opencv --libs --cflags) -lgsl -lcblas

//
// 테스트 동영상 다운로드

// https://github.com/georgesung/road_lane_line_detection


#include <opencv2/opencv.hpp>  
#include <opencv2/imgproc.hpp>
#include <gsl/gsl_fit.h>
#include <iostream>  


using namespace cv;
using namespace std;



//Hough Transform 파라미터
float rho = 2; // distance resolution in pixels of the Hough grid
float theta = 1 * CV_PI / 180; // angular resolution in radians of the Hough grid
float hough_threshold = 15; // minimum number of votes(intersections in Hough grid cell)
float minLineLength = 10; //minimum number of pixels making up a line
float maxLineGap = 20; //maximum gap in pixels between connectable line segments


//Region - of - interest vertices, 관심 영역 범위 계산시 사용
//We want a trapezoid shape, with bottom edge at the bottom of the image
float trap_bottom_width = 0.85;  // width of bottom edge of trapezoid, expressed as percentage of image width
float trap_top_width = 0.07;     // ditto for top edge of trapezoid
float trap_height = 0.4;         // height of the trapezoid expressed as percentage of image height


//차선 색깔 범위
Scalar lower_white = Scalar(200, 200, 200); //흰색 차선 (RGB)
Scalar upper_white = Scalar(255, 255, 255);
Scalar lower_yellow = Scalar(10, 100, 100); //노란색 차선 (HSV)
Scalar upper_yellow = Scalar(40, 255, 255);



Mat region_of_interest(Mat img_edges, Point *points)
{
/*
Applies an image mask.

Only keeps the region of the image defined by the polygon
formed from `vertices`. The rest of the image is set to black.
*/

Mat img_mask = Mat::zeros(img_edges.rows, img_edges.cols, CV_8UC1);


Scalar ignore_mask_color = Scalar(255, 255, 255);
const Point* ppt[1] = { points };
int npt[] = { 4 };


//filling pixels inside the polygon defined by "vertices" with the fill color
fillPoly(img_mask, ppt, npt, 1, Scalar(255, 255, 255), LINE_8);


//returning the image only where mask pixels are nonzero
Mat img_masked;
bitwise_and(img_edges, img_mask, img_masked);


return img_masked;
}




void filter_colors(Mat _img_bgr, Mat &img_filtered)
{
// Filter the image to include only yellow and white pixels
UMat img_bgr;
_img_bgr.copyTo(img_bgr);
UMat img_hsv, img_combine;
UMat white_mask, white_image;
UMat yellow_mask, yellow_image;


//Filter white pixels
inRange(img_bgr, lower_white, upper_white, white_mask);
bitwise_and(img_bgr, img_bgr, white_image, white_mask);


//Filter yellow pixels( Hue 30 )
cvtColor(img_bgr, img_hsv, COLOR_BGR2HSV);


inRange(img_hsv, lower_yellow, upper_yellow, yellow_mask);
bitwise_and(img_bgr, img_bgr, yellow_image, yellow_mask);


//Combine the two above images
addWeighted(white_image, 1.0, yellow_image, 1.0, 0.0, img_combine);


img_combine.copyTo(img_filtered);
}



void draw_line(Mat &img_line, vector<Vec4i> lines)
{
if (lines.size() == 0) return;

/*
NOTE : this is the function you might want to use as a starting point once you want to
average / extrapolate the line segments you detect to map out the full
extent of the lane(going from the result shown in raw - lines - example.mp4
to that shown in P1_example.mp4).

Think about things like separating line segments by their
slope((y2 - y1) / (x2 - x1)) to decide which segments are part of the left
line vs.the right line.Then, you can average the position of each of
the lines and extrapolate to the top and bottom of the lane.

This function draws `lines` with `color` and `thickness`.
Lines are drawn on the image inplace(mutates the image).
If you want to make the lines semi - transparent, think about combining
this function with the weighted_img() function below
*/

// In case of error, don't draw the line(s)
bool draw_right = true;
bool draw_left = true;
int width = img_line.cols;
int height = img_line.rows;


//Find slopes of all lines
//But only care about lines where abs(slope) > slope_threshold
float slope_threshold = 0.5;
vector<float> slopes;
vector<Vec4i> new_lines;

for (int i = 0; i < lines.size(); i++)
{
Vec4i line = lines[i];
int x1 = line[0];
int y1 = line[1];
int x2 = line[2];
int y2 = line[3];


float slope;
//Calculate slope
if (x2 - x1 == 0) //corner case, avoiding division by 0
slope = 999.0; //practically infinite slope
else
slope = (y2 - y1) / (float)(x2 - x1);


//Filter lines based on slope
if (abs(slope) > slope_threshold) {
slopes.push_back(slope);
new_lines.push_back(line);
}
}



// Split lines into right_lines and left_lines, representing the right and left lane lines
// Right / left lane lines must have positive / negative slope, and be on the right / left half of the image
vector<Vec4i> right_lines;
vector<Vec4i> left_lines;

for (int i = 0; i < new_lines.size(); i++)
{

Vec4i line = new_lines[i];
float slope = slopes[i];

int x1 = line[0];
int y1 = line[1];
int x2 = line[2];
int y2 = line[3];


float cx = width * 0.5; //x coordinate of center of image

if (slope > 0 && x1 > cx && x2 > cx)
right_lines.push_back(line);
else if (slope < 0 && x1 < cx && x2 < cx)
left_lines.push_back(line);
}


//Run linear regression to find best fit line for right and left lane lines
//Right lane lines
double right_lines_x[1000];
double right_lines_y[1000];
float right_m, right_b;


int right_index = 0;
for (int i = 0; i < right_lines.size(); i++) {

Vec4i line = right_lines[i];
int x1 = line[0];
int y1 = line[1];
int x2 = line[2];
int y2 = line[3];

right_lines_x[right_index] = x1;
right_lines_y[right_index] = y1;
right_index++;
right_lines_x[right_index] = x2;
right_lines_y[right_index] = y2;
right_index++;
}


if (right_index > 0) {

double c0, c1, cov00, cov01, cov11, sumsq;
gsl_fit_linear(right_lines_x, 1, right_lines_y, 1, right_index,
&c0, &c1, &cov00, &cov01, &cov11, &sumsq);

//printf("# best fit: Y = %g + %g X\n", c0, c1);

right_m = c1;
right_b = c0;
}
else {
right_m = right_b = 1;

draw_right = false;
}



// Left lane lines
double left_lines_x[1000];
double left_lines_y[1000];
float left_m, left_b;

int left_index = 0;
for (int i = 0; i < left_lines.size(); i++) {

Vec4i line = left_lines[i];
int x1 = line[0];
int y1 = line[1];
int x2 = line[2];
int y2 = line[3];

left_lines_x[left_index] = x1;
left_lines_y[left_index] = y1;
left_index++;
left_lines_x[left_index] = x2;
left_lines_y[left_index] = y2;
left_index++;
}


if (left_index > 0) {
double c0, c1, cov00, cov01, cov11, sumsq;
gsl_fit_linear(left_lines_x, 1, left_lines_y, 1, left_index,
&c0, &c1, &cov00, &cov01, &cov11, &sumsq);

//printf("# best fit: Y = %g + %g X\n", c0, c1);

left_m = c1;
left_b = c0;
}
else {
left_m = left_b = 1;

draw_left = false;
}



//Find 2 end points for right and left lines, used for drawing the line
//y = m*x + b--> x = (y - b) / m
int y1 = height;
int y2 = height * (1 - trap_height);

float right_x1 = (y1 - right_b) / right_m;
float right_x2 = (y2 - right_b) / right_m;

float left_x1 = (y1 - left_b) / left_m;
float left_x2 = (y2 - left_b) / left_m;


//Convert calculated end points from float to int
y1 = int(y1);
y2 = int(y2);
right_x1 = int(right_x1);
right_x2 = int(right_x2);
left_x1 = int(left_x1);
left_x2 = int(left_x2);


//Draw the right and left lines on image
if (draw_right)
line(img_line, Point(right_x1, y1), Point(right_x2, y2), Scalar(255, 0, 0), 10);
if (draw_left)
line(img_line, Point(left_x1, y1), Point(left_x2, y2), Scalar(255, 0, 0), 10);

}



int main(int, char**)
{
char buf[256];
Mat img_bgr, img_gray, img_edges, img_hough, img_annotated;


VideoCapture videoCapture("challenge.mp4");

if (!videoCapture.isOpened())
{
cout << "동영상 파일을 열수 없습니다. \n" << endl;

char a;
cin >> a;

return 1;
}



videoCapture.read(img_bgr);
if (img_bgr.empty()) return -1;

VideoWriter writer;
int codec = VideoWriter::fourcc('X', 'V', 'I', 'D');  // select desired codec (must be available at runtime)
double fps = 25.0;                          // framerate of the created video stream
string filename = "./live.avi";             // name of the output video file
writer.open(filename, codec, fps, img_bgr.size(), CV_8UC3);
// check if we succeeded
if (!writer.isOpened()) {
cerr << "Could not open the output video file for write\n";
return -1;
}


videoCapture.read(img_bgr);
int width = img_bgr.size().width;
int height = img_bgr.size().height;

int count = 0;

while (1)
{

//1. 원본 영상을 읽어옴
videoCapture.read(img_bgr);
if (img_bgr.empty()) break;


//2. 미리 정해둔 흰색, 노란색 범위 내에 있는 부분만 차선후보로 따로 저장함
Mat img_filtered;
filter_colors(img_bgr, img_filtered);

//3. 그레이스케일 영상으로 변환하여 에지 성분을 추출
cvtColor(img_filtered, img_gray, COLOR_BGR2GRAY);
GaussianBlur(img_gray, img_gray, Size(3, 3), 0, 0);
Canny(img_gray, img_edges, 50, 150);



int width = img_filtered.cols;
int height = img_filtered.rows;


Point points[4];
points[0] = Point((width * (1 - trap_bottom_width)) / 2, height);
points[1] = Point((width * (1 - trap_top_width)) / 2, height - height * trap_height);
points[2] = Point(width - (width * (1 - trap_top_width)) / 2, height - height * trap_height);
points[3] = Point(width - (width * (1 - trap_bottom_width)) / 2, height);


//4. 차선 검출할 영역을 제한함(진행방향 바닥에 존재하는 차선으로 한정)
img_edges = region_of_interest(img_edges, points);


UMat uImage_edges;
img_edges.copyTo(uImage_edges);

//5. 직선 성분을 추출(각 직선의 시작좌표와 끝좌표를 계산함)
vector<Vec4i> lines;
HoughLinesP(uImage_edges, lines, rho, theta, hough_threshold, minLineLength, maxLineGap);




//6. 5번에서 추출한 직선성분으로부터 좌우 차선에 있을 가능성있는 직선들만 따로 뽑아서
//좌우 각각 하나씩 직선을 계산함 (Linear Least-Squares Fitting)
Mat img_line = Mat::zeros(img_bgr.rows, img_bgr.cols, CV_8UC3);
draw_line(img_line, lines);




//7. 원본 영상에 6번의 직선을 같이 보여줌
addWeighted(img_bgr, 0.8, img_line, 1.0, 0.0, img_annotated);


//8. 결과를 동영상 파일로 기록
writer << img_annotated;

count++;
if (count == 10) imwrite("img_annota1ted.jpg", img_annotated);

//9. 결과를 화면에 보여줌
Mat img_result;
resize(img_annotated, img_annotated, Size(width*0.7, height*0.7));
resize(img_edges, img_edges, Size(width*0.7, height*0.7));
cvtColor(img_edges, img_edges, COLOR_GRAY2BGR);
hconcat(img_edges, img_annotated, img_result);
imshow("차선 영상", img_result);




if (waitKey(1) == 27) break; //ESC키 누르면 종료  
}


return 0;
}




반응형

포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
질문을 남겨주면 가능한 빨리 답변드립니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 ~~

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요 ^^

  1. 이전 댓글 더보기
  2. 혜윤 2019.07.03 13:27

    main함수에 <8.결과를 동영상 파일로 기록> 여기
    writer<< img_annotated; 이 코드에서

    '이러한 피연산자와 일치하는 "<<"연산자가 없습니다.'
    '이항'<<':왼쪽 피연산자로 'cv::VideoCapture'형식을 사용하는 연산자가 없거나 허용되는 변환이 없습니다.'

    라는 두 오류가 뜨는데 어떻게 고쳐야할지 모르겠습니다 ㅠ
    현재 opencv, visual studio, gsl 모두 연동시켰고 딱 저 부분만 오류가 뜨는상황입니다 도와주세요 ㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.03 14:39 신고

      다음 코드로 변경해서 해보세요.

      writer.write(img_annotated);7

    • 혜윤 2019.07.03 14:46

      답변감사합니다 해결되었어요!!ㅎㅎ

  3. 혜윤 2019.07.03 15:45

    실행해보려하는데 영상이 3초에 1mm정도 움직이듯 느리게 움직입니다 ㅠ
    다른 댓글들 볼니 opencv를 활성화하라하시는데 어떻게하는 건가요 ??
    그리고 저는 리눅스전혀안쓰고 visual이서만 진행하고있습니다!

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.03 23:55 신고

      다음 포스트처럼 OpenCL을 활성화 할 수 있습니다.
      하지만 윈도우에서는 제대로 활성화가 안되는 경우가 있는데 원인을 잘 모르겠네요
      아래 포스트로 테스트해서 동작하는지 확인하고나서 차선인식 코드에 적용해보세요. 이미 OpenCL을 위한 코드 수정은 되있는 상태입니다.

      OpenCL 사용 여부에 따른 OpenCV 성능 비교 테스트
      https://webnautes.tistory.com/1287

    • 혜윤 2019.07.04 17:35

      이 포스트에 있는 차선인식 코드에
      #include <opencv2/core/ocl.hpp>이 해더를 추가 한 후
      메인문에
      if (!ocl::haveOpenCL()) {
      cout << "에러 : OpenCL을 사용할 수 없는 시스템입니다." << endl;
      return -1;
      }

      이 코드와
      ocl::setUseOpenCL(true);
      이 코드를 추가했습니다.

      하지만 결과는 여전히 느린데 혹시 빠트린게 있거나 코드 추가위치가 다른건가요 ??ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.04 18:10 신고

      윈도우 환경에서는 OpenCL을 동작시키는 부분이 확실치 않습니다.

  4. 혜윤 2019.07.03 16:56

    그리구 메인문에 2단계인 색 판별하는 순서부터
    하늘 부분의 색만 판단하고 도로부분의 색은 판단하지 못하는데 어떻게 해야할까요 ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.03 23:57 신고

      테스트 영상외에 다른 영상을 사용하면 제대로 동작안할 수 있습니다.
      {다른 영상을 사용한게 맞나요? )

      이 경우 코드에서 다음 부분을 수정해줘야 합니다.

      //차선 색깔 범위
      Scalar lower_white = Scalar(200, 200, 200); //흰색 차선 (RGB)
      Scalar upper_white = Scalar(255, 255, 255);
      Scalar lower_yellow = Scalar(10, 100, 100); //노란색 차선 (HSV)
      Scalar upper_yellow = Scalar(40, 255, 255);

    • 혜윤 2019.07.04 17:37

      다른 영상을 사용한게 맞습니다!
      안그래도 영상이 다르니 차선색범위를 바꿔줘야할거 같아서 변경했는데 맞는지도 모르겠고 어떻게 바꿔도 결과에 큰 차이가 없더라구요 ㅠㅠㅠ 바꿔도 큰 차이가 없거나하면 어떻게해야할까요
      아니면 혹시 테스트하신 영상을 제가 다운 받을 수 있을까요 ?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.04 18:08 신고

      다음 링크에서 다운로드 받으세요

      https://github.com/georgesung/road_lane_line_detection

  5. wqeqwe 2019.07.04 11:10

    ubuntu 16.04에서 작업하고 있고 알려주신 데로 컴파일 하고 실행시켰으나
    화면에 작은 검은색 이미지만 출력됩니다. 어떤게 문제인 지 모르겠네요..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.04 12:32 신고

      단계별로 imshow함수를 사용하여 출력해보세요.

      그리고 혹 포스트에서 테스트에 사용한 영상이 아니라면 위에 댓글로 언급한 것처럼 색 범위를 재정의해줘야 합니다.

  6. 혜윤 2019.07.08 09:45

    ROI구역 설정할 때 사다리꼴이 영상 밑변부터 딱맞춰서 시작설정되던데
    차에서 도로 영상을 찍을 때 차의 앞 범퍼?라해야되나 차가 영상 하단에 잡힐 경우를 대비해서
    사다리꼴의 밑변을 영상 밑변에서 몇센치정도 띄워서 ROI구역을 지정하려면 어떻게해야하나요 ??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.08 11:47 신고

      다음처럼 하면 offset에 지정한 픽셀만큼
      아래 빈공간을 두게됩니다.

      int offset = 50;
      points[0] = Point((width * (1 - trap_bottom_width)) / 2, height-offset);
      points[3] = Point(width - (width * (1 - trap_bottom_width)) / 2, height-offset);

    • 혜윤 2019.07.08 16:37

      감사합니다 잘되네요!!
      그럼 혹시 좌우 공간을 다르게 하려면 어떻게해야하나요 ??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.08 17:14 신고

      다음 두 변수값을 바꾸면서
      points[1] = Point((width * (1 - trap_top_width)) / 2, height - height * trap_height);
      points[2] = Point(width - (width * (1 - trap_top_width)) / 2, height - height * trap_height);


      region_of_interest 함수의 img_mask를 imshow로 출력해보세요.

      Mat region_of_interest(Mat img_edges, Point *points)
      {
      /*
      Applies an image mask.

      Only keeps the region of the image defined by the polygon
      formed from `vertices`. The rest of the image is set to black.
      */

      Mat img_mask = Mat::zeros(img_edges.rows, img_edges.cols, CV_8UC1);


      Scalar ignore_mask_color = Scalar(255, 255, 255);
      const Point* ppt[1] = { points };
      int npt[] = { 4 };


      //filling pixels inside the polygon defined by "vertices" with the fill color
      fillPoly(img_mask, ppt, npt, 1, Scalar(255, 255, 255), LINE_8);

  7. kimt 2019.07.18 18:33

    라즈베리파이환경에서 구현을 해보려는 학생입니다.
    nano 편집기 에서 사용할경우 gls 라이브러리를 추가해야하나요? 그리고 nano편집기에서 위에 소스를 그대로 복사해서 사용해도 작동하나요?
    opencv는 4.1.0을 사용하고있습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.18 18:47 신고

      코드 상단에 있는 gls 설치 방법대로 진행 하면 됩니다.

      gls와 opencv만 설치하면 동작합니다.

    • kimt 2019.07.18 18:52

      위의 링크에서는 비주얼스튜디오에서 진행하는데 비주얼스튜디오 없이도 설치가능한가요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.18 19:19 신고

      gls 설치를 말하시는 건가요?

      리눅스에선 다음처럼 설치하면 됩니다.

      sudo apt-get install libgsl-dev

    • kimt 2019.07.25 16:20

      Package opencv was not found in the pkg_config search path.
      Perhaps you should add the directory containing "opencv.pc"
      to the PKG_CONFIG_PATH ecvironment variable
      No package "opencv" found
      g++ error: lcblas : 그런파일이나 디렉터리가 없습니다.
      이러한 오류가나네요..
      혹시 어디서 실행해야 하며 libgsl-dev는 어디에 설치가 되나요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.25 18:03 신고

      opencv.pc가 없는 것으로봐서는 pkg-config를 지원하도록 opencv가 컴파일되지 않은듯 싶습니다.

      cmake시 다음 옵션이 추가되어 있어야 합니다.
      -D OPENCV_GENERATE_PKGCONFIG=ON


      포스트에 나온대로 다음처럼하면 컴파일에 필요한 opencv, gsl, cblas를 알아서 찾아서 사용하게 됩니다.

      g++ main.cpp -o main $(pkg-config opencv --libs --cflags) -lgsl -lcblas

    • kimt 2019.07.25 18:48

      그러면 opencv를 다시설치해야하는건가요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.25 18:54 신고

      혹시 어떤 자료를 보고 OpenCV 컴파일을 진행하셨나요?

      다음 명령시 에러가 난다면 cmake를 사용하여 작성하신 코드를 컴파일해야 할듯합니다.
      pkg-config opencv --libs --cflags

      또는 OpenCV를 다시 컴파일해야합니다.

    • kimt 2019.07.25 19:55

      앞에 써주신 opencv설치 방법그대로 하다가 설치확인부분 5번부분을 안했습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.25 20:16 신고

      아.. 원인을 찾은 듯합니다.

      컴파일시 아래 옵션 대신에
      $(pkg-config opencv --libs --cflags)

      아래 옵션을 사용하세요.
      $(pkg-config opencv4 --libs --cflags)

      차이는 opencv 대신에 opencv4를 사용하는 겁니다


      OpenCV 4.0 이상부터 opencv4로 해줘야 합니다.

  8. kimt 2019.09.16 21:20

    #include <opencv2/opencv.hpp>
    #include <opencv2/imgproc.hpp>
    opencv2에 두 파일이 없는데 어떻게 해야하나요??
    /home/pi/opencv/opencv-4.1.0/build/opencv2 경로에서 확인했습니다.
    g++ 하려고하니 계속 페이탈 에러가 뜨네요 도움부탁드립니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.16 22:59 신고

      OpenCV 빌드하여 설치하면 다음 위치에 존재하게 됩니다.

      /usr/local/include/opencv4/opencv2/

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.16 23:00 신고

      opencv 4.1.0을 사용중이라면 다음처럼해야 컴파일이 될겁니다.

      g++ main.cpp -o main $(pkg-config opencv4 --libs --cflags) -lgsl -lcblas

    • kimt 2019.09.17 02:35

      동영상파일은 어디서 다운받을수있나요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.17 05:35 신고

      원본코드가 있는 깃허브에 있습니다. 주소는 소스코드에 있습니다

  9. RRP 2019.10.15 02:32

    파란색선 두줄이 검출되는 중간에 가운데 선을 하나 draw하려고하는데 right_x1, right_x2, left_x1,left_x2를 출력해봤더니 1or 0이란 값이나오는데 이게 좌표값아닌가요?? 혹시 좌표값은 어디서 확인하며 가운데에 선을 draw하려면 어떻게하는지 알려주실수있나요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.15 12:45 신고

      아래 부분이 좌우선을 그리는 부분입니다.

      if (draw_right)
      line(img_line, Point(right_x1, y1), Point(right_x2, y2), Scalar(255, 0, 0), 10);
      if (draw_left)
      line(img_line, Point(left_x1, y1), Point(left_x2, y2), Scalar(255, 0, 0), 10);

    • RRP 2019.10.15 23:40

      소스보고 그부분인거 같아서 그부분처럼
      if(draw_right && draw_left)
      line(img_line,point(right_x1-left_x1,y1-10),point(right_x1-left_x1,y1+10),Scalar(255, 0, 0), 10);
      이런식으로 작성했는데 y축은 맞게 잘나오는데 x축의 경우 왼쪽에 붙어있길래 그 값을 printf로 출력해보니 right_x1는 값이 1이나오고 left_x1은 0이 나오네요..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.16 00:43 신고

      직선의 시작좌표와 끝좌표에 색을 다르게 해서 원을 그려서 확인해보는게 좋을듯합니다.

  10. yoon 2019.11.12 20:44

    안녕하세요 이 코드를 응용해서 다른것을 해보고 있습니다. 하다가
    [ INFO:0] VIDEOIO: Enabled backends(6, sorted by priority): FFMPEG(1000); GSTREAMER(990); V4L2(980); CV_IMAGES(970); CV_MJPEG(960); XINE(950)
    OpenCV | GStreamer warning: Cannot query video position: status=0 value=-1 duration=-1
    (/home/pi/opencv/opencv-3.4.3/modules/videoio/src/cap_gstreamer.cpp:936)
    Traceback (most recent call last):
    File "opencv_test39.py", line 20, in <module>
    contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    ValueError: too many values to unpack
    이러한 오류가 나오게 되었는데 어떤식으로 고쳐야 할까요..?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.12 22:18 신고

      OpenCV 버전 차이 때문에 발생한 에러입니다.

      포스트에선 OpenCV 4.x를 사용하고 있습니다.


      OpenCV 3.x라면 다음처럼 수정하세요.

      _, contours, hierarchy =cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    • yoon 2019.11.13 17:10

      Thank you!!!

  11. 잇쇼매 2019.11.21 09:51

    혹시 위에 나온 ROI부분 자세히 설명해주실수 있나요ㅠ?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.21 21:29 신고

      포스트의 region_of_interest 함수부분에 있는 이미지처럼 차선을 검출한 영역을 사다리꼴로 제한한 것입니다.

  12. 주행차 2019.11.30 16:59

    라즈베리파이를 사용하여 차선인식 자율주행차를 만드려고 하는데 캐니 엣지 후 허프 변환과정까지 마친다음 그 차선의 데이터를 바탕으로 조향을 하려고 합니다 그 방법은 roi 상의 특정 y 값에대한 차선의 x값이 양옆 차선 2개씩 각각 2개가 나오는데 그 x 값의 움직임을 통해 조향을 하려고 합니다
    그런데 그 특정 y 값에 대한 차선의 x값을 어떻게 구해야하는지 모르겠습니다 어떻게 그 좌표값을 구하는지 아시나요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.12.01 18:15 신고

      양쪽 차선에서 검출되는 2개의 선이 만나는 교차점을 구하면 될듯 합니다. 소실점(vanishing point)이라고 부릅니다.

  13. BlogIcon 주행차 2019.12.02 20:15

    질문을 바꾸어보겠습니다 한점에서 만나는 서로 다른 두 직선을 이미지 상에 그려져있을때 그 교점의 좌표값을 어떻게 구하는지가 궁금합니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.12.02 22:57 신고

      다시 생각해보니 소실점을 구할 필요가 없을 듯합니다.

      왼쪽 차선과 오른쪽 차선의 기울기를 측정하는게 어떨까 싶네요.

  14. 주행차 2019.12.25 17:09

    허프라인을 검출하고 그 라인의 방정식을 알 수 있을까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.12.25 17:14 신고

      허프라인 검출후 직선을 그리는 부분을 참고하면 직선의 방정식을 유도할수 있습니다

  15. 플래쉬 2020.01.04 14:27

    gsl_fit_linear를 대신할 수 있는 다른 함수가 opencv에 있나요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.01.04 16:21 신고

      opencv에는 없습니다.

    • 플래쉬 2020.01.09 15:11

      이 코드를 실행시키고 난 후에 곡선 차선인식을 시도하려고 합니다. 곡선 차선 인식에 대해 공부를 하고 코드르 찾고 있는데 마땅한 코드가 없고 직접 코드를 짜려고 했는데 sliding window 부분에서 막혔습니다. 혹시 곡선에 대한 글을 작성 할 계획이 있으신가요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.01.09 20:54 신고

      아직 없습니다...

  16. hy 2020.02.25 17:36

    안녕하세요
    왜 흰색은 rgb로 검출하고 노란색은 hsv로 검출한지 알 수 있을까요?
    그리고 각각의 값들을 저 수치로 설정한 이유는 무엇인가요?
    처음 접하는중이라 모르는게 많네요

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.02.25 18:02 신고

      hsv색공간의 hue성분에 흰색 범위가 없기 때문입니다. 노란색은 있어서 hsv로 검출합니다

  17. ㅇㅇ 2020.04.08 20:41

    좌우 차선에 있을 가능성 있는 직선들만 따로 뽑는다는게 무엇을 의미 하나요?
    그리고 최소제곱해를 쓰신거 같은데 라인 피팅 하는 부분이 어딘지 못찾겠네요..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.09 13:16 신고

      gsl_fit_linear 함수를 사용하여 좌우에서 에지를 하나씩 뽑습니다.

  18. 안녕하세요 2020.05.14 16:21

    위 코드를 동영상이 아닌 이미지 테스트할 떄, 고쳐야하는 부분 좀 알 수 있을까요?? 자꾸 에러가 나네요 ㅜㅜ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.14 22:46 신고

      다음 두 포스트에 있는 코드를 비교해서 이미지 다룰때랑 동영상 다룰때 차이를 확인해보세요

      https://webnautes.tistory.com/1350

      https://webnautes.tistory.com/1351

    • 안녕하세요 2020.06.09 22:29

      포스트를 참고하여 이 글의 코드에는 적용이 되는데, 만약 동영상 파일을 이미지 파일로 대체할 때 while(1) { cap.read(frame) ~ 에서
      while 문 안에 관련 함수들이 있다면 어떻게 처리를 해야 하나요? 단순히 while { }만 제거해주면 되는건가요?

  19. 별따닥이 2020.06.01 03:00

    안녕하세요. 항상 좋은 글 감사합니다!
    다름이 아니라 제가 이 코드를 올려주신대로 작성하고 실행했을 때 abort() has been called라는 문구로
    debug error가 발생했습니다. 혹시 이 문제에 대해서 해결방법을 알고 계시다면 알려주시면 정말 감사드리겠습니다 ㅠㅠ..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.06.01 06:57 신고

      올려주신 에러 메시지 보고 원인을 알기 함들듯합니다

    • 별따닥이 2020.06.01 10:27

      그렇군요 ㅠㅠ.. 답변 감사합니다!

  20. Favicon of https://jeep2u.tistory.com BlogIcon 질풍서림 2020.10.23 19:02 신고

    안드로이드에서 실행시키려면 어떻게 해야하는지 궁금합니다.

    webnautes 강의 중 ndk, cmake를 보고왔는데 두개를 결합해서 구동해보고 싶습니다.

    대충 방법이라도 알려주시면 찾아보고 해보겠습니다..^^

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.10.23 19:05 신고

      각각.독립적인 빌드 시스템이라 같이 사용 못할거.같습니다. 다른분이 시도한적이 있는데 성공하셨나 모르겠군요. 두 포스트에 댓글을 남기셨습니다

  21. Favicon of https://membejoin.tistory.com BlogIcon 초보지만열심히 2021.02.02 21:48 신고

    제가 폴더를 생성하고 c++노드를 만들어 내용을 복사 붙여 넣기를 한 뒤, 컴파일을 하니
    line_check.cpp:(.text._ZN2cv6StringC2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE[_ZN2cv6StringC5ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE]+0x69): undefined reference to `cv::String::allocate(unsigned long)'
    collect2: error: ld returned 1 exit status
    line_d/CMakeFiles/line_check.dir/build.make:112: recipe for target '/home/user/catkin_ws/devel/lib/line_d/line_check' failed
    make[2]: *** [/home/user/catkin_ws/devel/lib/line_d/line_check] Error 1
    CMakeFiles/Makefile2:1831: recipe for target 'line_d/CMakeFiles/line_check.dir/all' failed
    make[1]: *** [line_d/CMakeFiles/line_check.dir/all] Error 2
    make[1]: *** 끝나지 않은 작업을 기다리고 있습니다....

    이런 오류가 뜨게 되던데 혹시 어떤 방식으로 오류를 해결해야 할까요..

+ Recent posts