C# OpenCV 강좌 : 제 59강 - 광학 흐름 - PyrLK
광학 흐름 - PyrLK(Optical Flow Pyramid LK)
카메라와 피사체의 상대 운동
에 의하여 발생하는 피사체의 운동에 대한 패턴
을 검출합니다.
PyrLK(Pyramid Lucas Kanade)
방법은 입력 이미지와 피라미드 이미지
를 사용하여 코너
를 기준으로 광학 흐름
을 검출합니다.
이전 프레임(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 OpticalFlowPyrLK(IplImage previous, IplImage current)
{
IplImage prev = this.GrayScale(previous);
IplImage curr = this.GrayScale(current);
optical = current;
IplImage prev_pyramid = new IplImage(new CvSize(optical.Width + 8, optical.Height / 3), BitDepth.U8, 1);
IplImage curr_pyramid = new IplImage(new CvSize(optical.Width + 8, optical.Height / 3), BitDepth.U8, 1);
IplImage eigImg = new IplImage(optical.Size, BitDepth.U8, 1);
IplImage tempImg = new IplImage(optical.Size, BitDepth.U8, 1);
CvPoint2D32f[] corners;
CvPoint2D32f[] corners2;
int cornerCount = 600;
sbyte[] status;
CvTermCriteria criteria = new CvTermCriteria(100, 0.01);
Cv.GoodFeaturesToTrack(prev, eigImg, tempImg, out corners, ref cornerCount, 0.01, 15);
Cv.CalcOpticalFlowPyrLK(prev, curr, prev_pyramid, curr_pyramid, corners, out corners2, new CvSize(20, 20), 5, out status, criteria, LKFlowFlag.PyrAReady);
for (int i = 0; i < cornerCount; i++)
{
if (status[i] == 1)
{
Cv.DrawLine(optical, corners[i], corners2[i], CvColor.Red, 1, LineType.AntiAlias, 0);
Cv.DrawCircle(optical, corners2[i], 3, CvColor.Red, -1);
}
}
return optical;
}
public void Dispose()
{
if (gray!= null) Cv.ReleaseImage(gray);
if (optical != null) Cv.ReleaseImage(optical);
}
}
}
세부 코드
public IplImage OpticalFlowPyrLK(IplImage previous, IplImage current)
{
...
}
이전 프레임 previous
와 현재 프레임 current
를 매개변수로 사용하여 검출을 진행합니다.
IplImage prev = this.GrayScale(previous);
IplImage curr = this.GrayScale(current);
optical = current;
광학 흐름 함수는 그레이스케일
을 적용하여 검출을 진행합니다.
계산이미지로 사용할 prev
와 curr
변수에 그레이스케일
을 적용합니다.
이후, 결과로 사용할 optical
필드에 현재 프레임
을 사용합니다.
- Tip : 그레이스케일을 사용하여 검출하므로
급격한 밝기 변화
나노이즈
에는 정확한 검출을 얻어낼 수 없습니다.
IplImage prev_pyramid = new IplImage(new CvSize(optical.Width + 8, optical.Height / 3), BitDepth.U8, 1);
IplImage curr_pyramid = new IplImage(new CvSize(optical.Width + 8, optical.Height / 3), BitDepth.U8, 1);
LK
방법은 피라미드 이미지를 사용하므로 이전 프레임
과 현재 프레임
에 피라미드 이미지를 저장할 변수를 생성합니다.
피라미드 이미지의 크기는 너비
는 8
만큼 크며, 높이
는 1/3
값입니다.
IplImage eigImg = new IplImage(optical.Size, BitDepth.U8, 1);
IplImage tempImg = new IplImage(optical.Size, BitDepth.U8, 1);
코너 검출 함수인 GoodFeaturesToTrack()
을 사용할 예정이므로 eigImg
와 tempImg
를 저장합니다.
CvPoint2D32f[] corners;
CvPoint2D32f[] corners2;
GoodFeaturesToTrack()
에서 검출한 코너를 저장할 corners
와 광학 흐름으로 이동한 코너인 corners2
를 생성합니다.
int cornerCount = 600;
sbyte[] status;
CvTermCriteria criteria = new CvTermCriteria(100, 0.01);
cornerCount
는 반환할 코너의 최대 개수
를 설정합니다.
값이 너무 높을 경우 연산 속도가 느려집니다.
status
는 광학 흐름의 발생 유/무
를 저장합니다.
1
이 저장될 경우 광학 흐름이 발생하였으며, 0
이 저장될 경우 광학 흐름이 발생하지 않습니다.
criteria
를 사용하여 종료 기준
을 설정합니다.
Cv.GoodFeaturesToTrack(prev, eigImg, tempImg, out corners, ref cornerCount, 0.01, 15);
이전 프레임
에 대하여 코너
를 검출합니다.
Cv.CalcOpticalFlowPyrLK(prev, curr, prev_pyramid, curr_pyramid, corners, out corners2, new CvSize(20, 20), 5, out status, criteria, LKFlowFlag.PyrAReady);
Cv.CalcOpticalFlowPyrLK()
을 사용하여 광학 흐름
을 구합니다.
Cv.CalcOpticalFlowPyrLK(이전 프레임, 현재 프레임, 피라미드 이전 프레임, 피라미드 현재 프레임, 이전 프레임 코너 검출점, 계산된 현재 프레임 코너 검출점, 블록 크기, 레벨, 상태, 종결 기준, 플래그)
를 의미합니다.
계산된 현재 프레임 코너 검출점
에는 광학 흐름이 발생한 종료점의 위치를 반환합니다.
상태
는 광학흐름의 발생 유/무
를 반환합니다.
-
플래그
PyrAReady
: 이전 프레임의 피라미드를 사전에 계산PyrBReady
: 현재 프레임의 피라미드를 사전에 계산InitialGuesses
: 함수가 호출되기 전에 초기 추정 좌표를 포함 (이전 프레임의 코너 검출점이 현재 프레임의 코너 검출점)InitialFlow
: 함수가 호출되기 전에 초기 추정 좌표를 포함 (이전 프레임의 코너 검출점이 현재 프레임의 코너 검출점)GetMinEigenVals
: 최소 고유 값을 오류 측정 값으로 사용
-
Tip :
오류 측정값 (trackError)
- 계산된 값이 주변 움직임에 비해서 값이 너무 튀는 경우 제거하는 용도로 사용
for (int i = 0; i < cornerCount; i++)
{
if (status[i] == 1)
{
Cv.DrawLine(optical, corners[i], corners2[i], CvColor.Red, 1, LineType.AntiAlias, 0);
Cv.DrawCircle(optical, corners2[i], 3, CvColor.Red, -1);
}
}
for
문을 사용하여 검출된 코너의 개수
만큼 반복합니다.
상태
값을 사용하여 광학 흐름이 발생하였을 때 값을 출력하게합니다.
Cv.DrawLine()
과 Cv.DrawCircle()
을 사용하여 광학 흐름을 optical
필드에 표시합니다.
- Tip :
dx
와dy
를 생성하여 일정 속도 이상, 이하의 값을 무시하거나 출력할 수 있습니다.
출력 결과
공유하기


댓글 남기기