연속적인 두 프레임에서 카메라나 물체의 이동에 의해 물체가 움직이는 명확한 패턴

위 이미지에서 공이 움직이는것인지, 카메라가 움직이는것인지 영상만으로는 판단하기 어렵다.

import numpy as np
import cv2
# Shi - Tomahi corner detecton을 사용하기 위한 파라미터 설정
corner_track_params = dict(maxCorners = 10, qualityLevel = 0.3, minDistance = 7, BlockSize=7)
# Lucas Kanade 알고리즘을 사용하기 위한 파라미터 설정
lk_params = dict(winSize=(200,200), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10,0.03))
cap = cv2.VideoCapture(0)
ret, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
# Sparse Feature를 코너검출 알고리즘을 통해 확보
prevPts = cv2.goodFeatureToTrack(prev_gray, mask=None, **corner_track_params)
mask = np.zeros_like(prev_frame)
while True:
ret, frame = cap.read()
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Lukas Kanade 알고리즘 실행
nextPts, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, frame_gray, prevPts, None, **lk_params, )
good_new = nextPts[status==1]
good_prev = prevPts[status==1]
for i, (new, prev) in enumerate(zip(good_new, good_prev)):
x_new, y_new = new.ravel()
x_prev, y_prev = prev.ravel()
mask = cv2.line(mask, (x_new, y_new), (x_prev, y_prev), (0,255,0), 3)
frame = cv2.circle(frame, (x_new, y_new), 8, (0,0,255), -1)
img = cv2.add(frame, mask)
cv2.imshow('tracking', img)
k = cv2.waitKey(30) & 0xFF
if k == 27:
break
prev_gray = frame_gray.copy()
prevPts = good_new.reshape(-1,1,2)
cv2.destroyAllWindows()
cap.release()
(criteria_type, max_count, epsilon)로 구성
criteria_type: 종료 기준 유형을 지정합니다. cv2.TERM_CRITERIA_EPS, cv2.TERM_CRITERIA_COUNT 또는 둘을 조합한 것으로 설정됩니다.max_count: 최대 반복 횟수입니다.epsilon: 원하는 정확도의 임계값입니다.cv2.TERM_CRITERIA_EPS (1): 알고리즘이 어느 정도의 정확도(epsilon)에 도달했을 때 반복을 종료합니다. 이는 반복 중 알고리즘의 변화(예: 두 프레임 간의 유사성)의 크기가 설정된 epsilon 이하가 되었을 때 종료를 의미합니다.cv2.TERM_CRITERIA_COUNT (2): 알고리즘이 정해진 반복 횟수(count)에 도달했을 때 종료합니다. 즉, 알고리즘이 특정 횟수만큼 반복을 수행한 후 자동으로 종료됩니다.cv2.TERM_CRITERIA_MAX (4): 알고리즘이 특정 최대 시간(max time) 동안 실행된 후 종료합니다. (이 옵션은 종종 사용되지 않습니다.)1 혹은 0 값을 리턴하고 1은 성공 0은 실패를 의미함
Numpy 배열의 [] 안에 조건을 주어 True인 경우만 반환하여 새로운 Numpy배열을 만드는 방법
# ---- Boolean Indexing 적용
good_new = nextPts[status==1]
good_prev = prevPts[status==1]
# ---- Boolean Indexing 미적용
good_new = []
good_prev = []
for i, v in enumerate(status)
if v == True:
good_new.append(nextPts[i])
good_prev.append(prevPts[i])

특정한 Feature만 추적하는 Lucas Kanade 달리 전체 픽셀을 대상으로 움직임을 추적한다.
# Syntax
flow = cv2.calcOpticalFlowFarneback(prev, next, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags)
# Example
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
ret, frame1 = cap.read()
prvsImg = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
# 결과를 그리기 위한 mask
hsv_mask = np.zeros_like(frame1)
hsv_mask[:, :, 1] = 255
while True:
ret, frame2 = cap.read()
nextImg = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
# 계산된 결과를 직교 좌표 형식으로 리턴한다.
flow = cv2.calcOpticalFlowFarneback(prvsImg, nextImg, None, 0.5, 3, 15, 3, 5, 1.2, 0)
# 직교 좌표 형식을 극 좌표 형식으로 변화해 각도와, 크기를 구한다.
# 각도와 크기를 활용해서 HSV 형식의 이미지를 만들기 위함
# 움직이는 방향과 이동량에 따라 색도, 채도 변화
mag, ang = cv2.cartToPolar(flow[:, :, 0], flow[:, :, 1], angleInDegrees=True)
hsv_mask[:, :, 0] = ang/2
hsv_mask[:, :, 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
bgr = cv2.cvtColor(hsv_mask, cv2.COLOR_HSV2BGR)
cv2.imshow('frame', bgr)
k = cv2.waitKey(10) & 0xFF
if k == 27:
break
prvsImg = nextImg
cap.release()
cv2.destroyAllWindows()
prev: 이전 프레임의 이미지 (grayscale, 8-bit single-channel).next: 다음 프레임의 이미지 (grayscale, 8-bit single-channel).flow: 초기 흐름의 추정치 (주로 None으로 설정). 알고리즘이 시작 지점을 알파벳 설정하게끔 합니다.pyr_scale: 피라미드의 각 레벨 사이의 이미지 스케일 (0.5를 권장). 이는 이미지의 크기를 줄이는 비율을 뜻합니다.levels: 피라미드의 레벨 수. 기본값은 3입니다.winsize: 평균 이동 창의 크기. 일반적으로 15로 설정합니다.iterations: 각 피라미드 레벨에서 알고리즘의 반복 횟수. 기본값은 3입니다.poly_n: 픽셀 근처의 기저함수 폴리노미얼의 크기(5 또는 7).poly_sigma: poly_n 크기에서 처리될 가우시안 분포의 표준 편차 (1.5 또는 0.9를 권장).flags: 다양한 플래그들, 알고리즘의 동작을 제어합니다. cv2.OPTFLOW_USE_INITIAL_FLOW 또는 cv2.OPTFLOW_FARNEBACK_GAUSSIAN 등을 설정할 수 있습니다.flow: 2-channel (float32) 배열로, 1채널은 x 방향의 이동을, 다른 1채널은 y 방향의 이동을 가리킵니다.| Cartesian | Polar |
|---|---|
![]() | ![]() |
![]() | ![]() |