def smoothBorder(self, img, points = None): if points is None: points = self.facePointsStasm(img) mask = blender.mask_from_points(img.shape[:2], points) inner_points = self.expandConvex(points, ratio=0.9) inner_mask = blender.mask_from_points(img.shape[:2], inner_points) mask = mask-inner_mask return cv2.textureFlattening(img, mask, low_threshold=30 , high_threshold=45)
def test_local(): from functools import partial import scipy.ndimage import scipy.misc from facemorpher import locator from facemorpher import aligner # Load source image face_points_func = partial(locator.face_points, '../data') base_path = '../females/Screenshot 2015-03-04 17.11.12.png' src_path = '../females/BlDmB5QCYAAY8iw.jpg' src_img = scipy.ndimage.imread(src_path)[:, :, :3] # Define control points for warps src_points = face_points_func(src_path) base_img = scipy.ndimage.imread(base_path)[:, :, :3] base_points = face_points_func(base_path) size = (600, 500) src_img, src_points = aligner.resize_align(src_img, src_points, size) base_img, base_points = aligner.resize_align(base_img, base_points, size) result_points = locator.weighted_average_points(src_points, base_points, 0.2) # Perform transform dst_img1 = warp_image(src_img, src_points, result_points, size) dst_img2 = warp_image(base_img, base_points, result_points, size) from facemorpher import blender ave = blender.weighted_average(dst_img1, dst_img2, 0.6) mask = blender.mask_from_points(size, result_points) blended_img = blender.poisson_blend(dst_img1, dst_img2, mask)
def averager(imgpaths, dest_filename=None, width=500, height=600, background='black', blur_edges=False, out_filename='result.png', plot=False): size = (height, width) images = [] point_set = [] for path in imgpaths: img, points = load_image_points(path, size) if img is not None: images.append(img) point_set.append(points) if len(images) == 0: raise FileNotFoundError('Could not find any valid images.' + ' Supported formats are .jpg, .png, .jpeg') if dest_filename is not None: dest_img, dest_points = load_image_points(dest_filename, size) if dest_img is None or dest_points is None: raise Exception('No face or detected face points in dest img: ' + dest_filename) else: dest_img = np.zeros(images[0].shape, np.uint8) dest_points = locator.average_points(point_set) num_images = len(images) result_images = np.zeros(images[0].shape, np.float32) for i in range(num_images): result_images += warper.warp_image(images[i], point_set[i], dest_points, size, np.float32) result_image = np.uint8(result_images / num_images) face_indexes = np.nonzero(result_image) dest_img[face_indexes] = result_image[face_indexes] mask = blender.mask_from_points(size, dest_points) if blur_edges: blur_radius = 10 mask = cv2.blur(mask, (blur_radius, blur_radius)) if background in ('transparent', 'average'): dest_img = np.dstack((dest_img, mask)) if background == 'average': average_background = locator.average_points(images) dest_img = blender.overlay_image(dest_img, mask, average_background) print('Averaged {} images'.format(num_images)) plt = plotter.Plotter(plot, num_images=1, out_filename=out_filename) plt.save(dest_img) plt.plot_one(dest_img) plt.show()
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 averager(imgpaths, dest_filename=None, width=500, height=600, alpha=False, blur_edges=False, out_filename='result.png', plot=False): size = (height, width) images = [] point_set = [] for path in imgpaths: img, points = load_image_points(path, size) if img is not None: images.append(img) point_set.append(points) if len(images) == 0: raise FileNotFoundError('Could not find any valid images.' + ' Supported formats are .jpg, .png, .jpeg') if dest_filename is not None: dest_img, dest_points = load_image_points(dest_filename, size) if dest_img is None or dest_points is None: raise Exception('No face or detected face points in dest img: ' + dest_filename) else: dest_img = np.zeros(images[0].shape, np.uint8) dest_points = locator.average_points(point_set) num_images = len(images) result_images = np.zeros(images[0].shape, np.float32) for i in range(num_images): result_images += warper.warp_image(images[i], point_set[i], dest_points, size, np.float32) result_image = np.uint8(result_images / num_images) face_indexes = np.nonzero(result_image) dest_img[face_indexes] = result_image[face_indexes] mask = blender.mask_from_points(size, dest_points) if blur_edges: blur_radius = 10 mask = cv2.blur(mask, (blur_radius, blur_radius)) if alpha: dest_img = np.dstack((dest_img, mask)) mpimg.imsave(out_filename, dest_img) if plot: plt.axis('off') plt.imshow(dest_img) plt.show()
def average_face(imgpaths, width=500, height=500, background='black', blur_edges=False, out_filename='result.jpg'): size = (height, width) images = [] point_set = [] for path in imgpaths: img, points = load_image_points(path, size) if img is not None: images.append(img) point_set.append(points) if len(images) == 0: raise FileNotFoundError( 'Could not find any valid images. Supported formats are .jpg, .png, .jpeg' ) dest_img, dest_points = load_image_points(REFERENCE_IMG_PATH, size) num_images = len(images) result_images = np.zeros(images[0].shape, np.float32) for i in range(num_images): result_images += warper.warp_image(images[i], point_set[i], dest_points, size, np.float32) result_image = np.uint8(result_images / num_images) face_indexes = np.nonzero(result_image) dest_img[face_indexes] = result_image[face_indexes] mask = blender.mask_from_points(size, dest_points) if blur_edges: blur_radius = 10 mask = cv2.blur(mask, (blur_radius, blur_radius)) if background in ('transparent', 'average'): dest_img = np.dstack((dest_img, mask)) if background == 'average': # average_background = np.uint8(locator.average_points(images)) avg_background = perlin_background(images) avg_background[np.where( (avg_background == [0, 0, 0]).all(axis=2))] = [ 128, 128, 128 ] # black -> gray pixels in background dest_img = blender.overlay_image(dest_img, mask, avg_background) print('Averaged {} images'.format(num_images)) plt = plotter.Plotter(False, num_images=1, out_filename=out_filename) plt.save(dest_img)
def morph_ani( src_imgpaths, des_imgpath, num_pics, width=500, height=600, fps_in=24, slow_rate=5, out_frames=None, out_video=None, plot=False, ): num_frames = num_pics * slow_rate fps = fps_in * slow_rate video = videoer.Video(out_video, fps, width, height) plt = plotter.Plotter(plot, num_images=num_frames, out_folder=out_frames) dest_img, dest_points = load_image_points(des_imgpath, (height, width)) images_points_gen = load_valid_image_points(src_imgpaths, (height, width)) frameCount = 0 for src_img, src_points in images_points_gen: if frameCount == 0: p_face = src_img p_points = src_points avg_face0, avg_points0 = morph_one( src_img, src_points, dest_img, dest_points, 1 - float(frameCount) / (num_frames - 1), width, height) for i in range(0, slow_rate): avg_face, avg_points = morph_one(avg_face0, avg_points0, p_face, p_points, i / float(slow_rate - 1), width, height) mask = blender.mask_from_points(avg_face.shape[:2], avg_points) avg_face = np.dstack((avg_face, mask)) plt.plot_one(avg_face) plt.save(avg_face) video.write(avg_face) frameCount = frameCount + 1 p_face = avg_face0 p_points = avg_points0 video.end() plt.show()
def alpha_image(img, points): mask = blender.mask_from_points(img.shape[:2], points) return np.dstack((img, mask))
def warp_image(src_img, src_points, dest_points, dest_shape, dtype=np.uint8, result_type="zero", bk_img=None, smoothing=False): # Resultant image will not have an alpha channel num_chans = 3 src_img = src_img[:, :, :3] rows, cols = dest_shape[:2] try: if result_type == "white": # New attempt: fill with WHITE result_img = np.full((rows, cols, num_chans), 255, dtype) elif result_type == "image": # Take the background image, but make sure the destination points are empty # result_img = bk_img # Step 1: fill the background with zeroes result_img = np.zeros((rows, cols, num_chans), dtype) else: # Default (original) = fill with black = zero result_img = np.zeros((rows, cols, num_chans), dtype) # Calculate a set of triangles within [dest_points] delaunay = spatial.Delaunay(dest_points) tri_affines = np.asarray( list( triangular_affine_matrices(delaunay.simplices, src_points, dest_points))) process_warp(src_img, result_img, tri_affines, dest_points, delaunay) if result_type == "image": # Step 2: re-combine the background image and the face # Step 2.a: get a mask based on [dest_points] (filled with 255) mask = blender.mask_from_points((rows, cols), dest_points) # Step 2.b: combine two images based on the mask result_img[np.where(mask != 255)] = bk_img[np.where(mask != 255)] # Step 2.c: get the edge of the mask contour = cv2.Canny(mask, 100, 200) # Possibly add smoothing in a larger area if smoothing: kernel = np.ones((3, 3), np.uint8) contour = cv2.dilate(contour, kernel, iterations=1) # Create an image with the smoothing # TODO: work this out in a better way avg_img = cv2.addWeighted(bk_img, 0.5, src_img, 0.5, 0) else: # Step 2.d: Just take the average of two images avg_img = cv2.addWeighted(bk_img, 0.5, src_img, 0.5, 0) # Step 2.e: replace the contour with the averaged result_img[np.where(contour == 255)] = avg_img[np.where( contour == 255)] if bUseGrayScale: result_img = cv2.cvtColor(result_img, cv2.COLOR_RGB2GRAY) # cv2.COLOR_RGB2GRAY return result_img except: utils.DoError("warp_image: ") return np.zeros((rows, cols, num_chans), dtype)