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

   

윤곽선 (Contour)


1 영상이나 이미지의 윤곽선(컨투어)을 검출하기 위해 사용합니다. 영상이나 이미지에서 외곽과 내곽윤곽선(컨투어)을 검출 할 수 있습니다.


원본(Source, src)를 영상이나 이미지를 사용하면 됩니다.

영상 사용하기 : 3강 바로가기
이미지 사용하기 : 4강 바로가기


FindContours

Class


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;

namespace test
{
    class OpenCV : IDisposable
    {  
        IplImage bin;    
        IplImage con;        
        
        public IplImage Binary(IplImage src)
        {
            bin = new IplImage(src.Size, BitDepth.U8, 1);
            Cv.CvtColor(src, bin, ColorConversion.RgbToGray);
            Cv.Threshold(bin, bin, 150, 255, ThresholdType.Binary);
            return bin;
        }
                
        public IplImage Contour(IplImage src)
        {
            con = new IplImage(src.Size, BitDepth.U8, 3);
            bin = new IplImage(src.Size, BitDepth.U8, 1);

            Cv.Copy(src, con);
            bin = this.Binary(src);
            
            CvMemStorage Storage = new CvMemStorage();
            CvSeq<CvPoint> contours;
                    
            Cv.FindContours(bin, Storage, out contours, CvContour.SizeOf, ContourRetrieval.List, ContourChain.ApproxNone);        
            
            Cv.DrawContours(con, contours, CvColor.Yellow, CvColor.Red, 1, 4, LineType.AntiAlias);      
            
            Cv.ClearSeq(contours);
            Cv.ReleaseMemStorage(Storage);             
                                         
            return con;
        }
            
        public void Dispose()
        {
            if (bin != null) Cv.ReleaseImage(bin);        
            if (con != null) Cv.ReleaseImage(con);        
        }
    }
}

Class Code


con = new IplImage(src.Size, BitDepth.U8, 3);
bin = new IplImage(src.Size, BitDepth.U8, 1);

컨투어는 8Bit 단일 채널, Binary 영상으로 검출합니다. 출력할 이미지인 con과 검색할 이미지인 bin을 만듭니다.

Cv.Copy(src, con);
bin = this.Binary(src);

conbin을 만들고 이미지를 복사하고 덮어씌웁니다.

CvMemStorage Storage = new CvMemStorage();
CvSeq<CvPoint> contours;

Storage는 윤곽선(컨투어)의 메모리를 저장합니다. 점의 좌표들이 저장됩니다. contours는 윤곽선(컨투어)의 정보와 정수의 2D 좌표를 저장합니다.

Cv.FindContours(bin, Storage, out contours, CvContour.SizeOf, ContourRetrieval.List, ContourChain.ApproxNone); 

Cv.FindContours()를 이용하여 모든 윤곽선(컨투어)를 검색합니다. Cv,FindContours(이진화 이미지, 메모리 저장소, 윤곽선 저장, 자료구조의 크기, 검색 방법, 근사화 방법)을 의미합니다.

  • 검색 방법 (ContourRetrieval.*)

   * ContourRetrieval.CComp : 모든 윤곽선을 검색하여 2 단계 계층 구조로 구성합니다. 최상위 레벨은 구성 요소의 외곽(외부) 경계이고, 두 번째 레벨은 내곽(홀)의 경계입니다.

   * ContourRetrieval.External : 외곽 윤곽선만 검출합니다.

   * ContourRetrieval.List : 모든 윤곽선을 검출하여 list에 넣습니다.

   * ContourRetrieval.Tree : 모든 윤곽선을 검출하여 Tree계층 구조로 만듭니다.


  • 근사화 방법(ContourChain.*)

   * ContourChain.ApproxNone : 윤곽점들의 모든 점을 반환합니다.

   * ContourChain.ApproxSimple : 윤곽점들 단순화 수평, 수직 및 대각선 요소를 압축하고 끝점만 남겨 둡니다.

   * ContourChain.Code : 프리먼 체인 코드에서의 윤곽선으로 적용합니다.

* `ContourChain.ApproxTC89KCOS`, `ContourChain.ApproxTC89L1` : Teh-chin 알고리즘 적용합니다.

   * ContourChain.LinkRuns : 하나의 수평 세그먼트를 연결하여 완전히 다른 윤곽선 검색 알고리즘을 사용합니다.

Cv.DrawContours(con, contours, CvColor.Yellow, CvColor.Red, 1, 4, LineType.AntiAlias);

Cv.DrawContours()를 이용하여 컨투어를 그립니다. Cv.DrawContours(결과, 윤곽선, 외곽윤곽색상, 내곽윤곽색상, 최대레벨, 두께, 선형타입)입니다.

최대레벨은 0일 경우 지정된 윤곽선만 그리며, 1일 경우 윤곽선과 중첩 된 모든 윤곽선을 그립니다. 2일 경우 윤곽선과 중첩된 윤곽선, 중첩에 중첩된 모든 윤곽선을 그립니다.


Cv.ClearSeq(contours);
Cv.ReleaseMemStorage(Storage);        

Cv.ClearSeq()Cv.ReleaseMemStorage()를 이용하여 초기화 및 메모리를 해제합니다.


CvContourScanner

Class


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;

namespace test
{
    class OpenCV : IDisposable
    {  
        IplImage bin;    
        IplImage con;        
        
        public IplImage Binary(IplImage src)
        {
            bin = new IplImage(src.Size, BitDepth.U8, 1);
            Cv.CvtColor(src, bin, ColorConversion.RgbToGray);
            Cv.Threshold(bin, bin, 150, 255, ThresholdType.Binary);
            return bin;
        }
                
        public IplImage Contour(IplImage src)
        {
            con = new IplImage(src.Size, BitDepth.U8, 3);
            bin = new IplImage(src.Size, BitDepth.U8, 1);

            Cv.Copy(src, con);
            bin = this.Binary(src);
            
            CvMemStorage Storage = new CvMemStorage();
            CvSeq<CvPoint> contours;
                    
            CvContourScanner scanner = Cv.StartFindContours(bin, Storage, CvContour.SizeOf, ContourRetrieval.List, ContourChain.ApproxNone); 
            
            // #1        
            while(true)
            {
                contours = Cv.FindNextContour(scanner);

                if (contours == null) break;
                else
                {
                    Cv.DrawContours(con, contours, CvColor.Yellow, CvColor.Red, 1, 4, LineType.AntiAlias);
                }
            }
            Cv.EndFindContours(scanner);                
           
            // #2        
            //foreach (CvSeq<CvPoint> c in scanner)
            //{
            //    con.DrawContours(c, CvColor.Yellow, CvColor.Red, 1, 4, LineType.AntiAlias);
            //}
            //Cv.ClearSeq(contours);
            
            Cv.ReleaseMemStorage(Storage);             
                                         
            return con;
        }
            
        public void Dispose()
        {
            if (bin != null) Cv.ReleaseImage(bin);        
            if (con != null) Cv.ReleaseImage(con);        
        }
    }
}


Class Code


CvContourScanner scanner = Cv.StartFindContours(bin, Storage, CvContour.SizeOf, ContourRetrieval.List, ContourChain.ApproxNone); 

Cv.StartFindContours()하나의 윤곽선(컨투어)를 찾는데 사용합니다. Cv.StartFindContours(이진화 이미지, 메모리 저장소, 객체 할당, 검색 방법, 근사화 방법)을 의미합니다. CvContourScanner는 시퀀스의 정보를 가지고 있습니다.

while(true)
{
    contours = Cv.FindNextContour(scanner);

    if (contours == null) break;
    else
    {
        Cv.DrawContours(con, contours, CvColor.Yellow, CvColor.Red, 1, 4, LineType.AntiAlias);
    }
}
Cv.EndFindContours(scanner);

while(true)를 이용하여 한 개씩 찾아 모든 컨투어를 표시합니다. contours = Cv.FindNextContour(scanner)는 순차적으로 검사합니다. 검사할 컨투어가 없는 경우 null 값을 반환합니다.

이를 이용하여 if(contours == null)이 될 때 while문을 종료합니다. 그렇지 않으면 Cv.DrawContours()를 이용하여 컨투어를 하나씩 그려냅니다.

Cv.EndFindContours()는 스캐닝이 끝나면 스캐너를 완료상태로 만듭니다. 시퀀스의 첫 원소에 대한 포인터를 반환합니다.


Class Code


foreach (CvSeq<CvPoint> cont in scanner)
{
    con.DrawContours(cont, CvColor.Yellow, CvColor.Red, 1, 4, LineType.AntiAlias);
}
Cv.ClearSeq(contours);

foreach문을 이용하여 scanner로 찾은 모든 컨투어를 그려주는 방법입니다.

3 가지의 방법을 통하여 모든 컨투어 또는 일부 컨투어만 검출 할 수 있습니다.

  • Tip : if, for, 배열 등을 이용하여 일부 컨투어만을 검출 할 수 있습니다.


Result


2



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

⤧  Next post C# OpenCV 강좌 : 제 21강 - 코너 검출 (1) ⤧  Previous post C# OpenCV 강좌 : 제 19강 - 캡쳐 및 녹화