상위 목록: 하위 목록: 작성 날짜: 읽는 데 25 분 소요

광학 흐름 - BM(Optical Flow BM)

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

BM(Block Matching) 방법은 블록 단위로 이미지를 나누며 이전 프레임현재 프레임을 매칭하여 광학 흐름을 검출합니다.

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

원본(Source, src)은 영상을 사용합니다.



메인 코드

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

namespace Project
{
    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 OpticalFlowBM(IplImage previous, IplImage current)
        {
            IplImage prev = this.GrayScale(previous);
            IplImage curr = this.GrayScale(current);
            optical = current;

            int BlockSize = 16;
            int ShiftSize = 32;
            int MaxRange = 10;

            CvSize blockSize = new CvSize(BlockSize, BlockSize);
            CvSize shiftSize = new CvSize(ShiftSize, ShiftSize);
            CvSize maxrange = new CvSize(MaxRange, MaxRange);

            CvSize VelSize = new CvSize
            {
                Width = (optical.Width - blockSize.Width + shiftSize.Width) / shiftSize.Width,
                Height = (optical.Height - blockSize.Height + shiftSize.Height) / shiftSize.Height
            };

            CvMat velx = Cv.CreateMat(VelSize.Height, VelSize.Width, MatrixType.F32C1);
            CvMat vely = Cv.CreateMat(VelSize.Height, VelSize.Width, MatrixType.F32C1);

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

            Cv.CalcOpticalFlowBM(prev, curr, blockSize, shiftSize, maxrange, false, velx, vely);

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

                    Cv.DrawLine(optical, new CvPoint(j * ShiftSize, i * ShiftSize), new CvPoint(j * ShiftSize + dx, i * ShiftSize + dy), CvColor.Red, 3, LineType.AntiAlias, 0);
                                    
                }
            }
            return optical;
        }

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


세부 코드

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

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


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

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

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

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

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


int BlockSize = 16;
int ShiftSize = 32;
int MaxRange = 10;

함수에 사용되는 주요 상수인 BlockSize, ShiftSize, Range를 선언하고 값을 적용합니다.

BlockSize는 검출에 사용할 블록의 크기를 의미합니다.

ShiftSize블록의 이격 거리를 의미합니다. 값이 낮을 수록 검출 간격이 촘촘해집니다.

MaxRange는 블록 주변의 인접한 블록 크기를 의미합니다.

  • Tip : BlockSizeMaxRange의 값이 높을 수록, ShiftSize의 값이 낮을 수록 연산 시간이 길어집니다.


CvSize blockSize = new CvSize(BlockSize, BlockSize);
CvSize shiftSize = new CvSize(ShiftSize, ShiftSize);
CvSize maxrange = new CvSize(MaxRange, MaxRange);

광학 흐름 함수는 CvSize 형식을 인수로 사용하므로 각각의 변수에 값을 적용합니다.


CvSize VelSize = new CvSize
{
    Width = (optical.Width - blockSize.Width + shiftSize.Width) / shiftSize.Width,
    Height = (optical.Height - blockSize.Height + shiftSize.Height) / shiftSize.Height
};

광학 흐름에서 검출된 X 방향 속도 벡터Y 방향 속도 벡터의 값을 저장하기 위해 VelSize를 생성합니다.

블록 매칭 방식은 블록 크기에 대하여 광학 흐름을 계산하므로 원본 이미지의 크기보다 작습니다.

VelSize의 크기는 (프레임 크기 - 블록 크기 + 이격 거리) / 이격 거리입니다.


CvMat velx = Cv.CreateMat(VelSize.Height, VelSize.Width, MatrixType.F32C1);
CvMat vely = Cv.CreateMat(VelSize.Height, VelSize.Width, MatrixType.F32C1);

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

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

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

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

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


Cv.CalcOpticalFlowBM(prev, curr, blockSize, shiftSize, maxrange, false, velx, vely);

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

Cv.CalcOpticalFlowBM(이전 프레임, 현재 프레임, 블록 크기, 이격 거리, 인접한 블록 크기, 초기 근사값 속도 필드 사용 유/무, x 방향 속도 벡터, y 방향 속도 벡터)입니다.

초기 근사값 속도 필드 사용 유/무는 초기 근사값으로 입력 속도를 사용할지 여부를 결정합니다.

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


for (int i = 0; i < velx.Rows; i++)
{
    for (int j = 0; j < vely.Cols; j++)
    {
        ...
    }
}

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

로 반복을 실행합니다.


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.DrawLine(optical, new CvPoint(j * ShiftSize, i * ShiftSize), new CvPoint(j * ShiftSize + dx, i * ShiftSize + dy), CvColor.Red, 3, LineType.AntiAlias, 0);
        

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

광학 흐름의 발생 지점은 (j * ShiftSize, i * ShiftSize)입니다.

광학 흐름의 도착 지점은 (j * ShiftSize + dx, i * ShiftSize + dy)입니다.

dxdy의 값을 사용하여 광학 흐름의 속도를 출력할 수 있습니다.

  • Tip : dxdy를 이용하여 일정 속도 이상, 이하의 값을 무시하거나 출력할 수 있습니다.



출력 결과

댓글 남기기