光流是进行视频中运动对象轨迹标记的一种很常用的方法,在OpenCV中实现光流也很容易。
CalcOpticalFlowPyrLK
函数计算一个稀疏特征集的光流,使用金字塔中的迭代 Lucas-Kanade 方法。
简单的实现流程:
加载一段视频。
调用GoodFeaturesToTrack
函数寻找兴趣点。
调用CalcOpticalFlowPyrLK
函数计算出两帧图像中兴趣点的移动情况。
删除未移动的兴趣点。
在两次移动的点之间绘制一条线段。
代码示例:
import cv2.cv as cv
capture = cv.CaptureFromFile('img/myvideo.avi')
nbFrames = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_COUNT))
fps = cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FPS)
wait = int(1/fps * 1000/1)
width = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH))
height = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT))
prev_gray = cv.CreateImage((width,height), 8, 1) #Will hold the frame at t-1
gray = cv.CreateImage((width,height), 8, 1) # Will hold the current frame
prevPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1) #Will hold the pyr frame at t-1
currPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1) # idem at t
max_count = 500
qLevel= 0.01
minDist = 10
prev_points = [] #Points at t-1
curr_points = [] #Points at t
lines=[] #To keep all the lines overtime
for f in xrange( nbFrames ):
frame = cv.QueryFrame(capture) #Take a frame of the video
cv.CvtColor(frame, gray, cv.CV_BGR2GRAY) #Convert to gray
output = cv.CloneImage(frame)
prev_points = cv.GoodFeaturesToTrack(gray, None, None, max_count, qLevel, minDist) #Find points on the image
#Calculate the movement using the previous and the current frame using the previous points
curr_points, status, err = cv.CalcOpticalFlowPyrLK(prev_gray, gray, prevPyr, currPyr, prev_points, (10, 10), 3, (cv.CV_TERMCRIT_ITER|cv.CV_TERMCRIT_EPS,20, 0.03), 0)
#If points status are ok and distance not negligible keep the point
k = 0
for i in range(len(curr_points)):
nb = abs( int(prev_points[i][0])-int(curr_points[i][0]) ) + abs( int(prev_points[i][1])-int(curr_points[i][1]) )
if status[i] and nb > 2 :
prev_points[k] = prev_points[i]
curr_points[k] = curr_points[i]
k += 1
prev_points = prev_points[:k]
curr_points = curr_points[:k]
#At the end only interesting points are kept
#Draw all the previously kept lines otherwise they would be lost the next frame
for (pt1, pt2) in lines:
cv.Line(frame, pt1, pt2, (255,255,255))
#Draw the lines between each points at t-1 and t
for prevpoint, point in zip(prev_points,curr_points):
prevpoint = (int(prevpoint[0]),int(prevpoint[1]))
cv.Circle(frame, prevpoint, 15, 0)
point = (int(point[0]),int(point[1]))
cv.Circle(frame, point, 3, 255)
cv.Line(frame, prevpoint, point, (255,255,255))
lines.append((prevpoint,point)) #Append current lines to the lines list
cv.Copy(gray, prev_gray) #Put the current frame prev_gray
prev_points = curr_points
cv.ShowImage("The Video", frame)
#cv.WriteFrame(writer, frame)
cv.WaitKey(wait)
直接调用摄像头使用该方法:
import cv2.cv as cv
capture = cv.CaptureFromCAM(0)
width = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH))
height = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT))
prev_gray = cv.CreateImage((width,height), 8, 1)
gray = cv.CreateImage((width,height), 8, 1)
prevPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1) #Will hold the pyr frame at t-1
currPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1) # idem at t
max_count = 500
qLevel= 0.01
minDist = 10
prev_points = [] #Points at t-1
curr_points = [] #Points at t
lines=[] #To keep all the lines overtime
while True:
frame = cv.QueryFrame(capture)
cv.CvtColor(frame, gray, cv.CV_BGR2GRAY) #Convert to gray
output = cv.CloneImage(frame)
prev_points = cv.GoodFeaturesToTrack(gray, None, None, max_count, qLevel, minDist)
curr_points, status, err = cv.CalcOpticalFlowPyrLK(prev_gray, gray, prevPyr, currPyr, prev_points, (10, 10), 3, (cv.CV_TERMCRIT_ITER|cv.CV_TERMCRIT_EPS,20, 0.03), 0)
#If points status are ok and distance not negligible keep the point
k = 0
for i in range(len(curr_points)):
nb = abs( int(prev_points[i][0])-int(curr_points[i][0]) ) + abs( int(prev_points[i][1])-int(curr_points[i][1]) )
if status[i] and nb > 2 :
prev_points[k] = prev_points[i]
curr_points[k] = curr_points[i]
k += 1
prev_points = prev_points[:k]
curr_points = curr_points[:k]
#At the end only interesting points are kept
#Draw all the previously kept lines otherwise they would be lost the next frame
for (pt1, pt2) in lines:
cv.Line(frame, pt1, pt2, (255,255,255))
#Draw the lines between each points at t-1 and t
for prevpoint, point in zip(prev_points,curr_points):
prevpoint = (int(prevpoint[0]),int(prevpoint[1]))
cv.Circle(frame, prevpoint, 15, 0)
point = (int(point[0]),int(point[1]))
cv.Circle(frame, point, 3, 255)
cv.Line(frame, prevpoint, point, (255,255,255))
lines.append((prevpoint,point)) #Append current lines to the lines list
cv.Copy(gray, prev_gray) #Put the current frame prev_gray
prev_points = curr_points
cv.ShowImage("The Video", frame)
#cv.WriteFrame(writer, frame)
c = cv.WaitKey(1)
if c == 27: #Esc on Windows
break
cv.GoodFeaturesToTrack 函数可以检测出图像中最大特征值的角点,使用这个函数可以对图像中的特征点进行跟踪,从而绘制出运动轨迹。
直接加载视频:
import cv2.cv as cv
capture = cv.CaptureFromFile('img/myvideo.avi')
#-- Informations about the video --
nbFrames = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_COUNT))
fps = cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FPS)
wait = int(1/fps * 1000/1)
width = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH))
height = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT))
#For recording
#codec = cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FOURCC)
#writer=cv.CreateVideoWriter("img/output.avi", int(codec), int(fps), (width,height), 1) #Create writer with same parameters
#----------------------------------
prev_gray = cv.CreateImage((width,height), 8, 1) #Will hold the frame at t-1
gray = cv.CreateImage((width,height), 8, 1) # Will hold the current frame
output = cv.CreateImage((width,height), 8, 3)
prevPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1)
currPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1)
max_count = 500
qLevel= 0.01
minDist = 10
begin = True
initial = []
features = []
prev_points = []
curr_points = []
for f in xrange( nbFrames ):
frame = cv.QueryFrame(capture)
cv.CvtColor(frame, gray, cv.CV_BGR2GRAY) #Convert to gray
cv.Copy(frame, output)
if (len(prev_points) <= 10): #Try to get more points
#Detect points on the image
features = cv.GoodFeaturesToTrack(gray, None, None, max_count, qLevel, minDist)
prev_points.extend(features) #Add the new points to list
initial.extend(features) #Idem
if begin:
cv.Copy(gray, prev_gray) #Now we have two frames to compare
begin = False
#Compute movement
curr_points, status, err = cv.CalcOpticalFlowPyrLK(prev_gray, gray, prevPyr, currPyr, prev_points, (10, 10), 3, (cv.CV_TERMCRIT_ITER|cv.CV_TERMCRIT_EPS,20, 0.03), 0)
#If points status are ok and distance not negligible keep the point
k = 0
for i in range(len(curr_points)):
nb = abs( int(prev_points[i][0])-int(curr_points[i][0]) ) + abs( int(prev_points[i][1])-int(curr_points[i][1]) )
if status[i] and nb > 2 :
initial[k] = initial[i]
curr_points[k] = curr_points[i]
k += 1
curr_points = curr_points[:k]
initial = initial[:k]
#At the end only interesting points are kept
#Draw the line between the first position of a point and the
#last recorded position of the same point
for i in range(len(curr_points)):
cv.Line(output, (int(initial[i][0]),int(initial[i][1])), (int(curr_points[i][0]),int(curr_points[i][1])), (255,255,255))
cv.Circle(output, (int(curr_points[i][0]),int(curr_points[i][1])), 3, (255,255,255))
cv.Copy(gray, prev_gray)
prev_points = curr_points
cv.ShowImage("The Video", output)
cv.WriteFrame(writer, output)
cv.WaitKey(wait)
调用摄像头绘制:
import cv2.cv as cv
capture = cv.CaptureFromCAM(0)
width = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH))
height = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT))
prev_gray = cv.CreateImage((width,height), 8, 1) #Will hold the frame at t-1
gray = cv.CreateImage((width,height), 8, 1) # Will hold the current frame
output = cv.CreateImage((width,height), 8, 3)
prevPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1)
currPyr = cv.CreateImage((height / 3, width + 8), 8, cv.CV_8UC1)
max_count = 500
qLevel= 0.01
minDist = 10
begin = True
initial = []
features = []
prev_points = []
curr_points = []
while True:
frame = cv.QueryFrame(capture)
cv.CvtColor(frame, gray, cv.CV_BGR2GRAY) #Convert to gray
cv.Copy(frame, output)
if (len(prev_points) <= 10): #Try to get more points
#Detect points on the image
features = cv.GoodFeaturesToTrack(gray, None, None, max_count, qLevel, minDist)
prev_points.extend(features) #Add the new points to list
initial.extend(features) #Idem
if begin:
cv.Copy(gray, prev_gray) #Now we have two frames to compare
begin = False
#Compute movement
curr_points, status, err = cv.CalcOpticalFlowPyrLK(prev_gray, gray, prevPyr, currPyr, prev_points, (10, 10), 3, (cv.CV_TERMCRIT_ITER|cv.CV_TERMCRIT_EPS,20, 0.03), 0)
#If points status are ok and distance not negligible keep the point
k = 0
for i in range(len(curr_points)):
nb = abs( int(prev_points[i][0])-int(curr_points[i][0]) ) + abs( int(prev_points[i][1])-int(curr_points[i][1]) )
if status[i] and nb > 2 :
initial[k] = initial[i]
curr_points[k] = curr_points[i]
k += 1
curr_points = curr_points[:k]
initial = initial[:k]
for i in range(len(curr_points)):
cv.Line(output, (int(initial[i][0]),int(initial[i][1])), (int(curr_points[i][0]),int(curr_points[i][1])), (255,255,255))
cv.Circle(output, (int(curr_points[i][0]),int(curr_points[i][1])), 3, (255,255,255))
cv.Copy(gray, prev_gray)
prev_points = curr_points
cv.ShowImage("The Video", output)
c = cv.WaitKey(1)
if c == 27: #Esc on Windows
break