Spaces:
Sleeping
Sleeping
| """ | |
| Drawing helpers for pipeline visual outputs. | |
| All functions operate on RGB numpy arrays (uint8) and return RGB numpy arrays. | |
| They are stateless - no model or pipeline state is needed. | |
| """ | |
| import cv2 | |
| import numpy as np | |
| # Detection overlay | |
| def make_detection_overlay( | |
| image_rgb : np.ndarray, | |
| boxes : list[tuple], | |
| label : str | None = None, | |
| ) -> np.ndarray: | |
| """ | |
| Draw YOLO bounding boxes on the image. | |
| The box colour reflects the final classification label: | |
| - MALIGNANT -> red (#DC3232) | |
| - BENIGN -> green (#32B450) | |
| - Unknown -> grey (#B4B4B4) | |
| Parameters | |
| image_rgb : np.ndarray (H, W, 3) uint8, RGB | |
| boxes : list of (x1, y1, x2, y2, conf) tuples from NoduleDetector | |
| label : "MALIGNANT", "BENIGN", or None (classification not yet done) | |
| Returns | |
| ------- | |
| np.ndarray (H, W, 3) uint8, RGB - image with bounding boxes drawn | |
| """ | |
| overlay = image_rgb.copy() | |
| if label == "MALIGNANT": | |
| color = (220, 50, 50) | |
| elif label == "BENIGN": | |
| color = (50, 180, 80) | |
| else: | |
| color = (180, 180, 180) | |
| for (x1, y1, x2, y2, conf) in boxes: | |
| cv2.rectangle(overlay, (x1, y1), (x2, y2), color, 2) | |
| text = f"Nodule {conf:.0%}" | |
| org = (x1, max(y1 - 8, 12)) | |
| cv2.putText( | |
| overlay, text, org, | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, | |
| cv2.LINE_AA, | |
| ) | |
| return overlay | |
| # Segmentation overlay | |
| def make_segmentation_overlay( | |
| image_rgb : np.ndarray, | |
| mask : np.ndarray, | |
| alpha : float = 0.40, | |
| ) -> np.ndarray: | |
| """ | |
| Blend the UNet++ binary mask as a semi-transparent orange overlay. | |
| A solid contour is drawn on top for clearer boundary visualisation. | |
| Parameters | |
| image_rgb : np.ndarray (H, W, 3) uint8, RGB | |
| mask : np.ndarray (H, W) uint8, 0/255 | |
| alpha : float — opacity of the colour overlay (default 0.40) | |
| Returns | |
| np.ndarray (H, W, 3) uint8, RGB - blended image | |
| """ | |
| color_mask = np.zeros_like(image_rgb) | |
| color_mask[mask > 0] = [255, 140, 0] # orange | |
| blended = cv2.addWeighted(image_rgb, 1.0, | |
| color_mask, alpha, 0) | |
| # Draw contour outline for precise boundary visualisation | |
| contours, _ = cv2.findContours( | |
| mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE | |
| ) | |
| cv2.drawContours(blended, contours, -1, (255, 140, 0), 2) | |
| return blended | |