Skip to content
This repository was archived by the owner on Aug 10, 2022. It is now read-only.

Commit 69edda1

Browse files
authored
Barebones GUI for lucasKanadeOpticalFlow.py
1 parent 3d83ea4 commit 69edda1

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed

lucasKanadeOpticalFlow.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
"""Adapted from the OpenCV optical flow documentation code: https://docs.opencv.org/4.5.3/d4/dee/tutorial_optical_flow.html"""
2+
3+
""" ABOUT-----------------------------------------------------------------------
4+
Lucas Kanade Optical Flow calculates the optical flow (motion) of specific features in a video clip.
5+
The Shi-Tomasi corner detection is used to pick points in the video that are easy for to track.
6+
The optical flow algorithm will track where those features move.
7+
The visualization will draw a point over the tracked features and a trail of where the feature has been.
8+
The program currently outputs the result video to the same location as your input video, with the name <input_vid_filename>_LK_FLOW.mp4
9+
10+
The idea is that perhaps the data about how certain pixels/features are moving across the screen could be used to figure out how the player camera / aim was changing.
11+
"""
12+
13+
from tkinter import *
14+
from tkinter import filedialog
15+
import cv2 as cv
16+
import numpy as np
17+
from tkinter.messagebox import showinfo
18+
19+
# GUI FILE BROWSER------------------------------------------------------------
20+
21+
window = Tk()
22+
window.geometry('300x150') # sets the size of the GUI window
23+
window.title('Select a Video File') # creates a title for the window
24+
25+
# function allowing you to find/select video in GUI
26+
def get_file_path():
27+
global file_path
28+
# Open and return file path
29+
file_path = filedialog.askopenfilename(title = "Select a Video File", filetypes = (("mp4", "*.mp4"), ("mov files", "*.mov") ,("wmv", "*.wmv"), ("avi", "*.avi")))
30+
showinfo(title='Selected File', message=file_path)
31+
32+
# function allowing you to select the output path in the GUI
33+
def output():
34+
global outpath
35+
outpath = filedialog.asksaveasfilename(filetypes=[("mp4", '*.mp4')])
36+
window.destroy()
37+
38+
# Creating a button to search for the input file and to select the output destinatio and file name
39+
b1 = Button(window, text = 'Open a File', command = get_file_path).pack()
40+
b2 = Button(window, text = 'Save File Name', command = output).pack()
41+
window.mainloop()
42+
43+
44+
# PARAMETERS------------------------------------------------------------------
45+
46+
# path to input videofile
47+
vidpath = file_path
48+
49+
# do you want to save the video?
50+
savevid = True
51+
52+
# do you want to preview the output?
53+
previewWindow = True
54+
55+
# output video params
56+
fps = 20 # fps of output video, should match input video
57+
58+
# visualization parameters
59+
numPts = 5 # max number of points to track
60+
trailLength = 60 # how many frames to keep a fading trail behind a tracked point to show motion
61+
trailThickness = 8 # thickness of the trail to draw behind the target
62+
trailFade = 4 # the intensity at which the trail fades
63+
pointSize = 15 # pixel radius of the circle to draw over tracked points
64+
65+
# params for Shi-Tomasi corner detection
66+
shitomasi_params = {
67+
"qualityLevel": 0.3,
68+
"minDistance": 7,
69+
"blockSize": 7
70+
}
71+
72+
# params for Lucas-Kanade optical flow
73+
LK_params = {
74+
"winSize": (15,15),
75+
"maxLevel": 2,
76+
"criteria": (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03)
77+
}
78+
79+
80+
# SETUP -----------------------------------------------------------------------
81+
82+
# generate random colors
83+
color = np.random.randint(0,255,(100,3))
84+
85+
# read the video file into memory
86+
cap = cv.VideoCapture(vidpath)
87+
88+
# get the first frame
89+
_, old_frame = cap.read()
90+
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
91+
92+
# get resolution of video
93+
res_x = len(old_frame[0])
94+
res_y = len(old_frame)
95+
96+
# create crosshair mask
97+
crosshair_bottom = int(0.7*res_y)
98+
crosshair_top = int(0.3*res_y)
99+
crosshair_left = int(0.3*res_x)
100+
crosshair_right = int(0.7*res_x)
101+
crosshairmask = np.zeros(old_frame.shape[:2], dtype="uint8")
102+
cv.rectangle(crosshairmask, (crosshair_left, crosshair_top), (crosshair_right, crosshair_bottom), 255, -1)
103+
104+
# create masks for drawing purposes
105+
trail_history = [[[(0,0), (0,0)] for i in range(trailLength)] for i in range(numPts)]
106+
107+
# get features from first frame
108+
print(f"\nRunning Optical Flow on: {vidpath}")
109+
old_points = cv.goodFeaturesToTrack(old_gray, maxCorners=numPts, mask=crosshairmask, **shitomasi_params)
110+
111+
# if saving video
112+
if savevid:
113+
# path to save output video
114+
filename = outpath
115+
savepath = filename + '_LK_FLOW' + '.mp4'
116+
print(f"Saving Output video to: {savepath}")
117+
118+
# get shape of video frames
119+
height, width, channels = old_frame.shape
120+
121+
# setup videowriter object
122+
fourcc = cv.VideoWriter_fourcc(*'mp4v')
123+
videoOut = cv.VideoWriter(savepath, fourcc, fps, (width, height))
124+
125+
# PROCESS VIDEO ---------------------------------------------------------------
126+
while(True):
127+
# get next frame and convert to grayscale
128+
stillGoing, new_frame = cap.read()
129+
130+
# if video is over, quit
131+
if not stillGoing:
132+
break
133+
134+
# convert to grayscale
135+
new_frame_gray = cv.cvtColor(new_frame, cv.COLOR_BGR2GRAY)
136+
137+
# calculate optical flow
138+
new_points, st, err = cv.calcOpticalFlowPyrLK(old_gray, new_frame_gray, old_points, None, **LK_params)
139+
140+
# select good points
141+
if old_points is not None:
142+
good_new = new_points[st==1]
143+
good_old = old_points[st==1]
144+
145+
# create trail mask to add to image
146+
trailMask = np.zeros_like(old_frame)
147+
148+
# calculate motion lines and points
149+
for i,(new,old) in enumerate(zip(good_new, good_old)):
150+
# flatten coords
151+
a,b = new.ravel()
152+
c,d = old.ravel()
153+
154+
# list of the prev and current points converted to int
155+
linepts = [(int(a),int(b)), (int(c),int(d))]
156+
157+
# add points to the trail history
158+
trail_history[i].insert(0, linepts)
159+
160+
# get color for this point
161+
pointColor = color[i].tolist()
162+
163+
# add trail lines
164+
for j in range(len(trail_history[i])):
165+
trailColor = [int( pointColor[0] - (trailFade*j) ), int( pointColor[1] - (trailFade*j) ), int( pointColor[2] - (trailFade*j) )] # fading colors
166+
trailMask = cv.line(trailMask, trail_history[i][j][0], trail_history[i][j][1], trailColor, thickness=trailThickness, lineType=cv.LINE_AA)
167+
168+
# get rid of the trail segment
169+
trail_history[i].pop()
170+
171+
# add circle over the point
172+
new_frame = cv.circle(new_frame, trail_history[i][0][0], pointSize, color[i].tolist(), -1)
173+
174+
# add trail to frame
175+
img = cv.add(new_frame, trailMask)
176+
177+
# show the frames
178+
if previewWindow:
179+
cv.imshow('optical flow', img)
180+
181+
# write frames to new output video
182+
if savevid:
183+
videoOut.write(img)
184+
185+
# kill window if ESC is pressed
186+
k = cv.waitKey(30) & 0xff
187+
if k == 27:
188+
break
189+
190+
# update previous frame and previous points
191+
old_gray = new_frame_gray.copy()
192+
old_points = good_new.reshape(-1,1,2)
193+
194+
# if old_points < numPts, get new points
195+
if (numPts - len(old_points)) > 0:
196+
old_points = cv.goodFeaturesToTrack(old_gray, maxCorners=numPts, mask=crosshairmask, **shitomasi_params)
197+
198+
# after video is finished
199+
print('\nComplete!\n')

0 commit comments

Comments
 (0)