{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"#### Version history\n* V1 - Inference with basic model trained 1000 iterations\n* V2 - Same training procedure but for more iterations. Validation score .267, LB score .286\n* V3 - Same model weights as previously, but score thresholds set for each class individually. LB score .287\n* V4 - Another small inference time improvement - throw away predictions with area smaller than a threshold (set per cell type). LB score .294","metadata":{}},{"cell_type":"markdown","source":"## Inference and submission\nAfter [part one](https://www.kaggle.com/slawekbiel/positive-score-with-detectron-1-3-input-data/) and [part two](https://www.kaggle.com/slawekbiel/positive-score-with-detectron-2-3-training) we have a trained model. I'm attaching it to this notebook through a dataset. Now all that's left is to run all the test files through it.\n\nThere are two minor details we need to handle:\n- The submission notebooks don't have access to the internet, in order to install detectron2 I needed to download dependecies with `pip download`, put them into a dataset and attach it to the notebook: https://www.kaggle.com/slawekbiel/detectron-05\n- The masks we submit can't overlap, see [the discussion](https://www.kaggle.com/c/sartorius-cell-instance-segmentation/discussion/279790#1550666). So I'm manually clipping the output returned from the model) I'm processing the masks ordereded by score, so in the case of conflict the more confident one remaines whole and the other one gets clipped.","metadata":{}},{"cell_type":"code","source":"!pip install ../input/detectron-05/whls/pycocotools-2.0.2/dist/pycocotools-2.0.2.tar --no-index --find-links ../input/detectron-05/whls \n!pip install ../input/detectron-05/whls/fvcore-0.1.5.post20211019/fvcore-0.1.5.post20211019 --no-index --find-links ../input/detectron-05/whls \n!pip install ../input/detectron-05/whls/antlr4-python3-runtime-4.8/antlr4-python3-runtime-4.8 --no-index --find-links ../input/detectron-05/whls \n!pip install ../input/detectron-05/whls/detectron2-0.5/detectron2 --no-index --find-links ../input/detectron-05/whls ","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-11-03T01:44:33.092239Z","iopub.execute_input":"2021-11-03T01:44:33.092490Z","iopub.status.idle":"2021-11-03T01:47:52.943823Z","shell.execute_reply.started":"2021-11-03T01:44:33.092462Z","shell.execute_reply":"2021-11-03T01:47:52.942811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import detectron2\nimport torch\nfrom detectron2 import model_zoo\nfrom detectron2.engine import DefaultPredictor\nfrom detectron2.config import get_cfg\nfrom PIL import Image\nimport cv2\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom fastcore.all import *","metadata":{"execution":{"iopub.status.busy":"2021-11-03T01:47:52.946158Z","iopub.execute_input":"2021-11-03T01:47:52.950180Z","iopub.status.idle":"2021-11-03T01:47:54.357101Z","shell.execute_reply.started":"2021-11-03T01:47:52.950138Z","shell.execute_reply":"2021-11-03T01:47:54.356236Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataDir=Path('../input/sartorius-cell-instance-segmentation')","metadata":{"execution":{"iopub.status.busy":"2021-11-03T01:47:54.358524Z","iopub.execute_input":"2021-11-03T01:47:54.358957Z","iopub.status.idle":"2021-11-03T01:47:54.364049Z","shell.execute_reply.started":"2021-11-03T01:47:54.358891Z","shell.execute_reply":"2021-11-03T01:47:54.363313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# From https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_decode(mask_rle, shape=(520, 704)):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (height,width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape)  # Needed to align to RLE direction\n\ndef rle_encode(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels = img.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef get_masks(fn, predictor):\n    im = cv2.imread(str(fn))\n    pred = predictor(im)\n    pred_class = torch.mode(pred['instances'].pred_classes)[0]\n    take = pred['instances'].scores >= THRESHOLDS[pred_class]\n    pred_masks = pred['instances'].pred_masks[take]\n    pred_masks = pred_masks.cpu().numpy()\n    res = []\n    used = np.zeros(im.shape[:2], dtype=int) \n    for mask in pred_masks:\n        mask = mask * (1-used)\n        if mask.sum() >= MIN_PIXELS[pred_class]: # skip predictions with small area\n            used += mask\n            res.append(rle_encode(mask))\n    return res","metadata":{"execution":{"iopub.status.busy":"2021-11-03T01:47:54.366475Z","iopub.execute_input":"2021-11-03T01:47:54.367152Z","iopub.status.idle":"2021-11-03T01:47:54.382191Z","shell.execute_reply.started":"2021-11-03T01:47:54.367080Z","shell.execute_reply":"2021-11-03T01:47:54.381241Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ids, masks=[],[]\ntest_names = (dataDir/'test').ls()","metadata":{"execution":{"iopub.status.busy":"2021-11-03T01:47:54.383793Z","iopub.execute_input":"2021-11-03T01:47:54.384065Z","iopub.status.idle":"2021-11-03T01:47:54.399457Z","shell.execute_reply.started":"2021-11-03T01:47:54.384026Z","shell.execute_reply":"2021-11-03T01:47:54.398832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Initiate a Predictor from our trained model","metadata":{}},{"cell_type":"code","source":"# !ls ../input/k/qq1623620766/positive-score-with-detectron-2-3-training/output/model_0004999.pth","metadata":{"execution":{"iopub.status.busy":"2021-11-03T01:47:54.401171Z","iopub.execute_input":"2021-11-03T01:47:54.401688Z","iopub.status.idle":"2021-11-03T01:47:54.405712Z","shell.execute_reply.started":"2021-11-03T01:47:54.401651Z","shell.execute_reply":"2021-11-03T01:47:54.404570Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfg = get_cfg()\ncfg.merge_from_file(model_zoo.get_config_file(\"COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml\"))\ncfg.INPUT.MASK_FORMAT='bitmask'\ncfg.MODEL.ROI_HEADS.NUM_CLASSES = 3 \ncfg.MODEL.WEIGHTS = '../input/positive-score-with-detectron-2-3-trainin-512/output/model_final.pth'\ncfg.TEST.DETECTIONS_PER_IMAGE = 1000\npredictor = DefaultPredictor(cfg)\nTHRESHOLDS = [.15, .35, .55]\nMIN_PIXELS = [75, 150, 75]","metadata":{"execution":{"iopub.status.busy":"2021-11-03T01:47:54.407413Z","iopub.execute_input":"2021-11-03T01:47:54.408021Z","iopub.status.idle":"2021-11-03T01:48:02.446888Z","shell.execute_reply.started":"2021-11-03T01:47:54.407904Z","shell.execute_reply":"2021-11-03T01:48:02.446102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Look at the outputs on a sample test file to sanity check\nI'm encoding here in the competition format and decoding back to bit mask just to make sure everything is fine","metadata":{}},{"cell_type":"code","source":"encoded_masks = get_masks(test_names[0], predictor)\n\n_, axs = plt.subplots(1,2, figsize=(40,15))\naxs[1].imshow(cv2.imread(str(test_names[0])))\nfor enc in encoded_masks:\n    dec = rle_decode(enc)\n    axs[0].imshow(np.ma.masked_where(dec==0, dec))","metadata":{"execution":{"iopub.status.busy":"2021-10-31T13:17:31.138496Z","iopub.execute_input":"2021-10-31T13:17:31.139131Z","iopub.status.idle":"2021-10-31T13:17:53.138215Z","shell.execute_reply.started":"2021-10-31T13:17:31.139091Z","shell.execute_reply":"2021-10-31T13:17:53.137549Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Looks good, so lets generate masks for all the files and create a submission","metadata":{}},{"cell_type":"code","source":"for fn in test_names:\n    encoded_masks = get_masks(fn, predictor)\n    for enc in encoded_masks:\n        ids.append(fn.stem)\n        masks.append(enc)","metadata":{"execution":{"iopub.status.busy":"2021-10-31T13:17:53.139432Z","iopub.execute_input":"2021-10-31T13:17:53.139782Z","iopub.status.idle":"2021-10-31T13:17:53.856328Z","shell.execute_reply.started":"2021-10-31T13:17:53.139715Z","shell.execute_reply":"2021-10-31T13:17:53.8556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd.DataFrame({'id':ids, 'predicted':masks}).to_csv('submission.csv', index=False)\npd.read_csv('submission.csv').head()","metadata":{"execution":{"iopub.status.busy":"2021-10-31T13:17:53.858316Z","iopub.execute_input":"2021-10-31T13:17:53.85857Z","iopub.status.idle":"2021-10-31T13:17:53.891884Z","shell.execute_reply.started":"2021-10-31T13:17:53.858536Z","shell.execute_reply":"2021-10-31T13:17:53.891164Z"},"trusted":true},"execution_count":null,"outputs":[]}]}