Object Tracking - 1

qkdk·2024년 8월 26일

openCV

목록 보기
9/15
post-thumbnail

Optical Flow

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

Assumtion

  1. 프레임이 넘어가도 물체의 픽셀 강도는 일정해야 한다.
    • ex) 프레임 사이에서 깜밖이는 전구는 추적하기 어렵다.
  2. 인접한 픽셀끼리는 비슷한 움직임을 가진다.


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

Lucas Kanade 알고리즘

  • sparse feature(전체 픽셀중 특정한 feature) 만 추적하는 알고리즘.
    • 이미지 내 모든 feature의 흐름을 계산해야 한다면
      dense optical flow를 추적할수 있는 Gunnar Farneback 알고리즘을 사용한다.
  • 이미지 픽셀의 변화량은 직교좌 표계의 좌표 형식으로 출력

cv2.calcOpticalFlowPyrLK

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()

calcOpticalFlowPyrLK criteria

(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) 동안 실행된 후 종료합니다. (이 옵션은 종종 사용되지 않습니다.)

calcOpticalFlowPyrLK status

1 혹은 0 값을 리턴하고 1은 성공 0은 실패를 의미함

참고) Numpy Boolean Indexing

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])

Gunnar Farneback 알고리즘

특정한 Feature만 추적하는 Lucas Kanade 달리 전체 픽셀을 대상으로 움직임을 추적한다.

cv2.calcOpticalFlowFarneback


# 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()

매개변수

  1. prev: 이전 프레임의 이미지 (grayscale, 8-bit single-channel).
  2. next: 다음 프레임의 이미지 (grayscale, 8-bit single-channel).
  3. flow: 초기 흐름의 추정치 (주로 None으로 설정). 알고리즘이 시작 지점을 알파벳 설정하게끔 합니다.
  4. pyr_scale: 피라미드의 각 레벨 사이의 이미지 스케일 (0.5를 권장). 이는 이미지의 크기를 줄이는 비율을 뜻합니다.
  5. levels: 피라미드의 레벨 수. 기본값은 3입니다.
  6. winsize: 평균 이동 창의 크기. 일반적으로 15로 설정합니다.
  7. iterations: 각 피라미드 레벨에서 알고리즘의 반복 횟수. 기본값은 3입니다.
  8. poly_n: 픽셀 근처의 기저함수 폴리노미얼의 크기(5 또는 7).
  9. poly_sigma: poly_n 크기에서 처리될 가우시안 분포의 표준 편차 (1.5 또는 0.9를 권장).
  10. flags: 다양한 플래그들, 알고리즘의 동작을 제어합니다. cv2.OPTFLOW_USE_INITIAL_FLOW 또는 cv2.OPTFLOW_FARNEBACK_GAUSSIAN 등을 설정할 수 있습니다.

반환값

  • flow: 2-channel (float32) 배열로, 1채널은 x 방향의 이동을, 다른 1채널은 y 방향의 이동을 가리킵니다.

참고) Cartesian coordinate(직교 좌표) , Polar coordinate(극 좌표)

CartesianPolar
profile
qkdk

0개의 댓글