$ sudo apt-get install imagemagick
$ brew install imagemagick
$ pip install imutils
images/
: examples of input images for which we want to make an animated GIF. I found some images with me, but feel free to add my own.assets/
: this folder contains our face detector, face face detector and all images + associated masks. With these assets, we will overlay points and text on the original images from the first folder.config.json
.config.json
and create_gif.py
.config.json
file and insert the following key / value pairs: { "face_detector_prototxt": "assets/deploy.prototxt", "face_detector_weights": "assets/res10_300x300_ssd_iter_140000.caffemodel", "landmark_predictor": "assets/shape_predictor_68_face_landmarks.dat",
"sunglasses": "assets/sunglasses.png", "sunglasses_mask": "assets/sunglasses_mask.png", "deal_with_it": "assets/deal_with_it.png", "deal_with_it_mask": "assets/deal_with_it_mask.png",
"min_confidence": 0.5, "steps": 20, "delay": 5, "final_delay": 250, "loop": 0, "temp_dir": "temp" }
min_confidence
: minimum required face detection probability.steps
: the number of frames in the final animation. Each "step" moves the sunglasses from the top border down to the goal (i.e., to the eyes).delay
: delay between frames in hundredths of a second.final_delay
: the last frame delay in hundredths of a second (useful in this context, since we want the text to appear longer than the other frames).loop
: a null value indicates that GIF repeats forever, otherwise specify a positive integer for the number of repetitions of the animation.temp_dir
: the temporary directory in which each frame is stored will be before creating the final GIF.create_gif.py
and paste the following code: # from imutils import face_utils from imutils import paths import numpy as np import argparse import imutils import shutil import json import dlib import cv2 import sys import os
overlay_image
function: def overlay_image(bg, fg, fgMask, coords): # (, ) # (sH, sW) = fg.shape[:2] (x, y) = coords # # , , ** # , overlay = np.zeros(bg.shape, dtype="uint8") overlay[y:y + sH, x:x + sW] = fg # - , ** ** # , , # , alpha = np.zeros(bg.shape[:2], dtype="uint8") alpha[y:y + sH, x:x + sW] = fgMask alpha = np.dstack([alpha] * 3) # - , # - output = alpha_blend(overlay, bg, alpha) # return output
overlay_image
function superimposes the foreground ( fg
) on the upper part of the background image ( bg
) on the coordinates coords
(coordinates (x, y) ), implementing alpha transparency on the foreground mask fgMask
. def alpha_blend(fg, bg, alpha): # , - # [0, 1] fg = fg.astype("float") bg = bg.astype("float") alpha = alpha.astype("float") / 255 # - fg = cv2.multiply(alpha, fg) bg = cv2.multiply(1 - alpha, bg) # , output = cv2.add(fg, bg) # return output.astype("uint8")
convert
command: def create_gif(inputPath, outputPath, delay, finalDelay, loop): # imagePaths = sorted(list(paths.list_images(inputPath))) # lastPath = imagePaths[-1] imagePaths = imagePaths[:-1] # imagemagick 'convert' # GIF # ( ) cmd = "convert -delay {} {} -delay {} {} -loop {} {}".format( delay, " ".join(imagePaths), finalDelay, lastPath, loop, outputPath) os.system(cmd)
create_gif
function takes a set of images and assembles them into a GIF animation with a specified delay between frames and loops. All this handles ImageMagick — we simply wrap the convert
command in a function that dynamically processes various parameters.convert
arguments. There you will see how many features this command has!imagePaths
.imagePaths
to eliminate the last path.convert
to create a GIF animation. # ap = argparse.ArgumentParser() ap.add_argument("-c", "--config", required=True, help="path to configuration file") ap.add_argument("-i", "--image", required=True, help="path to input image") ap.add_argument("-o", "--output", required=True, help="path to output GIF") args = vars(ap.parse_args())
--config
: path to the JSON configuration file. We covered the configuration file in the previous section.--image
: path to the input image, against which the animation is created (ie, face detection, adding sunglasses, and then text).--output
: the path to the final GIF. # JSON, # config = json.loads(open(args["config"]).read()) sg = cv2.imread(config["sunglasses"]) sgMask = cv2.imread(config["sunglasses_mask"]) # ( ), # , , # GIF- shutil.rmtree(config["temp_dir"], ignore_errors=True) os.makedirs(config["temp_dir"])
# load our OpenCV face detector and dlib facial landmark predictor print("[INFO] loading models...") detector = cv2.dnn.readNetFromCaffe(config["face_detector_prototxt"], config["face_detector_weights"]) predictor = dlib.shape_predictor(config["landmark_predictor"])
cv2.dnn.readNetFromCaffe
. The dnn
module is available only in OpenCV 3.3 or later. The face detector will detect the presence of faces in the image: # image = cv2.imread(args["image"]) (H, W) = image.shape[:2] blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0)) # print("[INFO] computing object detections...") detector.setInput(blob) detections = detector.forward() # , # , i = np.argmax(detections[0, 0, :, 2]) confidence = detections[0, 0, i, 2] # if confidence < config["min_confidence"]: print("[INFO] no reliable faces found") sys.exit(0)
image
.blob
for sending to the neural network face detector. This article describes how blobFromImage
from OpenCV works. # (x, y) # box = detections[0, 0, i, 3:7] * np.array([W, H, W, H]) (startX, startY, endX, endY) = box.astype("int") # dlib # rect = dlib.rectangle(int(startX), int(startY), int(endX), int(endY)) shape = predictor(image, rect) shape = face_utils.shape_to_np(shape) # , # (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"] leftEyePts = shape[lStart:lEnd] rightEyePts = shape[rStart:rEnd]
rectangle
object in dlib and apply face localization.leftEyePts
and rightEyePts
, respectively. # leftEyeCenter = leftEyePts.mean(axis=0).astype("int") rightEyeCenter = rightEyePts.mean(axis=0).astype("int") # dY = rightEyeCenter[1] - leftEyeCenter[1] dX = rightEyeCenter[0] - leftEyeCenter[0] angle = np.degrees(np.arctan2(dY, dX)) - 180 # , # sg = imutils.rotate_bound(sg, angle) # ** , # — # 90% sgW = int((endX - startX) * 0.9) sg = imutils.resize(sg, width=sgW) # ( , # ), # - — # , # sgMask = cv2.cvtColor(sgMask, cv2.COLOR_BGR2GRAY) sgMask = cv2.threshold(sgMask, 0, 255, cv2.THRESH_BINARY)[1] sgMask = imutils.rotate_bound(sgMask, angle) sgMask = imutils.resize(sgMask, width=sgW, inter=cv2.INTER_NEAREST)
rotate
, so that OpenCV will not cut off parts that are not visible after affine transformation. # , # N # steps = np.linspace(0, rightEyeCenter[1], config["steps"], dtype="int") # start looping over the steps for (i, y) in enumerate(steps): # # , ** # , # shiftX = int(sg.shape[1] * 0.25) shiftY = int(sg.shape[0] * 0.35) y = max(0, y - shiftY) # add the sunglasses to the image output = overlay_image(image, sg, sgMask, (rightEyeCenter[0] - shiftX, y))
"steps"
variable in the JSON configuration file, we generate y-coordinates for each frame. To do this, effortlessly use the function linspace
from NumPy.overlay_image
function, overlay_image
generate the final output
frame. # , # "DEAL WITH IT" if i == len(steps) - 1: # "DEAL WITH IT" , # dwi = cv2.imread(config["deal_with_it"]) dwiMask = cv2.imread(config["deal_with_it_mask"]) dwiMask = cv2.cvtColor(dwiMask, cv2.COLOR_BGR2GRAY) dwiMask = cv2.threshold(dwiMask, 0, 255, cv2.THRESH_BINARY)[1] # 80% # oW = int(W * 0.8) dwi = imutils.resize(dwi, width=oW) dwiMask = imutils.resize(dwiMask, width=oW, inter=cv2.INTER_NEAREST) # , , # oX = int(W * 0.1) oY = int(H * 0.8) output = overlay_image(output, dwi, dwiMask, (oX, oY))
# p = os.path.sep.join([config["temp_dir"], "{}.jpg".format( str(i).zfill(8))]) cv2.imwrite(p, output) # , # GIF- print("[INFO] creating GIF...") create_gif(config["temp_dir"], args["output"], config["delay"], config["final_delay"], config["loop"]) # -- print("[INFO] cleaning up...") shutil.rmtree(config["temp_dir"], ignore_errors=True)
create_gif
to create a GIF-animation file. Remember, this is a shell that passes parameters to the ImageMagick convert
command line tool. $ python create_gif.py --config config.json --image images/adrian.jpg \ --output adrian_out.gif [INFO] loading models... [INFO] computing object detections... [INFO] creating GIF... [INFO] cleaning up...
$ python create_gif.py --config config.json --image images/adrian_jp.jpg \ --output adrian_jp_out.gif [INFO] loading models... [INFO] computing object detections... [INFO] creating GIF... [INFO] cleaning up...
$ python create_gif.py --config config.json --image images/vampire.jpg \ --output vampire_out.gif [INFO] loading models... [INFO] computing object detections... [INFO] creating GIF... [INFO] cleaning up...
$ python create_gif.py --config config.json --image images/rooster.jpg \ --output rooster_out.gif [INFO] loading models... [INFO] computing object detections... [INFO] creating GIF... [INFO] cleaning up...
$ python create_gif.py --config config.json --image images/pupper.jpg \ --output pupper_out.gif [INFO] loading models... [INFO] computing object detections... [INFO] creating GIF... [INFO] cleaning up...
$ python create_gif.py --config config.json --image images/adrian.jpg \ --output adrian_out.gif ... Traceback (most recent call last): File "create_gif.py", line 142, in <module> (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] AttributeError: module 'imutils.face_utils' has no attribute 'FACIAL_LANDMARKS_IDXS'
$ pip install --upgrade imutils Collecting imutils ... Successfully installed imutils-0.5.1
imutils.face_utils
uses the 68-point detector of landmarks, built-in to dlib (as in this article). There is a faster 5-point detector , which now also works with imutils. I recently updated imutils to support both detectors (so you may see an error).Source: https://habr.com/ru/post/429024/
All Articles