{"cells":[{"metadata":{"_uuid":"16533d2064b40e45a13bbf8e8e9a845bdaf071ae"},"cell_type":"markdown","source":"# Mask R-CNN - Train on Shapes Dataset\n\n\nThis notebook shows how to train Mask R-CNN on your own dataset. To keep things simple we use a synthetic dataset of shapes (squares, triangles, and circles) which enables fast training. You'd still need a GPU, though, because the network backbone is a Resnet101, which would be too slow to train on a CPU. On a GPU, you can start to get okay-ish results in a few minutes, and good results in less than an hour.\n\nThe code of the *Shapes* dataset is included below. It generates images on the fly, so it doesn't require downloading any data. And it can generate images of any size, so we pick a small image size to train faster. "},{"metadata":{"trusted":false,"_uuid":"1296f7e1aafd37f210097b35103a20ffecaf45fd"},"cell_type":"code","source":"# # Disable GPU - too small to process\nimport os\nos.environ['CUDA_VISIBLE_DEVICES'] = \"\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"e2891118fff082fd9ea45b0b2f60672290f816b2"},"cell_type":"code","source":"import datetime\nfrom imgaug import augmenters as iaa\nimport math\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport re\nimport random\nimport sys\nimport time\nimport cv2\n\nfrom keras.preprocessing.image import array_to_img, img_to_array, load_img\n\nfrom mrcnn.config import Config\nfrom mrcnn import utils\nimport mrcnn.model as modellib\nfrom mrcnn import visualize\nfrom mrcnn.model import log\n\nimport skimage\nfrom sklearn.model_selection import train_test_split\n\nimport tqdm\nfrom tqdm._tqdm_notebook import tqdm_notebook    # Progress Monitor\n\n# Root directory of the project\nROOT_DIR = os.path.abspath(\".\")\nsys.path.append(ROOT_DIR)  # To find local version of the library\nprint('ROOT',ROOT_DIR)\n\n# Data Dir where the source images live\nDATA_DIR =  os.path.abspath(os.path.join(ROOT_DIR, \"../../../Kaggle-Input/Airbus Ship Detection Challenge\"))\nprint('DATA',DATA_DIR)\n\n# Save submission files here\nRESULTS_DIR = os.path.join(DATA_DIR, \"results/\")\nprint('RESULTS', RESULTS_DIR)\n\n%matplotlib inline \n\n# Directory to save logs and trained model\nMODEL_DIR = os.path.join(ROOT_DIR, \"logs\")\nprint('MODEL',MODEL_DIR)\n\n# Import Mask RCNN\n# Local path to trained weights file\nCOCO_MODEL_PATH = os.path.join(ROOT_DIR, \"mask_rcnn_coco.h5\")\n# Download COCO trained weights from Releases if needed\nif not os.path.exists(COCO_MODEL_PATH):\n    utils.download_trained_weights(COCO_MODEL_PATH)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b84e951dc5e19be8dadb2d3272e468378a4620b6"},"cell_type":"markdown","source":"# Modlog\n+ Baseline 2018.11.09 scored 0.368\n+ 2018.11.?? Expand training as per nucleus, image augmentation, all dataset\n\n## Enhancements?\n+ !! Expand the training process!!\n+ ?? Try to filter on only images with ships>0\n+ ?? Merge masks into a single?"},{"metadata":{"_uuid":"bbd8f97e48ad24f4c7cd69f018770d9496d7ee74"},"cell_type":"markdown","source":"## Configurations"},{"metadata":{"trusted":false,"_uuid":"4e72a5ae77b5d770ab434786a506fe47bddbceaf"},"cell_type":"code","source":"class ShipsConfig(Config):\n    \"\"\"Configuration for training on the toy shapes dataset.\n    Derives from the base Config class and overrides values specific\n    to the toy shapes dataset.\n    \"\"\"\n    # Give the configuration a recognizable name\n    NAME = \"ships\"\n    \n    DETECTION_MIN_CONFIDENCE = 0.95 \n\n    # Train on 1 GPU and 8 images per GPU. We can put multiple images on each\n    # GPU because the images are small. Batch size is 8 (GPUs * images/GPU).\n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 8\n#     IMAGES_PER_GPU = 1\n\n    # Number of classes (including background)\n    NUM_CLASSES = 1 + 1  # background + 1 shape=ship\n\n    # Use small images for faster training. Set the limits of the small side\n    # the large side, and that determines the image shape.\n    IMAGE_MIN_DIM = 768\n    IMAGE_MAX_DIM = 768\n\n    # Use smaller anchors because our image and objects are small\n    RPN_ANCHOR_SCALES = (8, 16, 32, 64, 128)  # anchor side in pixels\n\n    # Reduce training ROIs per image because the images are small and have\n    # few objects. Aim to allow ROI sampling to pick 33% positive ROIs.\n    TRAIN_ROIS_PER_IMAGE = 32\n\n    # Use a small epoch since the data is simple\n    STEPS_PER_EPOCH = 100\n\n    # use small validation steps since the epoch is small\n    VALIDATION_STEPS = 5\n\nconfig = ShipsConfig()\nconfig.display()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"499f322758fded1e567888764946f1f59ebf2be2"},"cell_type":"markdown","source":"## Notebook Preferences"},{"metadata":{"trusted":false,"_uuid":"3151c51504c69ba082a24bd29cb038a32648ea6e"},"cell_type":"code","source":"def get_ax(rows=1, cols=1, size=8):\n    \"\"\"Return a Matplotlib Axes array to be used in\n    all visualizations in the notebook. Provide a\n    central point to control graph sizes.\n    \n    Change the default size attribute to control the size\n    of rendered images\n    \"\"\"\n    _, ax = plt.subplots(rows, cols, figsize=(size*cols, size*rows))\n    return ax","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"208edb88a4251c62bd82486ec4e106ba43c93960"},"cell_type":"markdown","source":"## Model class definition"},{"metadata":{"trusted":false,"_uuid":"a9c185be25051f7e8d4d9b39d6931429ee1af761"},"cell_type":"code","source":"class ShipDataset(utils.Dataset):\n\n    \"\"\"Load a subset of the ship dataset.\n    dataset_dir: Root directory of the dataset.\n    subset: Subset to load: train or val\n    sample:(optional)number to load\n    \"\"\"\n    def load_ships(self, dataset_dir, subset, imageIdList, sample=None):\n        exclude_list = ['6384c3e78.jpg','13703f040.jpg', '14715c06d.jpg',  '33e0ff2d5.jpg',\n                '4d4e09f2a.jpg', '877691df8.jpg', '8b909bb20.jpg', 'a8d99130e.jpg', \n                'ad55c3143.jpg', 'c8260c541.jpg', 'd6c7f17c7.jpg', 'dc3e7c901.jpg',\n                'e44dffe88.jpg', 'ef87bad36.jpg', 'f083256d8.jpg'] #corrupted images\n        \n        # Add classes. We have only one class to add.\n        self.add_class(\"ship\", 1, \"ship\")\n\n        # Train or validation dataset?\n        assert subset in [\"train\", \"test\"]\n        dataset_dir = os.path.join(dataset_dir, subset)\n        \n        # Loop\n        load_count=0\n        if( sample is None ):\n            sample = len(imageIdList)\n        for n, id_ in tqdm_notebook(enumerate(imageIdList[:sample]), total=sample):\n            if( not(id_ in exclude_list)):\n                self.add_image(\"ship\", image_id=id_, path=os.path.join(dataset_dir, id_))\n                load_count = load_count + 1\n            if( load_count > sample):\n                break\n        \n        # Journal \n        print('load_ships: subset',subset,' Sample=[',sample,'] Total=',load_count)\n        print(dataset_dir)\n        print(\"---\")\n    \n    \"\"\"Load an image from the ship dataset.\n    image_id: filename identifying the image\n    \"\"\"\n    def load_image(self, image_id):\n        \"\"\"Load the specified image and return a [H,W,3] Numpy array.\n        \"\"\"\n        # Load image\n        image = img_to_array(skimage.io.imread(self.image_info[image_id]['path']))\n\n        # Set the height&width attributes for mask\n        image_info = self.image_info[image_id]\n        image_info['height'] = image.shape[0]\n        image_info['width'] = image.shape[1]\n#         print('shape', image.shape, 'height', image_info['height'], 'width', image_info['width'])\n        \n        # If grayscale. Convert to RGB for consistency.\n        if image.ndim != 3:\n            image = skimage.color.gray2rgb(image)\n        # If has an alpha channel, remove it for consistency\n        if image.shape[-1] == 4:\n            image = image[..., :3]\n        return image\n    \n    '''\n    rle_decode: run-length as string formated (start length)\n    mask_rle: the encoded pixel string\n    shape: (height,width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n    '''\n    def rle_decode(self, mask_rle, shape):\n        # Set class defaults\n        bg_class_id = self.class_names.index(\"BG\")\n        ship_class_id = self.class_names.index(\"ship\")\n\n        img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n\n        # Not all ships have masks\n        #print('\\n**** mask',mask_rle)\n        if( pd.isnull(mask_rle) or mask_rle == 0):\n            return img.reshape(shape), bg_class_id\n        if( pd.isnull(mask_rle) or len(mask_rle)==0):\n            return img.reshape(shape), bg_class_id\n\n        # Split the RLE encoding into pairs\n        s = mask_rle.split()\n        starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n\n        # Different length\n        delta=len(starts)-len(lengths)\n        for x in range(0, delta): \n            lengths = np.append(lengths, [1])\n            print('delta', delta, 'start', starts.shape, 'lengths', lengths.shape)\n\n        # Bump & loop\n        starts -= 1\n        ends = starts + lengths\n        for lo, hi in zip(starts, ends):\n            img[lo:hi] = 1\n\n        \"\"\" CRITICAL Transpose the image mask!! \"\"\"\n        img = img.reshape(shape).T\n#         img = Image.fromarray(img).convert('RGB')\n#         print('img sum',np.sum([img]))\n           \n        return img, ship_class_id\n\n    \"\"\"Load a mask from the ship dataset.\n            image_id: filename identifying the image\n    RETURNS    masks: A bool array of shape [height, width, instance count] with one mask per instance.\n           class_ids: a 1D array of class IDs of the instance masks.    \n    \"\"\"\n    def load_mask(self, image_id):\n        # If not a balloon dataset image, delegate to parent class.\n        image_info = self.image_info[image_id]\n        if image_info[\"source\"] != \"ship\":\n            return super(self.__class__, self).load_mask(image_id)\n\n        # There may be many masks, or one, or NONE\n        mask_rle = masks_df['EP'][masks_df[masks_df['ImageId'] == image_info['id']].index]\n        mask_count = len(mask_rle)\n        if( mask_count > 1 ):\n            masks_rle = np.zeros([image_info['height'], image_info['width'], mask_count], dtype=np.uint8)\n        else:\n            masks_rle = np.zeros([image_info['height'], image_info['width'], 1], dtype=np.uint8)\n        \n        class_ids = np.arange(1, dtype=int)\n\n        for i, mask_str in enumerate(mask_rle):\n            # First get the rle encoding string\n            mask_rle, class_id = self.rle_decode( mask_str, (768,768) )\n        \n            # Now resize the 768 image\n            info = self.image_info[image_id]\n            mask_rle = np.resize(mask_rle, (image_info['height'], image_info['width'], 1))\n#             print('mask shape',mask_rle.shape,'count',mask_count,'for class', class_id, self.class_names[class_id])\n        \n            # Journal the mask into the return array\n            for h in range(0, mask_rle.shape[0]):\n                for w in range(0, mask_rle.shape[1]):\n                    if( mask_rle[h][w] == 1 ):\n#                         masks_rle[h][w][i:i+1] = 1\n                        masks_rle[h][w][i] = 1\n        \n            # And resize the class array\n            if( mask_count > 0 ):\n                class_ids = np.arange(mask_count, dtype=int)\n            # stuff the arrays with the returned classid\n            class_ids = np.full_like(class_ids, class_id)\n\n        # Return mask, and a ship class ID\n        return masks_rle.astype(np.bool), class_ids.astype(np.int32)\n\n    def image_reference(self, image_id):\n        \"\"\"Return the path of the image.\"\"\"\n        info = self.image_info[image_id]\n        if info[\"source\"] == \"balloon\":\n            return info[\"path\"]\n        else:\n            super(self.__class__, self).image_reference(image_id)\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d677505e3d825b5cd2539d362006e2e75cbb8a8c"},"cell_type":"markdown","source":"## Dataset\n\nLoad the training ship segmentation file to get the imageIDs and the mask encoding"},{"metadata":{"trusted":false,"_uuid":"b34a56dce96254358dd73f41afd3176adc90ebda"},"cell_type":"code","source":"masks_df = pd.read_csv( os.path.join(ROOT_DIR, 'train_ship_segmentations.csv') )\nmasks_df['ships'] = masks_df['EncodedPixels'].map(lambda c_row: 1 if isinstance(c_row, str) else 0)\nmasks_df['has_ship'] = masks_df['ships']\n# masks_df['EP'] = masks_df['EncodedPixels'] + ' '\nmasks_df['EP'] = masks_df['EncodedPixels'].map(lambda c_row: ' '+c_row if isinstance(c_row, str) else '')\n\nprint(masks_df.shape[0], 'masks_df found')\nprint(masks_df['ImageId'].value_counts().shape[0], 'unique images')\nmasks_df.head(20)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"c51469ca909c481068276758468230948fa54805"},"cell_type":"code","source":"unique_img_ids = masks_df.groupby(by='ImageId', group_keys=True).agg( {'ships':'sum', 'has_ship':'max'} ).reset_index()\nprint(unique_img_ids.shape[0], 'unique_img_ids found')\nunique_img_ids.head(20)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"17833869ce1fcba802ac6417682193e4abc1ec2b"},"cell_type":"code","source":"unique_img_ids[['has_ship','ships']].hist()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e94180b368031691bd174f6db42ad9c71418d4a6"},"cell_type":"markdown","source":"### Exclude corrupted"},{"metadata":{"trusted":false,"_uuid":"00a4b9ec92aaed06d69285f5c88ca236c0a07327"},"cell_type":"code","source":"exclude_list = ['6384c3e78.jpg','13703f040.jpg', '14715c06d.jpg',  '33e0ff2d5.jpg',\n                '4d4e09f2a.jpg', '877691df8.jpg', '8b909bb20.jpg', 'a8d99130e.jpg', \n                'ad55c3143.jpg', 'c8260c541.jpg', 'd6c7f17c7.jpg', 'dc3e7c901.jpg',\n                'e44dffe88.jpg', 'ef87bad36.jpg', 'f083256d8.jpg'] #corrupted images\n\nfor e1 in exclude_list:\n    unique_img_ids.drop( unique_img_ids[unique_img_ids['ImageId'] == e1].index, inplace=True)\n\nprint(unique_img_ids.shape[0], 'training and validation images')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7e1b200f9d54cffa5db4e3ab9818fb85453b8a10"},"cell_type":"markdown","source":"One of the challenges of this competition is strong data unbalance. Even if only images with ships are considered, the ratio of mask pixels to the total number of pixels is ~1:1000. If images with no ships are included, this ratio goes to ~1:10000, which is quite tough to handle. Therefore, I drop all images without ships, that makes the training set more balanced and also reduces the time per each epoch almost by 4 times."},{"metadata":{"_uuid":"885024da72ef06b1515572d833f63d84db353578"},"cell_type":"markdown","source":"### First run: train on all images"},{"metadata":{"trusted":false,"_uuid":"bc836d1c99a2c1a091c32f925081635a0a76fdf9"},"cell_type":"code","source":"# unique_img_ids.drop( unique_img_ids[unique_img_ids['ships'] == 0].index, inplace=True)\n\n# print(unique_img_ids.shape[0], 'training and validation images')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"34af06ea3c352faba191d4930c7fc7e098f75cf6"},"cell_type":"markdown","source":"### Split into training and validation groups\nWe stratify by the number of boats appearing so we have nice balances in each set"},{"metadata":{"trusted":false,"_uuid":"185b227478484bd3ee9bfe43183def3e24ed426d"},"cell_type":"code","source":"train_ids, valid_ids = train_test_split(unique_img_ids, test_size = 0.3#)\n                                        ,stratify = unique_img_ids['ships'])\ntrain_df = pd.merge(unique_img_ids, train_ids)\nvalid_df = pd.merge(unique_img_ids, valid_ids)\nprint(train_df.shape[0], 'training masks')\nprint(valid_df.shape[0], 'validation masks')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d98c6aeda271fd8ff62da5aa3e8bdaa6ed010dc1"},"cell_type":"markdown","source":"## Process data"},{"metadata":{"trusted":false,"_uuid":"fd295b299d55f5179aaa36e21bc73ded178a6767"},"cell_type":"code","source":"# Training dataset\ndataset_train = ShipDataset()\ndataset_train.load_ships(dataset_dir=DATA_DIR, subset=\"train\", imageIdList=train_df['ImageId'])\ndataset_train.prepare()\n\n# Validation dataset\ndataset_val = ShipDataset()\ndataset_val.load_ships(dataset_dir=DATA_DIR, subset=\"train\", imageIdList=valid_df['ImageId'])\ndataset_val.prepare()","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"1c2d2a3db5d443dc2e09efccfaf218a485856bd0"},"cell_type":"code","source":"# Load and display random samples\nimage_ids = np.random.choice(dataset_train.image_ids, 4)\nprint('image_ids',image_ids)\n        \nfor image_id in image_ids:\n#     print(image_id)\n    image = dataset_train.load_image(image_id)\n    mask, class_ids = dataset_train.load_mask(image_id)\n    visualize.display_top_masks(image, mask, class_ids, dataset_train.class_names, 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"4db789fb555db77db17acab69beca7da13baf08c"},"cell_type":"code","source":"# Load and display random samples\nimage_ids = np.random.choice(dataset_val.image_ids, 4)\nprint('image_ids',image_ids)\n        \nfor image_id in image_ids:\n#     print(image_id)\n    image = dataset_val.load_image(image_id)\n    mask, class_ids = dataset_val.load_mask(image_id)\n    visualize.display_top_masks(image, mask, class_ids, dataset_train.class_names, 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"d17358fa8b2114e402f333edce19b4374be4915e"},"cell_type":"code","source":"# Load random image and mask.\nimage_id = np.random.choice(dataset_val.image_ids, 1)[0]\nimage = dataset_val.load_image(image_id)\nmask, class_ids = dataset_val.load_mask(image_id)\n# Compute Bounding box\nbbox = utils.extract_bboxes(mask)\n\n# Display image and additional stats\nprint(\"image_id \", image_id, dataset_train.image_reference(image_id))\nlog(\"image\", image)\nlog(\"mask\", mask)\nlog(\"class_ids\", class_ids)\nlog(\"bbox\", bbox)\n# Display image and instances\nvisualize.display_instances(image, bbox, mask, class_ids, dataset_train.class_names)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9d1fb68597af68695a9538e9da30c92d9a7a5423"},"cell_type":"markdown","source":"## Create Model"},{"metadata":{"trusted":false,"_uuid":"773a13065c04c14ab3c4051dd292dc25d675f07c"},"cell_type":"code","source":"# Create model in training mode\nmodel = modellib.MaskRCNN(mode=\"training\", config=config,\n                          model_dir=MODEL_DIR)","execution_count":null,"outputs":[]},{"metadata":{"scrolled":false,"trusted":false,"_uuid":"9c135313a285d262d216a4bae0f419cf5eada683"},"cell_type":"code","source":"# Which weights to start with?\ninit_with = \"coco\"  # imagenet, coco, or last\n# init_with = \"imagenet\"  # imagenet, coco, or last\n\nif init_with == \"imagenet\":\n    model.load_weights(model.get_imagenet_weights(), by_name=True)\nelif init_with == \"coco\":\n    # Load weights trained on MS COCO, but skip layers that\n    # are different due to the different number of classes\n    # See README for instructions to download the COCO weights\n    model.load_weights(COCO_MODEL_PATH, by_name=True,\n                       exclude=[\"mrcnn_class_logits\", \"mrcnn_bbox_fc\", \n                                \"mrcnn_bbox\", \"mrcnn_mask\"])\nelif init_with == \"last\":\n    # Load the last model you trained and continue training\n    model.load_weights(model.find_last(), by_name=True)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"33c3db97cf2cb3b09ebeeafe2b666e298e77c8fd"},"cell_type":"markdown","source":"## Training\n\nTrain in two stages:\n1. Only the heads. Here we're freezing all the backbone layers and training only the randomly initialized layers (i.e. the ones that we didn't use pre-trained weights from MS COCO). To train only the head layers, pass `layers='heads'` to the `train()` function.\n\n2. Fine-tune all layers. For this simple example it's not necessary, but we're including it to show the process. Simply pass `layers=\"all` to train all layers."},{"metadata":{"trusted":false,"_uuid":"31cd62f64d0d81257d1f8f7f28cd7e98d7ba7727"},"cell_type":"code","source":"# Image augmentation\n# http://imgaug.readthedocs.io/en/latest/source/augmenters.html\naugmentation = iaa.SomeOf((0, 2), [\n    iaa.Fliplr(0.5),\n    iaa.Flipud(0.5),\n    iaa.OneOf([iaa.Affine(rotate=90),\n               iaa.Affine(rotate=180),\n               iaa.Affine(rotate=270)]),\n    iaa.Multiply((0.8, 1.5)),\n    iaa.GaussianBlur(sigma=(0.0, 5.0))\n])","execution_count":null,"outputs":[]},{"metadata":{"scrolled":false,"trusted":false,"_uuid":"4adf08f20ef8286376e7d3dd1d33b7749b817812"},"cell_type":"code","source":"# If starting from imagenet, train heads only for a bit\n# since they have random weights\nprint(\"Train network heads\")\nmodel.train(dataset_train, dataset_val,\n            learning_rate=config.LEARNING_RATE,\n            epochs=20,\n            augmentation=augmentation,\n            layers='heads')\n\n# model.train(dataset_train, dataset_val, \n#             learning_rate=config.LEARNING_RATE, \n#             epochs=1, \n#             layers='heads')","execution_count":null,"outputs":[]},{"metadata":{"scrolled":true,"trusted":false,"_uuid":"48ab9743ba777c2d15c66eb0fb7baf4e53c8c645"},"cell_type":"code","source":"# Fine tune all layers\n# Passing layers=\"all\" trains all layers. You can also \n# pass a regular expression to select which layers to\n# train by name pattern.\nprint(\"Train all layers\")\nmodel.train(dataset_train, dataset_val,\n            learning_rate=config.LEARNING_RATE,\n            epochs=40,\n            augmentation=augmentation,\n            layers='all')\n\n# model.train(dataset_train, dataset_val, \n#             learning_rate=config.LEARNING_RATE / 10,\n#             epochs=2, \n#             layers=\"all\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"30d189440b2d4f742d2145ba8b639498e742d3f6"},"cell_type":"code","source":"# Save weights\n# Typically not needed because callbacks save after every epoch\n# Uncomment to save manually\nmodel_path = os.path.join(MODEL_DIR, \"mask_rcnn_shapes.h5\")\nmodel.keras_model.save_weights(model_path)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"60b145cb39ee709eb38272eccfb975bd3a0d6cda"},"cell_type":"markdown","source":"## Detection"},{"metadata":{"trusted":false,"_uuid":"dba384055c55cf8eb6b28bc3d7676f7018bfd0ed"},"cell_type":"code","source":"class InferenceConfig(ShipsConfig):\n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 1\n\ninference_config = InferenceConfig()\n\n# Recreate the model in inference mode\nmodel = modellib.MaskRCNN(mode=\"inference\", \n                          config=inference_config,\n                          model_dir=MODEL_DIR)\n\n# Get path to saved weights\n# Either set a specific path or find last trained weights\n# model_path = os.path.join(ROOT_DIR, \".h5 file name here\")\nmodel_path = model.find_last()\n\n# Load trained weights\nprint(\"Loading weights from \", model_path)\nmodel.load_weights(model_path, by_name=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"f4ee50e98e25161616f1f2e54e92c6826f5486e1"},"cell_type":"code","source":"# Test on a random image\nimage_id = random.choice(dataset_val.image_ids)\noriginal_image, image_meta, gt_class_id, gt_bbox, gt_mask =\\\n    modellib.load_image_gt(dataset_val, inference_config, \n                           image_id, use_mini_mask=False)\n\nlog(\"original_image\", original_image)\nlog(\"image_meta\", image_meta)\nlog(\"gt_class_id\", gt_class_id)\nlog(\"gt_bbox\", gt_bbox)\nlog(\"gt_mask\", gt_mask)\n\nvisualize.display_instances(original_image, gt_bbox, gt_mask, gt_class_id, \n                            dataset_train.class_names, figsize=(8, 8))","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"7127e4c0d181a22080d76449c554c9b3329e52b3"},"cell_type":"code","source":"results = model.detect([original_image], verbose=1)\n\nr = results[0]\nvisualize.display_instances(original_image, r['rois'], r['masks'], r['class_ids'], \n                            dataset_val.class_names, r['scores'], ax=get_ax())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"76896901ffdbe92205737c047454c256c61a2894"},"cell_type":"markdown","source":"## Evaluation"},{"metadata":{"trusted":false,"_uuid":"9c237092718112d7c5d44aeda349280c266ad21b"},"cell_type":"code","source":"# Compute VOC-Style mAP @ IoU=0.5\n# Running on 10 images. Increase for better accuracy.\nimage_ids = np.random.choice(dataset_val.image_ids, 10)\nAPs = []\nfor image_id in image_ids:\n    # Load image and ground truth data\n    image, image_meta, gt_class_id, gt_bbox, gt_mask =\\\n        modellib.load_image_gt(dataset_val, inference_config,\n                               image_id, use_mini_mask=False)\n    molded_images = np.expand_dims(modellib.mold_image(image, inference_config), 0)\n    # Run object detection\n    results = model.detect([image], verbose=0)\n    r = results[0]\n    # Compute AP\n    AP, precisions, recalls, overlaps =\\\n        utils.compute_ap(gt_bbox, gt_class_id, gt_mask,\n                         r[\"rois\"], r[\"class_ids\"], r[\"scores\"], r['masks'])\n    APs.append(AP)\n    \nprint(\"mAP: \", np.mean(APs))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f83cd6d6d83a687d5419bfb05384e12e17562880"},"cell_type":"markdown","source":"## Submission"},{"metadata":{"_uuid":"8846d5207677a5d4eb5bc6960409869be2d8d99c"},"cell_type":"markdown","source":"### Fetch and process the test dataset"},{"metadata":{"trusted":false,"_uuid":"c5838f1fa2b77befe800c19207d0c8822172a7ed"},"cell_type":"code","source":"# Test dataset\nsubmission_df = pd.read_csv('sample_submission.csv')\nsubmission_df.head(20)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"a3cb9c0ef2f65438e5d46ad0baf2f80e18765e1b"},"cell_type":"code","source":"dataset_test = ShipDataset()\ndataset_test.load_ships(dataset_dir=DATA_DIR, subset=\"test\", imageIdList=submission_df['ImageId'])\ndataset_test.prepare()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"771b123fa855d79dc6baec7060c9654cf2b3d6a3"},"cell_type":"markdown","source":"### Encoding & execution"},{"metadata":{"scrolled":true,"trusted":false,"_uuid":"6aa6c86951aad22fb77c73872f4009b95ee1a87b"},"cell_type":"code","source":"def rle_encode(mask):\n    \"\"\"Encodes a mask in Run Length Encoding (RLE).\n    Returns a string of space-separated values.\n    \"\"\"\n    assert mask.ndim == 2, \"Mask must be of shape [Height, Width]\"\n    # Flatten it column wise\n    m = mask.T.flatten()\n    # Compute gradient. Equals 1 or -1 at transition points\n    g = np.diff(np.concatenate([[0], m, [0]]), n=1)\n    # 1-based indicies of transition points (where gradient != 0)\n    rle = np.where(g != 0)[0].reshape([-1, 2]) + 1\n    # Convert second index in each pair to lenth\n    rle[:, 1] = rle[:, 1] - rle[:, 0]\n    return \" \".join(map(str, rle.flatten()))\n\ndef mask_to_rle(image_id, mask, scores):\n    \"Encodes instance masks to submission format.\"\n    assert mask.ndim == 3, \"Mask must be [H, W, count]\"\n    # If mask is empty, return line with image ID only\n    if mask.shape[-1] == 0:\n        return \"{},\".format(image_id)\n    # Remove mask overlaps\n    # Multiply each instance mask by its score order\n    # then take the maximum across the last dimension\n    order = np.argsort(scores)[::-1] + 1  # 1-based descending\n    mask = np.max(mask * np.reshape(order, [1, 1, -1]), -1)\n    # Loop over instance masks\n    lines = []\n    for o in order:\n        m = np.where(mask == o, 1, 0)\n        # Skip if empty\n        if m.sum() == 0.0:\n            continue\n        rle = rle_encode(m)\n        lines.append(\"{}, {}\".format(image_id, rle))\n    return \"\\n\".join(lines)\n\n# Create directory\nif not os.path.exists(RESULTS_DIR):\n    os.makedirs(RESULTS_DIR)\nsubmit_dir = \"submit_{:%Y%m%dT%H%M%S}\".format(datetime.datetime.now())\nsubmit_dir = os.path.join(RESULTS_DIR, submit_dir)\nos.makedirs(submit_dir)\nprint('Submission results in',submit_dir)\n\n# Load over images\nsubmission = []\nfor image_id in tqdm_notebook(dataset_test.image_ids):\n    # Load image and run detection\n    image = dataset_test.load_image(image_id)\n    \n    # Detect objects\n    r = model.detect([image], verbose=0)[0]\n    \n    # Encode image to RLE. Returns a string of multiple lines\n    source_id = dataset_test.image_info[image_id][\"id\"]\n#     rle = mask_to_rle(source_id, r[\"masks\"], r[\"scores\"])\n#     submission.append(rle)\n    num_instances = len(r['rois'])\n\n    for i in range(num_instances):\n        mi = r[\"masks\"][...,i]\n        mi = np.reshape(mi, (mi.shape[0],mi.shape[1],1))\n        if r['scores'][i] > config.DETECTION_MIN_CONFIDENCE:\n            rle = mask_to_rle(source_id, mi, r[\"scores\"][i])\n            submission.append(rle)\n#     # Save image with masks\n#     visualize.display_instances(\n#         image, r['rois'], r['masks'], r['class_ids'],\n#         dataset_test.class_names, r['scores'],\n#         show_bbox=False, show_mask=False,\n#         title=\"Predictions \"+source_id)\n#     plt.savefig(\"{}/{}.png\".format(submit_dir, dataset_test.image_info[image_id][\"id\"]))\n\n# Save to csv file\nsubmission = \"ImageId,EncodedPixels\\n\" + \"\\n\".join(submission)\nfile_path = os.path.join(submit_dir, \"submit.csv\")\nwith open(file_path, \"w\") as f:\n    f.write(submission)\nprint(\"Saved to \", submit_dir)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"880cb76e61cdf6b141f9722df1e6c6d8ee47267e"},"cell_type":"code","source":"print(submission[:835])","execution_count":null,"outputs":[]},{"metadata":{"trusted":false,"_uuid":"41ac0ef1ffbdf26f1aefd512ce59960a68989767"},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":1}