""" :: Morph from source to destination face or Morph through all images in a folder Usage: morpher.py (--src= --dest= | --images=) [--width=] [--height=] [--num=] [--fps=] [--out_frames=] [--out_video=] [--plot] [--background=(black|transparent|average)] Options: -h, --help Show this screen. --src= Filepath to source image (.jpg, .jpeg, .png) --dest= Filepath to destination image (.jpg, .jpeg, .png) --images= Folderpath to images --width= Custom width of the images/video [default: 500] --height= Custom height of the images/video [default: 600] --num= Number of morph frames [default: 20] --fps= Number frames per second for the video [default: 10] --out_frames= Folder path to save all image frames --out_video= Filename to save a video --plot Flag to plot images to result.png [default: False] --background= Background of images to be one of (black|transparent|average) [default: black] --version Show version. """ from docopt import docopt import os import numpy as np import cv2 from facemorpher import locator from facemorpher import aligner from facemorpher import warper from facemorpher import blender from facemorpher import plotter from facemorpher import videoer def verify_args(args): if args['--images'] is None: valid = os.path.isfile(args['--src']) & os.path.isfile(args['--dest']) if not valid: print('--src=%s or --dest=%s file does not exist. Double check the supplied paths' % ( args['--src'], args['--dest'])) exit(1) else: valid = os.path.isdir(args['--images']) if not valid: print('--images=%s is not a valid directory' % args['--images']) exit(1) def load_image_points(path, size): img = cv2.imread(path) points = locator.face_points(img) if len(points) == 0: print('No face in %s' % path) return None, None else: return aligner.resize_align(img, points, size) def load_valid_image_points(imgpaths, size): for path in imgpaths: img, points = load_image_points(path, size) if img is not None: print(path) yield (img, points) def list_imgpaths(images_folder=None, src_image=None, dest_image=None): if images_folder is None: yield src_image yield dest_image else: for fname in os.listdir(images_folder): if (fname.lower().endswith('.jpg') or fname.lower().endswith('.png') or fname.lower().endswith('.jpeg')): yield os.path.join(images_folder, fname) def morph(src_img, src_points, dest_img, dest_points, video, width=500, height=600, num_frames=20, fps=10, out_frames=None, out_video=None, plot=False, background='black'): """ Create a morph sequence from source to destination image :param src_img: ndarray source image :param src_points: source image array of x,y face points :param dest_img: ndarray destination image :param dest_points: destination image array of x,y face points :param video: facemorpher.videoer.Video object """ size = (height, width) stall_frames = np.clip(int(fps*0.15), 1, fps) # Show first & last longer plt = plotter.Plotter(plot, num_images=num_frames, out_folder=out_frames) num_frames -= (stall_frames * 2) # No need to process src and dest image plt.plot_one(src_img) video.write(src_img, 1) # Produce morph frames! for percent in np.linspace(1, 0, num=num_frames): points = locator.weighted_average_points(src_points, dest_points, percent) src_face = warper.warp_image(src_img, src_points, points, size) end_face = warper.warp_image(dest_img, dest_points, points, size) average_face = blender.weighted_average(src_face, end_face, percent) if background in ('transparent', 'average'): mask = blender.mask_from_points(average_face.shape[:2], points) average_face = np.dstack((average_face, mask)) if background == 'average': average_background = blender.weighted_average(src_img, dest_img, percent) average_face = blender.overlay_image(average_face, mask, average_background) plt.plot_one(average_face) plt.save(average_face) video.write(average_face) plt.plot_one(dest_img) video.write(dest_img, stall_frames) plt.show() def morpher(imgpaths, width=500, height=600, num_frames=20, fps=10, out_frames=None, out_video=None, plot=False, background='black'): """ Create a morph sequence from multiple images in imgpaths :param imgpaths: array or generator of image paths """ video = videoer.Video(out_video, fps, width, height) images_points_gen = load_valid_image_points(imgpaths, (height, width)) src_img, src_points = next(images_points_gen) for dest_img, dest_points in images_points_gen: morph(src_img, src_points, dest_img, dest_points, video, width, height, num_frames, fps, out_frames, out_video, plot, background) src_img, src_points = dest_img, dest_points video.end() def main(): args = docopt(__doc__, version='Face Morpher 1.0') verify_args(args) morpher(list_imgpaths(args['--images'], args['--src'], args['--dest']), int(args['--width']), int(args['--height']), int(args['--num']), int(args['--fps']), args['--out_frames'], args['--out_video'], args['--plot'], args['--background']) if __name__ == "__main__": main()