Spaces:
Sleeping
Sleeping
| import numpy as np | |
| import cv2 | |
| import torch | |
| from segment_anything import SamPredictor, sam_model_registry | |
| from diffusers import StableDiffusionInpaintPipeline | |
| import gradio as gr | |
| from PIL import Image, ImageDraw | |
| import matplotlib.pyplot as plt | |
| from matplotlib.colors import LinearSegmentedColormap | |
| import tempfile | |
| import os | |
| # Initialize models (cached for performance) | |
| sam_model = None | |
| sd_pipe = None | |
| predictor = None | |
| def load_models(): | |
| global sam_model, sd_pipe, predictor | |
| if sam_model is None: | |
| print("Loading SAM model...") | |
| sam_model = sam_model_registry["vit_b"](checkpoint="sam_vit_b_01ec64.pth") # Use available checkpoint | |
| missing_keys, unexpected_keys = sam_model.load_state_dict( | |
| torch.load("sam_vit_b_01ec64.pth"), strict=False | |
| ) | |
| if missing_keys or unexpected_keys: | |
| print(f"Warning: Missing keys: {missing_keys}, Unexpected keys: {unexpected_keys}") | |
| predictor = SamPredictor(sam_model) | |
| if sd_pipe is None: | |
| print("Loading Stable Diffusion model...") | |
| sd_pipe = StableDiffusionInpaintPipeline.from_pretrained( | |
| "stabilityai/stable-diffusion-2-inpainting", | |
| torch_dtype=torch.float16, | |
| safety_checker=None | |
| ).to("cuda") | |
| return predictor, sd_pipe | |
| def process_house_image(image, scale): | |
| predictor, _ = load_models() | |
| # Convert to RGB and numpy array | |
| image = np.array(image) | |
| if image.shape[-1] == 4: # Remove alpha channel if exists | |
| image = image[..., :3] | |
| # Process with SAM | |
| predictor.set_image(image) | |
| masks, scores, _ = predictor.predict() | |
| # Filter for roof segments | |
| roof_masks = [] | |
| for i, mask in enumerate(masks): | |
| # Basic roof filtering (position and size) | |
| y_indices, x_indices = np.where(mask) | |
| if len(y_indices) == 0: | |
| continue | |
| centroid_y = np.mean(y_indices) | |
| height_ratio = (np.max(y_indices) - np.min(y_indices)) / image.shape[0] | |
| # Roofs are typically in upper half and cover significant vertical space | |
| if centroid_y < image.shape[0] * 0.6 and height_ratio > 0.1: | |
| roof_masks.append((mask, scores[i])) | |
| # Sort by score and select top masks | |
| roof_masks.sort(key=lambda x: x[1], reverse=True) | |
| final_mask = np.zeros(image.shape[:2], dtype=bool) | |
| # Combine top 3 roof masks | |
| for mask, _ in roof_masks[:3]: | |
| final_mask = np.logical_or(final_mask, mask) | |
| # Create overlay visualization | |
| overlay = image.copy() | |
| cmap = LinearSegmentedColormap.from_list('roof_cmap', ['#00000000', '#ff000080']) | |
| mask_rgb = (cmap(final_mask.astype(float))[..., :3] * 255).astype(np.uint8) | |
| overlay = (0.6 * overlay + 0.4 * mask_rgb).astype(np.uint8) | |
| # Calculate area | |
| roof_pixels = np.sum(final_mask) | |
| roof_area = roof_pixels / (scale ** 2) # in square meters | |
| return overlay, final_mask, roof_area | |
| def calculate_sheets(roof_area, sheet_width, sheet_height, waste_factor=0.15): | |
| sheet_area = sheet_width * sheet_height | |
| sheets = (roof_area / sheet_area) * (1 + waste_factor) | |
| return int(np.ceil(sheets)) | |
| def generate_new_roof(image, roof_mask, pattern_prompt, sheet_width, sheet_height): | |
| _, sd_pipe = load_models() | |
| # Convert to PIL format | |
| image_pil = Image.fromarray(image) | |
| # Convert mask to PIL format | |
| mask_pil = Image.fromarray(roof_mask.astype(np.uint8) * 255) | |
| # Enhance prompt with sheet dimensions | |
| enhanced_prompt = f"{pattern_prompt}, {sheet_width:.2f}m x {sheet_height:.2f}m sheets, architectural visualization, photorealistic" | |
| # Generate new roof | |
| result = sd_pipe( | |
| prompt=enhanced_prompt, | |
| image=image_pil, | |
| mask_image=mask_pil, | |
| num_inference_steps=30, | |
| guidance_scale=7.5 | |
| ).images[0] | |
| return result | |
| def full_process(image, scale, sheet_width, sheet_height, pattern_prompt): | |
| # Convert image to numpy array | |
| if isinstance(image, str): | |
| image = np.array(Image.open(image)) | |
| else: | |
| image = np.array(image) | |
| # Process image to get roof mask | |
| overlay, roof_mask, roof_area = process_house_image(image, scale) | |
| # Calculate sheets needed | |
| sheets_needed = calculate_sheets(roof_area, sheet_width, sheet_height) | |
| # Generate new roof visualization | |
| new_roof_image = generate_new_roof(image, roof_mask, pattern_prompt, sheet_width, sheet_height) | |
| # Create result visualization | |
| fig, ax = plt.subplots(1, 3, figsize=(18, 6)) | |
| # Original with overlay | |
| ax[0].imshow(overlay) | |
| ax[0].set_title("Roof Segmentation") | |
| ax[0].axis('off') | |
| # New roof | |
| ax[1].imshow(new_roof_image) | |
| ax[1].set_title("New Roof Design") | |
| ax[1].axis('off') | |
| # Info panel | |
| info_text = f"Roof Area: {roof_area:.2f} m²\n\n" \ | |
| f"Sheet Size: {sheet_width} × {sheet_height} m\n\n" \ | |
| f"Sheets Needed: {sheets_needed}\n\n" \ | |
| f"Pattern: {pattern_prompt}" | |
| ax[2].text(0.1, 0.5, info_text, fontsize=12, | |
| bbox=dict(facecolor='white', alpha=0.8)) | |
| ax[2].axis('off') | |
| plt.tight_layout() | |
| # Save to temp file | |
| temp_file = tempfile.NamedTemporaryFile(suffix=".png", delete=False) | |
| plt.savefig(temp_file.name, bbox_inches='tight') | |
| plt.close() | |
| return temp_file.name, sheets_needed | |
| # Gradio interface | |
| with gr.Blocks(title="Roof Renovation System", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# 🏠 Roof Segmentation & Renovation System") | |
| gr.Markdown("Upload a house image, specify dimensions, and visualize new roof designs with material calculations") | |
| with gr.Row(): | |
| with gr.Column(): | |
| image_input = gr.Image(label="Upload House Image", type="filepath") | |
| scale_input = gr.Slider(1, 500, value=100, | |
| label="Scale (pixels per meter)", | |
| info="Adjust based on image perspective") | |
| pattern_prompt = gr.Textbox(label="Roof Pattern Description", | |
| value="modern red tile pattern", | |
| placeholder="Describe the new roof pattern") | |
| with gr.Row(): | |
| sheet_width = gr.Number(label="Sheet Width (meters)", value=0.5) | |
| sheet_height = gr.Number(label="Sheet Height (meters)", value=2.0) | |
| submit_btn = gr.Button("Generate Roof Design", variant="primary") | |
| with gr.Column(): | |
| output_image = gr.Image(label="Results", interactive=False) | |
| sheets_output = gr.Number(label="Sheets Needed", interactive=False) | |
| submit_btn.click( | |
| fn=full_process, | |
| inputs=[image_input, scale_input, sheet_width, sheet_height, pattern_prompt], | |
| outputs=[output_image, sheets_output] | |
| ) | |
| gr.Markdown("### How It Works:") | |
| gr.Markdown("1. Upload a house image (aerial or perspective view) \n" | |
| "2. Adjust the scale (pixels per meter) based on image perspective \n" | |
| "3. Enter new roof sheet dimensions \n" | |
| "4. Describe your desired roof pattern \n" | |
| "5. Click generate to see the new design and material requirements") | |
| gr.Markdown("### Technical Notes:") | |
| gr.Markdown("- Uses Meta's Segment Anything Model (SAM) for roof detection \n" | |
| "- Utilizes Stable Diffusion for realistic roof pattern generation \n" | |
| "- Material calculations include 15% wastage factor \n" | |
| "- Processing may take 20-40 seconds depending on image size") | |
| # For Hugging Face Spaces deployment | |
| if __name__ == "__main__": | |
| # Create example images directory if needed | |
| os.makedirs("examples", exist_ok=True) | |
| # Run the app | |
| demo.launch() |