kabancov_et commited on
Commit
eea39e9
·
1 Parent(s): 5383c97

� Optimize color analysis performance: add caching, smart image resizing, and faster KMeans

Browse files
Files changed (2) hide show
  1. clothing_detector.py +16 -0
  2. process.py +83 -12
clothing_detector.py CHANGED
@@ -839,6 +839,22 @@ class ClothingDetector:
839
  # Load original image directly from bytes
840
  original_image = Image.open(BytesIO(original_image_bytes))
841
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
842
  # Create mask for selected clothing or all clothing
843
  if selected_clothing:
844
  # Find class ID for selected clothing
 
839
  # Load original image directly from bytes
840
  original_image = Image.open(BytesIO(original_image_bytes))
841
 
842
+ # Optimize image size for faster color analysis while maintaining quality
843
+ # Large images can slow down color analysis significantly
844
+ if original_image.width > 800 or original_image.height > 800:
845
+ # Calculate optimal size (balance between quality and speed)
846
+ max_dim = max(original_image.width, original_image.height)
847
+ if max_dim > 2000:
848
+ target_size = (800, 800) # Very large images
849
+ elif max_dim > 1200:
850
+ target_size = (1000, 1000) # Large images
851
+ else:
852
+ target_size = (1200, 1200) # Medium-large images
853
+
854
+ # Resize while maintaining aspect ratio
855
+ original_image.thumbnail(target_size, Image.LANCZOS)
856
+ logger.info(f"🔄 Optimized image size from {original_image.width}x{original_image.height} to {target_size[0]}x{target_size[1]} for faster processing")
857
+
858
  # Create mask for selected clothing or all clothing
859
  if selected_clothing:
860
  # Find class ID for selected clothing
process.py CHANGED
@@ -2,6 +2,8 @@ from PIL import Image
2
  from io import BytesIO
3
  from sklearn.cluster import KMeans
4
  import base64
 
 
5
 
6
  import os
7
  import uuid
@@ -11,6 +13,30 @@ import numpy as np
11
  from rembg import remove
12
  REMBG_AVAILABLE = True
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  def get_dominant_color(processed_bytes, k=3):
16
  # Step 1: load transparent image
@@ -32,8 +58,18 @@ def get_dominant_color(processed_bytes, k=3):
32
 
33
 
34
  def get_dominant_color_from_base64(base64_image, k=3):
35
- """Compute dominant color from base64-encoded clothing-only image."""
36
  try:
 
 
 
 
 
 
 
 
 
 
37
  # Step 1: Decode base64 to bytes
38
  if base64_image.startswith('data:image'):
39
  # Remove data URL prefix
@@ -45,9 +81,26 @@ def get_dominant_color_from_base64(base64_image, k=3):
45
 
46
  # Step 2: Load image and convert to RGBA
47
  image = Image.open(BytesIO(image_bytes)).convert("RGBA")
48
- image = image.resize((100, 100)) # Resize to speed up
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- # Step 3: Filter only visible (non-transparent) pixels
51
  np_image = np.array(image)
52
  rgb_pixels = np_image[...,:3] # Ignore alpha channel
53
  alpha = np_image[..., 3]
@@ -55,17 +108,35 @@ def get_dominant_color_from_base64(base64_image, k=3):
55
 
56
  # Check if we have any visible pixels
57
  if len(rgb_pixels) == 0:
58
- return "rgb(0, 0, 0)" # Fallback to black if no visible pixels
59
-
60
- # Step 4: KMeans clustering
61
- kmeans = KMeans(n_clusters=k, n_init='auto')
62
- kmeans.fit(rgb_pixels)
63
- dominant_color = kmeans.cluster_centers_[0]
64
- r, g, b = map(int, dominant_color)
65
- return f"rgb({r}, {g}, {b})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
  except Exception as e:
68
- print(f"Error in get_dominant_color_from_base64: {e}")
69
  return "rgb(0, 0, 0)" # Fallback to black on error
70
 
71
 
 
2
  from io import BytesIO
3
  from sklearn.cluster import KMeans
4
  import base64
5
+ import hashlib
6
+ import time
7
 
8
  import os
9
  import uuid
 
13
  from rembg import remove
14
  REMBG_AVAILABLE = True
15
 
16
+ # Cache for dominant colors (image_hash -> color_result)
17
+ _color_cache = {}
18
+ _cache_ttl = 3600 # 1 hour TTL
19
+
20
+ def _get_image_hash_from_base64(base64_image):
21
+ """Create hash from base64 image for caching."""
22
+ if base64_image.startswith('data:image'):
23
+ base64_data = base64_image.split(',')[1]
24
+ else:
25
+ base64_data = base64_image
26
+ return hashlib.md5(base64_data.encode()).hexdigest()
27
+
28
+ def _cleanup_color_cache():
29
+ """Remove expired cache entries."""
30
+ global _color_cache
31
+ current_time = time.time()
32
+ expired_keys = [
33
+ key for key, (_, timestamp) in _color_cache.items()
34
+ if current_time - timestamp > _cache_ttl
35
+ ]
36
+ for key in expired_keys:
37
+ del _color_cache[key]
38
+ if expired_keys:
39
+ print(f"Cleaned up {len(expired_keys)} expired color cache entries")
40
 
41
  def get_dominant_color(processed_bytes, k=3):
42
  # Step 1: load transparent image
 
58
 
59
 
60
  def get_dominant_color_from_base64(base64_image, k=3):
61
+ """Compute dominant color from base64-encoded clothing-only image with caching."""
62
  try:
63
+ # Check cache first
64
+ image_hash = _get_image_hash_from_base64(base64_image)
65
+ if image_hash in _color_cache:
66
+ color_result, timestamp = _color_cache[image_hash]
67
+ if time.time() - timestamp < _cache_ttl:
68
+ print(f"🎨 Using cached color result for hash: {image_hash[:8]}...")
69
+ return color_result
70
+
71
+ print(f"🎨 Computing dominant color for new image (hash: {image_hash[:8]}...)")
72
+
73
  # Step 1: Decode base64 to bytes
74
  if base64_image.startswith('data:image'):
75
  # Remove data URL prefix
 
81
 
82
  # Step 2: Load image and convert to RGBA
83
  image = Image.open(BytesIO(image_bytes)).convert("RGBA")
84
+
85
+ # Step 3: Optimize size for faster processing
86
+ # Use smaller size for very large images, but keep reasonable quality
87
+ if image.width > 200 or image.height > 200:
88
+ # Calculate optimal size (balance between speed and quality)
89
+ max_dim = max(image.width, image.height)
90
+ if max_dim > 1000:
91
+ target_size = (150, 150) # Very large images
92
+ elif max_dim > 500:
93
+ target_size = (200, 200) # Large images
94
+ else:
95
+ target_size = (100, 100) # Medium images
96
+
97
+ image = image.resize(target_size, Image.LANCZOS)
98
+ print(f"🔄 Resized image from {image.width}x{image.height} to {target_size[0]}x{target_size[1]} for faster processing")
99
+ else:
100
+ # Small images - resize to standard size for consistency
101
+ image = image.resize((100, 100), Image.LANCZOS)
102
 
103
+ # Step 4: Filter only visible (non-transparent) pixels
104
  np_image = np.array(image)
105
  rgb_pixels = np_image[...,:3] # Ignore alpha channel
106
  alpha = np_image[..., 3]
 
108
 
109
  # Check if we have any visible pixels
110
  if len(rgb_pixels) == 0:
111
+ result = "rgb(0, 0, 0)" # Fallback to black if no visible pixels
112
+ else:
113
+ # Step 5: Optimized KMeans clustering
114
+ # Use fewer clusters for faster processing on smaller datasets
115
+ actual_k = min(k, len(rgb_pixels) // 10) # Ensure we have enough pixels per cluster
116
+ if actual_k < 1:
117
+ actual_k = 1
118
+
119
+ # Use faster KMeans settings
120
+ kmeans = KMeans(
121
+ n_clusters=actual_k,
122
+ n_init=1, # Single initialization for speed
123
+ max_iter=100, # Limit iterations
124
+ random_state=42 # Deterministic results
125
+ )
126
+ kmeans.fit(rgb_pixels)
127
+ dominant_color = kmeans.cluster_centers_[0]
128
+ r, g, b = map(int, dominant_color)
129
+ result = f"rgb({r}, {g}, {b})"
130
+
131
+ # Cache the result
132
+ _color_cache[image_hash] = (result, time.time())
133
+ _cleanup_color_cache() # Clean up expired entries
134
+
135
+ print(f"✅ Color analysis completed: {result}")
136
+ return result
137
 
138
  except Exception as e:
139
+ print(f"Error in get_dominant_color_from_base64: {e}")
140
  return "rgb(0, 0, 0)" # Fallback to black on error
141
 
142