Source code for supervisely.geometry.multichannel_bitmap

# coding: utf-8
import base64
import zlib
import io
import numpy as np

from supervisely.geometry.bitmap_base import BitmapBase, resize_origin_and_bitmap
from supervisely.geometry.point_location import PointLocation
from supervisely.geometry.constants import MULTICHANNEL_BITMAP


[docs] class MultichannelBitmap(BitmapBase): """Bitmap mask with multiple channels (e.g. multi-class segmentation). Immutable."""
[docs] @staticmethod def geometry_name(): """ """ return 'multichannelBitmap'
def __init__(self, data, origin: PointLocation = None, sly_id=None, class_id=None, labeler_login=None, updated_at=None, created_at=None): """ MultichannelBitmap is a geometry for a single :class:`~supervisely.annotation.label.Label`. :class:`~supervisely.geometry.multichannel_bitmap.MultichannelBitmap` object is immutable. :param data: bool numpy array :param origin: points (x and y coordinates) of the top left corner of a bitmap, i.e. the position of the bitmap within the image :type origin: :class:`~supervisely.geometry.point_location.PointLocation` :param sly_id: MultichannelBitmap ID in Supervisely server. :type sly_id: int, optional :param class_id: ID of ObjClass to which MultichannelBitmap belongs. :type class_id: int, optional :param labeler_login: Login of the user who created :class:`~supervisely.geometry.multichannel_bitmap.MultichannelBitmap`. :type labeler_login: str, optional :param updated_at: Date and Time when MultichannelBitmap was modified last. Date Format: Year:Month:Day:Hour:Minute:Seconds. Example: '2021-01-22T19:37:50.158Z'. :type updated_at: str, optional :param created_at: Date and Time when MultichannelBitmap was created. Date Format is the same as in "updated_at" parameter. :type created_at: str, optional :raises TypeError: if origin is not a :class:`~supervisely.geometry.point_location.PointLocation` object :raises TypeError: if data is not a bool numpy array :raises ValueError: if data is not a 3-dimensional numpy array """ super().__init__(data, origin, expected_data_dims=3, sly_id=sly_id, class_id=class_id, labeler_login=labeler_login, updated_at=updated_at, created_at=created_at) @classmethod def _impl_json_class_name(cls): """ Returns the name of the geometry. :returns: name of the geometry :rtype: str """ return MULTICHANNEL_BITMAP
[docs] def rotate(self, rotator): """ Rotates the MultichannelBitmap within the full image canvas and rotate the whole canvas with a given rotator (ImageRotator class object contain size of image and angle to rotate) :param rotator: Class for image rotation. :type rotator: :class:`~supervisely.geometry.image_rotator.ImageRotator` :returns: Rotated MultichannelBitmap. :rtype: :class:`~supervisely.geometry.multichannel_bitmap.MultichannelBitmap` """ full_img_data = np.zeros(rotator.src_imsize + self.data.shape[2:], dtype=self.data.dtype) full_img_data[ self.origin.row:(self.origin.row + self.data.shape[0]), self.origin.col:(self.origin.col + self.data.shape[1]), ...] = self.data[:, :, ...] rotated_full_data = rotator.rotate_img(full_img_data, use_inter_nearest=True) # Rotate the bounding box to find out the bounding box of the rotated bitmap within the full image. rotated_bbox = self.to_bbox().rotate(rotator) rotated_origin = PointLocation(row=rotated_bbox.top, col=rotated_bbox.left) return MultichannelBitmap(data=rotated_bbox.get_cropped_numpy_slice(rotated_full_data), origin=rotated_origin)
[docs] def crop(self, rect): """ Crops the current MultichannelBitmap object with a given rectangle :param rect: Rectangle for crop. :type rect: :class:`~supervisely.geometry.rectangle.Rectangle` :returns: Cropped MultichannelBitmap. :rtype: :class:`~supervisely.geometry.multichannel_bitmap.MultichannelBitmap` """ maybe_cropped_area = self.to_bbox().crop(rect) if len(maybe_cropped_area) == 0: return [] else: [cropped_area] = maybe_cropped_area cropped_origin = PointLocation(row=cropped_area.top, col=cropped_area.left) cropped_area_in_data = cropped_area.translate(drow=-self._origin.row, dcol=-self.origin.col) return [MultichannelBitmap(data=cropped_area_in_data.get_cropped_numpy_slice(self._data), origin=cropped_origin,)]
[docs] def resize(self, in_size, out_size): """ Resize the current MultichannelBitmap to match a certain size :param in_size: input image size :type in_size: Tuple[int, int] :param out_size: output image size :type out_size: Tuple[int, int] :returns: MultichannelBitmap after resize. :rtype: :class:`~supervisely.geometry.multichannel_bitmap.MultichannelBitmap` """ scaled_origin, scaled_data = resize_origin_and_bitmap(self._origin, self._data, in_size, out_size) return MultichannelBitmap(data=scaled_data, origin=scaled_origin)
def _draw_impl(self, bitmap, color, thickness=1, config=None): """ """ self.to_bbox().get_cropped_numpy_slice(bitmap)[...] = color def _draw_contour_impl(self, bitmap, color, thickness=1, config=None): """ """ bbox = self.to_bbox() # Not forwarding the config here directly since a Rectangle cannot know # about our config format. bbox.draw_contour(bitmap, color, thickness) @property def area(self): """ Returns the area of the current MultichannelBitmap. :returns: Area of the MultichannelBitmap. :rtype: int """ return self.data.shape[0] * self.data.shape[1]
[docs] @staticmethod def base64_2_data(s: str) -> np.ndarray: """ The function base64_2_data convert base64 encoded string to numpy :param s: string :type s: str :returns: numpy array """ saved_bytes = io.BytesIO(zlib.decompress(base64.b64decode(s))) return np.load(saved_bytes)
[docs] @staticmethod def data_2_base64(data: np.ndarray) -> str: """ he function data_2_base64 convert numpy array to base64 encoded string :param data: numpy array :type data: np.ndarray :returns: string """ bytes_io = io.BytesIO() np.save(bytes_io, data, allow_pickle=False) return base64.b64encode(zlib.compress(bytes_io.getvalue())).decode('utf-8')
[docs] def validate(self, name, settings): """ """ pass # No need name validation - inner geometry type.