{"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":"# **KDDM 2 SARTORIUS CELL INSTANCE SEGMENTATION CELLPOSE TRAINING NOTEBOOK**","metadata":{}},{"cell_type":"markdown","source":"# INSTALL All DEPENDENCIES\n* **Install Cellpose and required dependencies**","metadata":{}},{"cell_type":"code","source":"!pip install ../input/kddm2/cellpose/fastremap-1.12.2-cp37-cp37m-manylinux2010_x86_64.whl --no-deps\n!pip install ../input/kddm2/cellpose/natsort-8.0.1-py3-none-any.whl --no-deps\n!pip install ../input/kddm2/cellpose/pytorch_ranger-0.1.1-py3-none-any.whl --no-deps\n!pip install ../input/kddm2/cellpose/torch_optimizer-0.3.0-py3-none-any.whl --no-deps\n!pip install ../input/kddm2/cellpose/numpy-1.20.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl --no-deps\n!pip install ../input/kddm2/cellpose/cellpose-0.7.2-py3-none-any.whl --no-deps\n!pip install ../input/kddm2/cellpose/edt-2.1.1-cp37-cp37m-manylinux2014_x86_64.whl --no-deps\n\n!nvidia-smi","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-01-26T13:18:26.216540Z","iopub.execute_input":"2022-01-26T13:18:26.217119Z","iopub.status.idle":"2022-01-26T13:21:03.920548Z","shell.execute_reply.started":"2022-01-26T13:18:26.217081Z","shell.execute_reply":"2022-01-26T13:21:03.919773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **PREPARE TRAININGS DATA**\n* **Generate masks and images**","metadata":{}},{"cell_type":"code","source":"import os\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport cv2\nimport pandas as pd\nfrom tqdm import tqdm\n\nimport shutil\nimport random\nimport torch\n\n\n#define path to input image folder, annotation csv and output folders for images and masks\nSARTORIUS_CLASSES = ['astro', 'cort', 'shsy5y']\nannotation_file = \"/kaggle/input/sartorius-cell-instance-segmentation/train.csv\"\nimg_dir = \"/kaggle/input/sartorius-cell-instance-segmentation/train\"\ntrain_dir = \"/kaggle/working/train\"\nval_dir = \"/kaggle/working/val\"\n\n!rm -rf $train_dir\n!rm -rf $val_dir\n\nos.makedirs(train_dir, exist_ok=True)\nos.makedirs(val_dir, exist_ok=True)\n\n#random train/val split\npercentage_in_train = 0.95\nframes = os.listdir(img_dir)\nnum_train = int(len(frames) * percentage_in_train)\nrandom.seed(0)\ntrain_frames = set( random.sample(frames, num_train) )\n\n\nwith open(annotation_file, \"r\") as f:\n    anns = f.read().split(\"\\n\")\nanns = anns[1:-1]\nprint(\"annotations\", len(anns))\n\n#create dict with annotation masks per img and convert rle to \"start to end\" pixel masks\nanns_per_img_id = {}\nfor a in anns:\n    values = a.split(\",\")\n    id = values[0]\n    cell_type = values[4]\n    rle_mask = values[1].split(\" \")\n\n    starts = list(map(lambda x: int(x) - 1, rle_mask[0::2]))\n    lengths = list(map(int, rle_mask[1::2]))\n    ends = [x + y for x, y in zip(starts, lengths)]\n\n    if id not in anns_per_img_id.keys():\n        anns_per_img_id[id] = {}\n        anns_per_img_id[id][\"start_end\"] = []\n\n    anns_per_img_id[id][\"start_end\"].append( [starts, ends] )\n    anns_per_img_id[id][\"cell_type\"] = cell_type\nnum_imgs_keys = len(anns_per_img_id.keys())\nprint(\"anns_per_img_id\", num_imgs_keys)\n\n\n#generate image masks for one cell type\nselected_cell_type = 'cort'\nimg_mask = np.empty((704*520), dtype=np.uint16)\nimg_mask_2d = np.empty((520, 704), dtype=np.uint16)\nfor i in tqdm( range(0, num_imgs_keys) ):\n    k = list(anns_per_img_id.keys())[i]\n    anns = anns_per_img_id[k]\n    masks = anns[\"start_end\"]\n    id = anns[\"cell_type\"]\n\n    if id != selected_cell_type:\n        continue\n    \n    img_mask.fill(0)\n    pixel_value_per_instance = 0\n    for m in masks:\n        pixel_value_per_instance = pixel_value_per_instance + 1\n        for start, end in zip(m[0], m[1]):\n            img_mask[start:end] = pixel_value_per_instance\n    img_mask_2d = img_mask.reshape((520, 704))\n    #print(np.max(img_mask_2d))\n    \n    orig_fname = \"{:s}.png\".format(k)\n    orig_file = cv2.imread(os.path.join(img_dir, orig_fname))\n    out_dir = train_dir if orig_fname in train_frames else val_dir\n    #shutil.copy( os.path.join(img_dir, orig_fname), os.path.join(out_dir, fname_img) )\n    cv2.imwrite( os.path.join(out_dir, \"{:s}.tif\".format(k)), orig_file[:, :, 0] )\n    cv2.imwrite( os.path.join(out_dir, \"{:s}_masks.tif\".format(k)), img_mask_2d )\n    flip = random.randint(0, 2)\n    if flip == 2:\n        continue\n    cv2.imwrite( os.path.join(out_dir, \"{:s}flip.tif\".format(k)), cv2.flip(orig_file[:, :, 0], flip) )\n    cv2.imwrite( os.path.join(out_dir, \"{:s}flip_masks.tif\".format(k)), cv2.flip(img_mask_2d, flip) )\n    \nprint( \"train imgs:\", len(os.listdir(train_dir)) // 2 )\nprint( \"val imgs:\", len(os.listdir(val_dir)) // 2 )\n!mkdir /kaggle/working/train/models/","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:22:11.763486Z","iopub.execute_input":"2022-01-26T13:22:11.763793Z","iopub.status.idle":"2022-01-26T13:22:29.112833Z","shell.execute_reply.started":"2022-01-26T13:22:11.763757Z","shell.execute_reply":"2022-01-26T13:22:29.111883Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **TRAINING**\n","metadata":{}},{"cell_type":"markdown","source":"* **Load pretrained models**","metadata":{}},{"cell_type":"code","source":"import os\nimport shutil\n\ncellpose_cache_path = os.path.join(\"/\", \"root\", \".cellpose\")\nmodel_folder = os.path.join(cellpose_cache_path, \"models\")\n!mkdir -p $model_folder\nshutil.copy(\"/kaggle/input/kddm2/cellpose/cyto2torch_1\", model_folder)\n\n!ls /root/.cellpose/models/\n!ls /kaggle/input/kddm2/cellpose\n!ls /kaggle/working","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:33:11.749801Z","iopub.execute_input":"2022-01-26T13:33:11.750442Z","iopub.status.idle":"2022-01-26T13:33:14.431826Z","shell.execute_reply.started":"2022-01-26T13:33:11.750396Z","shell.execute_reply":"2022-01-26T13:33:14.431006Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* **Enable Cellpose logging for notebooks**","metadata":{}},{"cell_type":"code","source":"with open(\"/opt/conda/lib/python3.7/site-packages/cellpose/core.py\", \"r\") as f:\n    core_file = f.read().split(\"\\n\")\ncore_file[35] = \"core_logger.addHandler(logging.StreamHandler(stream=sys.stdout))\"\ncore_file[965] = \"                if iepoch==self.n_epochs-1 or iepoch%save_every==0:\"\ncore_file[972] = '                        file_name = \"{}_{}_{}_{}\".format(self.net_type, file_label, d.strftime(\"%Y_%m_%d_%H_%M_%S.%f\"), \"epoch_\" + str(iepoch))'\nwith open(\"/opt/conda/lib/python3.7/site-packages/cellpose/core.py\", \"w\") as f:\n    f.write( (\"\\n\").join(core_file) )\n    \nwith open(\"/opt/conda/lib/python3.7/site-packages/cellpose/models.py\", \"r\") as f:\n    models_file = f.read().split(\"\\n\")\nmodels_file[11] = \"models_logger.addHandler(logging.StreamHandler(stream=sys.stdout))\"\nwith open(\"/opt/conda/lib/python3.7/site-packages/cellpose/models.py\", \"w\") as f:\n    f.write( (\"\\n\").join(models_file) )\n\nwith open(\"/opt/conda/lib/python3.7/site-packages/cellpose/__main__.py\", \"r\") as f:\n    main_file = f.read().split(\"\\n\")\nmain_file[24] = \"logger.addHandler(logging.StreamHandler(stream=sys.stdout))\"\nwith open(\"/opt/conda/lib/python3.7/site-packages/cellpose/__main__.py\", \"w\") as f:\n    f.write( (\"\\n\").join(main_file) )","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:33:21.136078Z","iopub.execute_input":"2022-01-26T13:33:21.136356Z","iopub.status.idle":"2022-01-26T13:33:21.149831Z","shell.execute_reply.started":"2022-01-26T13:33:21.136322Z","shell.execute_reply":"2022-01-26T13:33:21.148275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* **Execute training**","metadata":{}},{"cell_type":"code","source":"ptm = \"cyto2_torch\" #set pretrained model\n!python -m cellpose --train --dir $train_dir --test_dir $val_dir --pretrained_model $ptm --diameter 16 --n_epochs 200 --save_every 10 --learning_rate 0.001 --flow_threshold 0.3 --mask_threshold -0.3 --verbose --use_gpu --chan 0 --chan 0 --batch_size 8","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:33:29.053647Z","iopub.execute_input":"2022-01-26T13:33:29.053961Z","iopub.status.idle":"2022-01-26T13:40:57.094614Z","shell.execute_reply.started":"2022-01-26T13:33:29.053924Z","shell.execute_reply":"2022-01-26T13:40:57.093786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* **Check cellpose output**","metadata":{}},{"cell_type":"code","source":"#!mkdir /kaggle/working/train && mkdir /kaggle/working/train/models/\n#!cp ../input/cp-sartorius/cellpose_residual_on_style_on_concatenation_off_train_2021_12_11_21_36_25.480280 /kaggle/working/train/models/\n!date\n#!ls -l /kaggle/working/models/\n!ls -l /root/.cellpose/models/\n!ls -lh /kaggle/working/train/models/","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:40:59.197436Z","iopub.execute_input":"2022-01-26T13:40:59.198315Z","iopub.status.idle":"2022-01-26T13:41:01.404454Z","shell.execute_reply.started":"2022-01-26T13:40:59.198274Z","shell.execute_reply":"2022-01-26T13:41:01.403612Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **INFERENCE**","metadata":{}},{"cell_type":"markdown","source":"* **Functions to compute mAP**","metadata":{}},{"cell_type":"code","source":"def compute_iou(labels, y_pred):\n    \"\"\"\n    Computes the IoU for instance labels and predictions.\n    Args:\n        labels (np array): Labels.\n        y_pred (np array): predictions\n    Returns:\n        np array: IoU matrix, of size true_objects x pred_objects.\n    \"\"\"\n    #print(np.unique(labels, return_counts=True))\n    #print(np.unique(y_pred, return_counts=True))\n    true_objects = len(np.unique(labels))\n    pred_objects = len(np.unique(y_pred))\n    # Compute intersection between all objects\n    intersection = np.histogram2d(labels.flatten(), y_pred.flatten(), bins=(true_objects, pred_objects))[0]\n    # Compute areas (needed for finding the union between all objects)\n    area_true = np.histogram(labels, bins=true_objects)[0]\n    area_pred = np.histogram(y_pred, bins=pred_objects)[0]\n    area_true = np.expand_dims(area_true, -1)\n    area_pred = np.expand_dims(area_pred, 0)\n    # Compute union\n    union = area_true + area_pred - intersection\n    iou = intersection / union\n    return iou[1:, 1:]  # exclude background\n\ndef precision_at(threshold, iou):\n    \"\"\"\n    Computes the precision at a given threshold.\n\n    Args:\n        threshold (float): Threshold.\n        iou (np array [n_truths x n_preds]): IoU matrix.\n\n    Returns:\n        int: Number of true positives,\n        int: Number of false positives,\n        int: Number of false negatives.\n    \"\"\"\n    matches = iou > threshold\n    true_positives = np.sum(matches, axis=1) >= 1  # Correct objects\n    false_negatives = np.sum(matches, axis=1) == 0  # Missed objects\n    false_positives = np.sum(matches, axis=0) == 0  # Extra objects\n    tp, fp, fn = (\n        np.sum(true_positives),\n        np.sum(false_positives),\n        np.sum(false_negatives),\n    )\n    return tp, fp, fn\n\ndef iou_map(truths, preds, verbose=0):\n    \"\"\"\n    Computes the metric for the competition.\n    Masks contain the segmented pixels where each object has one value associated,\n    and 0 is the background.\n\n    Args:\n        truths (list of masks): Ground truths.\n        preds (list of masks): Predictions.\n        verbose (int, optional): Whether to print infos. Defaults to 0.\n\n    Returns:\n        float: mAP.\n    \"\"\"\n    ious = [compute_iou(truth, pred) for truth, pred in zip(truths, preds)]\n    \n    print(ious[0].shape)\n\n    if verbose:\n        print(\"Thresh\\tTP\\tFP\\tFN\\tPrec.\")\n\n    prec = []\n    iou_per_img = np.empty((len(ious), len(truths)))\n    for i, t in enumerate(np.arange(0.5, 1.0, 0.05)):\n        tps, fps, fns = 0, 0, 0\n        for j, iou in enumerate(ious):\n            tp, fp, fn = precision_at(t, iou)\n            tps += tp\n            fps += fp\n            fns += fn\n            iou_per_img[i, j] = tp / (tp + fp + fn)\n\n        p = tps / (tps + fps + fns)\n        prec.append(p)\n\n        if verbose:\n            print(\"{:1.3f}\\t{}\\t{}\\t{}\\t{:1.3f}\".format(t, tps, fps, fns, p))\n\n    if verbose:\n        print(\"AP\\t-\\t-\\t-\\t{:1.3f}\".format(np.mean(prec)))\n    \n    return np.mean(prec), iou_per_img","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:41:03.687989Z","iopub.execute_input":"2022-01-26T13:41:03.688461Z","iopub.status.idle":"2022-01-26T13:41:03.707362Z","shell.execute_reply.started":"2022-01-26T13:41:03.688423Z","shell.execute_reply":"2022-01-26T13:41:03.706604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* **Inference script has to be written to file due to numpy version issues on Kaggle notebooks**","metadata":{}},{"cell_type":"code","source":"%%writefile run.py\nfrom cellpose import models, io, plot\nimport cv2\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport pandas as pd\nfrom pathlib import Path\nimport sys\nfrom tqdm import tqdm\n\ndef rle_encode(img):\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\nif __name__ == \"__main__\":\n    pretrained_model_path = sys.argv[1]\n    dm = int(sys.argv[2])\n    ft = float(sys.argv[3])\n    mt = float(sys.argv[4])\n    #model_dir = '/kaggle/working/train/models'\n    #model_list = os.listdir(model_dir)\n    #model_list.sort()\n    #pretrained_model_path = os.path.join(model_dir, model_list[-1])\n    \n    print(\"Testing model: \", pretrained_model_path)\n    print(\"DM: {:d}   FT: {:.2f}   MT: {:.2f}\".format(dm, ft, mt))\n\n    test_dir = Path('/kaggle/working/val')\n    test_files = [fname for fname in test_dir.iterdir() if (\"_mask\" not in fname.stem and \"_flow\" not in fname.stem and \"flip\" not in fname.stem)]\n\n    model = models.CellposeModel(gpu=True, pretrained_model=pretrained_model_path)\n\n    ids, masks = [],[]\n    for i in tqdm( range(len(test_files)) ):\n        fn = test_files[i]\n        #print( \"[{:2d}/{:d}] {:s}\".format(i, len(test_files), fn.stem) )\n        img = io.imread( str(fn) )\n        preds, flows, _ = model.eval(img, diameter=dm, channels=[0,0], augment=True, resample=True, flow_threshold=ft, mask_threshold=mt, omni=False)\n        cv2.imwrite( \"/kaggle/working/masks/{:s}.tif\".format(fn.stem), preds )\n        for i in range (1, preds.max() + 1):\n            ids.append(fn.stem)\n            masks.append(rle_encode(preds == i))\n\n    pd.DataFrame({'id': ids, 'predicted': masks}).to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:44:02.566470Z","iopub.execute_input":"2022-01-26T13:44:02.567279Z","iopub.status.idle":"2022-01-26T13:44:02.575752Z","shell.execute_reply.started":"2022-01-26T13:44:02.567239Z","shell.execute_reply":"2022-01-26T13:44:02.574657Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* **Evaluate models with different hyperparameters**","metadata":{}},{"cell_type":"code","source":"models = [\"/kaggle/input/kddm2/cellpose/cort9\"]\n\ndms = [16, 18]\nfts = [0.3]\nmts = [-0.3]\n\nfor model in models:\n    for dm in dms:\n        for ft in fts:\n            for mt in mts:\n                !rm -rf /kaggle/working/masks\n\n                !mkdir /kaggle/working/masks\n                !python run.py $model $dm $ft $mt\n                pred_files = os.listdir(\"/kaggle/working/masks\")\n                y_pred = []\n                masks = []\n                for f in pred_files:\n                    pred = cv2.imread(\"/kaggle/working/masks/{:s}.tif\".format(f[:-4]), -1)\n                    m = cv2.imread(\"/kaggle/working/val/{:s}_masks.tif\".format(f[:-4]), -1)\n                    y_pred.append(pred)\n                    masks.append(m)\n\n                __, iou_per_img = iou_map(masks, y_pred, verbose=1)\n            ","metadata":{"execution":{"iopub.status.busy":"2022-01-26T13:44:30.818292Z","iopub.execute_input":"2022-01-26T13:44:30.818600Z","iopub.status.idle":"2022-01-26T13:46:13.746135Z","shell.execute_reply.started":"2022-01-26T13:44:30.818550Z","shell.execute_reply":"2022-01-26T13:46:13.745273Z"},"trusted":true},"execution_count":null,"outputs":[]}]}