Skip to content

Instantly share code, notes, and snippets.

@DoggettCK
Created September 6, 2019 16:12
Show Gist options
  • Save DoggettCK/8ba5bcee8c4d97aa67f09465c8f3307f to your computer and use it in GitHub Desktop.
Save DoggettCK/8ba5bcee8c4d97aa67f09465c8f3307f to your computer and use it in GitHub Desktop.
OpenCV Python script to put the Matrix falling letters behind Taylor Swift for trueheart78
#!/usr/bin/env python
import os
import sys
import numpy as np
import cv2
FRAME_DELAY = int(sys.argv[1]) if len(sys.argv) > 1 else 30
foreground = cv2.VideoCapture('glowy_eyes.gif')
background = cv2.VideoCapture('matrix.gif')
fg_bg = cv2.createBackgroundSubtractorKNN()
def edge_detect(channel):
sobel_x = cv2.Sobel(channel, cv2.CV_16S, 1, 0)
sobel_y = cv2.Sobel(channel, cv2.CV_16S, 0, 1)
sobel = np.hypot(sobel_x, sobel_y)
sobel[sobel > 255] = 255;
return sobel
def find_significant_contours(frame, edge_image):
image, contours, hierarchy = cv2.findContours(edge_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Find level 1 contours
level_1 = []
for i, tupl in enumerate(hierarchy[0]):
# Each array is in format (next, prev, first child, parent)
# Filter ones without a parent
if tupl[3] == -1:
tupl = np.insert(tupl, 0, [i])
level_1.append(tupl)
# From among them, find the contours with large surface area
significant = []
too_small = edge_image.size * 0.025
for tupl in level_1:
contour = contours[tupl[0]]
area = cv2.contourArea(contour)
if area > too_small:
significant.append([contour, area])
# cv2.drawContours(frame, [contour], 0, (0, 255, 0), 1, cv2.LINE_AA, maxLevel=1)
significant.sort(key=lambda x: x[1])
return [x[0] for x in significant]
def process_frame(fg_frame, bg_frame):
blurred = cv2.GaussianBlur(fg_frame, (11, 11), 0)
x, y = fg_frame.shape[:2]
# bg_frame = cv2.resize(bg_frame, (y, x))
bg_frame = bg_frame[0:x, 0:y]
edge_image = np.max(np.array([
edge_detect(blurred[:, :, 0]),
edge_detect(blurred[:, :, 1]),
edge_detect(blurred[:, :, 2])
]), axis=0)
mean = 0.05 * np.mean(edge_image)
# Zero any value less than mean to reduce noise
edge_image[edge_image <= mean] = 0
edge_image_8u = np.asarray(edge_image, np.uint8)
# Find contours
significant = find_significant_contours(fg_frame, edge_image_8u)
mask = edge_image.copy()
mask[mask > 0] = 0
cv2.fillPoly(mask, significant, 255)
# invert mask
inv_mask = np.logical_not(mask)
# remove the background
fg_frame[inv_mask] = bg_frame[inv_mask]
return fg_frame
frames = []
with open('image_list.txt', 'w') as file_list:
while(True):
fg_has_frame, fg_frame = foreground.read()
bg_has_frame, bg_frame = background.read()
if not all([fg_has_frame, bg_has_frame]):
break
processed = process_frame(fg_frame, bg_frame)
cv2.imshow('frame', processed)
filename = 'frame_{}.png'.format(len(frames))
cv2.imwrite(filename, processed)
file_list.write('{}\n'.format(filename))
frames.append(processed.copy())
k = cv2.waitKey(FRAME_DELAY) & 0xff
if k == 27:
break
os.system('convert @image_list.txt glowy_output.gif')
foreground.release()
background.release()
cv2.destroyAllWindows()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment