Download contentClassifier.py from andevs/bgremove: direct link, hf CLI and curl.
- Browser
- Download file 6.59 kB
-
https://huggingface.co/spaces/andevs/bgremove/resolve/main/contentClassifier.py
- Command line
-
hf download hf://spaces/andevs/bgremove/contentClassifier.py
-
curl -L -o contentClassifier.py https://huggingface.co/spaces/andevs/bgremove/resolve/main/contentClassifier.py
6.59 kB
| # contentClassifier.py - Lightweight Content Detection | |
| # FROZEN - DO NOT MODIFY | |
| # NOTE: Thresholds tuned 2026-01 to fix edge_density ratios. | |
| # A red circle at 150x150 has edge_density ~0.02-0.05, NOT 0.4. | |
| import numpy as np | |
| from PIL import Image | |
| from collections import Counter | |
| class ContentClassifier: | |
| """Lightweight content detection - FROZEN.""" | |
| def __init__(self): | |
| self.categories = [ | |
| 'simple_graphic', 'human_hair', 'human', 'anime', | |
| 'logo_icon', 'product_white_bg', 'general_photo', 'complex' | |
| ] | |
| def classify(self, image: Image.Image) -> dict: | |
| """Classify image content.""" | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| small = image.copy() | |
| small.thumbnail((150, 150), Image.Resampling.LANCZOS) | |
| np_img = np.array(small) | |
| h, w = np_img.shape[:2] | |
| signals = { | |
| 'dimensions': (w, h), | |
| 'aspect_ratio': h / w if w > 0 else 0, | |
| 'border_uniformity': self._border_uniformity(np_img), | |
| 'edge_density': self._edge_density(np_img), | |
| 'color_complexity': self._color_complexity(small), | |
| 'skin_score': self._skin_score(np_img), | |
| 'border_connected': self._border_connected(np_img), | |
| 'texture': self._texture(np_img) | |
| } | |
| category = self._classify(signals) | |
| confidence = self._calculate_confidence(category, signals) | |
| return { | |
| 'category': category, | |
| 'confidence': confidence, | |
| 'signals': signals | |
| } | |
| def _border_uniformity(self, np_img: np.ndarray) -> float: | |
| h, w = np_img.shape[:2] | |
| border_pixels = [] | |
| step = max(1, min(h, w) // 10) | |
| for x in range(0, w, step): | |
| border_pixels.append(tuple(np_img[0, x][:3])) | |
| border_pixels.append(tuple(np_img[h - 1, x][:3])) | |
| for y in range(0, h, step): | |
| border_pixels.append(tuple(np_img[y, 0][:3])) | |
| border_pixels.append(tuple(np_img[y, w - 1][:3])) | |
| if not border_pixels: | |
| return 0.0 | |
| unique = len(set(border_pixels)) | |
| return 1 - (unique / len(border_pixels)) | |
| def _edge_density(self, np_img: np.ndarray) -> float: | |
| gray = np.mean(np_img, axis=2).astype(np.float32) | |
| grad_x = np.abs(gray[:, 1:] - gray[:, :-1]) | |
| grad_y = np.abs(gray[1:, :] - gray[:-1, :]) | |
| edges = (grad_x > 25).sum() + (grad_y > 25).sum() | |
| total = (gray.shape[0] - 1) * gray.shape[1] + gray.shape[0] * (gray.shape[1] - 1) | |
| return edges / total if total > 0 else 0 | |
| def _color_complexity(self, image: Image.Image) -> float: | |
| quantized = image.quantize(colors=32) | |
| unique = len(quantized.getcolors()) | |
| return min(1.0, unique / 32) | |
| def _skin_score(self, np_img: np.ndarray) -> float: | |
| h, w = np_img.shape[:2] | |
| pixels = [] | |
| step = max(1, min(h, w) // 5) | |
| for y in range(0, h, step): | |
| for x in range(0, w, step): | |
| pixels.append(np_img[y, x][:3]) | |
| skin = 0 | |
| for r, g, b in pixels: | |
| if (r > 60 and g > 40 and b > 20 and | |
| r > g and r > b and | |
| abs(r - g) < 60 and abs(r - b) < 60): | |
| skin += 1 | |
| return skin / len(pixels) if pixels else 0 | |
| def _border_connected(self, np_img: np.ndarray) -> float: | |
| h, w = np_img.shape[:2] | |
| border_colors = set() | |
| border_colors.add(tuple(np_img[0, 0][:3])) | |
| border_colors.add(tuple(np_img[0, w - 1][:3])) | |
| border_colors.add(tuple(np_img[h - 1, 0][:3])) | |
| border_colors.add(tuple(np_img[h - 1, w - 1][:3])) | |
| interior_colors = set() | |
| step = max(1, min(h, w) // 4) | |
| for y in range(h // 4, 3 * h // 4, step): | |
| for x in range(w // 4, 3 * w // 4, step): | |
| interior_colors.add(tuple(np_img[y, x][:3])) | |
| overlap = border_colors & interior_colors | |
| return len(overlap) / max(len(border_colors), 1) | |
| def _texture(self, np_img: np.ndarray) -> float: | |
| gray = np.mean(np_img, axis=2).astype(np.float32) | |
| variance = np.var(gray) | |
| return min(1.0, variance / 5000) | |
| def _classify(self, signals: dict) -> str: | |
| s = signals | |
| # ===== SIMPLE GRAPHIC (circles, shapes, solid-background subjects) ===== | |
| # Lowered edge_density thresholds so simple shapes route here. | |
| # NOTE: 0.4 threshold was never reached on typical images. | |
| if (s['color_complexity'] < 0.35 and | |
| s['edge_density'] > 0.05 and | |
| s['border_uniformity'] > 0.6): | |
| return 'simple_graphic' | |
| # ===== LOGO / ICON (flat, low-colour, high-contrast) ===== | |
| if (s['color_complexity'] < 0.35 and | |
| s['edge_density'] > 0.10 and | |
| s['border_uniformity'] > 0.5 and | |
| s['texture'] < 0.3): | |
| return 'logo_icon' | |
| # ===== HUMAN WITH HAIR ===== | |
| if (s['skin_score'] > 0.12 and | |
| s['texture'] > 0.25 and | |
| s['edge_density'] > 0.04): | |
| return 'human_hair' | |
| # ===== HUMAN (SIMPLE) ===== | |
| if s['skin_score'] > 0.08: | |
| return 'human' | |
| # ===== ANIME ===== | |
| if (s['skin_score'] < 0.10 and | |
| s['color_complexity'] > 0.25 and | |
| s['texture'] < 0.35 and | |
| s['edge_density'] > 0.08): | |
| return 'anime' | |
| # ===== PRODUCT ON WHITE BG ===== | |
| if (s['border_uniformity'] > 0.55 and | |
| s['color_complexity'] < 0.55 and | |
| s['edge_density'] < 0.35): | |
| return 'product_white_bg' | |
| # ===== GENERAL PHOTO ===== | |
| if s['color_complexity'] > 0.3: | |
| return 'general_photo' | |
| return 'complex' | |
| def _calculate_confidence(self, category: str, signals: dict) -> float: | |
| base = 0.7 | |
| if category == 'simple_graphic': | |
| if signals['border_uniformity'] > 0.8: | |
| base += 0.2 | |
| elif category == 'human_hair': | |
| if signals['skin_score'] > 0.2 and signals['texture'] > 0.4: | |
| base += 0.2 | |
| elif category == 'human': | |
| if signals['skin_score'] > 0.15: | |
| base += 0.2 | |
| elif category == 'logo_icon': | |
| if signals['border_uniformity'] > 0.7: | |
| base += 0.15 | |
| elif category == 'product_white_bg': | |
| if signals['border_uniformity'] > 0.7: | |
| base += 0.15 | |
| return min(1.0, base) |