{"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":"# <center> Sartorius - Cell Instance Segmentation - EDA </center>\n\n<img src=\"https://storage.googleapis.com/kaggle-competitions/kaggle/30201/logos/header.png\" style=\"float: left; margin-right: 5px;\" />\n","metadata":{"execution":{"iopub.status.busy":"2021-11-09T15:50:12.131374Z","iopub.execute_input":"2021-11-09T15:50:12.131639Z","iopub.status.idle":"2021-11-09T15:50:12.136857Z","shell.execute_reply.started":"2021-11-09T15:50:12.131613Z","shell.execute_reply":"2021-11-09T15:50:12.135446Z"}}},{"cell_type":"markdown","source":"The overview of the problem and a summary of the data can be found [here](https://www.kaggle.com/c/sartorius-cell-instance-segmentation/overview) and [here](https://www.kaggle.com/c/sartorius-cell-instance-segmentation/data), respectively.\n\n# Task\n\nThe task of this challenge is to detect and delineate distinct objects of interest in biological images depicting neuronal cell types commonly used in the study of neurological disorders. More specifically, this will be done using phase contrast microscopy images to train and test computer vision model for instance segmentation of neuronal cells.\n\n\n\n\n\n\n","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\n\nimport numpy as np \nimport pandas as pd\n\nimport plotly.express as px\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n%config InlineBackend.figure_formats = ['svg']\n\ndf_train = pd.read_csv('../input/sartorius-cell-instance-segmentation/train.csv')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:09:09.707802Z","iopub.execute_input":"2021-11-15T16:09:09.708459Z","iopub.status.idle":"2021-11-15T16:09:12.639309Z","shell.execute_reply.started":"2021-11-15T16:09:09.708343Z","shell.execute_reply":"2021-11-15T16:09:12.638660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1. Meta Data\n\nThe meta data is given only for the train data, meaning that they cannot be used to predict the test data but they should be used to construct a solid cross validation strategy. So let's start understanding the statistical properties of the meta data.\n\nThe meta data, which is given in the `train.csv` file, contains 7 categorical and 2 numerical features (see table below). Each row points to an image with the `id` column and its associated mask with the `annotation` column. The annotations are given in the \"run length encoded pixels\" format. Furthermore each row contains the `cell type` information.","metadata":{}},{"cell_type":"code","source":"df_train.info()","metadata":{"execution":{"iopub.status.busy":"2021-11-15T16:09:12.640632Z","iopub.execute_input":"2021-11-15T16:09:12.641367Z","iopub.status.idle":"2021-11-15T16:09:12.726012Z","shell.execute_reply.started":"2021-11-15T16:09:12.641333Z","shell.execute_reply":"2021-11-15T16:09:12.724880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-15T16:09:12.727301Z","iopub.execute_input":"2021-11-15T16:09:12.727578Z","iopub.status.idle":"2021-11-15T16:09:12.755605Z","shell.execute_reply.started":"2021-11-15T16:09:12.727542Z","shell.execute_reply":"2021-11-15T16:09:12.753083Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train['sample_id'].nunique()","metadata":{"execution":{"iopub.status.busy":"2021-11-15T16:09:12.757694Z","iopub.execute_input":"2021-11-15T16:09:12.758024Z","iopub.status.idle":"2021-11-15T16:09:12.777618Z","shell.execute_reply.started":"2021-11-15T16:09:12.757968Z","shell.execute_reply":"2021-11-15T16:09:12.777023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1.1 Images\n\nThere are only 606 images in the train set, which is small for training neural network models and can easily lead to an overfitting problem. However, it's well known that this problem can be easily mitigated with the use of appropriate augmentation techniques (e.g. [Ronneberger et al. 2015](https://arxiv.org/pdf/1505.04597.pdf)).\n\nAll the images have the same shape: (704 x 520) px. This is nice to have in a dataset because there won't be any complications due to a variable image resolution.","metadata":{}},{"cell_type":"code","source":"print(f'Number of unique images: {df_train.id.nunique()}')\nprint(f'Do all the images have a width of 704: {(df_train[\"width\"]==704).all()}')\nprint(f'Do all the images have a height of 520: {(df_train[\"height\"]==520).all()}')","metadata":{"execution":{"iopub.status.busy":"2021-11-15T16:09:12.779063Z","iopub.execute_input":"2021-11-15T16:09:12.779645Z","iopub.status.idle":"2021-11-15T16:09:12.796401Z","shell.execute_reply.started":"2021-11-15T16:09:12.779600Z","shell.execute_reply":"2021-11-15T16:09:12.795723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The number of instances in each image is remarkably variable (see figure below). Some statistical measures are as follows:\n- Most of the images have more than 47 instances annotated.\n- The minimum number of instances is 4.\n- The maximum number of instances is 790.\n\nThese numbers are extremely critical to train an instance segmentation model. For instance, the famous Mask RCNN model requires the information of \"maximum number of detections\".","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots()\n\nninstances_per_image = df_train[['id']].value_counts().sort_values()\nninstances_per_image.index = range(606)\nninstances_per_image.median()\nninstances_per_image.plot.bar(ax=ax)\n\nax.set_xticklabels([])\nax.set_xlabel('Images')\nax.set_ylabel('Number of Instances')\nplt.show()\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:09:12.797366Z","iopub.execute_input":"2021-11-15T16:09:12.797925Z","iopub.status.idle":"2021-11-15T16:09:16.257811Z","shell.execute_reply.started":"2021-11-15T16:09:12.797874Z","shell.execute_reply":"2021-11-15T16:09:16.257078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The number of rows in the `train.csv` file is more than 606. This means that there are more than 1 instances in a given image.","metadata":{}},{"cell_type":"markdown","source":"## 1.2 Cell Types\n\nEach image is annotated with one of the three cell types *shsy5y*, *asto*, and *cort*. The distribution of the cell types is shown in the figure below. While the most represented cell type is *shsy5y* with ~52k instances in the train set, cell types *cort* and *astro* are annotated only ~10.5k times each. This means that the data is biased towards cell type *shsy5y*.","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(1, 1)\ndf_train[['cell_type']].value_counts().plot.bar(ax=ax)\nax.set_ylabel('Number of Instances')\nfig.tight_layout()\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:09:16.258971Z","iopub.execute_input":"2021-11-15T16:09:16.259268Z","iopub.status.idle":"2021-11-15T16:09:16.473338Z","shell.execute_reply.started":"2021-11-15T16:09:16.259231Z","shell.execute_reply":"2021-11-15T16:09:16.472441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The number of unique `id` and `cell_type` combinations is equal to the number of unique images. This means that each image is associated with a unique cell type! See the result below. This also explains the distribution of the image numbers in the train.csv file. The images observed abundantly are associated with the most observed cell type shsy5y.","metadata":{}},{"cell_type":"code","source":"df_train[['id', 'cell_type']].value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-11-15T16:09:16.474966Z","iopub.execute_input":"2021-11-15T16:09:16.475515Z","iopub.status.idle":"2021-11-15T16:09:16.513799Z","shell.execute_reply.started":"2021-11-15T16:09:16.475473Z","shell.execute_reply":"2021-11-15T16:09:16.513140Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Since each image is associated with only 1 cell type, we can count the number of images associated with each cell type. The figure below shows that most of the images are associated with the cell type `cort`, which agrees with our previous findings. ","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(1, 1)\ndf_train.groupby(['id','cell_type'])['cell_type'].first().value_counts().plot.bar(ax=ax)\nax.set_ylabel('Number of Images')\nax.set_xlabel('Cell Types')\nfig.tight_layout()\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:09:16.515021Z","iopub.execute_input":"2021-11-15T16:09:16.515424Z","iopub.status.idle":"2021-11-15T16:09:16.736549Z","shell.execute_reply.started":"2021-11-15T16:09:16.515396Z","shell.execute_reply":"2021-11-15T16:09:16.735724Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Train Images\n\nIt's time to look at the images and the masks now. The figure below shows randomly selected images corresponding to each of the three distinct cell types. Each cell type has its own unique morphological properties. \n\n- `astro` instances are the biggest in shape. They cover a lot of space in the masks.\n- `cort` instances are smaller than the other cell types in general and they are in circle-like shapes. They don't cover much space in the masks.\n- `shsy5y` instances are slightly bigger, elongated and more abundant than the cort instances. They cover more space than the `cort` cells.","metadata":{}},{"cell_type":"code","source":"def make_mask(mask_files, image_shape=(520, 704), color=False):\n    mask = np.zeros(image_shape).ravel()\n    for i, mask_file in enumerate(mask_files):\n        couples = np.array(mask_file.split()).reshape(-1, 2).astype(int)\n        couples[:, 1] = couples[:, 0] + couples[:, 1]\n        for couple in couples:\n            if color:\n                mask[couple[0]: couple[1]] = i\n            else:\n                mask[couple[0]: couple[1]] = 1\n    mask = mask.reshape(520, 704)\n    return mask\n\ndef plot_image(image_id='0030fd0e6378'):\n    fig, ax = plt.subplots(1, 2, figsize=(14,5))\n    cell_type = df_train.loc[df_train['id'] == image_id, 'cell_type'][0:1].values\n    \n    file_name = os.path.join(\n        '../input/sartorius-cell-instance-segmentation',\n        'train', image_id + '.png')\n    image = plt.imread(file_name)\n    mask_files = df_train.loc[df_train['id'] == image_id, 'annotation']\n    mask = make_mask(mask_files)\n\n    ax[0].imshow(\n        image,\n        cmap = plt.get_cmap('winter'), \n        origin = 'upper',\n        vmax = np.quantile(image, 0.99),\n        vmin = np.quantile(image, 0.05)\n    )\n    ax[0].set_title(f'Source [{image_id}]')\n    ax[0].axis('off')\n    \n    ax[1].imshow(\n        image,\n        cmap = plt.get_cmap('winter'), \n        origin = 'upper',\n        vmax = 255,\n        vmin = 0)\n    ax[1].imshow(mask, alpha=1, cmap=plt.get_cmap('seismic'))\n    ax[1].set_title(f'Source [{image_id}] + Mask {cell_type}')\n    ax[1].axis('off')\n    plt.show()\n\nselect_image_ids = []\nselect_image_ids.append(df_train.loc[df_train['cell_type'] == 'astro', 'id'].sample(1).to_list()[0])\nselect_image_ids.append(df_train.loc[df_train['cell_type'] == 'cort', 'id'].sample(1).to_list()[0])\nselect_image_ids.append(df_train.loc[df_train['cell_type'] == 'shsy5y', 'id'].sample(1).to_list()[0])\n\nfor image_id in select_image_ids:\n    plot_image(image_id)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:09:16.740288Z","iopub.execute_input":"2021-11-15T16:09:16.740774Z","iopub.status.idle":"2021-11-15T16:09:18.392144Z","shell.execute_reply.started":"2021-11-15T16:09:16.740723Z","shell.execute_reply":"2021-11-15T16:09:18.391352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Overlapping Instances","metadata":{}},{"cell_type":"markdown","source":"The quote paraphrased from the [data page](https://www.kaggle.com/c/sartorius-cell-instance-segmentation/data) clearly indicates that that there are overlapping instances. Let's calculate the percentage of these overlaps.\n\n> Note: while predictions are not allowed to overlap, the training labels are provided in full (with overlapping portions included). This is to ensure that models are provided the full data for each object. Removing overlap in predictions is a task for the competitor.\n\nMethod: Decode the run-length-encoded masks into pixels and count the number of each pixel's occurence. Any pixel counted more than once indicates an overlap (see code below).","metadata":{}},{"cell_type":"code","source":"def overlap_percentage(image_id, return_pixel_counts=False):\n    mask_files = df_train.loc[df_train['id'] == image_id, 'annotation']\n    result = np.array([]).astype(int)\n    for mask_file in mask_files:\n        couples = np.array(mask_file.split()).reshape(-1, 2).astype(int)\n        couples[:, 1] = couples[:, 0] + couples[:, 1]\n        for i, couple in enumerate(couples):\n            result = np.append(result, np.arange(couple[0], couple[1]))\n            \n    pixel_counts = pd.DataFrame(result, columns=['Pixels'])['Pixels'].value_counts()\n    overlap_percentage = pixel_counts[pixel_counts > 1].sum() / pixel_counts.sum()\n    \n    if return_pixel_counts:\n        return overlap_percentage, pixel_counts\n    else: \n        return overlap_percentage\n\ndf_id_cell = df_train[['id', 'cell_type']].drop_duplicates().reset_index(drop=True)\ndf_id_cell['Overlap'] = 0.\n\nfor image_id in df_id_cell['id'].unique():\n    df_id_cell.loc[df_id_cell['id']==image_id, 'Overlap'] = overlap_percentage(image_id)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:09:18.393484Z","iopub.execute_input":"2021-11-15T16:09:18.393680Z","iopub.status.idle":"2021-11-15T16:10:26.997870Z","shell.execute_reply.started":"2021-11-15T16:09:18.393656Z","shell.execute_reply":"2021-11-15T16:10:26.996984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The figure below shows the overlap fractions in each image as a function of the cell type. There are 3 images with very big overlap fractions! Two of them belongs to shsy5y cell type and one to cort cell type.","metadata":{}},{"cell_type":"code","source":"ax = sns.stripplot(x='cell_type', y='Overlap', data=df_id_cell, size=4, color=\".3\", linewidth=0)\nax.set(ylabel='Overlap Fraction')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:10:26.998950Z","iopub.execute_input":"2021-11-15T16:10:26.999208Z","iopub.status.idle":"2021-11-15T16:10:27.232376Z","shell.execute_reply.started":"2021-11-15T16:10:26.999162Z","shell.execute_reply":"2021-11-15T16:10:27.231554Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f'Median overlap fraction of entire train data set: {np.round(df_id_cell[\"Overlap\"].median(), 2)*100} %')","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:10:27.233834Z","iopub.execute_input":"2021-11-15T16:10:27.234148Z","iopub.status.idle":"2021-11-15T16:10:27.239336Z","shell.execute_reply.started":"2021-11-15T16:10:27.234107Z","shell.execute_reply":"2021-11-15T16:10:27.238577Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's identify these three images with the highest overlap fractions and visualize them and their respective masks to see what's unusual.\n\nMethod: We will decode the run-length-encoded masks into an image of shape (520, 704, numberofinstances) and add the pixels on the last axis. Any overlapping instances will have a value greater than 1 and non-overlapping pixels will have a value 0 or 1.","metadata":{}},{"cell_type":"code","source":"outliers = df_id_cell.sort_values(by='Overlap', ascending=False).head(3)\noutliers","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:10:27.240632Z","iopub.execute_input":"2021-11-15T16:10:27.240917Z","iopub.status.idle":"2021-11-15T16:10:27.262342Z","shell.execute_reply.started":"2021-11-15T16:10:27.240876Z","shell.execute_reply":"2021-11-15T16:10:27.261487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_mask(mask_files, image_shape=(520, 704), color=False):\n    masks = np.zeros(image_shape + (len(mask_files),))\n\n    for i, mask_file in enumerate(mask_files):\n        mask = np.zeros(image_shape).ravel()\n        couples = np.array(mask_file.split()).reshape(-1, 2).astype(int)\n        couples[:, 1] = couples[:, 0] + couples[:, 1]\n        for couple in couples:\n            if color:\n                mask[couple[0]: couple[1]] = i\n            else:\n                mask[couple[0]: couple[1]] = 1\n        mask = mask.reshape(520, 704)\n        masks[:, :, i] = mask\n        del mask\n    return masks\n\ndef plot_outlier(idx):\n    image_id = outliers.iloc[idx]['id']\n    cell_type = outliers.iloc[idx]['cell_type']\n    mask_files = df_train.loc[df_train['id'] == image_id, 'annotation']\n    masks = make_mask(mask_files)\n\n    fig = px.imshow(masks.sum(-1))\n    fig.update_layout(title_text=f'id: {image_id}, cell_type: {cell_type}', title_x=0.5)\n    fig.show()","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-11-15T16:10:27.264033Z","iopub.execute_input":"2021-11-15T16:10:27.264695Z","iopub.status.idle":"2021-11-15T16:10:27.278449Z","shell.execute_reply.started":"2021-11-15T16:10:27.264649Z","shell.execute_reply":"2021-11-15T16:10:27.277854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Outlier 1\n\nLooks like we have an interesting case here. Outlier 1 mainly consist of duplicate instances! Note that purple pixels are the pixels that are seen only once (no overlap) whereas the other colors (e.g.) indicates at least 1 overlapping pixel.\n\n","metadata":{}},{"cell_type":"code","source":"plot_outlier(0)","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-11-15T16:10:27.279296Z","iopub.execute_input":"2021-11-15T16:10:27.280043Z","iopub.status.idle":"2021-11-15T16:10:28.569852Z","shell.execute_reply.started":"2021-11-15T16:10:27.280012Z","shell.execute_reply":"2021-11-15T16:10:28.568888Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Outlier 2\n\nThe overlaps in Outlier 2 is different than the ones in Outlier 1. Here, small instances overlap with larger instances.","metadata":{}},{"cell_type":"code","source":"plot_outlier(1)","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-11-15T16:10:28.571296Z","iopub.execute_input":"2021-11-15T16:10:28.571519Z","iopub.status.idle":"2021-11-15T16:10:33.243857Z","shell.execute_reply.started":"2021-11-15T16:10:28.571493Z","shell.execute_reply":"2021-11-15T16:10:33.242739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Outlier 3\n\nThe overlaps in Outlier 3 is not as severe as the ones in the Outlier 1 and Outlier 2. There are only a small number of small instances overlapping with bigger instances.","metadata":{}},{"cell_type":"code","source":"plot_outlier(2)","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-11-15T16:10:33.245235Z","iopub.execute_input":"2021-11-15T16:10:33.245607Z","iopub.status.idle":"2021-11-15T16:10:37.970687Z","shell.execute_reply.started":"2021-11-15T16:10:33.245558Z","shell.execute_reply":"2021-11-15T16:10:37.969812Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"These outliers have to be taken care of to avoid use of duplicated instances!","metadata":{}},{"cell_type":"markdown","source":"# 4. Bounding Box Properties\n\nInstance segmentation models uses bounding boxes to locate the instances. Therefore, the sizes of the bounding boxes are important for the models. Let's examine the bounding boxes of the instances.","metadata":{}},{"cell_type":"code","source":"def rle2mask(rle, img_w, img_h):\n    array = np.fromiter(rle.split(), dtype = np.uint32)\n    array = array.reshape(-1, 2)\n    array[:,0] = array[:, 0] - 1\n    \n    mask_decompressed = np.concatenate([np.arange(i[0], i[0] + i[1], dtype=np.uint32) for i in array])\n\n    msk_img = np.zeros(img_w * img_h, dtype = np.uint8)\n    msk_img[mask_decompressed] = 1\n    msk_img = msk_img.reshape((img_h, img_w))\n    msk_img = np.asfortranarray(msk_img)\n    \n    return msk_img\n\ndef instance_hg_wd(rle):\n    mask = rle2mask(rle, 520, 704)\n    nonzerocoords = np.transpose(np.nonzero(mask))\n    height = nonzerocoords[:,0].max() - nonzerocoords[:,0].min()\n    width = nonzerocoords[:,1].max() - nonzerocoords[:,1].min()\n    return height, width\n\ndef get_ins_hw():\n    df_train[['ins_height', 'ins_width']] = 0\n    hw = pd.DataFrame(df_train['annotation'].apply(instance_hg_wd).to_list())\n    df_train['ins_height'] = hw[0]\n    df_train['ins_width'] = hw[1]\n    return df_train\n\ndf_train = get_ins_hw()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:10:37.972407Z","iopub.execute_input":"2021-11-15T16:10:37.972697Z","iopub.status.idle":"2021-11-15T16:15:26.480759Z","shell.execute_reply.started":"2021-11-15T16:10:37.972662Z","shell.execute_reply":"2021-11-15T16:15:26.479865Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = px.scatter(\n    df_train, x=\"ins_width\", y=\"ins_height\", color=\"cell_type\",\n    labels={\n         \"ins_width\": \"Instance Height (px)\",\n         \"ins_height\": \"Instance Width (px)\",\n         \"cell_type\": \"Cell Type\"\n     },)\nfig.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-15T16:55:14.394139Z","iopub.execute_input":"2021-11-15T16:55:14.394665Z","iopub.status.idle":"2021-11-15T16:55:14.845111Z","shell.execute_reply.started":"2021-11-15T16:55:14.394634Z","shell.execute_reply":"2021-11-15T16:55:14.841165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The figure above shows the instance heights and the instance widths as a function of the cell types. The instance heights and the instance widths of the astro cells (red) are strikingly different than those of cort cells (green) and shsy5y cells (blue). Astro cell instances are in general wider and longer, consistent with our visual inspection.\n","metadata":{}},{"cell_type":"code","source":"print(f\"Minimum instance height: {df_train['ins_height'].min()} px\")\nprint(f\"Maximum instance height: {df_train['ins_height'].max()} px\")\nprint(f\"Minimum instance width: {df_train['ins_width'].min()} px\")\nprint(f\"Maximum instance width: {df_train['ins_width'].max()} px\")","metadata":{"execution":{"iopub.status.busy":"2021-11-15T16:15:26.507138Z","iopub.execute_input":"2021-11-15T16:15:26.507731Z","iopub.status.idle":"2021-11-15T16:15:26.519578Z","shell.execute_reply.started":"2021-11-15T16:15:26.507686Z","shell.execute_reply":"2021-11-15T16:15:26.518742Z"},"trusted":true},"execution_count":null,"outputs":[]}]}