CoolFace
Apppublic

Roboflow/SoM

sourceHugging Facemitupdated 3y agoView on Hugging Face
75likes
utils.py169 linesDownload Raw Back to root
1import cv22import som3 4import numpy as np5import supervision as sv6 7 8class Visualizer:9 10    def __init__(11        self,12        line_thickness: int = 2,13        mask_opacity: float = 0.1,14        text_scale: float = 0.615    ) -> None:16        self.box_annotator = sv.BoundingBoxAnnotator(17            color_lookup=sv.ColorLookup.INDEX,18            thickness=line_thickness)19        self.mask_annotator = sv.MaskAnnotator(20            color_lookup=sv.ColorLookup.INDEX,21            opacity=mask_opacity)22        self.polygon_annotator = sv.PolygonAnnotator(23            color_lookup=sv.ColorLookup.INDEX,24            thickness=line_thickness)25        self.label_annotator = sv.LabelAnnotator(26            color=sv.Color.black(),27            text_color=sv.Color.white(),28            color_lookup=sv.ColorLookup.INDEX,29            text_position=sv.Position.CENTER_OF_MASS,30            text_scale=text_scale)31 32    def visualize(33        self,34        image: np.ndarray,35        detections: sv.Detections,36        with_box: bool,37        with_mask: bool,38        with_polygon: bool,39        with_label: bool40    ) -> np.ndarray:41        annotated_image = image.copy()42        if with_box:43            annotated_image = self.box_annotator.annotate(44                scene=annotated_image, detections=detections)45        if with_mask:46            annotated_image = self.mask_annotator.annotate(47                scene=annotated_image, detections=detections)48        if with_polygon:49            annotated_image = self.polygon_annotator.annotate(50                scene=annotated_image, detections=detections)51        if with_label:52            labels = list(map(str, range(len(detections))))53            annotated_image = self.label_annotator.annotate(54                scene=annotated_image, detections=detections, labels=labels)55        return annotated_image56 57 58def refine_mask(59    mask: np.ndarray,60    area_threshold: float,61    mode: str = 'islands'62) -> np.ndarray:63    """64    Refines a mask by removing small islands or filling small holes based on area65    threshold.66 67    Parameters:68        mask (np.ndarray): Input binary mask.69        area_threshold (float): Threshold for relative area to remove or fill features.70        mode (str): Operation mode ('islands' for removing islands, 'holes' for filling71                    holes).72 73    Returns:74        np.ndarray: Refined binary mask.75    """76    mask = np.uint8(mask * 255)77    operation = cv2.RETR_EXTERNAL if mode == 'islands' else cv2.RETR_CCOMP78    contours, _ = cv2.findContours(79        mask, operation, cv2.CHAIN_APPROX_SIMPLE80    )81    total_area = cv2.countNonZero(mask) if mode == 'islands' else mask.size82 83    for contour in contours:84        area = cv2.contourArea(contour)85        relative_area = area / total_area86        if relative_area < area_threshold:87            cv2.drawContours(88                image=mask,89                contours=[contour],90                contourIdx=-1,91                color=(0 if mode == 'islands' else 255),92                thickness=-193            )94 95    return np.where(mask > 0, 1, 0).astype(bool)96 97 98def filter_masks_by_relative_area(99    masks: np.ndarray,100    min_relative_area: float = 0.02,101    max_relative_area: float = 1.0102) -> np.ndarray:103    """104    Filters out masks based on their relative area.105 106    Parameters:107        masks (np.ndarray): A 3D numpy array where each slice along the third dimension108            represents a mask.109        min_relative_area (float): Minimum relative area threshold for keeping a mask.110        max_relative_area (float): Maximum relative area threshold for keeping a mask.111 112    Returns:113        np.ndarray: A 3D numpy array of filtered masks.114    """115    mask_areas = masks.sum(axis=(1, 2))116    total_area = masks.shape[1] * masks.shape[2]117    relative_areas = mask_areas / total_area118    min_area_filter = relative_areas >= min_relative_area119    max_area_filter = relative_areas <= max_relative_area120    return masks[min_area_filter & max_area_filter]121 122 123def postprocess_masks(124    detections: sv.Detections,125    area_threshold: float = 0.01,126    min_relative_area: float = 0.01,127    max_relative_area: float = 1.0,128    iou_threshold: float = 0.9129) -> sv.Detections:130    """131    Post-processes the masks of detection objects by removing small islands and filling132    small holes.133 134    Parameters:135        detections (sv.Detections): Detection objects to be filtered.136        area_threshold (float): Threshold for relative area to remove or fill features.137        min_relative_area (float): Minimum relative area threshold for detections.138        max_relative_area (float): Maximum relative area threshold for detections.139        iou_threshold (float): The IoU threshold above which masks will be considered as140            overlapping.141 142    Returns:143        np.ndarray: Post-processed masks.144    """145    masks = detections.mask.copy()146    for i in range(len(masks)):147        masks[i] = refine_mask(148            mask=masks[i],149            area_threshold=area_threshold,150            mode='islands'151        )152        masks[i] = refine_mask(153            mask=masks[i],154            area_threshold=area_threshold,155            mode='holes'156        )157    masks = filter_masks_by_relative_area(158        masks=masks,159        min_relative_area=min_relative_area,160        max_relative_area=max_relative_area)161    masks = som.mask_non_max_suppression(162        masks=masks,163        iou_threshold=iou_threshold)164 165    return sv.Detections(166        xyxy=sv.mask_to_xyxy(masks),167        mask=masks168    )169