primerz commited on
Commit
a628de7
·
verified ·
1 Parent(s): 718b84e

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +84 -25
utils.py CHANGED
@@ -1,5 +1,6 @@
1
  """
2
  Utility functions for Pixagram AI Pixel Art Generator
 
3
  """
4
  import numpy as np
5
  import cv2
@@ -8,6 +9,40 @@ from PIL import Image, ImageEnhance, ImageFilter, ImageDraw
8
  from config import COLOR_MATCH_CONFIG, FACE_MASK_CONFIG, AGE_BRACKETS
9
 
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  def sanitize_text(text):
12
  """
13
  Remove or replace problematic characters (emojis, special unicode)
@@ -218,15 +253,29 @@ def create_face_mask(image, face_bbox, feather=None):
218
  return mask
219
 
220
 
221
- def draw_kps(image_pil, kps, color_list=[(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255)]):
222
- """Draw facial keypoints on image for InstantID ControlNet"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  stickwidth = 4
224
  limbSeq = np.array([[0, 2], [1, 2], [3, 2], [4, 2]])
225
  kps = np.array(kps)
226
 
227
- w, h = image_pil.size
228
- out_img = np.zeros([h, w, 3])
229
-
230
  for i in range(len(limbSeq)):
231
  index = limbSeq[i]
232
  color = color_list[index[0]]
@@ -235,10 +284,9 @@ def draw_kps(image_pil, kps, color_list=[(255, 0, 0), (0, 255, 0), (0, 0, 255),
235
  y = kps[index][:, 1]
236
  length = ((x[0] - x[1]) ** 2 + (y[0] - y[1]) ** 2) ** 0.5
237
  angle = math.degrees(math.atan2(y[0] - y[1], x[0] - x[1]))
238
- polygon = cv2.ellipse2Poly(
239
- (int(np.mean(x)), int(np.mean(y))), (int(length / 2), stickwidth), int(angle), 0, 360, 1
240
- )
241
  out_img = cv2.fillConvexPoly(out_img.copy(), polygon, color)
 
242
  out_img = (out_img * 0.6).astype(np.uint8)
243
 
244
  for idx_kp, kp in enumerate(kps):
@@ -246,49 +294,56 @@ def draw_kps(image_pil, kps, color_list=[(255, 0, 0), (0, 255, 0), (0, 0, 255),
246
  x, y = kp
247
  out_img = cv2.circle(out_img.copy(), (int(x), int(y)), 10, color, -1)
248
 
249
- out_img_pil = Image.fromarray(out_img.astype(np.uint8))
250
- return out_img_pil
251
 
252
 
253
  def get_facial_attributes(face):
254
  """
255
- Extract comprehensive facial attributes.
256
- Returns dict with age, gender, expression, quality metrics.
 
 
 
 
 
257
  """
258
  attributes = {
 
259
  'age': None,
260
  'gender': None,
261
  'expression': None,
262
- 'quality': 1.0,
263
- 'pose_angle': 0,
264
- 'description': []
265
  }
266
 
267
- # Age extraction
268
  try:
269
  if hasattr(face, 'age'):
270
  age = int(face.age)
271
  attributes['age'] = age
 
 
272
  for min_age, max_age, label in AGE_BRACKETS:
273
  if min_age <= age < max_age:
274
  attributes['description'].append(label)
275
  break
276
- except (ValueError, TypeError, AttributeError) as e:
277
- print(f"[WARNING] Age extraction failed: {e}")
278
 
279
- # Gender extraction
280
  try:
281
  if hasattr(face, 'gender'):
282
  gender_code = int(face.gender)
283
  attributes['gender'] = gender_code
284
  if gender_code == 1:
285
- attributes['description'].append("male")
286
  elif gender_code == 0:
287
- attributes['description'].append("female")
288
- except (ValueError, TypeError, AttributeError) as e:
289
- print(f"[WARNING] Gender extraction failed: {e}")
290
 
291
- # Expression/emotion detection (if available)
292
  try:
293
  if hasattr(face, 'emotion'):
294
  # Some InsightFace models provide emotion
@@ -409,6 +464,10 @@ def calculate_optimal_size(original_width, original_height, recommended_sizes=No
409
  Returns:
410
  Tuple of (optimal_width, optimal_height) as multiples of 64
411
  """
 
 
 
 
412
  aspect_ratio = original_width / original_height
413
 
414
  # Legacy mode: use recommended sizes if provided
@@ -506,4 +565,4 @@ def enhance_face_crop(face_crop):
506
  return face_crop_final
507
 
508
 
509
- print("[OK] Utilities loaded")
 
1
  """
2
  Utility functions for Pixagram AI Pixel Art Generator
3
+ UPDATED VERSION with type safety helpers
4
  """
5
  import numpy as np
6
  import cv2
 
9
  from config import COLOR_MATCH_CONFIG, FACE_MASK_CONFIG, AGE_BRACKETS
10
 
11
 
12
+ # ============================================
13
+ # NEW: Type Safety Helpers
14
+ # ============================================
15
+
16
+ def ensure_int(value):
17
+ """
18
+ Convert numpy.int64 or similar to Python int.
19
+ Prevents tensor construction errors with PIL dimensions.
20
+ """
21
+ if isinstance(value, (int, float)):
22
+ return int(value)
23
+ if hasattr(value, 'item'):
24
+ return int(value.item())
25
+ return int(value)
26
+
27
+
28
+ def safe_image_size(image):
29
+ """
30
+ Get image size as pure Python ints (not numpy.int64).
31
+ Prevents errors when using PIL dimensions in tensor operations.
32
+
33
+ Args:
34
+ image: PIL Image
35
+
36
+ Returns:
37
+ Tuple of (width, height) as Python ints
38
+ """
39
+ return (ensure_int(image.width), ensure_int(image.height))
40
+
41
+
42
+ # ============================================
43
+ # Original Utility Functions
44
+ # ============================================
45
+
46
  def sanitize_text(text):
47
  """
48
  Remove or replace problematic characters (emojis, special unicode)
 
253
  return mask
254
 
255
 
256
+ def draw_kps(image_pil, kps, color_list=[(255,0,0), (0,255,0), (0,0,255), (255,255,0), (255,0,255)]):
257
+ """
258
+ Draw facial keypoints on image.
259
+
260
+ Args:
261
+ image_pil: PIL Image
262
+ kps: Keypoints array from InsightFace
263
+ color_list: List of colors for different keypoints
264
+
265
+ Returns:
266
+ PIL Image with keypoints drawn
267
+ """
268
+ import cv2
269
+ import numpy as np
270
+ from PIL import Image
271
+
272
  stickwidth = 4
273
  limbSeq = np.array([[0, 2], [1, 2], [3, 2], [4, 2]])
274
  kps = np.array(kps)
275
 
276
+ # Convert PIL to OpenCV
277
+ out_img = np.array(image_pil)
278
+
279
  for i in range(len(limbSeq)):
280
  index = limbSeq[i]
281
  color = color_list[index[0]]
 
284
  y = kps[index][:, 1]
285
  length = ((x[0] - x[1]) ** 2 + (y[0] - y[1]) ** 2) ** 0.5
286
  angle = math.degrees(math.atan2(y[0] - y[1], x[0] - x[1]))
287
+ polygon = cv2.ellipse2Poly((int(np.mean(x)), int(np.mean(y))), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
 
 
288
  out_img = cv2.fillConvexPoly(out_img.copy(), polygon, color)
289
+
290
  out_img = (out_img * 0.6).astype(np.uint8)
291
 
292
  for idx_kp, kp in enumerate(kps):
 
294
  x, y = kp
295
  out_img = cv2.circle(out_img.copy(), (int(x), int(y)), 10, color, -1)
296
 
297
+ # Convert back to PIL
298
+ return Image.fromarray(out_img.astype(np.uint8))
299
 
300
 
301
  def get_facial_attributes(face):
302
  """
303
+ Extract facial attributes from InsightFace detection.
304
+
305
+ Args:
306
+ face: InsightFace face detection object
307
+
308
+ Returns:
309
+ Dictionary of facial attributes
310
  """
311
  attributes = {
312
+ 'description': [],
313
  'age': None,
314
  'gender': None,
315
  'expression': None,
316
+ 'pose_angle': None,
317
+ 'quality': None
 
318
  }
319
 
320
+ # Age
321
  try:
322
  if hasattr(face, 'age'):
323
  age = int(face.age)
324
  attributes['age'] = age
325
+
326
+ # Age bracket
327
  for min_age, max_age, label in AGE_BRACKETS:
328
  if min_age <= age < max_age:
329
  attributes['description'].append(label)
330
  break
331
+ except (ValueError, TypeError, AttributeError):
332
+ pass
333
 
334
+ # Gender
335
  try:
336
  if hasattr(face, 'gender'):
337
  gender_code = int(face.gender)
338
  attributes['gender'] = gender_code
339
  if gender_code == 1:
340
+ attributes['description'].append('male')
341
  elif gender_code == 0:
342
+ attributes['description'].append('female')
343
+ except (ValueError, TypeError, AttributeError):
344
+ pass
345
 
346
+ # Expression
347
  try:
348
  if hasattr(face, 'emotion'):
349
  # Some InsightFace models provide emotion
 
464
  Returns:
465
  Tuple of (optimal_width, optimal_height) as multiples of 64
466
  """
467
+ # Ensure pure Python ints
468
+ original_width = ensure_int(original_width)
469
+ original_height = ensure_int(original_height)
470
+
471
  aspect_ratio = original_width / original_height
472
 
473
  # Legacy mode: use recommended sizes if provided
 
565
  return face_crop_final
566
 
567
 
568
+ print("[OK] Utilities loaded with type safety helpers")