""" Instance Processor — DefectDiffu Edition Stripped-down version: DefectDiffu does NOT use 16x16 patch mappings, so all patch-based artifact logic has been removed. Retained utilities: - bbox ↔ mask helpers (for verification cropping) - IoU calculation - Visualization helpers """ import random import torch import numpy as np from PIL import Image from typing import List, Dict, Tuple, Optional, Union import matplotlib.pyplot as plt import matplotlib.patches as patches class InstanceProcessor: """Utility class for detection post-processing and mask operations.""" @staticmethod def calculate_iou(box1: Union[List, np.ndarray], box2: Union[List, np.ndarray]) -> float: x1 = max(box1[0], box2[0]) y1 = max(box1[1], box2[1]) x2 = min(box1[2], box2[2]) y2 = min(box1[3], box2[3]) if x2 <= x1 or y2 <= y1: return 0.0 intersection = (x2 - x1) * (y2 - y1) area1 = (box1[2] - box1[0]) * (box1[3] - box1[1]) area2 = (box2[2] - box2[0]) * (box2[3] - box2[1]) union = area1 + area2 - intersection return intersection / union if union > 0 else 0.0 @staticmethod def mask_from_bbox(bbox: Tuple[int, int, int, int], img_shape: Tuple[int, ...]) -> np.ndarray: """Create a binary mask from a bounding box.""" h, w = img_shape[:2] mask = np.zeros((h, w), dtype=np.uint8) x1, y1, x2, y2 = bbox x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(w, x2), min(h, y2) if x2 > x1 and y2 > y1: mask[y1:y2, x1:x2] = 1 return mask @staticmethod def get_bbox_from_mask(mask: np.ndarray, margin: int = 0) -> Tuple[int, int, int, int]: """Compute tight bounding box from binary mask, with optional margin.""" ys, xs = np.where(mask > 0) if len(ys) == 0: return (0, 0, 0, 0) y1, y2 = ys.min(), ys.max() x1, x2 = xs.min(), xs.max() h, w = mask.shape x1 = max(0, x1 - margin) y1 = max(0, y1 - margin) x2 = min(w, x2 + margin) y2 = min(h, y2 + margin) return (x1, y1, x2, y2) @staticmethod def visualize_generation_result( original_image: np.ndarray, generated_image: np.ndarray, defect_mask: np.ndarray, output_path: str, title: str = "DefectDiffu Generation Result" ): """Create a 3-panel visualization: original, generated, mask overlay.""" fig, axes = plt.subplots(1, 3, figsize=(18, 6)) axes[0].imshow(original_image) axes[0].set_title("Original (Planning Reference)") axes[0].axis("off") axes[1].imshow(generated_image) axes[1].set_title("Generated Defect Image") axes[1].axis("off") axes[2].imshow(generated_image) axes[2].imshow(defect_mask, alpha=0.5, cmap="Reds") axes[2].set_title("Defect Mask Overlay") axes[2].axis("off") fig.suptitle(title, fontsize=14) plt.tight_layout() plt.savefig(output_path, dpi=150, bbox_inches="tight") plt.close(fig) print(f"[Viz] Saved result visualization to {output_path}") @staticmethod def resize_to_square(image: np.ndarray, size: int = 512) -> np.ndarray: """Resize image to square (DefectDiffu expects 512x512).""" pil_img = Image.fromarray(image) if isinstance(image, np.ndarray) else image pil_img = pil_img.resize((size, size), Image.LANCZOS) return np.array(pil_img)