EmguCV, OpenCVSharp 카메라 캡쳐 코드 비교


실제 영상처리와 관련한 개발은 주로 C++을 사용하겠지만

인터페이스부분은 C#으로 작성할 예정이라

C#에서의 OpenCV 지원을 알아보다가

C#에서 사용가능하도록 랩핑(Wrapping)되어 있는 2개의 오픈소스 프로젝트를 발견하였다.


EmguCV

http://www.emgu.com/

사용하는 OpenCV 버젼에 맞는 래핑버젼을 사용할 수 있고

인터넷 상에 떠도는 이야기로는 안정성면에서도 좋다고 한다.


OpenCVSharp

https://github.com/shimat/opencvsharp/wiki

홈페이지를 보면 일본사람이 개발했다는 것을 알수있고

아무래도 동양권에서 개발한 것이라 그런지

국내에서는 좀더 선호도가 높다고 한다.

네이버 커뮤니티도 있다.

http://cafe.naver.com/opencvsharp


최근활동은 없긴하지만

검색해보면 국내 블러그도 주로 OpenCVSharp을 사용한 결과들을 주로 포스팅하고 있다.


두개의 라이브러리를 테스트하면서

EmgvCV에서 기본제공하는 Camera Capture 프로그램과 유사한 OpenCVSharp 기반 프로그램을 작성한 결과를 바탕으로 간단하게 두 라이브러리 차이에 대해 소개해 보겠다.


두 라이브러리 모두 OpenCV 2.4.10을 기반으로 작성된 버젼을 사용하였다.





왼쪽이 EmguCV에서 제공하는 샘플 코드 프로그램이고

오른쪽이 왼쪽내용과 유사하게 작성한 OpenCVSharp 버젼이다.


<<Camera Capture>> EmguCV


//----------------------------------------------------------------------------

//  Copyright (C) 2004-2013 by EMGU. All rights reserved.       

//----------------------------------------------------------------------------


using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Emgu.CV;

using Emgu.CV.Structure;

using Emgu.Util;


namespace CameraCapture

{

   public partial class CameraCapture : Form

   {

      private Capture _capture = null;

      private bool _captureInProgress;


      private int canny_lth = 100;

      private int canny_merge_v = 50;

      public CameraCapture()

      {

         InitializeComponent();

         try

         {

             _capture = new Capture(0);

            _capture.ImageGrabbed += ProcessFrame;

         }

         catch (NullReferenceException excpt)

         {

            MessageBox.Show(excpt.Message);

         }

      }


      private void ProcessFrame(object sender, EventArgs arg)

      {

         Image<Bgr, Byte> frame = _capture.RetrieveBgrFrame();


         Image<Gray, Byte> grayFrame = frame.Convert<Gray, Byte>();

         Image<Gray, Byte> smallGrayFrame = grayFrame.PyrDown();

         Image<Gray, Byte> smoothedGrayFrame = smallGrayFrame.PyrUp();

         Image<Gray, Byte> cannyFrame = smoothedGrayFrame.Canny(canny_lth, canny_merge_v);


         

         captureImageBox.Image = frame;

         grayscaleImageBox.Image = grayFrame;

         smoothedGrayscaleImageBox.Image = smoothedGrayFrame;

         cannyImageBox.Image = cannyFrame;

      }


      private void captureButtonClick(object sender, EventArgs e)

      {

         if (_capture != null)

         {

            if (_captureInProgress)

            {  //stop the capture

               captureButton.Text = "Start Capture";

               _capture.Pause();

            }

            else

            {

               //start the capture

               captureButton.Text = "Stop";

               _capture.Start();

            }


            _captureInProgress = !_captureInProgress;

         }

      }


      private void ReleaseData()

      {

         if (_capture != null)

            _capture.Dispose();

      }


      private void FlipHorizontalButtonClick(object sender, EventArgs e)

      {

         if (_capture != null) _capture.FlipHorizontal = !_capture.FlipHorizontal;

      }


      private void FlipVerticalButtonClick(object sender, EventArgs e)

      {

         if (_capture != null) _capture.FlipVertical = !_capture.FlipVertical;

      }

   }

}




<<OpenCVSharpCapture>> OpenCVSharp


using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using OpenCvSharp;

using OpenCvSharp.UserInterface;

using OpenCvSharp.Extensions;

using System.Threading;


namespace OpenSharpCapture

{

    public partial class frmMain : Form

    {

        CvCapture _capture;

        IplImage _image;

        Thread captureThread;


        delegate void SetPictureCallback(IplImage im);


        private bool _captureInProgress = false;

        private bool _bStop = false;


        public frmMain()

        {

            InitializeComponent();

            _capture = new CvCapture(0);

            captureThread = new Thread(DoCapture);

            captureThread.Start();

        }


        private void btnCapture_Click(object sender, EventArgs e)

        {

            if (_capture != null)

            {

                if (_captureInProgress)

                {  //stop the capture


                    btnCapture.Text = "Start Capture";


                }

                else

                {

                    btnCapture.Text = "Stop";

                    //_captureInProgress = false;

                }


                _captureInProgress = !_captureInProgress;

            }


        }


        private void SetImage1(IplImage im)

        {

            if (this.pictureBoxIpl1.InvokeRequired)

            {

                SetPictureCallback p = new SetPictureCallback(SetImage1);

                this.Invoke(p, new object[] { im });

            }

            else

            {

                this.pictureBoxIpl1.ImageIpl = im;

            }

        }


        private void SetImage2(IplImage im)

        {

            if (this.pictureBoxIpl2.InvokeRequired)

            {

                SetPictureCallback p = new SetPictureCallback(SetImage2);

                this.Invoke(p, new object[] { im });

            }

            else

            {

                this.pictureBoxIpl2.ImageIpl = im;

            }

        }


        private void SetImage3(IplImage im)

        {

            if (this.pictureBoxIpl3.InvokeRequired)

            {

                SetPictureCallback p = new SetPictureCallback(SetImage3);

                this.Invoke(p, new object[] { im });

            }

            else

            {

                this.pictureBoxIpl3.ImageIpl = im;

            }

        }


        private void SetImage4(IplImage im)

        {

            if (this.pictureBoxIpl4.InvokeRequired)

            {

                SetPictureCallback p = new SetPictureCallback(SetImage4);

                this.Invoke(p, new object[] { im });

            }

            else

            {

                this.pictureBoxIpl4.ImageIpl = im;

            }

        }


        public void DoCapture()

        {

            while (true)

            {

                if (_captureInProgress)

                {

                    _image = _capture.QueryFrame();

                    IplImage gimg = new IplImage(_image.Size, BitDepth.U8, 1);

                    IplImage gsimg = new IplImage(_image.Size, BitDepth.U8, 1);

                    IplImage cimg = Cv.CreateImage(new CvSize(_image.Width, _image.Height), BitDepth.U8, 1); ;

                    SetImage1(_image);

                    Cv.CvtColor(_image, gimg, ColorConversion.BgrToGray);

                    SetImage2(gimg);

                    //gimg.Smooth(gimg, SmoothType.Gaussian, 5);

                    gimg.Smooth(gimg, SmoothType.Blur, 10);

                    SetImage3(gimg);

                    Cv.Canny(_image, cimg, 50, 200);

                    SetImage4(cimg);

                    Cv.WaitKey(100);

                    Thread.Sleep(20);

                }

                if (_bStop)

                {

                    break;

                }

            }

        }


        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)

        {

            if (_captureInProgress)

                _captureInProgress = false;


            Thread.Sleep(1);


            _bStop = true;

            Thread.Sleep(1);

            captureThread.Abort();


            Thread.Sleep(1);

            captureThread.Join();


            if(_capture != null)

                _capture.Dispose();

        }

    }

}





두 라이브러리에서 큰 차이점은

EmguCV는 래핑을 통해 좀더 C#스타일로 쓰기 편하게 만들었다면

OpenCVSharp은 OpenCV 원형에 충실한 래핑으로 OpenCV 원형을 이해하면 충분히 활용이 가능하도록 했다는 점에서 각각 장점이 있다고 본다.


위의 코드에서 몇가지 사항만 비교해보면


EmguCV 에서는 Capture 객체에 대해 이벤트핸들러를 지원하기 때문에 별도로 캡쳐에 대한 쓰레드 처리가 필요없지만, OpenCVSharp에서는 캡쳐를 진행하기 위해 별도의 쓰레드 생성이 필요했고 그에 따른 델리게이션 처리도 필요하였다.


이미지 표출을 위한 컨트롤은 윈도우에서 기본으로 제공하는 PictureBox 를 사용하는 것도 가능한 것으로 나오지만 두 라이브러리 모두 별도의 컨트롤 ImageBox(EmguCV), PictureBoxIpl(OpenCVSharp) 을 따로 제공하고 있어 해당 컨트롤을 사용하면 별도의 데이터 변환없이 사용할 수 있었다.



-----


그외에 OpenCVSharp은 CPP 스타일의 코딩이 가능하도록 해줘서

OpenCV와 유사한 코드형태를 유지할 수 있어 유리하게 보였지만

일단 테스트 코드에서는 CPP 스타일 적용은 실패했으며, 좀더 테스트를 진행해볼 예정이다.



CameraCapture.zip


OpenSharpCapture.zip


Posted by 휘프노스
,