{"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":"## <font color='emperal'/> **📘 📘 📘 Introduction**\n\nCellpose Model is very sensitive about **diameter** of cell. In order to achive high accuracy, we have to train Size Model which predicts diameter of every single cell then use that predicted diameter to predict mask.\n\nIn the scope of this notebook, Size Model is trained with Satorius dataset and additional LIVECell Shsy5y images proposed by the host.\n\n📌 LIVECell Shsy5y data generator can be founded here: https://www.kaggle.com/code/luukhang/sartorius-livecell-shsy5y-data-gen\n\n📌 Train Cellpose notebook can be founded here: **Update later**\n\n📌 Inference Cellpose notebook can be founded here: https://www.kaggle.com/luukhang/sartorius-cellpose-inference\n\nCellpose link: https://github.com/MouseLand/cellpose\n\n*Please upvote if this notebook is useful 👍 👍 👍*","metadata":{}},{"cell_type":"markdown","source":"## <font color='emperal'/> **Setting, import and prepare data**","metadata":{}},{"cell_type":"code","source":"!pip uninstall -y -q yellowbrick\n\n!pip install -q tifffile # contains tools to operate tiff-files\n!pip install -q folium==0.2.1\n!pip install -q imgaug==0.2.5\n!pip install -q opencv-python==3.4.5.20\n!pip install opencv-python-headless==4.1.2.30\n!pip install -q wget\n!pip install -q memory_profiler\n!pip install -q fpdf\n!pip install -q pycocotools\n!pip install -q fastremap\n!pip install -q natsort","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:12:52.275387Z","iopub.execute_input":"2022-06-26T09:12:52.276118Z","iopub.status.idle":"2022-06-26T09:15:22.441279Z","shell.execute_reply.started":"2022-06-26T09:12:52.276021Z","shell.execute_reply":"2022-06-26T09:15:22.440325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nsys.path.append('../input/sartorius-instance-segmentation/cellpose/cellpose')","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:22.443567Z","iopub.execute_input":"2022-06-26T09:15:22.443949Z","iopub.status.idle":"2022-06-26T09:15:22.448623Z","shell.execute_reply.started":"2022-06-26T09:15:22.443910Z","shell.execute_reply":"2022-06-26T09:15:22.447772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\nimport os\nimport sys\nimport tifffile\nimport imageio\nimport glob\nimport shutil\nfrom cellpose import models, io, plot, utils\nimport glob\nimport shutil\nimport imgaug.augmenters as iaa\nimport imgaug as ia\nimport time\nimport cv2\nimport torch","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:22.450572Z","iopub.execute_input":"2022-06-26T09:15:22.450934Z","iopub.status.idle":"2022-06-26T09:15:29.158361Z","shell.execute_reply.started":"2022-06-26T09:15:22.450901Z","shell.execute_reply":"2022-06-26T09:15:29.157585Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!mkdir './cellpose_dataset_train'\n!mkdir './cellpose_dataset_val'\n!mkdir './models'","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:29.159606Z","iopub.execute_input":"2022-06-26T09:15:29.160189Z","iopub.status.idle":"2022-06-26T09:15:31.124753Z","shell.execute_reply.started":"2022-06-26T09:15:29.160153Z","shell.execute_reply":"2022-06-26T09:15:31.123682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Original Image Dimensions\nHEIGHT = 520\nWIDTH = 704\nSHAPE = (HEIGHT, WIDTH)","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:31.127551Z","iopub.execute_input":"2022-06-26T09:15:31.128156Z","iopub.status.idle":"2022-06-26T09:15:31.134285Z","shell.execute_reply.started":"2022-06-26T09:15:31.128117Z","shell.execute_reply":"2022-06-26T09:15:31.133478Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def rles_to_mask(encs, shape=SHAPE):\n    \"\"\"\n    Decodes a rle.\n\n    Args:\n        encs (list of str): Rles for each class.\n        shape (tuple [2]): Mask size.\n\n    Returns:\n        np array [shape]: Mask.\n    \"\"\"\n    img = np.zeros(shape[0] * shape[1], dtype=np.uint)\n    if type(encs)==float:\n        return img\n    for m, enc in enumerate(encs):\n        if isinstance(enc, np.float) and np.isnan(enc):\n            continue\n        enc_split = enc.split()\n        for i in range(len(enc_split) // 2):\n            start = int(enc_split[2 * i]) - 1\n            length = int(enc_split[2 * i + 1])\n            img[start: start + length] = 1 + m\n    return img.reshape(shape)","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:31.135815Z","iopub.execute_input":"2022-06-26T09:15:31.136193Z","iopub.status.idle":"2022-06-26T09:15:31.145101Z","shell.execute_reply.started":"2022-06-26T09:15:31.136146Z","shell.execute_reply":"2022-06-26T09:15:31.144307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def compute_iou(labels, y_pred):\n    \"\"\"\n    Computes the IoU for instance labels and predictions.\n\n    Args:\n        labels (np array): Labels.\n        y_pred (np array): predictions\n\n    Returns:\n        np array: IoU matrix, of size true_objects x pred_objects.\n    \"\"\"\n\n    true_objects = len(np.unique(labels))\n    pred_objects = len(np.unique(y_pred))\n\n    # Compute intersection between all objects\n    intersection = np.histogram2d(\n        labels.flatten(), y_pred.flatten(), bins=(true_objects, pred_objects)\n    )[0]\n\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\n    # Compute union\n    union = area_true + area_pred - intersection\n    iou = intersection / union\n    \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\nfrom tqdm.auto import tqdm\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 = [\n        compute_iou(rles_to_mask(truth,shape), rles_to_mask(pred,shape)) \n            for truth, pred in tqdm(zip(truths, preds))\n    ]\n    \n    if verbose:\n        print(ious[0].shape)\n        print(\"Thresh\\tTP\\tFP\\tFN\\tPrec.\")\n\n    prec = []\n    for t in np.arange(0.5, 1.0, 0.05):\n        tps, fps, fns = 0, 0, 0\n        for iou in ious:\n            tp, fp, fn = precision_at(t, iou)\n            tps += tp\n            fps += fp\n            fns += 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)\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)","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:31.146607Z","iopub.execute_input":"2022-06-26T09:15:31.147142Z","iopub.status.idle":"2022-06-26T09:15:31.165413Z","shell.execute_reply.started":"2022-06-26T09:15:31.147100Z","shell.execute_reply":"2022-06-26T09:15:31.164660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for f in glob.glob('../input/sartorius-cellpose-dataset/train/*.tif'):\n    if 'flows' not in f:\n        shutil.copy(f, os.path.join('./cellpose_dataset_train', f.split('/')[-1]))","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:31.166757Z","iopub.execute_input":"2022-06-26T09:15:31.167235Z","iopub.status.idle":"2022-06-26T09:15:40.652362Z","shell.execute_reply.started":"2022-06-26T09:15:31.167197Z","shell.execute_reply":"2022-06-26T09:15:40.651567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for f in glob.glob('../input/sartorius-cellpose-dataset/val/*.tif'):\n    if 'flows' not in f:\n        shutil.copy(f, os.path.join('./cellpose_dataset_val', f.split('/')[-1]))","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:40.653564Z","iopub.execute_input":"2022-06-26T09:15:40.653920Z","iopub.status.idle":"2022-06-26T09:15:42.543444Z","shell.execute_reply.started":"2022-06-26T09:15:40.653885Z","shell.execute_reply":"2022-06-26T09:15:42.542633Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_folder = './cellpose_dataset_train'\ntest_folder = './cellpose_dataset_val'","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:42.546122Z","iopub.execute_input":"2022-06-26T09:15:42.546459Z","iopub.status.idle":"2022-06-26T09:15:42.552758Z","shell.execute_reply.started":"2022-06-26T09:15:42.546431Z","shell.execute_reply":"2022-06-26T09:15:42.552003Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls {train_folder} | wc -l\n!ls {test_folder} | wc -l","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:42.554377Z","iopub.execute_input":"2022-06-26T09:15:42.554809Z","iopub.status.idle":"2022-06-26T09:15:43.875121Z","shell.execute_reply.started":"2022-06-26T09:15:42.554773Z","shell.execute_reply":"2022-06-26T09:15:43.874177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_img_files = np.sort(glob.glob(os.path.join('cellpose_dataset_train', '*img.tif')))\ntrain_mask_files = np.sort(glob.glob(os.path.join('cellpose_dataset_train', '*masks.tif')))\n\nval_img_files = np.sort(glob.glob(os.path.join('cellpose_dataset_val', '*img.tif')))\nval_mask_files = np.sort(glob.glob(os.path.join('cellpose_dataset_val', \"*masks.tif\")))\n\nassert (len(train_img_files ) == len(train_mask_files)) and (len(val_img_files) == len(val_mask_files))\n\ntrain_imgs = [tifffile.imread(img_file) for img_file in tqdm(train_img_files)]\ntrain_masks = [tifffile.imread(flows_file) for flows_file in tqdm(train_mask_files)]\n\nval_imgs = [tifffile.imread(img_file) for img_file in tqdm(val_img_files)]\nval_masks = [tifffile.imread(flows_file) for flows_file in tqdm(val_mask_files)]","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:43.878343Z","iopub.execute_input":"2022-06-26T09:15:43.878660Z","iopub.status.idle":"2022-06-26T09:15:44.839470Z","shell.execute_reply.started":"2022-06-26T09:15:43.878627Z","shell.execute_reply":"2022-06-26T09:15:44.838584Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(train_imgs))\nprint(len(val_imgs))","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:44.840989Z","iopub.execute_input":"2022-06-26T09:15:44.841361Z","iopub.status.idle":"2022-06-26T09:15:44.846288Z","shell.execute_reply.started":"2022-06-26T09:15:44.841323Z","shell.execute_reply":"2022-06-26T09:15:44.845258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <font color='emperal'/> **Init and train size model**","metadata":{}},{"cell_type":"code","source":"model_path = '../input/sartorius-weight/cellpose_250622_concat_epoch_500'","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:44.850061Z","iopub.execute_input":"2022-06-26T09:15:44.850628Z","iopub.status.idle":"2022-06-26T09:15:44.855609Z","shell.execute_reply.started":"2022-06-26T09:15:44.850574Z","shell.execute_reply":"2022-06-26T09:15:44.854769Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_path = shutil.copy(model_path, './models')","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:44.857069Z","iopub.execute_input":"2022-06-26T09:15:44.857667Z","iopub.status.idle":"2022-06-26T09:15:45.271943Z","shell.execute_reply.started":"2022-06-26T09:15:44.857629Z","shell.execute_reply":"2022-06-26T09:15:45.271109Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cp_model = models.CellposeModel(gpu=True, pretrained_model='./models/cellpose_250622_concat_epoch_500', nchan=2)\nsz_model = models.SizeModel(cp_model=cp_model)","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:45.273173Z","iopub.execute_input":"2022-06-26T09:15:45.273560Z","iopub.status.idle":"2022-06-26T09:15:48.203547Z","shell.execute_reply.started":"2022-06-26T09:15:45.273508Z","shell.execute_reply":"2022-06-26T09:15:48.202746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from cellpose.io import logger_setup\nlogger, log_file = logger_setup()\n\nstart_time = time.time()\n\nparams, epochs, train_corr_vls, val_corr_vls = sz_model.train(train_imgs, train_masks, val_imgs, val_masks, \n                        channels=[0, 0], n_epochs=200, learning_rate=0.01)\n\nend_time = time.time()","metadata":{"execution":{"iopub.status.busy":"2022-06-26T09:15:48.204830Z","iopub.execute_input":"2022-06-26T09:15:48.205157Z","iopub.status.idle":"2022-06-26T10:28:03.877255Z","shell.execute_reply.started":"2022-06-26T09:15:48.205121Z","shell.execute_reply":"2022-06-26T10:28:03.876334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training_time = (end_time - start_time) / 60\n\nprint('Train {} epochs took {} minutes'.format(200, training_time))","metadata":{"execution":{"iopub.status.busy":"2022-06-26T10:28:03.878677Z","iopub.execute_input":"2022-06-26T10:28:03.879273Z","iopub.status.idle":"2022-06-26T10:28:03.885563Z","shell.execute_reply.started":"2022-06-26T10:28:03.879236Z","shell.execute_reply":"2022-06-26T10:28:03.884420Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = plt.figure(figsize=(6, 4))\n\nplt.plot(epochs, train_corr_vls)\nplt.plot(epochs, val_corr_vls)\nplt.legend(['train_corr', 'val_corr'])\n\nplt.xlabel('Epoch')\nplt.ylabel('Correlation')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-26T10:28:03.887027Z","iopub.execute_input":"2022-06-26T10:28:03.887436Z","iopub.status.idle":"2022-06-26T10:28:04.115680Z","shell.execute_reply.started":"2022-06-26T10:28:03.887403Z","shell.execute_reply":"2022-06-26T10:28:04.114996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Train corr', train_corr_vls[-1])\nprint('Val corr', val_corr_vls[-1])","metadata":{"execution":{"iopub.status.busy":"2022-06-26T10:28:56.380365Z","iopub.execute_input":"2022-06-26T10:28:56.380944Z","iopub.status.idle":"2022-06-26T10:28:56.386183Z","shell.execute_reply.started":"2022-06-26T10:28:56.380906Z","shell.execute_reply":"2022-06-26T10:28:56.385413Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.listdir('./models')","metadata":{"execution":{"iopub.status.busy":"2022-06-26T10:29:27.123103Z","iopub.execute_input":"2022-06-26T10:29:27.123468Z","iopub.status.idle":"2022-06-26T10:29:27.130347Z","shell.execute_reply.started":"2022-06-26T10:29:27.123438Z","shell.execute_reply":"2022-06-26T10:29:27.129557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a href='./models/cellpose_250622_concat_epoch_500_size.npy'/> Download","metadata":{}},{"cell_type":"markdown","source":"## <font color='emperal'/> **References**\n\nhttps://www.kaggle.com/code/aishikai/scs-cellpose-train","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}