아래 코드를 이용하여 이미지를 비교하면 아래와 같이

이미지 포인트를 비교해 사각형을 그려준다.

(EmguCV 3.3 기준)

 

        public static Mat Draw(Mat modelImage, Mat observedImage, out long matchTime)
        {
            Mat homography;
            VectorOfKeyPoint modelKeyPoints;
            VectorOfKeyPoint observedKeyPoints;
            using (VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch())
            {
                Mat mask;
                FindMatch(modelImage, observedImage, out matchTime, out modelKeyPoints, out observedKeyPoints, matches,
                   out mask, out homography);

                //Draw the matched keypoints
                Mat result = new Mat();
                Features2DToolbox.DrawMatches(modelImage, modelKeyPoints, observedImage, observedKeyPoints,
                   matches, result, new MCvScalar(255, 255, 255), new MCvScalar(255, 255, 255), mask);

                #region draw the projected region on the image

                if (homography != null)
                {
                    //draw a rectangle along the projected model
                    Rectangle rect = new Rectangle(Point.Empty, modelImage.Size);
                    PointF[] pts = new PointF[]
                    {
                  new PointF(rect.Left, rect.Bottom),
                  new PointF(rect.Right, rect.Bottom),
                  new PointF(rect.Right, rect.Top),
                  new PointF(rect.Left, rect.Top)
                    };
                    pts = CvInvoke.PerspectiveTransform(pts, homography);

                    Point[] points = Array.ConvertAll<PointF, Point>(pts, Point.Round);
                    using (VectorOfPoint vp = new VectorOfPoint(points))
                    {
                        CvInvoke.Polylines(result, vp, true, new MCvScalar(255, 0, 0, 255), 5);
                    }

                }

                #endregion

                return result;

            }


        }

        public static void FindMatch(Mat modelImage, Mat observedImage, out long matchTime, out VectorOfKeyPoint modelKeyPoints, out VectorOfKeyPoint observedKeyPoints, VectorOfVectorOfDMatch matches, out Mat mask, out Mat homography)
        {
            int k = 2;
            double uniquenessThreshold = 0.8;
            double hessianThresh = 300;

            Stopwatch watch;
            homography = null;

            modelKeyPoints = new VectorOfKeyPoint();
            observedKeyPoints = new VectorOfKeyPoint();

#if !__IOS__
            if (CudaInvoke.HasCuda)
            {
                CudaSURF surfCuda = new CudaSURF((float)hessianThresh);
                using (GpuMat gpuModelImage = new GpuMat(modelImage))
                //extract features from the object image
                using (GpuMat gpuModelKeyPoints = surfCuda.DetectKeyPointsRaw(gpuModelImage, null))
                using (GpuMat gpuModelDescriptors = surfCuda.ComputeDescriptorsRaw(gpuModelImage, null, gpuModelKeyPoints))
                using (CudaBFMatcher matcher = new CudaBFMatcher(DistanceType.L2))
                {
                    surfCuda.DownloadKeypoints(gpuModelKeyPoints, modelKeyPoints);
                    watch = Stopwatch.StartNew();

                    // extract features from the observed image
                    using (GpuMat gpuObservedImage = new GpuMat(observedImage))
                    using (GpuMat gpuObservedKeyPoints = surfCuda.DetectKeyPointsRaw(gpuObservedImage, null))
                    using (GpuMat gpuObservedDescriptors = surfCuda.ComputeDescriptorsRaw(gpuObservedImage, null, gpuObservedKeyPoints))
                    //using (GpuMat tmp = new GpuMat())
                    //using (Stream stream = new Stream())
                    {
                        matcher.KnnMatch(gpuObservedDescriptors, gpuModelDescriptors, matches, k);

                        surfCuda.DownloadKeypoints(gpuObservedKeyPoints, observedKeyPoints);

                        mask = new Mat(matches.Size, 1, DepthType.Cv8U, 1);
                        mask.SetTo(new MCvScalar(255));
                        Features2DToolbox.VoteForUniqueness(matches, uniquenessThreshold, mask);

                        int nonZeroCount = CvInvoke.CountNonZero(mask);
                        if (nonZeroCount >= 4)
                        {
                            nonZeroCount = Features2DToolbox.VoteForSizeAndOrientation(modelKeyPoints, observedKeyPoints,
                               matches, mask, 1.5, 20);
                            if (nonZeroCount >= 4)
                                homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(modelKeyPoints,
                                   observedKeyPoints, matches, mask, 2);
                        }
                    }
                    watch.Stop();
                }
            }
            else
#endif
            {
                using (UMat uModelImage = modelImage.GetUMat(AccessType.Read))
                using (UMat uObservedImage = observedImage.GetUMat(AccessType.Read))
                {
                    SURF surfCPU = new SURF(hessianThresh);
                    //extract features from the object image
                    UMat modelDescriptors = new UMat();
                    surfCPU.DetectAndCompute(uModelImage, null, modelKeyPoints, modelDescriptors, false);

                    watch = Stopwatch.StartNew();

                    // extract features from the observed image
                    UMat observedDescriptors = new UMat();
                    surfCPU.DetectAndCompute(uObservedImage, null, observedKeyPoints, observedDescriptors, false);
                    BFMatcher matcher = new BFMatcher(DistanceType.L2);
                    matcher.Add(modelDescriptors);

                    matcher.KnnMatch(observedDescriptors, matches, k, null);
                    mask = new Mat(matches.Size, 1, DepthType.Cv8U, 1);
                    mask.SetTo(new MCvScalar(255));
                    Features2DToolbox.VoteForUniqueness(matches, uniquenessThreshold, mask);

                    int nonZeroCount = CvInvoke.CountNonZero(mask);
                    if (nonZeroCount >= 4)
                    {
                        nonZeroCount = Features2DToolbox.VoteForSizeAndOrientation(modelKeyPoints, observedKeyPoints,
                           matches, mask, 1.5, 20);
                        if (nonZeroCount >= 4)
                            homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(modelKeyPoints,
                               observedKeyPoints, matches, mask, 2);
                    }

                    watch.Stop();
                }
            }
            matchTime = watch.ElapsedMilliseconds;
        }

EmguCV 버전에 따라 코드가 너무 달라지는것들이 있다 ㅠ

하나씩 정리해봐야겠다.

EmguCV 3.3 버전 기준이다.

 

Index fln = new Index(supermatrix, 4);

아래 처럼 변경하면 된다. (EmguCV 3.3)

Index fln = new Index(supermatrix, new KdTreeIndexParams(4))’,

  1. Favicon of http://kjun.kr kjun.kr 2018.03.12 13:38 신고

    https://www.codeproject.com/Articles/1187512/Image-recognition-with-Csharp-and-Emgu-libraries

(문)
각각 두가지에 대하여 캠으로 받아 

IplImage *frame = 0;
Mat frame;

을 캠으로 받아서 depth를 출력해봤더니

IplImage 에서는 depth값이 8이 나오고

Mat 에서는 depth값이 0이 나오더라구요

각각의 의미에 대해 알고 싶습니다.

가령 Mat img(480,720,CV_32F) 인 경우에는 depth값이 5이고,

IplImage* img; img = cvCreateImage(cvSize(480,720),IPL_DEPTH_32F,3); 인 경우는 32로 나오고

Mat에서의 depth 5값은 IplImage에서의 depth 32값과 같은걸로 알고 있는데 아닌지요

왜 캠으로 frame을 받을때는 각각의 depth값이 다르게 나오는지도 너무 궁금하네요..

(답)

두 구조체/클래스는 서로 다른 값 정의를 가지고 있습니다.

뜻은 같지만 그 뜻을 표현하는 방식이 다른거죠

예를 들면 우리가 먹는 사과를 우리나라는 "사과"라고 발음하고 표기하지만

영미권에서는 "애플"이라 발음하고 "apple"라고 쓰는 것과 비슷합니다.


8U 즉, 한 픽셀에 8비트의 언사인드 값을 가지는 타입을 예로 들어봅시다

iplimage의 depth는 IPL_DEPTH_8U 를 리턴합니다.(types_c.h)

디파인을 따라가보면 8입니다.

이 IPL_DEPTH 계열 디파인들은 비트수를 그대로 표현해 주고 signed인 경우에 0x80000000 을 OR시켜 알려줍니다.

그런데 Mat의 depth()는 CV_8U 를 리턴합니다.(cvdef.h)

이 값은 0입니다.

이 CV_계열 디파인들은 단순열거 되어 있습니다.

즉, 같은 8비트 계열인 CV_8S 는 1입니다.

두 구조체/클래스의 스코프가 다르기 때문에 나오는 문제죠

아마도 bpp를 계산하고 싶으신 듯 한데,

mat은 elemSize()라는 함수를 가지고 있습니다.

한 픽셀을 몇 "바이트"로 표현하는지 리턴해 주는 함수입니다.

따라서 여기에 8을 곱하면 bpp의 계산이 가능합니다

Visual Studio 2017 에서 C++ 을 이용해 OpenCV 를 활용해 보려고 합니다.

이를 위해선 먼저 환경 설정이 필요합니다.

 

1. OpenCV 다운로드

아래 링크에서 파일을 다운로드 받습니다. (2018-02-27 기준 최신버전 경로)

https://sourceforge.net/projects/opencvlibrary/files/latest/download

그외 최신버전은 아래 링크에서 확인합니다.

https://sourceforge.net/projects/opencvlibrary/

아래 처럼 다운로드가 완료되면 실행합니다.

적당한곳에 압축을 풉니다.

압축을 풀면 아래와 같이 구성 되어있습니다.

환경설정에 필요한건 build 폴더 입니다.

Visual Studio 에서 이용할 폴더는 build 에서 include, x64 폴더입니다.

x64 의 vc15 가 visual studio 2017 에 대응됩니다. .

(vc14 를 사용해도 상관은 없더군요..)

 

2. 환경 변수 설정

내컴퓨터 속성에서 고급 시스템 설정으로 진입

환경변수 클릭.

환경 변수에서 상단의 사용자 변수의 Path 선택하고 하단의 시스템 변수에서 Path 를 선택하고 편집을 클릭합니다.

앞서 압축을 푼 폴더에서 .....\build\x64\vc15\bin 경로를 추가합니다.

 

3. 프로젝트 구성

C++ 프로젝트를 만듭니다.

새프로젝트 추가에서 아래처럼 Visual C++ 에서 빈프로젝트를 하나 생성합니다.

프로젝트 속성에서 본겨적인 환경설정 작업이 시작됩니다.

먼저 상단의 구성은 '모든구성' 을 선택하고 플랫폼은 'x64' 로 선택합니다.

링커 위에 'C/C++' 항목이 보여야합니다.

이를 위해선 프로젝트에서 새 항목 추가를 선택하여 아래처럼 cpp 파일을 하나 만들어 줍니다.

다시 프로젝트 속성에 진입하면 아래 처럼 C/C++ 카테고리가 추가된 걸 확인 할수 있습니다.

먼저 추가된 C/C++ 항목을 펼쳐서 일반 항목을 선택합니다.

여기서 추가 포함 디렉터리 에서 우측에 화살표를 클릭해 편집을 클릭합니다.

아래와 같은 팝업 창이 하나 뜹니다. 여기서 추가버튼(+폴더모양)을 클릭하면 아래처럼 입력할수 있는 칸이 나오는데

여기서 우측의 ... 버튼을 클릭합니다.

...\build\include 경로를 선택하여 추가합니다.

추가가 되면 추가 포함 디렉터리에 아래처럼 표시가 됩니다.

 

이번에는 링커 항목에서 일반 을 선택합니다.

추가 라이브러리 디렉터리 에서 우측 끝 화살표를 클릭해 편집을 선택합니다.

아래와 같은 팝업창이 하나뜨고

아래 경로를 선택 및 추가합니다.

...\build\x64\vc15\lib

추가가 되면 추가 라이브러리디렉터리에 아래처럼 표시가 됩니다.

이제 링커에서 입력 항목을 선택합니다.

추가 종속성에서 편집을 선택합니다.

여기서 아래 두 lib 파일을 넣어줍니다.

위 두 파일 명을 아래 경로에의파일 명입니다.

마지막에 d 가 붙은건 Debug 때 사용되고 그렇지 않은건 Release 때 사용됩니다.

추가가 완료되면 아래처럼 추가한 항목이 추가 종속성 앞쪽에 보여집니다.

확인을 눌러 프로젝트 속성창을 빠져나옵니다.

이제 아래 그림 처럼 x64 로 두고 디버깅을 시작하면 됩니다.

이미지 하나를 canny 로직을 적용해 봅니다.

먼저 사진 파일 하나를 명칭을 image 로하여 프로젝트 폴더에 옮겨 놓습니다.

제가 사용한 이미지는 아래 이미지 입니다.

소스코드

#include<opencv2/opencv.hpp>
#include<iostream>
#include<conio.h>
int main() {
 cv::Mat imgOriginal;
 cv::Mat imgGrayscale;
 cv::Mat imgBlurred;
 cv::Mat imgCanny;

 imgOriginal = cv::imread("image.png");
 if (imgOriginal.empty()) {
  std::cout << "error: image not read from file\n\n";
  _getch();
  return(0);
 }

 cv::cvtColor(imgOriginal, imgGrayscale, CV_BGR2GRAY);

 cv::GaussianBlur(imgGrayscale,
  imgBlurred,
  cv::Size(5, 5),
  1.5);
 cv::Canny(imgBlurred,
  imgCanny,
  82,
  164);

 cv::namedWindow("imgOriginal", CV_WINDOW_AUTOSIZE);
 cv::namedWindow("imgCanny", CV_WINDOW_AUTOSIZE);

 cv::imshow("imgOriginal", imgOriginal);
 cv::imshow("imgCanny", imgCanny);

 cv::waitKey(0);

 return(0);
}

실행해 보면 아래처럼 Canny 가 적용된 이미지 출력을 확인할 수 있습니다.

 

 

  1. Favicon of http://kjun.kr kjun.kr 2018.03.09 09:15 신고

    https://stackoverflow.com/questions/4975913/how-do-i-use-emgu-cvs-surf-library-to-match-to-a-library-of-images?rq=1

  2. Favicon of http://kjun.kr kjun.kr 2018.03.09 13:27 신고

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/95ebd6bc-72cb-4a8c-9b63-a5b3de682fe6/cannot-convert-from-int-to-emgucvflanniindexparams?forum=clr

  3. Favicon of http://kjun.kr kjun.kr 2018.03.09 13:38 신고

    https://stackoverflow.com/questions/33657853/emgucv-surf-determine-matched-pairs-of-points?rq=1

  4. Favicon of http://kjun.kr kjun.kr 2018.03.09 15:00 신고

    https://github.com/emgucv/emgucv/issues/2

  5. Favicon of http://kjun.kr kjun.kr 2018.03.09 15:21 신고

    https://stackoverflow.com/questions/39764844/finding-matching-image-within-a-collection-of-images-using-emgucv-3-1-0

https://076923.github.io/posts/C-opencv-1/

SVM의 개념

SVM은 상당히 다룰 내용이 많은 learning algorithm이다.

하지만, 아직은 SVM을 제대로 공부한 적이 없는 필자와 같은 상태의 사람들은 논문을 보거나 SVM을 프로젝트에 이용하려 할때 기본 개념을 알고 있을 필요가 있다.



SVM(Support vector machine)은 2개의 범주를 분류하는 이진 분류기이다.

다음 그림은 SVM의 개념을 설명하는 것이다. feature들은 그림과 같은 vector공간에 vector로 표시된다. 그림에서 보는 것처럼 하얀 색 vector들을 A그룹에 속하는 white point라고 하고, 그 반대로 검은색 vector들을 B그룹에 속하는 black point라고 하자.



이러한 벡터 가운데 같은 범주를 기준으로 바깥으로 위치한 벡터들의 연결선으로 이루어진 닫혀진 다각형을 convex hull이라고 한다. convex hull안의 벡터들은 그룹을 분류하는 데 그다지 큰 영향을 미치지 않는다. 그룹을 분류하는데 가장 큰 영향을 미치는 것들은 바깥에 위치한 벡터들이다. 그룹을 분류하는 선, 면을 hyperplane이라고 한다.
그림에서 보는 것처럼 그룹을 나눌 수 있는 hyperplane은 무수히 많다.
하지만, 직관적으로 그룹들의 convex hull에 속한 벡터들 중 가장 가까운 벡터와 수직거리로 가장 먼 거리를 가진 hyperplane이 2그룹을 효과적으로 분류할 것이다.
이러한 hyperplane을 maximum hyperplane이라고 부르고 이때 가장 가까운 벡터들을 support vector라고 한다. hyperplane이 재조정 될때는 support vector역시 재계산 되어야 한다. hyperplane은 선형 또는 비선형 모든 형태로 표현이 가능하며 일정 수식의 방정식으로 표현이 가능하기 때문에 간단한 수식으로 두 그룹을 분류할 수 있다.

이제 해야 할 일은 이 두 그룹 간의 거리를 최대한으로 하여 categorization할 때 발생할 수 있는 오류를 최소화 해야 한다. 그룹 간 거리를 최대한으로 하기 위해서 공업 수학 시간에 배운 적이 있을(공학도라면) 다변수 함수의 최대, 최소 값 찾는 데 이용되는 라고랑지의 미정계수법을 사용한다. 라고랑지 미정계수법의 원리는 그리 어려운 내용이 아니다.(수학적 내용 Lable의 라고랑지의 미정계수법 참고)

그런데 SVM 역시 두 그룹간의 거리를 최대로 하는 가중치 값들(다변수)을 정하는 것이므로 다른 머신 러닝 방법과 유사한 측면이 있다. 그렇다면, 왜 Decision tree, Concept learning, 그리고 neural network같은 걸 쓰지 않고 SVM을 쓰는 걸까? 그것을 바로 target이 2그룹 중 하나로 분류되는 경우에 특화되어 있기 때문이다. 예를 들면, 2가지 그룹으로 분류하는 방법으로 Decision tree를 쓸 수도 있고, neural network를 쓸수도 있고 Concept learning을 할 수도 있다. 만약 training set이 선형적인 hyperplane으로 나눠질 수 있다면 모든 경우가 거의 비슷한 성능을 할 것이다. 하지만 비 선형적인 경우 neural network가 가장 좋은 성능을 내게 될 것이라고 하자. 하지만, 만약 이 비선형성이 보다 높은 차원에서 볼 때 선형성을 뛴다고 하면 차원을 확대해서 보다 더 빠르고 쉬운 선형적 머신 러닝 알고리즘을 사용할 수 있지 않을까? 우리는 두 개의 그룹으로 분류하기 때문에 차원의 수를 아주 많이 확대하지 않고도 training set을 선형적으로 바꿀 수 있을 것이라고 직관적으로 생각할 수 있다. 그런 의미에서 SVM이 효과적인 측면을 갖는다고 말할 수 있다.

출처 : http://channelofchaos.blogspot.kr/2007/08/svm_10.html

'OpenCV_EmguCV' 카테고리의 다른 글

SVM의 개념  (0) 2018.02.15

+ Recent posts

티스토리 툴바