| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- # Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved
- # pyre-unsafe
- import cv2
- import numpy as np
- import pycocotools.mask as mask_utils
- from PIL import Image
- from .helpers.visualizer import Visualizer
- from .helpers.zoom_in import render_zoom_in
- def visualize(
- input_json: dict,
- zoom_in_index: int | None = None,
- mask_alpha: float = 0.15,
- label_mode: str = "1",
- font_size_multiplier: float = 1.2,
- boarder_width_multiplier: float = 0,
- ):
- """
- Unified visualization function.
- If zoom_in_index is None:
- - Render all masks in input_json (equivalent to visualize_masks_from_result_json).
- - Returns: PIL.Image
- If zoom_in_index is provided:
- - Returns two PIL.Images:
- 1) Output identical to zoom_in_and_visualize(input_json, index).
- 2) The same instance rendered via the general overlay using the color
- returned by (1), equivalent to calling visualize_masks_from_result_json
- on a single-mask json_i with color=color_hex.
- """
- # Common fields
- orig_h = int(input_json["orig_img_h"])
- orig_w = int(input_json["orig_img_w"])
- img_path = input_json["original_image_path"]
- # ---------- Mode A: Full-scene render ----------
- if zoom_in_index is None:
- boxes = np.array(input_json["pred_boxes"])
- rle_masks = [
- {"size": (orig_h, orig_w), "counts": rle}
- for rle in input_json["pred_masks"]
- ]
- binary_masks = [mask_utils.decode(rle) for rle in rle_masks]
- img_bgr = cv2.imread(img_path)
- if img_bgr is None:
- raise FileNotFoundError(f"Could not read image: {img_path}")
- img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
- viz = Visualizer(
- img_rgb,
- font_size_multiplier=font_size_multiplier,
- boarder_width_multiplier=boarder_width_multiplier,
- )
- viz.overlay_instances(
- boxes=boxes,
- masks=rle_masks,
- binary_masks=binary_masks,
- assigned_colors=None,
- alpha=mask_alpha,
- label_mode=label_mode,
- )
- pil_all_masks = Image.fromarray(viz.output.get_image())
- return pil_all_masks
- # ---------- Mode B: Zoom-in pair ----------
- else:
- idx = int(zoom_in_index)
- num_masks = len(input_json.get("pred_masks", []))
- if idx < 0 or idx >= num_masks:
- raise ValueError(
- f"zoom_in_index {idx} is out of range (0..{num_masks - 1})."
- )
- # (1) Replicate zoom_in_and_visualize
- object_data = {
- "labels": [{"noun_phrase": f"mask_{idx}"}],
- "segmentation": {
- "counts": input_json["pred_masks"][idx],
- "size": [orig_h, orig_w],
- },
- }
- pil_img = Image.open(img_path)
- pil_mask_i_zoomed, color_hex = render_zoom_in(
- object_data, pil_img, mask_alpha=mask_alpha
- )
- # (2) Single-instance render with the same color
- boxes_i = np.array([input_json["pred_boxes"][idx]])
- rle_i = {"size": (orig_h, orig_w), "counts": input_json["pred_masks"][idx]}
- bin_i = mask_utils.decode(rle_i)
- img_bgr_i = cv2.imread(img_path)
- if img_bgr_i is None:
- raise FileNotFoundError(f"Could not read image: {img_path}")
- img_rgb_i = cv2.cvtColor(img_bgr_i, cv2.COLOR_BGR2RGB)
- viz_i = Visualizer(
- img_rgb_i,
- font_size_multiplier=font_size_multiplier,
- boarder_width_multiplier=boarder_width_multiplier,
- )
- viz_i.overlay_instances(
- boxes=boxes_i,
- masks=[rle_i],
- binary_masks=[bin_i],
- assigned_colors=[color_hex],
- alpha=mask_alpha,
- label_mode=label_mode,
- )
- pil_mask_i = Image.fromarray(viz_i.output.get_image())
- return pil_mask_i, pil_mask_i_zoomed
|