{"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":"# Utility class Tile()\n\nIn this notebook, I have created a class Tile, which lets you instantiate a Tile object to quickly access metadata, as well as some useful utility functions to help you analyze data and experiment in this challenge quickly and elegantly.\n\nI have utilized functions from other notebooks in this notebook, namely:\n\n<u1>\n    Leonid Kulyk's amazing <a href=https://www.kaggle.com/code/leonidkulyk/eda-hubmap-hhv-interactive-annotations/notebook>plot_annotated_image</a> function,<br>\n    ITK8191's <a href=https://www.kaggle.com/code/itsuki9180/hubmap-making-dataset>make_seg_mask</a> function\n    ","metadata":{}},{"cell_type":"code","source":"#Setup\n\nimport os\nimport pandas as pd\nimport numpy as np\nimport json\nimport tifffile as tiff\nimport cv2\nfrom PIL import Image\nimport plotly.graph_objects as go\nimport matplotlib.pyplot as plt\n\ninput_path = \"/kaggle/input/hubmap-hacking-the-human-vasculature/\"\nworking_path = \"/kaggle/working/\"\noutput_path = \"/kaggle/output/\"","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:15.511601Z","iopub.execute_input":"2023-06-17T08:48:15.512304Z","iopub.status.idle":"2023-06-17T08:48:15.981764Z","shell.execute_reply.started":"2023-06-17T08:48:15.512269Z","shell.execute_reply":"2023-06-17T08:48:15.980846Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wsi_meta = pd.read_csv(os.path.join(input_path, \"wsi_meta.csv\"))\n\ntile_meta = pd.read_csv(os.path.join(input_path, \"tile_meta.csv\"))\ntile_meta = tile_meta[tile_meta['dataset'] != 3]\n\ntile_ids = [tile_id for tile_id in tile_meta.sort_values(['source_wsi', 'i', 'j'])['id']]","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:15.984135Z","iopub.execute_input":"2023-06-17T08:48:15.984579Z","iopub.status.idle":"2023-06-17T08:48:16.044944Z","shell.execute_reply.started":"2023-06-17T08:48:15.984537Z","shell.execute_reply":"2023-06-17T08:48:16.043964Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open(os.path.join(input_path, 'polygons.jsonl'), 'r') as f:\n    labels_list = [json.loads(line) for line in f]","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:16.046157Z","iopub.execute_input":"2023-06-17T08:48:16.046462Z","iopub.status.idle":"2023-06-17T08:48:19.760321Z","shell.execute_reply.started":"2023-06-17T08:48:16.046436Z","shell.execute_reply":"2023-06-17T08:48:19.759416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Tile():\n    def __init__(self, tile_id, labels_list=labels_list, input_path=os.path.join(input_path,\"train\")):\n        \n        # Get metadata from the provided csv files\n        \n        tile_info = tile_meta[tile_meta['id']==tile_id]\n        \n        self.id = tile_id\n        self.wsi = tile_info['source_wsi'].item()\n        self.dataset = tile_info['dataset'].item()\n        self.i = tile_info['i'].item()\n        self.j = tile_info['j'].item()\n        self.path = os.path.join(input_path,self.id+\".tif\")\n        \n        wsi_info = wsi_meta[wsi_meta['source_wsi'] == self.wsi]\n        \n        self.age = wsi_info['age'].item()\n        self.sex = wsi_info['sex'].item()\n        self.race = wsi_info['race'].item()\n        self.height = wsi_info['height'].item()\n        self.weight = wsi_info['weight'].item()\n        self.bmi = wsi_info['bmi'].item()\n        \n        self.labels_list = labels_list\n        \n    \n    def get_labels(self):\n        \n        # Returns the annotations dictionary from the label list\n        \n        return next((d['annotations'] for d in self.labels_list if d['id']==self.id))\n    \n    \n    def read_image(self):\n        \n        array = tiff.imread(self.path)\n        im = Image.fromarray(array)\n        \n        width = im.size[0]\n        height = im.size[1]\n    \n        return im, width, height\n    \n    \n    def get_mask(self):\n        \n        labels = self.get_labels()\n        \n        mask = np.zeros((512,512), dtype=np.float32)\n        \n        for label in labels:\n            coords = label['coordinates']\n            if label['type'] == \"blood_vessel\":\n                for cd in coords:\n                    rr, cc = np.array([i[1] for i in cd]), np.asarray([i[0] for i in cd])\n                    mask[rr, cc] = 1\n        \n        contours,_ = cv2.findContours((mask*255).astype(np.uint8), 1, 2)\n        filled_mask = np.zeros([mask.shape[0], mask.shape[1], 3], dtype=\"uint8\")\n\n        for p in contours:\n            cv2.fillPoly(filled_mask, [p], (255, 255, 255))\n        \n        return filled_mask\n    \n    \n    def write_tile_and_mask(self):\n        \n        tile_im = np.array(self.read_image()[0])\n        mask = self.get_mask()\n        \n        if np.sum(mask)>0:\n            cv2.imwrite(os.path.join(working_path,f'{self.id}.png'), tile_im)\n            cv2.imwrite(os.path.join(working_path,f'{self.id}_mask.png'), mask)\n        \n    \n    \n    def get_cartesian_coords(self, coords, img_height):\n        coords_array = np.array(coords).squeeze()\n        xs = coords_array[:, 0]\n        ys = -coords_array[:, 1] + img_height\n\n        return xs, ys\n\n    \n\n    def plot_annotated_image(self, scale_factor: int = 1.0):\n        \n        img_example, img_width, img_height = self.read_image()\n        labels = self.get_labels()\n\n        # create figure\n        fig = go.Figure()\n\n        # add invisible scatter trace\n        fig.add_trace(\n            go.Scatter(\n                x=[0, img_width],\n                y=[0, img_height],\n                mode=\"markers\",\n                marker_opacity=0\n            )\n        )\n\n        # configure axes\n        fig.update_xaxes(\n            visible=False,\n            range=[0, img_width]\n        )\n\n        fig.update_yaxes(\n            visible=False,\n            range=[0, img_height],\n            # the scaleanchor attribute ensures that the aspect ratio stays constant\n            scaleanchor=\"x\"\n        )\n\n        # add image\n        fig.add_layout_image(dict(\n            x=0,\n            sizex=img_width,\n            y=img_height,\n            sizey=img_height,\n            xref=\"x\", yref=\"y\",\n            opacity=1.0,\n            layer=\"below\",\n            sizing=\"stretch\",\n            source=img_example\n        ))\n\n        # add polygons\n        for label in labels:\n            name = label[\"type\"]\n            xs, ys = self.get_cartesian_coords(label[\"coordinates\"], img_height)\n            fig.add_trace(go.Scatter(\n                x=xs, y=ys, fill=\"toself\",\n                name=name,\n                hovertemplate=\"%{name}\",\n                mode='lines'\n            ))\n\n        # configure other layout\n        fig.update_layout(\n            width=img_width * scale_factor,\n            height=img_height * scale_factor,\n            margin={\"l\": 0, \"r\": 0, \"t\": 0, \"b\": 0},\n            showlegend=False\n        )\n\n        # disable the autosize on double click because it adds unwanted margins around the image\n        # and finally show figure\n        fig.show(config={'doubleClick': 'reset'})","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:19.762432Z","iopub.execute_input":"2023-06-17T08:48:19.762773Z","iopub.status.idle":"2023-06-17T08:48:19.783978Z","shell.execute_reply.started":"2023-06-17T08:48:19.762720Z","shell.execute_reply":"2023-06-17T08:48:19.782387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a Tile object from some tile id\nex = Tile(tile_ids[10])","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:19.786799Z","iopub.execute_input":"2023-06-17T08:48:19.787494Z","iopub.status.idle":"2023-06-17T08:48:19.843033Z","shell.execute_reply.started":"2023-06-17T08:48:19.787451Z","shell.execute_reply":"2023-06-17T08:48:19.840808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Use Leonid Kulyk's amazing visualization function to look at the tile\n\nex.plot_annotated_image()","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:19.845287Z","iopub.execute_input":"2023-06-17T08:48:19.846154Z","iopub.status.idle":"2023-06-17T08:48:20.491825Z","shell.execute_reply.started":"2023-06-17T08:48:19.846110Z","shell.execute_reply":"2023-06-17T08:48:20.490463Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Use \n\nmask_example = ex.get_mask()\nplt.imshow(mask_example)","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:20.493679Z","iopub.execute_input":"2023-06-17T08:48:20.494075Z","iopub.status.idle":"2023-06-17T08:48:20.797243Z","shell.execute_reply.started":"2023-06-17T08:48:20.494039Z","shell.execute_reply":"2023-06-17T08:48:20.796288Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# To simplify training, we can save tiles and their mask to the working directory in png format.\n\nfor tiles in tile_ids:\n    tile = Tile(tiles)\n    tile.write_tile_and_mask()","metadata":{"execution":{"iopub.status.busy":"2023-06-17T08:48:20.798416Z","iopub.execute_input":"2023-06-17T08:48:20.798761Z","iopub.status.idle":"2023-06-17T08:49:45.806552Z","shell.execute_reply.started":"2023-06-17T08:48:20.798709Z","shell.execute_reply":"2023-06-17T08:49:45.805076Z"},"trusted":true},"execution_count":null,"outputs":[]}]}