[GRASS-SVN] r73201 - grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib
svn_grass at osgeo.org
svn_grass at osgeo.org
Tue Aug 28 06:02:06 PDT 2018
Author: pesekon2
Date: 2018-08-28 06:02:06 -0700 (Tue, 28 Aug 2018)
New Revision: 73201
Modified:
grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/config.py
grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/model.py
grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/utils.py
Log:
spellcheck and update API for newer versions of libraries
Modified: grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/config.py
===================================================================
--- grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/config.py 2018-08-28 08:40:12 UTC (rev 73200)
+++ grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/config.py 2018-08-28 13:02:06 UTC (rev 73201)
@@ -57,7 +57,7 @@
# How many anchors per image to use for RPN training
RPN_TRAIN_ANCHORS_PER_IMAGE = 256
- # ROIs kept after non-maximum supression (training and inference)
+ # ROIs kept after non-maximum suppression (training and inference)
POST_NMS_ROIS_TRAINING = 2000
POST_NMS_ROIS_INFERENCE = 1000
@@ -100,7 +100,7 @@
# Learning rate and momentum
# The Mask RCNN paper uses lr=0.02, but on TensorFlow it causes
- # weights to explode. Likely due to differences in optimzer
+ # weights to explode. Likely due to differences in optimizer
# implementation.
LEARNING_RATE = 0.001 # 0.002 before change
LEARNING_MOMENTUM = 0.9
@@ -161,7 +161,7 @@
self.VALIDATION_STEPS = validationSteps
# Input image resizing
- # Generally, use the "square" resizing mode for training and inferencing
+ # Generally, use the "square" resizing mode for training and predicting
# and it should work well in most cases. In this mode, images are scaled
# up such that the small side is = IMAGE_MIN_DIM, but ensuring that the
# scaling doesn't make the long side > IMAGE_MAX_DIM. Then the image is
@@ -204,6 +204,8 @@
[self.IMAGE_MAX_DIM, self.IMAGE_MAX_DIM, 3])
# Compute backbone size from input image size
+ # TODO Ondrej Pesek: Maybe delete it and see Matterport's (avoid math
+ # import)
self.BACKBONE_SHAPES = np.array(
[[int(math.ceil(self.IMAGE_SHAPE[0] / stride)),
int(math.ceil(self.IMAGE_SHAPE[1] / stride))]
@@ -212,7 +214,7 @@
# Train or freeze batch normalization layers
# None: Train BN layers in a normal mode
# False: Freeze BN layers (recommended for small batch size)
- # True: Set layer in training mode even when inferencing
+ # True: Set layer in training mode even when predicting
self.TRAIN_BN = trainBatchNorm
# Image meta data length
Modified: grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/model.py
===================================================================
--- grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/model.py 2018-08-28 08:40:12 UTC (rev 73200)
+++ grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/model.py 2018-08-28 13:02:06 UTC (rev 73201)
@@ -1,6 +1,6 @@
"""
Mask R-CNN
-The main Mask R-CNN model implemenetation.
+The main Mask R-CNN model implementation.
Copyright (c) 2017 Matterport, Inc.
Licensed under the MIT License (see LICENSE for details)
@@ -61,13 +61,12 @@
so this layer is often frozen (via setting in Config class) and functions
as linear layer.
"""
-
def call(self, inputs, training=None):
"""
Note about training values:
None: Train BN layers. This is the normal mode
False: Freeze BN layers. Good when batch size is small
- True: (don't use). Set layer in training mode even when inferencing
+ True: (don't use). Set layer in training mode even when making inferences
"""
return super(self.__class__, self).call(inputs, training=training)
@@ -101,12 +100,12 @@
"""The identity_block is the block that has no conv layer at shortcut
# Arguments
input_tensor: input tensor
- kernel_size: defualt 3, the kernel size of middle conv layer at main path
+ kernel_size: default 3, the kernel size of middle conv layer at main path
filters: list of integers, the nb_filters of 3 conv layer at main path
stage: integer, current stage label, used for generating layer names
block: 'a','b'..., current block label, used for generating layer names
use_bias: Boolean. To use or not use a bias in conv layers.
- train_bn: Boolean. Train or freeze Batch Norm layres
+ train_bn: Boolean. Train or freeze Batch Norm layers
"""
nb_filter1, nb_filter2, nb_filter3 = filters
conv_name_base = 'res' + str(stage) + block + '_branch'
@@ -136,12 +135,12 @@
"""conv_block is the block that has a conv layer at shortcut
# Arguments
input_tensor: input tensor
- kernel_size: defualt 3, the kernel size of middle conv layer at main path
+ kernel_size: default 3, the kernel size of middle conv layer at main path
filters: list of integers, the nb_filters of 3 conv layer at main path
stage: integer, current stage label, used for generating layer names
block: 'a','b'..., current block label, used for generating layer names
use_bias: Boolean. To use or not use a bias in conv layers.
- train_bn: Boolean. Train or freeze Batch Norm layres
+ train_bn: Boolean. Train or freeze Batch Norm layers
Note that from stage 3, the first conv layer at main path is with subsample=(2,2)
And the shortcut should have subsample=(2,2) as well
"""
@@ -176,7 +175,7 @@
"""Build a ResNet graph.
architecture: Can be resnet50 or resnet101
stage5: Boolean. If False, stage5 of the network is not created
- train_bn: Boolean. Train or freeze Batch Norm layres
+ train_bn: Boolean. Train or freeze Batch Norm layers
"""
assert architecture in ["resnet50", "resnet101"]
# Stage 1
@@ -271,8 +270,7 @@
Proposals in normalized coordinates [batch, rois, (y1, x1, y2, x2)]
"""
- def __init__(self, proposal_count, nms_threshold,
- config=None, **kwargs):
+ def __init__(self, proposal_count, nms_threshold, config=None, **kwargs):
super(ProposalLayer, self).__init__(**kwargs)
self.config = config
self.proposal_count = proposal_count
@@ -342,7 +340,7 @@
############################################################
def log2_graph(x):
- """Implementatin of Log2. TF doesn't have a native implemenation."""
+ """Implementation of Log2. TF doesn't have a native implementation."""
return tf.log(x) / tf.log(2.0)
@@ -404,7 +402,7 @@
ix = tf.where(tf.equal(roi_level, level))
level_boxes = tf.gather_nd(boxes, ix)
- # Box indicies for crop_and_resize.
+ # Box indices for crop_and_resize.
box_indices = tf.cast(ix[:, 0], tf.int32)
# Keep track of which box is mapped to which level
@@ -462,9 +460,9 @@
"""Computes IoU overlaps between two sets of boxes.
boxes1, boxes2: [N, (y1, x1, y2, x2)].
"""
- # 1. Tile boxes2 and repeate boxes1. This allows us to compare
+ # 1. Tile boxes2 and repeat boxes1. This allows us to compare
# every boxes1 against every boxes2 without loops.
- # TF doesn't have an equivalent to np.repeate() so simulate it
+ # TF doesn't have an equivalent to np.repeat() so simulate it
# using tf.tile() and tf.reshape.
b1 = tf.reshape(tf.tile(tf.expand_dims(boxes1, 1),
[1, 1, tf.shape(boxes2)[0]]), [-1, 4])
@@ -544,7 +542,7 @@
crowd_iou_max = tf.reduce_max(crowd_overlaps, axis=1)
no_crowd_bool = (crowd_iou_max < 0.001)
- # Determine postive and negative ROIs
+ # Determine positive and negative ROIs
roi_iou_max = tf.reduce_max(overlaps, axis=1)
# 1. Positive ROIs are those with >= 0.5 IoU with a GT box
positive_roi_bool = (roi_iou_max >= 0.5)
@@ -589,7 +587,7 @@
# Compute mask targets
boxes = positive_rois
if config.USE_MINI_MASK:
- # Transform ROI corrdinates from normalized image space
+ # Transform ROI coordinates from normalized image space
# to normalized mini-mask space.
y1, x1, y2, x2 = tf.split(positive_rois, 4, axis=1)
gt_y1, gt_x1, gt_y2, gt_x2 = tf.split(roi_gt_boxes, 4, axis=1)
@@ -746,7 +744,7 @@
tf.gather(pre_nms_scores, ixs),
max_output_size=config.DETECTION_MAX_INSTANCES,
iou_threshold=config.DETECTION_NMS_THRESHOLD)
- # Map indicies
+ # Map indices
class_keep = tf.gather(keep, tf.gather(ixs, class_keep))
# Pad with -1 so returned tensors have the same shape
gap = config.DETECTION_MAX_INSTANCES - tf.shape(class_keep)[0]
@@ -849,8 +847,8 @@
rpn_bbox: [batch, H, W, (dy, dx, log(dh), log(dw))] Deltas to be
applied to anchors.
"""
- # TODO: check if stride of 2 causes alignment issues if the featuremap
- # is not even.
+ # TODO: check if stride of 2 causes alignment issues if the feature map
+ # is not even.
# Shared convolutional base of the RPN
shared = KL.Conv2D(512, (3, 3), padding='same', activation='relu',
strides=anchor_stride,
@@ -913,12 +911,12 @@
rois: [batch, num_rois, (y1, x1, y2, x2)] Proposal boxes in normalized
coordinates.
- feature_maps: List of feature maps from diffent layers of the pyramid,
+ feature_maps: List of feature maps from different layers of the pyramid,
[P2, P3, P4, P5]. Each has a different resolution.
- image_meta: [batch, (meta data)] Image details. See compose_image_meta()
pool_size: The width of the square feature map generated from ROI Pooling.
num_classes: number of classes, which determines the depth of the results
- train_bn: Boolean. Train or freeze Batch Norm layres
+ train_bn: Boolean. Train or freeze Batch Norm layers
fc_layers_size: Size of the 2 FC layers
Returns:
@@ -930,8 +928,7 @@
# ROI Pooling
# Shape: [batch, num_boxes, pool_height, pool_width, channels]
x = PyramidROIAlign([pool_size, pool_size],
- name="roi_align_classifier")(
- [rois, image_meta] + feature_maps)
+ name="roi_align_classifier")([rois, image_meta] + feature_maps)
# Two 1024 FC layers (implemented with Conv2D for consistency)
x = KL.TimeDistributed(KL.Conv2D(fc_layers_size, (pool_size, pool_size), padding="valid"),
name="mrcnn_class_conv1")(x)
@@ -968,12 +965,12 @@
rois: [batch, num_rois, (y1, x1, y2, x2)] Proposal boxes in normalized
coordinates.
- feature_maps: List of feature maps from diffent layers of the pyramid,
+ feature_maps: List of feature maps from different layers of the pyramid,
[P2, P3, P4, P5]. Each has a different resolution.
image_meta: [batch, (meta data)] Image details. See compose_image_meta()
pool_size: The width of the square feature map generated from ROI Pooling.
num_classes: number of classes, which determines the depth of the results
- train_bn: Boolean. Train or freeze Batch Norm layres
+ train_bn: Boolean. Train or freeze Batch Norm layers
Returns: Masks [batch, roi_count, height, width, num_classes]
"""
@@ -1020,7 +1017,7 @@
def smooth_l1_loss(y_true, y_pred):
"""Implements Smooth-L1 loss.
- y_true and y_pred are typicallly: [N, 4], but could be any shape.
+ y_true and y_pred are typically: [N, 4], but could be any shape.
"""
diff = K.abs(y_true - y_pred)
less_than_one = K.cast(K.less(diff, 1.0), "float32")
@@ -1045,7 +1042,7 @@
# Pick rows that contribute to the loss and filter out the rest.
rpn_class_logits = tf.gather_nd(rpn_class_logits, indices)
anchor_class = tf.gather_nd(anchor_class, indices)
- # Crossentropy loss
+ # Cross entropy loss
loss = K.sparse_categorical_crossentropy(target=anchor_class,
output=rpn_class_logits,
from_logits=True)
@@ -1135,7 +1132,7 @@
pred_bbox = K.reshape(pred_bbox, (-1, K.int_shape(pred_bbox)[2], 4))
# Only positive ROIs contribute to the loss. And only
- # the right class_id of each ROI. Get their indicies.
+ # the right class_id of each ROI. Get their indices.
positive_roi_ix = tf.where(target_class_ids > 0)[:, 0]
positive_roi_class_ids = tf.cast(
tf.gather(target_class_ids, positive_roi_ix), tf.int64)
@@ -1198,10 +1195,9 @@
def load_image_gt(dataset, config, image_id, augment=False, augmentation=None,
use_mini_mask=False):
- """
- Load and return ground truth data for an image (image, mask, bounding boxes).
+ """Load and return ground truth data for an image (image, mask, bounding boxes).
- augment: (Depricated. Use augmentation instead). If true, apply random
+ augment: (deprecated. Use augmentation instead). If true, apply random
image augmentation. Currently, only horizontal flipping is offered.
augmentation: Optional. An imgaug (https://github.com/aleju/imgaug) augmentation.
For example, passing imgaug.augmenters.Fliplr(0.5) flips images
@@ -1240,7 +1236,7 @@
# Random horizontal flips.
# TODO: will be removed in a future update in favor of augmentation
if augment:
- logging.warning("'augment' is depricated. Use 'augmentation' instead.")
+ logging.warning("'augment' is deprecated. Use 'augmentation' instead.")
if random.randint(0, 1):
image = np.fliplr(image)
mask = np.fliplr(mask)
@@ -1250,7 +1246,7 @@
if augmentation:
import imgaug
- # Augmentors that are safe to apply to masks
+ # Augmenters that are safe to apply to masks
# Some, such as Affine, have settings that make them unsafe, so always
# test your augmentation on masks
MASK_AUGMENTERS = ["Sequential", "SomeOf", "OneOf", "Sometimes",
@@ -1259,7 +1255,7 @@
def hook(images, augmenter, parents, default):
"""Determines which augmenters to apply to masks."""
- return (augmenter.__class__.__name__ in MASK_AUGMENTERS)
+ return augmenter.__class__.__name__ in MASK_AUGMENTERS
# Store shapes before augmentation to compare
image_shape = image.shape
@@ -1313,7 +1309,7 @@
rpn_rois: [N, (y1, x1, y2, x2)] proposal boxes.
gt_class_ids: [instance count] Integer class IDs
gt_boxes: [instance count, (y1, x1, y2, x2)]
- gt_masks: [height, width, instance count] Grund truth masks. Can be full
+ gt_masks: [height, width, instance count] Ground truth masks. Can be full
size or mini-masks.
Returns:
@@ -1368,7 +1364,7 @@
# Negative ROIs are those with max IoU 0.1-0.5 (hard example mining)
# TODO: To hard example mine or not to hard example mine, that's the question
-# bg_ids = np.where((rpn_roi_iou_max >= 0.1) & (rpn_roi_iou_max < 0.5))[0]
+ # bg_ids = np.where((rpn_roi_iou_max >= 0.1) & (rpn_roi_iou_max < 0.5))[0]
bg_ids = np.where(rpn_roi_iou_max < 0.5)[0]
# Subsample ROIs. Aim for 33% foreground.
@@ -1384,7 +1380,7 @@
keep_bg_ids = np.random.choice(bg_ids, remaining, replace=False)
else:
keep_bg_ids = bg_ids
- # Combine indicies of ROIs to keep
+ # Combine indices of ROIs to keep
keep = np.concatenate([keep_fg_ids, keep_bg_ids])
# Need more?
remaining = config.TRAIN_ROIS_PER_IMAGE - keep.shape[0]
@@ -1448,10 +1444,7 @@
# Resize mini mask to size of GT box
placeholder[gt_y1:gt_y2, gt_x1:gt_x2] = \
np.round(skimage.transform.resize(
- class_mask,
- (gt_h, gt_w),
- order=1,
- mode="constant")).astype(bool)
+ class_mask, (gt_h, gt_w), order=1, mode="constant")).astype(bool)
# Place the mini batch in the placeholder
class_mask = placeholder
@@ -1458,8 +1451,7 @@
# Pick part of the mask and resize it
y1, x1, y2, x2 = rois[i].astype(np.int32)
m = class_mask[y1:y2, x1:x2]
- mask = skimage.transform.resize(m, config.MASK_SHAPE, order=1,
- mode="constant")
+ mask = skimage.transform.resize(m, config.MASK_SHAPE, order=1, mode="constant")
masks[i, :, :, class_id] = mask
return rois, roi_gt_class_ids, bboxes, masks
@@ -1651,7 +1643,8 @@
def data_generator(dataset, config, shuffle=True, augment=False, augmentation=None,
- random_rois=0, batch_size=1, detection_targets=False):
+ random_rois=0, batch_size=1, detection_targets=False,
+ no_augmentation_sources=None):
"""A generator that returns images and corresponding target class ids,
bounding box deltas, and masks.
@@ -1658,7 +1651,7 @@
dataset: The Dataset object to pick data from
config: The model config object
shuffle: If True, shuffles the samples before every epoch
- augment: (Depricated. Use augmentation instead). If true, apply random
+ augment: (deprecated. Use augmentation instead). If true, apply random
image augmentation. Currently, only horizontal flipping is offered.
augmentation: Optional. An imgaug (https://github.com/aleju/imgaug) augmentation.
For example, passing imgaug.augmenters.Fliplr(0.5) flips images
@@ -1670,9 +1663,12 @@
detection_targets: If True, generate detection targets (class IDs, bbox
deltas, and masks). Typically for debugging or visualizations because
in trainig detection targets are generated by DetectionTargetLayer.
+ no_augmentation_sources: Optional. List of sources to exclude for
+ augmentation. A source is string that identifies a dataset and is
+ defined in the Dataset class.
Returns a Python generator. Upon calling next() on it, the
- generator returns two lists, inputs and outputs. The containtes
+ generator returns two lists, inputs and outputs. The contents
of the lists differs depending on the received arguments:
inputs list:
- images: [batch, H, W, C]
@@ -1693,6 +1689,7 @@
image_index = -1
image_ids = np.copy(dataset.image_ids)
error_count = 0
+ no_augmentation_sources = no_augmentation_sources or []
# Anchors
# [anchor_count, (y1, x1, y2, x2)]
@@ -1703,7 +1700,7 @@
config.BACKBONE_STRIDES,
config.RPN_ANCHOR_STRIDE)
- # Keras requires a generator to run indefinately.
+ # Keras requires a generator to run indefinitely.
while True:
try:
# Increment index to pick next image. Shuffle if at the start of an epoch.
@@ -1713,10 +1710,18 @@
# Get GT bounding boxes and masks for image.
image_id = image_ids[image_index]
- image, image_meta, gt_class_ids, gt_boxes, gt_masks, error = \
+
+ # If the image source is not to be augmented pass None as augmentation
+ if dataset.image_info[image_id]['source'] in no_augmentation_sources:
+ image, image_meta, gt_class_ids, gt_boxes, gt_masks, error = \
load_image_gt(dataset, config, image_id, augment=augment,
- augmentation=augmentation,
+ augmentation=None,
use_mini_mask=config.USE_MINI_MASK)
+ else:
+ image, image_meta, gt_class_ids, gt_boxes, gt_masks = \
+ load_image_gt(dataset, config, image_id, augment=augment,
+ augmentation=augmentation,
+ use_mini_mask=config.USE_MINI_MASK)
if error:
import warnings
@@ -1948,8 +1953,7 @@
anchors = self.get_anchors(config.IMAGE_SHAPE)
# Duplicate across the batch dimension because Keras requires it
# TODO: can this be optimized to avoid duplicating the anchors?
- anchors = np.broadcast_to(anchors,
- (config.BATCH_SIZE,) + anchors.shape)
+ anchors = np.broadcast_to(anchors, (config.BATCH_SIZE,) + anchors.shape)
# A hack to get around Keras's bad support for constants
anchors = KL.Lambda(lambda x: tf.Variable(anchors), name="anchors")(input_image)
else:
@@ -2065,8 +2069,7 @@
# Create masks for detections
detection_boxes = KL.Lambda(lambda x: x[..., :4])(detections)
- mrcnn_mask = build_fpn_mask_graph(detection_boxes,
- mrcnn_feature_maps,
+ mrcnn_mask = build_fpn_mask_graph(detection_boxes, mrcnn_feature_maps,
input_image_meta,
config.MASK_POOL_SIZE,
config.NUM_CLASSES,
@@ -2099,8 +2102,7 @@
import errno
raise FileNotFoundError(
errno.ENOENT,
- "Could not find model directory under {}".format(
- self.model_dir))
+ "Could not find model directory under {}".format(self.model_dir))
# Pick last directory
dir_name = os.path.join(self.model_dir, dir_names[-1])
# Find the last checkpoint
@@ -2110,20 +2112,24 @@
if not checkpoints:
import errno
raise FileNotFoundError(
- errno.ENOENT,
- "Could not find weight files in {}".format(
- dir_name))
+ errno.ENOENT, "Could not find weight files in {}".format(dir_name))
checkpoint = os.path.join(dir_name, checkpoints[-1])
return checkpoint
def load_weights(self, filepath, by_name=False, exclude=None):
- """Modified version of the correspoding Keras function with
+ """Modified version of the corresponding Keras function with
the addition of multi-GPU support and the ability to exclude
some layers from loading.
- exlude: list of layer names to excluce
+ exclude: list of layer names to exclude
"""
import h5py
- from keras.engine import topology
+ # Conditional import to support versions of Keras before 2.2
+ # TODO: remove in about 6 months (end of 2018)
+ try:
+ from keras.engine import saving
+ except ImportError:
+ # Keras before 2.2 used the 'topology' namespace.
+ from keras.engine import topology as saving
if exclude:
by_name = True
@@ -2145,9 +2151,9 @@
layers = filter(lambda l: l.name not in exclude, layers)
if by_name:
- topology.load_weights_from_hdf5_group_by_name(f, layers)
+ saving.load_weights_from_hdf5_group_by_name(f, layers)
else:
- topology.load_weights_from_hdf5_group(f, layers)
+ saving.load_weights_from_hdf5_group(f, layers)
if hasattr(f, 'close'):
f.close()
@@ -2248,7 +2254,7 @@
layer.layer.trainable = trainable
else:
layer.trainable = trainable
- # Print trainble layer names
+ # Print trainable layer names
if trainable and verbose > 0:
log("{}{:20} ({})".format(" " * indent, layer.name,
layer.__class__.__name__))
@@ -2277,7 +2283,7 @@
int(m.group(4)), int(m.group(5)))
# Epoch number in file is 1-based, and in Keras code it's 0-based.
# So, adjust for that then increment by one to start from the next epoch
- self.epoch = int(m.group(6))
+ self.epoch = int(m.group(6)) - 1 + 1
print('Re-starting from epoch %d' % self.epoch)
# Directory for training logs
@@ -2295,7 +2301,7 @@
"*epoch*", "{epoch:04d}")
def train(self, train_dataset, val_dataset, learning_rate, epochs, layers,
- augmentation=None):
+ augmentation=None, custom_callbacks=None, no_augmentation_sources=None):
"""Train the model.
train_dataset, val_dataset: Training and validation Dataset objects.
learning_rate: The learning rate to train with
@@ -2316,12 +2322,17 @@
flips images right/left 50% of the time. You can pass complex
augmentations as well. This augmentation applies 50% of the
time, and when it does it flips images right/left half the time
- and adds a Gausssian blur with a random sigma in range 0 to 5.
+ and adds a Gaussian blur with a random sigma in range 0 to 5.
augmentation = imgaug.augmenters.Sometimes(0.5, [
imgaug.augmenters.Fliplr(0.5),
imgaug.augmenters.GaussianBlur(sigma=(0.0, 5.0))
])
+ custom_callbacks: Optional. Add custom callbacks to be called
+ with the keras fit_generator method. Must be list of type keras.callbacks.
+ no_augmentation_sources: Optional. List of sources to exclude for
+ augmentation. A source is string that identifies a dataset and is
+ defined in the Dataset class.
"""
assert self.mode == "training", "Create model in training mode."
@@ -2340,10 +2351,10 @@
layers = layer_regex[layers]
# Data generators
- train_generator = data_generator(train_dataset, self.config,
- shuffle=True,
+ train_generator = data_generator(train_dataset, self.config, shuffle=True,
augmentation=augmentation,
- batch_size=self.config.BATCH_SIZE)
+ batch_size=self.config.BATCH_SIZE,
+ no_augmentation_sources=no_augmentation_sources)
val_generator = data_generator(val_dataset, self.config, shuffle=True,
batch_size=self.config.BATCH_SIZE)
@@ -2355,6 +2366,10 @@
verbose=0, save_weights_only=True),
]
+ # Add custom callbacks to the list
+ if custom_callbacks:
+ callbacks += custom_callbacks
+
# Train
log("\nStarting at epoch {}. LR={}\n".format(self.epoch, learning_rate))
log("Checkpoint Path: {}".format(self.checkpoint_path))
@@ -2386,10 +2401,10 @@
def mold_inputs(self, images):
"""Takes a list of images and modifies them to the format expected
as an input to the neural network.
- images: List of image matricies [height,width,depth]. Images can have
+ images: List of image matrices [height,width,depth]. Images can have
different sizes.
- Returns 3 Numpy matricies:
+ Returns 3 Numpy matrices:
molded_images: [N, h, w, 3]. Images resized and normalized.
image_metas: [N, length of meta data]. Details about each image.
windows: [N, (y1, x1, y2, x2)]. The portion of the image that has the
@@ -2555,7 +2570,7 @@
the model.
molded_images: List of images loaded using load_image_gt()
- image_metas: image meta data, also retruned by load_image_gt()
+ image_metas: image meta data, also returned by load_image_gt()
Returns a list of dicts, one dict per image. The dict contains:
rois: [N, (y1, x1, y2, x2)] detection bounding boxes
@@ -2683,7 +2698,7 @@
outputs.
image_metas: If provided, the images are assumed to be already
- molded (i.e. resized, padded, and noramlized)
+ molded (i.e. resized, padded, and normalized)
outputs: List of tuples (name, tensor) to compute. The tensors are
symbolic TensorFlow tensors and the names are for easy tracking.
@@ -2808,7 +2823,7 @@
def mold_image(images, config):
- """Expects an RGB image (or array of images) and subtraces
+ """Expects an RGB image (or array of images) and subtracts
the mean pixel and converts it to float. Expects image
colors in RGB order.
"""
@@ -2825,7 +2840,7 @@
############################################################
def trim_zeros_graph(boxes, name=None):
- """Often boxes are represented with matricies of shape [N, 4] and
+ """Often boxes are represented with matrices of shape [N, 4] and
are padded with zeros. This removes zero boxes.
boxes: [N, 4] matrix of boxes.
Modified: grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/utils.py
===================================================================
--- grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/utils.py 2018-08-28 08:40:12 UTC (rev 73200)
+++ grass-addons/grass7/imagery/i.ann.maskrcnn/maskrcnnlib/utils.py 2018-08-28 13:02:06 UTC (rev 73201)
@@ -10,6 +10,7 @@
Different way of removing the alpha channel (contained in load_mask)
Deleted download_train_weights
A little refactoring made
+ # last cross-check with Matterport's implementation: 28/08/2018
"""
import os
@@ -66,7 +67,7 @@
boxes_area: array of length boxes_count.
Note: the areas are passed in rather than calculated here for
- efficency. Calculate once in the caller to avoid duplicate work.
+ efficiency. Calculate once in the caller to avoid duplicate work.
"""
# Calculate intersection areas
y1 = np.maximum(box[0], boxes[:, 0])
@@ -99,9 +100,9 @@
def compute_overlaps_masks(masks1, masks2):
- '''Computes IoU overlaps between two sets of masks.
+ """Computes IoU overlaps between two sets of masks.
masks1, masks2: [Height, Width, instances]
- '''
+ """
# If either set of masks is empty return empty result
if masks1.shape[0] == 0 or masks2.shape[0] == 0:
@@ -121,7 +122,7 @@
def non_max_suppression(boxes, scores, threshold):
- """Performs non-maximum supression and returns indicies of kept boxes.
+ """Performs non-maximum suppression and returns indices of kept boxes.
boxes: [N, (y1, x1, y2, x2)]. Notice that (y2, x2) lays outside the box.
scores: 1-D array of box scores.
threshold: Float. IoU threshold to use for filtering.
@@ -148,10 +149,10 @@
# Compute IoU of the picked box with the rest
iou = compute_iou(boxes[i], boxes[ixs[1:]], area[i], area[ixs[1:]])
# Identify boxes with IoU over the threshold. This
- # returns indicies into ixs[1:], so add 1 to get
- # indicies into ixs.
+ # returns indices into ixs[1:], so add 1 to get
+ # indices into ixs.
remove_ixs = np.where(iou > threshold)[0] + 1
- # Remove indicies of the picked and overlapped boxes.
+ # Remove indices of the picked and overlapped boxes.
ixs = np.delete(ixs, remove_ixs)
ixs = np.delete(ixs, 0)
return np.array(pick, dtype=np.int32)
@@ -318,8 +319,7 @@
self.source_class_ids[source].append(i)
def map_source_class_id(self, source_class_id):
- """
- Takes a source class ID and returns the int class ID assigned to it.
+ """Takes a source class ID and returns the int class ID assigned to it.
For example:
dataset.map_source_class_id("coco.12") -> 23
@@ -327,10 +327,7 @@
return self.class_from_source_map[source_class_id]
def get_source_class_id(self, class_id, source):
- """
- Map an internal class ID to the corresponding class ID in the source
- dataset.
- """
+ """Map an internal class ID to the corresponding class ID in the source dataset."""
info = self.class_info[class_id]
assert info['source'] == source
return info['id']
@@ -352,14 +349,13 @@
def source_image_link(self, image_id):
"""Returns the path or URL to the image.
- Override this to return a URL to the image if it's availble online for
- easy debugging.
+ Override this to return a URL to the image if it's available online for easy
+ debugging.
"""
return self.image_info[image_id]["path"]
def load_image(self, image_id):
- """
- Load the specified image and return a [H,W,3] Numpy array.
+ """Load the specified image and return a [H,W,3] Numpy array.
Modified by Ondrej Pesek to not care about alpha channel
"""
@@ -394,16 +390,15 @@
path=image)
def get_mask(self, image_id):
- """
- Load instance masks for the given image.
+ """Load instance masks for the given image.
This function converts the different mask format to one format in the
form of an array of binary masks of shape [height, width, instances].
Returns:
- masks: A bool array of shape [height, width, instance count] with
- a binary mask per instance.
- class_ids: a 1D array of class IDs of the instance masks.
+ masks: A bool array of shape [height, width, instance count] with
+ a binary mask per instance.
+ class_ids: a 1D array of class IDs of the instance masks.
Written by Ondrej Pesek
"""
@@ -442,10 +437,8 @@
-def resize_image(image, min_dim=None, max_dim=None, min_scale=None,
- mode="square"):
- """
- Resizes an image keeping the aspect ratio unchanged.
+def resize_image(image, min_dim=None, max_dim=None, min_scale=None, mode="square"):
+ """Resizes an image keeping the aspect ratio unchanged.
min_dim: if provided, resizes the image such that it's smaller
dimension == min_dim
@@ -627,8 +620,7 @@
"""
threshold = 0.5
y1, x1, y2, x2 = bbox
- mask = skimage.transform.resize(mask, (y2 - y1, x2 - x1), order=1,
- mode="constant")
+ mask = skimage.transform.resize(mask, (y2 - y1, x2 - x1), order=1, mode="constant")
mask = np.where(mask >= threshold, 1, 0).astype(np.bool)
# Put the mask in the right location.
More information about the grass-commit
mailing list