Skip to content
Snippets Groups Projects
Unverified Commit 5db9b2e3 authored by Cao Yuhang's avatar Cao Yuhang Committed by GitHub
Browse files

Rename BitMap to Bitmap, perfect unit test. (#2391)

* rename BitMap to Bitmap, add input check for polygon

* fix test mask

* fix test config

* complete test

* add mask contest test for bitmap resize

* update with np.diag

* perfect test polygon resize

* perfect test polygon crop
parent da09e7e5
No related branches found
No related tags found
No related merge requests found
from .mask_target import mask_target
from .structures import BitMapMasks, PolygonMasks
from .structures import BitmapMasks, PolygonMasks
from .utils import split_combined_polys
__all__ = [
'split_combined_polys', 'mask_target', 'BitMapMasks', 'PolygonMasks'
'split_combined_polys', 'mask_target', 'BitmapMasks', 'PolygonMasks'
]
......@@ -40,6 +40,11 @@ class BaseInstanceMasks(metaclass=ABCMeta):
def expand(self, expanded_h, expanded_w, top, left):
pass
@property
@abstractmethod
def areas(self):
pass
@abstractmethod
def to_ndarray(self):
pass
......@@ -49,7 +54,7 @@ class BaseInstanceMasks(metaclass=ABCMeta):
pass
class BitMapMasks(BaseInstanceMasks):
class BitmapMasks(BaseInstanceMasks):
"""This class represents masks in the form of bitmaps.
Args:
......@@ -78,7 +83,7 @@ class BitMapMasks(BaseInstanceMasks):
def __getitem__(self, index):
masks = self.masks[index].reshape(-1, self.height, self.width)
return BitMapMasks(masks, self.height, self.width)
return BitmapMasks(masks, self.height, self.width)
def __iter__(self):
return iter(self.masks)
......@@ -95,7 +100,7 @@ class BitMapMasks(BaseInstanceMasks):
interpolation (str): same as :func:`mmcv.imrescale`
Returns:
BitMapMasks: the rescaled masks
BitmapMasks: the rescaled masks
"""
if len(self.masks) == 0:
new_w, new_h = mmcv.rescale_size((self.width, self.height), scale)
......@@ -106,7 +111,7 @@ class BitMapMasks(BaseInstanceMasks):
for mask in self.masks
])
height, width = rescaled_masks.shape[1:]
return BitMapMasks(rescaled_masks, height, width)
return BitmapMasks(rescaled_masks, height, width)
def resize(self, out_shape, interpolation='nearest'):
"""Resize masks to the given out_shape.
......@@ -116,7 +121,7 @@ class BitMapMasks(BaseInstanceMasks):
interpolation (str): see `mmcv.imresize`
Returns:
BitMapMasks: the resized masks
BitmapMasks: the resized masks
"""
if len(self.masks) == 0:
resized_masks = np.empty((0, *out_shape), dtype=np.uint8)
......@@ -125,7 +130,7 @@ class BitMapMasks(BaseInstanceMasks):
mmcv.imresize(mask, out_shape, interpolation=interpolation)
for mask in self.masks
])
return BitMapMasks(resized_masks, *out_shape)
return BitmapMasks(resized_masks, *out_shape)
def flip(self, flip_direction='horizontal'):
"""flip masks alone the given direction.
......@@ -134,7 +139,7 @@ class BitMapMasks(BaseInstanceMasks):
flip_direction (str): either 'horizontal' or 'vertical'
Returns:
BitMapMasks: the flipped masks
BitmapMasks: the flipped masks
"""
assert flip_direction in ('horizontal', 'vertical')
......@@ -145,7 +150,7 @@ class BitMapMasks(BaseInstanceMasks):
mmcv.imflip(mask, direction=flip_direction)
for mask in self.masks
])
return BitMapMasks(flipped_masks, self.height, self.width)
return BitmapMasks(flipped_masks, self.height, self.width)
def pad(self, out_shape, pad_val=0):
"""Pad masks to the given size of (h, w).
......@@ -155,7 +160,7 @@ class BitMapMasks(BaseInstanceMasks):
pad_val (int): the padded value
Returns:
BitMapMasks: the padded masks
BitmapMasks: the padded masks
"""
if len(self.masks) == 0:
padded_masks = np.empty((0, *out_shape), dtype=np.uint8)
......@@ -164,7 +169,7 @@ class BitMapMasks(BaseInstanceMasks):
mmcv.impad(mask, out_shape, pad_val=pad_val)
for mask in self.masks
])
return BitMapMasks(padded_masks, *out_shape)
return BitmapMasks(padded_masks, *out_shape)
def crop(self, bbox):
"""Crop each mask by the given bbox.
......@@ -173,7 +178,7 @@ class BitMapMasks(BaseInstanceMasks):
bbox (ndarray): bbox in format [x1, y1, x2, y2], shape (4, )
Return:
BitMapMasks: the cropped masks.
BitmapMasks: the cropped masks.
"""
assert isinstance(bbox, np.ndarray)
assert bbox.ndim == 1
......@@ -190,7 +195,7 @@ class BitMapMasks(BaseInstanceMasks):
cropped_masks = np.empty((0, h, w), dtype=np.uint8)
else:
cropped_masks = self.masks[:, y1:y1 + h, x1:x1 + w]
return BitMapMasks(cropped_masks, h, w)
return BitmapMasks(cropped_masks, h, w)
def crop_and_resize(self,
bboxes,
......@@ -214,7 +219,7 @@ class BitMapMasks(BaseInstanceMasks):
"""
if len(self.masks) == 0:
empty_masks = np.empty((0, *out_shape), dtype=np.uint8)
return BitMapMasks(empty_masks, *out_shape)
return BitmapMasks(empty_masks, *out_shape)
resized_masks = []
for i in range(len(bboxes)):
......@@ -228,7 +233,7 @@ class BitMapMasks(BaseInstanceMasks):
mask[y1:y1 + h, x1:x1 + w],
out_shape,
interpolation=interpolation))
return BitMapMasks(np.stack(resized_masks), *out_shape)
return BitmapMasks(np.stack(resized_masks), *out_shape)
def expand(self, expanded_h, expanded_w, top, left):
"""see `transforms.Expand`."""
......@@ -240,7 +245,16 @@ class BitMapMasks(BaseInstanceMasks):
dtype=np.uint8)
expanded_mask[:, top:top + self.height,
left:left + self.width] = self.masks
return BitMapMasks(expanded_mask, expanded_h, expanded_w)
return BitmapMasks(expanded_mask, expanded_h, expanded_w)
@property
def areas(self):
"""Compute area of each instance
Return:
ndarray: areas of each instance
"""
return self.masks.sum((1, 2))
def to_ndarray(self):
return self.masks
......@@ -297,7 +311,7 @@ class PolygonMasks(BaseInstanceMasks):
return len(self.masks)
def rescale(self, scale, interpolation=None):
"""see BitMapMasks.rescale"""
"""see BitmapMasks.rescale"""
new_w, new_h = mmcv.rescale_size((self.width, self.height), scale)
if len(self.masks) == 0:
rescaled_masks = PolygonMasks([], new_h, new_w)
......@@ -306,7 +320,7 @@ class PolygonMasks(BaseInstanceMasks):
return rescaled_masks
def resize(self, out_shape, interpolation=None):
"""see BitMapMasks.resize"""
"""see BitmapMasks.resize"""
if len(self.masks) == 0:
resized_masks = PolygonMasks([], *out_shape)
else:
......@@ -325,7 +339,7 @@ class PolygonMasks(BaseInstanceMasks):
return resized_masks
def flip(self, flip_direction='horizontal'):
"""see BitMapMasks.flip"""
"""see BitmapMasks.flip"""
assert flip_direction in ('horizontal', 'vertical')
if len(self.masks) == 0:
flipped_masks = PolygonMasks([], self.height, self.width)
......@@ -349,7 +363,7 @@ class PolygonMasks(BaseInstanceMasks):
return flipped_masks
def crop(self, bbox):
"""see BitMapMasks.crop"""
"""see BitmapMasks.crop"""
assert isinstance(bbox, np.ndarray)
assert bbox.ndim == 1
......@@ -368,6 +382,7 @@ class PolygonMasks(BaseInstanceMasks):
for poly_per_obj in self.masks:
cropped_poly_per_obj = []
for p in poly_per_obj:
# pycocotools will clip the boundary
p = p.copy()
p[0::2] -= bbox[0]
p[1::2] -= bbox[1]
......@@ -388,7 +403,7 @@ class PolygonMasks(BaseInstanceMasks):
out_shape,
inds,
interpolation='bilinear'):
"""see BitMapMasks.crop_and_resize"""
"""see BitmapMasks.crop_and_resize"""
out_h, out_w = out_shape
if len(self.masks) == 0:
return PolygonMasks([], out_h, out_w)
......@@ -407,6 +422,7 @@ class PolygonMasks(BaseInstanceMasks):
for p in mask:
p = p.copy()
# crop
# pycocotools will clip the boundary
p[0::2] -= bbox[0]
p[1::2] -= bbox[1]
......@@ -420,7 +436,42 @@ class PolygonMasks(BaseInstanceMasks):
def to_bitmap(self):
"""convert polygon masks to bitmap masks"""
bitmap_masks = self.to_ndarray()
return BitMapMasks(bitmap_masks, self.height, self.width)
return BitmapMasks(bitmap_masks, self.height, self.width)
@property
def areas(self):
"""Compute areas of masks.
This func is modified from
https://github.com/facebookresearch/detectron2/blob/ffff8acc35ea88ad1cb1806ab0f00b4c1c5dbfd9/detectron2/structures/masks.py#L387
Only works with Polygons, using the shoelace formula
Return:
ndarray: areas of each instance
""" # noqa: W501
area = []
for polygons_per_obj in self.masks:
area_per_obj = 0
for p in polygons_per_obj:
area_per_obj += self._polygon_area(p[0::2], p[1::2])
area.append(area_per_obj)
return np.asarray(area)
def _polygon_area(self, x, y):
"""Compute the area of a component of a polygon.
Using the shoelace formula:
https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates
Args:
x (ndarray): x coordinates of the component
y (ndarray): y coordinates of the component
Return:
float: the are of the component
""" # noqa: 501
return 0.5 * np.abs(
np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
def to_ndarray(self):
if len(self.masks) == 0:
......
......@@ -4,7 +4,7 @@ import mmcv
import numpy as np
import pycocotools.mask as maskUtils
from mmdet.core import BitMapMasks, PolygonMasks
from mmdet.core import BitmapMasks, PolygonMasks
from ..registry import PIPELINES
......@@ -149,7 +149,7 @@ class LoadAnnotations(object):
h, w = results['img_info']['height'], results['img_info']['width']
gt_masks = results['ann_info']['masks']
if self.poly2mask:
gt_masks = BitMapMasks(
gt_masks = BitmapMasks(
[self._poly2mask(mask, h, w) for mask in gt_masks], h, w)
else:
gt_masks = PolygonMasks(
......
from os.path import dirname, exists, join, relpath
from mmdet.core import BitMapMasks, PolygonMasks
from mmdet.core import BitmapMasks, PolygonMasks
def _get_config_directory():
......@@ -98,7 +98,7 @@ def test_config_data_pipeline():
assert mode in ('polygon', 'bitmap')
if mode == 'bitmap':
masks = np.random.randint(0, 2, (num_obj, h, w), dtype=np.uint8)
masks = BitMapMasks(masks, h, w)
masks = BitmapMasks(masks, h, w)
else:
masks = []
for i in range(num_obj):
......
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment