From bc35858aff9ca963c77845b92609f386d5d45053 Mon Sep 17 00:00:00 2001 From: jinyeom Date: Thu, 3 Oct 2019 14:11:33 -0500 Subject: [PATCH 1/6] fixed dataset keys to match the new format --- nucleus/dataset/base.py | 12 ++++++------ nucleus/dataset/detections.py | 2 +- nucleus/dataset/jerseys.py | 8 ++++---- nucleus/dataset/keys.py | 12 ++++++------ nucleus/dataset/tools/watson.py | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/nucleus/dataset/base.py b/nucleus/dataset/base.py index d019e81..59b18ca 100644 --- a/nucleus/dataset/base.py +++ b/nucleus/dataset/base.py @@ -230,14 +230,14 @@ def _create_image_from_row(row: pd.Series) -> Image: """ path = row[DatasetKeys.PATH.value] - labels = row.get(DatasetKeys.LABELS.value) + attrs = row.get(DatasetKeys.ATTRS.value) boxes = row.get(DatasetKeys.BOXES.value) - boxes_labels = row.get(DatasetKeys.BOXES_LABELS.value) + labels = row.get(DatasetKeys.LABELS.value) if boxes is not None: box_list = [ - Box(ijhw=ijhw, labels=labels) - for ijhw, labels in zip(boxes, boxes_labels) + Box(ijhw=ijhw, labels=box_labels) + for ijhw, box_labels in zip(boxes, labels) if None not in ijhw and all([c > 0 for c in ijhw[:2]]) ] else: @@ -245,7 +245,7 @@ def _create_image_from_row(row: pd.Series) -> Image: return Image.from_path( path=path, - labels=labels, + labels=attrs, box_collection=box_list ) @@ -467,7 +467,7 @@ def create_random_split( sample = tf.random.uniform((), minval=0, maxval=1) if sample <= val_prop: self.df.at[i, DatasetSplitKeys.RANDOM.value] = ( - DatasetPartitionKeys.VAL.value + DatasetPartitionKeys.DEV.value ) if val_prop < sample <= test_prop + val_prop: self.df.at[i, DatasetSplitKeys.RANDOM.value] = ( diff --git a/nucleus/dataset/detections.py b/nucleus/dataset/detections.py index 3e016f1..9824bc2 100644 --- a/nucleus/dataset/detections.py +++ b/nucleus/dataset/detections.py @@ -207,7 +207,7 @@ def unique_boxes_labels(self) -> List[List[str]]: """ return self.unique_elements_from_list_column( - column=DatasetKeys.BOXES_LABELS.value + column=DatasetKeys.LABELS.value ) def view_row(self, index: int, image_args: Dict = None): diff --git a/nucleus/dataset/jerseys.py b/nucleus/dataset/jerseys.py index 1e17681..5fda618 100644 --- a/nucleus/dataset/jerseys.py +++ b/nucleus/dataset/jerseys.py @@ -53,10 +53,10 @@ def unique_labels( """ if label_position is None: - label_position = range(len(self.df[DatasetKeys.LABELS.value][0])) + label_position = range(len(self.df[DatasetKeys.ATTRS.value][0])) uniques = [[] for _ in label_position] - for labels in self.df[DatasetKeys.LABELS.value]: + for labels in self.df[DatasetKeys.ATTRS.value]: for i, label in enumerate(np.asanyarray(labels)[label_position]): if label is None: continue @@ -85,12 +85,12 @@ def create_label_count_df( df = self.df if label_position is None: - label_position = range(len(df[DatasetKeys.LABELS.value][0])) + label_position = range(len(df[DatasetKeys.ATTRS.value][0])) elif isinstance(label_position, int): label_position = [label_position] labels_dict = {} - for labels in df[DatasetKeys.LABELS.value]: + for labels in df[DatasetKeys.ATTRS.value]: for i, label in enumerate(np.asanyarray(labels)[label_position]): if label is None: continue diff --git a/nucleus/dataset/keys.py b/nucleus/dataset/keys.py index b5da19d..5df71c6 100644 --- a/nucleus/dataset/keys.py +++ b/nucleus/dataset/keys.py @@ -8,17 +8,17 @@ class DatasetKeys(Enum): NAME = 'name' CACHE = 'cache' PATH = 'path' + ATTRS = 'attrs' + BOXES = 'bbxs' LABELS = 'labels' - BOXES = 'boxes' - BOXES_LABELS = 'boxes_labels' - N_BOXES = 'n_boxes' + N_BOXES = 'n_bbxs' @export class DatasetListKeys(Enum): + ATTRS = 'attrs' + BOXES = 'bbxs' LABELS = 'labels' - BOXES = 'boxes' - BOXES_LABELS = 'boxes_labels' @export @@ -29,5 +29,5 @@ class DatasetSplitKeys(Enum): @export class DatasetPartitionKeys(Enum): TEST = 'test' - VAL = 'val' + DEV = 'dev' TRAIN = 'train' diff --git a/nucleus/dataset/tools/watson.py b/nucleus/dataset/tools/watson.py index e0ca71a..aa3df2f 100644 --- a/nucleus/dataset/tools/watson.py +++ b/nucleus/dataset/tools/watson.py @@ -145,9 +145,9 @@ def create_examples_from_jobs( yield { DatasetKeys.PATH.value: example['source'], - DatasetKeys.LABELS.value: example['frameTags'], + DatasetKeys.ATTRS.value: example['frameTags'], DatasetKeys.BOXES.value: ijhw_list, - DatasetKeys.BOXES_LABELS.value: labels_list, + DatasetKeys.LABELS.value: labels_list, DatasetKeys.N_BOXES.value: len(ijhw_list), 'src': 'watson' } From a7cb7d9f84be4b2a29da77972f01643e1ae09f79 Mon Sep 17 00:00:00 2001 From: jinyeom Date: Thu, 3 Oct 2019 17:05:50 -0500 Subject: [PATCH 2/6] added split scheme --- nucleus/dataset/keys.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nucleus/dataset/keys.py b/nucleus/dataset/keys.py index 5df71c6..c219b42 100644 --- a/nucleus/dataset/keys.py +++ b/nucleus/dataset/keys.py @@ -23,7 +23,8 @@ class DatasetListKeys(Enum): @export class DatasetSplitKeys(Enum): - RANDOM = 'split_random' + RANDOM = 'set_split_random' + FEED = 'set_split_feed' @export From 1633c39e4aeafc5a56f38d04f1734e7ff4264326 Mon Sep 17 00:00:00 2001 From: jinyeom Date: Thu, 3 Oct 2019 17:06:02 -0500 Subject: [PATCH 3/6] type fix --- nucleus/dataset/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nucleus/dataset/base.py b/nucleus/dataset/base.py index 59b18ca..611878e 100644 --- a/nucleus/dataset/base.py +++ b/nucleus/dataset/base.py @@ -652,7 +652,7 @@ def _serialize_example( def get_ds( self, partition: Union[DatasetPartitionKeys, str], - split_column: Optional[Union[DatasetPartitionKeys, str]] = None, + split_column: Optional[Union[DatasetSplitKeys, str]] = None, n_examples: Optional[int] = None, shuffle: Optional[int] = 100, repeat: Optional[int] = 1, From 142736b0a1945ee8ec66d623e9fd4e4c4e78b773 Mon Sep 17 00:00:00 2001 From: jinyeom Date: Mon, 7 Oct 2019 14:33:56 -0500 Subject: [PATCH 4/6] added fix for when data is unlabelled (None) --- nucleus/dataset/base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nucleus/dataset/base.py b/nucleus/dataset/base.py index 611878e..625253f 100644 --- a/nucleus/dataset/base.py +++ b/nucleus/dataset/base.py @@ -777,13 +777,17 @@ def unique_elements_from_list_column( is_list = True if is_list: + # rows with labels = None (as opposed to []) are not labelled + labels_column = self.df[column] + labels_column = labels_column[labels_column.notnull()] + # if label_position is None: label_position = range( - np.max([len(labels) for labels in self.df[column]]) + np.max([len(labels) for labels in labels_column]) ) uniques = [[] for _ in label_position] - for labels in self.df[column]: + for labels in labels_column: for i, label in enumerate(labels): if label is None: continue From cecb7429d5fcad5780ebb512f2a5c0e47f517142 Mon Sep 17 00:00:00 2001 From: Joan Alabort Date: Thu, 10 Oct 2019 13:04:58 +0100 Subject: [PATCH 5/6] Make AF dataset work --- nucleus/box/tools.py | 11 +++--- nucleus/dataset/base.py | 78 +++++++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/nucleus/box/tools.py b/nucleus/box/tools.py index 3ad683c..3f45d99 100644 --- a/nucleus/box/tools.py +++ b/nucleus/box/tools.py @@ -86,10 +86,13 @@ def ijkl_to_xywh(ijkl: tf.Tensor) -> tf.Tensor: @export @name_scope def swap_axes_order(coords: tf.Tensor) -> tf.Tensor: - coord_indices = [1, 0, 3, 2] - other_indices = list(range(len(coord_indices), tf_get_shape(coords)[-1])) - indices = coord_indices + other_indices - return coords[..., indices] + return tf.concat([ + coords[..., 1][None], + coords[..., 0][None], + coords[..., 3][None], + coords[..., 2][None], + coords[..., 4:] + ], axis=-1) @export diff --git a/nucleus/dataset/base.py b/nucleus/dataset/base.py index 625253f..a4ce1bd 100644 --- a/nucleus/dataset/base.py +++ b/nucleus/dataset/base.py @@ -236,9 +236,10 @@ def _create_image_from_row(row: pd.Series) -> Image: if boxes is not None: box_list = [ - Box(ijhw=ijhw, labels=box_labels) - for ijhw, box_labels in zip(boxes, labels) - if None not in ijhw and all([c > 0 for c in ijhw[:2]]) + Box.from_xywh(xywh=tf.convert_to_tensor(xywh), + labels=box_labels) + for xywh, box_labels in zip(boxes, labels) + if None not in xywh and all([c > 0 for c in xywh[:2]]) ] else: box_list = None @@ -758,6 +759,7 @@ def expand_list_column(self, column: str) -> None: def unique_elements_from_list_column( self, column: str, + flat: bool = True # label_position: Optional[Union[int, List[int]]] = None ) -> List[List[str]]: r""" @@ -765,50 +767,56 @@ def unique_elements_from_list_column( Parameters ---------- column + flat label_position Returns ------- """ - try: - is_list = not isinstance(self.df[column][0][0], list) - except IndexError: - is_list = True - - if is_list: - # rows with labels = None (as opposed to []) are not labelled + if flat: labels_column = self.df[column] labels_column = labels_column[labels_column.notnull()] + return [sorted(list(set([e for l in labels_column for e in l])))] + else: + try: + is_list = not isinstance(self.df[column][0][0], list) + except IndexError: + is_list = True + + if is_list: + # rows with labels = None (as opposed to []) are not labelled + labels_column = self.df[column] + labels_column = labels_column[labels_column.notnull()] + + # if label_position is None: + label_position = range( + np.max([len(labels) for labels in labels_column]) + ) - # if label_position is None: - label_position = range( - np.max([len(labels) for labels in labels_column]) - ) + uniques = [[] for _ in label_position] + for labels in labels_column: + for i, label in enumerate(labels): + if label is None: + continue + uniques[i].append(label) - uniques = [[] for _ in label_position] - for labels in labels_column: - for i, label in enumerate(labels): - if label is None: - continue - uniques[i].append(label) + return [sorted(list(set(unique))) for unique in uniques] + else: + # if label_position is None: + label_position = range(len(self.df[column][0][0])) - return [sorted(list(set(unique))) for unique in uniques] - else: - # if label_position is None: - label_position = range(len(self.df[column][0][0])) - - uniques = [[] for _ in label_position] - for labels in self.df[column]: - if not isinstance(labels, list): - continue - for label in labels: - for i, l in enumerate(np.asanyarray(label)): - if l is None: - continue - uniques[i].append(l) + uniques = [[] for _ in label_position] + for labels in self.df[column]: + if not isinstance(labels, list): + continue + for label in labels: + for i, l in enumerate(np.asanyarray(label)): + if l is None: + continue + uniques[i].append(l) - return [sorted(list(set(unique))) for unique in uniques] + return [sorted(list(set(unique))) for unique in uniques] def view_row(self, index: int, image_args: Dict = None): _, image = self[index] From 612016a83015bb16d37e93e14aa24d15926fc9b6 Mon Sep 17 00:00:00 2001 From: Joan Alabort Date: Fri, 11 Oct 2019 12:47:55 +0100 Subject: [PATCH 6/6] Better deal with images that have no boxes --- nucleus/dataset/base.py | 2 +- nucleus/transform/base.py | 24 ++- nucleus/transform/geometric.py | 274 +++++++++++++------------- nucleus/transform/geometric_addons.py | 8 +- 4 files changed, 165 insertions(+), 143 deletions(-) diff --git a/nucleus/dataset/base.py b/nucleus/dataset/base.py index a4ce1bd..279e37a 100644 --- a/nucleus/dataset/base.py +++ b/nucleus/dataset/base.py @@ -234,7 +234,7 @@ def _create_image_from_row(row: pd.Series) -> Image: boxes = row.get(DatasetKeys.BOXES.value) labels = row.get(DatasetKeys.LABELS.value) - if boxes is not None: + if boxes not in [None, [[]]]: box_list = [ Box.from_xywh(xywh=tf.convert_to_tensor(xywh), labels=box_labels) diff --git a/nucleus/transform/base.py b/nucleus/transform/base.py index aea453b..0fe8c9f 100644 --- a/nucleus/transform/base.py +++ b/nucleus/transform/base.py @@ -50,6 +50,23 @@ class DeterministicTransform(Transform): """ n_factors: int + @staticmethod + def valid_boxes(boxes: tf.Tensor): + r""" + + Parameters + ---------- + boxes + + Returns + ------- + + """ + if boxes is None or tf.equal(tf.reduce_mean(boxes), -1): + return False + + return True + @abstractmethod def _operation( self, @@ -165,11 +182,11 @@ def __call__( """ # TODO: Some of these n_factors checks seem a bit hacky... Can we do # better? - if self.transform.n_factors == -1: + if self.transform.n_factors == -1 and boxes is not None: factors_shape = tf_get_shape(boxes[..., :4]) elif self.transform.n_factors == -2: factors_shape = tf_get_shape(image) - elif self.transform.n_factors == -3: + elif self.transform.n_factors == -3 and boxes is not None: factors_shape = () self.min_factor = 0 self.max_factor = tf.cast( @@ -177,7 +194,8 @@ def __call__( dtype=tf.float32 ) else: - factors_shape = (self.transform.n_factors,) + n_factors = self.transform.n_factors + factors_shape = (n_factors,) if n_factors > 0 else (1,) factors = tf.random.uniform( shape=factors_shape, diff --git a/nucleus/transform/geometric.py b/nucleus/transform/geometric.py index dea3ba0..0def09c 100644 --- a/nucleus/transform/geometric.py +++ b/nucleus/transform/geometric.py @@ -40,11 +40,12 @@ def _operation( flipped_boxes The horizontally flipped boxes. """ - max_boxes = tf_get_shape(boxes)[0] - boxes = unpad_tensor(boxes) image = tf.image.flip_left_right(image) - boxes = flip_boxes_left_right(boxes) - boxes = pad_tensor(boxes, max_length=max_boxes) + if self.valid_boxes(boxes=boxes): + max_boxes = tf_get_shape(boxes)[0] + boxes = unpad_tensor(boxes) + boxes = flip_boxes_left_right(boxes) + boxes = pad_tensor(boxes, max_length=max_boxes) return image, boxes @@ -84,9 +85,6 @@ def _operation( zoomed_boxes The zoomed boxes. """ - max_boxes = tf_get_shape(boxes)[0] - boxes = unpad_tensor(boxes) - image_shape = tf_get_shape(image) height, width, _ = image_shape @@ -96,28 +94,32 @@ def _operation( image = tf.image.resize(image, size=(new_height, new_width)) image = tf.image.resize_with_crop_or_pad(image, height, width) - ijhw_tensor = boxes[..., :4] * tf.convert_to_tensor( - [zoom_factor[0], zoom_factor[1]] * 2 - ) - offsets = tf.stack( - [(1.0 - zoom_factor[0]) / 2, (1.0 - zoom_factor[1]) / 2] - ) - boxes = tf.concat( - [ - ijhw_tensor[..., :2] + offsets, - ijhw_tensor[..., 2:], - boxes[..., 4:] - ], - axis=-1 - ) - - boxes = filter_boxes(boxes, pad=self.pad) - - boxes = tf.cond( - tf.equal(self.pad, True), - lambda: pad_tensor(boxes, max_length=max_boxes), - lambda: boxes - ) + if self.valid_boxes(boxes=boxes): + max_boxes = tf_get_shape(boxes)[0] + boxes = unpad_tensor(boxes) + + ijhw_tensor = boxes[..., :4] * tf.convert_to_tensor( + [zoom_factor[0], zoom_factor[1]] * 2 + ) + offsets = tf.stack( + [(1.0 - zoom_factor[0]) / 2, (1.0 - zoom_factor[1]) / 2] + ) + boxes = tf.concat( + [ + ijhw_tensor[..., :2] + offsets, + ijhw_tensor[..., 2:], + boxes[..., 4:] + ], + axis=-1 + ) + + boxes = filter_boxes(boxes, pad=self.pad) + + boxes = tf.cond( + tf.equal(self.pad, True), + lambda: pad_tensor(boxes, max_length=max_boxes), + lambda: boxes + ) return image, boxes @@ -139,8 +141,8 @@ class RandomZoom(RandomTransform): def __init__( self, pad: bool = True, - min_factor: float = 0.25, - max_factor: float = 1.25 + min_factor: float = 0.15, + max_factor: float = 1.15 ) -> None: super().__init__( transform=Zoom(pad=pad), @@ -180,16 +182,17 @@ def _operation( jittered_boxes The jittered bounding boxes. """ - max_boxes = tf_get_shape(boxes)[0] + if self.valid_boxes(boxes=boxes): + max_boxes = tf_get_shape(boxes)[0] - jitter = tf.concat( - [boxes[..., 2:4], boxes[..., 2:4]], axis=-1 - ) * jitter_factors - ijhw_tensor = boxes[..., :4] + jitter - boxes = tf.concat([ijhw_tensor, boxes[..., 4:]], axis=-1) + jitter = tf.concat( + [boxes[..., 2:4], boxes[..., 2:4]], axis=-1 + ) * jitter_factors + ijhw_tensor = boxes[..., :4] + jitter + boxes = tf.concat([ijhw_tensor, boxes[..., 4:]], axis=-1) - boxes = unpad_tensor(boxes, padding_value=0, boolean_fn=tf.less) - boxes = pad_tensor(boxes, max_length=max_boxes) + boxes = unpad_tensor(boxes, padding_value=0, boolean_fn=tf.less) + boxes = pad_tensor(boxes, max_length=max_boxes) return image, boxes @@ -272,102 +275,103 @@ def _operation( boxes """ - if not isinstance(box_index, tf.Tensor): - box_index = tf.convert_to_tensor(box_index) - box_index = tf.round(box_index) - box_index = tf.cast(box_index, dtype=tf.int32) - - max_boxes = tf_get_shape(boxes)[0] - boxes = unpad_tensor(boxes) - - height, width, _ = tf_get_shape(image) - resolution = tf.convert_to_tensor([height, width], dtype=tf.float32) - - ijhw_tensor = scale_coords(boxes[..., :4], resolution) - yxhw = ijhw_to_yxhw(ijhw_tensor[box_index]) - - offset_size = yxhw - tf.concat([self.size, self.size], axis=-1) / 2 - target_size = tf.concat([self.size, self.size], axis=-1) - - offset_height, target_height = tf.cond( - tf.less(offset_size[0], 0), - lambda: ( - tf.constant(0, dtype=tf.float32), - target_size[0] - ), - lambda: (offset_size[0], target_size[0]) - ) - offset_height = tf.cast(offset_height, tf.float32) - target_height = tf.cast(target_height, tf.float32) - - offset_width, target_width = tf.cond( - tf.less(offset_size[1], 0), - lambda: ( - tf.constant(0, dtype=tf.float32), - target_size[1] - ), - lambda: (offset_size[1], target_size[1]) - ) - offset_width = tf.cast(offset_width, tf.float32) - target_width = tf.cast(target_width, tf.float32) - - offset_height, target_height = tf.cond( - tf.greater_equal( - offset_height + target_height, - tf.cast(height, dtype=tf.float32) - ), - lambda: ( - offset_height - (tf.cast(height, dtype=tf.float32) - target_height), - target_size[0] - ), - lambda: (offset_height, target_height) - ) - - offset_width, target_width = tf.cond( - tf.greater_equal( - offset_width + target_width, - tf.cast(width, dtype=tf.float32) - ), - lambda: ( - offset_width - (tf.cast(width, dtype=tf.float32) - target_width), - target_size[1] - ), - lambda: (offset_width, target_width) - ) - - image = tf.image.crop_to_bounding_box( - image, - tf.cast(offset_height, dtype=tf.int32), - tf.cast(offset_width, dtype=tf.int32), - tf.cast(target_height, dtype=tf.int32), - tf.cast(target_width, dtype=tf.int32) - ) - - offset = tf.convert_to_tensor( - [offset_height, offset_width], - dtype=tf.float32 - ) - ij_tensor = ijhw_tensor[..., :2] - offset - - ijhw_tensor = tf.concat( - [ij_tensor, ijhw_tensor[..., 2:4]], axis=-1 - ) - - scale = tf.convert_to_tensor( - [target_height, target_width], - dtype=tf.float32 - ) - ijhw_tensor = ijhw_tensor / tf.concat([scale, scale], axis=-1) - - boxes = tf.concat([ijhw_tensor, boxes[..., 4:]], axis=-1) - - boxes = filter_boxes(boxes, pad=self.pad) - - boxes = tf.cond( - tf.equal(self.pad, True), - lambda: pad_tensor(boxes, max_length=max_boxes), - lambda: boxes - ) + if self.valid_boxes(boxes=boxes): + if not isinstance(box_index, tf.Tensor): + box_index = tf.convert_to_tensor(box_index) + box_index = tf.round(box_index) + box_index = tf.cast(box_index, dtype=tf.int32) + + max_boxes = tf_get_shape(boxes)[0] + boxes = unpad_tensor(boxes) + + height, width, _ = tf_get_shape(image) + resolution = tf.convert_to_tensor([height, width], dtype=tf.float32) + + ijhw_tensor = scale_coords(boxes[..., :4], resolution) + yxhw = ijhw_to_yxhw(ijhw_tensor[box_index]) + + offset_size = yxhw - tf.concat([self.size, self.size], axis=-1) / 2 + target_size = tf.concat([self.size, self.size], axis=-1) + + offset_height, target_height = tf.cond( + tf.less(offset_size[0], 0), + lambda: ( + tf.constant(0, dtype=tf.float32), + target_size[0] + ), + lambda: (offset_size[0], target_size[0]) + ) + offset_height = tf.cast(offset_height, tf.float32) + target_height = tf.cast(target_height, tf.float32) + + offset_width, target_width = tf.cond( + tf.less(offset_size[1], 0), + lambda: ( + tf.constant(0, dtype=tf.float32), + target_size[1] + ), + lambda: (offset_size[1], target_size[1]) + ) + offset_width = tf.cast(offset_width, tf.float32) + target_width = tf.cast(target_width, tf.float32) + + offset_height, target_height = tf.cond( + tf.greater_equal( + offset_height + target_height, + tf.cast(height, dtype=tf.float32) + ), + lambda: ( + offset_height - (tf.cast(height, dtype=tf.float32) - target_height), + target_size[0] + ), + lambda: (offset_height, target_height) + ) + + offset_width, target_width = tf.cond( + tf.greater_equal( + offset_width + target_width, + tf.cast(width, dtype=tf.float32) + ), + lambda: ( + offset_width - (tf.cast(width, dtype=tf.float32) - target_width), + target_size[1] + ), + lambda: (offset_width, target_width) + ) + + image = tf.image.crop_to_bounding_box( + image, + tf.cast(offset_height, dtype=tf.int32), + tf.cast(offset_width, dtype=tf.int32), + tf.cast(target_height, dtype=tf.int32), + tf.cast(target_width, dtype=tf.int32) + ) + + offset = tf.convert_to_tensor( + [offset_height, offset_width], + dtype=tf.float32 + ) + ij_tensor = ijhw_tensor[..., :2] - offset + + ijhw_tensor = tf.concat( + [ij_tensor, ijhw_tensor[..., 2:4]], axis=-1 + ) + + scale = tf.convert_to_tensor( + [target_height, target_width], + dtype=tf.float32 + ) + ijhw_tensor = ijhw_tensor / tf.concat([scale, scale], axis=-1) + + boxes = tf.concat([ijhw_tensor, boxes[..., 4:]], axis=-1) + + boxes = filter_boxes(boxes, pad=self.pad) + + boxes = tf.cond( + tf.equal(self.pad, True), + lambda: pad_tensor(boxes, max_length=max_boxes), + lambda: boxes + ) return image, boxes diff --git a/nucleus/transform/geometric_addons.py b/nucleus/transform/geometric_addons.py index 90b255c..1ead730 100644 --- a/nucleus/transform/geometric_addons.py +++ b/nucleus/transform/geometric_addons.py @@ -54,7 +54,7 @@ def _operation( interpolation='BILINEAR', ) - if boxes is not None: + if self.valid_boxes(boxes=boxes): offsets = tf.stack([pan_factor[0], pan_factor[1]]) boxes = tf.concat( [ @@ -85,8 +85,8 @@ class RandomPan(RandomTransform): """ def __init__( self, - min_factor: float = -0.2, - max_factor: float = 0.2, + min_factor: float = -0.1, + max_factor: float = 0.1, pad: bool = True ) -> None: super().__init__( @@ -133,7 +133,7 @@ def _operation( angle = (pi / 180) * angle_factor image = tfa.image.rotate(image, angle, interpolation='BILINEAR') - if boxes is not None: + if self.valid_boxes(boxes=boxes): height, width, _ = tf_get_shape(image) image_shape = tf.stack([height, width])