Optical Flow

จาก Morange Wiki

Optical Flow

วัตถุประสงค์

  • เรียนรู้เกี่ยวกับการใช้งานและแนวคิดของ Optical Flow และวิธีการประมาณการโดย Lucas-Kanade
  • ใช้ฟังก์ชั่น cv2.calcOpticalFlowPyrLK() เพื่อติิดตามจุดต่างๆในวิดีโอ

ทฤษฏี

  • Optical flow นั้นคือ pattern ของ apparent motion โดยจะมี 2 ส่วนเฟรมที่ติดต่อกัน จะเกิดจากการเคลื่อนที่ของวัตถุหรือกล้อง เรียกว่า 2D Vector โดยที่อีก vector นึง จะแสดงเป็นการเคลื่อนไหวของจุดจากเฟรมแรก และ เฟรมที่สอง
  • ในการพิจารณา (Consider) พิกเซล I(x,y,t) และจะมีการเคลื่อนที่เป็นระยะทางเรียกค่านี้คือ (dx,dy) ในเฟรมต่อไป โดยจะมีสูตรคือ I(x , y , t) = I( x + dx , y + dy , t + dt)
  • และหลังจากนั้นใช้ชุดเวลาเทย์เลอร์เข้ามาช่วยโดยหาจากด้าน ขวามือ และลบด้วยสูตรปกติและหารด้วย DT เพื่อหาค่าสมการต่อไปนี้ fxu + fyv + ft=0
    • เมื่อ
Selection 088.png
  • ข้านบนนี้จะเรียกว่าการไหลของ Optical โดนจะสามารถหาค่า FX และ การไล่ระดับสีของภาพได้ในทำนองเดียวกันกัับ Gradient ไปตามเวลา แต่ (U,V) เราจะไม่สามารถแก้ปัญหานี้อย่างใดอย่างหนั่งกับสมการสองตัวแปรที่ไม่รู้จักได้ โดยมีวิธีแก้ไขหลากหลายวิธีมากเกี่ยวกับสิ่งนี้ โดยหนึ่งในนั้นคือ Lucas-Kanade

Lucas-Kanade method

  • โดยเราจะสามารถเห็นสมมติฐานก่อนว่าทุกๆพิกเซลที่อยู่ใกล้เคียง จะเคลื่อนไหวคล้ายกัน โดยวิธีของ Lucas-Kanade นั้นจะใช้ patch ขนาดเท่ากับ 3x3 รอบจุด จะสามารถเห็นทั้งหมด 9 จุดที่มีการเคลื่อนไหวเหมือนกัน เราสามารถหา (fx , fy , ft) สำหรับทั้ง 9 จุดนั้น มีสองตัวแปรที่ไม่รู้จักซึ่งมีมากกว่ากำหนด โดยทางออกที่ดีที่สุดนั้นจะได้รับรู้ด้วยวิธีที่หาค่าน้องของตาราง โดยด้านล่างนี้จะเป็นทางออกสุดท้ายที่เป็นสองสมการ สองปัญหาที่ไม่รู้จักและการแก้ปัญหาจะได้รับการแก้ไขด้วยวิธีข้างล่างนี้ ด้วยสูตร
Selection 089.png

Lucas-Kanade Optical Flow in OpenCV

  • ใน OpenCV นั้นมีฟังก์ชั่นที่เข้ามาช่วยการใช้ Lucas-Kanade Optical Flow คือฟังก์ชั่นที่ชื่อว่า cv2.calcOpticalFlowPyrLK(). ต่อไป จะเป็นการสร้างตัวอย่างการใช้งานแบบง่าๆ โดยมีฟังก์ชั้นเพื่อตัดสินใจ (decide) เราจะใช้ฟังก์ชั่น cv2.goodFeaturesToTrack()

CODE EXAMPLE

import numpy as np
 import cv2
 
 cap = cv2.VideoCapture('slow.flv')
 
 # params for ShiTomasi corner detection
 feature_params = dict( maxCorners = 100,
                        qualityLevel = 0.3,
                        minDistance = 7,
                        blockSize = 7 )
 
 # Parameters for lucas kanade optical flow
 lk_params = dict( winSize  = (15,15),
                   maxLevel = 2,
                   criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
 
 # Create some random colors
 color = np.random.randint(0,255,(100,3))
 
 # Take first frame and find corners in it
 ret, old_frame = cap.read()
 old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
 p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
 
 # Create a mask image for drawing purposes
 mask = np.zeros_like(old_frame)
 
 while(1):
     ret,frame = cap.read()
     frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
 
     # calculate optical flow
     p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
 
     # Select good points
     good_new = p1[st==1]
     good_old = p0[st==1]
 
     # draw the tracks
     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
 
     # Now update the previous frame and previous points
     old_gray = frame_gray.copy()
     p0 = good_new.reshape(-1,1,2)
 
 cv2.destroyAllWindows()
 cap.release()

Result

Selection 090.png

Dense Optical Flow in OpenCV

  • ใน Method ของ Lucas-Kanade นั้น จะคำนวณการไหลของ Optical flow สำหรับกลุ่มหรือพวกที่มีคุณลักษณะที่เบาบาง โดย OpenCV จะมีขั้นตอนและวิธีการที่จะหาการไหลของแสงหนาแน่น โดยจะคำนวณการไหลของแสงสำหรับจุดที่อยู่ในกรอบ โดนจะขึ้นอยู่กับขั้นตอนของ Gunner Farneback's algorithm โดวจะอธิบายในกรอบการเคลื่อนไหวและประเมินจากการขยายตัวของพหุนาม)
      • ในด้านล่างนี้จะเป็นตัวอย่างเพื่อให้เห็นถึงวิธีการหาการไหลของแสงหนาแน่น

Code Example

import cv2
import numpy as np
cap = cv2.VideoCapture(0)

ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[...,1] = 255

while(1):
   ret, frame2 = cap.read()
   next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)

   flow = cv2.calcOpticalFlowFarneback(prvs,next, 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)
   bgr = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)

   cv2.imshow('frame2',bgr)
   k = cv2.waitKey(30) & 0xff
   if k == 27:
       break
   elif k == ord('s'):
       cv2.imwrite('opticalfb.png',frame2)
       cv2.imwrite('opticalhsv.png',bgr)
   prvs = next

cap.release()
cv2.destroyAllWindows()

Result

กดปุ่ม S เพื่อบันทึกภาพที่ตรวจหาความหนาแน่นและภาพปกติ

กด ESC เพื่อออก
Selection 091.png