andevs commited on
Commit
cc1d1af
·
verified ·
1 Parent(s): a6ceac4

Create contentClassifier.py

Browse files
Files changed (1) hide show
  1. contentClassifier.py +176 -0
contentClassifier.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # contentClassifier.py - Lightweight Content Detection
2
+ # FROZEN - DO NOT MODIFY
3
+
4
+ import numpy as np
5
+ from PIL import Image
6
+ from collections import Counter
7
+
8
+ class ContentClassifier:
9
+ """Lightweight content detection - FROZEN."""
10
+
11
+ def __init__(self):
12
+ self.categories = [
13
+ 'simple_graphic', 'human_hair', 'human', 'anime',
14
+ 'logo_icon', 'product_white_bg', 'general_photo', 'complex'
15
+ ]
16
+
17
+ def classify(self, image: Image.Image) -> dict:
18
+ """Classify image content."""
19
+ if image.mode != 'RGB':
20
+ image = image.convert('RGB')
21
+
22
+ small = image.copy()
23
+ small.thumbnail((150, 150), Image.Resampling.LANCZOS)
24
+ np_img = np.array(small)
25
+ h, w = np_img.shape[:2]
26
+
27
+ signals = {
28
+ 'dimensions': (w, h),
29
+ 'aspect_ratio': h / w if w > 0 else 0,
30
+ 'border_uniformity': self._border_uniformity(np_img),
31
+ 'edge_density': self._edge_density(np_img),
32
+ 'color_complexity': self._color_complexity(small),
33
+ 'skin_score': self._skin_score(np_img),
34
+ 'border_connected': self._border_connected(np_img),
35
+ 'texture': self._texture(np_img)
36
+ }
37
+
38
+ category = self._classify(signals)
39
+ confidence = self._calculate_confidence(category, signals)
40
+
41
+ return {
42
+ 'category': category,
43
+ 'confidence': confidence,
44
+ 'signals': signals
45
+ }
46
+
47
+ def _border_uniformity(self, np_img: np.ndarray) -> float:
48
+ h, w = np_img.shape[:2]
49
+ border_pixels = []
50
+ step = max(1, min(h, w) // 10)
51
+
52
+ for x in range(0, w, step):
53
+ border_pixels.append(tuple(np_img[0, x][:3]))
54
+ border_pixels.append(tuple(np_img[h-1, x][:3]))
55
+ for y in range(0, h, step):
56
+ border_pixels.append(tuple(np_img[y, 0][:3]))
57
+ border_pixels.append(tuple(np_img[y, w-1][:3]))
58
+
59
+ if not border_pixels:
60
+ return 0.0
61
+
62
+ unique = len(set(border_pixels))
63
+ return 1 - (unique / len(border_pixels))
64
+
65
+ def _edge_density(self, np_img: np.ndarray) -> float:
66
+ gray = np.mean(np_img, axis=2).astype(np.float32)
67
+ grad_x = np.abs(gray[:, 1:] - gray[:, :-1])
68
+ grad_y = np.abs(gray[1:, :] - gray[:-1, :])
69
+
70
+ edges = (grad_x > 25).sum() + (grad_y > 25).sum()
71
+ total = (gray.shape[0] - 1) * gray.shape[1] + gray.shape[0] * (gray.shape[1] - 1)
72
+
73
+ return edges / total if total > 0 else 0
74
+
75
+ def _color_complexity(self, image: Image.Image) -> float:
76
+ quantized = image.quantize(colors=32)
77
+ unique = len(quantized.getcolors())
78
+ return min(1.0, unique / 32)
79
+
80
+ def _skin_score(self, np_img: np.ndarray) -> float:
81
+ h, w = np_img.shape[:2]
82
+ pixels = []
83
+ step = max(1, min(h, w) // 5)
84
+ for y in range(0, h, step):
85
+ for x in range(0, w, step):
86
+ pixels.append(np_img[y, x][:3])
87
+
88
+ skin = 0
89
+ for r, g, b in pixels:
90
+ if (r > 60 and g > 40 and b > 20 and
91
+ r > g and r > b and
92
+ abs(r - g) < 60 and abs(r - b) < 60):
93
+ skin += 1
94
+
95
+ return skin / len(pixels) if pixels else 0
96
+
97
+ def _border_connected(self, np_img: np.ndarray) -> float:
98
+ h, w = np_img.shape[:2]
99
+ border_colors = set()
100
+ border_colors.add(tuple(np_img[0, 0][:3]))
101
+ border_colors.add(tuple(np_img[0, w-1][:3]))
102
+ border_colors.add(tuple(np_img[h-1, 0][:3]))
103
+ border_colors.add(tuple(np_img[h-1, w-1][:3]))
104
+
105
+ interior_colors = set()
106
+ step = max(1, min(h, w) // 4)
107
+ for y in range(h//4, 3*h//4, step):
108
+ for x in range(w//4, 3*w//4, step):
109
+ interior_colors.add(tuple(np_img[y, x][:3]))
110
+
111
+ overlap = border_colors & interior_colors
112
+ return len(overlap) / max(len(border_colors), 1)
113
+
114
+ def _texture(self, np_img: np.ndarray) -> float:
115
+ gray = np.mean(np_img, axis=2).astype(np.float32)
116
+ variance = np.var(gray)
117
+ return min(1.0, variance / 5000)
118
+
119
+ def _classify(self, signals: dict) -> str:
120
+ s = signals
121
+
122
+ # Simple graphic
123
+ if (s['color_complexity'] < 0.3 and
124
+ s['edge_density'] > 0.3 and
125
+ s['border_uniformity'] > 0.7):
126
+ return 'simple_graphic'
127
+
128
+ # Human with hair
129
+ if (s['skin_score'] > 0.15 and
130
+ s['texture'] > 0.3 and
131
+ s['edge_density'] > 0.2):
132
+ return 'human_hair'
133
+
134
+ # Human
135
+ if s['skin_score'] > 0.1:
136
+ return 'human'
137
+
138
+ # Anime
139
+ if (s['skin_score'] < 0.08 and
140
+ s['color_complexity'] > 0.3 and
141
+ s['texture'] < 0.3 and
142
+ s['edge_density'] > 0.3):
143
+ return 'anime'
144
+
145
+ # Logo/icon
146
+ if (s['edge_density'] > 0.4 and
147
+ s['color_complexity'] < 0.3 and
148
+ s['border_uniformity'] > 0.6):
149
+ return 'logo_icon'
150
+
151
+ # Product on white bg
152
+ if (s['border_uniformity'] > 0.6 and
153
+ s['color_complexity'] < 0.5 and
154
+ s['edge_density'] < 0.4):
155
+ return 'product_white_bg'
156
+
157
+ # General photo
158
+ if s['color_complexity'] > 0.3:
159
+ return 'general_photo'
160
+
161
+ return 'complex'
162
+
163
+ def _calculate_confidence(self, category: str, signals: dict) -> float:
164
+ base = 0.7
165
+
166
+ if category == 'simple_graphic':
167
+ if signals['border_uniformity'] > 0.8:
168
+ base += 0.2
169
+ elif category == 'human_hair':
170
+ if signals['skin_score'] > 0.2 and signals['texture'] > 0.4:
171
+ base += 0.2
172
+ elif category == 'human':
173
+ if signals['skin_score'] > 0.15:
174
+ base += 0.2
175
+
176
+ return min(1.0, base)