Skip to content

Commit

Permalink
initial SOF w/o grid
Browse files Browse the repository at this point in the history
  • Loading branch information
Edit-3D committed May 20, 2024
1 parent 8547073 commit 889c257
Showing 1 changed file with 136 additions and 0 deletions.
136 changes: 136 additions & 0 deletions SOF.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import cv2 as cv
import numpy as np

def draw_trapezoid(frame, trapezoid):
"""Draw the trapezoid on the frame."""
cv.polylines(frame, [np.array(trapezoid)], isClosed=True, color=(150, 10, 150), thickness=1)

def is_motion_inwards(point, trapezoid):
"""Check if the motion is coming inwards towards the trapezoid from the left or right."""
pt = np.array(point, dtype=np.float32)
# Check if the point is outside the trapezoid
if cv.pointPolygonTest(np.array(trapezoid, dtype=np.float32), pt, False) < 0:
# Get the x and y coordinates of the motion point
x, y = point
# Get the x-coordinates of the left and right boundaries of the trapezoid
left_x, right_x = trapezoid[0][0], trapezoid[1][0]
# Check if the point is between the left and right boundaries of the trapezoid
if left_x < x < right_x:
# Get the y-coordinates of the top and bottom boundaries of the trapezoid at the given x-coordinate
top_y = np.interp(x, [trapezoid[0][0], trapezoid[1][0]], [trapezoid[0][1], trapezoid[1][1]])
bottom_y = np.interp(x, [trapezoid[3][0], trapezoid[2][0]], [trapezoid[3][1], trapezoid[2][1]])
# Check if the point is not above or below the trapezoid at the given x-coordinate
if bottom_y < y < top_y:
return True
return False




paths = [
'../comma2k/Chunk_1/b0c9d2329ad1606b|2018-08-17--14-55-39/7/video.hevc', # Jen's path
'../comma2k/Chunk_2/b0c9d2329ad1606b|2018-10-09--14-06-32/10/video.hevc', #doesn't work well
'../comma2k/Chunk_2/b0c9d2329ad1606b|2018-09-23--12-52-06/45/video.hevc', # detects all cut-ins, no false positives
'../comma2k/Chunk_2/b0c9d2329ad1606b|2018-10-09--15-48-37/16/video.hevc' # works well
]

my_path = paths[2]
maxCorners = 20
qualityLevel = 0.1
minDistance = 100
blockSize = 2
winsize = (15, 15)
maxLevel = 2
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 0.03)

# Parameters for Shi-Tomasi corner detection minDistance defines how far side to side is required to identify the object.
feature_params = dict(maxCorners=maxCorners, qualityLevel=qualityLevel, minDistance=minDistance, blockSize=blockSize)
# Parameters for Lucas-Kanade optical flow
lk_params = dict(winSize=winsize, maxLevel=maxLevel, criteria=criteria)
# The video feed is read in as a VideoCapture object
cap = cv.VideoCapture(my_path)

# Define the trapezoid's corners
top_left = (550, 400)
top_right = (650, 400)
bottom_right = (850, 600)
bottom_left = (350, 600)
trapezoid = [top_left, top_right, bottom_right, bottom_left]

# Variable for color to draw optical flow track
color = (0, 255, 0)

# ret = a boolean return value from getting the frame, first_frame = the first frame in the entire video sequence
ret, first_frame = cap.read()
# Converts frame to grayscale because we only need the luminance channel for detecting edges - less computationally expensive
prev_gray = cv.cvtColor(first_frame, cv.COLOR_BGR2GRAY)
# Finds the strongest corners in the first frame by Shi-Tomasi method - we will track the optical flow for these corners
prev = cv.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
# Creates an image filled with zero intensities with the same dimensions as the frame - for later drawing purposes
mask = np.zeros_like(first_frame)

# Create a separate output window
cv.namedWindow("Output", cv.WINDOW_NORMAL)
cv.resizeWindow("Output", 1000, 800)

# Main loop
while(cap.isOpened()):
# Clear the mask for drawing lines
mask[:] = 0

# ret = a boolean return value from getting the frame, frame = the current frame being projected in the video
ret, frame = cap.read()
if not ret:
break

# Converts each frame to grayscale - we previously only converted the first frame to grayscale
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# Calculates sparse optical flow by Lucas-Kanade method
prev = cv.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
next, status, error = cv.calcOpticalFlowPyrLK(prev_gray, gray, prev, None, **lk_params)
# Selects good feature points for previous position
good_old = prev[status == 1].astype(int)
# Selects good feature points for next position
good_new = next[status == 1].astype(int)

# Draw the optical flow tracks
for i, (new, old) in enumerate(zip(good_new, good_old)):
# Returns a contiguous flattened array as (x, y) coordinates for new point
a, b = new.ravel()
# Returns a contiguous flattened array as (x, y) coordinates for old point
c, d = old.ravel()

# If the motion is
# If the motion is coming inwards towards the trapezoid
if is_motion_inwards((a, b), trapezoid):
# Draw the line
mask = cv.line(mask, (a, b), (c, d), color, 2)
# Draw the circle
frame = cv.circle(frame, (a, b), 5, color, 3)

# Additional checks to filter out noisy points
if (0 <= a < frame.shape[1]) and (0 <= b < frame.shape[0]):
# Mark the detected cut-in position with a red box
cv.rectangle(frame, (a - 20, b - 20),(a + 20, b + 20), (0, 0, 255), 2)
print("Cut-in at position:", (a, b))
# Pause video and wait for user input
cv.waitKey(0)

# Overlays the optical flow tracks on the original frame
output = cv.add(frame, mask)
# Updates previous frame
prev_gray = gray.copy()

# Draw the trapezoid on the frame
draw_trapezoid(output, trapezoid)

# Opens a new window and displays the output frame
cv.imshow("Output", output)
# Frames are read by intervals of 10 milliseconds. The programs breaks out of the while loop when the user presses the 'q' key
key = cv.waitKey(1)
if key & 0xFF == ord('q'):
break

# Release the VideoCapture object and close all windows
cap.release()
cv.destroyAllWindows()

0 comments on commit 889c257

Please sign in to comment.