Update vtoonify_model.py
Browse files- vtoonify_model.py +30 -19
vtoonify_model.py
CHANGED
@@ -22,8 +22,6 @@ import logging
|
|
22 |
from PIL import Image
|
23 |
|
24 |
|
25 |
-
|
26 |
-
|
27 |
# Configure logging
|
28 |
logging.basicConfig(level=logging.INFO)
|
29 |
|
@@ -98,6 +96,23 @@ class Model():
|
|
98 |
exstyle = self.vtoonify.zplus2wplus(exstyle)
|
99 |
return exstyle, 'Model of %s loaded.' % (style_type)
|
100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
def detect_and_align(self, frame, top, bottom, left, right, return_para=False):
|
102 |
message = 'Error: no face detected! Please retry or change the photo.'
|
103 |
instyle = None
|
@@ -107,10 +122,11 @@ class Model():
|
|
107 |
if len(faces) > 0:
|
108 |
logging.info(f"Detected {len(faces)} face(s).")
|
109 |
face = faces[0]
|
110 |
-
|
|
|
111 |
|
112 |
# Align face based on mapped landmarks
|
113 |
-
aligned_face = self.align_face(frame,
|
114 |
if aligned_face is not None:
|
115 |
logging.info(f"Aligned face shape: {aligned_face.shape}")
|
116 |
with torch.no_grad():
|
@@ -130,8 +146,7 @@ class Model():
|
|
130 |
return frame, instyle, message
|
131 |
|
132 |
def align_face(self, image, landmarks):
|
133 |
-
#
|
134 |
-
# Example: use specific indices for eyes and mouth
|
135 |
eye_left = np.mean(landmarks[36:42], axis=0)
|
136 |
eye_right = np.mean(landmarks[42:48], axis=0)
|
137 |
mouth_left = landmarks[48]
|
@@ -161,22 +176,16 @@ class Model():
|
|
161 |
img = img.resize((output_size, output_size), Image.ANTIALIAS)
|
162 |
|
163 |
return np.array(img)
|
164 |
-
|
165 |
-
|
|
|
166 |
return np.zeros((256, 256, 3), np.uint8), None, 'Error: fail to load empty file.'
|
167 |
-
|
168 |
-
# Convert bytes to a NumPy array
|
169 |
-
nparr = np.frombuffer(image_data, np.uint8)
|
170 |
-
|
171 |
-
# Decode the image data
|
172 |
-
frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
173 |
-
|
174 |
if frame is None:
|
175 |
-
return np.zeros((256, 256, 3), np.uint8), None, 'Error: fail to
|
176 |
-
|
177 |
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
178 |
return self.detect_and_align(frame, top, bottom, left, right)
|
179 |
-
|
180 |
def image_toonify(self, aligned_face: np.ndarray, instyle: torch.Tensor, exstyle: torch.Tensor, style_degree: float, style_type: str) -> tuple:
|
181 |
if instyle is None or aligned_face is None:
|
182 |
return np.zeros((256, 256, 3), np.uint8), 'Oops, something wrong with the input. Please go to Step 2 and Rescale Image/First Frame again.'
|
@@ -207,4 +216,6 @@ class Model():
|
|
207 |
"""Convert a tensor image to OpenCV format."""
|
208 |
tmp = ((img.cpu().numpy().transpose(1, 2, 0) + 1.0) * 127.5).astype(np.uint8).copy()
|
209 |
logging.debug(f"Converted image shape: {tmp.shape}, strides: {tmp.strides}")
|
210 |
-
return cv2.cvtColor(tmp, cv2.COLOR_RGB2BGR)
|
|
|
|
|
|
22 |
from PIL import Image
|
23 |
|
24 |
|
|
|
|
|
25 |
# Configure logging
|
26 |
logging.basicConfig(level=logging.INFO)
|
27 |
|
|
|
96 |
exstyle = self.vtoonify.zplus2wplus(exstyle)
|
97 |
return exstyle, 'Model of %s loaded.' % (style_type)
|
98 |
|
99 |
+
def convert_106_to_68(self, landmarks_106):
|
100 |
+
# Mapping from 106 landmarks to 68 landmarks
|
101 |
+
landmark106to68 = [
|
102 |
+
1, 10, 12, 14, 16, 3, 5, 7, 0, 23, 21, 19, 32, 30, 28, 26, 17, # Face outline
|
103 |
+
43, 48, 49, 51, 50, # Left eyebrow
|
104 |
+
102, 103, 104, 105, 101, # Right eyebrow
|
105 |
+
72, 73, 74, 86, 78, 79, 80, 85, 84, # Nose
|
106 |
+
35, 41, 42, 39, 37, 36, # Left eye
|
107 |
+
89, 95, 96, 93, 91, 90, # Right eye
|
108 |
+
52, 64, 63, 71, 67, 68, 61, 58, 59, 53, 56, 55, 65, 66, 62, 70, 69, 57, 60, 54 # Mouth
|
109 |
+
]
|
110 |
+
|
111 |
+
# Convert 106 landmarks to 68 landmarks
|
112 |
+
landmarks_68 = [landmarks_106[index] for index in landmark106to68]
|
113 |
+
|
114 |
+
return landmarks_68
|
115 |
+
|
116 |
def detect_and_align(self, frame, top, bottom, left, right, return_para=False):
|
117 |
message = 'Error: no face detected! Please retry or change the photo.'
|
118 |
instyle = None
|
|
|
122 |
if len(faces) > 0:
|
123 |
logging.info(f"Detected {len(faces)} face(s).")
|
124 |
face = faces[0]
|
125 |
+
landmarks_106 = face.landmark_2d_106
|
126 |
+
landmarks_68 = self.convert_106_to_68(landmarks_106)
|
127 |
|
128 |
# Align face based on mapped landmarks
|
129 |
+
aligned_face = self.align_face(frame, landmarks_68)
|
130 |
if aligned_face is not None:
|
131 |
logging.info(f"Aligned face shape: {aligned_face.shape}")
|
132 |
with torch.no_grad():
|
|
|
146 |
return frame, instyle, message
|
147 |
|
148 |
def align_face(self, image, landmarks):
|
149 |
+
# Example alignment logic using 68 landmarks
|
|
|
150 |
eye_left = np.mean(landmarks[36:42], axis=0)
|
151 |
eye_right = np.mean(landmarks[42:48], axis=0)
|
152 |
mouth_left = landmarks[48]
|
|
|
176 |
img = img.resize((output_size, output_size), Image.ANTIALIAS)
|
177 |
|
178 |
return np.array(img)
|
179 |
+
|
180 |
+
def detect_and_align_image(self, image: str, top: int, bottom: int, left: int, right: int) -> tuple:
|
181 |
+
if image is None:
|
182 |
return np.zeros((256, 256, 3), np.uint8), None, 'Error: fail to load empty file.'
|
183 |
+
frame = cv2.imread(image)
|
|
|
|
|
|
|
|
|
|
|
|
|
184 |
if frame is None:
|
185 |
+
return np.zeros((256, 256, 3), np.uint8), None, 'Error: fail to load the image.'
|
|
|
186 |
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
187 |
return self.detect_and_align(frame, top, bottom, left, right)
|
188 |
+
|
189 |
def image_toonify(self, aligned_face: np.ndarray, instyle: torch.Tensor, exstyle: torch.Tensor, style_degree: float, style_type: str) -> tuple:
|
190 |
if instyle is None or aligned_face is None:
|
191 |
return np.zeros((256, 256, 3), np.uint8), 'Oops, something wrong with the input. Please go to Step 2 and Rescale Image/First Frame again.'
|
|
|
216 |
"""Convert a tensor image to OpenCV format."""
|
217 |
tmp = ((img.cpu().numpy().transpose(1, 2, 0) + 1.0) * 127.5).astype(np.uint8).copy()
|
218 |
logging.debug(f"Converted image shape: {tmp.shape}, strides: {tmp.strides}")
|
219 |
+
return cv2.cvtColor(tmp, cv2.COLOR_RGB2BGR)
|
220 |
+
|
221 |
+
|