Spaces:
Running
on
Zero
Running
on
Zero
Delete color_mapper.py
Browse files- color_mapper.py +0 -270
color_mapper.py
DELETED
@@ -1,270 +0,0 @@
|
|
1 |
-
import numpy as np
|
2 |
-
from typing import Dict, List, Tuple, Union, Any
|
3 |
-
|
4 |
-
class ColorMapper:
|
5 |
-
"""
|
6 |
-
A class for consistent color mapping of object detection classes
|
7 |
-
Provides color schemes for visualization in both RGB and hex formats
|
8 |
-
"""
|
9 |
-
|
10 |
-
# Class categories for better organization
|
11 |
-
CATEGORIES = {
|
12 |
-
"person": [0],
|
13 |
-
"vehicles": [1, 2, 3, 4, 5, 6, 7, 8],
|
14 |
-
"traffic": [9, 10, 11, 12],
|
15 |
-
"animals": [14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
16 |
-
"outdoor": [13, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33],
|
17 |
-
"sports": [34, 35, 36, 37, 38],
|
18 |
-
"kitchen": [39, 40, 41, 42, 43, 44, 45],
|
19 |
-
"food": [46, 47, 48, 49, 50, 51, 52, 53, 54, 55],
|
20 |
-
"furniture": [56, 57, 58, 59, 60, 61],
|
21 |
-
"electronics": [62, 63, 64, 65, 66, 67, 68, 69, 70],
|
22 |
-
"household": [71, 72, 73, 74, 75, 76, 77, 78, 79]
|
23 |
-
}
|
24 |
-
|
25 |
-
# Base colors for each category (in HSV for easier variation)
|
26 |
-
CATEGORY_COLORS = {
|
27 |
-
"person": (0, 0.8, 0.9), # Red
|
28 |
-
"vehicles": (210, 0.8, 0.9), # Blue
|
29 |
-
"traffic": (45, 0.8, 0.9), # Orange
|
30 |
-
"animals": (120, 0.7, 0.8), # Green
|
31 |
-
"outdoor": (180, 0.7, 0.9), # Cyan
|
32 |
-
"sports": (270, 0.7, 0.8), # Purple
|
33 |
-
"kitchen": (30, 0.7, 0.9), # Light Orange
|
34 |
-
"food": (330, 0.7, 0.85), # Pink
|
35 |
-
"furniture": (150, 0.5, 0.85), # Light Green
|
36 |
-
"electronics": (240, 0.6, 0.9), # Light Blue
|
37 |
-
"household": (60, 0.6, 0.9) # Yellow
|
38 |
-
}
|
39 |
-
|
40 |
-
def __init__(self):
|
41 |
-
"""Initialize the ColorMapper with COCO class mappings"""
|
42 |
-
self.class_names = self._get_coco_classes()
|
43 |
-
self.color_map = self._generate_color_map()
|
44 |
-
|
45 |
-
def _get_coco_classes(self) -> Dict[int, str]:
|
46 |
-
"""Get the standard COCO class names with their IDs"""
|
47 |
-
return {
|
48 |
-
0: 'person', 1: 'bicycle', 2: 'car', 3: 'motorcycle', 4: 'airplane',
|
49 |
-
5: 'bus', 6: 'train', 7: 'truck', 8: 'boat', 9: 'traffic light',
|
50 |
-
10: 'fire hydrant', 11: 'stop sign', 12: 'parking meter', 13: 'bench',
|
51 |
-
14: 'bird', 15: 'cat', 16: 'dog', 17: 'horse', 18: 'sheep', 19: 'cow',
|
52 |
-
20: 'elephant', 21: 'bear', 22: 'zebra', 23: 'giraffe', 24: 'backpack',
|
53 |
-
25: 'umbrella', 26: 'handbag', 27: 'tie', 28: 'suitcase', 29: 'frisbee',
|
54 |
-
30: 'skis', 31: 'snowboard', 32: 'sports ball', 33: 'kite', 34: 'baseball bat',
|
55 |
-
35: 'baseball glove', 36: 'skateboard', 37: 'surfboard', 38: 'tennis racket',
|
56 |
-
39: 'bottle', 40: 'wine glass', 41: 'cup', 42: 'fork', 43: 'knife',
|
57 |
-
44: 'spoon', 45: 'bowl', 46: 'banana', 47: 'apple', 48: 'sandwich',
|
58 |
-
49: 'orange', 50: 'broccoli', 51: 'carrot', 52: 'hot dog', 53: 'pizza',
|
59 |
-
54: 'donut', 55: 'cake', 56: 'chair', 57: 'couch', 58: 'potted plant',
|
60 |
-
59: 'bed', 60: 'dining table', 61: 'toilet', 62: 'tv', 63: 'laptop',
|
61 |
-
64: 'mouse', 65: 'remote', 66: 'keyboard', 67: 'cell phone', 68: 'microwave',
|
62 |
-
69: 'oven', 70: 'toaster', 71: 'sink', 72: 'refrigerator', 73: 'book',
|
63 |
-
74: 'clock', 75: 'vase', 76: 'scissors', 77: 'teddy bear', 78: 'hair drier',
|
64 |
-
79: 'toothbrush'
|
65 |
-
}
|
66 |
-
|
67 |
-
def _hsv_to_rgb(self, h: float, s: float, v: float) -> Tuple[int, int, int]:
|
68 |
-
"""
|
69 |
-
Convert HSV color to RGB
|
70 |
-
|
71 |
-
Args:
|
72 |
-
h: Hue (0-360)
|
73 |
-
s: Saturation (0-1)
|
74 |
-
v: Value (0-1)
|
75 |
-
|
76 |
-
Returns:
|
77 |
-
Tuple of (R, G, B) values (0-255)
|
78 |
-
"""
|
79 |
-
h = h / 60
|
80 |
-
i = int(h)
|
81 |
-
f = h - i
|
82 |
-
p = v * (1 - s)
|
83 |
-
q = v * (1 - s * f)
|
84 |
-
t = v * (1 - s * (1 - f))
|
85 |
-
|
86 |
-
if i == 0:
|
87 |
-
r, g, b = v, t, p
|
88 |
-
elif i == 1:
|
89 |
-
r, g, b = q, v, p
|
90 |
-
elif i == 2:
|
91 |
-
r, g, b = p, v, t
|
92 |
-
elif i == 3:
|
93 |
-
r, g, b = p, q, v
|
94 |
-
elif i == 4:
|
95 |
-
r, g, b = t, p, v
|
96 |
-
else:
|
97 |
-
r, g, b = v, p, q
|
98 |
-
|
99 |
-
return (int(r * 255), int(g * 255), int(b * 255))
|
100 |
-
|
101 |
-
def _rgb_to_hex(self, rgb: Tuple[int, int, int]) -> str:
|
102 |
-
"""
|
103 |
-
Convert RGB color to hex color code
|
104 |
-
|
105 |
-
Args:
|
106 |
-
rgb: Tuple of (R, G, B) values (0-255)
|
107 |
-
|
108 |
-
Returns:
|
109 |
-
Hex color code (e.g. '#FF0000')
|
110 |
-
"""
|
111 |
-
return f'#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}'
|
112 |
-
|
113 |
-
def _find_category(self, class_id: int) -> str:
|
114 |
-
"""
|
115 |
-
Find the category for a given class ID
|
116 |
-
|
117 |
-
Args:
|
118 |
-
class_id: Class ID (0-79)
|
119 |
-
|
120 |
-
Returns:
|
121 |
-
Category name
|
122 |
-
"""
|
123 |
-
for category, ids in self.CATEGORIES.items():
|
124 |
-
if class_id in ids:
|
125 |
-
return category
|
126 |
-
return "other" # Fallback
|
127 |
-
|
128 |
-
def _generate_color_map(self) -> Dict:
|
129 |
-
"""
|
130 |
-
Generate a color map for all 80 COCO classes
|
131 |
-
|
132 |
-
Returns:
|
133 |
-
Dictionary mapping class IDs and names to color values
|
134 |
-
"""
|
135 |
-
color_map = {
|
136 |
-
'by_id': {}, # Map class ID to RGB and hex
|
137 |
-
'by_name': {}, # Map class name to RGB and hex
|
138 |
-
'categories': {} # Map category to base color
|
139 |
-
}
|
140 |
-
|
141 |
-
# Generate colors for categories
|
142 |
-
for category, hsv in self.CATEGORY_COLORS.items():
|
143 |
-
rgb = self._hsv_to_rgb(hsv[0], hsv[1], hsv[2])
|
144 |
-
hex_color = self._rgb_to_hex(rgb)
|
145 |
-
color_map['categories'][category] = {
|
146 |
-
'rgb': rgb,
|
147 |
-
'hex': hex_color
|
148 |
-
}
|
149 |
-
|
150 |
-
# Generate variations for each class within a category
|
151 |
-
for class_id, class_name in self.class_names.items():
|
152 |
-
category = self._find_category(class_id)
|
153 |
-
base_hsv = self.CATEGORY_COLORS.get(category, (0, 0, 0.8)) # Default gray
|
154 |
-
|
155 |
-
# Slightly vary the hue and saturation within the category
|
156 |
-
ids_in_category = self.CATEGORIES.get(category, [])
|
157 |
-
if ids_in_category:
|
158 |
-
position = ids_in_category.index(class_id) if class_id in ids_in_category else 0
|
159 |
-
variation = position / max(1, len(ids_in_category) - 1) # 0 to 1
|
160 |
-
|
161 |
-
# Vary hue slightly (±15°) and saturation
|
162 |
-
h_offset = 30 * variation - 15 # -15 to +15
|
163 |
-
s_offset = 0.2 * variation # 0 to 0.2
|
164 |
-
|
165 |
-
h = (base_hsv[0] + h_offset) % 360
|
166 |
-
s = min(1.0, base_hsv[1] + s_offset)
|
167 |
-
v = base_hsv[2]
|
168 |
-
else:
|
169 |
-
h, s, v = base_hsv
|
170 |
-
|
171 |
-
rgb = self._hsv_to_rgb(h, s, v)
|
172 |
-
hex_color = self._rgb_to_hex(rgb)
|
173 |
-
|
174 |
-
# Store in both mappings
|
175 |
-
color_map['by_id'][class_id] = {
|
176 |
-
'rgb': rgb,
|
177 |
-
'hex': hex_color,
|
178 |
-
'category': category
|
179 |
-
}
|
180 |
-
|
181 |
-
color_map['by_name'][class_name] = {
|
182 |
-
'rgb': rgb,
|
183 |
-
'hex': hex_color,
|
184 |
-
'category': category
|
185 |
-
}
|
186 |
-
|
187 |
-
return color_map
|
188 |
-
|
189 |
-
def get_color(self, class_identifier: Union[int, str], format: str = 'hex') -> Any:
|
190 |
-
"""
|
191 |
-
Get color for a specific class
|
192 |
-
|
193 |
-
Args:
|
194 |
-
class_identifier: Class ID (int) or name (str)
|
195 |
-
format: Color format ('hex', 'rgb', or 'bgr')
|
196 |
-
|
197 |
-
Returns:
|
198 |
-
Color in requested format
|
199 |
-
"""
|
200 |
-
# Determine if identifier is an ID or name
|
201 |
-
if isinstance(class_identifier, int):
|
202 |
-
color_info = self.color_map['by_id'].get(class_identifier)
|
203 |
-
else:
|
204 |
-
color_info = self.color_map['by_name'].get(class_identifier)
|
205 |
-
|
206 |
-
if not color_info:
|
207 |
-
# Fallback color if not found
|
208 |
-
return '#CCCCCC' if format == 'hex' else (204, 204, 204)
|
209 |
-
|
210 |
-
if format == 'hex':
|
211 |
-
return color_info['hex']
|
212 |
-
elif format == 'rgb':
|
213 |
-
return color_info['rgb']
|
214 |
-
elif format == 'bgr':
|
215 |
-
# Convert RGB to BGR for OpenCV
|
216 |
-
r, g, b = color_info['rgb']
|
217 |
-
return (b, g, r)
|
218 |
-
else:
|
219 |
-
return color_info['rgb']
|
220 |
-
|
221 |
-
def get_all_colors(self, format: str = 'hex') -> Dict:
|
222 |
-
"""
|
223 |
-
Get all colors in the specified format
|
224 |
-
|
225 |
-
Args:
|
226 |
-
format: Color format ('hex', 'rgb', or 'bgr')
|
227 |
-
|
228 |
-
Returns:
|
229 |
-
Dictionary mapping class names to colors
|
230 |
-
"""
|
231 |
-
result = {}
|
232 |
-
for class_id, class_name in self.class_names.items():
|
233 |
-
result[class_name] = self.get_color(class_id, format)
|
234 |
-
return result
|
235 |
-
|
236 |
-
def get_category_colors(self, format: str = 'hex') -> Dict:
|
237 |
-
"""
|
238 |
-
Get base colors for each category
|
239 |
-
|
240 |
-
Args:
|
241 |
-
format: Color format ('hex', 'rgb', or 'bgr')
|
242 |
-
|
243 |
-
Returns:
|
244 |
-
Dictionary mapping categories to colors
|
245 |
-
"""
|
246 |
-
result = {}
|
247 |
-
for category, color_info in self.color_map['categories'].items():
|
248 |
-
if format == 'hex':
|
249 |
-
result[category] = color_info['hex']
|
250 |
-
elif format == 'bgr':
|
251 |
-
r, g, b = color_info['rgb']
|
252 |
-
result[category] = (b, g, r)
|
253 |
-
else:
|
254 |
-
result[category] = color_info['rgb']
|
255 |
-
return result
|
256 |
-
|
257 |
-
def get_category_for_class(self, class_identifier: Union[int, str]) -> str:
|
258 |
-
"""
|
259 |
-
Get the category for a specific class
|
260 |
-
|
261 |
-
Args:
|
262 |
-
class_identifier: Class ID (int) or name (str)
|
263 |
-
|
264 |
-
Returns:
|
265 |
-
Category name
|
266 |
-
"""
|
267 |
-
if isinstance(class_identifier, int):
|
268 |
-
return self.color_map['by_id'].get(class_identifier, {}).get('category', 'other')
|
269 |
-
else:
|
270 |
-
return self.color_map['by_name'].get(class_identifier, {}).get('category', 'other')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|