Skip to content

Instantly share code, notes, and snippets.

@fepegar
Created April 18, 2020 14:06
Show Gist options
  • Save fepegar/572ba08ae43ee2ae60fc0d1fcaf574e0 to your computer and use it in GitHub Desktop.
Save fepegar/572ba08ae43ee2ae60fc0d1fcaf574e0 to your computer and use it in GitHub Desktop.
import tempfile
from pathlib import Path
from subprocess import call
# pip install moviepy opencv-python scikit-image face_recognition
import cv2
import numpy as np
from PIL import Image
from tqdm import tqdm
import face_recognition
from moviepy.editor import VideoFileClip, ImageSequenceClip
from skimage.morphology import convex_hull_image
def get_face_landmarks(image):
face_landmarks_list = face_recognition.face_landmarks(image)
if not face_landmarks_list:
return
face_landmarks_dict = face_landmarks_list[0]
return get_landmarks_from_dict(image, face_landmarks_dict)
def get_landmarks_from_dict(image, face_landmarks_dict, clip=True):
face_landmarks = np.vstack(list(face_landmarks_dict.values()))
si, sj, _ = image.shape
np.clip(face_landmarks, (0, 0), (sj - 1, si - 1), out=face_landmarks)
landmarks_j, landmarks_i = face_landmarks.T
return landmarks_i, landmarks_j
def move_face(image, translation=(0, 0), show_landmarks=False):
# Get face landmarks
face_landmarks = get_face_landmarks(image)
if face_landmarks is None:
return None
# Make image with landmarks only
si, sj, _ = image.shape
mask = np.zeros((si, sj), dtype=np.uint8)
mask[face_landmarks] = 1
# Compute convex hull
chull = convex_hull_image(mask)
face_indices = np.where(chull)
# Extract face pixels
values = image[face_indices]
# Remove face
image[chull] = 0
# Show landmarks
if show_landmarks:
image[face_landmarks] = 255
# Move face
ti, tj = translation
face_indices = np.array(face_indices)
face_indices[0] += int(ti)
face_indices[1] += int(tj)
face_indices = np.clip(face_indices.T, (0, 0), (si - 1, sj - 1)).T
image[tuple(face_indices)] = values
def drop_face(input_path, start_time, pixels_per_second, output_path):
"""Make face fall.
Args:
input_path:
start_time: Starting time in seconds.
pixels_per_second: Falling velocity.
output_path:
"""
input_path = Path(input_path)
start_time = float(start_time)
pixels_per_second = float(pixels_per_second)
output_path = Path(output_path)
clip = VideoFileClip(str(input_path))
pixels_per_frame = pixels_per_second / clip.fps
num_frames = int(clip.fps * clip.duration)
offset = 0
with tempfile.NamedTemporaryFile(suffix='.mp4') as f:
with tempfile.TemporaryDirectory() as tmpdirname:
paths = []
for index, frame in tqdm(enumerate(clip.iter_frames()), total=num_frames):
frame = frame.copy()
time = 1 / clip.fps * index
if time > start_time:
offset += pixels_per_frame
move_face(frame, translation=(offset, 0))
output_frame_name = f'{input_path.stem}_{index:08d}.jpg'
output_frame_path = Path(tmpdirname, output_frame_name)
Image.fromarray(frame).save(output_frame_path)
paths.append(str(output_frame_path))
new_clip = ImageSequenceClip(paths, fps=clip.fps)
new_clip.write_videofile(f.name)
command = [
'ffmpeg',
'-i', f.name,
'-i', input_path,
'-c', 'copy',
'-map', '0:v:0',
'-map', '1:a:0',
'-shortest',
output_path,
'-y',
]
command = [str(arg) for arg in command]
call(command)
def capture():
# pylint: disable=no-member
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
_, frame = cap.read()
# Our operations on the frame come here
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
move_face(rgb, (65, -120))
bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
bgr = bgr[:, ::-1]
# Display the resulting frame
cv2.imshow('frame', bgr)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
import sys
print(sys.argv[1:])
input_path, start_time, pixels_per_second, output_path = sys.argv[1:]
drop_face(input_path, start_time, pixels_per_second, output_path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment