|
|
| import mediapipe as mp |
| import cv2 |
| import numpy as np |
| import os |
| import shutil |
| import subprocess |
| import uuid |
|
|
|
|
|
|
| model_path = "./face_landmarker_v2_with_blendshapes.task" |
|
|
| BaseOptions = mp.tasks.BaseOptions |
| FaceLandmarker = mp.tasks.vision.FaceLandmarker |
| FaceLandmarkerOptions = mp.tasks.vision.FaceLandmarkerOptions |
| VisionRunningMode = mp.tasks.vision.RunningMode |
|
|
| options = FaceLandmarkerOptions( |
| base_options=BaseOptions(model_asset_path=model_path), |
| running_mode=VisionRunningMode.IMAGE, |
| num_faces=3 |
| ) |
| landmarker = FaceLandmarker.create_from_options(options) |
|
|
|
|
| def align_to_fixed_eyes(image, lm, ref_left_eye, ref_right_eye, canvas_size=1024): |
| """Align a face to fixed eye positions on a canvas.""" |
| h, w, _ = image.shape |
|
|
| |
| left_eye = np.array([lm[468].x * w, lm[468].y * h]) |
| right_eye = np.array([lm[473].x * w, lm[473].y * h]) |
|
|
| |
| dx = right_eye[0] - left_eye[0] |
| dy = right_eye[1] - left_eye[1] |
| angle = np.degrees(np.arctan2(dy, dx)) |
|
|
| |
| eye_dist = np.linalg.norm(right_eye - left_eye) |
| ref_eye_dist = np.linalg.norm(np.array(ref_right_eye) - np.array(ref_left_eye)) |
| scale = ref_eye_dist / eye_dist |
|
|
| |
| eye_center = (left_eye + right_eye) / 2 |
| ref_center = (np.array(ref_left_eye) + np.array(ref_right_eye)) / 2 |
|
|
| |
| M = cv2.getRotationMatrix2D(tuple(eye_center), angle, scale) |
| M[0,2] += (ref_center[0] - eye_center[0]) |
| M[1,2] += (ref_center[1] - eye_center[1]) |
|
|
| |
| offset_x = canvas_size//2 - int(ref_center[0]) |
| offset_y = canvas_size//2 - int(ref_center[1]) |
| M[0,2] += offset_x |
| M[1,2] += offset_y |
|
|
| aligned = cv2.warpAffine(image, M, (canvas_size, canvas_size), flags=cv2.INTER_CUBIC) |
| return aligned |
|
|
|
|
| def process_images_fixed_eyes(files, output_folder="./aligned_fixed_eyes", canvas_size=1024): |
| """Process a list of image files and align faces with fixed eyes.""" |
| if os.path.exists(output_folder): |
| shutil.rmtree(output_folder) |
| os.makedirs(output_folder, exist_ok=True) |
| |
| ref_left_eye, ref_right_eye = None, None |
|
|
| for idx, in_path in enumerate(files): |
| image = cv2.imread(in_path) |
| if image is None: |
| continue |
|
|
| mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=image) |
| result = landmarker.detect(mp_image) |
|
|
| |
| if not result.face_landmarks: |
| continue |
| if len(result.face_landmarks) > 1: |
| continue |
|
|
| lm = result.face_landmarks[0] |
|
|
| |
| if ref_left_eye is None or ref_right_eye is None: |
| h, w, _ = image.shape |
| ref_left_eye = (int(lm[468].x * w), int(lm[468].y * h)) |
| ref_right_eye = (int(lm[473].x * w), int(lm[473].y * h)) |
|
|
| aligned = align_to_fixed_eyes(image, lm, ref_left_eye, ref_right_eye, canvas_size) |
|
|
| out_path = os.path.join(output_folder, f"{idx:04d}.png") |
| cv2.imwrite(out_path, aligned) |
|
|
| import os |
| import uuid |
| import subprocess |
|
|
| def create_timelapse(input_folder="./aligned_fixed_eyes", output_folder="./download", fps_in=10, fps_out=30): |
| """ |
| Create a timelapse video using FFmpeg and a file list (handles missing numbers) |
| Output is hidden unless FFmpeg fails. |
| """ |
| os.makedirs(output_folder, exist_ok=True) |
|
|
| |
| images = sorted([ |
| f for f in os.listdir(input_folder) |
| if f.lower().endswith((".png", ".jpg", ".jpeg")) |
| ]) |
|
|
| if not images: |
| print("No images found in folder:", input_folder) |
| return None |
|
|
| |
| file_list_path = "./file_list.txt" |
| with open(file_list_path, "w") as f: |
| for img in images: |
| f.write(f"file '{os.path.join(input_folder, img)}'\n") |
|
|
| random_str = str(uuid.uuid4())[:6] |
| output_path = os.path.join(output_folder, f"{random_str}.mp4") |
|
|
| |
| command = [ |
| "ffmpeg", |
| "-y", |
| "-r", str(fps_in), |
| "-f", "concat", |
| "-safe", "0", |
| "-i", file_list_path, |
| "-c:v", "libx264", |
| "-r", str(fps_out), |
| "-pix_fmt", "yuv420p", |
| output_path |
| ] |
|
|
| |
| result = subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
| os.remove(file_list_path) |
|
|
| if result.returncode == 0: |
| return output_path |
| else: |
| print("FFmpeg failed. Command was:") |
| print(" ".join(command)) |
| return None |
|
|
|
|
| def EyeLock_Timelapse(selfies_folder, aligned_folder="./aligned_fixed_eyes", output_folder="./download",image_duraion=0.1): |
| """Wrapper: process selfies from a folder and create a timelapse.""" |
| files = sorted([ |
| os.path.join(selfies_folder, f) |
| for f in os.listdir(selfies_folder) |
| if f.lower().endswith((".jpg", ".png", ".jpeg")) |
| ]) |
| process_images_fixed_eyes(files, aligned_folder, canvas_size=1024) |
| output_video = create_timelapse(aligned_folder, output_folder,fps_in=image_duraion*100) |
| print("Final video:", output_video) |
| return output_video |
|
|
|
|
| |
| |
| |
|
|