C# OpenCV 강좌 : 제 58강 - 광학 흐름 - LK

   

광학 흐름 - LK (Optical Flow LK)


1 카메라와 피사체의 상대 운동에 의하여 발생하는 피사체의 운동에 대한 패턴을 검출합니다.

LK(Lucas Kanade) 방법은 입력 이미지에 블록 크기를 기준으로 광학 흐름을 검출합니다.


이전 프레임(Previous)현재 프레임(Current)은 영상이나 이미지를 사용하면 됩니다.


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


Main Code


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

namespace test
{
    class OpenCV : IDisposable
    {
        IplImage gray;    
        IplImage optical;

        public IplImage GrayScale(IplImage src)
        {
            gray = new IplImage(src.Size, BitDepth.U8, 1);
            Cv.CvtColor(src, gray, ColorConversion.BgrToGray);
            return gray;
        }

        public IplImage OpticalFlowLK(IplImage previous, IplImage current)
        {
            IplImage prev = this.GrayScale(previous);
            IplImage curr = this.GrayScale(current);
            optical = current;

            int rows = optical.Height;
            int cols = optical.Width;

            CvMat velx = Cv.CreateMat(rows, cols, MatrixType.F32C1);
            CvMat vely = Cv.CreateMat(rows, cols, MatrixType.F32C1);

            velx.SetZero();
            vely.SetZero();

            Cv.CalcOpticalFlowLK(prev, curr, new CvSize(15, 15), velx, vely);

            for (int i = 0; i < cols; i += 15)
            {
                for (int j = 0; j < rows; j += 15)
                {
                    int dx = (int)Cv.GetReal2D(velx, j, i);
                    int dy = (int)Cv.GetReal2D(vely, j, i);

                    Cv.DrawCircle(optical, i, j, 1, CvColor.Red);

                    if (Math.Abs(dx) < 30 && Math.Abs(dy) < 30)
                    {
                        if (Math.Abs(dx) < 10 && Math.Abs(dy) < 10) continue;

                        Cv.DrawLine(optical, Cv.Point(i, j), Cv.Point(i + dx, j + dy), CvColor.Blue, 1, LineType.AntiAlias, 0);
                        Cv.DrawCircle(optical, new CvPoint(i + dx, j + dy), 3, CvColor.Blue, -1);
                    }
                }
            }
                    
            return optical;
        }
            
        public void Dispose()
        {
            if (gray!= null) Cv.ReleaseImage(gray);   
            if (optical != null) Cv.ReleaseImage(optical);  
        }
    }
}


Class Code


public IplImage OpticalFlowLK(IplImage previous, IplImage current)
{
    ...
}

이전 프레임 previous와 현재 프레임 current를 매개변수로 사용하여 검출을 진행합니다.


IplImage prev = this.GrayScale(previous);
IplImage curr = this.GrayScale(current);
optical = current;

광학 흐름 함수는 그레이스케일을 적용하여 검출을 진행합니다.

계산이미지로 사용할 prevcurr 변수에 그레이스케일을 적용합니다.

이후, 결과로 사용할 optical 필드에 현재 프레임을 사용합니다.


  • Tip : 그레이스케일을 사용하여 검출하므로 급격한 밝기 변화노이즈에는 정확한 검출을 얻어낼 수 없습니다.


int rows = optical.Height;
int cols = optical.Width;

CvMat velx = Cv.CreateMat(rows, cols, MatrixType.F32C1);
CvMat vely = Cv.CreateMat(rows, cols, MatrixType.F32C1);

Cv.SetZero(velx);
Cv.SetZero(vely);

을 설정합니다. 매트릭스는 행의 개수 x 열의 개수로 사용합니다.

X 방향 속도 벡터를 저장할 velxY 방향 속도 벡터를 저장할 vely매트릭스 형식으로 생성합니다.

행의 개수높이의 크기와 같으며, 열의 개수너비의 크기와 같습니다.

매트릭스를 생성하였으므로, SetZero()를 통하여 매트릭스의 값을 0으로 초기화합니다.


Cv.CalcOpticalFlowLK(prev, curr, new CvSize(15, 15), velx, vely);

Cv.CalcOpticalFlowLK()을 사용하여 광학 흐름을 구합니다.

Cv.CalcOpticalFlowLK(이전 프레임, 현재 프레임, 블록 크기, x 방향 속도 벡터, y 방향 속도 벡터)입니다.


블록 크기는 해당 블록 내에 픽셀들은 모두 같은 움직임으로 가정하는 크기입니다.

x 방향 속도 벡터y 방향 속도 벡터에 광학 흐름의 값이 담깁니다.


for (int i = 0; i < cols; i += 15)
{
    for (int j = 0; j < rows; j += 15)
    {
       ...
    }
}

이중 for문을 사용하여 속도 벡터의 값을 출력합니다.

로 반복을 실행합니다.

변환식에서 값을 +=15로 두어, 화면을 15 픽셀마다 광학흐름을 검출합니다.


int dx = (int)Cv.GetReal2D(velx, i, j);
int dy = (int)Cv.GetReal2D(vely, i, j);

Cv.GetReal2D() 함수를 사용하여 매트릭스에 담겨있는 속도 벡터 성분을 불러옵니다.

Cv.GetReal2D(matrix, index0, index1)을 의미합니다.

index0행 방향(↓)을 의미합니다.

index1열 방향(→)을 의미합니다.


Cv.DrawCircle(optical, i, j, 1, CvColor.Red);

이미지의 15 간격마다 붉은색 지점을 표시합니다.


if (Math.Abs(dx) < 30 && Math.Abs(dy) < 30)
{
    if (Math.Abs(dx) < 10 && Math.Abs(dy) < 10) continue;

    Cv.DrawLine(optical, Cv.Point(i, j), Cv.Point(i + dx, j + dy), CvColor.Blue, 1, LineType.AntiAlias, 0);
    Cv.DrawCircle(optical, new CvPoint(i + dx, j + dy), 3, CvColor.Blue, -1);
}

if문과 Math.Abs()를 사용하여 일정 값 이상, 이하의 값을 무시하여 출력합니다.

Cv.DrawLine()Cv.DrawCircle()을 사용하여 광학 흐름을 optical 필드에 표시합니다.


Result


2



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

⤧  Next post C# OpenCV 강좌 : 제 59강 - 광학 흐름 - PyrLK ⤧  Previous post C# OpenCV 강좌 : 제 57강 - 광학 흐름 - HS