Skip to content

roi_handler

data.transform.roi.roi_handler

ROICalculator

Bases: ABC

ROI 좌표 계산을 위한 추상 클래스입니다.

set abstractmethod

set(**kwargs) -> None
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
@abstractmethod
def set(self, **kwargs) -> None:
    pass

__call__ abstractmethod

__call__(image: Union[Image, ndarray], get_intermediate_results: bool = False, warmup: bool = False, **data) -> Dict
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
@abstractmethod
def __call__(
    self,
    image: Union[Image.Image, np.ndarray],
    get_intermediate_results: bool = False,
    warmup: bool = False,
    **data,
) -> Dict:
    pass

RelativeBoxROI

RelativeBoxROI(**kwargs)

Bases: ROICalculator

이미지 크기에 비례하는 상대좌표 박스로 ROI를 계산합니다.

Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def __init__(self, **kwargs) -> None:
    self.set(**kwargs)

set

set(left: float, top: float, right: float, bottom: float) -> None
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def set(self, left: float, top: float, right: float, bottom: float) -> None:
    if not (0.0 <= left < right <= 1.0) or not (0.0 <= top < bottom <= 1.0):
        raise SimpleROIParameterValueError

    self.left = left
    self.top = top
    self.right = right
    self.bottom = bottom

__call__

__call__(image: Union[Image, ndarray], get_intermediate_results: bool = False, warmup: bool = False, **data) -> Dict
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def __call__(
    self,
    image: Union[Image.Image, np.ndarray],
    get_intermediate_results: bool = False,
    warmup: bool = False,
    **data,
) -> Dict:
    w, h = read_image_size(image)
    left = int(self.left * w)
    right = max(int(self.right * w), left + 1)
    top = int(self.top * h)
    bottom = max(int(self.bottom * h), top + 1)
    return {"roi_coordinates": [left, top, right, bottom]}

PixelIntensityROI

PixelIntensityROI(**kwargs)

Bases: ROICalculator

픽셀값이 intensity 범위에 들어오는 픽셀만 필터링한 뒤, 필터링 된 픽셀들의 컨투어를 찾고, 가장 면적이 큰 컨투어를 감싸는 최소 박스로 ROI를 계산합니다.

Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def __init__(self, **kwargs) -> None:
    self.set(**kwargs)

set

set(intensity: Union[Tuple[int, int], List[int]], expansion: int, inversion: bool, offset_left: float, offset_right: float, offset_top: float, offset_bottom: float) -> None
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def set(
    self,
    intensity: Union[Tuple[int, int], List[int]],
    expansion: int,
    inversion: bool,
    offset_left: float,
    offset_right: float,
    offset_top: float,
    offset_bottom: float,
) -> None:
    if (
        not len(intensity) == 2
        or not isinstance(intensity[0], int)
        or not isinstance(intensity[1], int)
        or not (0 <= intensity[0] < intensity[1] <= 255)
    ):
        raise AdvancedROIParameterValueError

    if not (-10 <= expansion <= 10):
        raise AdvancedROIParameterValueError

    for offset in (offset_left, offset_right, offset_top, offset_bottom):
        if not (0 <= offset <= 2):
            raise AdvancedROIParameterValueError
    if not (offset_left + offset_right >= 0.1) or not (offset_top + offset_bottom >= 0.1):
        raise AdvancedROIParameterValueError

    self.intensity = intensity
    self.expansion = expansion
    self.inversion = inversion
    self.offset_left = offset_left
    self.offset_right = offset_right
    self.offset_top = offset_top
    self.offset_bottom = offset_bottom

__call__

__call__(image: Union[Image, ndarray], get_intermediate_results: bool = False, warmup: bool = False, **data) -> Dict
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def __call__(
    self,
    image: Union[Image.Image, np.ndarray],
    get_intermediate_results: bool = False,
    warmup: bool = False,
    **data,
) -> Dict:
    w, h = read_image_size(image)

    # PixelIntensityROI의 경우 input image size가 같더라도 output image size가 계속해서 달라질 수 있으며,
    # output image size의 최대 값은 input image size이기 때문에, warmup 시에 input image size를 return 함.
    if warmup:
        output = {"roi_coordinates": [0, 0, w, h]}
        return output

    # 후속 연산을 위해 grayscale numpy array 이미지로 변환
    filtered_image = to_numpy(image, copy=False)
    filtered_image = convert_image_mode(filtered_image, "L", copy=False)

    # intensity 범위에 들어오는 픽셀을 찾고, 해당 픽셀들을 expand & inverse
    filtered_image = cv2.inRange(filtered_image, lowerb=self.intensity[0], upperb=self.intensity[1])

    expansion_kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(3, 3), anchor=(1, 1))
    if self.expansion > 0:
        filtered_image = cv2.dilate(
            filtered_image, kernel=expansion_kernel, anchor=(-1, -1), iterations=2 * self.expansion
        )
    elif self.expansion < 0:
        filtered_image = cv2.erode(
            filtered_image, kernel=expansion_kernel, anchor=(-1, -1), iterations=2 * self.expansion
        )

    if self.inversion:
        filtered_image = cv2.bitwise_not(filtered_image)

    contours, _ = cv2.findContours(
        filtered_image, mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE
    )
    if contours:
        # contour 중 면적 가장 큰 것 선택 & bounding box 계산
        contour_areas = list(map(cv2.contourArea, contours))
        max_area_contour = contours[np.argmax(contour_areas)]
        left, top, roi_width, roi_height = cv2.boundingRect(max_area_contour)

        # bounding box에 offset 적용 & 이미지 내부로 clip
        roi_center_x = left + (roi_width / 2)
        roi_center_y = top + (roi_height / 2)

        roi_left = roi_center_x - (self.offset_left * roi_width / 2)
        roi_right = roi_center_x + (self.offset_right * roi_width / 2)
        roi_top = roi_center_y - (self.offset_top * roi_height / 2)
        roi_bottom = roi_center_y + (self.offset_bottom * roi_height / 2)

        roi_left = int(np.clip(roi_left, 0, w - 1))
        roi_right = int(np.clip(roi_right, roi_left + 1, w))
        roi_top = int(np.clip(roi_top, 0, h - 1))
        roi_bottom = int(np.clip(roi_bottom, roi_top + 1, h))

        roi_coordinates = [roi_left, roi_top, roi_right, roi_bottom]
    else:
        roi_coordinates = [0, 0, w, h]

    output = {"roi_coordinates": roi_coordinates}
    if get_intermediate_results:
        output["filtered_image"] = filtered_image // 255
    return output

AutoRelativeBoxROI

AutoRelativeBoxROI(padding: str = 'medium')

Bases: RelativeBoxROI

RelativeBoxROI class with automatic coordinate calculation.

Monostate pattern applied to prevent ROI coordinate mismatch between train ~ validation dataset.

Attributes:

  • left (float) –

    ROI coordinates.

  • top (float) –

    ROI coordinates.

  • right (float) –

    ROI coordinates.

  • bottom (float) –

    ROI coordinates.

  • is_ready (bol) –

    Whether auto ROI coordinates is set.

  • image_hw (Optional[List[int]]) –

    dataset image size.

  • discard_outer_polygons (bool) –

    Flag for discarding polygons outside of current ROI region.

  • expand_ratio

    Ratio for expanding ROI region. Larger value means more padding.

Example
# in dataset building...
@property
def files(self):
    return self._files

@files.setter
def files(self, value):
    self._files = value

    if isinstance(self.transform.roi_handler.roi_calculator, AutoRelativeBoxROI):
        self.transform.roi_handler.roi_calculator.autoupdate_roi_coordinate(self)
                                                                          # self is dataset
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def __init__(self, padding: str = "medium"):
    self.__dict__ = self.__shared_state

    if padding not in self.REGION_EXPAND_RATIO.keys():
        raise AutoROIParameterValueError(f"AutoROI mode {padding} not available.")

    self.expand_ratio = self.REGION_EXPAND_RATIO[padding]

__shared_state class-attribute instance-attribute

__shared_state = {'_is_ready': False, '_image_hw': None, '_left': -1.0, '_top': -1.0, '_right': -1.0, '_bottom': -1.0}

REGION_EXPAND_RATIO class-attribute instance-attribute

REGION_EXPAND_RATIO = {'small': 0.05, 'medium': 0.1, 'large': 0.2}

__dict__ instance-attribute

__dict__ = __shared_state

expand_ratio instance-attribute

expand_ratio = REGION_EXPAND_RATIO[padding]

is_ready property writable

is_ready

image_hw property writable

image_hw

left property writable

left

top property writable

top

right property writable

right

bottom property writable

bottom

_check_auto_roi_is_ready

_check_auto_roi_is_ready(function)
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def _check_auto_roi_is_ready(function):
    def wrapper(self, *args, **kwargs):
        if self.is_ready:
            return function(self, *args, **kwargs)
        else:
            raise AutoROIParameterRuntimeError(
                "AutoROI not avaliable. Check log and dataset config."
            )

    return wrapper

__call__

__call__(*args, **kwargs)
Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
@_check_auto_roi_is_ready
def __call__(self, *args, **kwargs):
    return super().__call__(*args, **kwargs)

autoupdate_roi_coordinate

autoupdate_roi_coordinate(dataset: Dataset) -> bool

Automatically update ROI coordinate using dataset information.

Returns success flag.

Source code in SaigeToolkit/data/transform/roi/roi_calculator.py
def autoupdate_roi_coordinate(self, dataset: Dataset) -> bool:
    """Automatically update ROI coordinate using dataset information.

    Returns success flag.
    """

    # XXX: only called once
    if self.is_ready:
        logger.info("ROI coordinate already set. Update only once")
        return True

    marginal_label_region_xyxy = np.array([np.inf, np.inf, 0, 0])

    for data_idx in range(len(dataset)):
        file = dataset.files[data_idx]
        data_dict = dataset.load_image(file)
        data_dict.update(dataset.load_label(file))

        data_image: Union[Image.Image, np.ndarray] = data_dict["image"]
        current_img_hw = list(read_image_size(data_image))[::-1]

        if self.image_hw:
            if self.image_hw != current_img_hw:
                logger.warn("Variation in image size detected.. autoROI update aborted.")
                logger.warn(
                    f"Expected image size: {self.image_hw} / Given image size: {current_img_hw}"
                )
                return False

        self.image_hw = current_img_hw

        for polygon in data_dict["polygons"]:
            x_min, x_max = np.min(polygon[:, 0]), np.max(polygon[:, 0])
            y_min, y_max = np.min(polygon[:, 1]), np.max(polygon[:, 1])

            if x_min < marginal_label_region_xyxy[0]:
                marginal_label_region_xyxy[0] = x_min

            if y_min < marginal_label_region_xyxy[1]:
                marginal_label_region_xyxy[1] = y_min

            if x_max > marginal_label_region_xyxy[2]:
                marginal_label_region_xyxy[2] = x_max

            if y_max > marginal_label_region_xyxy[3]:
                marginal_label_region_xyxy[3] = y_max

    if not _check_region_xyxy_is_valid(marginal_label_region_xyxy):
        logger.warning(
            "AutoROI setting update failed. Needs at least one training data & valid polygon."
        )
        return False

    marginal_label_region_xyxy = _expand_region_xyxy(marginal_label_region_xyxy, self.expand_ratio)
    marginal_label_region_xyxy = _fit_region_into_img_size(
        marginal_label_region_xyxy,
        self.image_hw,
    )

    self.left = marginal_label_region_xyxy[0] / self.image_hw[1]
    self.top = marginal_label_region_xyxy[1] / self.image_hw[0]
    self.right = marginal_label_region_xyxy[2] / self.image_hw[1]
    self.bottom = marginal_label_region_xyxy[3] / self.image_hw[0]

    self.is_ready = True
    marginal_label_region_ratio = [self.left, self.top, self.right, self.bottom]
    logger.info(f"AutoROI setting success. ROI coordinates: {marginal_label_region_ratio}")

    return True

ROIHandler

ROIHandler(**kwargs)

ROI 기능을 수행합니다. 이미지에 대한 ROI 좌표를 계산해 크롭하고, 크롭된 이미지에 blind_mask를 적용해 마스크 영역의 픽셀 값을 0으로 치환합니다.

Source code in SaigeToolkit/data/transform/roi/roi_handler.py
def __init__(self, **kwargs) -> None:
    self.set(**kwargs)

roi_calculator_types class-attribute instance-attribute

roi_calculator_types = {'simple': RelativeBoxROI, 'advanced': PixelIntensityROI, 'auto': AutoRelativeBoxROI}

roi_calculator instance-attribute

roi_calculator: ROICalculator

set

set(mode: str, blind_mask: Union[None, ndarray, str], image_only: bool = False, discard_outer_polygons: bool = False, det_blind_mask_threshold: float = 0.5, **kwargs)
Source code in SaigeToolkit/data/transform/roi/roi_handler.py
def set(
    self,
    mode: str,
    blind_mask: Union[None, np.ndarray, str],
    image_only: bool = False,
    discard_outer_polygons: bool = False,
    det_blind_mask_threshold: float = 0.5,
    **kwargs,
):
    if mode not in self.roi_calculator_types:
        raise ROIModeError

    # mode 변경된 경우 roi_calculator 인스턴스 새로 생성, 그렇지 않은 경우 set 호출
    if not hasattr(self, "mode") or self.mode != mode:
        self.mode = mode
        self.roi_calculator = self.roi_calculator_types[mode](**kwargs)
    else:
        self.roi_calculator.set(**kwargs)

    if isinstance(blind_mask, str):
        blind_mask = np.array(Image.open(blind_mask))
    if blind_mask is not None and (blind_mask.dtype != np.uint8 or blind_mask.ndim != 2):
        raise ROIBlindMaskValueError
    self.blind_mask = blind_mask

    self.image_only = image_only

    self.discard_outer_polygons = discard_outer_polygons

    self.det_blind_mask_threshold = det_blind_mask_threshold

apply_crop

apply_crop(image: Union[Image, ndarray, List[Union[Image, ndarray]]], return_revert_params: bool = False, warmup: bool = False, **data) -> Union[Dict, Tuple[Dict, Dict]]
Source code in SaigeToolkit/data/transform/roi/roi_handler.py
def apply_crop(
    self,
    image: Union[Image.Image, np.ndarray, List[Union[Image.Image, np.ndarray]]],
    return_revert_params: bool = False,
    warmup: bool = False,
    **data,
) -> Union[Dict, Tuple[Dict, Dict]]:
    multipage = isinstance(image, list)

    roi_info = self.roi_calculator(
        image=image[0] if multipage else image,
        get_intermediate_results=False,
        warmup=warmup,
        **data,
    )
    roi_coordinates = roi_info["roi_coordinates"]  # [left, top, right, bottom]

    if multipage:
        image_size_before_roi = read_image_size(image[0])
        cropped_image = [crop(image_i, roi_coordinates) for image_i in image]
        image_size_after_roi = read_image_size(cropped_image[0])
    else:
        image_size_before_roi = read_image_size(image)
        cropped_image = crop(image, roi_coordinates)
        image_size_after_roi = read_image_size(cropped_image)

    data["image"] = cropped_image

    revert_params = {
        "image_size_before_roi": image_size_before_roi,
        "roi_coordinates": roi_coordinates,
        "image_size_after_roi": image_size_after_roi,
    }

    if self.image_only:
        return (data, revert_params) if return_revert_params else data

    # 라벨 크롭 & 마스킹
    if "mask" in data:
        data["mask"] = crop(data["mask"], roi_coordinates)

    if "bboxes" in data:
        data["bboxes"] = crop_box(data["bboxes"], roi_coordinates)

    if "polygons" in data:
        # NOTE: polygons에는 blind_mask가 적용되지 않습니다.
        new_polygons = []
        new_data = {key: [] for key in ["strings", "ignore"] if key in data}
        # TODO: add any keys to be updated which should have same length with polygons.

        for idx, polygon in enumerate(data["polygons"]):
            if self.discard_outer_polygons and not self._check_polygon_within_box(
                polygon, roi_coordinates
            ):
                continue

            new_polygons.append(polygon)
            for k, v in new_data.items():
                v.append(data[k][idx])

        new_polygons = translate_polygon(
            polygons=new_polygons,
            offset=roi_coordinates[:2],
        )

        data.update({"polygons": new_polygons, **new_data})

    return (data, revert_params) if return_revert_params else data

apply_mask

apply_mask(image: Union[Image, ndarray, List], **data)
Source code in SaigeToolkit/data/transform/roi/roi_handler.py
def apply_mask(
    self,
    image: Union[Image.Image, np.ndarray, List],
    **data,
):
    if self.blind_mask is None:
        data["image"] = image
        return data

    multipage = isinstance(image, List)

    # cv2 resize: 약간 부정확하지만 빠름. 여기서는 아주 정확할 필요없음.
    image_size = read_image_size(image[0] if multipage else image)
    blind_mask = cv2.resize(self.blind_mask, dsize=image_size, interpolation=cv2.INTER_NEAREST)
    bool_mask = blind_mask > 0

    if multipage:
        data["image"] = [fill_pixels_with_mask(image_i, bool_mask, value=0) for image_i in image]
    else:
        data["image"] = fill_pixels_with_mask(image, bool_mask, value=0)

    if self.image_only:
        return data

    # 라벨 크롭 & 마스킹
    if "mask" in data:
        data["mask"] = fill_pixels_with_mask(data["mask"], bool_mask, value=0)

    if "bboxes" in data:
        indices_alive = []

        if len(data["bboxes"]):
            xyxy_bboxes = data["bboxes"].convert_coordinate("xyxy")

            for i, bbox in enumerate(xyxy_bboxes):
                x0, y0, x1, y1 = bbox

                label_area = (x1 - x0) * (y1 - y0)
                overlapping_area = np.sum(blind_mask[int(y0) : int(y1), int(x0) : int(x1)])

                if overlapping_area / label_area < self.det_blind_mask_threshold:
                    indices_alive.append(i)

        data["bboxes"] = data["bboxes"][indices_alive]

        if "labels" in data:
            data["labels"] = data["labels"][indices_alive]

    return data

__call__

__call__(return_revert_params: bool = False, warmup: bool = False, **data) -> Union[Dict, Tuple[Dict, Dict]]
Source code in SaigeToolkit/data/transform/roi/roi_handler.py
def __call__(
    self,
    return_revert_params: bool = False,
    warmup: bool = False,
    **data,
) -> Union[Dict, Tuple[Dict, Dict]]:
    if return_revert_params:
        data, revert_params = self.apply_crop(return_revert_params=True, warmup=warmup, **data)
        data = self.apply_mask(**data)
        return data, revert_params
    else:
        data = self.apply_crop(return_revert_params=False, warmup=warmup, **data)
        data = self.apply_mask(**data)
        return data

_check_polygon_within_box

_check_polygon_within_box(polygon: List[List[float]], roi_coordinates: List[int]) -> bool
Source code in SaigeToolkit/data/transform/roi/roi_handler.py
def _check_polygon_within_box(self, polygon: List[List[float]], roi_coordinates: List[int]) -> bool:
    left, top, right, bottom = roi_coordinates

    def check_x_cord(p):
        return p >= left and p < right

    def check_y_cord(p):
        return p >= top and p < bottom

    def check_point_within_box(point_xy):
        x, y = point_xy
        return check_x_cord(x) and check_y_cord(y)

    return all(check_point_within_box(p) for p in polygon)

crop_box

crop_box(bboxes: BBoxesType, cropping_box: Union[ndarray, List[int]]) -> BBoxesType

bbox crop. An image is cropped at (new_left, new_top, new_right, new_bottom)

Source code in SaigeToolkit/data/transform/box_function.py
@preserve_coordinates
def crop_box(bboxes: BBoxesType, cropping_box: Union[np.ndarray, List[int]]) -> BBoxesType:
    """bbox crop. An image is cropped at (new_left, new_top, new_right, new_bottom)"""
    dtype = bboxes.dtype
    new_left, new_top, new_right, new_bottom = map(round, cropping_box)
    new_h = new_bottom - new_top
    new_bboxes = bboxes - (new_left, new_top, new_left, new_top)
    new_bboxes[:, [0, 1]] = np.maximum(new_bboxes[:, [0, 1]], 0)
    new_bboxes[:, [2, 3]] = np.minimum(new_bboxes[:, [2, 3]], (new_right - new_left, new_h))
    return new_bboxes.astype(dtype)

translate_polygon

translate_polygon(polygons: PolygonType, offset: Tuple[int]) -> PolygonType

Translate polyfon from [(x1, y1), ... ] to [(x1 - x_offset), (y1 - y_offset), ...]

Source code in SaigeToolkit/data/transform/polygon_function.py
def translate_polygon(
    polygons: PolygonType,
    offset: Tuple[int],
) -> PolygonType:
    """Translate polyfon from [(x1, y1), ... ] to [(x1 - x_offset), (y1 - y_offset), ...]"""
    if len(polygons) == 0:
        return polygons

    if offset[0] == 0 and offset[1] == 0:
        return polygons

    dtype = polygons[0].dtype

    x_offset, y_offset = np.array(offset).astype(dtype)

    new_polygons = deepcopy(polygons)

    for polygon in new_polygons:
        polygon[:, 0] -= x_offset
        polygon[:, 1] -= y_offset

    return new_polygons

crop

crop(image: Union[Image, Tensor, ndarray], coordinates: Union[List[int], Tuple[int, int, int, int]]) -> Union[Image, Tensor, ndarray]

이미지의 coordinates 좌표영역을 크롭합니다. coordinates가 이미지를 벗어나는 경우 zero padding 합니다.

Parameters:

  • image (Union[Image, Tensor, ndarray]) –

    image

  • coordinates (Union[List[int], Tuple[int, int, int, int]]) –

    [left, top, right, bottom]

Returns:

  • Union[Image, Tensor, ndarray]

    Union[Image.Image, torch.Tensor, np.ndarray]: cropped image

Source code in SaigeToolkit/data/transform/image_function.py
def crop(
    image: Union[Image.Image, torch.Tensor, np.ndarray],
    coordinates: Union[List[int], Tuple[int, int, int, int]],
) -> Union[Image.Image, torch.Tensor, np.ndarray]:
    """이미지의 coordinates 좌표영역을 크롭합니다. coordinates가 이미지를 벗어나는 경우 zero padding 합니다.

    Args:
        image (Union[Image.Image, torch.Tensor, np.ndarray]): image
        coordinates (Union[List[int], Tuple[int, int, int, int]]): [left, top, right, bottom]

    Returns:
        Union[Image.Image, torch.Tensor, np.ndarray]: cropped image
    """
    if isinstance(image, Image.Image):
        return image.crop(coordinates)
    else:
        left, top, right, bottom = coordinates
        image_w, image_h = read_image_size(image)
        cropped_shape = (bottom - top, right - left)
        if image.ndim == 3:
            cropped_shape = (*cropped_shape, image.shape[2])
        if isinstance(image, torch.Tensor):
            cropped_image = torch.zeros(*cropped_shape, dtype=image.dtype, device=image.device)
        else:
            cropped_image = np.zeros(cropped_shape, dtype=image.dtype)
        cropped_image[
            np.clip(0, top, bottom) - top : np.clip(image_h, top, bottom) - top,
            np.clip(0, left, right) - left : np.clip(image_w, left, right) - left,
        ] = image[
            np.clip(top, 0, image_h) : np.clip(bottom, 0, image_h),
            np.clip(left, 0, image_w) : np.clip(right, 0, image_w),
        ]
        return cropped_image

read_image_size

read_image_size(image: Union[Image, Tensor, ndarray]) -> ImageSizeType

image size: (W, H)

Source code in SaigeToolkit/data/transform/image_function.py
def read_image_size(image: Union[Image.Image, torch.Tensor, np.ndarray]) -> ImageSizeType:
    """image size: (W, H)"""
    if isinstance(image, Image.Image):
        return image.size
    elif isinstance(image, torch.Tensor) and image.ndim in (2, 3, 4):  # HW, CHW, BCHW
        return (image.shape[-1], image.shape[-2])
    elif isinstance(image, np.ndarray) and image.ndim in (2, 3):  # HW, HWC
        return (image.shape[1], image.shape[0])
    else:
        raise NotImplementedError

fill_pixels_with_mask

fill_pixels_with_mask(image: Union[Image, ndarray], bool_mask: ndarray, value: Union[int, float] = 0) -> Union[Image, ndarray]

image 중 bool_mask=True인 픽셀들을 value로 채웁니다.

Parameters:

  • image (Union[Image, ndarray]) –

    image (HW or HWC)

  • bool_mask (ndarray) –

    boolean mask (HW)

  • value (Union[int, float], default: 0 ) –

    . Defaults to 0.

Returns:

  • Union[Image, ndarray]

    Union[Image.Image, np.ndarray]: 결과 이미지

Note
  • image가 Image.Image인 경우, 결과 이미지도 Image.Image로 반환합니다.
  • image의 값이 inplace로 변경됩니다. 값이 변경되지 않길 원한다면, copy를 해주세요.
Source code in SaigeToolkit/data/transform/image_function.py
def fill_pixels_with_mask(
    image: Union[Image.Image, np.ndarray],
    bool_mask: np.ndarray,
    value: Union[int, float] = 0,
) -> Union[Image.Image, np.ndarray]:
    """image 중 bool_mask=True인 픽셀들을 value로 채웁니다.

    Args:
        image (Union[Image.Image, np.ndarray]): image (HW or HWC)
        bool_mask (np.ndarray): boolean mask (HW)
        value (Union[int, float], optional): . Defaults to 0.

    Returns:
        Union[Image.Image, np.ndarray]: 결과 이미지

    Note:
        - image가 Image.Image인 경우, 결과 이미지도 Image.Image로 반환합니다.
        - image의 값이 inplace로 변경됩니다. 값이 변경되지 않길 원한다면, copy를 해주세요.
    """
    is_pil = isinstance(image, Image.Image)
    image = np.asarray(image)

    assert image.ndim in [2, 3] and image.shape[:2] == bool_mask.shape  # HW or HWC

    if image.ndim > bool_mask.ndim:  # max ndim difference is 1
        bool_mask = np.expand_dims(bool_mask, axis=-1)

    image = np.where(bool_mask, value, image)

    if is_pil:
        image = Image.fromarray(image)

    return image