Spaces:
Build error
Build error
| import os | |
| import io | |
| import base64 | |
| import json | |
| import requests | |
| from PIL import Image | |
| import numpy as np | |
| from flask import Flask, render_template, request, jsonify, send_from_directory | |
| from flask_cors import CORS | |
| import torch | |
| from transformers import ( | |
| AutoImageProcessor, | |
| AutoModelForImageClassification, | |
| BlipProcessor, | |
| BlipForConditionalGeneration, | |
| pipeline | |
| ) | |
| import cv2 | |
| from skimage import measure | |
| import logging | |
| import threading | |
| import time | |
| import socket | |
| import subprocess | |
| import sys | |
| from datetime import datetime | |
| import psutil | |
| from transformers import CLIPProcessor, CLIPModel | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| def convert_numpy_types(obj): | |
| """Convert numpy types to Python native types for JSON serialization""" | |
| if isinstance(obj, np.integer): | |
| return int(obj) | |
| elif isinstance(obj, np.floating): | |
| return float(obj) | |
| elif isinstance(obj, np.ndarray): | |
| return obj.tolist() | |
| elif isinstance(obj, dict): | |
| return {key: convert_numpy_types(value) for key, value in obj.items()} | |
| elif isinstance(obj, list): | |
| return [convert_numpy_types(item) for item in obj] | |
| else: | |
| return obj | |
| def find_free_port(start_port=7860, max_attempts=10): | |
| """Find a free port starting from start_port""" | |
| for port in range(start_port, start_port + max_attempts): | |
| try: | |
| with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: | |
| s.bind(('localhost', port)) | |
| return port | |
| except OSError: | |
| continue | |
| return start_port # Return default port if no free port found | |
| def kill_process_on_port(port): | |
| """Kill process running on a specific port""" | |
| try: | |
| if sys.platform.startswith('win'): | |
| # Windows | |
| cmd = f'netstat -ano | findstr :{port}' | |
| result = subprocess.run(cmd, shell=True, capture_output=True, text=True) | |
| if result.stdout: | |
| lines = result.stdout.strip().split('\n') | |
| for line in lines: | |
| if f':{port}' in line: | |
| parts = line.split() | |
| if len(parts) >= 5: | |
| pid = parts[-1] | |
| subprocess.run(f'taskkill /PID {pid} /F', shell=True) | |
| logger.info(f"Killed process {pid} on port {port}") | |
| else: | |
| # Linux/Mac | |
| cmd = f'lsof -ti:{port}' | |
| result = subprocess.run(cmd, shell=True, capture_output=True, text=True) | |
| if result.stdout.strip(): | |
| pids = result.stdout.strip().split('\n') | |
| for pid in pids: | |
| if pid: | |
| subprocess.run(f'kill -9 {pid}', shell=True) | |
| logger.info(f"Killed process {pid} on port {port}") | |
| except Exception as e: | |
| logger.warning(f"Could not kill process on port {port}: {e}") | |
| # Initialize Flask app with correct template folder | |
| app = Flask(__name__, template_folder='templates') | |
| app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size | |
| CORS(app) | |
| # Global variables for models | |
| room_classifier = None | |
| image_captioner = None | |
| quality_analyzer = None | |
| yolo_model = None | |
| clip_processor = None | |
| clip_model = None | |
| def load_models(): | |
| """Load advanced AI models for comprehensive analysis""" | |
| global room_classifier, image_captioner, object_detector, clip_processor, clip_model | |
| try: | |
| logger.info("Loading advanced AI models...") | |
| # 1. Room Classification Model | |
| room_classifier = pipeline( | |
| "image-classification", | |
| model="andupets/real-estate-image-classification", | |
| device=0 if torch.cuda.is_available() else -1 | |
| ) | |
| logger.info("β Room classification model loaded") | |
| # 2. Advanced Image Captioning Model | |
| image_captioner = pipeline( | |
| "image-to-text", | |
| model="Salesforce/blip-image-captioning-base", | |
| device=0 if torch.cuda.is_available() else -1 | |
| ) | |
| logger.info("β Image captioning model loaded") | |
| # 3. Advanced Object Detection Model | |
| object_detector = pipeline( | |
| "object-detection", | |
| model="facebook/detr-resnet-50", | |
| device=0 if torch.cuda.is_available() else -1 | |
| ) | |
| logger.info("β Object detection model loaded") | |
| # 4. CLIP Model for Advanced Scene Understanding | |
| try: | |
| clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32") | |
| clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32") | |
| if torch.cuda.is_available(): | |
| clip_model = clip_model.to('cuda') | |
| logger.info("β CLIP model loaded for scene understanding") | |
| except Exception as e: | |
| logger.warning(f"CLIP model loading failed: {e}") | |
| clip_processor = None | |
| clip_model = None | |
| # 5. Advanced Image Quality Assessment Model | |
| try: | |
| from transformers import AutoImageProcessor, AutoModelForImageClassification | |
| quality_processor = AutoImageProcessor.from_pretrained("microsoft/resnet-50") | |
| quality_model = AutoModelForImageClassification.from_pretrained("microsoft/resnet-50") | |
| if torch.cuda.is_available(): | |
| quality_model = quality_model.to('cuda') | |
| logger.info("β Advanced quality assessment model loaded") | |
| except Exception as e: | |
| logger.warning(f"Quality model loading failed: {e}") | |
| quality_processor = None | |
| quality_model = None | |
| # 6. YOLO Model for Additional Object Detection | |
| try: | |
| from ultralytics import YOLO | |
| yolo_model = YOLO('yolov8n.pt') | |
| logger.info("β YOLO model loaded for enhanced object detection") | |
| except Exception as e: | |
| logger.warning(f"YOLO model loading failed: {e}") | |
| yolo_model = None | |
| logger.info("All advanced AI models loaded successfully!") | |
| return True | |
| except Exception as e: | |
| logger.error(f"Error loading models: {str(e)}") | |
| return False | |
| def detect_objects_advanced(image): | |
| """Advanced AI-powered object detection using multiple models""" | |
| try: | |
| # Convert PIL image to numpy array | |
| img_array = np.array(image) | |
| # Ensure we have a valid image | |
| if img_array.size == 0: | |
| raise ValueError("Empty image array") | |
| # Multiple AI models for comprehensive object detection | |
| detected_objects = [] | |
| object_analysis = {} | |
| # 1. DETR (DEtection TRansformer) - Primary detection | |
| try: | |
| from transformers import DetrImageProcessor, DetrForObjectDetection | |
| import torch | |
| processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") | |
| model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") | |
| # Prepare image for DETR | |
| inputs = processor(images=image, return_tensors="pt") | |
| with torch.no_grad(): | |
| outputs = model(**inputs) | |
| # Post-process outputs | |
| target_sizes = torch.tensor([image.size[::-1]]) | |
| results = processor.post_process_object_detection(outputs, target_sizes=target_sizes, threshold=0.5)[0] | |
| for score, label, box in zip(results["scores"], results["labels"], results["boxes"]): | |
| detected_objects.append({ | |
| "label": model.config.id2label[label.item()], | |
| "confidence": float(score.item()), # Convert to Python float | |
| "bbox": [float(x) for x in box.tolist()] # Convert to Python floats | |
| }) | |
| except Exception as e: | |
| logger.warning(f"DETR detection failed: {str(e)}") | |
| # 2. YOLO v8 (if available) for additional detection | |
| try: | |
| from ultralytics import YOLO | |
| # Load YOLO model | |
| yolo_model = YOLO('yolov8n.pt') | |
| # Run inference | |
| results = yolo_model(image) | |
| for result in results: | |
| boxes = result.boxes | |
| if boxes is not None: | |
| for box in boxes: | |
| # Get box coordinates | |
| x1, y1, x2, y2 = box.xyxy[0].cpu().numpy() | |
| confidence = float(box.conf[0].cpu().numpy()) # Convert to Python float | |
| class_id = int(box.cls[0].cpu().numpy()) | |
| class_name = yolo_model.names[class_id] | |
| # Add if not already detected by DETR | |
| if not any(obj["label"].lower() == class_name.lower() for obj in detected_objects): | |
| detected_objects.append({ | |
| "label": class_name, | |
| "confidence": confidence, | |
| "bbox": [float(x1), float(y1), float(x2), float(y2)] # Convert to Python floats | |
| }) | |
| except Exception as e: | |
| logger.warning(f"YOLO detection failed: {str(e)}") | |
| # 3. Scene understanding with CLIP | |
| try: | |
| from transformers import CLIPProcessor, CLIPModel | |
| clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32") | |
| clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32") | |
| # Define real estate specific text prompts | |
| real_estate_concepts = [ | |
| "modern furniture", "luxury interior", "kitchen appliances", "bathroom fixtures", | |
| "bedroom furniture", "living room setup", "dining area", "home office", | |
| "outdoor space", "balcony", "garden", "parking space", "storage area", | |
| "lighting fixtures", "flooring", "wall decor", "window treatments", | |
| "architectural features", "high ceilings", "open floor plan" | |
| ] | |
| # Process image and text | |
| inputs = clip_processor(images=image, text=real_estate_concepts, return_tensors="pt", padding=True) | |
| with torch.no_grad(): | |
| outputs = clip_model(**inputs) | |
| logits_per_image = outputs.logits_per_image | |
| probs = logits_per_image.softmax(dim=-1) | |
| # Get top matches | |
| top_concepts = [] | |
| for i, prob in enumerate(probs[0]): | |
| if prob > 0.3: # Threshold for relevance | |
| top_concepts.append({ | |
| "concept": real_estate_concepts[i], | |
| "confidence": float(prob.item()) # Convert to Python float | |
| }) | |
| object_analysis["scene_concepts"] = sorted(top_concepts, key=lambda x: x["confidence"], reverse=True)[:5] | |
| except Exception as e: | |
| logger.warning(f"CLIP analysis failed: {str(e)}") | |
| object_analysis["scene_concepts"] = [] | |
| # 4. Advanced object categorization and analysis | |
| if detected_objects: | |
| # Categorize objects by type | |
| furniture_objects = [obj for obj in detected_objects if any(keyword in obj["label"].lower() | |
| for keyword in ["chair", "table", "bed", "sofa", "desk", "cabinet", "shelf"])] | |
| appliance_objects = [obj for obj in detected_objects if any(keyword in obj["label"].lower() | |
| for keyword in ["refrigerator", "oven", "microwave", "dishwasher", "washer", "dryer"])] | |
| fixture_objects = [obj for obj in detected_objects if any(keyword in obj["label"].lower() | |
| for keyword in ["sink", "toilet", "bathtub", "shower", "faucet", "light", "lamp"])] | |
| # Calculate object density and distribution | |
| img_area = img_array.shape[0] * img_array.shape[1] | |
| object_density = float(len(detected_objects) / (img_area / 10000)) # Convert to Python float | |
| # Analyze object positioning | |
| center_x, center_y = img_array.shape[1] / 2, img_array.shape[0] / 2 | |
| centered_objects = 0 | |
| for obj in detected_objects: | |
| bbox = obj["bbox"] | |
| obj_center_x = (bbox[0] + bbox[2]) / 2 | |
| obj_center_y = (bbox[1] + bbox[3]) / 2 | |
| # Check if object is in center region | |
| if (abs(obj_center_x - center_x) < center_x * 0.3 and | |
| abs(obj_center_y - center_y) < center_y * 0.3): | |
| centered_objects += 1 | |
| object_analysis.update({ | |
| "total_objects": len(detected_objects), | |
| "furniture_count": len(furniture_objects), | |
| "appliance_count": len(appliance_objects), | |
| "fixture_count": len(fixture_objects), | |
| "object_density": object_density, | |
| "centered_objects": centered_objects, | |
| "composition_balance": float(centered_objects / len(detected_objects)) if detected_objects else 0.0, # Convert to Python float | |
| "object_categories": { | |
| "furniture": [obj["label"] for obj in furniture_objects], | |
| "appliances": [obj["label"] for obj in appliance_objects], | |
| "fixtures": [obj["label"] for obj in fixture_objects], | |
| "other": [obj["label"] for obj in detected_objects | |
| if obj not in furniture_objects + appliance_objects + fixture_objects] | |
| } | |
| }) | |
| else: | |
| # Fallback analysis when no objects detected | |
| object_analysis.update({ | |
| "total_objects": 0, | |
| "furniture_count": 0, | |
| "appliance_count": 0, | |
| "fixture_count": 0, | |
| "object_density": 0.0, | |
| "centered_objects": 0, | |
| "composition_balance": 0.0, | |
| "object_categories": {"furniture": [], "appliances": [], "fixtures": [], "other": []} | |
| }) | |
| # 5. Dynamic room type inference from objects | |
| room_type_confidence = {} | |
| # Kitchen indicators | |
| kitchen_indicators = ["refrigerator", "oven", "microwave", "dishwasher", "sink", "stove"] | |
| kitchen_score = sum(1 for obj in detected_objects | |
| if any(indicator in obj["label"].lower() for indicator in kitchen_indicators)) | |
| room_type_confidence["kitchen"] = min(1.0, kitchen_score / 3) | |
| # Bathroom indicators | |
| bathroom_indicators = ["toilet", "bathtub", "shower", "sink", "mirror"] | |
| bathroom_score = sum(1 for obj in detected_objects | |
| if any(indicator in obj["label"].lower() for indicator in bathroom_indicators)) | |
| room_type_confidence["bathroom"] = min(1.0, bathroom_score / 2) | |
| # Bedroom indicators | |
| bedroom_indicators = ["bed", "nightstand", "dresser", "wardrobe"] | |
| bedroom_score = sum(1 for obj in detected_objects | |
| if any(indicator in obj["label"].lower() for indicator in bedroom_indicators)) | |
| room_type_confidence["bedroom"] = min(1.0, bedroom_score / 2) | |
| # Living room indicators | |
| living_indicators = ["sofa", "tv", "coffee table", "armchair", "fireplace"] | |
| living_score = sum(1 for obj in detected_objects | |
| if any(indicator in obj["label"].lower() for indicator in living_indicators)) | |
| room_type_confidence["living room"] = min(1.0, living_score / 2) | |
| object_analysis["room_type_inference"] = room_type_confidence | |
| return { | |
| "objects": detected_objects, | |
| "analysis": object_analysis | |
| } | |
| except Exception as e: | |
| logger.error(f"Error in object detection: {str(e)}") | |
| return { | |
| "objects": [], | |
| "analysis": { | |
| "total_objects": 0, | |
| "furniture_count": 0, | |
| "appliance_count": 0, | |
| "fixture_count": 0, | |
| "object_density": 0.0, | |
| "centered_objects": 0, | |
| "composition_balance": 0.0, | |
| "object_categories": {"furniture": [], "appliances": [], "fixtures": [], "other": []}, | |
| "room_type_inference": {}, | |
| "scene_concepts": [] | |
| } | |
| } | |
| def analyze_image_quality(image): | |
| """Advanced AI-powered image quality analysis using multiple models""" | |
| try: | |
| # Convert PIL image to numpy array | |
| img_array = np.array(image) | |
| # Ensure we have a valid image | |
| if img_array.size == 0: | |
| raise ValueError("Empty image array") | |
| # Convert to grayscale for analysis | |
| if len(img_array.shape) == 3: | |
| gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY) | |
| else: | |
| gray = img_array | |
| # Ensure we have valid dimensions | |
| if gray.shape[0] < 10 or gray.shape[1] < 10: | |
| raise ValueError("Image too small for analysis") | |
| # Advanced AI-based quality metrics | |
| quality_metrics = {} | |
| # 1. BRISQUE (Blind/Referenceless Image Spatial Quality Evaluator) | |
| try: | |
| from skimage.metrics import structural_similarity as ssim | |
| from skimage.filters import sobel | |
| # Calculate BRISQUE-like features | |
| sobel_img = sobel(gray) | |
| quality_metrics['sharpness'] = float(np.std(sobel_img)) | |
| # Local binary pattern for texture analysis | |
| from skimage.feature import local_binary_pattern | |
| lbp = local_binary_pattern(gray, P=8, R=1, method='uniform') | |
| quality_metrics['texture_quality'] = float(np.std(lbp)) | |
| except: | |
| quality_metrics['sharpness'] = float(cv2.Laplacian(gray, cv2.CV_64F).var()) | |
| quality_metrics['texture_quality'] = 50.0 | |
| # 2. Advanced brightness analysis with histogram | |
| hist = cv2.calcHist([gray], [0], None, [256], [0, 256]) | |
| hist_norm = hist.ravel() / hist.sum() | |
| # Calculate multiple brightness metrics | |
| quality_metrics['brightness_mean'] = float(np.mean(gray)) | |
| quality_metrics['brightness_std'] = float(np.std(gray)) | |
| quality_metrics['brightness_skew'] = float(np.mean(((gray - np.mean(gray)) / np.std(gray)) ** 3)) | |
| quality_metrics['brightness_kurtosis'] = float(np.mean(((gray - np.mean(gray)) / np.std(gray)) ** 4)) | |
| # 3. Advanced contrast analysis | |
| quality_metrics['contrast'] = float(np.std(gray)) | |
| quality_metrics['contrast_ratio'] = float(np.max(gray) / (np.min(gray) + 1e-8)) | |
| # 4. Noise analysis using multiple methods | |
| try: | |
| # Structural similarity for noise | |
| blurred = cv2.GaussianBlur(gray, (5, 5), 0) | |
| quality_metrics['noise_level'] = 1 - ssim(gray, blurred) | |
| # Wavelet-based noise estimation | |
| import pywt | |
| coeffs = pywt.wavedec2(gray, 'db1', level=1) | |
| detail_coeffs = coeffs[1] | |
| quality_metrics['wavelet_noise'] = float(np.std(detail_coeffs[0])) | |
| except: | |
| quality_metrics['noise_level'] = 0.05 | |
| quality_metrics['wavelet_noise'] = 10.0 | |
| # 5. Resolution and composition analysis | |
| height, width = gray.shape | |
| quality_metrics['resolution'] = f"{width}x{height}" | |
| quality_metrics['aspect_ratio'] = width / height | |
| quality_metrics['pixel_density'] = (width * height) / 10000 | |
| # 6. Composition analysis using rule of thirds | |
| thirds_h = height // 3 | |
| thirds_w = width // 3 | |
| # Check intersection points of rule of thirds | |
| intersection_points = [ | |
| gray[thirds_h, thirds_w], | |
| gray[thirds_h, 2*thirds_w], | |
| gray[2*thirds_h, thirds_w], | |
| gray[2*thirds_h, 2*thirds_w] | |
| ] | |
| quality_metrics['composition_score'] = float(np.std(intersection_points)) | |
| # 7. Color analysis (if color image) | |
| if len(img_array.shape) == 3: | |
| # Convert to different color spaces | |
| hsv = cv2.cvtColor(img_array, cv2.COLOR_RGB2HSV) | |
| lab = cv2.cvtColor(img_array, cv2.COLOR_RGB2LAB) | |
| quality_metrics['color_saturation'] = float(np.mean(hsv[:, :, 1])) | |
| quality_metrics['color_variance'] = float(np.std(lab)) | |
| quality_metrics['color_balance'] = float(np.std([np.mean(img_array[:, :, i]) for i in range(3)])) | |
| else: | |
| quality_metrics['color_saturation'] = 0.0 | |
| quality_metrics['color_variance'] = 0.0 | |
| quality_metrics['color_balance'] = 0.0 | |
| # AI-based quality scoring using weighted combination | |
| quality_score = 0 | |
| quality_issues = [] | |
| # Dynamic scoring based on image characteristics | |
| # Brightness scoring (0-20 points) | |
| brightness_score = 0 | |
| if quality_metrics['brightness_mean'] < 30: | |
| quality_issues.append("Image is extremely dark") | |
| brightness_score = 2 | |
| elif quality_metrics['brightness_mean'] < 50: | |
| quality_issues.append("Image is too dark") | |
| brightness_score = 8 | |
| elif quality_metrics['brightness_mean'] > 220: | |
| quality_issues.append("Image is overexposed") | |
| brightness_score = 5 | |
| elif quality_metrics['brightness_mean'] > 180: | |
| quality_issues.append("Image is somewhat overexposed") | |
| brightness_score = 12 | |
| else: | |
| brightness_score = 20 | |
| # Contrast scoring (0-20 points) | |
| contrast_score = 0 | |
| if quality_metrics['contrast'] < 15: | |
| quality_issues.append("Very low contrast") | |
| contrast_score = 2 | |
| elif quality_metrics['contrast'] < 30: | |
| quality_issues.append("Low contrast") | |
| contrast_score = 8 | |
| else: | |
| contrast_score = 20 | |
| # Sharpness scoring (0-20 points) | |
| sharpness_score = 0 | |
| if quality_metrics['sharpness'] < 30: | |
| quality_issues.append("Image is very blurry") | |
| sharpness_score = 2 | |
| elif quality_metrics['sharpness'] < 60: | |
| quality_issues.append("Image appears blurry") | |
| sharpness_score = 8 | |
| else: | |
| sharpness_score = 20 | |
| # Noise scoring (0-20 points) | |
| noise_score = 0 | |
| if quality_metrics['noise_level'] > 0.2: | |
| quality_issues.append("High noise level") | |
| noise_score = 5 | |
| elif quality_metrics['noise_level'] > 0.1: | |
| quality_issues.append("Moderate noise level") | |
| noise_score = 12 | |
| else: | |
| noise_score = 20 | |
| # Resolution scoring (0-10 points) | |
| resolution_score = 0 | |
| if quality_metrics['pixel_density'] < 20: | |
| quality_issues.append("Very low resolution") | |
| resolution_score = 2 | |
| elif quality_metrics['pixel_density'] < 50: | |
| quality_issues.append("Low resolution") | |
| resolution_score = 5 | |
| else: | |
| resolution_score = 10 | |
| # Composition scoring (0-10 points) | |
| composition_score = min(10, quality_metrics['composition_score'] / 10) | |
| if composition_score < 3: | |
| quality_issues.append("Poor composition") | |
| # Calculate total score | |
| quality_score = brightness_score + contrast_score + sharpness_score + noise_score + resolution_score + composition_score | |
| quality_score = min(100, quality_score) | |
| # Dynamic quality level determination | |
| if quality_score >= 90: | |
| quality_level = "Exceptional Quality" | |
| elif quality_score >= 80: | |
| quality_level = "High Quality" | |
| elif quality_score >= 70: | |
| quality_level = "Good Quality" | |
| elif quality_score >= 50: | |
| quality_level = "Medium Quality" | |
| else: | |
| quality_level = "Low Quality" | |
| return { | |
| "quality_level": quality_level, | |
| "quality_score": quality_score, | |
| "issues": quality_issues, | |
| "metrics": quality_metrics | |
| } | |
| except Exception as e: | |
| logger.error(f"Error in quality analysis: {str(e)}") | |
| return { | |
| "quality_level": "Analysis Failed", | |
| "quality_score": 0, | |
| "issues": ["Unable to analyze image quality"], | |
| "metrics": {} | |
| } | |
| def get_room_type_description(room_type): | |
| """Get detailed description for room types""" | |
| descriptions = { | |
| "bathroom": "A bathroom with toilet, sink, and shower/bathtub facilities", | |
| "bedroom": "A sleeping area with bed and bedroom furniture", | |
| "dining room": "A formal dining area with table and chairs for meals", | |
| "house facade": "The exterior front view of a house or building", | |
| "kitchen": "A cooking area with appliances, countertops, and storage", | |
| "living room": "A main living area with seating and entertainment space", | |
| "sao paulo apartment facade": "Exterior view of an apartment building" | |
| } | |
| return descriptions.get(room_type, f"A {room_type} area") | |
| def index(): | |
| """Main page for the image analyzer""" | |
| return render_template('index.html') | |
| def analyze_image(): | |
| """Advanced AI-powered property image analysis with comprehensive insights""" | |
| try: | |
| if 'image' not in request.files: | |
| return jsonify({'error': 'No image uploaded'}), 400 | |
| file = request.files['image'] | |
| if file.filename == '': | |
| return jsonify({'error': 'No image selected'}), 400 | |
| # Load and validate image | |
| try: | |
| image = Image.open(file.stream).convert('RGB') | |
| # Resize if too large for processing | |
| max_size = 1024 | |
| if max(image.size) > max_size: | |
| image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS) | |
| except Exception as e: | |
| return jsonify({'error': f'Invalid image format: {str(e)}'}), 400 | |
| logger.info(f"Starting advanced analysis for image: {file.filename}") | |
| # 1. Advanced Room Classification with Confidence Analysis | |
| try: | |
| room_results = room_classifier(image) | |
| room_classification = { | |
| 'room_type': room_results[0]['label'].lower(), | |
| 'confidence': round(room_results[0]['score'] * 100, 2), | |
| 'all_predictions': [ | |
| {'room_type': result['label'].lower(), 'confidence': round(result['score'] * 100, 2)} | |
| for result in room_results[:3] | |
| ] | |
| } | |
| logger.info(f"Room classification: {room_classification['room_type']} ({room_classification['confidence']}%)") | |
| except Exception as e: | |
| logger.error(f"Room classification failed: {e}") | |
| room_classification = {'room_type': 'unknown', 'confidence': 0, 'all_predictions': []} | |
| # 2. Advanced Image Captioning with Enhanced Descriptions | |
| try: | |
| caption_results = image_captioner(image, max_new_tokens=50) | |
| image_caption = { | |
| 'caption': caption_results[0]['generated_text'], | |
| 'confidence': 0.85, # BLIP confidence | |
| 'enhanced_description': f"A {room_classification['room_type']} featuring {caption_results[0]['generated_text']}" | |
| } | |
| logger.info(f"Image caption: {image_caption['caption']}") | |
| except Exception as e: | |
| logger.error(f"Image captioning failed: {e}") | |
| image_caption = {'caption': 'Unable to generate caption', 'confidence': 0, 'enhanced_description': ''} | |
| # 3. Advanced Object Detection with Multiple Models | |
| try: | |
| object_detection = detect_objects_advanced(image) | |
| logger.info(f"Object detection: {len(object_detection.get('objects', []))} objects detected") | |
| except Exception as e: | |
| logger.error(f"Object detection failed: {e}") | |
| object_detection = {'objects': [], 'analysis': {}} | |
| # 4. Advanced Image Quality Analysis with Multiple Metrics | |
| try: | |
| quality_analysis = analyze_image_quality(image) | |
| logger.info(f"Quality analysis: {quality_analysis['quality_level']} ({quality_analysis['quality_score']}/100)") | |
| except Exception as e: | |
| logger.error(f"Quality analysis failed: {e}") | |
| quality_analysis = {'quality_level': 'Analysis Failed', 'quality_score': 0, 'issues': [], 'metrics': {}} | |
| # 5. Advanced Room Size Estimation | |
| try: | |
| room_size = estimate_room_size(image, room_classification['room_type']) | |
| logger.info(f"Room size estimation: {room_size['estimated_size']}") | |
| except Exception as e: | |
| logger.error(f"Room size estimation failed: {e}") | |
| room_size = {'estimated_size': 'Unable to estimate', 'confidence': 'Low'} | |
| # 6. Advanced Property Assessment with AI Insights | |
| try: | |
| property_assessment = assess_property_image_quality( | |
| room_classification, quality_analysis, object_detection, image_caption | |
| ) | |
| logger.info(f"Property assessment: {property_assessment['overall_score']}/100") | |
| except Exception as e: | |
| logger.error(f"Property assessment failed: {e}") | |
| property_assessment = {'overall_score': 0, 'strengths': [], 'weaknesses': [], 'recommendations': []} | |
| # 7. Advanced Property Insights with Market Analysis | |
| try: | |
| property_insights = generate_property_insights( | |
| room_classification, quality_analysis, object_detection, image_caption | |
| ) | |
| logger.info("Property insights generated successfully") | |
| except Exception as e: | |
| logger.error(f"Property insights generation failed: {e}") | |
| property_insights = {'marketing_tips': [], 'target_audience': [], 'pricing_considerations': []} | |
| # 8. Advanced Scene Understanding with CLIP | |
| try: | |
| if clip_processor and clip_model: | |
| scene_analysis = analyze_scene_with_clip(image, room_classification['room_type']) | |
| logger.info("CLIP scene analysis completed") | |
| else: | |
| scene_analysis = {'scene_concepts': [], 'style_analysis': {}} | |
| except Exception as e: | |
| logger.error(f"Scene analysis failed: {e}") | |
| scene_analysis = {'scene_concepts': [], 'style_analysis': {}} | |
| # 9. Advanced Market Positioning Analysis | |
| try: | |
| market_analysis = analyze_market_positioning( | |
| room_classification, quality_analysis, object_detection, property_assessment | |
| ) | |
| logger.info("Market positioning analysis completed") | |
| except Exception as e: | |
| logger.error(f"Market positioning analysis failed: {e}") | |
| market_analysis = {'market_tier': 'Unknown', 'competitive_position': 'Unknown'} | |
| # 10. Generate Comprehensive Report | |
| comprehensive_report = { | |
| 'analysis_summary': { | |
| 'room_type': room_classification['room_type'], | |
| 'room_confidence': room_classification['confidence'], | |
| 'quality_score': quality_analysis['quality_score'], | |
| 'object_count': len(object_detection.get('objects', [])), | |
| 'overall_assessment_score': property_assessment['overall_score'], | |
| 'professional_grade': property_assessment.get('professional_grade', False) | |
| }, | |
| 'room_classification': room_classification, | |
| 'image_caption': image_caption, | |
| 'object_detection': object_detection, | |
| 'quality_analysis': quality_analysis, | |
| 'room_size_estimation': room_size, | |
| 'property_assessment': property_assessment, | |
| 'property_insights': property_insights, | |
| 'scene_analysis': scene_analysis, | |
| 'market_analysis': market_analysis, | |
| 'technical_metadata': { | |
| 'image_dimensions': image.size, | |
| 'analysis_timestamp': datetime.now().isoformat(), | |
| 'models_used': ['Room Classification', 'BLIP Captioning', 'DETR Detection', 'Quality Analysis', 'CLIP Scene Understanding'], | |
| 'processing_time': 'Real-time' | |
| } | |
| } | |
| # Convert numpy types to Python native types for JSON serialization | |
| comprehensive_report = convert_numpy_types(comprehensive_report) | |
| logger.info("Advanced analysis completed successfully") | |
| return jsonify(comprehensive_report) | |
| except Exception as e: | |
| logger.error(f"Analysis failed: {str(e)}") | |
| return jsonify({'error': f'Analysis failed: {str(e)}'}), 500 | |
| def health_check(): | |
| """Health check endpoint""" | |
| return jsonify({ | |
| 'status': 'healthy', | |
| 'models_loaded': { | |
| 'room_classifier': room_classifier is not None, | |
| 'image_captioner': image_captioner is not None, | |
| 'yolo_model': yolo_model is not None | |
| } | |
| }) | |
| def static_files(filename): | |
| """Serve static files""" | |
| return send_from_directory('static', filename) | |
| def assess_property_image_quality(room_classification, quality_analysis, object_detection, image_caption): | |
| """Advanced AI-powered property image assessment using dynamic analysis""" | |
| try: | |
| # Initialize assessment components | |
| assessment = { | |
| "overall_score": 0, | |
| "strengths": [], | |
| "weaknesses": [], | |
| "recommendations": [], | |
| "market_appeal": {}, | |
| "technical_analysis": {}, | |
| "composition_analysis": {}, | |
| "professional_grade": False | |
| } | |
| # 1. Dynamic Room Classification Analysis (0-25 points) | |
| room_score = 0 | |
| room_confidence = room_classification.get('confidence', 0) | |
| room_type = room_classification.get('room_type', 'unknown') | |
| # AI-based room type validation | |
| if room_confidence > 0.8: | |
| room_score = 25 | |
| assessment["strengths"].append(f"Clear {room_type} identification with high confidence") | |
| elif room_confidence > 0.6: | |
| room_score = 20 | |
| assessment["strengths"].append(f"Good {room_type} identification") | |
| elif room_confidence > 0.4: | |
| room_score = 15 | |
| assessment["weaknesses"].append(f"Uncertain room type identification") | |
| else: | |
| room_score = 5 | |
| assessment["weaknesses"].append("Poor room type identification") | |
| # Cross-validate with object detection | |
| object_room_inference = object_detection.get('analysis', {}).get('room_type_inference', {}) | |
| if room_type in object_room_inference: | |
| object_confidence = object_room_inference[room_type] | |
| if object_confidence > 0.5: | |
| room_score += 5 # Bonus for consistency | |
| assessment["strengths"].append("Object detection confirms room type") | |
| else: | |
| room_score -= 5 # Penalty for inconsistency | |
| assessment["weaknesses"].append("Object detection contradicts room type") | |
| # 2. Advanced Quality Analysis (0-30 points) | |
| quality_score = 0 | |
| quality_metrics = quality_analysis.get('metrics', {}) | |
| quality_level = quality_analysis.get('quality_level', 'Unknown') | |
| quality_issues = quality_analysis.get('issues', []) | |
| # Dynamic quality scoring based on multiple factors | |
| base_quality = quality_analysis.get('quality_score', 0) | |
| # Technical quality factors | |
| brightness_score = 0 | |
| brightness_mean = quality_metrics.get('brightness_mean', 128) | |
| if 60 <= brightness_mean <= 180: | |
| brightness_score = 8 | |
| elif 40 <= brightness_mean <= 200: | |
| brightness_score = 5 | |
| else: | |
| brightness_score = 2 | |
| contrast_score = 0 | |
| contrast = quality_metrics.get('contrast', 0) | |
| if contrast > 40: | |
| contrast_score = 8 | |
| elif contrast > 25: | |
| contrast_score = 5 | |
| else: | |
| contrast_score = 2 | |
| sharpness_score = 0 | |
| sharpness = quality_metrics.get('sharpness', 0) | |
| if sharpness > 80: | |
| sharpness_score = 8 | |
| elif sharpness > 50: | |
| sharpness_score = 5 | |
| else: | |
| sharpness_score = 2 | |
| noise_score = 0 | |
| noise_level = quality_metrics.get('noise_level', 0) | |
| if noise_level < 0.05: | |
| noise_score = 6 | |
| elif noise_level < 0.1: | |
| noise_score = 4 | |
| else: | |
| noise_score = 1 | |
| quality_score = brightness_score + contrast_score + sharpness_score + noise_score | |
| # Add quality issues to weaknesses | |
| for issue in quality_issues: | |
| assessment["weaknesses"].append(f"Quality: {issue}") | |
| # 3. Advanced Object Detection Analysis (0-25 points) | |
| object_score = 0 | |
| object_analysis = object_detection.get('analysis', {}) | |
| detected_objects = object_detection.get('objects', []) | |
| # Object richness scoring | |
| total_objects = object_analysis.get('total_objects', 0) | |
| if total_objects >= 8: | |
| object_score += 10 | |
| assessment["strengths"].append("Rich object content enhances property appeal") | |
| elif total_objects >= 5: | |
| object_score += 7 | |
| assessment["strengths"].append("Good object variety") | |
| elif total_objects >= 3: | |
| object_score += 4 | |
| else: | |
| object_score += 1 | |
| assessment["weaknesses"].append("Limited object content") | |
| # Object relevance scoring | |
| furniture_count = object_analysis.get('furniture_count', 0) | |
| appliance_count = object_analysis.get('appliance_count', 0) | |
| fixture_count = object_analysis.get('fixture_count', 0) | |
| if furniture_count > 0: | |
| object_score += 5 | |
| if appliance_count > 0: | |
| object_score += 5 | |
| if fixture_count > 0: | |
| object_score += 5 | |
| # Composition balance | |
| composition_balance = object_analysis.get('composition_balance', 0) | |
| if composition_balance > 0.6: | |
| object_score += 5 | |
| assessment["strengths"].append("Well-balanced object composition") | |
| elif composition_balance < 0.3: | |
| object_score -= 3 | |
| assessment["weaknesses"].append("Poor object distribution") | |
| # 4. Dynamic Caption Quality Analysis (0-20 points) | |
| caption_score = 0 | |
| caption = image_caption.get('caption', '') | |
| if caption: | |
| # Analyze caption length and content | |
| caption_length = len(caption.split()) | |
| # Check for real estate keywords | |
| real_estate_keywords = ['room', 'kitchen', 'bathroom', 'bedroom', 'living', 'dining', | |
| 'modern', 'luxury', 'spacious', 'bright', 'clean', 'furnished'] | |
| keyword_count = sum(1 for keyword in real_estate_keywords if keyword.lower() in caption.lower()) | |
| if caption_length >= 10 and keyword_count >= 3: | |
| caption_score = 20 | |
| assessment["strengths"].append("Comprehensive and relevant caption") | |
| elif caption_length >= 8 and keyword_count >= 2: | |
| caption_score = 15 | |
| assessment["strengths"].append("Good descriptive caption") | |
| elif caption_length >= 5: | |
| caption_score = 10 | |
| else: | |
| caption_score = 5 | |
| assessment["weaknesses"].append("Limited caption description") | |
| else: | |
| assessment["weaknesses"].append("No caption generated") | |
| # 5. Calculate Overall Score with Dynamic Weighting | |
| overall_score = room_score + quality_score + object_score + caption_score | |
| # Normalize to 100-point scale | |
| overall_score = min(100, overall_score) | |
| # 6. Advanced Market Appeal Analysis | |
| market_appeal = {} | |
| # Professional grade determination | |
| if overall_score >= 85: | |
| assessment["professional_grade"] = True | |
| market_appeal["level"] = "Premium" | |
| market_appeal["description"] = "Professional-grade image suitable for luxury listings" | |
| elif overall_score >= 75: | |
| assessment["professional_grade"] = True | |
| market_appeal["level"] = "Professional" | |
| market_appeal["description"] = "High-quality image for professional listings" | |
| elif overall_score >= 60: | |
| market_appeal["level"] = "Standard" | |
| market_appeal["description"] = "Acceptable quality for standard listings" | |
| else: | |
| market_appeal["level"] = "Needs Improvement" | |
| market_appeal["description"] = "Image requires enhancement before listing" | |
| # Target audience analysis | |
| if room_type in ['kitchen', 'bathroom'] and quality_score > 20: | |
| market_appeal["target_audience"] = "Home buyers, Investors" | |
| elif room_type in ['bedroom', 'living room'] and object_score > 15: | |
| market_appeal["target_audience"] = "Families, Young professionals" | |
| else: | |
| market_appeal["target_audience"] = "General buyers" | |
| # 7. Dynamic Recommendations Generation | |
| recommendations = [] | |
| # Quality-based recommendations | |
| if brightness_mean < 50: | |
| recommendations.append("Increase lighting or use HDR techniques") | |
| elif brightness_mean > 200: | |
| recommendations.append("Reduce exposure to avoid overexposure") | |
| if contrast < 25: | |
| recommendations.append("Enhance contrast in post-processing") | |
| if sharpness < 50: | |
| recommendations.append("Use tripod or image stabilization for sharper photos") | |
| if noise_level > 0.1: | |
| recommendations.append("Use noise reduction software") | |
| # Object-based recommendations | |
| if total_objects < 3: | |
| recommendations.append("Include more furniture or decorative elements") | |
| if composition_balance < 0.4: | |
| recommendations.append("Reposition camera for better object distribution") | |
| # Room-specific recommendations | |
| if room_type == 'kitchen' and appliance_count < 2: | |
| recommendations.append("Highlight kitchen appliances and features") | |
| elif room_type == 'bathroom' and fixture_count < 2: | |
| recommendations.append("Showcase bathroom fixtures and amenities") | |
| # Technical recommendations | |
| if not assessment["professional_grade"]: | |
| recommendations.append("Consider professional photography services") | |
| assessment["recommendations"] = recommendations | |
| assessment["overall_score"] = overall_score | |
| assessment["market_appeal"] = market_appeal | |
| # 8. Technical Analysis Summary | |
| assessment["technical_analysis"] = { | |
| "room_classification_score": room_score, | |
| "quality_analysis_score": quality_score, | |
| "object_detection_score": object_score, | |
| "caption_quality_score": caption_score, | |
| "composition_balance": composition_balance, | |
| "object_density": object_analysis.get('object_density', 0), | |
| "quality_metrics_summary": { | |
| "brightness": round(brightness_mean, 1), | |
| "contrast": round(contrast, 1), | |
| "sharpness": round(sharpness, 1), | |
| "noise_level": round(noise_level, 3) | |
| } | |
| } | |
| return assessment | |
| except Exception as e: | |
| logger.error(f"Error in property assessment: {str(e)}") | |
| return { | |
| "overall_score": 0, | |
| "strengths": [], | |
| "weaknesses": ["Assessment failed due to technical error"], | |
| "recommendations": ["Please try again with a different image"], | |
| "market_appeal": {"level": "Unknown", "description": "Unable to assess"}, | |
| "professional_grade": False, | |
| "technical_analysis": {} | |
| } | |
| def estimate_room_size(image, room_type='unknown'): | |
| """Advanced AI-powered room size estimation using multiple analysis methods""" | |
| try: | |
| # Convert PIL image to numpy array | |
| img_array = np.array(image) | |
| # Get image dimensions | |
| height, width = img_array.shape[:2] | |
| image_area = height * width | |
| # 1. Object-based size estimation | |
| object_analysis = detect_objects_advanced(image) | |
| detected_objects = object_analysis.get('objects', []) | |
| object_analysis_data = object_analysis.get('analysis', {}) | |
| # Categorize objects by typical sizes | |
| large_furniture = [obj for obj in detected_objects | |
| if obj['label'].lower() in ['bed', 'sofa', 'couch', 'dining table', 'kitchen island']] | |
| medium_furniture = [obj for obj in detected_objects | |
| if obj['label'].lower() in ['chair', 'desk', 'cabinet', 'dresser', 'nightstand']] | |
| small_furniture = [obj for obj in detected_objects | |
| if obj['label'].lower() in ['lamp', 'plant', 'vase', 'picture frame']] | |
| # 2. Object density analysis | |
| total_objects = len(detected_objects) | |
| object_density = object_analysis_data.get('object_density', 0) | |
| # 3. Perspective and depth analysis | |
| # Analyze object positioning for depth perception | |
| if detected_objects: | |
| # Calculate object distribution across image | |
| x_positions = [] | |
| y_positions = [] | |
| object_sizes = [] | |
| for obj in detected_objects: | |
| bbox = obj['bbox'] | |
| x_center = (bbox[0] + bbox[2]) / 2 | |
| y_center = (bbox[1] + bbox[3]) / 2 | |
| obj_width = bbox[2] - bbox[0] | |
| obj_height = bbox[3] - bbox[1] | |
| obj_area = obj_width * obj_height | |
| x_positions.append(x_center) | |
| y_positions.append(y_center) | |
| object_sizes.append(obj_area) | |
| # Calculate spatial distribution | |
| x_variance = float(np.var(x_positions)) if len(x_positions) > 1 else 0.0 | |
| y_variance = float(np.var(y_positions)) if len(y_positions) > 1 else 0.0 | |
| avg_object_size = float(np.mean(object_sizes)) if object_sizes else 0.0 | |
| # Depth perception indicators | |
| size_variance = float(np.var(object_sizes)) if len(object_sizes) > 1 else 0.0 | |
| depth_indicator = float(size_variance / (avg_object_size + 1e-8)) | |
| else: | |
| x_variance = y_variance = avg_object_size = depth_indicator = 0.0 | |
| # 4. Advanced size estimation algorithm | |
| size_score = 0 | |
| size_factors = {} | |
| # Object count factor (0-30 points) | |
| if total_objects >= 10: | |
| size_score += 30 | |
| size_factors["object_count"] = "Very High" | |
| elif total_objects >= 7: | |
| size_score += 25 | |
| size_factors["object_count"] = "High" | |
| elif total_objects >= 5: | |
| size_score += 20 | |
| size_factors["object_count"] = "Medium-High" | |
| elif total_objects >= 3: | |
| size_score += 15 | |
| size_factors["object_count"] = "Medium" | |
| elif total_objects >= 1: | |
| size_score += 10 | |
| size_factors["object_count"] = "Low" | |
| else: | |
| size_factors["object_count"] = "Very Low" | |
| # Furniture type factor (0-25 points) | |
| furniture_score = 0 | |
| if len(large_furniture) >= 3: | |
| furniture_score = 25 | |
| size_factors["furniture_type"] = "Multiple large pieces" | |
| elif len(large_furniture) >= 2: | |
| furniture_score = 20 | |
| size_factors["furniture_type"] = "Several large pieces" | |
| elif len(large_furniture) >= 1: | |
| furniture_score = 15 | |
| size_factors["furniture_type"] = "Some large pieces" | |
| elif len(medium_furniture) >= 3: | |
| furniture_score = 12 | |
| size_factors["furniture_type"] = "Multiple medium pieces" | |
| elif len(medium_furniture) >= 1: | |
| furniture_score = 8 | |
| size_factors["furniture_type"] = "Some medium pieces" | |
| else: | |
| furniture_score = 5 | |
| size_factors["furniture_type"] = "Small pieces only" | |
| size_score += furniture_score | |
| # Object density factor (0-20 points) | |
| if object_density > 15: | |
| size_score += 20 | |
| size_factors["object_density"] = "Very High" | |
| elif object_density > 10: | |
| size_score += 15 | |
| size_factors["object_density"] = "High" | |
| elif object_density > 5: | |
| size_score += 10 | |
| size_factors["object_density"] = "Medium" | |
| elif object_density > 2: | |
| size_score += 5 | |
| size_factors["object_density"] = "Low" | |
| else: | |
| size_factors["object_density"] = "Very Low" | |
| # Spatial distribution factor (0-15 points) | |
| spatial_score = 0 | |
| if x_variance > 10000 and y_variance > 10000: | |
| spatial_score = 15 | |
| size_factors["spatial_distribution"] = "Wide spread" | |
| elif x_variance > 5000 or y_variance > 5000: | |
| spatial_score = 10 | |
| size_factors["spatial_distribution"] = "Moderate spread" | |
| elif x_variance > 1000 or y_variance > 1000: | |
| spatial_score = 5 | |
| size_factors["spatial_distribution"] = "Limited spread" | |
| else: | |
| size_factors["spatial_distribution"] = "Concentrated" | |
| size_score += spatial_score | |
| # Depth perception factor (0-10 points) | |
| if depth_indicator > 0.5: | |
| size_score += 10 | |
| size_factors["depth_perception"] = "Strong depth" | |
| elif depth_indicator > 0.2: | |
| size_score += 7 | |
| size_factors["depth_perception"] = "Moderate depth" | |
| elif depth_indicator > 0.1: | |
| size_score += 4 | |
| size_factors["depth_perception"] = "Limited depth" | |
| else: | |
| size_factors["depth_perception"] = "Flat perspective" | |
| # 5. Dynamic size classification | |
| if size_score >= 80: | |
| estimated_size = "Very Large (300+ sq ft)" | |
| size_category = "XL" | |
| confidence = "High" | |
| elif size_score >= 65: | |
| estimated_size = "Large (200-300 sq ft)" | |
| size_category = "L" | |
| confidence = "High" | |
| elif size_score >= 50: | |
| estimated_size = "Medium-Large (150-200 sq ft)" | |
| size_category = "ML" | |
| confidence = "Medium" | |
| elif size_score >= 35: | |
| estimated_size = "Medium (100-150 sq ft)" | |
| size_category = "M" | |
| confidence = "Medium" | |
| elif size_score >= 20: | |
| estimated_size = "Small-Medium (80-100 sq ft)" | |
| size_category = "SM" | |
| confidence = "Medium" | |
| elif size_score >= 10: | |
| estimated_size = "Small (50-80 sq ft)" | |
| size_category = "S" | |
| confidence = "Low" | |
| else: | |
| estimated_size = "Very Small (<50 sq ft)" | |
| size_category = "XS" | |
| confidence = "Low" | |
| # 6. Room type specific adjustments | |
| # Use the room_type parameter passed to the function | |
| # Adjust size based on room type expectations | |
| if room_type == 'kitchen': | |
| if size_category in ['XS', 'S']: | |
| estimated_size = "Compact Kitchen (50-80 sq ft)" | |
| elif size_category in ['M', 'ML']: | |
| estimated_size = "Standard Kitchen (100-150 sq ft)" | |
| else: | |
| estimated_size = "Large Kitchen (150+ sq ft)" | |
| elif room_type == 'bathroom': | |
| if size_category in ['XS', 'S']: | |
| estimated_size = "Standard Bathroom (40-60 sq ft)" | |
| elif size_category in ['M', 'ML']: | |
| estimated_size = "Large Bathroom (60-100 sq ft)" | |
| else: | |
| estimated_size = "Luxury Bathroom (100+ sq ft)" | |
| elif room_type == 'bedroom': | |
| if size_category in ['XS', 'S']: | |
| estimated_size = "Small Bedroom (80-120 sq ft)" | |
| elif size_category in ['M', 'ML']: | |
| estimated_size = "Standard Bedroom (120-180 sq ft)" | |
| else: | |
| estimated_size = "Large Bedroom (180+ sq ft)" | |
| # 7. Confidence calculation | |
| confidence_factors = [] | |
| if total_objects >= 5: | |
| confidence_factors.append("Multiple objects detected") | |
| if len(large_furniture) >= 1: | |
| confidence_factors.append("Large furniture present") | |
| if object_density > 5: | |
| confidence_factors.append("Good object density") | |
| if x_variance > 5000 and y_variance > 5000: | |
| confidence_factors.append("Good spatial distribution") | |
| if len(confidence_factors) >= 3: | |
| confidence = "High" | |
| elif len(confidence_factors) >= 2: | |
| confidence = "Medium" | |
| else: | |
| confidence = "Low" | |
| return { | |
| "estimated_size": estimated_size, | |
| "size_category": size_category, | |
| "confidence": confidence, | |
| "confidence_factors": confidence_factors, | |
| "size_score": int(size_score), # Convert to Python int | |
| "size_factors": size_factors, | |
| "object_analysis": { | |
| "total_objects": int(total_objects), # Convert to Python int | |
| "large_furniture": int(len(large_furniture)), # Convert to Python int | |
| "medium_furniture": int(len(medium_furniture)), # Convert to Python int | |
| "small_furniture": int(len(small_furniture)), # Convert to Python int | |
| "object_density": float(round(object_density, 2)), # Convert to Python float | |
| "spatial_variance": { | |
| "x_variance": float(round(x_variance, 2)), # Convert to Python float | |
| "y_variance": float(round(y_variance, 2)) # Convert to Python float | |
| }, | |
| "depth_indicator": float(round(depth_indicator, 3)) # Convert to Python float | |
| } | |
| } | |
| except Exception as e: | |
| logger.error(f"Error in room size estimation: {str(e)}") | |
| return { | |
| "estimated_size": "Unable to estimate", | |
| "size_category": "Unknown", | |
| "confidence": "Low", | |
| "confidence_factors": ["Estimation failed"], | |
| "size_score": 0, | |
| "size_factors": {}, | |
| "object_analysis": { | |
| "total_objects": 0, | |
| "large_furniture": 0, | |
| "medium_furniture": 0, | |
| "small_furniture": 0, | |
| "object_density": 0.0, | |
| "spatial_variance": {"x_variance": 0.0, "y_variance": 0.0}, | |
| "depth_indicator": 0.0 | |
| } | |
| } | |
| def generate_property_insights(room_classification, quality_analysis, object_detection, image_caption): | |
| """Advanced AI-powered property insights generation using dynamic analysis""" | |
| try: | |
| insights = { | |
| "marketing_tips": [], | |
| "target_audience": [], | |
| "pricing_considerations": [], | |
| "improvement_suggestions": [], | |
| "market_positioning": {}, | |
| "competitive_analysis": {}, | |
| "investment_potential": {}, | |
| "staging_recommendations": [] | |
| } | |
| # Extract key data | |
| room_type = room_classification.get('room_type', 'unknown') | |
| room_confidence = room_classification.get('confidence', 0) | |
| quality_score = quality_analysis.get('quality_score', 0) | |
| quality_level = quality_analysis.get('quality_level', 'Unknown') | |
| object_analysis = object_detection.get('analysis', {}) | |
| detected_objects = object_detection.get('objects', []) | |
| caption = image_caption.get('caption', '') | |
| # 1. Dynamic Marketing Tips Generation | |
| marketing_tips = [] | |
| # Quality-based marketing tips | |
| if quality_score >= 80: | |
| marketing_tips.append("Highlight the professional photography quality in listings") | |
| marketing_tips.append("Use this image as a primary showcase photo") | |
| elif quality_score >= 60: | |
| marketing_tips.append("Consider this image for secondary photo positions") | |
| else: | |
| marketing_tips.append("Use this image sparingly or improve before listing") | |
| # Room-specific marketing tips | |
| if room_type == 'kitchen': | |
| marketing_tips.append("Emphasize modern kitchen features and appliances") | |
| marketing_tips.append("Highlight the kitchen as the heart of the home") | |
| if object_analysis.get('appliance_count', 0) >= 3: | |
| marketing_tips.append("Showcase the fully equipped kitchen") | |
| elif room_type == 'bathroom': | |
| marketing_tips.append("Focus on cleanliness and modern fixtures") | |
| marketing_tips.append("Highlight bathroom luxury and comfort") | |
| elif room_type == 'bedroom': | |
| marketing_tips.append("Emphasize comfort and relaxation potential") | |
| marketing_tips.append("Highlight bedroom size and natural light") | |
| elif room_type == 'living room': | |
| marketing_tips.append("Showcase entertainment and family gathering spaces") | |
| marketing_tips.append("Highlight living room versatility and flow") | |
| # Object-based marketing tips | |
| furniture_count = object_analysis.get('furniture_count', 0) | |
| if furniture_count >= 5: | |
| marketing_tips.append("Emphasize the fully furnished and move-in ready aspect") | |
| elif furniture_count >= 3: | |
| marketing_tips.append("Highlight the well-appointed space") | |
| # 2. Advanced Target Audience Analysis | |
| target_audience = [] | |
| # Room type based targeting | |
| if room_type == 'kitchen': | |
| target_audience.extend(["Home chefs", "Families", "Entertainment enthusiasts"]) | |
| elif room_type == 'bathroom': | |
| target_audience.extend(["Luxury seekers", "Families", "Young professionals"]) | |
| elif room_type == 'bedroom': | |
| target_audience.extend(["Families", "Young professionals", "Students"]) | |
| elif room_type == 'living room': | |
| target_audience.extend(["Families", "Entertainment lovers", "Social people"]) | |
| # Quality-based targeting | |
| if quality_score >= 85: | |
| target_audience.append("Luxury buyers") | |
| elif quality_score >= 70: | |
| target_audience.append("Professional buyers") | |
| else: | |
| target_audience.append("Budget-conscious buyers") | |
| # Object-based targeting | |
| if object_analysis.get('appliance_count', 0) >= 3: | |
| target_audience.append("Modern lifestyle seekers") | |
| if object_analysis.get('fixture_count', 0) >= 3: | |
| target_audience.append("Quality-conscious buyers") | |
| # 3. Dynamic Pricing Considerations | |
| pricing_considerations = [] | |
| # Quality impact on pricing | |
| if quality_score >= 80: | |
| pricing_considerations.append("High-quality images can support premium pricing") | |
| elif quality_score >= 60: | |
| pricing_considerations.append("Standard image quality supports market-rate pricing") | |
| else: | |
| pricing_considerations.append("Image quality may limit pricing potential") | |
| # Room type pricing impact | |
| if room_type == 'kitchen': | |
| pricing_considerations.append("Kitchen quality significantly impacts property value") | |
| if object_analysis.get('appliance_count', 0) >= 3: | |
| pricing_considerations.append("Modern appliances justify higher pricing") | |
| elif room_type == 'bathroom': | |
| pricing_considerations.append("Bathroom luxury can command premium pricing") | |
| elif room_type == 'bedroom': | |
| pricing_considerations.append("Bedroom appeal affects family buyer pricing") | |
| # Object density pricing impact | |
| object_density = object_analysis.get('object_density', 0) | |
| if object_density > 10: | |
| pricing_considerations.append("Rich content suggests well-appointed property") | |
| elif object_density < 3: | |
| pricing_considerations.append("Sparse content may suggest staging needed") | |
| # 4. AI-Powered Improvement Suggestions | |
| improvement_suggestions = [] | |
| # Technical improvements | |
| if quality_score < 70: | |
| improvement_suggestions.append("Improve lighting and exposure for better image quality") | |
| improvement_suggestions.append("Use professional photography equipment") | |
| # Composition improvements | |
| composition_balance = object_analysis.get('composition_balance', 0) | |
| if composition_balance < 0.4: | |
| improvement_suggestions.append("Reposition camera for better composition") | |
| improvement_suggestions.append("Follow rule of thirds for better framing") | |
| # Content improvements | |
| total_objects = object_analysis.get('total_objects', 0) | |
| if total_objects < 3: | |
| improvement_suggestions.append("Add more furniture or decorative elements") | |
| improvement_suggestions.append("Consider professional staging") | |
| # Room-specific improvements | |
| if room_type == 'kitchen' and object_analysis.get('appliance_count', 0) < 2: | |
| improvement_suggestions.append("Highlight kitchen appliances and features") | |
| elif room_type == 'bathroom' and object_analysis.get('fixture_count', 0) < 2: | |
| improvement_suggestions.append("Showcase bathroom fixtures and amenities") | |
| # 5. Advanced Market Positioning Analysis | |
| market_positioning = {} | |
| # Quality positioning | |
| if quality_score >= 85: | |
| market_positioning["quality_tier"] = "Premium" | |
| market_positioning["positioning_statement"] = "Luxury property with professional presentation" | |
| elif quality_score >= 70: | |
| market_positioning["quality_tier"] = "Professional" | |
| market_positioning["positioning_statement"] = "Well-presented property suitable for discerning buyers" | |
| elif quality_score >= 50: | |
| market_positioning["quality_tier"] = "Standard" | |
| market_positioning["positioning_statement"] = "Standard property presentation" | |
| else: | |
| market_positioning["quality_tier"] = "Needs Improvement" | |
| market_positioning["positioning_statement"] = "Property requires better presentation" | |
| # Room type positioning | |
| if room_type in ['kitchen', 'bathroom']: | |
| market_positioning["focus_area"] = "High-value rooms" | |
| market_positioning["key_message"] = f"Showcase {room_type} quality and features" | |
| else: | |
| market_positioning["focus_area"] = "Living spaces" | |
| market_positioning["key_message"] = f"Highlight {room_type} comfort and functionality" | |
| # 6. Competitive Analysis | |
| competitive_analysis = {} | |
| # Quality comparison | |
| if quality_score >= 80: | |
| competitive_analysis["quality_advantage"] = "Above average" | |
| competitive_analysis["differentiation"] = "Professional presentation sets property apart" | |
| elif quality_score >= 60: | |
| competitive_analysis["quality_advantage"] = "Competitive" | |
| competitive_analysis["differentiation"] = "Standard presentation meets market expectations" | |
| else: | |
| competitive_analysis["quality_advantage"] = "Below average" | |
| competitive_analysis["differentiation"] = "Needs improvement to compete effectively" | |
| # Content comparison | |
| if object_analysis.get('total_objects', 0) >= 8: | |
| competitive_analysis["content_advantage"] = "Rich content" | |
| competitive_analysis["content_message"] = "Well-appointed space with many features" | |
| elif object_analysis.get('total_objects', 0) >= 5: | |
| competitive_analysis["content_advantage"] = "Good content" | |
| competitive_analysis["content_message"] = "Adequate feature representation" | |
| else: | |
| competitive_analysis["content_advantage"] = "Limited content" | |
| competitive_analysis["content_message"] = "Consider adding more features" | |
| # 7. Investment Potential Analysis | |
| investment_potential = {} | |
| # Quality impact on investment | |
| if quality_score >= 80: | |
| investment_potential["presentation_value"] = "High" | |
| investment_potential["roi_potential"] = "Excellent - professional presentation supports premium pricing" | |
| elif quality_score >= 60: | |
| investment_potential["presentation_value"] = "Medium" | |
| investment_potential["roi_potential"] = "Good - standard presentation supports market pricing" | |
| else: | |
| investment_potential["presentation_value"] = "Low" | |
| investment_potential["roi_potential"] = "Limited - poor presentation may reduce pricing potential" | |
| # Room type investment impact | |
| if room_type in ['kitchen', 'bathroom']: | |
| investment_potential["room_value"] = "High-impact rooms" | |
| investment_potential["investment_priority"] = "Focus on these rooms for maximum ROI" | |
| else: | |
| investment_potential["room_value"] = "Standard rooms" | |
| investment_potential["investment_priority"] = "Ensure adequate presentation quality" | |
| # 8. Dynamic Staging Recommendations | |
| staging_recommendations = [] | |
| # Object-based staging | |
| if object_analysis.get('furniture_count', 0) < 2: | |
| staging_recommendations.append("Add key furniture pieces to define the space") | |
| if object_analysis.get('appliance_count', 0) < 2 and room_type == 'kitchen': | |
| staging_recommendations.append("Highlight kitchen appliances and modern features") | |
| if object_analysis.get('fixture_count', 0) < 2 and room_type == 'bathroom': | |
| staging_recommendations.append("Showcase bathroom fixtures and luxury amenities") | |
| # Quality-based staging | |
| if quality_score < 60: | |
| staging_recommendations.append("Improve lighting and staging for better presentation") | |
| # Room-specific staging | |
| if room_type == 'bedroom': | |
| staging_recommendations.append("Create a welcoming and comfortable bedroom atmosphere") | |
| elif room_type == 'living room': | |
| staging_recommendations.append("Stage for entertainment and family gatherings") | |
| # Update insights dictionary | |
| insights.update({ | |
| "marketing_tips": marketing_tips, | |
| "target_audience": list(set(target_audience)), # Remove duplicates | |
| "pricing_considerations": pricing_considerations, | |
| "improvement_suggestions": improvement_suggestions, | |
| "market_positioning": market_positioning, | |
| "competitive_analysis": competitive_analysis, | |
| "investment_potential": investment_potential, | |
| "staging_recommendations": staging_recommendations | |
| }) | |
| return insights | |
| except Exception as e: | |
| logger.error(f"Error in generating property insights: {str(e)}") | |
| return { | |
| "marketing_tips": ["Unable to generate marketing tips"], | |
| "target_audience": ["General buyers"], | |
| "pricing_considerations": ["Standard market pricing"], | |
| "improvement_suggestions": ["Improve image quality and content"], | |
| "market_positioning": {"quality_tier": "Unknown"}, | |
| "competitive_analysis": {"quality_advantage": "Unknown"}, | |
| "investment_potential": {"presentation_value": "Unknown"}, | |
| "staging_recommendations": ["Consider professional staging"] | |
| } | |
| def analyze_scene_with_clip(image, room_type): | |
| """Advanced scene understanding using CLIP model""" | |
| try: | |
| if not clip_processor or not clip_model: | |
| return {'scene_concepts': [], 'style_analysis': {}} | |
| # Define comprehensive real estate concepts | |
| real_estate_concepts = [ | |
| # Style concepts | |
| "modern design", "traditional style", "contemporary", "luxury", "minimalist", "rustic", "industrial", | |
| "scandinavian", "mediterranean", "asian fusion", "art deco", "mid-century modern", | |
| # Quality indicators | |
| "high-end finishes", "premium materials", "custom details", "architectural features", | |
| "natural light", "open floor plan", "high ceilings", "hardwood floors", "granite countertops", | |
| # Functionality | |
| "functional layout", "efficient design", "storage solutions", "entertainment space", | |
| "home office", "flexible space", "outdoor living", "smart home features", | |
| # Room-specific concepts | |
| "gourmet kitchen", "spa-like bathroom", "master suite", "entertainment room", | |
| "dining area", "living space", "bedroom retreat", "home gym", "wine cellar" | |
| ] | |
| # Process image and text | |
| inputs = clip_processor(images=image, text=real_estate_concepts, return_tensors="pt", padding=True) | |
| with torch.no_grad(): | |
| outputs = clip_model(**inputs) | |
| logits_per_image = outputs.logits_per_image | |
| probs = logits_per_image.softmax(dim=-1) | |
| # Get top matches | |
| top_concepts = [] | |
| for i, prob in enumerate(probs[0]): | |
| if prob > 0.2: # Lower threshold for more concepts | |
| top_concepts.append({ | |
| "concept": real_estate_concepts[i], | |
| "confidence": float(prob) | |
| }) | |
| # Analyze style patterns | |
| style_keywords = ["modern", "traditional", "contemporary", "luxury", "minimalist", "rustic", "industrial"] | |
| style_scores = {} | |
| for style in style_keywords: | |
| style_score = sum(concept["confidence"] for concept in top_concepts if style in concept["concept"].lower()) | |
| if style_score > 0: | |
| style_scores[style] = style_score | |
| # Determine dominant style | |
| dominant_style = max(style_scores.items(), key=lambda x: x[1]) if style_scores else ("unknown", 0) | |
| return { | |
| "scene_concepts": sorted(top_concepts, key=lambda x: x["confidence"], reverse=True)[:10], | |
| "style_analysis": { | |
| "dominant_style": dominant_style[0], | |
| "style_confidence": dominant_style[1], | |
| "all_style_scores": style_scores, | |
| "quality_indicators": [concept for concept in top_concepts if "quality" in concept["concept"].lower() or "premium" in concept["concept"].lower()], | |
| "functionality_indicators": [concept for concept in top_concepts if "functional" in concept["concept"].lower() or "efficient" in concept["concept"].lower()] | |
| } | |
| } | |
| except Exception as e: | |
| logger.error(f"CLIP scene analysis failed: {str(e)}") | |
| return {'scene_concepts': [], 'style_analysis': {}} | |
| def analyze_market_positioning(room_classification, quality_analysis, object_detection, property_assessment): | |
| """Advanced market positioning analysis""" | |
| try: | |
| market_analysis = { | |
| "market_tier": "Unknown", | |
| "competitive_position": "Unknown", | |
| "price_positioning": "Unknown", | |
| "target_market": "Unknown", | |
| "differentiation_factors": [], | |
| "market_opportunities": [], | |
| "competitive_advantages": [], | |
| "market_risks": [] | |
| } | |
| # Extract key metrics | |
| quality_score = quality_analysis.get('quality_score', 0) | |
| overall_score = property_assessment.get('overall_score', 0) | |
| room_type = room_classification.get('room_type', 'unknown') | |
| room_confidence = room_classification.get('confidence', 0) | |
| object_analysis = object_detection.get('analysis', {}) | |
| # 1. Market Tier Analysis | |
| if overall_score >= 85: | |
| market_analysis["market_tier"] = "Luxury" | |
| market_analysis["price_positioning"] = "Premium" | |
| market_analysis["target_market"] = "High-end buyers, investors" | |
| elif overall_score >= 75: | |
| market_analysis["market_tier"] = "Premium" | |
| market_analysis["price_positioning"] = "Above market" | |
| market_analysis["target_market"] = "Professional buyers, families" | |
| elif overall_score >= 60: | |
| market_analysis["market_tier"] = "Standard" | |
| market_analysis["price_positioning"] = "Market rate" | |
| market_analysis["target_market"] = "General buyers" | |
| else: | |
| market_analysis["market_tier"] = "Value" | |
| market_analysis["price_positioning"] = "Below market" | |
| market_analysis["target_market"] = "Budget-conscious buyers" | |
| # 2. Competitive Position Analysis | |
| if quality_score >= 80 and room_confidence >= 80: | |
| market_analysis["competitive_position"] = "Strong" | |
| market_analysis["competitive_advantages"].append("High-quality presentation") | |
| market_analysis["competitive_advantages"].append("Clear room identification") | |
| elif quality_score >= 60 and room_confidence >= 60: | |
| market_analysis["competitive_position"] = "Competitive" | |
| market_analysis["competitive_advantages"].append("Good presentation quality") | |
| else: | |
| market_analysis["competitive_position"] = "Needs improvement" | |
| market_analysis["market_risks"].append("Poor presentation may limit appeal") | |
| # 3. Room-specific positioning | |
| if room_type in ['kitchen', 'bathroom']: | |
| appliance_count = object_analysis.get('appliance_count', 0) | |
| fixture_count = object_analysis.get('fixture_count', 0) | |
| if room_type == 'kitchen' and appliance_count >= 3: | |
| market_analysis["differentiation_factors"].append("Fully equipped kitchen") | |
| market_analysis["competitive_advantages"].append("Modern kitchen appliances") | |
| elif room_type == 'bathroom' and fixture_count >= 2: | |
| market_analysis["differentiation_factors"].append("Well-appointed bathroom") | |
| market_analysis["competitive_advantages"].append("Quality bathroom fixtures") | |
| # 4. Object density analysis | |
| object_density = object_analysis.get('object_density', 0) | |
| if object_density > 10: | |
| market_analysis["differentiation_factors"].append("Rich content and features") | |
| market_analysis["competitive_advantages"].append("Well-appointed space") | |
| elif object_density < 3: | |
| market_analysis["market_risks"].append("Limited content may reduce appeal") | |
| # 5. Market opportunities | |
| if quality_score < 70: | |
| market_analysis["market_opportunities"].append("Improve image quality for better positioning") | |
| if room_confidence < 70: | |
| market_analysis["market_opportunities"].append("Enhance room type clarity") | |
| if object_density < 5: | |
| market_analysis["market_opportunities"].append("Add more features and furniture") | |
| # 6. Professional grade analysis | |
| if property_assessment.get('professional_grade', False): | |
| market_analysis["competitive_advantages"].append("Professional-grade presentation") | |
| market_analysis["differentiation_factors"].append("High-quality photography") | |
| else: | |
| market_analysis["market_opportunities"].append("Consider professional photography") | |
| return market_analysis | |
| except Exception as e: | |
| logger.error(f"Market positioning analysis failed: {str(e)}") | |
| return { | |
| "market_tier": "Unknown", | |
| "competitive_position": "Unknown", | |
| "price_positioning": "Unknown", | |
| "target_market": "Unknown", | |
| "differentiation_factors": [], | |
| "market_opportunities": [], | |
| "competitive_advantages": [], | |
| "market_risks": [] | |
| } | |
| def classify_room_type(image): | |
| """Classify room type using the loaded room classifier""" | |
| try: | |
| if room_classifier is None: | |
| return {'room_type': 'unknown', 'confidence': 0} | |
| results = room_classifier(image) | |
| return { | |
| 'room_type': results[0]['label'].lower(), | |
| 'confidence': round(results[0]['score'] * 100, 2) | |
| } | |
| except Exception as e: | |
| logger.error(f"Room classification failed: {str(e)}") | |
| return {'room_type': 'unknown', 'confidence': 0} | |
| if __name__ == '__main__': | |
| # Load models on startup | |
| logger.info("Starting Real Estate Image Analyzer...") | |
| load_models() | |
| # Get port from environment variable or use default | |
| port = int(os.environ.get('PORT', 7860)) | |
| # For Hugging Face Spaces, use 0.0.0.0 to bind to all interfaces | |
| host = '0.0.0.0' | |
| logger.info(f"Starting Flask app on {host}:{port}") | |
| logger.info(f"Access your application at: http://localhost:{port}") | |
| # Start Flask app | |
| app.run(port=port, debug=False, host=host) | |