Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
matterport
GitHub Repository: matterport/Mask_RCNN
Path: blob/master/samples/nucleus/inspect_nucleus_model.ipynb
240 views
Kernel: Python 3

Mask R-CNN - Inspect Nucleus Trained Model

Code and visualizations to test, debug, and evaluate the Mask R-CNN model.

import os import sys import random import math import re import time import numpy as np import tensorflow as tf import matplotlib import matplotlib.pyplot as plt import matplotlib.patches as patches # Root directory of the project ROOT_DIR = os.path.abspath("../../") # Import Mask RCNN sys.path.append(ROOT_DIR) # To find local version of the library from mrcnn import utils from mrcnn import visualize from mrcnn.visualize import display_images import mrcnn.model as modellib from mrcnn.model import log import nucleus %matplotlib inline # Directory to save logs and trained model LOGS_DIR = os.path.join(ROOT_DIR, "logs")
/usr/local/lib/python3.5/dist-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`. from ._conv import register_converters as _register_converters Using TensorFlow backend.
# Comment out to reload imported modules if they change # %load_ext autoreload # %autoreload 2

Configurations

# Dataset directory DATASET_DIR = os.path.join(ROOT_DIR, "datasets/nucleus") # Inference Configuration config = nucleus.NucleusInferenceConfig() config.display()
Configurations: BACKBONE resnet50 BACKBONE_STRIDES [4, 8, 16, 32, 64] BATCH_SIZE 1 BBOX_STD_DEV [0.1 0.1 0.2 0.2] DETECTION_MAX_INSTANCES 400 DETECTION_MIN_CONFIDENCE 0 DETECTION_NMS_THRESHOLD 0.3 GPU_COUNT 1 GRADIENT_CLIP_NORM 5.0 IMAGES_PER_GPU 1 IMAGE_MAX_DIM 512 IMAGE_META_SIZE 14 IMAGE_MIN_DIM 512 IMAGE_MIN_SCALE 2.0 IMAGE_RESIZE_MODE pad64 IMAGE_SHAPE [512 512 3] LEARNING_MOMENTUM 0.9 LEARNING_RATE 0.001 MASK_POOL_SIZE 14 MASK_SHAPE [28, 28] MAX_GT_INSTANCES 200 MEAN_PIXEL [43.53 39.56 48.22] MINI_MASK_SHAPE (56, 56) NAME nucleus NUM_CLASSES 2 POOL_SIZE 7 POST_NMS_ROIS_INFERENCE 2000 POST_NMS_ROIS_TRAINING 1000 ROI_POSITIVE_RATIO 0.33 RPN_ANCHOR_RATIOS [0.5, 1, 2] RPN_ANCHOR_SCALES (8, 16, 32, 64, 128) RPN_ANCHOR_STRIDE 1 RPN_BBOX_STD_DEV [0.1 0.1 0.2 0.2] RPN_NMS_THRESHOLD 0.7 RPN_TRAIN_ANCHORS_PER_IMAGE 64 STEPS_PER_EPOCH 105 TRAIN_BN False TRAIN_ROIS_PER_IMAGE 128 USE_MINI_MASK True USE_RPN_ROIS True VALIDATION_STEPS 4 WEIGHT_DECAY 0.0001

Notebook Preferences

# Device to load the neural network on. # Useful if you're training a model on the same # machine, in which case use CPU and leave the # GPU for training. DEVICE = "/cpu:0" # /cpu:0 or /gpu:0 # Inspect the model in training or inference modes # values: 'inference' or 'training' # Only inference mode is supported right now TEST_MODE = "inference"
def get_ax(rows=1, cols=1, size=16): """Return a Matplotlib Axes array to be used in all visualizations in the notebook. Provide a central point to control graph sizes. Adjust the size attribute to control how big to render images """ fig, ax = plt.subplots(rows, cols, figsize=(size*cols, size*rows)) fig.tight_layout() return ax

Load Validation Dataset

# Load validation dataset dataset = nucleus.NucleusDataset() dataset.load_nucleus(DATASET_DIR, "val") dataset.prepare() print("Images: {}\nClasses: {}".format(len(dataset.image_ids), dataset.class_names))
Images: 25 Classes: ['BG', 'nucleus']

Load Model

# Create model in inference mode with tf.device(DEVICE): model = modellib.MaskRCNN(mode="inference", model_dir=LOGS_DIR, config=config)
# Path to a specific weights file # weights_path = "/path/to/mask_rcnn_nucleus.h5" # Or, load the last model you trained weights_path = model.find_last() # Load weights print("Loading weights ", weights_path) model.load_weights(weights_path, by_name=True)
Loading weights /deeplearning/mask_rcnn/logs/nucleus20180408T2244/mask_rcnn_nucleus_0032.h5

Run Detection

image_id = random.choice(dataset.image_ids) image, image_meta, gt_class_id, gt_bbox, gt_mask =\ modellib.load_image_gt(dataset, config, image_id, use_mini_mask=False) info = dataset.image_info[image_id] print("image ID: {}.{} ({}) {}".format(info["source"], info["id"], image_id, dataset.image_reference(image_id))) print("Original image shape: ", modellib.parse_image_meta(image_meta[np.newaxis,...])["original_image_shape"][0]) # Run object detection results = model.detect_molded(np.expand_dims(image, 0), np.expand_dims(image_meta, 0), verbose=1) # Display results r = results[0] log("gt_class_id", gt_class_id) log("gt_bbox", gt_bbox) log("gt_mask", gt_mask) # Compute AP over range 0.5 to 0.95 and print it utils.compute_ap_range(gt_bbox, gt_class_id, gt_mask, r['rois'], r['class_ids'], r['scores'], r['masks'], verbose=1) visualize.display_differences( image, gt_bbox, gt_class_id, gt_mask, r['rois'], r['class_ids'], r['scores'], r['masks'], dataset.class_names, ax=get_ax(), show_box=False, show_mask=False, iou_threshold=0.5, score_threshold=0.5)
image ID: nucleus.4193474b2f1c72f735b13633b219d9cabdd43c21d9c2bb4dfc4809f104ba4c06 (20) 4193474b2f1c72f735b13633b219d9cabdd43c21d9c2bb4dfc4809f104ba4c06 Original image shape: [256 320 3] Processing 1 images image shape: (512, 640, 3) min: 1.00000 max: 229.00000 uint8 molded_images shape: (1, 512, 640, 3) min: 1.00000 max: 229.00000 uint8 image_metas shape: (1, 14) min: 0.00000 max: 640.00000 float64 anchors shape: (1, 81840, 4) min: -0.17712 max: 1.05188 float32 gt_class_id shape: (12,) min: 1.00000 max: 1.00000 int32 gt_bbox shape: (12, 4) min: 4.00000 max: 588.00000 int32 gt_mask shape: (512, 640, 12) min: 0.00000 max: 1.00000 bool AP @0.50: 0.903 AP @0.55: 0.903 AP @0.60: 0.903 AP @0.65: 0.787 AP @0.70: 0.354 AP @0.75: 0.354 AP @0.80: 0.167 AP @0.85: 0.042 AP @0.90: 0.042 AP @0.95: 0.000 AP @0.50-0.95: 0.445
Image in a Jupyter notebook
# Display predictions only # visualize.display_instances(image, r['rois'], r['masks'], r['class_ids'], # dataset.class_names, r['scores'], ax=get_ax(1), # show_bbox=False, show_mask=False, # title="Predictions")
# Display Ground Truth only # visualize.display_instances(image, gt_bbox, gt_mask, gt_class_id, # dataset.class_names, ax=get_ax(1), # show_bbox=False, show_mask=False, # title="Ground Truth")

Compute AP on Batch of Images

def compute_batch_ap(dataset, image_ids, verbose=1): APs = [] for image_id in image_ids: # Load image image, image_meta, gt_class_id, gt_bbox, gt_mask =\ modellib.load_image_gt(dataset, config, image_id, use_mini_mask=False) # Run object detection results = model.detect_molded(image[np.newaxis], image_meta[np.newaxis], verbose=0) # Compute AP over range 0.5 to 0.95 r = results[0] ap = utils.compute_ap_range( gt_bbox, gt_class_id, gt_mask, r['rois'], r['class_ids'], r['scores'], r['masks'], verbose=0) APs.append(ap) if verbose: info = dataset.image_info[image_id] meta = modellib.parse_image_meta(image_meta[np.newaxis,...]) print("{:3} {} AP: {:.2f}".format( meta["image_id"][0], meta["original_image_shape"][0], ap)) return APs # Run on validation set limit = 5 APs = compute_batch_ap(dataset, dataset.image_ids[:limit]) print("Mean AP overa {} images: {:.4f}".format(len(APs), np.mean(APs)))
0 [256 320 3] AP: 0.41 1 [256 320 3] AP: 0.44 2 [256 320 3] AP: 0.69 3 [1040 1388 3] AP: 0.74 4 [256 320 3] AP: 0.33 Mean AP overa 5 images: 0.5215

Step by Step Prediction

Stage 1: Region Proposal Network

The Region Proposal Network (RPN) runs a lightweight binary classifier on a lot of boxes (anchors) over the image and returns object/no-object scores. Anchors with high objectness score (positive anchors) are passed to the stage two to be classified.

Often, even positive anchors don't cover objects fully. So the RPN also regresses a refinement (a delta in location and size) to be applied to the anchors to shift it and resize it a bit to the correct boundaries of the object.

1.a RPN Targets

The RPN targets are the training values for the RPN. To generate the targets, we start with a grid of anchors that cover the full image at different scales, and then we compute the IoU of the anchors with ground truth object. Positive anchors are those that have an IoU >= 0.7 with any ground truth object, and negative anchors are those that don't cover any object by more than 0.3 IoU. Anchors in between (i.e. cover an object by IoU >= 0.3 but < 0.7) are considered neutral and excluded from training.

To train the RPN regressor, we also compute the shift and resizing needed to make the anchor cover the ground truth object completely.

# Get anchors and convert to pixel coordinates anchors = model.get_anchors(image.shape) anchors = utils.denorm_boxes(anchors, image.shape[:2]) log("anchors", anchors) # Generate RPN trainig targets # target_rpn_match is 1 for positive anchors, -1 for negative anchors # and 0 for neutral anchors. target_rpn_match, target_rpn_bbox = modellib.build_rpn_targets( image.shape, anchors, gt_class_id, gt_bbox, model.config) log("target_rpn_match", target_rpn_match) log("target_rpn_bbox", target_rpn_bbox) positive_anchor_ix = np.where(target_rpn_match[:] == 1)[0] negative_anchor_ix = np.where(target_rpn_match[:] == -1)[0] neutral_anchor_ix = np.where(target_rpn_match[:] == 0)[0] positive_anchors = anchors[positive_anchor_ix] negative_anchors = anchors[negative_anchor_ix] neutral_anchors = anchors[neutral_anchor_ix] log("positive_anchors", positive_anchors) log("negative_anchors", negative_anchors) log("neutral anchors", neutral_anchors) # Apply refinement deltas to positive anchors refined_anchors = utils.apply_box_deltas( positive_anchors, target_rpn_bbox[:positive_anchors.shape[0]] * model.config.RPN_BBOX_STD_DEV) log("refined_anchors", refined_anchors, )
anchors shape: (81840, 4) min: -91.00000 max: 667.00000 int32 target_rpn_match shape: (81840,) min: -1.00000 max: 1.00000 int32 target_rpn_bbox shape: (64, 4) min: -2.60870 max: 2.50000 float64 positive_anchors shape: (12, 4) min: 0.00000 max: 589.00000 int32 negative_anchors shape: (52, 4) min: -2.00000 max: 642.00000 int32 neutral anchors shape: (81776, 4) min: -91.00000 max: 667.00000 int32 refined_anchors shape: (12, 4) min: 4.00000 max: 588.00000 float32
# Display positive anchors before refinement (dotted) and # after refinement (solid). visualize.draw_boxes( image, ax=get_ax(), boxes=positive_anchors, refined_boxes=refined_anchors)
Image in a Jupyter notebook

1.b RPN Predictions

Here we run the RPN graph and display its predictions.

# Run RPN sub-graph pillar = model.keras_model.get_layer("ROI").output # node to start searching from # TF 1.4 and 1.9 introduce new versions of NMS. Search for all names to support TF 1.3~1.10 nms_node = model.ancestor(pillar, "ROI/rpn_non_max_suppression:0") if nms_node is None: nms_node = model.ancestor(pillar, "ROI/rpn_non_max_suppression/NonMaxSuppressionV2:0") if nms_node is None: #TF 1.9-1.10 nms_node = model.ancestor(pillar, "ROI/rpn_non_max_suppression/NonMaxSuppressionV3:0") rpn = model.run_graph(image[np.newaxis], [ ("rpn_class", model.keras_model.get_layer("rpn_class").output), ("pre_nms_anchors", model.ancestor(pillar, "ROI/pre_nms_anchors:0")), ("refined_anchors", model.ancestor(pillar, "ROI/refined_anchors:0")), ("refined_anchors_clipped", model.ancestor(pillar, "ROI/refined_anchors_clipped:0")), ("post_nms_anchor_ix", nms_node), ("proposals", model.keras_model.get_layer("ROI").output), ], image_metas=image_meta[np.newaxis])
rpn_class shape: (1, 81840, 2) min: 0.00000 max: 1.00000 float32 pre_nms_anchors shape: (1, 6000, 4) min: -0.17712 max: 1.05188 float32 refined_anchors shape: (1, 6000, 4) min: -0.15117 max: 1.23829 float32 refined_anchors_clipped shape: (1, 6000, 4) min: 0.00000 max: 1.00000 float32 post_nms_anchor_ix shape: (2000,) min: 0.00000 max: 2684.00000 int32 proposals shape: (1, 2000, 4) min: 0.00000 max: 1.00000 float32
# Show top anchors by score (before refinement) limit = 100 sorted_anchor_ids = np.argsort(rpn['rpn_class'][:,:,1].flatten())[::-1] visualize.draw_boxes(image, boxes=anchors[sorted_anchor_ids[:limit]], ax=get_ax())
Image in a Jupyter notebook
# Show top anchors with refinement. Then with clipping to image boundaries limit = 50 ax = get_ax(1, 2) pre_nms_anchors = utils.denorm_boxes(rpn["pre_nms_anchors"][0], image.shape[:2]) refined_anchors = utils.denorm_boxes(rpn["refined_anchors"][0], image.shape[:2]) refined_anchors_clipped = utils.denorm_boxes(rpn["refined_anchors_clipped"][0], image.shape[:2]) visualize.draw_boxes(image, boxes=pre_nms_anchors[:limit], refined_boxes=refined_anchors[:limit], ax=ax[0]) visualize.draw_boxes(image, refined_boxes=refined_anchors_clipped[:limit], ax=ax[1])
Image in a Jupyter notebook
# Show refined anchors after non-max suppression limit = 50 ixs = rpn["post_nms_anchor_ix"][:limit] visualize.draw_boxes(image, refined_boxes=refined_anchors_clipped[ixs], ax=get_ax())
Image in a Jupyter notebook
# Show final proposals # These are the same as the previous step (refined anchors # after NMS) but with coordinates normalized to [0, 1] range. limit = 50 # Convert back to image coordinates for display # h, w = config.IMAGE_SHAPE[:2] # proposals = rpn['proposals'][0, :limit] * np.array([h, w, h, w]) visualize.draw_boxes( image, ax=get_ax(), refined_boxes=utils.denorm_boxes(rpn['proposals'][0, :limit], image.shape[:2]))
Image in a Jupyter notebook

Stage 2: Proposal Classification

This stage takes the region proposals from the RPN and classifies them.

2.a Proposal Classification

Run the classifier heads on proposals to generate class propbabilities and bounding box regressions.

# Get input and output to classifier and mask heads. mrcnn = model.run_graph([image], [ ("proposals", model.keras_model.get_layer("ROI").output), ("probs", model.keras_model.get_layer("mrcnn_class").output), ("deltas", model.keras_model.get_layer("mrcnn_bbox").output), ("masks", model.keras_model.get_layer("mrcnn_mask").output), ("detections", model.keras_model.get_layer("mrcnn_detection").output), ])
proposals shape: (1, 2000, 4) min: 0.00000 max: 1.00000 float32 probs shape: (1, 2000, 2) min: 0.00000 max: 1.00000 float32 deltas shape: (1, 2000, 2, 4) min: -3.47788 max: 3.38944 float32 masks shape: (1, 400, 28, 28, 2) min: 0.00004 max: 0.99984 float32 detections shape: (1, 400, 6) min: 0.00000 max: 1.00000 float32
# Get detection class IDs. Trim zero padding. det_class_ids = mrcnn['detections'][0, :, 4].astype(np.int32) det_count = np.where(det_class_ids == 0)[0][0] det_class_ids = det_class_ids[:det_count] detections = mrcnn['detections'][0, :det_count] print("{} detections: {}".format( det_count, np.array(dataset.class_names)[det_class_ids])) captions = ["{} {:.3f}".format(dataset.class_names[int(c)], s) if c > 0 else "" for c, s in zip(detections[:, 4], detections[:, 5])] visualize.draw_boxes( image, refined_boxes=utils.denorm_boxes(detections[:, :4], image.shape[:2]), visibilities=[2] * len(detections), captions=captions, title="Detections", ax=get_ax())
19 detections: ['nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus']
Image in a Jupyter notebook

2.c Step by Step Detection

Here we dive deeper into the process of processing the detections.

# Proposals are in normalized coordinates proposals = mrcnn["proposals"][0] # Class ID, score, and mask per proposal roi_class_ids = np.argmax(mrcnn["probs"][0], axis=1) roi_scores = mrcnn["probs"][0, np.arange(roi_class_ids.shape[0]), roi_class_ids] roi_class_names = np.array(dataset.class_names)[roi_class_ids] roi_positive_ixs = np.where(roi_class_ids > 0)[0] # How many ROIs vs empty rows? print("{} Valid proposals out of {}".format(np.sum(np.any(proposals, axis=1)), proposals.shape[0])) print("{} Positive ROIs".format(len(roi_positive_ixs))) # Class counts print(list(zip(*np.unique(roi_class_names, return_counts=True))))
2000 Valid proposals out of 2000 69 Positive ROIs [('BG', 1931), ('nucleus', 69)]
# Display a random sample of proposals. # Proposals classified as background are dotted, and # the rest show their class and confidence score. limit = 200 ixs = np.random.randint(0, proposals.shape[0], limit) captions = ["{} {:.3f}".format(dataset.class_names[c], s) if c > 0 else "" for c, s in zip(roi_class_ids[ixs], roi_scores[ixs])] visualize.draw_boxes( image, boxes=utils.denorm_boxes(proposals[ixs], image.shape[:2]), visibilities=np.where(roi_class_ids[ixs] > 0, 2, 1), captions=captions, title="ROIs Before Refinement", ax=get_ax())
Image in a Jupyter notebook

Apply Bounding Box Refinement

# Class-specific bounding box shifts. roi_bbox_specific = mrcnn["deltas"][0, np.arange(proposals.shape[0]), roi_class_ids] log("roi_bbox_specific", roi_bbox_specific) # Apply bounding box transformations # Shape: [N, (y1, x1, y2, x2)] refined_proposals = utils.apply_box_deltas( proposals, roi_bbox_specific * config.BBOX_STD_DEV) log("refined_proposals", refined_proposals) # Show positive proposals # ids = np.arange(roi_boxes.shape[0]) # Display all limit = 5 ids = np.random.randint(0, len(roi_positive_ixs), limit) # Display random sample captions = ["{} {:.3f}".format(dataset.class_names[c], s) if c > 0 else "" for c, s in zip(roi_class_ids[roi_positive_ixs][ids], roi_scores[roi_positive_ixs][ids])] visualize.draw_boxes( image, ax=get_ax(), boxes=utils.denorm_boxes(proposals[roi_positive_ixs][ids], image.shape[:2]), refined_boxes=utils.denorm_boxes(refined_proposals[roi_positive_ixs][ids], image.shape[:2]), visibilities=np.where(roi_class_ids[roi_positive_ixs][ids] > 0, 1, 0), captions=captions, title="ROIs After Refinement")
roi_bbox_specific shape: (2000, 4) min: -2.55332 max: 3.38944 float32 refined_proposals shape: (2000, 4) min: -0.00621 max: 1.06598 float32
Image in a Jupyter notebook

Filter Low Confidence Detections

# Remove boxes classified as background keep = np.where(roi_class_ids > 0)[0] print("Keep {} detections:\n{}".format(keep.shape[0], keep))
Keep 69 detections: [ 0 1 2 3 4 5 6 7 8 9 10 12 13 16 17 19 21 24 29 37 39 44 47 50 53 54 58 69 70 77 85 87 117 123 125 126 128 133 148 158 187 198 214 221 243 255 259 269 282 318 354 476 662 705 770 812 885 971 980 1048 1073 1143 1211 1366 1378 1481 1483 1893 1914]
# Remove low confidence detections keep = np.intersect1d(keep, np.where(roi_scores >= config.DETECTION_MIN_CONFIDENCE)[0]) print("Remove boxes below {} confidence. Keep {}:\n{}".format( config.DETECTION_MIN_CONFIDENCE, keep.shape[0], keep))
Remove boxes below 0 confidence. Keep 69: [ 0 1 2 3 4 5 6 7 8 9 10 12 13 16 17 19 21 24 29 37 39 44 47 50 53 54 58 69 70 77 85 87 117 123 125 126 128 133 148 158 187 198 214 221 243 255 259 269 282 318 354 476 662 705 770 812 885 971 980 1048 1073 1143 1211 1366 1378 1481 1483 1893 1914]

Per-Class Non-Max Suppression

# Apply per-class non-max suppression pre_nms_boxes = refined_proposals[keep] pre_nms_scores = roi_scores[keep] pre_nms_class_ids = roi_class_ids[keep] nms_keep = [] for class_id in np.unique(pre_nms_class_ids): # Pick detections of this class ixs = np.where(pre_nms_class_ids == class_id)[0] # Apply NMS class_keep = utils.non_max_suppression(pre_nms_boxes[ixs], pre_nms_scores[ixs], config.DETECTION_NMS_THRESHOLD) # Map indicies class_keep = keep[ixs[class_keep]] nms_keep = np.union1d(nms_keep, class_keep) print("{:22}: {} -> {}".format(dataset.class_names[class_id][:20], keep[ixs], class_keep)) keep = np.intersect1d(keep, nms_keep).astype(np.int32) print("\nKept after per-class NMS: {}\n{}".format(keep.shape[0], keep))
nucleus : [ 0 1 2 3 4 5 6 7 8 9 10 12 13 16 17 19 21 24 29 37 39 44 47 50 53 54 58 69 70 77 85 87 117 123 125 126 128 133 148 158 187 198 214 221 243 255 259 269 282 318 354 476 662 705 770 812 885 971 980 1048 1073 1143 1211 1366 1378 1481 1483 1893 1914] -> [ 13 8 0 4 3 9 318 85 12 2 54 44 126 19 50 47 1914 29 133] Kept after per-class NMS: 19 [ 0 2 3 4 8 9 12 13 19 29 44 47 50 54 85 126 133 318 1914]
# Show final detections ixs = np.arange(len(keep)) # Display all # ixs = np.random.randint(0, len(keep), 10) # Display random sample captions = ["{} {:.3f}".format(dataset.class_names[c], s) if c > 0 else "" for c, s in zip(roi_class_ids[keep][ixs], roi_scores[keep][ixs])] visualize.draw_boxes( image, boxes=utils.denorm_boxes(proposals[keep][ixs], image.shape[:2]), refined_boxes=utils.denorm_boxes(refined_proposals[keep][ixs], image.shape[:2]), visibilities=np.where(roi_class_ids[keep][ixs] > 0, 1, 0), captions=captions, title="Detections after NMS", ax=get_ax())
Image in a Jupyter notebook

Stage 3: Generating Masks

This stage takes the detections (refined bounding boxes and class IDs) from the previous layer and runs the mask head to generate segmentation masks for every instance.

3.a Mask Targets

These are the training targets for the mask branch

limit = 8 display_images(np.transpose(gt_mask[..., :limit], [2, 0, 1]), cmap="Blues")
Image in a Jupyter notebook

3.b Predicted Masks

# Get predictions of mask head mrcnn = model.run_graph([image], [ ("detections", model.keras_model.get_layer("mrcnn_detection").output), ("masks", model.keras_model.get_layer("mrcnn_mask").output), ]) # Get detection class IDs. Trim zero padding. det_class_ids = mrcnn['detections'][0, :, 4].astype(np.int32) det_count = np.where(det_class_ids == 0)[0][0] det_class_ids = det_class_ids[:det_count] print("{} detections: {}".format( det_count, np.array(dataset.class_names)[det_class_ids]))
detections shape: (1, 400, 6) min: 0.00000 max: 1.00000 float32 masks shape: (1, 400, 28, 28, 2) min: 0.00004 max: 0.99984 float32 19 detections: ['nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus' 'nucleus']
# Masks det_boxes = utils.denorm_boxes(mrcnn["detections"][0, :, :4], image.shape[:2]) det_mask_specific = np.array([mrcnn["masks"][0, i, :, :, c] for i, c in enumerate(det_class_ids)]) det_masks = np.array([utils.unmold_mask(m, det_boxes[i], image.shape) for i, m in enumerate(det_mask_specific)]) log("det_mask_specific", det_mask_specific) log("det_masks", det_masks)
det_mask_specific shape: (19, 28, 28) min: 0.00004 max: 0.99984 float32 det_masks shape: (19, 512, 640) min: 0.00000 max: 1.00000 bool
display_images(det_mask_specific[:4] * 255, cmap="Blues", interpolation="none")
Image in a Jupyter notebook
display_images(det_masks[:4] * 255, cmap="Blues", interpolation="none")
Image in a Jupyter notebook

Visualize Activations

In some cases it helps to look at the output from different layers and visualize them to catch issues and odd patterns.

# Get activations of a few sample layers activations = model.run_graph([image], [ ("input_image", tf.identity(model.keras_model.get_layer("input_image").output)), ("res2c_out", model.keras_model.get_layer("res2c_out").output), ("res3c_out", model.keras_model.get_layer("res3c_out").output), ("rpn_bbox", model.keras_model.get_layer("rpn_bbox").output), ("roi", model.keras_model.get_layer("ROI").output), ])
input_image shape: (1, 1024, 1280, 3) min: -38.56000 max: 184.47000 float32 res2c_out shape: (1, 256, 320, 256) min: 0.00000 max: 18.96384 float32 res3c_out shape: (1, 128, 160, 512) min: 0.00000 max: 14.29902 float32 rpn_bbox shape: (1, 327360, 4) min: -14.41855 max: 60.67371 float32 roi shape: (1, 2000, 4) min: 0.00000 max: 1.00000 float32
# Backbone feature map display_images(np.transpose(activations["res2c_out"][0,:,:,:4], [2, 0, 1]), cols=4)
Image in a Jupyter notebook