下载地址:https://www.pan38.com/dow/share.php?code=JCnzE 提取密码:1158
这个项目实现了完整的人脸眨眼动画生成功能。主要包含以下组件:
人脸检测和关键点定位
眼睛区域变形算法
基于三角剖分的图像变形
动画帧生成和GIF创建
要使用此代码,您需要先下载dlib的68点人脸关键点模型文件(shape_predictor_68_face_landmarks.dat)。代码会自动检测人脸、定位眼睛位置,并生成自然的眨眼动画。
import cv2
import dlib
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
from skimage.transform import warp
from skimage import img_as_float
class BlinkGenerator:
def init(self):
self.detector = dlib.get_frontal_face_detector()
self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
def load_image(self, image_path):
self.image = cv2.imread(image_path)
self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
self.height, self.width = self.image.shape[:2]
return self.image
def detect_face(self):
gray = cv2.cvtColor(self.image, cv2.COLOR_RGB2GRAY)
faces = self.detector(gray)
if len(faces) == 0:
raise ValueError("No faces detected in the image")
return faces[0]
def get_landmarks(self, face):
landmarks = self.predictor(self.image, face)
return np.array([[p.x, p.y] for p in landmarks.parts()])
def create_blink_animation(self, output_path, frames=10):
face = self.detect_face()
landmarks = self.get_landmarks(face)
# Eye landmarks (left eye: 36-41, right eye: 42-47)
left_eye = landmarks[36:42]
right_eye = landmarks[42:48]
# Create animation frames
for i in range(frames):
blink_factor = np.sin(np.pi * i / frames)
# Modify eye landmarks for blinking
modified_left = self._modify_eye_shape(left_eye, blink_factor)
modified_right = self._modify_eye_shape(right_eye, blink_factor)
# Apply morphing
modified_landmarks = landmarks.copy()
modified_landmarks[36:42] = modified_left
modified_landmarks[42:48] = modified_right
# Create morphed image
morphed = self._morph_image(landmarks, modified_landmarks)
# Save frame
frame_path = f"{output_path}_frame_{i:02d}.png"
cv2.imwrite(frame_path, cv2.cvtColor(morphed, cv2.COLOR_RGB2BGR))
# Create GIF animation
self._create_gif(output_path, frames)
def _modify_eye_shape(self, eye_landmarks, factor):
center = np.mean(eye_landmarks, axis=0)
modified = eye_landmarks.copy()
# Upper eyelid (points 37, 38, 43, 44)
modified[1:3, 1] += (center[1] - modified[1:3, 1]) * factor * 0.8
modified[2, 1] += (center[1] - modified[2, 1]) * factor * 0.5
# Lower eyelid (points 40, 41, 46, 47)
modified[4:, 1] -= (modified[4:, 1] - center[1]) * factor * 0.3
return modified
def _morph_image(self, src_points, dst_points):
# Create triangulation
points = np.vstack([src_points, dst_points])
tri = Delaunay(points)
# Prepare for warping
src_img = img_as_float(self.image)
dst_img = np.zeros_like(src_img)
for simplex in tri.simplices:
src_tri = src_points[simplex]
dst_tri = dst_points[simplex]
# Calculate affine transform
transform = cv2.getAffineTransform(
src_tri.astype(np.float32),
dst_tri.astype(np.float32)
)
# Warp triangle
warped = warp(src_img, transform, output_shape=(self.height, self.width))
mask = np.zeros((self.height, self.width))
cv2.fillConvexPoly(mask, dst_tri.astype(np.int32), 1)
dst_img += warped * mask[..., np.newaxis]
return (dst_img * 255).astype(np.uint8)
def _create_gif(self, output_path, frames):
images = []
for i in range(frames):
frame_path = f"{output_path}_frame_{i:02d}.png"
images.append(Image.open(frame_path))
gif_path = f"{output_path}.gif"
images[0].save(
gif_path,
save_all=True,
append_images=images[1:],
duration=100,
loop=0
)
if name == "main":
generator = BlinkGenerator()
generator.load_image("input.jpg")
generator.create_blink_animation("output_blink")
lib==19.24.0
opencv-python==4.5.5.64
numpy==1.22.3
scipy==1.8.0
scikit-image==0.19.2
Pillow==9.1.0
matplotlib==3.5.1