반응형

안녕하세요

 

제 블로그에 있는 OpenCv 예제 포스팅을 따라하시던분께서 요청하신 DLL 파일입니다

 

제가 연습한 부분에서는 굳이 사용하지 않으셔도 되는 파일이지만 필요하실수도 있을거 같아서

 

업로드해 드립니다

 

해당 DLL파일은 Nuget 설치시 패키지에 포함된 DLL파일이 아닌 Sharp 사용자가 개인 컨버팅을 통해

 

제작한 파일로 알고 있습니다

 

OpenCv를 통해 SVM 구현시에 사용되는걸로 알고 있는데

 

필요하신 분은 아래 링크를 통해 다운로드 받으시길 바랍니다

 

OpenCvSharp.MachineLearning.zip
0.02MB

반응형
반응형

안녕하세요

 

이번 시간 포스팅 내용은 제목과 똑같이 원본이미지에서 찾고자 하는 부분이미지를 매칭하여

 

원본이미지에 해당 부분을 검출해 내는 기능입니다

 

이미 인터넷상에 많은 정보가 있기에 별도 설명은 생략하고 바로 기능 구현으로 넘어가겠습니다

 

해당 포스팅도 저번 포스팅에 이어 진행됨에 따라 내용이 중간 생략 부분이 있을 수 있으니

 

이해가 잘되시면 이전 포스팅을 검색해 보시기 바랍니다

 

https://codingman.tistory.com/49

 

[C#]OpenCvSharp 라이브러리 사용하기 #1

안녕하세요 저번 포스팅에 C#으로 OpenCvSharp 라이브러리를 등록하여 구현하는 포스팅을 준비하던중에 OpenCv 3,4 버전에서 오류가 발생하는 문제가 있다는 얘길 듣고 부랴부랴 포스팅 내용을 검토해봤는데 역시..

codingman.tistory.com

 

템플릿 매칭을 하기 위해 두개의 이미지를 불러와야 하는데요

 

그러기 위한 디자인 UI 부터 보여드릴께요

 

[디자인]

- 메뉴에서 유틸리티 -> 템플릿매칭 버튼을 생성

 

- 해당 버튼에서 발생할 클릭 이벤트 활성화

 

- 이벤트 구문에 다음과 같이 소스 코딩 합니다

        private void 템플릿매칭ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (src == null) return;
            using (Match Sq = new Match())
            using (IplImage temp = Sq.Templit(src, src2))
            {
                result = temp.Clone();

            }

            using (CvWindow wind = new CvWindow("결과창"))
            {
                wind.Image = result;
                Cv.WaitKey(0);
            }
        }

 

- 템플릿 매칭 기능이 동작할 클래스를 하나 생성합니다

 

- 생성된 클래스에 다음과 같으 코딩합니다

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

namespace OpenCV_V1
{
    class Match : IDisposable
    {
        IplImage match;

        public IplImage Templit(IplImage src, IplImage temp)
        {
            match = src;
            IplImage templit = temp;
            IplImage tm = new IplImage(new CvSize(match.Size.Width - templit.Size.Width + 1, match.Size.Height - templit.Size.Height + 1), BitDepth.F32, 1);

            CvPoint minloc, maxloc;
            Double minval, maxval;

            Cv.MatchTemplate(match, templit, tm, MatchTemplateMethod.SqDiffNormed);

            Cv.MinMaxLoc(tm, out minval, out maxval, out minloc, out maxloc);

            Cv.DrawRect(match, new CvRect(minloc.X, minloc.Y, templit.Width, templit.Height), CvColor.Red, 3);

            return match;
        }

        public void Dispose()
        {
            if (match != null) Cv.ReleaseImage(match);
        }
    }
}

 

- 그림읽기를 통해 원본 이미지를 불러옵니다

 

- 그림읽기2를 통해 매칭 이미지를 불러옵니다

 

- 유틸리티 -> 템플릿매칭 버튼을 눌러 결과를 확인 합니다

 

[결과 창]

 

- 이렇게 매칭 이미지(쯔위)를 원본이미지에서 검출해 내는것을 확인 할 수 있습니다

 

- 프로그램을 구현하신뒤 다른 이미지로 테스트 해보시기 바랍니다

반응형
반응형

안녕하세요

 

이번 시간 포스팅할 부분은 OpenCvSharp를 이용한 PCA(Principal Component Analysis)를 구현해 볼려고 합니다

 

근데 아무 저와 똑같이 PCA가 뭐지??라는 분들이 많으실거 같아서 검색을 통해 제가 알아본 내용을

 

간략하게 소개 하고 구현 해보도록 할께요

 

▶ 주성분분석(Principal Component Analysis)

      - PCA는 분포된 데이터들의 주성분(Principal Component)를 찾아 고차원의 데이터를 저차원의 데이터로 환원

        시키는 기법

 

 

음... 정의를 보아도 무슨말인지 잘 이해가 되지 않습니다. 제가 이해한것을 표현하자면 어떤한 데이터의 주성분을

 

분석하여 데이터로 환원한뒤 그 환원된 값의 분포도에 따라 어떠한 형태를 띄고 있는지를 보는 거라고 이해하고

 

있는데 이게 맞는것인지를 모르겠습니다

 

제가 이해한 정의를 가지고 프로그래밍을 분석하여 구현하였는데

 

프로그램적 순서를 보면 이렇습니다 

 

① 원본 이미지

② 대상 이미지

③ 형상 비교를 통한 매치율 분석

 

이론은 간단하지만 구현하고 분석을 쉽지가 않더라구요 아무 저도 프로그래머다 보니 논리적 이론 보다는

 

프로그램적 구현쪽을 더 많이 파고 있는 입장이라 완벽한 설명은 불가합니다

 

좀 더 구체적 이해와 설명이 필요하시면 검색을 통해 이해 하시는게 좋을거 같습니다

 

그럼 다시 본론으로 돌아와서 프로그램 구현을 시작해 보겠습니다

 

해당 포스팅의 글 역시 기존 OpenCvSharp 포스팅 자료와 이어서 진행되오니

 

이해가 안가거나 중간 생략된 부분은 이전 포스팅을 참조하시기 바랍니다

 

https://codingman.tistory.com/49

 

[C#]OpenCvSharp 라이브러리 사용하기 #1

안녕하세요 저번 포스팅에 C#으로 OpenCvSharp 라이브러리를 등록하여 구현하는 포스팅을 준비하던중에 OpenCv 3,4 버전에서 오류가 발생하는 문제가 있다는 얘길 듣고 부랴부랴 포스팅 내용을 검토해봤는데 역시..

codingman.tistory.com

 

그럼 프로그램 동작을 위한 디자인 부분부터 설명하겠습니다

 

[디자인]

- 메뉴에 머쉰런닝 탭에 PCA 버튼을 만들고 학습과 검출의 세부 버튼을 생성합니다

 

- 학습과 검출 세부 버튼에 클릭시 동작 할 수 있도록 이벤트를 활성화 시킵니다

 

- 해당 이벤트에 다음과 같이 코딩합니다

 

* 학습

        private void 학습ToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            using (PCA pca = new PCA())
            using (IplImage temp = pca.LearnPCA())
            {
                if (temp == null)
                {
                    MessageBox.Show("데이타 파일 오류");
                    return;
                }
                result = temp.Clone();

            }
            pictureBoxIpl2.ImageIpl = result;
        }

 

*검출

        private void 검출ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (PCA pca = new PCA())
            {
                String[] temp = pca.recognize();
                if (temp == null) MessageBox.Show("오류발생 확인요망");
                for (int i = 0; i < temp.Length; i++)
                    listBox1.Items.Add(temp[i]);

            }
        }

 

- PCA 기능 동작을 위한 클래스 파일 생성합니다

 

- 생성된 클래스에 다음과 같이 소스코드를 작성 합니다

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

namespace OpenCV_V1
{
    class PCA : IDisposable
    {
        #region 초기값
        int nTrainFaces = 0;
        int nEigens = 0;

        IplImage result;
        IplImage[] faceImgArr;
        IplImage[] eigenVectArr;
        IplImage pAvgTrainImg;
        float[,] projectedTrainFaceMat;
        float[] eigenValMat;
        CvMat personNumTruthMat;

        public void Dispose()
        {
            if (result != null) Cv.ReleaseImage(result);
            if (!pAvgTrainImg.IsDisposed) pAvgTrainImg.Dispose();
            if (!faceImgArr[0].IsDisposed)
            {
                for (int i = 0; i < nTrainFaces; i++)
                {
                    faceImgArr[i].Dispose();
                }
            }
            if (!eigenVectArr[0].IsDisposed)
            {
                for (int i = 0; i < nEigens; i++)
                    eigenVectArr[i].Dispose();
            }
            if (!personNumTruthMat.IsDisposed) Cv.ReleaseMat(personNumTruthMat);
        }
        #endregion

        #region PCA 학습

        public unsafe IplImage LearnPCA()
        {
            //학습할 이미지 사진 파일명을 나열한 텍스파일 읽기
            nTrainFaces = loadFaceImgArray("train.txt");
            if (nTrainFaces < 2)
            {
                //MessageBox.Show("데이타 파일이 잘못 되었슴");
                return null;
            }

            doPCA();

            projectedTrainFaceMat = new float[nTrainFaces, nEigens];
            float[] tempProject = new float[nEigens];

            for (int i = 0; i < nTrainFaces; i++)
            {
                Cv.EigenDecomposite(faceImgArr[i], eigenVectArr, pAvgTrainImg, tempProject);//projectedTrainFaceMat[i]);
                projectedTrainFaceMat[i, 0] = tempProject[0];
                projectedTrainFaceMat[i, 1] = tempProject[1];
            }


            storeTraingData();
            return result;
        }

        private int loadFaceImgArray(String file)
        {
            //텍스트 파일에 나열되어진 파일명대로 이미지 읽어서 메모리에 저장하기   
            int i = 0;
            string[] lines = File.ReadAllLines(file); //학습할 파일명 획득
            faceImgArr = new IplImage[lines.Length]; //학습할 얼굴 갯수만큼 메모리 확보

            foreach (string line in lines) //텍스트 파일에서 읽은 파일명으로
            {
                faceImgArr[i] = new IplImage(lines[i], LoadMode.GrayScale); //사진이미지 읽어 오기
                i++;
            }
            return lines.Length;

        }

        private unsafe void doPCA()
        {
            CvTermCriteria calcLimit;
            CvSize faceImgSize;
            nEigens = nTrainFaces - 1;

            faceImgSize.Width = faceImgArr[0].Width;
            faceImgSize.Height = faceImgArr[0].Height;

            eigenVectArr = new IplImage[nEigens];

            for (int i = 0; i < nEigens; i++)
                eigenVectArr[i] = new IplImage(faceImgSize, BitDepth.F32, 1);

            eigenValMat = new float[nEigens];
            pAvgTrainImg = new IplImage(faceImgSize, BitDepth.F32, 1);
            calcLimit = new CvTermCriteria(CriteriaType.Iteration, nEigens, 1);
            Cv.CalcEigenObjects(faceImgArr, eigenVectArr, 0, calcLimit, pAvgTrainImg, eigenValMat);

        }

        //학습된 데이타 저장하기
        private void storeTraingData()
        {
            int[] faceID = { 1, 2, 4, 5 };//사실은 텍스트 파일에 같이 써야 하지만 분리하기 귀찮아서
            personNumTruthMat = new CvMat(1, nTrainFaces, MatrixType.S32C1, faceID);

            using (CvFileStorage fs = new CvFileStorage("facedata.xml", null, FileStorageMode.Write))
            using (CvMat eigen = new CvMat(1, nEigens, MatrixType.F32C1, eigenValMat))
            using (CvMat projected = new CvMat(nTrainFaces, nEigens, MatrixType.F32C1, projectedTrainFaceMat))
            {
                fs.WriteInt("nEigens", nEigens);
                fs.WriteInt("nTrainFaces", nTrainFaces);
                fs.Write("trainPersonNumMat", personNumTruthMat);
                fs.Write("eigenValMat", eigen);
                fs.Write("projectedTrainFaceMat", projected);
                fs.Write("avgTrainImg", pAvgTrainImg);

                for (int i = 0; i < nEigens; i++)
                {
                    String varName = String.Format("eigenVect_{0}", i);
                    fs.Write(varName, eigenVectArr[i]);
                }
                result = new IplImage(pAvgTrainImg.Size, BitDepth.U8, 1);
                //Currentley, bitmap converter can read not only BitDeth.U8 but also 32F
                Cv.CvtScale(pAvgTrainImg, result);
            }
        }
        #endregion

        #region PCA 인식

        public unsafe String[] recognize()
        {
            String[] str;

            int nTestFaces = 0; //비교할 이미지 숫자
            CvMat trainPersonNumMat; //학습으로 저장된
            float[] projectedTestFace;

            nTestFaces = loadFaceImgArray("test.txt"); //얼굴갯수 리턴
            projectedTestFace = new float[nEigens];

            if (!loadTranningData(out trainPersonNumMat)) return null;

            projectedTrainFaceMat = new float[trainPersonNumMat.Rows, trainPersonNumMat.Cols];//배열확보
            float[] tempProject = new float[nEigens];

            for (int i = 0; i < trainPersonNumMat.Rows; i++)
                for (int j = 0; j < trainPersonNumMat.Cols; j++)
                    projectedTrainFaceMat[i, j] = (float)Cv.mGet(trainPersonNumMat, i, j); //저장된 값 이동

            str = new String[nTestFaces]; //비교할 파일 갯수 만큼

            for (int i = 0; i < nTestFaces; i++)
            {
                int iNearest, nearest, truth;

                Cv.EigenDecomposite(faceImgArr[i], eigenVectArr, pAvgTrainImg, tempProject);

                iNearest = findNearestNeighbor(tempProject); //근접도
                truth = (int)Cv.mGet(personNumTruthMat, 0, i); //실제 명기된 ID
                nearest = (int)Cv.mGet(personNumTruthMat, 0, iNearest);

                str[i] = String.Format("nearest ID ={0}, Real ID = {1}", nearest, truth);

            }
            return str;


        }

        private int findNearestNeighbor(float[] projecTestFace)
        {
            double leastDistSq = 1e12;// DBL_MAX;
            int iTrain, iNearest = 0;

            for (iTrain = 0; iTrain < nTrainFaces; iTrain++) //비교할 얼굴 갯수
            {
                double distSq = 0;

                for (int i = 0; i < nEigens; i++)
                {
                    float d_i = projecTestFace[i] - projectedTrainFaceMat[iTrain, i];//학습 파일에서 읽은 값과
                    //distSq += d_i*d_i / eigenValMat->data.fl;  // 마할노비스 거리
                    distSq += d_i * d_i; // 유클리디안 거리
                }

                if (distSq < leastDistSq) //더 근접한 값이 있는지 찾음
                {
                    leastDistSq = distSq;
                    iNearest = iTrain;     //몇번째 얼굴인지
                }
            }
            return iNearest;
        }

        //저장된 학습 데이타 읽어 오기
        private bool loadTranningData(out CvMat pTrainPersonNumMat)
        {
            CvFileNode param;

            using (CvFileStorage fs = new CvFileStorage("facedata.xml", null, FileStorageMode.Read))
            {
                pTrainPersonNumMat = null;
                if (fs == null) return false;

                nEigens = fs.ReadIntByName(null, "nEigens");//고유치
                nTrainFaces = fs.ReadIntByName(null, "nTrainFaces"); //학습 갯수
                param = Cv.GetFileNodeByName(fs, null, "trainPersonNumMat");
                personNumTruthMat = fs.Read<CvMat>(param); //얼굴ID

                param = Cv.GetFileNodeByName(fs, null, "projectedTrainFaceMat");//학습된 값
                pTrainPersonNumMat = fs.Read<CvMat>(param);

                param = Cv.GetFileNodeByName(fs, null, "avgTrainImg"); //학습한 평균 이미지
                pAvgTrainImg = fs.Read<IplImage>(param);
                eigenVectArr = new IplImage[nEigens];
                for (int i = 0; i < nEigens; i++)
                {
                    String varName = String.Format("eigenVect_{0}", i);
                    param = Cv.GetFileNodeByName(fs, null, varName);
                    eigenVectArr[i] = fs.Read<IplImage>(param);
                }

            }
            int[] faceID = { 1, 2, 4, 5, 1, 2, 4, 5, 1, 2, 4, 5, 1, 2, 4, 5, 1, 2, 4, 5, 1, 2, 4, 5 };//사실은 텍스트 파일에 같이 써야 하지만 분리하기 귀찮아서
            personNumTruthMat = new CvMat(1, nTrainFaces, MatrixType.S32C1, faceID);//ID 배정

            return true;
        }
        #endregion
    }
}

 

- 해당 클래스가 정상 동작을 하기 위한 기초 첨부파일을 다운 받습니다

S1245_XML.zip
0.39MB

 

- 첨부파일의 내용을 Debug 폴더에 붙여 넣기 합니다

 

*첨부파일 내용

1) s1,s2,s4,s5 비교 그룹 이미지 (형식 : bpm)

   (샘플 얼굴 이미지 사진 입니다)

 

2) test.txt

   : 그룹에 포함되어 있는 파일 정보

 

3) train.txt

   : 그룹에 대표 이미지 지정

 

3) facedata.xml

   : 학습 정보 기록을 위한 xml

 

- 실행 한뒤 각 그룹 이미지 폴더에서 1.bpm을 불러와 검출을 시도합니다

 

[결과 창]

 

 

- 저는 샘플 사진으로 트와이스 맴버를 bpm으로 전환하여 비교 하여 다른 얼굴들과 어느정도 분석이 가능한지

  테스트를 해봤습니다(좋아하니깐!)

 

- 쯔위는 s5 그룹에 대표입니다

 

- 좌측 listBox1의 결과를 분석하면 해당 폴더의 그룹별 매칭 상태가 나타나는데 90%로 정도 쯔위를 판별해

  내는것을 볼수 있습니다

 

- 해당 자료를 구현하여 다른 샘플을 통해 다른 각도로 수정하여 응용해 보시기 바랍니다

 

반응형
반응형

안녕하세요

 

이번 포스팅에서는 OpenCvSharp 기능을 통해서 이미지속의 피부색을 검출하고 검출된 영역에

 

윤곽선을 표기하고 표시된 윤곽선 내부에 포함된 오목 부분을 추가 검출하여 손가락의 구분을 

 

나누어 보도록 하겠습니다

 

이번 포스팅도 OpenCvSharp 기능 구현 포스팅으로 처음 포스팅 내용과 이어지는 부분이

 

존재합니다

 

이해가 잘 되지 않으시다면 앞전 포스팅을 읽어보시고 보신다면 이해가 좀 더 빠를거 같아서

 

아래 OpenCvSharp의 처음 포스팅을 링크해 드리겠습니다

https://codingman.tistory.com/49

 

[C#]OpenCvSharp 라이브러리 사용하기 #1

안녕하세요 저번 포스팅에 C#으로 OpenCvSharp 라이브러리를 등록하여 구현하는 포스팅을 준비하던중에 OpenCv 3,4 버전에서 오류가 발생하는 문제가 있다는 얘길 듣고 부랴부랴 포스팅 내용을 검토해봤는데 역시..

codingman.tistory.com

[디자인]

- 메뉴버튼을 생성합니다

 

-생성한 메뉴에 클릭시 발생할 이벤트를 생성합니다

 

-생성된 클릭 이벤트 부분에 아래와 같이 코딩해줍니다

 

[Source Code]

        private void 피부색검출ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ConvextyDefect Cx = new ConvextyDefect();
            Cx.ConvextyDefectb();
        }

 

- 그다음 피부색 검출을 위한 기능 구현을 위해 클래스 파일을 생성합니다

   (클래스명칭은 ConvextyDefect.cs로 생성하였습니다.)

 

[Source Code]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using OpenCvSharp.Blob;
using OpenCvSharp.CPlusPlus;
using OpenCvSharp.UserInterface;

namespace OpenCV_V1
{
    class ConvextyDefect
    {
        public void ConvextyDefectb()
        {
            using (IplImage imgSrc = new IplImage("hand.jpg", LoadMode.Color))
            using (IplImage imgHSV = new IplImage(imgSrc.Size, BitDepth.U8, 3))
            using (IplImage imgH = new IplImage(imgSrc.Size, BitDepth.U8, 1))
            using (IplImage imgS = new IplImage(imgSrc.Size, BitDepth.U8, 1))
            using (IplImage imgV = new IplImage(imgSrc.Size, BitDepth.U8, 1))
            using (IplImage imgBackProjection = new IplImage(imgSrc.Size, BitDepth.U8, 1))
            using (IplImage imgFlesh = new IplImage(imgSrc.Size, BitDepth.U8, 1))
            using (IplImage imgHull = new IplImage(imgSrc.Size, BitDepth.U8, 1))
            using (IplImage imgDefect = new IplImage(imgSrc.Size, BitDepth.U8, 3))
            using (IplImage imgContour = new IplImage(imgSrc.Size, BitDepth.U8, 3))
            using (CvMemStorage storage = new CvMemStorage())
            {
                //RGB -> HSV
                Cv.CvtColor(imgSrc, imgHSV, ColorConversion.BgrToHsv);
                Cv.CvtPixToPlane(imgHSV, imgH, imgS, imgV, null);
                IplImage[] hsvPlanes = { imgH, imgS, imgV };

                //피부색 영역을 구한다
                RetrieveFleshRegion(imgSrc, hsvPlanes, imgBackProjection);
                
                //최대의 면적의 영역을 남긴다
                FilterByMaximalBolb(imgBackProjection, imgFlesh);
                Interpolate(imgFlesh);

                //윤곽을 구한다
                CvSeq<CvPoint> contours = FindContours(imgFlesh, storage);
                if(contours != null)
                {
                    Cv.DrawContours(imgContour, contours, CvColor.Red, CvColor.Green, 0, 3, LineType.AntiAlias);

                    //요철을 구한다
                    int[] hull;
                    Cv.ConvexHull2(contours, out hull, ConvexHullOrientation.Clockwise);
                    Cv.Copy(imgFlesh, imgHull);

                    DrawConvexHull(contours, hull, imgHull);

                    //오목한 상태 결손을 구한다
                    Cv.Copy(imgContour, imgDefect);
                    CvSeq<CvConvexityDefect> defect = Cv.ConvexityDefects(contours, hull);
                    DrawDefects(imgDefect, defect);

                }
                using (new CvWindow("src", imgSrc))
                using (new CvWindow("back projection", imgBackProjection))
                using (new CvWindow("hull", imgHull))
                using (new CvWindow("defect", imgDefect))
                {
                    Cv.WaitKey();
                }
            }
        }

        ///<summary>
        ///백 프로젝션에 의해 피부색 영역을 구한다
        ///</summary>
        ///<param name="imgSrc"></param>
        ///<param name="hsvPlanes"></param>
        ///<param name="imgDst"></param>
        private void RetrieveFleshRegion(IplImage imgSrc, IplImage[] hsvPlanes, IplImage imgDst)
        {
            int[] histSize = new int[] { 30, 32 };
            float[] hRanges = { 0.0f, 20f };
            float[] sRanges = { 50f, 255f };
            float[][] ranges = { hRanges, sRanges };
            imgDst.Zero();
            using (CvHistogram hist = new CvHistogram(histSize, HistogramFormat.Array, ranges, true))
            {
                hist.Calc(hsvPlanes, false, null);
                float minValue, maxValue;
                hist.GetMinMaxValue(out minValue, out maxValue);
                hist.Normalize(imgSrc.Width * imgSrc.Height * 255 / maxValue);
                Cv.CalcBackProject(hsvPlanes, imgDst, hist);
            }
        }

        ///<summary>
        ///라벨링에 의해 최대 면적의 영역을 남긴다
        ///</summary>
        ///<param name="imgSrc"></param>
        ///<param name="imgDst"></param>
        private void FilterByMaximalBolb(IplImage imgSrc, IplImage imgDst)
        {
            CvBlobs blobs = new CvBlobs();

            imgDst.Zero();
            blobs.Label(imgSrc);
            CvBlob max = blobs.GreaterBlob();
            if (max == null)
            {
                return;
            }
            blobs.FilterByArea(max.Area, max.Area);
            blobs.FilterLabels(imgDst);
        }

        ///<summary>
        ///결손 영역을 보완한다
        ///</summary>
        ///<param name="img"></param>
        private void Interpolate(IplImage img)
        {
            Cv.Dilate(img, img, null, 2);
            Cv.Erode(img, img, null, 2);
        }

        ///<summary>
        ///윤곽을 얻는다
        ///</summary>
        ///<param name="img"></param>
        ///<param name="storage"></param>
        ///<returns></returns>
        private CvSeq<CvPoint> FindContours(IplImage img, CvMemStorage storage)
        {
            //윤곽 추출
            CvSeq<CvPoint> contours;
            using (IplImage imgClone = img.Clone())
            {
                Cv.FindContours(imgClone, storage, out contours);
                if(contours == null)
                {
                    return null;
                }
                contours = Cv.ApproxPoly(contours, CvContour.SizeOf, storage, ApproxPolyMethod.DP, 3, true);
            }

            //제일 긴 것 같은 윤곽만을 얻는다
            CvSeq<CvPoint> max = contours;
            for(CvSeq<CvPoint> c = contours; c != null; c=c.HNext)
            {
                if(max.Total < c.Total)
                {
                    max = c;
                }
            }
            return max;
        }

        ///<summary>
        ///ConvexHull 그리기
        ///</summary>
        ///<param name="contours"></param>
        ///<param name="hull"></param>
        ///<param name="img"></param>
        private void DrawConvexHull(CvSeq<CvPoint> contours, int[] hull, IplImage img)
        {
            CvPoint pt0 = contours[hull.Last()].Value;

            foreach(int idx in hull)
            {
                CvPoint pt = contours[idx].Value;
                Cv.Line(img, pt0, pt, new CvColor(255, 255, 255));
                pt0 = pt;
            }

        }

        ///<summary>
        ///ConvexityDefects 그리기
        ///</summary>
        ///<param name="img"></param>
        ///<param name="defect"></param>
        private void DrawDefects(IplImage img, CvSeq<CvConvexityDefect> defect)
        {
            int count = 0;
            foreach(CvConvexityDefect item in defect)
            {
                CvPoint p1 = item.Start, p2 = item.End;
                double dist = Getdistance(p1, p2);
                CvPoint2D64f mid = GetMidpoint(p1, p2);
                img.DrawLine(p1, p2, CvColor.White, 3);
                img.DrawCircle(item.DepthPoint, 10, CvColor.Green, -1);
                img.DrawLine(mid, item.DepthPoint, CvColor.White, 1);
                Console.WriteLine("No:{0} Depth:{1} Dist:{2}", count, item.Depth, dist);
                count++;
            }
        }

        ///<summary>
        ///2점간의 거리를 얻는다
        ///</summary>
        ///<param name="p1"></param>
        ///<param name="p2"></param>
        ///<returns></returns>
        private double Getdistance(CvPoint p1, CvPoint p2)
        {
            return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
        }

        ///<summary>
        ///2점의 중점을 얻는다
        ///</summary>
        ///<param name="p1"></param>
        ///<param name="p2"></param>
        ///<returns></returns>
        private CvPoint2D64f GetMidpoint(CvPoint p1, CvPoint p2)
        {
            return new CvPoint2D64f
            {
                X = (p1.X + p2.X) / 2.0,
                Y = (p1.Y + p2.Y) / 2.0
            };
        }
    }
}

 

- "hand.jgp"파일은 해당 프로젝트의 Debug 폴더에 넣으시면 되고 예제 파일은 제가 사용한 파일을 업로드

 

하겠습니다

 

hand.JPG
0.03MB

 

- 이렇게 이미지 파일을 넣은 뒤 빌드하여 실행하시면 다음과 같은 결과가 나타나게 됩니다

 

[결과 창]

*원본이미지

* Back projection

 

*hull

 

*defect

반응형
반응형

안녕하세요

 

벌써 OpenCv에 대해 연구한지 꽤 시간이 흘렀습니다

 

점점 난이도가 올라가면서 포스팅 자료 만드는 시간이 오래 걸리고 있습니다ㅠ

 

쉬운 기능만 구현하기에는 연습에 의미가 없고...

 

그렇다고 재미있는 기능을 따라가자니 너무 어렵고...ㅎㅎㅎ

 

그래도 존버는 승리한다는 말이 있듯이 포기하지말고 힘내서 차근차근 하나씩 도전 해보도록 하겠습니다

 

이번 업로드할 포스팅 주제는 픽셀변환입니다

 

OpenCvSharp의 함수중 CvColor을 사용하여 R,G,B 영역을 변경시켜 전체영역의 색상을 변경 시키는 것인데요

 

기존에 포스팅했던 것을 쭉 같이 봐오셨다면 그렇게 어렵게 느껴지지는 않을거 같습니다

 

이전 자료는 블로그 내에서 찾아보시거나 아래 링크를 확인하시면 찾기 쉽습니다

 

https://codingman.tistory.com/49

 

[C#]OpenCvSharp 라이브러리 사용하기 #1

안녕하세요 저번 포스팅에 C#으로 OpenCvSharp 라이브러리를 등록하여 구현하는 포스팅을 준비하던중에 OpenCv 3,4 버전에서 오류가 발생하는 문제가 있다는 얘길 듣고 부랴부랴 포스팅 내용을 검토해봤는데 역시..

codingman.tistory.com

그러면 픽셀변환에 대해서 포스팅 시작하겠습니다

 

처음에 디자인으로 버튼 생성 부터 포스팅 할꼐요

 

[디자인]

- 필터 메뉴에서 하부메뉴로 필셀변환이라고 등록합니다

 

- 그다음 픽셀변환 버튼에 클릭시 발생할 이벤트를 생성합니다

 

- 클릭 이벤트 발생시 시행될 코드는 다음과 같습니다

        private void 픽셀변환ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (src == null) return;
            using (pix Px = new pix())
            using (IplImage temp = Px.PixelAccess(src))
            {
                result = temp.Clone();

            }
            pictureBoxIpl2.ImageIpl = result;
        }

 

- 그리고 필셀변환 기능을 수행할 클래스를 생성해야 겠지요?

 

- 저는 Pix.cs라는 명칭으로 생성하였습니다

 

[pix.cs Source Code]

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

namespace OpenCV_V1
{
    class pix : IDisposable
    {
        IplImage Px;

        public IplImage PixelAccess(IplImage src)
        {

            using (IplImage img = src.Clone())
            {
                // (1) 픽셀 데이타(R,G,B)를 차례차례 취득해, 변경한다
                // 저속이지만 간단한 방법

                for (int y = 0; y < img.Height; y++)
                {
                    for (int x = 0; x < img.Width; x++)
                    {
                        CvColor c = img[y, x];
                        img[y, x] = new CvColor
                        {
                            B = (byte)Math.Round(c.B * 0.7 + 10),
                            G = (byte)Math.Round(c.G * 1.0),
                            R = (byte)Math.Round(c.R * 0.0),
                        };
                    }
                }


                /*
                // 포인터를 사용한 고속의 방법
                unsafe
                {
                    byte* ptr = (byte*) img.ImageData;    // 화소 데이터에의 포인터
                    for (int y = 0; y < img.Height; y++)
                    {
                        for (int x = 0; x < img.Width; x++)
                        {
                            int offset = (img.WidthStep * y) + (x * 3);
                            byte b = ptr[offset + 0];    // B
                            byte g = ptr[offset + 1];    // G
                            byte r = ptr[offset + 2];    // R
                            ptr[offset + 0] = (byte)Math.Round(b * 0.7 + 10);
                            ptr[offset + 1] = (byte)Math.Round(g * 1.0);
                            ptr[offset + 2] = (byte)Math.Round(r * 0.0);
                        }
                    }
                }
                //*/
                /*
                // unsafe는 아니고 IntPtr로 시도한 방법 (VB.NET 방향)
                
                    IntPtr ptr = img.ImageData;
                    for (int y = 0; y < img.Height; y++)
                    {
                        for (int x = 0; x < img.Width; x++)
                        {
                            int offset = (img.WidthStep * y) + (x * 3);
                            byte b = Marshal.ReadByte(ptr, offset + 0);    // B
                            byte g = Marshal.ReadByte(ptr, offset + 1);    // G
                            byte r = Marshal.ReadByte(ptr, offset + 2);    // R
                            Marshal.WriteByte(ptr, offset + 0, (byte)Math.Round(b * 0.7 + 10));
                            Marshal.WriteByte(ptr, offset + 1, (byte)Math.Round(g * 1.0));
                            Marshal.WriteByte(ptr, offset + 2, (byte)Math.Round(r * 0.0));
                        }
                    }
                
                //*/
                //*/

                Px = img.Clone();
                return Px;

            }
        }
        public void Dispose()
        {
            if (Px != null) Cv.ReleaseImage(Px);
        }
    }
}

 

- 이렇게 클래스 생성과 코딩까지 완료 하셨다면 빌드 하시고 실행시켜 픽셀변환을 실행해 보세요

 

[결과 창]

반응형
반응형

안녕하세요

 

요즘 하루에 2개 이상씩 포스팅하고 있네요ㅎㅎ

 

얼른 목표로 하고 있는 이미지 기법등을 공부해서 C#으로 이미지 판독 프로그램이나

 

OCR 같은 지능형 프로그램을 만들어보는게 목표인데

 

아직 OpenCvSharp만 디딥다 파고 있습니다ㅎㅎ

 

기본 정보를 모으고 그걸 구현해보고 오류 수정하고 매일 이걸 반복하고 있으니 이젠 제가

 

블로거인지...프로그래머인지...사진편집가인지 헷갈리네요ㅎㅎㅎ

 

아무튼 OpenCv 포스팅을 이어서 하겠습니다

 

이번 포스팅은 이미지 와핑이라는 것인데요

 

와핑이라는게 이미지를 왜곡시키는 기능인데요

 

이미지를 찌그러 트리거나 회전시키거나 뭐 이런 기능을 다 와핑이라고 표현하는거 같습니다

 

그 와핑이라는것을 C# OpenCvSharp를 이용해서 구현해 보도록 할께요

 

처음 오시느 분들은 앞 포스팅에서 라이브러리 등록부터 보시고 오시면 좋을거 같습니다

 

https://codingman.tistory.com/49

 

[C#]OpenCvSharp 라이브러리 사용하기 #1

안녕하세요 저번 포스팅에 C#으로 OpenCvSharp 라이브러리를 등록하여 구현하는 포스팅을 준비하던중에 OpenCv 3,4 버전에서 오류가 발생하는 문제가 있다는 얘길 듣고 부랴부랴 포스팅 내용을 검토해봤는데 역시..

codingman.tistory.com

 

[디자인]

- 메뉴에 와핑 버튼을 생성해줍니다

 

- 와핑 버튼에 아래 같이 클릭 이벤트를 생성해 주구요

 

 

- 늘 반복적으로 하는 행위라서 간단하게 넘어가겠습니다

 

- 클릭 이벤트 지점에 다음과 같이 코딩 합니다

 

        private void 와핑ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (src == null) return;
            using (perspect Pp = new perspect())
            using (IplImage temp = Pp.Perspective(src))
            {
                result = temp.Clone();

            }
            pictureBoxIpl2.ImageIpl = result;
        }

 

- 그 다음 이미지를 와핑 시키기 위해서 사용할 클래스를 생성해줍니다

 

 

- 그리고 생성된 클래스 파일에 아래와 같이 코딩해 줍니다

 

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

namespace OpenCV_V1
{
    class perspect : IDisposable
    {
        IplImage Pp;
        public IplImage Perspective(IplImage srcImg)
        {
            // cvGetPerspectiveTransform + cvWarpPerspective
            // 화상상의 4점에 대응하는 투시 투영 변환 행렬을 계산해, 그 행렬을 이용해 화상 전체의 투시 투영 변환을 실시한다.

            // (1) 화상을 읽어들여, 출력용 화상 영역의 확보를 행한다
            using (IplImage dstImg = srcImg.Clone())
            {
                // (2) 사각형의 변환전과 변환 후의 대응하는 정점을 각각 세트 해
                //    cvWarpPerspective를 이용해 투시 투영 변환 행렬을 요구한다
                CvPoint2D32f[] srcPnt = new CvPoint2D32f[4];
                CvPoint2D32f[] dstPnt = new CvPoint2D32f[4];
                srcPnt[0] = new CvPoint2D32f(150.0f, 150.0f);
                srcPnt[1] = new CvPoint2D32f(150.0f, 300.0f);
                srcPnt[2] = new CvPoint2D32f(350.0f, 300.0f);
                srcPnt[3] = new CvPoint2D32f(350.0f, 150.0f);
                dstPnt[0] = new CvPoint2D32f(200.0f, 200.0f);
                dstPnt[1] = new CvPoint2D32f(150.0f, 300.0f);
                dstPnt[2] = new CvPoint2D32f(350.0f, 300.0f);
                dstPnt[3] = new CvPoint2D32f(300.0f, 200.0f);
                using (CvMat mapMatrix = Cv.GetPerspectiveTransform(srcPnt, dstPnt))
                {
                    // (3) 지정된 아핀 행렬에 의해, cvWarpAffine를 이용해 화상을 회전시킨다
                    Cv.WarpPerspective(srcImg, dstImg, mapMatrix, Interpolation.Linear | Interpolation.FillOutliers, CvScalar.ScalarAll(100));
                    Pp = dstImg.Clone();
                    return Pp;

                }
            }
        }
        public void Dispose()
        {
            if (Pp != null) Cv.ReleaseImage(Pp);
        }
    }
}

 

- 그런 다음 이미지를 불러오고 와핑 시키면 다음과 같은 결과가 나타나게 됩니다

 

[결과 창]

 

반응형
반응형

안녕하세요

 

저번 포스팅에서 점멸을 포함하는 사각형 그리기에 대해 포스팅 했었는데요

 

https://codingman.tistory.com/67

 

[C#] OpenCvSharp 포인트를 찾아 영역 지정 하기

안녕하세요 이번 포스팅은 저번 포스팅에 이어서 랜덤 포인터로 점을 그리고 그 점들을 모두 포함하는 사각형을 그리는 방법을 포스팅 할려고 합니다 포인터 영역 지정을 할때 응용하면 참 좋을거 같은데요 저번..

codingman.tistory.com

 

이번 포스팅에서는 형태와 상관없이 외곽 점들을 연결시켜 영역을 지정하는것을 해볼려고 합니다

 

OpenCvSharp 함수 중에 ConvexHull2 이라는 함수를 사용해서 구현을 해볼려고 합니다

 

이번 포스팅 역시 OpenCvSharp를 이어서 진행 되니 처음 시작 하시는 분의 경우 앞 포스팅을 확인하시기

 

바랍니다

 

https://codingman.tistory.com/49

 

[C#]OpenCvSharp 라이브러리 사용하기 #1

안녕하세요 저번 포스팅에 C#으로 OpenCvSharp 라이브러리를 등록하여 구현하는 포스팅을 준비하던중에 OpenCv 3,4 버전에서 오류가 발생하는 문제가 있다는 얘길 듣고 부랴부랴 포스팅 내용을 검토해봤는데 역시..

codingman.tistory.com

 

처음에 할 것을 역시 버튼 만들기 겠죠?

 

[디자인]

- Menu에서 다음과 같이 메뉴를 등록시켜 주세요

 

- 다음은 컨벡스 헐이라는 메뉴를 클릭 했을때 동작 할 수 있도록 클릭 이벤트를 생성 합니다

 

- 클릭 이벤트를 생성한뒤 해당 이벤트 발생 지점에 다음과 같으 코딩을 해줍니다

        private void 컨벡스헐ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CvWindow wind = new CvWindow("결과창");
            convex Cx = new convex();
            Cx.ConvexHull(this.pictureBoxIpl1, this.pictureBoxIpl2, wind);
        }

 

- 랜덤 점 생성과 생성된 점들의 외곽을 구하고 그 외곽을 연결시키는 기능을 실행할 클래스를 생성합니다

  (클래스 파일 생성은 첫 포스팅에서 확인하세요)

 

- 생성된 클래스 파일에 다음과 같은 기능을 할 코딩 소스를 넣어 짭니다

  (오류 수정과 테스트로 인한 주석이 존재하니 양해 바랍니다)

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

namespace OpenCV_V1
{
    class convex
    {
        IplImage _dst;

        public void ConvexHull(PictureBoxIpl p1, PictureBoxIpl p2, CvWindow wind)
        {

            IplImage img = Cv.CreateImage(new CvSize(500, 500), BitDepth.U8, 3);
            Random rand = new Random();

            int count = rand.Next() % 100 + 1;

            //임의의 점 생성
            CvPoint[] ptseq = new CvPoint[count];
            for (int i = 0; i < count; i++)
            {
                ptseq[i] = new CvPoint
                {
                    X = rand.Next() % (img.Width),
                    Y = rand.Next() % (img.Height)
                };

                /*
                CvPoint pt = new CvPoint
                {
                    X = rand.Next() % (img.Width / 2) + img.Width / 4,
                    Y = rand.Next() % (img.Height / 2) + img.Height / 4
                };
                Cv.SeqPush<CvPoint>(ptseq, pt);
                */
            }


            // 점 그리기
            Cv.Zero(img);
            foreach(CvPoint pt in ptseq)
            {
                Cv.Circle(img, pt, 2, new CvColor(255, 0, 0), -1);
            }

            /*
            for (int i = 0; i < count; i++)
            {
                CvPoint pt = Cv.GetSeqElem<CvPoint>(ptseq, i).Value;
                Cv.Circle(img, pt, 2, new CvColor(255, 0, 0), -1);
            }
            */

            //임의의 점 그린 사진 보여주기
            p1.ImageIpl = img;

            _dst = img.Clone();

            // Hull 찾기
            CvPoint[] hull;
            Cv.ConvexHull2(ptseq, out hull, ConvexHullOrientation.Clockwise);

            //Hull 그리기
            CvPoint pt0 = hull[hull.Length - 1];
            foreach (CvPoint pt in hull)
            {
                Cv.Line(_dst, pt0, pt, CvColor.Green, 3, LineType.AntiAlias);
                pt0 = pt;
            }

            /*
            for (int i = 0; i < hull.Total; i++)
            {
                CvPoint pt = Cv.GetSeqElem<Pointer<CvPoint>>(hull, i).Value.Entity;             // CvPoint pt = **CV_GET_SEQ_ELEM( CvPoint*, hull, i );
                Cv.Line(img, pt0, pt, new CvColor(0, 255, 0));
                pt0 = pt;
            }
            */

            p2.ImageIpl = _dst;
            wind.Image = _dst;
            img.Dispose();
        }
    }
}

 

- 필드 후 실행한 결과 화면 입니다

 

반응형
반응형

안녕하세요

 

이번 포스팅은 저번 포스팅에 이어서 랜덤 포인터로 점을 그리고 그 점들을 모두 포함하는 사각형을

 

그리는 방법을 포스팅 할려고 합니다

 

포인터 영역 지정을 할때 응용하면 참 좋을거 같은데요

 

저번 포스팅에서 사용한 원형, 사각형 검출 포스팅에 이어져서 보시면 좋을거 같습니다

 

https://codingman.tistory.com/66

 

[C#] OpenCvSharp 이미지 사각, 원형 검출하기

안녕하세요 "코딩연습생"입니다ㅎㅎ 저번 포스팅에서 컨투어(윤곽선) 검출을 포스팅 했었는데요 https://codingman.tistory.com/65 [C#] OpenCvSharp 이미지 컨투어(윤곽선) 찾기 안녕하세요 요즘은 매일 OpenCvSh..

codingman.tistory.com

반복 내용은 생략하고 진행 하도록 할께요

 

[디자인]

- 아래 이미지와 같이 메뉴를 생성해 주세요

 

- 생성된 메뉴에 클릭 이벤트 지정

[Source Code]

        private void 바운딩ToolStripMenuItem_Click(object sender, EventArgs e)
        {

            CvWindow wind = new CvWindow("결과창");
            Bounding Bd = new Bounding();
            Bd.BoundingRect(this.pictureBoxIpl1, this.pictureBoxIpl2, wind);
            
        }

 

- 임의 점과 점들을 포함하는 사각형을 그리기 위한 클래스 생성

[Source Code]

using System;
using System.Collections.Generic;
using System.Text;
using OpenCvSharp;
using OpenCvSharp.UserInterface;

namespace OpenCV_V1
{
    class Bounding
    {
        public void BoundingRect(PictureBoxIpl p1, PictureBoxIpl p2, CvWindow wind)
        {
            // cvBoundingRect 
            // 점렬을 포함 하는 사각형을 구한다

            // (1) 화상과 메모리스트레이지를 확보해 초기화한다
            using (IplImage img = new IplImage(640, 480, BitDepth.U8, 3))
            using (CvMemStorage storage = new CvMemStorage(0))
            {
                img.Zero();
                CvRNG rng = new CvRNG(DateTime.Now);
                // (2) 점렬을 생성한다
                ///*
                // 간단한 방법 (보통 배열을 사용한다)
                CvPoint[] points = new CvPoint[50];
                for (int i = 0; i < 50; i++)
                {
                    points[i] = new CvPoint()
                    {
                        X = (int)(rng.RandInt() % (img.Width / 2) + img.Width / 4),
                        Y = (int)(rng.RandInt() % (img.Height / 2) + img.Height / 4)
                    };
                    img.Circle(points[i], 3, new CvColor(0, 255, 0), Cv.FILLED);
                }

                p1.ImageIpl = img;

                // (3) 점렬을 포함 하는 사각형을 구해 그린다
                CvRect rect = Cv.BoundingRect(points);
                img.Rectangle(new CvPoint(rect.X, rect.Y), new CvPoint(rect.X + rect.Width, rect.Y + rect.Height), new CvColor(255, 0, 0), 2);

                p2.ImageIpl = img;
                wind.Image = img;

            }
        }
    }
}

 

- 빌드 후 오류 없이 빌드가 된다면 실행 하게 되면 다음과 같은 결과가 나타납니다

 

[결과 창]

반응형

+ Recent posts