반응형

이전 포스팅에서 다루었던 이미지에서 도형을 검출하는 예제 코드를 웹캠 기반으로 수정하여 테스트해 본 과정을 설명합니다.



OpenCV 사용해서 실시간으로 도형 검출하기(shape detection) 1 / 2 -  이미지에서 검출

http://webnautes.tistory.com/1097


OpenCV 사용해서 실시간으로 도형 검출하기(shape detection) 2 / 2 -  웹캠에서 검출




2016. 12. 29   최초 작성

2018.   6. 30 도형 판정하는 방법 변경(  내각 체크 → Convex polygon 여부 검사 )

                      webcam으로 테스트 추가




프린트한 이미지에 있는 도형들을 검출해본 테스트  결과입니다.












이전 포스팅에서 사용했던 도형 검출 코드를 수정하여  웹캠에서 가져온 이미지를 사용하도록 했습니다.

다음 부분들이 변경되었습니다.


  • 이진화 방법을 adaptiveThreshold 함수로 변경

  • GaussianBlur 함수 추가

  • 노란색과 연두색 도형이 인식이 잘안되어 색깔 변경


parameter 1,  2, 3은 테스트시 이 값들을 조정하면 더 좋은 결과를 얻을 수 있을 거 같아 추가한 주석입니다.



웹캠으로부터 영상가져오는 기본 코드는 아래 소스 코드를 참고하였습니다.

https://github.com/opencv/opencv/blob/master/samples/cpp/videocapture_basic.cpp



#include "opencv2/opencv.hpp"
#include <iostream>  
#include <string>


using namespace cv;
using namespace std;



//Contour 영역 내에 텍스트 쓰기
//https://github.com/bsdnoobz/opencv-code/blob/master/shape-detect.cpp
void setLabel(Mat& image, string str, vector<Point> contour)
{
int fontface = FONT_HERSHEY_SIMPLEX;
double scale = 0.5;
int thickness = 1;
int baseline = 0;

Size text = getTextSize(str, fontface, scale, thickness, &baseline);
Rect r = boundingRect(contour);

Point pt(r.x + ((r.width - text.width) / 2), r.y + ((r.height + text.height) / 2));
rectangle(image, pt + Point(0, baseline), pt + Point(text.width, -text.height), CV_RGB(200, 200, 200), FILLED);
putText(image, str, pt, fontface, scale, CV_RGB(0, 0, 0), thickness, 8);
}



int main(int, char**)
{
Mat img_input, img_result, img_gray;


//--- INITIALIZE VIDEOCAPTURE
VideoCapture cap;
// open the default camera using default API
// cap.open(0);
// OR advance usage: select any API backend
int deviceID = 0;             // 0 = open default camera
int apiID = cv::CAP_ANY;      // 0 = autodetect default API
     // open selected camera using selected API
cap.open(deviceID + apiID);
// check if we succeeded
if (!cap.isOpened()) {
cerr << "ERROR! Unable to open camera\n";
return -1;
}



for (;;)
{

cap.read(img_input);
// check if we succeeded
if (img_input.empty()) {
cerr << "ERROR! blank frame grabbed\n";
break;
}


Mat img_temp;
//그레이스케일 이미지로 변환  
cvtColor(img_input, img_temp, COLOR_BGR2GRAY);
GaussianBlur(img_temp, img_temp, Size(5, 5), 0, 0);   


//이진화 이미지로 변환
adaptiveThreshold(img_temp, img_gray,
255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV,
201, 7);                                  // <-----------------------   parameter 1, 2


//contour를 찾는다.
vector<vector<Point> > contours;
findContours(img_gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);

//contour를 근사화한다.
vector<Point2f> approx;
img_result = img_input.clone();

for (size_t i = 0; i < contours.size(); i++)
{
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);

if (fabs(contourArea(Mat(approx))) > 1000 && fabs(contourArea(Mat(approx)))< 10000)  // <-----------------------   parameter 3
{

int size = approx.size();
const char *name[]= { "none", "none", "none", "triangle", "quadrangle", "pentagon", "hexagon", "heptagon", "octagon", "nonagon", "decagon"  };

switch (size){

case 3: case 4: case 5:
case 6: case 10:
if (isContourConvex(Mat(approx))) { // convex 인지 검사


//Contour를 근사화한 직선을 그린다.
if (size % 2 == 0) {
line(img_result, approx[0], approx[approx.size() - 1], Scalar(0, 255, 0), 3);

for (int k = 0; k < size - 1; k++)
line(img_result, approx[k], approx[k + 1], Scalar(0, 255, 0), 3);

for (int k = 0; k < size; k++)
circle(img_result, approx[k], 3, Scalar(0, 0, 255));
}
else {
line(img_result, approx[0], approx[approx.size() - 1], Scalar(0, 255, 0), 3);

for (int k = 0; k < size - 1; k++)
line(img_result, approx[k], approx[k + 1], Scalar(0, 255, 0), 3);

for (int k = 0; k < size; k++)
circle(img_result, approx[k], 3, Scalar(0, 0, 255));
}

//검출된 도형에 대한 라벨을 출력한다.

setLabel(img_result, name[size], contours[i]);
}

break;



deafult:

break;
}


}



}


// show window and wait for a key with timeout long enough to show images
imshow("result", img_result);
imshow("binary", img_gray);

if (waitKey(5) == 27) //ESC 키 누를 때 까지 대기
break;

}

return 0;
}





반응형

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

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

유튜브 구독하기


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

  1. Jay 2019.01.14 13:38

    감사합니다!

    혹시 파이썬 코드도 어디서 볼수있는곳이 있을까요?

  2. 2019.03.10 17:36

    비밀댓글입니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.03.10 17:39 신고

      Hough Circle Transform을 하면 됩니다.
      다음 글을 참고하세요

      https://webnautes.tistory.com/949

  3. OpenCV_동동 2019.03.16 18:07

    혹시 Grayscale로 변환을 하고 Binarization으로 변환을 하는 이유가 있을까요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.03.16 18:21 신고

      이진화 함수들이 하나의 채널을 가진 이미지를 요구하기 때문입니다.

  4. soo 2019.05.10 15:40

    혹시 신호등 좌회전인식을하려고하는데 어떤 방향으로 접근을 해야될까요? 좌회전이 생각보다 어려워서.....

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

      컨투어를 검출하여 특징을 추출해야 할 듯합니다.

      컨투어 구성 규칙을 찾아서 하면 어떨까 싶네요.

  5. 2019.06.02 17:31

    비밀댓글입니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.03 00:36 신고

      근사화된 도형은 아래 변수에 이미 저장되어 있습니다.

      vector<Point2f> approx;

    • 2019.06.03 01:30

      비밀댓글입니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.03 17:46 신고

      1. OpenCV 함수에서 바이너리 영상을 요구하기 때문입니다.

      2. 다음 코드에서 approx에 따라 name에 미리 입력해놓은 도형 이름을 가져와 화면에 보여주고 있습니다.

      setLabel(img_result, name[size], contours[i]);


      3. 짝수각형때 근사화된 컨투어 그리는 방법과

      if (size % 2 == 0)

      아닐떄 그리는 방법을 구분해서 그려야 할거 같아서 그랬습니다.

      drawContours() 함수를 사용하는 좀더 간단한 방법이 있긴합니다.

+ Recent posts