Instructions to use mlworks90/fashion-inpainting-system with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use mlworks90/fashion-inpainting-system with Diffusers:
pip install -U diffusers transformers accelerate
from diffusers import ControlNetModel, StableDiffusionControlNetPipeline controlnet = ControlNetModel.from_pretrained("mlworks90/fashion-inpainting-system") pipe = StableDiffusionControlNetPipeline.from_pretrained( "runwayml/stable-diffusion-v1-5", controlnet=controlnet ) - Notebooks
- Google Colab
- Kaggle
| """ | |
| BALANCED GENDER DETECTION FIX | |
| ============================= | |
| ISSUE IDENTIFIED: | |
| The current gender detection is heavily biased toward male detection: | |
| - "red evening dress" with woman source β generates man in dress | |
| - System defaults to male unless there are NO male indicators at all | |
| PROBLEM IN CURRENT CODE: | |
| - if male_score > 0.6: return male (reasonable) | |
| - elif male_score > 0.3: return male (TOO AGGRESSIVE) | |
| - else: return female (only as last resort) | |
| SOLUTION: | |
| - Balanced scoring system that considers both male AND female indicators | |
| - Proper thresholds for both genders | |
| - Better facial analysis that doesn't bias toward masculinity | |
| """ | |
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| from typing import Dict, Tuple, Optional | |
| import os | |
| class BalancedGenderDetector: | |
| """ | |
| BALANCED gender detection that works equally well for men and women | |
| Fixes the current bias toward male classification | |
| """ | |
| def __init__(self): | |
| self.face_cascade = self._load_face_cascade() | |
| print("π§ BALANCED Gender Detector initialized") | |
| print(" β Equal consideration for male and female features") | |
| print(" β Removes male bias from detection logic") | |
| print(" β Better thresholds for both genders") | |
| def _load_face_cascade(self): | |
| """Load face cascade""" | |
| try: | |
| cascade_paths = [ | |
| cv2.data.haarcascades + 'haarcascade_frontalface_default.xml', | |
| 'haarcascade_frontalface_default.xml' | |
| ] | |
| for path in cascade_paths: | |
| if os.path.exists(path): | |
| return cv2.CascadeClassifier(path) | |
| return None | |
| except Exception as e: | |
| print(f"β οΈ Error loading face cascade: {e}") | |
| return None | |
| def detect_gender_balanced(self, image_path: str) -> Dict: | |
| """ | |
| BALANCED gender detection from image | |
| Returns proper classification for both men and women | |
| """ | |
| print(f"π BALANCED gender detection: {os.path.basename(image_path)}") | |
| try: | |
| # Load image | |
| image = cv2.imread(image_path) | |
| if image is None: | |
| raise ValueError(f"Could not load image: {image_path}") | |
| # Detect face | |
| face_bbox = self._detect_main_face(image) | |
| if face_bbox is None: | |
| print(" β οΈ No face detected - using fallback analysis") | |
| return self._analyze_without_face(image) | |
| fx, fy, fw, fh = face_bbox | |
| print(f" β Face detected: {fw}x{fh} at ({fx}, {fy})") | |
| # Extract face region | |
| face_region = image[fy:fy+fh, fx:fx+fw] | |
| face_gray = cv2.cvtColor(face_region, cv2.COLOR_BGR2GRAY) | |
| # BALANCED analysis - consider both male AND female indicators | |
| male_indicators = self._analyze_male_indicators(face_region, face_gray, fw, fh) | |
| female_indicators = self._analyze_female_indicators(face_region, face_gray, fw, fh) | |
| # Make balanced decision | |
| gender_result = self._make_balanced_gender_decision(male_indicators, female_indicators) | |
| print(f" π Male indicators: {male_indicators['total_score']:.2f}") | |
| print(f" π Female indicators: {female_indicators['total_score']:.2f}") | |
| print(f" π― Final gender: {gender_result['gender']} (conf: {gender_result['confidence']:.2f})") | |
| return gender_result | |
| except Exception as e: | |
| print(f" β Gender detection failed: {e}") | |
| return { | |
| 'gender': 'neutral', | |
| 'confidence': 0.5, | |
| 'method': 'error_fallback' | |
| } | |
| def _detect_main_face(self, image: np.ndarray) -> Optional[Tuple[int, int, int, int]]: | |
| """Detect main face in image""" | |
| if self.face_cascade is None: | |
| return None | |
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
| faces = self.face_cascade.detectMultiScale(gray, 1.1, 4, minSize=(60, 60)) | |
| if len(faces) == 0: | |
| return None | |
| return tuple(max(faces, key=lambda x: x[2] * x[3])) | |
| def _analyze_male_indicators(self, face_region: np.ndarray, face_gray: np.ndarray, fw: int, fh: int) -> Dict: | |
| """ | |
| Analyze indicators that suggest MALE gender | |
| More conservative than the current overly aggressive detection | |
| """ | |
| male_score = 0.0 | |
| indicators = {} | |
| # 1. Face width-to-height ratio (men often have wider faces) | |
| aspect_ratio = fw / fh | |
| indicators['aspect_ratio'] = aspect_ratio | |
| if aspect_ratio > 0.90: # More conservative threshold (was 0.85) | |
| male_score += 0.2 | |
| indicators['wide_face'] = True | |
| else: | |
| indicators['wide_face'] = False | |
| # 2. Facial hair detection (strong male indicator when present) | |
| facial_hair_result = self._detect_facial_hair_conservative(face_gray, fw, fh) | |
| indicators['facial_hair'] = facial_hair_result | |
| if facial_hair_result['detected'] and facial_hair_result['confidence'] > 0.7: | |
| male_score += 0.4 # Strong indicator | |
| print(f" π¨ Strong facial hair detected (conf: {facial_hair_result['confidence']:.2f})") | |
| elif facial_hair_result['detected']: | |
| male_score += 0.2 # Weak indicator | |
| print(f" π¨ Weak facial hair detected (conf: {facial_hair_result['confidence']:.2f})") | |
| # 3. Jawline sharpness (men often have more defined jawlines) | |
| jawline_result = self._analyze_jawline_sharpness(face_gray, fh) | |
| indicators['jawline'] = jawline_result | |
| if jawline_result['sharpness'] > 0.2: # More conservative | |
| male_score += 0.15 | |
| # 4. Eyebrow thickness (men often have thicker eyebrows) | |
| eyebrow_result = self._analyze_eyebrow_thickness(face_gray, fw, fh) | |
| indicators['eyebrows'] = eyebrow_result | |
| if eyebrow_result['thickness'] > 0.6: | |
| male_score += 0.1 | |
| indicators['total_score'] = male_score | |
| return indicators | |
| def _analyze_female_indicators(self, face_region: np.ndarray, face_gray: np.ndarray, fw: int, fh: int) -> Dict: | |
| """ | |
| Analyze indicators that suggest FEMALE gender | |
| NEW: The current system doesn't properly look for female indicators! | |
| """ | |
| female_score = 0.0 | |
| indicators = {} | |
| # 1. Face shape analysis (women often have more oval faces) | |
| aspect_ratio = fw / fh | |
| indicators['aspect_ratio'] = aspect_ratio | |
| if 0.75 <= aspect_ratio <= 0.85: # More oval/narrow | |
| female_score += 0.2 | |
| indicators['oval_face'] = True | |
| else: | |
| indicators['oval_face'] = False | |
| # 2. Skin smoothness (women often have smoother skin texture) | |
| smoothness_result = self._analyze_skin_smoothness(face_gray) | |
| indicators['skin_smoothness'] = smoothness_result | |
| if smoothness_result['smoothness'] > 0.6: | |
| female_score += 0.25 | |
| elif smoothness_result['smoothness'] > 0.4: | |
| female_score += 0.15 | |
| # 3. Eye makeup detection (subtle indicator) | |
| eye_makeup_result = self._detect_subtle_makeup(face_region, fw, fh) | |
| indicators['makeup'] = eye_makeup_result | |
| if eye_makeup_result['likely_makeup']: | |
| female_score += 0.2 | |
| # 4. Hair length analysis (longer hair often indicates female) | |
| # This is done at image level, not face level | |
| hair_length_result = self._estimate_hair_length_from_face(face_region, fw, fh) | |
| indicators['hair_length'] = hair_length_result | |
| if hair_length_result['appears_long']: | |
| female_score += 0.15 | |
| # 5. Facial feature delicacy (women often have more delicate features) | |
| delicacy_result = self._analyze_feature_delicacy(face_gray, fw, fh) | |
| indicators['feature_delicacy'] = delicacy_result | |
| if delicacy_result['delicate_score'] > 0.5: | |
| female_score += 0.1 | |
| indicators['total_score'] = female_score | |
| return indicators | |
| def _detect_facial_hair_conservative(self, face_gray: np.ndarray, fw: int, fh: int) -> Dict: | |
| """ | |
| CONSERVATIVE facial hair detection | |
| The current system is too aggressive - detecting shadows as facial hair | |
| """ | |
| if fh < 60: # Face too small for reliable detection | |
| return {'detected': False, 'confidence': 0.0, 'method': 'face_too_small'} | |
| # Focus on mustache and beard areas | |
| mustache_region = face_gray[int(fh*0.55):int(fh*0.75), int(fw*0.3):int(fw*0.7)] | |
| beard_region = face_gray[int(fh*0.7):int(fh*0.95), int(fw*0.2):int(fw*0.8)] | |
| facial_hair_detected = False | |
| confidence = 0.0 | |
| # Mustache analysis | |
| if mustache_region.size > 0: | |
| mustache_mean = np.mean(mustache_region) | |
| mustache_std = np.std(mustache_region) | |
| dark_pixel_ratio = np.sum(mustache_region < mustache_mean - mustache_std) / mustache_region.size | |
| if dark_pixel_ratio > 0.25: # More conservative (was 0.15) | |
| facial_hair_detected = True | |
| confidence += 0.4 | |
| # Beard analysis | |
| if beard_region.size > 0: | |
| beard_mean = np.mean(beard_region) | |
| beard_std = np.std(beard_region) | |
| dark_pixel_ratio = np.sum(beard_region < beard_mean - beard_std) / beard_region.size | |
| if dark_pixel_ratio > 0.20: # More conservative | |
| facial_hair_detected = True | |
| confidence += 0.6 | |
| # Additional texture analysis for confirmation | |
| if facial_hair_detected: | |
| # Check for hair-like texture patterns | |
| combined_region = np.vstack([mustache_region, beard_region]) if mustache_region.size > 0 and beard_region.size > 0 else beard_region | |
| if combined_region.size > 0: | |
| texture_variance = cv2.Laplacian(combined_region, cv2.CV_64F).var() | |
| if texture_variance > 50: # Hair has texture | |
| confidence += 0.2 | |
| else: | |
| confidence *= 0.7 # Reduce confidence if no texture | |
| return { | |
| 'detected': facial_hair_detected, | |
| 'confidence': min(1.0, confidence), | |
| 'method': 'conservative_analysis' | |
| } | |
| def _analyze_jawline_sharpness(self, face_gray: np.ndarray, fh: int) -> Dict: | |
| """Analyze jawline sharpness""" | |
| if fh < 60: | |
| return {'sharpness': 0.0} | |
| # Focus on jawline area | |
| jaw_region = face_gray[int(fh*0.75):, :] | |
| if jaw_region.size == 0: | |
| return {'sharpness': 0.0} | |
| # Edge detection for jawline sharpness | |
| edges = cv2.Canny(jaw_region, 50, 150) | |
| sharpness = np.mean(edges) / 255.0 | |
| return {'sharpness': sharpness} | |
| def _analyze_eyebrow_thickness(self, face_gray: np.ndarray, fw: int, fh: int) -> Dict: | |
| """Analyze eyebrow thickness""" | |
| if fh < 60: | |
| return {'thickness': 0.0} | |
| # Eyebrow region | |
| eyebrow_region = face_gray[int(fh*0.25):int(fh*0.45), int(fw*0.2):int(fw*0.8)] | |
| if eyebrow_region.size == 0: | |
| return {'thickness': 0.0} | |
| # Look for dark horizontal structures (eyebrows) | |
| mean_brightness = np.mean(eyebrow_region) | |
| dark_threshold = mean_brightness - 20 | |
| dark_pixels = np.sum(eyebrow_region < dark_threshold) | |
| thickness = dark_pixels / eyebrow_region.size | |
| return {'thickness': thickness} | |
| def _analyze_skin_smoothness(self, face_gray: np.ndarray) -> Dict: | |
| """Analyze skin texture smoothness""" | |
| # Use Laplacian variance to measure texture | |
| texture_variance = cv2.Laplacian(face_gray, cv2.CV_64F).var() | |
| # Lower variance = smoother skin | |
| # Normalize to 0-1 scale (rough approximation) | |
| smoothness = max(0, 1.0 - (texture_variance / 500.0)) | |
| return {'smoothness': smoothness, 'texture_variance': texture_variance} | |
| def _detect_subtle_makeup(self, face_region: np.ndarray, fw: int, fh: int) -> Dict: | |
| """Detect subtle makeup indicators""" | |
| if len(face_region.shape) != 3 or fh < 60: | |
| return {'likely_makeup': False, 'confidence': 0.0} | |
| # Focus on eye area | |
| eye_region = face_region[int(fh*0.3):int(fh*0.55), int(fw*0.2):int(fw*0.8)] | |
| if eye_region.size == 0: | |
| return {'likely_makeup': False, 'confidence': 0.0} | |
| # Look for color enhancement around eyes | |
| eye_rgb = cv2.cvtColor(eye_region, cv2.COLOR_BGR2RGB) | |
| # Check for enhanced colors (makeup often increases color saturation) | |
| saturation = np.std(eye_rgb, axis=2) | |
| high_saturation_ratio = np.sum(saturation > np.percentile(saturation, 80)) / saturation.size | |
| likely_makeup = high_saturation_ratio > 0.15 | |
| confidence = min(1.0, high_saturation_ratio * 3) | |
| return {'likely_makeup': likely_makeup, 'confidence': confidence} | |
| def _estimate_hair_length_from_face(self, face_region: np.ndarray, fw: int, fh: int) -> Dict: | |
| """Estimate hair length from visible hair around face""" | |
| # This is a rough estimate based on hair visible around face edges | |
| # Check hair regions around face | |
| hair_regions = [] | |
| if len(face_region.shape) == 3: | |
| gray_face = cv2.cvtColor(face_region, cv2.COLOR_BGR2GRAY) | |
| else: | |
| gray_face = face_region | |
| # Check top region for hair | |
| top_region = gray_face[:int(fh*0.2), :] | |
| if top_region.size > 0: | |
| hair_regions.append(top_region) | |
| # Check side regions | |
| left_region = gray_face[:, :int(fw*0.15)] | |
| right_region = gray_face[:, int(fw*0.85):] | |
| if left_region.size > 0: | |
| hair_regions.append(left_region) | |
| if right_region.size > 0: | |
| hair_regions.append(right_region) | |
| # Analyze for hair-like texture | |
| total_hair_indicators = 0 | |
| total_regions = len(hair_regions) | |
| for region in hair_regions: | |
| if region.size > 10: # Enough pixels to analyze | |
| texture_var = np.var(region) | |
| # Hair typically has more texture variation than skin | |
| if texture_var > 200: # Has hair-like texture | |
| total_hair_indicators += 1 | |
| hair_ratio = total_hair_indicators / max(1, total_regions) | |
| appears_long = hair_ratio > 0.5 | |
| return { | |
| 'appears_long': appears_long, | |
| 'hair_ratio': hair_ratio, | |
| 'regions_analyzed': total_regions | |
| } | |
| def _analyze_feature_delicacy(self, face_gray: np.ndarray, fw: int, fh: int) -> Dict: | |
| """Analyze overall feature delicacy""" | |
| # Use edge detection to measure feature sharpness | |
| edges = cv2.Canny(face_gray, 30, 100) # Lower thresholds for subtle features | |
| # Delicate features have softer, less harsh edges | |
| edge_intensity = np.mean(edges) | |
| # Lower edge intensity = more delicate features | |
| delicate_score = max(0, 1.0 - (edge_intensity / 50.0)) | |
| return {'delicate_score': delicate_score, 'edge_intensity': edge_intensity} | |
| def _make_balanced_gender_decision(self, male_indicators: Dict, female_indicators: Dict) -> Dict: | |
| """ | |
| BALANCED gender decision based on both male AND female indicators | |
| FIXES the current bias toward male classification | |
| """ | |
| male_score = male_indicators['total_score'] | |
| female_score = female_indicators['total_score'] | |
| print(f" π Gender scoring: Male={male_score:.2f}, Female={female_score:.2f}") | |
| # Clear male indicators (high confidence) | |
| if male_score > 0.7 and male_score > female_score + 0.3: | |
| return { | |
| 'gender': 'male', | |
| 'confidence': min(0.95, 0.6 + male_score), | |
| 'method': 'strong_male_indicators', | |
| 'male_score': male_score, | |
| 'female_score': female_score | |
| } | |
| # Clear female indicators (high confidence) | |
| elif female_score > 0.7 and female_score > male_score + 0.3: | |
| return { | |
| 'gender': 'female', | |
| 'confidence': min(0.95, 0.6 + female_score), | |
| 'method': 'strong_female_indicators', | |
| 'male_score': male_score, | |
| 'female_score': female_score | |
| } | |
| # Moderate male indicators | |
| elif male_score > 0.5 and male_score > female_score + 0.2: | |
| return { | |
| 'gender': 'male', | |
| 'confidence': 0.75, | |
| 'method': 'moderate_male_indicators', | |
| 'male_score': male_score, | |
| 'female_score': female_score | |
| } | |
| # Moderate female indicators | |
| elif female_score > 0.5 and female_score > male_score + 0.2: | |
| return { | |
| 'gender': 'female', | |
| 'confidence': 0.75, | |
| 'method': 'moderate_female_indicators', | |
| 'male_score': male_score, | |
| 'female_score': female_score | |
| } | |
| # Close scores - use slight preference but lower confidence | |
| elif male_score > female_score: | |
| return { | |
| 'gender': 'male', | |
| 'confidence': 0.6, | |
| 'method': 'slight_male_preference', | |
| 'male_score': male_score, | |
| 'female_score': female_score | |
| } | |
| elif female_score > male_score: | |
| return { | |
| 'gender': 'female', | |
| 'confidence': 0.6, | |
| 'method': 'slight_female_preference', | |
| 'male_score': male_score, | |
| 'female_score': female_score | |
| } | |
| # Equal scores - neutral | |
| else: | |
| return { | |
| 'gender': 'neutral', | |
| 'confidence': 0.5, | |
| 'method': 'equal_indicators', | |
| 'male_score': male_score, | |
| 'female_score': female_score | |
| } | |
| def _analyze_without_face(self, image: np.ndarray) -> Dict: | |
| """Fallback analysis when face detection fails""" | |
| print(" π Fallback analysis (no face detected)") | |
| # Simple image-based heuristics | |
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
| h, w = gray.shape | |
| # Hair length estimation from top region | |
| top_region = gray[:int(h*0.3), :] | |
| hair_variance = np.var(top_region) if top_region.size > 0 else 0 | |
| # Very rough estimation | |
| if hair_variance > 400: # High variance suggests longer/more complex hair | |
| return { | |
| 'gender': 'female', | |
| 'confidence': 0.6, | |
| 'method': 'image_fallback_long_hair' | |
| } | |
| else: | |
| return { | |
| 'gender': 'male', | |
| 'confidence': 0.6, | |
| 'method': 'image_fallback_short_hair' | |
| } | |
| def create_balanced_enhancer_patch(): | |
| """ | |
| Integration patch to replace the biased gender detection | |
| """ | |
| print("π§ BALANCED GENDER DETECTION PATCH") | |
| print("="*35) | |
| print("\nISSUE IDENTIFIED:") | |
| print(" Current system is biased toward MALE detection") | |
| print(" 'red evening dress' + woman image β man in dress") | |
| print(" Gender detection defaults to male unless NO male indicators") | |
| print("\nFIXES APPLIED:") | |
| print(" β Balanced scoring (considers both male AND female indicators)") | |
| print(" β Conservative facial hair detection (less false positives)") | |
| print(" β Female indicator analysis (missing in current system)") | |
| print(" β Proper decision thresholds for both genders") | |
| print("\nINTEGRATION:") | |
| print(""" | |
| # In your ImprovedUnifiedGenderAppearanceEnhancer class, replace: | |
| def _analyze_gender_simple(self, image, face_bbox): | |
| # Current biased logic | |
| # With: | |
| def _analyze_gender_simple(self, image, face_bbox): | |
| \"\"\"Use balanced gender detection\"\"\" | |
| if not hasattr(self, 'balanced_detector'): | |
| self.balanced_detector = BalancedGenderDetector() | |
| # Convert face_bbox to image_path analysis (simplified for integration) | |
| # For full fix, extract face region and analyze directly | |
| # Placeholder logic - you'll need to adapt this to your specific interface | |
| # The key is using balanced scoring instead of male-biased scoring | |
| male_score = 0.0 | |
| female_score = 0.0 | |
| # Facial analysis here... | |
| # Use the balanced decision logic from BalancedGenderDetector | |
| if male_score > female_score + 0.3: | |
| return {'gender': 'male', 'confidence': 0.8} | |
| elif female_score > male_score + 0.3: | |
| return {'gender': 'female', 'confidence': 0.8} | |
| else: | |
| return {'gender': 'neutral', 'confidence': 0.6} | |
| """) | |
| def test_balanced_detection(): | |
| """Test cases for balanced gender detection""" | |
| print("\nπ§ͺ TESTING BALANCED GENDER DETECTION") | |
| print("="*40) | |
| test_cases = [ | |
| { | |
| 'description': 'Woman with long hair and smooth skin', | |
| 'male_indicators': {'total_score': 0.1}, | |
| 'female_indicators': {'total_score': 0.8}, | |
| 'expected': 'female' | |
| }, | |
| { | |
| 'description': 'Man with facial hair and wide face', | |
| 'male_indicators': {'total_score': 0.9}, | |
| 'female_indicators': {'total_score': 0.2}, | |
| 'expected': 'male' | |
| }, | |
| { | |
| 'description': 'Ambiguous features (current system would default to male)', | |
| 'male_indicators': {'total_score': 0.4}, | |
| 'female_indicators': {'total_score': 0.5}, | |
| 'expected': 'female' # Should properly detect female now | |
| } | |
| ] | |
| detector = BalancedGenderDetector() | |
| for case in test_cases: | |
| result = detector._make_balanced_gender_decision( | |
| case['male_indicators'], | |
| case['female_indicators'] | |
| ) | |
| passed = result['gender'] == case['expected'] | |
| status = "β PASS" if passed else "β FAIL" | |
| print(f"{status} {case['description']}") | |
| print(f" Male: {case['male_indicators']['total_score']:.1f}, " | |
| f"Female: {case['female_indicators']['total_score']:.1f}") | |
| print(f" Result: {result['gender']} (expected: {case['expected']})") | |
| print(f" Method: {result['method']}") | |
| print() | |
| if __name__ == "__main__": | |
| print("π§ BALANCED GENDER DETECTION FIX") | |
| print("="*35) | |
| print("\nβ CURRENT PROBLEM:") | |
| print("System biased toward MALE detection") | |
| print("'red evening dress' + woman β man in dress") | |
| print("Defaults to male unless zero male indicators") | |
| print("\nβ SOLUTION PROVIDED:") | |
| print("β’ Balanced scoring for both genders") | |
| print("β’ Conservative facial hair detection") | |
| print("β’ Female indicator analysis (NEW)") | |
| print("β’ Proper decision thresholds") | |
| # Test the balanced detection | |
| test_balanced_detection() | |
| # Integration instructions | |
| create_balanced_enhancer_patch() | |
| print(f"\nπ― EXPECTED FIX:") | |
| print("β’ Woman + 'red evening dress' β woman in dress β ") | |
| print("β’ Man + 'business suit' β man in suit β ") | |
| print("β’ Equal consideration for both genders") | |
| print("β’ No more default-to-male bias") |