Skip to content

Instantly share code, notes, and snippets.

@rfong
Last active February 3, 2017 19:27
Show Gist options
  • Save rfong/1627a348dd8e8655f0647ccb4c7adae2 to your computer and use it in GitHub Desktop.
Save rfong/1627a348dd8e8655f0647ccb4c7adae2 to your computer and use it in GitHub Desktop.
Turn yourself into a slinky by time-delaying successive chunks of a webcam capture with OpenCV! https://vimeo.com/183155228
import copy
import datetime
import numpy as np
from optparse import OptionParser
import time
import cv2
from matplotlib import pyplot as plt
SHIFT_HEIGHT = 5
EPOCH = datetime.datetime.utcfromtimestamp(0)
def unix_time(dt):
return (dt - EPOCH).total_seconds() * 1.0
def get_unix_now():
return unix_time(datetime.datetime.now())
def get_frame(cam):
ret, frame = cam.read()
if not ret:
return
frame = cv2.resize(frame, (0,0), fx=0.3, fy=0.3)
return frame
def np_rotate_array(arr, shift):
"""Shift a numpy array to the right so that it wraps around."""
return np.concatenate([arr[shift:], arr[:shift]])
def fingerprint_frame(frame):
""" For quick dirty debugging """
return sum(sum(sum(frame)))
def main():
parser = OptionParser()
parser.add_option('-f', dest='input_name', default=0,
help='input video file (leave blank to use webcam)')
parser.add_option('-o', dest='output_name',
help='output video file')
parser.add_option('--time', action='store_true', default=False,
help='time it')
(options, args) = parser.parse_args()
cam = cv2.VideoCapture(options.input_name)
frame = get_frame(cam)
height = len(frame)
width = len(frame[0])
print 'frame size: %d x %d px' % (width, height)
# Retain a rolling buffer of time-delayed versions of the image,
# back to `height` frames ago
# TODO later: performance optimize by only keeping the triangle of delay rows
# we need (since the top row is delayed by 0 frames, we don't need any)
delay_buffer = []
# TODO later: worry about it not dividing perfectly into height
TIME_TRAVEL_LIMIT = height / SHIFT_HEIGHT
# Define the codec and create VideoWriter object
out = None
if options.output_name:
fourcc = cv2.cv.CV_FOURCC(*'mp4v')
out = cv2.VideoWriter(options.output_name, fourcc, 20.0, (width, height))
if options.time:
frame_execution_times = []
frame_mod = 0
# Begin main video processing loop
while cam.isOpened():
start = get_unix_now()
# Capture frame-by-frame
frame = get_frame(cam)
if frame is None:
break
image = copy.deepcopy(frame)
# Roll delay buffer forward
delay_buffer.insert(0, copy.deepcopy(frame))
delay_buffer = delay_buffer[:min(TIME_TRAVEL_LIMIT, len(delay_buffer))]
# Weird swirly time-delayed image
# `i`th frame ago for SHIFT_HEIGHT * i : SHIFT_HEIGHT * (i+1) rows
image = frame
for delay in xrange(TIME_TRAVEL_LIMIT):
if delay < len(delay_buffer):
for j in xrange(SHIFT_HEIGHT):
row_index = delay * SHIFT_HEIGHT + j
image[row_index] = delay_buffer[delay][row_index]
# Wacky funhouse mirror (make image slanted)
#for i, row in enumerate(image):
# image[i] = np_rotate_array(row, i)
# Edge detection for fun
#image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#image = cv2.Canny(image, 60, 120)
# Display the resulting frame
if frame_mod % 5 == 0:
cv2.imshow('frame', image)
frame_mod += 1
# Save to output
if out and image is not None:
#image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
out.write(image)
if options.time:
frame_execution_times.append(get_unix_now() - start)
# Check for quit signal
if cv2.waitKey(1) & 0xFF == ord('q'):
break
previous_frame = frame
# When everything done, release the camture
cam.release()
if out:
out.release()
cv2.destroyAllWindows()
if options.time:
print 'Frame execution times (%d frames taken):' % len(frame_execution_times)
print 'avg:', sum(frame_execution_times)/len(frame_execution_times)
print 'stddev:', np.std(frame_execution_times)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment