使用Python,OpenCV创建动画GIF图和模因生成器
在這篇博客中,我們將學習如何使用Python,OpenCV,dlib和ImageMagick工具箱創建動畫GIF。 然后,您將結合所有這些技術,使用OpenCV構建一個模因生成器(眼鏡👓和文字Deal with it)
效果圖:
首先,討論該項目的先決條件和依賴項,包括如何正確配置開發環境。
然后,將審查OpenCV GIF創建的項目/目錄結構。
最后,了解項目結構后,我們將審查(1)我們的配置文件,以及(2)我們的Python腳本,該腳本負責使用OpenCV創建GIF。
使用OpenCV構建模因生成器,可以教會我們實踐中使用的許多有價值的技術,包括:
- 如何執行基于深度學習的面部檢測
- 如何使用dlib庫進行面部標志檢測并提取眼睛區域
- 如何拍攝這兩個區域并計算眼睛之間的旋轉角度
- 最后,如何使用OpenCV生成動畫GIF(在ImageMagick的幫助下)
1. 先決條件和依賴項
1. OpenCV,dlib
OpenCV將用于面部檢測和基本圖像處理。dlib用于面部標志檢測,使我們能夠找到眼鏡并降下;
OpenCV安裝,通過pip:
pip install opencv
dlib安裝可參考: https://editor.csdn.net/md/?articleId=105896031
2. ImageMagick
ImageMagick是基于跨平臺命令行的工具,提供了很多圖像處理功能。
- 可通過一個命令將PNG/JPG轉換為PDF
- 可將多張圖片轉換成PDF幻燈片
- 可繪制多邊形,直線和其他形狀
- 可在單個命令中進行批處理顏色調整或者整個圖像數據集的空間尺寸調整
- 可通過一組輸入圖像生成GIF圖像。
要在Ubuntu(或Raspbian)上安裝ImageMagick,只需使用apt:
sudo apt-get install imagemagick
macOS上安裝ImageMagick,則可以使用HomeBrew:
brew install imagemagick
windows10上安裝ImageMagick,可以使用:
下載:ImageMagick-7.0.10-13-Q16-x64-static.exe http://www.imagemagick.org/script/download.php
安裝完cmd窗口測試:
magick logo: logo.gif
magick identify logo.gif
magick logo.gif win:
能看到圖片就是正常滴;
3. imutils
方便圖像處理的便捷方法類包。安裝:
pip install imutils
2. 項目結構
3. 生成GIF
# 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] = fgMaskalpha = np.dstack([alpha] * 3)# 執行Alpha混合以合并前景,背景和alpha通道output = alpha_blend(overlay, bg, alpha)# 返回輸出圖像return outputdef 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]imagePaths = imagePaths[:-1]# 構建 ImageMagick命令行以生成輸出的GIF,給一個足夠大的時間延遲以得到最終輸出動畫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中保存每個單獨的幀
shutil.rmtree(config["temp_dir"], ignore_errors=True)
os.makedirs(config["temp_dir"])# cv2.dnn.readNetFromCaffe: 加載OpenCV的面部檢測器,dnn模塊在OpenCV3.3 之后支持
# 加載dlib的面部標志檢測器(允許我們定位面部的結構:如眼睛、眉毛、鼻子、嘴、下頜)
print("[INFO] loading models...")
detector = cv2.dnn.readNetFromCaffe(config["face_detector_prototxt"],config["face_detector_weights"])
predictor = dlib.shape_predictor(config["landmark_predictor"])# 檢測 臉 及 眼睛位置
# 加載輸入圖像并從圖像構建輸入Blob
image = cv2.imread(args["image"])
image = imutils.resize(image, width=500)
(H, W) = image.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0,(300, 300), (104.0, 177.0, 123.0))# 通過面部檢測神經網絡傳遞Blob并獲得檢測結果
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)# 提取臉部 并計算面部標志
# 計算臉部的坐標邊界框
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]# 給定眼睛的坐標,我們可以計算太陽鏡的放置位置和放置方式:
# 計算每只眼睛的中心,然后計算眼睛質心之間的角度,就像執行面部對齊一樣。
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
print('angle: ', angle)# 根據計算的角度旋轉我們的眼鏡,確保眼鏡👓和頭是對齊的 【使用rotate_bound 而不是rotate,確保仿射變換后,OpenCV不會裁剪掉超出邊界的圖像部分。】
sg = imutils.rotate_bound(sg, angle)# 眼鏡👓不應該占據整個臉的寬度,最理想的是僅僅覆蓋了眼睛,這里我們將使用一個合適的寬度——臉寬度的90%作為眼鏡的寬度
sgW = int((endX - startX) * 0.9)
sg = imutils.resize(sg, width=sgW)# 應用于太陽鏡本身的相同操作也需要應用于面罩。首先,由于蒙版始終是二進制的,因此我們需要將蒙版轉換為灰度并將其二進制化。然后我們像對太陽鏡處理相同的步驟,旋轉蒙版并調整其大小
# 請注意,在調整遮罩大小時使用了最近鄰插值。這是因為我們的遮罩應該只有兩個值(0和255)。其他插值方法在審美上可能更美觀,但實際上會對我們的面罩有害。
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)# 創建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的字體渲染能力非常有限,而且想在文本上添加陰影和邊框,這也是OpenCV無法做到的。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]# resize圖像和蒙版為 輸出圖像寬度的 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 函數來生成GIF動畫文件,create_gif 函數是將參數傳遞給ImageMagick的convert的包裝器工具以執行其命令行。
create_gif(config["temp_dir"], args["output"], config["delay"],config["final_delay"], config["loop"])cv2.waitKey(0)
# 清理刪除臨時文件夾
print("[INFO] cleaning up...")
shutil.rmtree(config["temp_dir"], ignore_errors=True)
參考: https://www.pyimagesearch.com/2018/11/05/creating-gifs-with-opencv/210393818/
總結
以上是生活随笔為你收集整理的使用Python,OpenCV创建动画GIF图和模因生成器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java8 Stream应用:Map合并
- 下一篇: 他来了,请闭眼电视剧百度云资源1080P