Python/파이썬 OpenCV 공부

[파이썬 OpenCV] 영상의 모션 벡터 - 루카스 카나데 옵티컬 플로우 - cv2.calcOpticalFlowPyrLK

AI 꿈나무 2020. 10. 21. 15:46
반응형

옵티컬플로우 - Optical flow

 옵티컬플로우는 연속하는 두 프레임(영상)에서 카메라 또는 객체의 움직임에 의해 나타나는 객체의 이동 정보 패턴을 의미합니다.

 

 

 

출처 : OpenCV 튜토리얼

 픽셀이 어떻게 움직였는지를 화살표로 나타내고 있습니다.

 

옵티컬플로우 활용 분야

 손떨림을 보정해서 동영상을 저장하는 용도, 동영상을 압축할 때도 움직임 정보를 잘 활용하면 적은 bit를 활용해서 화질이 좋은 동영상으로 압축하는 데에 이용할 수 있습니다.

 

OpenCV 옵티컬플로우 계산 함수

(1) 루카스-카나데 알고리즘(Locas-Kanade algorithm)

 루카스-카나데 알고리즘은 지정한 점들에 대해 옵티컬플로우를 계산하는 방법입니다.

 (주로) Sparse points에 대한 이동 벡터를 계산합니다.

 특정 픽셀에서 옵티컬플로우 벡터를 계산합니다.

 

 cv2.calcOpticalFlowPyrLK 함수를 이용하여 구현할 수 있습니다.

 사용자가 지정한 몇몇의 점들에 대한 움직임 정보를 리턴하게 됩니다.

 따라서 다음 영역에서 어디로 이동했는지 알 수 있습니다.

 주로 코너점들을 지정하여 이용합니다.

 

(2) 파네백 알고리즘(Farneback's algorithm)

 모든 픽셀에 대해 옵티컬플로우를 계산하는 방법입니다.

 밀집 오티컬플로우라는 용어를 쓰기도 합니다.

 Dense points에 대한 이동 벡터를 계산하게 됩니다.

 이는 모든 픽셀에서 옵티컬플로우 벡터를 계산한다는 의미입니다.

 

OpenCV 옵티컬플로우 클래스

 OpenCV에서 제공하는 옵티컬플로우 클래스입니다.

 

출처 : OpenCV 홈페이지

 

루카스-카나데 옵티컬플로우 계산 함수 - cv2.calcOpticalFlowPyrLK

 인자가 상당히 많지만 거의 디폴트값을 사용해도 무난하게 사용할 수 있습니다.

 

cv2.calcOpticalFlowPyrLK(prevImg, nextImg, prevPts, nextPts, status=None, err=None, winSize=None, maxLevel=None, criteria=None, flags=None, minEigThreshold=None) -> nextPts, status, err

• prevImg, nextImg: 이전 프레임과 현재 프레임. 8비트 입력 영상.

• prevPts: 이전 프레임에서 추적할 점들. numpy.ndarray. shape=(N, 1, 2), dtype=np.float32.

• nextPts: (출력) prevPts 점들이 이동한 (현재 프레임) 좌표.

• status: (출력) 점들의 매칭 상태. numpy.ndarray. shape=(N, 1), dtype=np.uint8. i번째 원소가 1이면 prevPts의 i번째 점이 nextPts의 i번째 점으로 이동.

• err: 결과 오차 정보. numpy.ndarray. shape=(N, 1), dtype=np.float32.

• winSize: 각 피라미드 레벨에서 검색할 윈도우 크기. 기본값은 (21, 21).

• maxLevel: 최대 피라미드 레벨. 0이면 피라미드 사용 안 함. 기본값은 3.

• criteria: (반복 알고리즘의) 종료 기준

 디폴트값이 없는 인자를 살펴보겠습니다.

 prevPts는 추적 하고 싶은 이전 프레임의 좌표를 의미합니다. (ndarry, (N,1,2), float32)

 nextPts는 출력값이며 prevPts가 어디로 이동했는지에 대한 정보를 저장합니다. 입력에 None을 줍니다.

 satus와 err도 출력으로 받습니다.

 

 status는 ndarray, (N,1), unit8 형태로 N은 이전 점에서 추적할 좌표 개수를 의미합니다.

 추적하다가 점이 갑자기 사라질 수 있고, 알고리즘이 못찾아서 놓칠수 있습니다.

 추적이 잘 됬나 안되었나를 알려주는 플래그 값이 저장되어 있는 행렬입니다.

 원소 값이 1이면 추적이 잘되었고 0이면 추적을 잘 못한 것입니다.

 

 err는 에러 값을 저장하고 있는 행렬입니다. (N,1) float32 형태입니다.

 

 나머지는 디폴트값을 지정해주어도 괜찮습니다.

 

루카스-카나데 옵티컬플로우 예제 코드

 입력 영상의 모든 픽셀을 파악하는 것이 아니라 내가 지정한 점들만 어디로 이동했는지를 알려주는 함수입니다.

 이를 잘 이용하면 트랙킹을 하는데에 유용하게 사용할 수 있습니다.

 

src1 = cv2.imread('frame1.jpg')
src2 = cv2.imread('frame2.jpg')

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 그레이스케일로 변환
gray1 = cv2.cvtColor(src1, cv2.COLOR_BGR2GRAY)

# 코너점 찾는 함수, 그레이스케일 영상만 입력 가능
pt1 = cv2.goodFeaturesToTrack(gray1, 50, 0.01, 10)

# 찾은 코너점 정보를 옵티컬플로우 함수에 입력
# src1, src2에서 움직임 정보를 찾아내고 pt1에 입력한 좌표가 어디로 이동했는지 파악
pt2, status, err = cv2.calcOpticalFlowPyrLK(src1, src2, pt1, None)

# 가중합으로 개체가 어느 정도 이동했는지 보기 위함
dst = cv2.addWeighted(src1, 0.5, src2, 0.5, 0)

# pt1과 pt2를 화면에 표시
for i in range(pt2.shape[0]):
    if status[i,0] == 0: # status = 0인 것은 제외, 잘못 찾은 것을 의미
        continue
        
    cv2.circle(dst, tuple(pt1[i, 0]), 4, (0, 255, 255), 2, cv2.LINE_AA)
    cv2.circle(dst, tuple(pt2[i, 0]), 4, (0, 0, 255), 2, cv2.LINE_AA(
    
    # pt1과 pt2를 이어주는 선 그리기
    cv2.arrowedLine(dst, tuple(pt1[i, 0]), tuple(pt2[i, 0]), (0, 255, 0), 2)
    
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

 

 큐브의 코너점들이 어떻게 이동했는지 시각적으로 나타내고 있습니다.

 


 

OpenCV 튜토리얼과 황선규 박사님의 'OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝' 을 공부하면서 정리해 보았습니다.

docs.opencv.org/4.3.0/d4/dee/tutorial_optical_flow.html

 

OpenCV: Optical Flow

Goal In this chapter, We will understand the concepts of optical flow and its estimation using Lucas-Kanade method. We will use functions like cv.calcOpticalFlowPyrLK() to track feature points in a video. We will create a dense optical flow field using the

docs.opencv.org

반응형