C# OpenCV 강좌 : 제 56강 - 광학 흐름 - BM
광학 흐름 - BM(Optical Flow BM)
카메라와 피사체의 상대 운동
에 의하여 발생하는 피사체의 운동에 대한 패턴
을 검출합니다.
BM(Block Matching)
방법은 블록 단위로 이미지를 나누며 이전 프레임
과 현재 프레임
을 매칭하여 광학 흐름
을 검출합니다.
이전 프레임(Previous)
과 현재 프레임(Current)
은 영상이나 이미지를 사용합니다.
원본(Source, src)
은 영상을 사용합니다.
영상 사용하기
: 3강 바로가기
메인 코드
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;
광학 흐름 함수는 그레이스케일
을 적용하여 검출을 진행합니다.
계산이미지로 사용할 prev
와 curr
변수에 그레이스케일
을 적용합니다.
이후, 결과로 사용할 optical
필드에 현재 프레임
을 사용합니다.
- Tip : 그레이스케일을 사용하여 검출하므로
급격한 밝기 변화
나노이즈
에는 정확한 검출을 얻어낼 수 없습니다.
int BlockSize = 16;
int ShiftSize = 32;
int MaxRange = 10;
함수에 사용되는 주요 상수인 BlockSize
, ShiftSize
, Range
를 선언하고 값을 적용합니다.
BlockSize
는 검출에 사용할 블록의 크기
를 의미합니다.
ShiftSize
는 블록의 이격 거리
를 의미합니다. 값이 낮을 수록 검출 간격이 촘촘해집니다.
MaxRange
는 블록 주변의 인접한 블록 크기를 의미합니다.
- Tip :
BlockSize
와MaxRange
의 값이 높을 수록,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 방향 속도 벡터
를 저장할 velx
와 Y 방향 속도 벡터
를 저장할 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)
입니다.
dx
와 dy
의 값을 사용하여 광학 흐름의 속도를 출력할 수 있습니다.
- Tip :
dx
와dy
를 이용하여 일정 속도 이상, 이하의 값을 무시하거나 출력할 수 있습니다.
출력 결과
공유하기


댓글 남기기