AvatarResurrectionChatbot commited on
Commit
556a15f
·
verified ·
1 Parent(s): f3022b1

Update idle_generator.py

Browse files
Files changed (1) hide show
  1. idle_generator.py +24 -49
idle_generator.py CHANGED
@@ -12,9 +12,6 @@ import logging
12
  import torch
13
  import cv2
14
  import numpy as np
15
- import albumentations as A
16
- import albumentations.pytorch.transforms as A_pytorch
17
- import face_alignment
18
 
19
  logger = logging.getLogger(__name__)
20
 
@@ -45,65 +42,42 @@ CLIP_MODES = [
45
  class IdleVideoGenerator:
46
  """Generates idle animation clips using FLOAT model with scripted pose control."""
47
 
48
- def __init__(self, float_model, device, input_size=512):
49
  """
50
  Args:
51
  float_model: Loaded FLOAT model instance (already on device, eval mode)
52
  device: torch device
53
  input_size: image size (default 512)
 
54
  """
55
  self.model = float_model
56
  self.device = device
57
  self.input_size = input_size
58
-
59
- # Face alignment for cropping
60
- self.fa = face_alignment.FaceAlignment(
61
- face_alignment.LandmarksType.TWO_D, flip_input=False, device=str(device)
62
- )
63
-
64
- # Image transform
65
- self.transform = A.Compose([
66
- A.Resize(height=input_size, width=input_size, interpolation=cv2.INTER_AREA),
67
- A.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)),
68
- A_pytorch.ToTensorV2(),
69
- ])
70
 
71
  # Store original sample function for monkey-patching
72
  self._original_sample = self.model.sample
73
 
74
- def _crop_face(self, img):
75
- """Detect and crop face from image."""
76
- mult = 360.0 / img.shape[0]
77
- interp = cv2.INTER_AREA if mult < 1.0 else cv2.INTER_CUBIC
78
- resized = cv2.resize(img, (0, 0), fx=mult, fy=mult, interpolation=interp)
79
-
80
- bboxes = self.fa.face_detector.detect_from_image(resized)
81
- if not bboxes:
82
- # No face found — just resize
83
- return cv2.resize(img, (self.input_size, self.input_size))
84
-
85
- x1, y1, x2, y2, score = bboxes[0]
86
- mx = int((x1 + x2) / 2 / mult)
87
- my = int((y1 + y2) / 2 / mult)
88
- bs = int(max(y2 - y1, x2 - x1) / mult * 0.8)
89
-
90
- img = cv2.copyMakeBorder(img, bs, bs, bs, bs, cv2.BORDER_REFLECT_101)
91
- my += bs
92
- mx += bs
93
- crop = img[my:my + 2 * bs, mx:mx + 2 * bs]
94
- return cv2.resize(crop, (self.input_size, self.input_size))
95
-
96
- def _load_reference_image(self, image_path, no_crop=False):
97
- """Load and preprocess a reference image."""
98
- img = cv2.imread(image_path)
99
- if img is None:
100
- raise FileNotFoundError(f"Could not read image: {image_path}")
101
- img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
102
-
103
- if not no_crop:
104
- img = self._crop_face(img)
105
-
106
- return self.transform(image=img)['image'].unsqueeze(0).to(self.device)
107
 
108
  def generate(self, ref_image_path, output_dir, avatar_name,
109
  num_clips=NUM_CLIPS, progress_callback=None):
@@ -271,5 +245,6 @@ def get_idle_generator():
271
  float_model=lipsync.model,
272
  device=lipsync.device,
273
  input_size=lipsync.opt.input_size,
 
274
  )
275
  return _generator
 
12
  import torch
13
  import cv2
14
  import numpy as np
 
 
 
15
 
16
  logger = logging.getLogger(__name__)
17
 
 
42
  class IdleVideoGenerator:
43
  """Generates idle animation clips using FLOAT model with scripted pose control."""
44
 
45
+ def __init__(self, float_model, device, input_size=512, lipsync_instance=None):
46
  """
47
  Args:
48
  float_model: Loaded FLOAT model instance (already on device, eval mode)
49
  device: torch device
50
  input_size: image size (default 512)
51
+ lipsync_instance: FloatLipsync instance for reusing its face crop logic
52
  """
53
  self.model = float_model
54
  self.device = device
55
  self.input_size = input_size
56
+ self.lipsync = lipsync_instance
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  # Store original sample function for monkey-patching
59
  self._original_sample = self.model.sample
60
 
61
+ def _load_reference_image(self, image_path):
62
+ """Load and preprocess a reference image using float_lipsync's proven crop logic."""
63
+ if self.lipsync:
64
+ # Use the same crop/transform as float_lipsync for consistency
65
+ self.lipsync._preload_reference_image(image_path)
66
+ return self.lipsync.preprocessed_ref_image.clone()
67
+ else:
68
+ # Fallback: basic resize without face crop
69
+ import albumentations as A
70
+ import albumentations.pytorch.transforms as A_pytorch
71
+ img = cv2.imread(image_path)
72
+ if img is None:
73
+ raise FileNotFoundError(f"Could not read image: {image_path}")
74
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
75
+ transform = A.Compose([
76
+ A.Resize(height=self.input_size, width=self.input_size, interpolation=cv2.INTER_AREA),
77
+ A.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)),
78
+ A_pytorch.ToTensorV2(),
79
+ ])
80
+ return transform(image=img)['image'].unsqueeze(0).to(self.device)
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  def generate(self, ref_image_path, output_dir, avatar_name,
83
  num_clips=NUM_CLIPS, progress_callback=None):
 
245
  float_model=lipsync.model,
246
  device=lipsync.device,
247
  input_size=lipsync.opt.input_size,
248
+ lipsync_instance=lipsync,
249
  )
250
  return _generator