使⽤Python,OpenCV创建动画GIF图和模因⽣成器
在这篇博客中,我们将学习如何使⽤Python,OpenCV,dlib和ImageMagick⼯具箱创建动画GIF。 然后,您将结合所有这些技术,使⽤OpenCV构建⼀个模因⽣成器(眼镜 和⽂字Deal with it)
效果图:
⾸先,讨论该项⽬的先决条件和依赖项,包括如何正确配置开发环境。
然后,将审查OpenCV GIF创建的项⽬/⽬录结构。
最后,了解项⽬结构后,我们将审查(1)我们的配置⽂件,以及(2)我们的Python脚本,该脚本负责使⽤OpenCV创建GIF。
使⽤OpenCV构建模因⽣成器,可以教会我们实践中使⽤的许多有价值的技术,包括:
1. 如何执⾏基于深度学习的⾯部检测
2. 如何使⽤dlib库进⾏⾯部标志检测并提取眼睛区域
3. 如何拍摄这两个区域并计算眼睛之间的旋转⾓度
4. 最后,如何使⽤OpenCV⽣成动画GIF(在ImageMagick的帮助下)
1. 先决条件和依赖项
1. OpenCV,dlib
OpenCV将⽤于⾯部检测和基本图像处理。dlib⽤于⾯部标志检测,使我们能够到眼镜并降下;OpenCV安装,通过pip:
pip install opencv
2. ImageMagick
ImageMagick是基于跨平台命令⾏的⼯具,提供了很多图像处理功能。
可通过⼀个命令将PNG/JPG转换为PDF
可将多张图⽚转换成PDF幻灯⽚
可绘制多边形,直线和其他形状
可在单个命令中进⾏批处理颜⾊调整或者整个图像数据集的空间尺⼨调整
可通过⼀组输⼊图像⽣成GIF图像。
要在Ubuntu(或Raspbian)上安装ImageMagick,只需使⽤apt:
sudo apt-get install imagemagick
macOS上安装ImageMagick,则可以使⽤HomeBrew:
brew install imagemagick
magick logo: logo.gif
magick identify logo.gif
magick logo.gif win:
能看到图⽚就是正常滴;
3. imutils
⽅便图像处理的便捷⽅法类包。安装:
pip install imutils
2. 项⽬结构
3. ⽣成GIF
# USAGE
# USAGE
# python create_gif.py --config config.json --image images/girl.jpg --output girl_out.gif
# 导⼊必要的包
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
# 叠加前景图像(fg )在背景图像(bg )在位置坐标(它们是(x,y)坐标),从⽽可以通过前景蒙版fgMask实现Alpha透明。
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通道(控制给定的区域放置在哪⾥以及多⼤的透明度)也需要与输⼊图像具有与相同的宽度和⾼度,但是仅包括前景图⽚的蒙版层 alpha = np.zeros(bg.shape[:2], dtype="uint8")
alpha[y:y + sH, x:x + sW]= fgMask
alpha = np.dstack([alpha]*3)
# 执⾏Alpha混合以合并前景,背景和alpha通道
output = alpha_blend(overlay, bg, alpha)
# 返回输出图像
return output
def alpha_blend(fg, bg, alpha):
# 转换前景图,背景图,alpha层由8位的int 到float,确保alpha层透明度在[0,1]之间
fg = fg.astype("float")
bg = bg.astype("float")
alpha = alpha.astype("float")/255
# 执⾏ alpha 混合
fg = cv2.multiply(alpha, fg)
bg = cv2.multiply(1- alpha, bg)
# 叠加前景,背景图,以获取最终输出
output = cv2.add(fg, bg)
# 返回输出图像
return output.astype("uint8")
def create_gif(inputPath, outputPath, delay, finalDelay, loop):
# 获取输⼊路径的所有图像
imagePaths =sorted(list(paths.list_images(inputPath)))
# 移除list中的最后⼀个路径
lastPath = imagePaths[-1]
rectangle函数opencvimagePaths = imagePaths[:-1]
# 构建 ImageMagick命令⾏以⽣成输出的GIF,给⼀个⾜够⼤的时间延迟以得到最终输出动画
cmd ="magick -delay {} {} -delay {} {} -loop {} {}".format(
cmd ="magick -delay {} {} -delay {} {} -loop {} {}".format(
delay," ".join(imagePaths), finalDelay, lastPath, loop,
outputPath)
os.system(cmd)
# 构建命令⾏参数并解析
# --config: JSON配置⽂件的路径
# --image:输⼊图像的路径
# --output:输出gif的路径
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⽂件
# 读取太阳镜图像,太阳镜蒙版图像
config = json.loads(open(args["config"]).read())
sg = cv2.imread(config["sunglasses"])
sgMask = cv2.imread(config["sunglasses_mask"])
# 如果以前运⾏的脚本有任何残留,我们从磁盘上删除临时⽬录,然后重新创建⼀个空的临时⽬录。临时⽂件夹将在GIF中保存每个单独的帧(config["temp_dir"], ignore_errors=True)
os.makedirs(config["temp_dir"])
# adNetFromCaffe:加载OpenCV的⾯部检测器,dnn模块在OpenCV3.3 之后⽀持
# 加载dlib的⾯部标志检测器(允许我们定位⾯部的结构:如眼睛、眉⽑、⿐⼦、嘴、下颌)
print("[INFO] ")
detector = adNetFromCaffe(config["face_detector_prototxt"],
config["face_detector_weights"])
predictor = dlib.shape_predictor(config["landmark_predictor"])
# 检测脸及眼睛位置
# 加载输⼊图像并从图像构建输⼊Blob
image = cv2.imread(args["image"])
image = size(image, width=500)
(H, W)= image.shape[:2]
blob = cv2.dnn.size(image,(300,300)),1.0,
(300,300),(104.0,177.0,123.0))
# 通过⾯部检测神经⽹络传递Blob并获得检测结果
print("[INFO] computing ")
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")
# 提取脸部并计算⾯部标志
# 计算脸部的坐标边界框
box = detections[0,0, i,3:7]* np.array([W, H, W, H])
(startX, startY, endX, endY)= box.astype("int")
# 构建dlib矩形对象,并应⽤⾯部标志定位
rect = angle(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]
# 给定眼睛的坐标,我们可以计算太阳镜的放置位置和放置⽅式:
# 计算每只眼睛的中⼼,然后计算眼睛质⼼之间的⾓度,就像执⾏⾯部对齐⼀样。
leftEyeCenter = an(axis=0).astype("int")
rightEyeCenter = an(axis=0).astype("int")
# 计算眼睛中⼼的⾓度
dY = rightEyeCenter[1]- leftEyeCenter[1]
dX = rightEyeCenter[0]- leftEyeCenter[0]
angle = np.degrees(np.arctan2(dY, dX))-180
print('angle: ', angle)
# 根据计算的⾓度旋转我们的眼镜,确保眼镜 和头是对齐的【使⽤rotate_bound ⽽不是rotate,确保仿射变换后,OpenCV不会裁剪掉超出边界的图像部分。】
sg = ate_bound(sg, angle)
# 眼镜 不应该占据整个脸的宽度,最理想的是仅仅覆盖了眼睛,这⾥我们将使⽤⼀个合适的宽度——脸宽度的90%作为眼镜的宽度
sgW =int((endX - startX)*0.9)
sg = size(sg, width=sgW)
# 应⽤于太阳镜本⾝的相同操作也需要应⽤于⾯罩。⾸先,由于蒙版始终是⼆进制的,因此我们需要将蒙版转换为灰度并将其⼆进制化。然后我们像对太阳镜处理相同的步骤,旋转蒙版并调整其⼤⼩
# 请注意,在调整遮罩⼤⼩时使⽤了最近邻插值。这是因为我们的遮罩应该只有两个值(0和255)。其他插值⽅法在审美上可能更美观,但实际上会对我们的⾯罩有害。
sgMask = cv2.cvtColor(sgMask, cv2.COLOR_BGR2GRAY)
sgMask = cv2.threshold(sgMask,0,255, cv2.THRESH_BINARY)[1]
sgMask = ate_bound(sgMask, angle)
sgMask = size(sgMask, width=sgW, inter=cv2.INTER_NEAREST)
# 创建GIF帧
# 太阳镜将会从上边以相同的速度⽤N步滑向期待的眼睛的位置开始创建每⼀帧的效果
# 我们的太阳镜将从图像顶部掉落,每帧依次显⽰太阳镜逐渐靠近脸部,直到遮住眼睛为⽌。
# 使⽤我们的JSON配置变量“ steps” (步骤数),利⽤NumPy的linspace,在0和右眼的y值之间⽣成均匀间隔的y值,将太阳镜分别放在每个镜架上
# np.linspace主要⽤来创建等差数列
steps = np.linspace(0, rightEyeCenter[1], config["steps"],
dtype="int")
# 循环遍历步数
for(i, y)in enumerate(steps):
# 计算太阳镜的平移
# 确保太阳镜能覆盖每只眼睛,⽽不仅仅是到达眼中⼼的位置,根据经验确定了百分⽐值:x位移*0.25 y位移*0.35,并确保没有负值
shiftX =int(sg.shape[1]*0.25)
shiftY =int(sg.shape[0]*0.35)
y =max(0, y - shiftY)
# 利⽤overlay_image⽣成图像的⼀帧
output = overlay_image(image, sg, sgMask,
(rightEyeCenter[0]- shiftX, y))
# 我们的最终输出帧是⼀种特殊情况,因为它是“ DEAL WITH IT”⽂本,我们将通过另⼀种屏蔽操作在框架上绘制该⽂本:
# 如果是最后⼀步移动了,需要把“DEAL WITH IT”⽂本添加到帧的底部
# 选择使⽤图⽚是因为OpenCV的字体渲染能⼒⾮常有限,⽽且想在⽂本上添加阴影和边框,这也是O
penCV⽆法做到的。
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,
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论