차량의 뒷모양은 매우 복잡하여서 영상에서도 매우 복잡한 화소 변화로 인해 에지 특징이 잘 나타난다특히 수직 방향의 화소 변화가 두드러지기 때문에 수직 에지 성분이 잘 나타난다.  아래 그림은 수직 에지를 분석한 부분이다.


수직 방향 에지를 분석하여 전체 영역에서 흰색 부분만 추출하기 위해 침식 팽창 연산을 하여 차량 뒷부분을 뚜렷하게 강조 하였다.

 


해당 부분에서 가장 흰색영역이 큰 부분이 우리와 가장 인접한 차량이 된다해당 프로젝트에서 필요한 정보는 앞차와의 거리를 인식하기 위함 이기 때문에 앞차만을 위한 관심 영역을 지정하여 진행 하였다.



이 부분에서 차선을 기반으로 관심영역을 지정해 주면 앞차만을 인식 할 수 있다.


 

 첫번째 결과 이다차량을 정확하게 잡아주기는 하나 앞차선에 대한 관심영역 설정이 잘못되어 앞차량 보다 엣지성분이 많은 트럭을 잡게 되었다.




'Programing > OpenCV' 카테고리의 다른 글

제스쳐 인식  (0) 2016.11.30
제스쳐 인식  (0) 2016.11.30
플러드필  (0) 2016.11.30
Opencv C# 모폴로지  (0) 2016.11.30
영상 모폴로지 자체 커널 만들기  (0) 2016.11.30


'Programing > OpenCV' 카테고리의 다른 글

수직에지기반 차량인식  (0) 2016.11.30
제스쳐 인식  (0) 2016.11.30
플러드필  (0) 2016.11.30
Opencv C# 모폴로지  (0) 2016.11.30
영상 모폴로지 자체 커널 만들기  (0) 2016.11.30



핸드 트래킹을 통해 손의 제스쳐를 이용해 컴퓨터를 제어할 수 있는 프로그램제작을 위한 예광탄을 만들었다.

RGB 컬러모델을 HSV로 변환하여 피부영역을 구하고해당 피부영역의 최대 면적의 영역을 남긴다해당 영역의 윤곽을 구하고 영역 중의 가장 긴 부분을 손가락으로 판정하였으며판정 결과 오차와 정확한 손가락 검출이 불가능하여 손의 무게중심을 이용하는 방법으로 테스트 해봐야 할 것 같다.






class Gestures

    {

 

        int TPL_WIDTH = MouseSystem.img_move.Width;

        int TPL_HEIGHT = MouseSystem.img_move.Height;

       

        const double THRESHOLD = 0.3;

 

        IplImage tpl, tm;

        int object_x0, object_y0;

       

 

        IplImage imgHSV;

        IplImage imgH;

        IplImage imgS;

        IplImage imgV;

        IplImage imgBackProjection;

        IplImage imgFlesh;

        IplImage imgHull;

        IplImage imgDefect;

        IplImage imgContour;

        IplImage[] hsvPlanes = new IplImage[3];

        IplImage imgclick;

        IplImage imgmove;

        CvMemStorage storage = new CvMemStorage();

 

        CvHistogram hist;

       

        CvPoint p1, p2;

        public Gestures()

        {

        }

       

        unsafe Point getCenter(IplImage img)

        {

            float []mask = new float[3];

            IplImage dist;

            IplImage dist8u;

            IplImage dist32s;

            int max;

            Point p = new Point();

            byte* ptr;

            dist = Cv.CreateImage(Cv.GetSize(img), BitDepth.F32, 1);

            dist8u = Cv.CloneImage(img);

            dist32s = Cv.CreateImage(Cv.GetSize(img), BitDepth.S32, 1);

 

            //거리변환 행렬 생성

            mask[0] = 1f;

            mask[1] = 1.5f;

 

            Cv.DistTransform(img, dist, DistanceType.User, 3, mask, null);

 

            Cv.ConvertScale(dist, dist, 10000);

            Cv.Pow(dist, dist, 0.5);

 

            Cv.ConvertScale(dist, dist32s, 1.00.5);

            Cv.AndS(dist32s, Cv.ScalarAll(255), dist32s, null);

            Cv.ConvertScale(dist32s, dist8u, 10);

            max = 0;

            for (int i = max; i < dist8u.Height; i++)

            {

                int index = i * dist8u.WidthStep;

                for (int j = 0; j<dist8u.Width; j++)

                {

                    ptr = (byte*)img.ImageData;                   

                    if((char)ptr[index +j] > max)

                    {

                        max = (char)dist8u[index + j];

                        p.X = j;

                        p.Y = i;

                    }                   

                }

            }

            return p;

 

        }

        public void Convexty(IplImage imgSrc)

        {

            try

            {

                imgHSV = new IplImage(imgSrc.Size, BitDepth.U8, 3);

                imgH = new IplImage(imgSrc.Size, BitDepth.U8, 1);

                imgS = new IplImage(imgSrc.Size, BitDepth.U8, 1);

                imgV = new IplImage(imgSrc.Size, BitDepth.U8, 1);

                imgBackProjection = new IplImage(imgSrc.Size, BitDepth.U8, 1);

                imgFlesh = new IplImage(imgSrc.Size, BitDepth.U8, 1);

                imgHull = new IplImage(imgSrc.Size, BitDepth.U8, 1);

                imgDefect = new IplImage(imgSrc.Size, BitDepth.U8, 3);

                imgContour = new IplImage(imgSrc.Size, BitDepth.U8, 3);               

                //imagmove;              

 

                int WINDOW_WIDTH = imgSrc.Width;

                int WINDOW_HEIGHT = imgSrc.Height;

 

                // RGB -> HSV

                Cv.CvtColor(imgSrc, imgHSV, ColorConversion.BgrToHsv);

                Cv.Split(imgHSV, imgH, imgS, imgV, null);

                hsvPlanes[0] = imgH;

                hsvPlanes[1] = imgS;

                hsvPlanes[2] = imgV;

 

                // 피부색 영역을 구한다

                RetrieveFleshRegion(imgSrc, hsvPlanes, imgBackProjection);

 

                // 최대의 면적의 영역을 남긴다

                FilterByMaximalBlob(imgBackProjection, imgFlesh);

                Interpolate(imgFlesh);

 

                //템플릿 매칭

               

                 //윤곽을 구한다

                CvSeq<CvPoint> contours = FindContours(imgFlesh, storage);

                if (contours != null)

                {

                    Cv.DrawContours(imgContour, contours, CvColor.Red, CvColor.Green, 03LineType.AntiAlias);

                    // 요철을 구한다

                    CvSeq hull = Cv.ConvexHull2(contours, storage, ConvexHullOrientation.Clockwise, false);

                    Cv.Copy(imgFlesh, imgHull);

                     //윤곽을 구합니다.

                    DrawConvexHull(hull, imgHull);

                    //DrawConvexHull(hull, imgBackProjection);

                   

                    // 오목한 상태 결손을 구한다

                    Cv.Copy(imgContour, imgDefect);

                    CvSeq<CvConvexityDefect> defect = Cv.ConvexityDefects(contours, hull, storage);

                    DrawDefects(imgDefect, defect);

                }

                else

                {

                    return;

                }

                imgmove = new IplImage(new CvSize(imgBackProjection.Width - MouseSystem.img_move.Width + 1,

                    imgBackProjection.Height - MouseSystem.img_move.Height + 1), BitDepth.F32, 1);

 

               

 

                //MouseSystem.imgResult[0] = imgSrc;

                tpl = MouseSystem.img_move;

                tm = new IplImage(new CvSize(WINDOW_WIDTH - TPL_WIDTH + 1, WINDOW_HEIGHT - TPL_HEIGHT + 1), BitDepth.F32, 1);

                trackObject(imgBackProjection);

 

                //Point result = getCenter(imgBackProjection);

                //imgDefect.DrawCircle(new CvPoint(result.X,result.Y), 70, new CvScalar(255,255,255) -1);

                MouseSystem.imgResult[0] = imgSrc;

                MouseSystem.imgResult[1] = imgBackProjection;

                MouseSystem.imgResult[2] = imgDefect;

                Cv.WaitKey();               

            }

            catch

            {

            }

        }

     

 

        private void DrawDefects(IplImage img, CvSeq<CvConvexityDefect> defect)

        {

            int count = 0;

            try

            {               

                foreach (CvConvexityDefect item in defect)

                {

                    p1 = item.Start;

                    p2 = item.End;

                    double dist = GetDistance(p1, p2);

                    CvPoint2D64f mid = GetMidpoint(p1, p2);

                    img.DrawLine(p1, p2, CvColor.White, 3);

                    if (count == 1)

                    {

                        img.DrawCircle(item.Start, 10CvColor.Pink, -1);

                        MouseSystem.MousePointStart = item.Start;

                    }

                    else

                    {

                        img.DrawCircle(item.Start, 10CvColor.Green, -1);

                    }

                    img.DrawLine(mid, item.DepthPoint, CvColor.White, 1);

                    count++;             

                }

 

                MouseSystem.str_ControlsCount = (count - 1).ToString();

                switch (count - 1)

                {

                    case 1:

                        MouseSystem.str_MouseState = "한개";

                        //NativeMethod.mouse_event(NativeMethod.WM_LBUTTONDOWN, 0, 0, 0, 0);

                        break;

                    case 2:

                        MouseSystem.str_MouseState = "두개";

                        //NativeMethod.mouse_event(NativeMethod.WM_LBUTTONDOWN, 0, 0, 0, 0);

                        break;

                    case 3:

                        MouseSystem.str_MouseState = "세개";

                        //NativeMethod.mouse_event(NativeMethod.WM_LBUTTONUP, 0, 0, 0, 0);

                        break;

                    case 4:

                        MouseSystem.str_MouseState = "네개";

                        //NativeMethod.mouse_event(NativeMethod.WM_LBUTTONUP, 0, 0, 0, 0);

                        break;

                    case 5:

                        MouseSystem.str_MouseState = "다섯개";

                        //NativeMethod.mouse_event(NativeMethod.WM_LBUTTONUP, 0, 0, 0, 0);

                        break;

                }

            }

            catch

            {

            }

        }      

 

        private CvPoint2D64f GetMidpoint(CvPoint p1, CvPoint p2)

        {

            return new CvPoint2D64f

            {

                X = (p1.X + p2.X) / 2.0,

                Y = (p1.Y + p2.Y) / 2.0

            };

         }

 

        private double GetDistance(CvPoint p1, CvPoint p2)

        {

            return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));

        }

 

        void DrawConvexHull(CvSeq hull, IplImage img)

        {

            CvPoint pt0 = Cv.GetSeqElem<Pointer<CvPoint>>(hull, hull.Total - 1).Value.Entity;

            for (int i = 0; i < hull.Total; i++)

            {

                CvPoint pt = Cv.GetSeqElem<Pointer<CvPoint>>(hull, i).Value.Entity;

                Cv.Line(img, pt0, pt, new CvColor(255255255));

                pt0 = pt;

            }

        }

 

        public CvSeq<CvPoint> FindContours(IplImage img, CvMemStorage storage)

        {

            // 윤곽 추출

            CvSeq<CvPoint> contours;

            IplImage imgClone = img.Clone();

            Cv.FindContours(imgClone, storage, out contours);

            if (contours == null)

            {

                return null;

            }

            contours = Cv.ApproxPoly(contours, CvContour.SizeOf, storage, ApproxPolyMethod.DP, 3true);

 

            // 제일   같은 윤곽만을 얻는다

            CvSeq<CvPoint> max = contours;

            for (CvSeq<CvPoint> c = contours; c != null; c = c.HNext)

            {

                if (max.Total < c.Total)

                {

                    max = c;

                }

            }

            return max;

        }

        public void Interpolate(IplImage img)

        {

            Cv.Dilate(img, img, null2);

            Cv.Erode(img, img, null2);

        }

 

        private void FilterByMaximalBlob(IplImage imgSrc, IplImage imgDst)

        {

            using (CvBlobs blobs = new CvBlobs())

            using (IplImage imgLabelData = new IplImage(imgSrc.Size, CvBlobLib.DepthLabel, 1))

            {

                imgDst.Zero();

                blobs.Label(imgSrc, imgLabelData);

                CvBlob max = blobs[blobs.GreaterBlob()];

                if (max == null)

                {

                    return;

                }

                blobs.FilterByArea(max.Area, max.Area);

                blobs.FilterLabels(imgLabelData, imgDst);

                MouseSystem.MousePointEnd = new CvPoint((int)max.Centroid.X, (int)max.MinY);

            }

        }

 

        private void RetrieveFleshRegion(IplImage imgSrc, IplImage[] hsvPlanes, IplImage imgDst)

        {

            int[] histSize = new int[] { 3032 };

            float[] hRanges = { 0.0f20f };

            float[] sRanges = { 50f255f };

            float[][] ranges = { hRanges, sRanges };

 

            imgDst.Zero();

            hist = new CvHistogram(histSize, HistogramFormat.Array, ranges, true);

            hist.Calc(hsvPlanes, falsenull);

            float minValue, maxValue;

            hist.GetMinMaxValue(out minValue, out maxValue);

            hist.Normalize(imgSrc.Width * imgSrc.Height * 255 / maxValue);

            Cv.CalcBackProject(hsvPlanes, imgDst, hist);

        }

 

        private void trackObject(IplImage img_src)

        {

            int WINDOW_WIDTH = img_src.Width;

            int WINDOW_HEIGHT = img_src.Height;

            try

            {

                CvPoint minloc, maxloc;

                double minval, maxval;

                int win_x0 = object_x0 - ((WINDOW_WIDTH - TPL_WIDTH) / 2);

                int win_y0 = object_y0 - ((WINDOW_HEIGHT - TPL_HEIGHT) / 2);

                Cv.SetImageROI(img_src, new CvRect(win_x0, win_y0, WINDOW_WIDTH, WINDOW_HEIGHT));

                Cv.MatchTemplate(img_src, tpl, tm, MatchTemplateMethod.SqDiffNormed);

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

                Cv.ResetImageROI(img_src);

                if (minval <= THRESHOLD)

                {

                    object_x0 = win_x0 + minloc.X;

                    object_y0 = win_y0 + minloc.Y;

                    Cv.DrawRect(img_src, new CvRect(object_x0, object_y0, TPL_WIDTH, TPL_HEIGHT), CvColor.Red, 2);

                }

            }

            catch

            {               

            }

        }

    }

 

'Programing > OpenCV' 카테고리의 다른 글

수직에지기반 차량인식  (0) 2016.11.30
제스쳐 인식  (0) 2016.11.30
플러드필  (0) 2016.11.30
Opencv C# 모폴로지  (0) 2016.11.30
영상 모폴로지 자체 커널 만들기  (0) 2016.11.30

플러드필은 영상에서 특정 영역을 고립시키거나 구분할 때 사용되는 기능이며영상 처리 및 영상 분석 등의 전처리 과정으로 널리 사용된다플러드필은 입력 영상으로부터 마스크를 만드는 용도로 사용될 수 있는데이러한 마스크는 추후 제한된 영역에서만 연산을 수행하도록 하여 전체 연산 속도를 향살시키는 용도로 사용될 수 있다.마스트는 Cv.FloodFill() 함수의 인자로도 사용될  있으며 경우 채우기가 완료된 곳을 제어하는 용도로 사용된다.

 

OpenCV에서 제공하는 플러드필은 다른 페인팅 프로그램에서 채우기 기능의 일반적인 형태이다일단 영상에서 씨앗점을 선택해 주어야 하고 점과 유사한 이웃 점들이 모두같은 색깔로 칠해진다다른 페인팅 프로그램과 조금 다른 점은 OpenCV 플러드필은 주변 픽셀과 색상이 완전히 똑같을 필요가 없다는 점이다Cv.FloodFill() 함수는 주변 픽셀값이 이미 색칠되어 있는 인접한 픽셀값 또는 씨앗점 픽셀값과 비교하여 특정 범위 안에 있으면 색을 칠한다플러드필은 선택적으로 전달되는 mask 인자에 의해 동작이 제한될 있다.

 

FloodFill(CvArr image, CvPoint seed_point, CvScalar new_val, CvScalar lo_diff, CvScalar up_diff, out CvConnectedComp comp, int flags, CvArr mask);



첫 번째 인자 image는 입력 영상이며, 8비트 또는 실수형이고, 1또는 3채널 영상이다플러드필은 씨앗점에서 시작하고추후 선택된 영역은 newVal값으로 색칠한다특정 픽셀값이 이미 색칠되어 있는 이웃 픽셀의 밝기값에서 lo_diff를 뺀 값보다 같거나 크고, up_Diff를 더한 값보다는 같거나 작을 경우이 픽셀을 newVal 값으로 색칠한다만약flags 인자에 CV_FLOODFILL_FIXED_RANGE 값이 포함되어 있따면인접한 이웃 픽셀과 값을 비교하지 않고 씨앗점의 픽셀값과 비교한다. comp인자가 null이 아니면채워진 영역에 대한 통계 정보를 담고 있는 CvConnectedComp 구조체를 가리킨다.                  

씨앗점으로 (흰색)지정

 



mask 인자는 cvFloodFill() 함수의 입력으로도 작용하고출력으로도 사용된다입력으로 사용될 경우에는 채우기가 적용될 영역을 제한하는 역할을 담당하고출력으로 사용될 경우에는 실제 채워진 영역을 가리키는 용도로 사용된다. Mask null이 아니라면입력 영상보다 가로세로 방향으로 2픽셀 더 큰, 8비트 1채널 영상이어야 한다입력영상보다 큰 영상으로 mask를 설정하는 이유는 내부 작업을 쉽고 빠르게 수행하기 위함이다. Mask 영상에서(x+1,y+1)위치의 픽셀은 입력 영상의 (x,y)좌표의 픽셀과 대응된다.  Flags의 하위 8비트는 4또는 8값을 가질 수 있다이 값은 채우기 알고리즘에서 연결성을 제어하는 용도로 사용된다이 값이 8이면양쪽 대각선 방향으로 인접한 픽셀들도 이웃 픽셀로 간주도니다상위 8비트(16~23) CV_FLOODFILL_FIXED_RANGE 또는 CV_FLOODFILL_MASK_ONLY 값을 가질 수 있다. CV_FLOODFILL_FIXED_RANGE가 설정되면채우기 알고리즘에서 이웃 픽셀의 값과 비교하는 대신 씨앗점 픽셀값과 비교한다. CV_FLOODFILL_MASK_ ONLY가 설정되면 원본 영상에 채우기 결과를 설정하는 대신mask 영상에만 결과를 표현한다. CV_FLOODFILL_MASK_ONLY를 설정하는 경우 반드시 mask 인자값을 설정해 주어야 한다.


if (src == nullreturn;

            IplImage dst = Cv.CreateImage(src.Size, BitDepth.U8, 3);

 

            IplImage tempimage = src.Clone();            

 

            //Cv.FloodFill(src, new CvPoint(3, 240),0,7.0,7.0);

            //Cv.FloodFill(src,new CvPoint(0,255), 255, 7.0, 7.0);

            CvConnectedComp aa = new CvConnectedComp();

            

 

            Cv.FloodFill(src, Cv.Point(10255), Cv.RGB(255255255), Cv.ScalarAll(25.0),Cv.ScalarAll(25.0));

            

            resultwb = new WriteableBitmap(src.Width, src.Height, 9696PixelFormats.Bgr24, null);

 

            WriteableBitmapConverter.ToWriteableBitmap(src, resultwb);

 

            image2.Source = resultwb;


 

'Programing > OpenCV' 카테고리의 다른 글

제스쳐 인식  (0) 2016.11.30
제스쳐 인식  (0) 2016.11.30
Opencv C# 모폴로지  (0) 2016.11.30
영상 모폴로지 자체 커널 만들기  (0) 2016.11.30
영상 모폴로지  (0) 2016.11.30

이진 영상의 모폴로지는 기본적인 침식과 팽창 연산만 알고 있어도 충분하다그러나 그레이스케일 또는 컬러 영상의 경우유용한 모폴로지 연산들이 더 존재한다모폴로지 연산들은 모두 하나의 함수 Cv.MorphologyEx()를 이용한다.

 

public static void MorphologyEx(CvArr src, CvArr dst, CvArr temp, IplConvKernel element, MorphologyOperation operation, int iterations);

 

Cv.MorphologyEx() 함수는 두개의 새로운 매개변수를  받는다하나는 temp 배열이고다른 하나는 operation이다. Temp 배열이 선택적으로 사용된다, temp 배열이 사용되는 경우 배열은 입력 영상과동일한 크기여야 한다.

 

Operation 인자에 지정할  있는 

Operation

모폴로지 연산

Temp 영상의 필요 유무

MorphologyOperation.Open

열기

불필요

MorphologyOperation.Close

닫기

불필요

MorphologyOperation.Gradient

모폴로지 그래디언트

항상필요

MorphologyOperation.Tophat

탑 

바꿔치기 전용(src = dst)

MorphologyOperation.Blackhat

블랙 

바꿔치기 전용(src = dst)


열기와 닫기 연산

열기와 닫기 연산은 침식과 팽창 연산을 조합하여 사용함으로써 이루어진다열기 연산의 경우침식 연산을 수행한 후 팽창 연산을 수행한다열기 연산은 종종 이진 영상에서 독립된 영역들의 개수를 세기 위해서 사용된다예를 들어현미경으로 관찰한 세포 영상을 임계값을 이용하여 이진화한 후열기 연산을 수행하여 가까이 붙어 있는 세포들을 서로 떨어뜨리려 놓고 나서 세포의 수를 세곤 한다닫기 연산은 팽창을 먼저 하고 침식 연산을 수행한다닫기 연산은 연결된 구성 요소 알고리즘 등에서 잡음에 의한 원치 않은 분할을 최소한으로 줄이고자 사용한다연결된 구성 요소 레이블링을 수행하기 위해 보통 침식 또는 닫기 연산을 수행하여 잡음을 먼저 제거하고열기 연산을 수행하여 다시 가까이 인접한 큰 영역들을 합쳐준다열기 또는 닫기 연산의 결과는 침식 또는 팽창 연산의 결과와 유사하지만열기 및 닫기 연산이 연결된 영역을 보다 정확하게 보존해준다.

 

IplImage tempimage = src.Clone();

 

            IplConvKernel element = Cv.CreateStructuringElementEx(3,322ElementShape.Rect, null);

 

            Cv.MorphologyEx(src, dst, tempimage, element, MorphologyOperation.Open, 3);



모폴로지 열기 연산의 결과작은 크기의 밝은 영역은 사라지고남아 있는 밝은 역역의 크기는 유지된채 고립되어 있다.


모폴로지 닫기 연산의 결과밝은 영역들이 서로 합쳐졌지만 기본 크기는 유지하고 있다.

 

Iterations 2를 전달하여 닫기 연산을 두 번 수행하는 것은 팽창-침식-팽창-침식 연산을 수행하는 것 같은 효과를 나타낸다그러나 이러한 연산은 그다지 유용하지 않다보통의 경우팽창-팽창-침식-침식 같은 연산을 원할 것이다팽창-팽창-침식-침식과 같은 방식으로 모폴로지 연산을 수행해야 팽창-침식을 한 번 수행하는 것보다 더 큰 잡을을 제거하는 효과가 있다.

 

모폴로지 그래디언트

이 연산을 이진 영상에 적용하면 영역의 외곽선만 남기는 효과가 있다.

Gradient(src) = dilate(src) - erdoe(src)



모폴로지 그래디언트 연산의 결과주변부 엣지가 밝게 표현된다.

 

그래이스케일 영상에 대한 모폴로지 그래디언트 연산 결과는 영상에서 밝기값 변화량의 정도를 알려준다모폴로지 그래디언트는 밝은 영역 주위를 고립시켜서 이를 하나의 객체로써 다룰 수 있도록 해준다영역의 완전한 외곽선은 확정된 영역으로부터 수축된 영역을 빼는 차연산을 통해 쉽게 구할 수 있다이는 객체의 그래디언트를 계산 하는 것과는 다르다.(그래디언트 관한 소벨샤르 연산)

 

-햇 연산과 블랙-햇 연산

이들 연산은 주변보다 밝거나 어두운 영역을 고립시키는 효과가 있기 때문에 특정 객체 내부에서 밝기 변화가 두드러진 부분을 찾아내기 위해 사용된다예를 들어생체조직 또는 세포를 촬영한 현미경 영상을 다룰 때 사용되기도 한다.

TopHat(src) = src - open(src)

BlackHat(src) = close(src) - src

 

-햇 연산은 입력 영상에서 열기 연산이 수행된 영상을 빼는 연산이다열기 연산은 영상에서 밝기값이 크게 튀는 작은 덩어리를 제거하는 효과가 있다그러므로 src에서open(src)를 빼게 되면 결과적으로 원본 src영상에서 주변보다 밝았던 부분만 남아서 강조되어 보인다이러한 효과는 커널의 크기에 의존적이다이와 반대로 블랙-햇 연산은 원본 영상에서 주변보다 어두운 영역만 강조한다.


모폴로지 탑-햇 연산의 결과주변보다 밝은 영역들이 강조되었다.


모폴로지 블랙-햇 연산 결과주변보다 어두운 영역들이 강조되었다.

'Programing > OpenCV' 카테고리의 다른 글

제스쳐 인식  (0) 2016.11.30
플러드필  (0) 2016.11.30
영상 모폴로지 자체 커널 만들기  (0) 2016.11.30
영상 모폴로지  (0) 2016.11.30
스무딩(블러링)  (0) 2016.11.30

OpenCV에서는 IplConvKernel 구조체를 사용하여 자체적인 모폴로지 커널을 사용할 수 있다이 커널은 Cv.CreateStructuringElementEx() 함수를 통해서 생성하고,ReleaseStructuringElement() 함수를 이용하여 해제한다

 

public static IplConvKernel CreateStructuringElementEx(int cols, int rows, int anchor_x, int anchor_y, ElementShape shape, int[,] values);

모폴로지 커널은 컨볼루션 커널과 달리 수치값을 필요로 하지 않는다모폴로지 커널의 모양은 커널이 영상을 스캔하는 동안 최대값 또는 최소값 계산에 필요한 픽셀 위치를 알려주는 역할만 할 뿐이다고정점은 커널이 원본 영상에 어떻게 정렬되어야 하는지 알려주는 역할을 하고또한 모폴로지 연산의 결과값이 원본 영상의 어느 픽셀에 대입되어야 하는지 알려준다커널을 새로 생성하려면 구성요소를 감싸는 사각형을 표현하는 cols rows를 지정해주어야 한다그 다음으로 고정점의 좌표 (x,y)를 나타내는 anchor_xanchor_y를 지정한다. CV_SHAPE_CUSTOM 값이 사용되면 정수형 배열 values에 의해 지정된 형태의 커널이 생성된다이 배열은 rows x cols 크기의 1차원 배열이며 0이 아닌 값으로 지정된 위치의 원소는 커널에서 사용되는 픽셀이다. Values null이면 모든 원소값이 0이 아닌 것으로 간주되어 사각형 형태의 커널이 생성된다.

 

다섯 번째 인자 shape 

Shape 

의미

CV_SHAPE_RECT

사각형 커널

CV_SHAPE_CROSS

십자 모양의 커널

CV_SHAPE_ELLIPSE

타원형 커널

CV_SHAPE_CUSTOM

Values 값으로 지정하는 사용자 정의 커널


'Programing > OpenCV' 카테고리의 다른 글

플러드필  (0) 2016.11.30
Opencv C# 모폴로지  (0) 2016.11.30
영상 모폴로지  (0) 2016.11.30
스무딩(블러링)  (0) 2016.11.30
영상 파일 포맷의 종류  (0) 2016.11.30

OpenCV는 빠르고 사용하기 쉬운 영상의 모폴로지 변환을 제공한다기본적인 모톨로지 연산은 팽창과 침식이다이들 기법은 영상 내에서 잡음 제거또는 구성 요소들의 결합 또는 분리 등의 폭넓은 분야에서 널리 사용된다모폴로지는 밝기값이 뭉쳐있는 영역이나 영상의 그래디언트를 구하는 용도로도 사용될 수 있다.

 

팽창과 침식
팽창연산도 영상과 커널과의 컨볼루션이다이때 영상을 A라고 하고커널을 B라고 하면 커널은 임의의 모양과 크기를 가질 수 있고고정점을 하나 갖는다대부분의 경우 커널은 중앙에 고정점을 가지고 있는 작은 정사각형 또는 원형으로 구성된다커널은 템플릿 또는 마스크의 일종이라고 생각할 수 있으며 팽창 연산은 국지적 최대값을 선택하는 효과가 있다영상 A를 커널 B로 스캔하면서 B와 겹치는 영역에서 최대 픽셀값을 구하고이 값을 고정점 아래에 있는 픽셀의 값으로 설정한다.


 

참조 :한빛미디어 Learining OpenCV 제대로 배우기

 

침식은 팽창과 반대되는 개념이다침식 연산은 커널 아래에서 국지적 최소값을 계산하는 것과 유사하다침식 연산 알고리즘은 영상 A를 커널 B로 스캔하면 B와 겹치는 영역에서 최소 픽셀값을 구하고이 값을 고정점 아래에 있는 픽셀의 값으로 설정한다.


영상 모톨로지는 임계값 연산 후 얻어지는 이진 영상에 대해서도 자주 실행된다그러나 팽창은 단지 최대값 연산이고 침식은 단지 최소값 연산이기 때문에 모폴로지는 일반 영상에서도 사용될 수 있다.

 

일반적으로 팽창은 영역 A를 확장시키는 반면침식은 영역 A를 축소시킨다또한 팽창은 영역 A에서 오목하게 들어간 부분을 채우는 효과가 있고침식은 볼록 튀어나온 부분을 제거하는 효과가 있다물론 이러한 결과는 커널의 모양에 따라 달라지기는 하지만 속이 꽉 채워져 있는 컨백스 형태의 커널이 사용될 경우에는 일반적으로 참이다. OpenCV에서는 침식을 위한 cvErode()와 팽창을 위한 cvDilate() 함수를 제공한다.

 

public static void Erode(CvArr src, CvArr dst, IplConvKernel element, int iterations);

 

public static void Dilate(CvArr src, CvArr dst, IplConvKernel element, int iterations);

 

cvErode()  cvDilate() 함수는 입력과 출력 영상을 인자로 받으며 바꿔치기 호출을 지원한다입력과 출력 영상을 동일한 영상으로 지정할 수 있다세번쨰 인자 B에는 커널을 전달할 수 있으며기본값으로 null이 설정된다커널이 null이면 중앙에 고정점이 있는 3x3 크기의 커널이 자동으로 사용된다마지막 인자 iterations에는 반복 횟수를 지정한다만약 iterations가 기본값 1이 아니면 함수 내부에서 해당 연산을 여러 번 반복하게 된다.


침식 또는 최소값 연산의 결과밝은 영역은 고립되거나 줄어든다.

Cv.Erode(src, dst,null,3);

 

침식연산은 영상에서 얼룩 잡음을 제거하기 위해 종종 사용되곤 한다얼룩은 침식으로 인해 없어지고중요한 큰 영역들은 영향을 받지 않고 그대로 남아있게 된다팽창 연산은 종종 연결된 구성 요소를 찾고자 할 때 사용된다잡음그림자 등 다양한 이유로 인하여 하나의 큰 영역이 여러 개의 구성 요소로 분리되는 경우가 발생하기 때문에 팽창 연산은 종종 필요하다.

 


팽창 또는 최대값 연산의 결과밝은 영역은 확장되어 합쳐지기도 한다.

 

'Programing > OpenCV' 카테고리의 다른 글

Opencv C# 모폴로지  (0) 2016.11.30
영상 모폴로지 자체 커널 만들기  (0) 2016.11.30
스무딩(블러링)  (0) 2016.11.30
영상 파일 포맷의 종류  (0) 2016.11.30
영상처리 컬러모델  (0) 2016.11.30

스무딩 또는 블러링은 매우 간단하면서도 빈번하게 사용되는 영상처리 기법이다스무딩 기법은 주로 영상의 잡음(noise) 또는 손상을 완화시키기 위해서 사용된다스무딩은 영상의 크기를 줄이는 경우에도 중요한 역할을 담당한다. OpenCV는 현재 다섯 가지 스무딩 방법을 제공한다모든 스무딩 연산은 하나의 함수 cvSmooth()에 구현되어 있으며함수의 인자에 의해 연산 방법이 결정된다.

 

        public static void Smooth(CvArr src, CvArr dst);

 

        public static void Smooth(CvArr src, CvArr dst, SmoothType smoothtype);

 

        public static void Smooth(CvArr src, CvArr dst, SmoothType smoothtype, int param1);

      

        public static void Smooth(CvArr src, CvArr dst, SmoothType smoothtype, int param1, int param2);

 

        public static void Smooth(CvArr src, CvArr dst, SmoothType smoothtype, int param1, int param2, double param3);

 

        public static void Smooth(CvArr src, CvArr dst, SmoothType smoothtype, int param1, int param2, double param3, double param4);

 

Src dst는 각각 입력 영상과 출력 영상을 가리킨다. cvSmooth() 함수는 param1, param2, param3, param4 라는 평이한 이름의 매개변수 네 개를 갖는데이들 매개변수의 의미는 세 번째 인자 smoothtype에 의해 결정된다.

Smoothtype 

이름

바꿔치기

채널개수

입력영상타입

출력영상타입

설명

CV_BLUR

단술 

블러

O

1,3

8u,32f

8u,32f

Param1 x param2 크기의 이웃 필셀값의 평균

CV_BLUR_NO_SCALE

스케일링 없는 

단순 

블러

X

1

8u

입력이 8u이면 16s 32f 이면 32f

Param1 x param2 크기의 이웃 픽셁밧의 합

CV_MEDIAN

중간값 블러

X

1,3

8u

8u

Param1 x param2 크기의 이웃 픽셀값에서 중간값

CV_GAUSSIAN

가우시안 블러

O

1,3

8u,32f

입력이 8u이면 16s, 32f 이면 32f

Param1 x param2 크기의 이웃 픽셀값의 가중치 합

CV_BILATERAL

양방향 블러

X

1,3

8u

8u

3 x 3 크기의 양방향 필터를 적용

색상 시그마 = param1

공간 시그마 = param2

단순 필터

CV_BLUR 값을 전달할 때 시행되는 단순 블러는 가장 간단한 형태의 스무딩 이다단순 블러는 1 또는 3채널, 8비트 또는 32 비트 실수형 영상에서 동작한다.


 

 

단순 블러부젼 픽셀값의 평균을 이용하여 스무딩을 수행한다왼쪽이 원본 영상이고 오른쪽이 결과 영상이다.

 

모든 스무딩 연산이 동일한 형태의 영상에서 동작하는 것은 아니다. Smoothtype 값에 CV_BLUR_NO_SCAL를 지정하면 스케일링 없는 단순 블러 연산이 수행된다이 연산은 기본적으로 단순 블러와 동일하지만 평균값 계산을 위한 나눗셈 연산이 없다이 때문에 입력 영상과 출력 영상은 서로 다른 데이터 타입을 가져야 하는데이는 계산 과정에서 오버플로가 생기는 것을 방지하기 위함이다스케일링 없는 단순 블러는 일반적으로 8비트 영상을 입력으로 받고, IPL_DEPTH_16S 또는 32S 데이터 타입의 출력 영상을 생성한다. 32비트 실수형 영상에서 실행할 경우결과 영상도 32비트 실수형 영상이다. 32비트 영상을 사용할 경우에도 반드시 지켜져야 만 한다스케일링 없는 단순 블러는 나눗셈 연산이 있는 단순 블러에 비해 연산 속도가 빠르다.


 

중간값 필터

중간값 필터는 픽셀 주변 사각형 영역에서 중간값을 선택하여 해당 픽셀의 값으로 교체하는 방식이며, smoothtype 값을 CV_MEDIAN으로 지정한다중간값 필터는 1 또는 3채널, 8비트 영상에서 동작하며원본 영상을 바꿔치기할 수 없다평균값을 사용하는 단순 블러는 홀로 독립되어 나타나는 잡음(이러한 잡음을 샷 잡음(shot noise)라고 부른다)에 민감하게 반응할 수 있다적은 개수의 픽셀에서 크게 튀는 값을 갖는 픽셀은 평균값 계산에 큰 영향을 줄 수 있기 때문이다중간값필터는 중간값을 선택함으로써 그러한 이상치를 무시할 수 있다중간값 블러주변 픽셀의 중간값을 이용하여 스무딩을 수행한다.



가우시안 필터

가우시안 필터는 동작이 빠른편은 아니지만 가장 유용한 필터이다가우시안 필터는 입력 영상의 각 픽셀에서 가우시안 커널과 컨볼루션을 하여 결과 영상을 생성한다. CvSmooth() 함수를 이용하여 가우시안 블러를 수행하려면 매개변수 값을 설정해 주어야 한다. Param1 param2에서 필터 윈도우의 가로와 세로 크기를 지정한다. Param3에는 선택적으로 가우시안 커널의 시그마값(표준편차)을 지정한다. Param3이 기본값인 0으로 설정되면 시그마 값을 자체적으로 계산하여 사용한다비대칭 모양의 커널 윈도우를 사용하려면 param4를 지정할 수 있다이 경우, param3은 가로 방향의 시그마값을, pararm4는 세로 방향의 시그마값을 나타낸다.

만약 param3 pararm4의 값을 지정하였는데 pararm1 = param2 = 0 으로 설정이되어 있다면커널 윈도우의 크기는 시그마값에 의해 자동으로 결정된다.

OpenCV는 자주 사용하는 가우시안 커널에 대하여 최적화된 성능의 스무딩을 보여준다표준 시그마값(param3 =0.0)을 갖는 3x3, 5x5, 7x7 크기의 가우시안 커널일 경우다른 커널에 비하여 연산 속도가 빠르다가우시안 블러는 8비트 또는 32비트 실수형을 갖는 1또는 3채널 영상에 대하여 적용할 수 있다.


 

양방향 필터

양방향 필터는 엣지 보존 스무딩 이라고 알려져 있는 영상 분석 방법 중의 하나이다양방향 필터는 가우시안 스무딩과 비교하면 쉽게 이해할 수 있다전형적인 가우시안 스무딩은 실제 영상의 픽셀값이 공간 상에서 천천히 변화하고이로 인하여 이웃 픽셀과 연관성을 많이 갖고 있지만 잡음은 갑작스럽게 값이 튄다는 점에서 착안되었다이러한 이유로 가우시안 스무딩은 신호는 보존하면서 잡음만 제거한다그러나 이 방법은 엣지 근처에서는 문제가 발생한다주변 값들과 상관성이 적은 엣지 부근에서도 가우시안 스무딩은 영상을 평탄하게 만듦으로써 엣지를 없애버린다양방향 필터는 약간의 연산 시간을 더 소모하지만 엣지를 보존하면서 영상의 스무딩을 수행한다.

가우시안 스무딩과 유사하게 양방향 필터 또한 각 픽셀과 주변 요소들로부터 가중 평균을 구한다이때 가중치는 두 개의 성분을 갖는데하나는 가우시안 스무딩에서 사용되는 가중치와 동일하다나머디 가중치 성분도 가우시안 가중치와 유사하지만 중심으로부터의 거리에 의해 결정되는 값이 아니라 중심 픽셀 값과의 밝기 차이에 의해 결정되는 가중치를 사용한다양방향 필터는 유사한 픽셀에 대하여 더 큰 가중치를 부여하는 가우시안 스무딩이라고 간주할 수 있다이 필터는 영상을 마치 수채화 그림처럼 바꿔주는 효과가 있다양방향 필터는 영상 분할을 위한 전처리 과정으로 사용되기도 한다양방향 필터는 두 개의 매개변수를 사용한다. Param1은 색 공간에서 사용되는 가우시안 커널의 크기를 나타낸다. Param1값이 클수록 더 넓은 분포의 밝기값(또는 생상값)이 평탄화 된다.param2는 공간 영역에서 사용되는 가우시안 커널의 너비를 나타낸다이는 가우시안 필터에서 시그마값과 유사하다.

 

 

namespace AboutOpenCV

{

    /// <summary>

    /// Window1.xaml 대한 상호 작용 논리

    /// </summary>

    public partial class Window1 : Window

    {

        WriteableBitmap wb;

        WriteableBitmap resultwb;

        IplImage src;

 

        public Window1()

        {            

            InitializeComponent();

        }

 

        private void MenuItem_Click(object sender, RoutedEventArgs e)

        {

            string imgpath; // 경로저장변수           

 

            OpenFileDialog dlg = new OpenFileDialog();

 

            dlg.DefaultExt = ".*";  //기본 확장자

            dlg.Filter = "Image Files(.*)|*.*"// 확장 파일 필터

            dlg.ShowDialog();

            imgpath = dlg.FileName;           

 

            src = new IplImage(imgpath, LoadMode.AnyColor);

            

            wb = new WriteableBitmap(src.Width, src.Height, 9696PixelFormats.Bgr24, null);

 

            WriteableBitmapConverter.ToWriteableBitmap(src,wb);         

 

            image1.Source = wb;            

            

          }

        //스무딩

 

        private void MenuItem_Click_1(object sender, RoutedEventArgs e)

        {                        

            if (src == nullreturn;

            IplImage dst = Cv.CreateImage(src.Size, BitDepth.U8, 3);

                        

            Cv.Smooth(src, dst,SmoothType.Bilateral,1,1);

            //Cv.Smooth(src, dst, SmoothType.Blur);

            //Cv.Smooth(src, dst, SmoothType.BlurNoScale);

            //Cv.Smooth(src, dst, SmoothType.Gaussian, 7);

 

            resultwb = new WriteableBitmap(dst.Width, dst.Height, 9696PixelFormats.Bgr24, null);

            

            WriteableBitmapConverter.ToWriteableBitmap(dst, resultwb);     

 

            image2.Source = resultwb;            

        }

    }

}

 

'Programing > OpenCV' 카테고리의 다른 글

영상 모폴로지 자체 커널 만들기  (0) 2016.11.30
영상 모폴로지  (0) 2016.11.30
영상 파일 포맷의 종류  (0) 2016.11.30
영상처리 컬러모델  (0) 2016.11.30
gray scale  (0) 2016.11.30

영상 파일 포맷의 종류

BMP, JPEG, RAW, GIF, PSD, TIFF 

 

BMP, JPEG 파일 포맷

영상의 색상 정보해상도 등을 알 수 있는 정보가 헤더에 포함되어 있다추가 작업이 필요해 프로그램이 더 복잡해 진다.

 

RAW파일 포맷

헤더 정보 없이 완전한 데이터만으로 구성된다복잡한 헤더 정보를 해석할 필요가 없어 영상처리가 복잡하지 않다헤더 정보가 없어 영상의 색상 정보나 해상도 정보를 사용자가 미리 알아야 하는 단점이 있다.



'Programing > OpenCV' 카테고리의 다른 글

영상 모폴로지  (0) 2016.11.30
스무딩(블러링)  (0) 2016.11.30
영상처리 컬러모델  (0) 2016.11.30
gray scale  (0) 2016.11.30
Opencv c# 2  (0) 2016.11.30

RGB, CMY(K), HIS, YCrCb, YUV 등의 컬러 모델이 있다.

 

RGB 컬러 모델

Red, Green, Blue 세 가지 색상 값을 이용해 색 표시 한다.



 

색을 혼합할수록 색이 밝아지는 가산 체계 사용 한다.

RGB를 같은 비율로 혼합하면 가장 밝은 흰색

 

Red(1, 0, 0) + Green(0, 1, 0) + Blue(0, 0, 1) = White(1, 1, 1)

 

빨강색(R)과초록색(G) 혼합→ 노란색(Yellow)

 

Red(1, 0, 0) + Green(0, 1, 0) = Yellow(1, 1, 0)

 

 

CMY 컬러 모델

청록색(Cyan), 자홍색(Magenta), 노랑색(Yellow) 기본 색으로 사용 한다.

RGB 컬러 모델에서 대각선으로 마주보는 색의 모양을 서로 바꿔 놓은 것처럼 보인다.


 

 

C,M,Y 세 가지 색을 더하면 (1,1,1)dl 되어 색의 밝기가 낮아지는 감산체계를 사용 한다.

RGB컬러와는 정반대 공간에 위치하므로청록색-빨강색자홍색-초록색노랑색-파란색은 보색 관계이다.

 

컬러 모델의 가산과 감산체계


 

HSI 컬러 모델

HSI = Hue(색상), Saturation(채도), Intensity(명도)

 


 

YCrCb 모델

명도에 더 민감한 인간 눈을 감안해 YCrCb컬러 모델이 개발 되었다명도는 Y, 푸른색 정보를 Cr, 붉은색 정보를 츄로 기호화 하고 눈에 민감한 명도 정보 Y는 그대로 유지민감하지 않은 Cr Cb색상 정보는 그 양을 줄여서 사용한다정지영상 압축 표준 방식인 JPEG와 동영상 압축 표준 방식인 MPEG에서 사용 된다.

'Programing > OpenCV' 카테고리의 다른 글

스무딩(블러링)  (0) 2016.11.30
영상 파일 포맷의 종류  (0) 2016.11.30
gray scale  (0) 2016.11.30
Opencv c# 2  (0) 2016.11.30
Opencv c#  (0) 2016.11.30

+ Recent posts