C# OpenCV 강좌 : 제 16강 - 윤곽선 검출

C# OpenCV4 Contours Detection

C# OpenCV 강좌 : 제 16강 - 윤곽선 검출
[ C#-OpenCvSharp4 ] - 윤대희

윤곽선 검출(Contours Detection)


1

가장자리 검출(Edge Detection) 함수는 입력 이미지에서 가장자리를 검출해 이미지로 반환했습니다.

하지만, 가장자리 검출 알고리즘은 검출된 객체들의 세그먼트(Segment) 구성 요소가 구분돼 있지 않아 어떤 형태인지 알 수 없었습니다.

윤곽선 검출(Contour Detection) 알고리즘은 전처리가 진행된 이미지에서 가장자리로 검출된 픽셀을 대상으로 세그먼테이션(Segmentation) 작업을 진행합니다.

그러므로, 윤곽선 검출 알고리즘은 검출된 객체들을 값(Value)으로 반환해 사용할 수 있습니다.

검출된 윤곽선은 형상의 분석과 물체 감지 및 인식에 가장 효과적인 방법 중 하나입니다.


  • Tip : 세그먼트(Segment)서로 다른 두 점을 연결하는 가장 짧은 선을 의미합니다.
  • Tip : 세그먼테이션(Segmentation)이란 이미지에서 각각의 픽셀들을 분류해 그룹화하는 것을 의미합니다.



Main Code


using System;
using OpenCvSharp;
using System.Collections.Generic;

namespace Project
{
    class Program
    {
        static void Main(string[] args)
        {
            Mat src = new Mat("hex.jpg");
            Mat yellow = new Mat();
            Mat dst = src.Clone();

            Point[][] contours;
            HierarchyIndex[] hierarchy;

            Cv2.InRange(src, new Scalar(0, 127, 127), new Scalar(100, 255, 255), yellow);
            Cv2.FindContours(yellow, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxTC89KCOS);

            List<Point[]> new_contours = new List<Point[]>();
            foreach (Point[] p in contours)
            {
                double length = Cv2.ArcLength(p, true);
                if ( length > 100)
                {
                    new_contours.Add(p);
                }
            }

            Cv2.DrawContours(dst, new_contours, -1, new Scalar(255, 0, 0), 2, LineTypes.AntiAlias, null, 1);
            Cv2.ImShow("dst", dst);
            Cv2.WaitKey(0);
        }   
    }
}



Detailed Code


Mat src = new Mat("hex.jpg");
Mat yellow = new Mat();
Mat dst = src.Clone();


new Mat을 사용해 이미지를 src에 할당합니다.

전처리 결과를 저장할 yellow를 선언합니다.

연산 결과를 저장할 dst를 선언합니다.



Point[][] contours;
HierarchyIndex[] hierarchy;

윤곽선 검출 알고리즘은 윤곽선의 실제 값이 저장될 contours와 그 윤곽선들의 계층 구조를 저장할 hierarchy를 선언합니다.

contoursPoint 형식의 2차원 배열이며, hierarchyHierarchyIndex 형식의 1차원 배열입니다.

contours의 차원 구조는 점 좌표(x, y)의 묶음과, 그 좌표들을 한 번 더 묶는 구조입니다.

좌표를 저장하기 위해서 Point 형식이며, 좌표들을 하나로 묶어 윤곽선을 구성하기 위해 Point[]가 됩니다.

이후, 윤곽선은 n개 이상 발생할 수 있으므로, Point[]를 묶는 Point[][]가 됩니다.

hierarchy에는 현재 노드(Node)의 정보가 담겨있습니다.

다음 윤곽선, 이전 윤곽선, 자식 노드, 부모 노드가 담겨있습니다.

이 정보를 담기 위해 1차원 배열이 됩니다.


  • Tip : 자식 노드란 자기 자신 안쪽에 있는 윤곽선을 지칭합니다.
  • Tip : 부모 노드란 자기 자신 바깥쪽에 있는 윤곽선을 지칭합니다.



Cv2.InRange(src, new Scalar(0, 127, 127), new Scalar(100, 255, 255), yellow);
Cv2.FindContours(yellow, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxTC89KCOS);

연산량을 줄이고 정확성을 높이기 위해 간단한 전처리(배열 요소의 범위 설정 함수)를 적용합니다.

윤곽선 검출 함수(Cv2.FindContours)객체의 구조를 판단하는 데 가장 많이 사용되는 알고리즘입니다.

Cv2.FindContours(원본 배열, 검출된 윤곽선, 계층 구조, 검색 방법, 근사 방법, 오프셋)로 윤곽선 검출을 적용합니다.

검출된 윤곽선out 키워드를 활용해 함수에서 검출된 윤곽선을 저장합니다.

계층 구조out 키워드를 활용해 함수에서 검출된 계층구조를 저장합니다.

검색 방법은 윤곽선을 검출해 어떤 계층 구조의 형태를 사용할지 설정합니다.

근사 방법은 윤곽점의 근사법을 설정합니다. 근사 방법에 따라 검출된 윤곽선(contours)에 포함될 좌표의 수정교함의 수준이 달라집니다.

오프셋은 반환된 윤곽점들의 좌푯값에 이동할 값을 설정합니다. 일반적으로 잘 사용하지 않습니다.



List<Point[]> new_contours = new List<Point[]>();

간단하게 불필요한 윤곽선을 제거하기 위해, List 형식의 Point[] 배열을 선언합니다.

List를 사용하기 위해 네임스페이스에 using System.Collections.Generic;를 추가합니다.

new_contours 변수에 일정 조건 이상의 윤곽선만 포함시키도록 하겠습니다.



foreach (Point[] p in contours)
{
    double length = Cv2.ArcLength(p, true);
    if ( length > 100)
    {
        new_contours.Add(p);
    }
}

반복문(foreach)를 활용해 검출된 윤곽선(contours)의 값을 검사합니다.

윤곽선 길이 함수(Cv2.ArcLength)를 활용해 length가 100 이상의 값만 new_contours에 추가합니다.



Cv2.DrawContours(dst, new_contours, -1, new Scalar(255, 0, 0), 2, LineTypes.AntiAlias, null, 1);

불필요한 윤곽선이 제거된 새로운 윤곽선 배열을 그립니다.

윤곽선 그리기 함수(Cv2.DrawContours)윤곽선을 간단하게 그려볼 수 있습니다.

Cv2.FindContours(결과 배열, 검출된 윤곽선, 윤곽선 번호, 색상, 두께, 선형 타입, 계층 구조, 계층 구조 최대 레벨)로 윤곽선 검출을 적용합니다.

윤곽선 번호는 지정된 윤곽선만 그릴 수 있습니다. 윤곽선 번호의 값을 -1로 사용할 경우, 모든 윤곽선을 그립니다.

계층 구조윤곽선 검출 함수에서 반환된 계층 구조를 의미합니다.

계층 구조 최대 레벨은 그려질 계층 구조의 깊이를 설정합니다. 계층 구조 최대 레벨을 0으로 설정할 경우 최상위 레벨만 그려집니다.

현재 새로운 윤곽선을 구성하였으므로, 계층 구조가 맞지 않으니 계층 구조null로 사용합니다.

계층 구조가 존재하지 않으므로 계층 구조 최대 레벨0의 이상의 값으로 사용합니다.

만약, 기본 윤곽선을 사용한다면 다음과 같이 그릴 수 있습니다.


Cv2.DrawContours(dst, contours, -1, new Scalar(255, 0, 0), 2, LineTypes.AntiAlias, hierarchy, 3);

새로운 윤곽선을 저장할 때, HierarchyIndex도 새롭게 저장한다면, 위의 구조와 동일하게 구현이 가능합니다.



검색 방법 플래그 종류

속성 의미
RetrievalModes.External 최외곽 윤곽선만 검색
RetrievalModes.List 모든 윤곽선을 검출하며, 계층 구조를 형성하지 않음(모든 윤곽선을 동일 레벨로 간주)
RetrievalModes.CComp 모든 윤곽선을 검색해서 2단계 계층 구조로 구성(최상위 레벨은 외곽, 두 번째 레벨은 내곽(홀))
RetrievalModes.Tree 모든 윤곽선을 검출하고 트리 구조로 구성



근사 방법 플래그 종류

속성 의미
ContourApproximationModes.ApproxNone 검출된 모든 윤곽점을 반환
ContourApproximationModes.ApproxSimple 수평, 수직, 대각선 부분을 압축해서 끝점만 반환
ContourApproximationModes.ApproxTC89L1 Teh-Chin 체인 근사 알고리즘을 적용
ContourApproximationModes.ApproxTC89KCOS Teh-Chin 체인 근사 알고리즘을 적용



Result


2


도움이 되셨다면 광고 클릭 부탁드립니다.


Book Image

책이 출간되었습니다!

C#과 파이썬을 활용한 OpenCV 4 프로그래밍

컴퓨터 비전 기초 이론부터 머신러닝을 활용한 영상 처리 프로젝트까지

  • C# OpenCvSharp4
  • Python OpenCV4
  • Using Tesseract
  • Using TensorFlow
  • Using Regular Expression
  • 윤대희 저 | 위키북스

    [yes24 바로가기] [알라딘 바로가기] [교보문고 바로가기]


    ⤧  Previous post C# OpenCV 강좌 : 제 15강 - 가장자리 검출 ⤧  Next post C# OpenCV 강좌 : 제 17강 - 다각형 근사
    C#-OpenCvSharp4 Category