< Optical Flow >
이번 장에서는,
- 광학 흐름의 개념에 대해서 이해하고 Lucas-Kanade 방법을 사용한 추정을 해보고
- cv2.calcOpticalFlowPyrLK() 함수를 사용해서 비디오에서의 특성 점을 추적해볼 것이다.
Optical Flow
광학 흐름은 물체나 카메라의 움직임에 의해 발생하는 두 개의 연속되는 프레임사이에서 객체의 외관상 움직임의 패턴이다. 각 벡터는 첫 번째 프레임에서 점이 두 번째 프레임으로 점으로의 움직임을 보여주는 이동 벡터인 2D 벡터 필드이다. 아래 이미지를 보자!
사진
위 사진은 공의 움직임을 5개 연속적인 프레임으로 보여준다. 화살표는 이동 벡터를 나타낸다. 광학 흐름은 다음과 같은 분야에서 적용된다 :
- 움직임을 통한 구조 분석
- 비디오 압축
- 비디오 안정화 : 깨끗한 영상으로 만드는 것
광학 흐름은 여러 가정위에서 작동한다.
- 객체의 픽셀 채도(intensity)는 연속된 프레임 사이에서 바뀌면 안된다.
- 인접부 픽셀들은 유사한 움직임을 갖는다.
첫 프레임에서의 픽셀
그리고 다음의 식을 얻기 위해, 오른쪽 식의 테일러 시리즈 근사치를 취하고, 공통항을 제거하고
fx=∂f∂x ;fy=∂f∂y u=dxdt ;v=dydt
위의 식을 Optical Flow 식이라고 부른다. 그 안에서,
Lucas-Kanade method
주변부 픽셀은 유사한 움직임을 갖는다는 가정을 봤었다. Lucas-Kanade 방법은 점을 주변으로 3x3 패치를 취한다. 그래서 총 9개의 점이 동일한 움직임을 갖게 된다. 그리고 9개 점에 대한
(Harris corner detector로 매트릭스의 역함수의 유사도를 체크한다. 이는 코너가 추적하기에 더 나은 포인트라는 것을 말한다)
사용자의 관점에서 보면 아이디어는 간단하다. 추적할 포인트들을 주고, 이 점들의 광학 흐름 벡터를 얻는 것이다. 하지만 다시 문제는 등장한다. 현재까지는, 작은 움직임을 다뤄왔었다. 그래서 이는 커다란 움직임이 있으면 실패했었다. 그래서 피라미드로 간다. 피라미드의 상위로 가게 되면, 작은 움직임은 제거되고 큰 움직임이 작은 움직임이 된다. 그래서 거기에 Lucas-Kanade를 적용해서, 스케일에 따른 광학 흐름을 얻는 것이다.
Lucas-Kanade Optical Flow in OpenCV
OpenCV는 이 모든걸 아우르는 cv2.calcOpticalFlowPyrLK() 함수를 제공한다. 여기서, 비디오 내의 점을 추적하기 위한 간단한 기능을 생성한다. 이 점을 결정하기 위해서, cv2.goodFeaturesToTrack()을 사용한다. 첫 프레임을 받아서, Shi-Tomasi 코너 점을 탐지하고, Lucas-Kanade 광학 흐름을 사용하여 반복적으로 점을 추적한다. cv2.calcOpticalFlowPyrLK() 함수를 위해, 이전의 프레임과 이전의 점, 다음 프레임을 보낸다. 다음 포인트를 찾으면 값이 1인 번호와 함께 반환하고, 그렇지 않으면 0이다. 이 다음 포인트를 다음 단계의 이전 포인트로 반복해서 보낸다. 아래 코드를 보자.
import numpy as np
import cv2
cap = cv2.VideoCapture('./videos/walking.avi')
# ShiTomasi 코너 탐지를 위한 파라미터
feature_params = dict(maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7,
blockSize = 7)
# Lucas-Kanade 광학 흐름을 위한 파라미터
lk_params = dict(winSize = (15,15),
maxLevel = 2,
criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 랜덤 색 생성하기
color = np.random.randint(0,255,(100,3))
# 첫 프레임을 가지고 코너를 찾자
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
# 그리기 위해서 마스크 이미지를 생성하기
mask = np.zeros_like(old_frame)
while(1):
res, frame = cap.read()
frame_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
# 광학 흐름 계산하기
p1,st,err = cv2.calcOpticalFlowPyrLK(old_gray,frame_gray,p0,None,**lk_params)
# 좋은 포인트를 고른다
good_new = p1[st==1]
good_old = p0[st==1]
# 추적하는 것 그리기
for i,(new,old) in enumerate(zip(good_new,good_old)):
a,b = new.ravel()
c,d = old.ravel()
mask = cv2.line(mask,(a,b),(c,d),color[i].tolist(),2)
frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1)
img = cv2.add(frame,mask)
cv2.imshow('frame',img)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
# 이제 이전 프레임과 이전 포인트를 업데이트한다.
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2)
cv2.destroyAllWindows()
cap.release()
이 코드는 다음 키포인트가 얼마나 정확한지 확인 못한다. 따라서 어떤 특징 포인트가 이미지내에서 사라지게 되면, 광학 흐름은 그것에 가깝에 보일 수 있는 다음 포인트를 찾을 가능성이 있다. 따라서 강건한 추적을 위해서, 특정 간격으로 코너 포인트를 감지해야 한다. OpenCV 샘플은 5프레임마다 특징점을 찾는 샘플을 제공한다. 또한 광학 흐름 포인트를 역 확인해서(backward-check) 좋은 점만 선택하도록 했다.
결과는 아래와 같다.
Dense Optical Flow in OpenCV
Lucas-Kanade 방법은 희소 특성 집합에 대한 광학 흐름을 계산한다. (위의 예에서 Shi-Tomasi 알고리즘을 사용하여 검출된 코너) OpenCV는 밀집한(dense) 광학 흐름을 찾기 위한 또 다른 알고리즘을 제공한다. 그것은 프레임의 모든 포인트에 대한 광학 흐름을 계산한다.
아래 샘플은 위의 알고리즘을 사용하여 밀집한 광학 흐름을 찾는 밥법을 보여준다. 영상속의 움직임을 크기와 방향, 2개의 채널로 나타내는데,
import numpy as np
import cv2
cap = cv2.VideoCapture('./videos/walking.avi')
ret, frame1 = cap.read()
prev = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[...,1] = 255
while(1):
ret, frame2 = cap.read()
nexts = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
flow = cv2.calcOpticalFlowFarneback(prev,nexts,None,0.5,3,15,3,5,1.2,0)
mag, ang = cv2.cartToPolar(flow[...,0],flow[...,1])
hsv[...,0] = ang*180/np.pi/2
hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)
rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)
cv2.imshow('frame2',rgb)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
elif k == ord('s'):
cv2.imwrite('opticalfb.png',frame2)
cv2.imwrite('opticalhsv.png',rgb)
prev = nexts
cap.release()
cv2.destroyAllWindows()
아래 결과를 보자.
위와 같이 사람의 형상이 번진것 처럼 보이며 추적한다.