{"cells":[{"metadata":{},"cell_type":"markdown","source":"If you consider training a classification approach on a patch level, one possible way is to prepare patch coordinates and targets in advance. Than use this data as an input into Dataset."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nfrom typing import Optional, List, Tuple\nfrom multiprocessing import Pool\n\nimport numpy as np\nimport openslide\nimport pandas as pd\nfrom tqdm.auto import tqdm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class PatchSlicer:\n    height: int\n    width: int\n\n    def __init__(self, path_to_wsi: str, step_size: int = 256):\n        self.path_to_wsi = path_to_wsi\n        self.step_size = step_size\n        self.patch_size = (self.step_size, self.step_size)\n        self.x = 0\n        self.y = 0\n\n    def patch_generator(self):\n        with openslide.OpenSlide(self.path_to_wsi) as wsi:\n            self.width = wsi.level_dimensions[0][0]\n            self.height = wsi.level_dimensions[0][1]\n            while self.y + self.step_size < self.height:\n                while self.x + self.step_size < self.width:\n                    coords = (self.x, self.y)\n                    yield wsi.read_region(coords, 0, self.patch_size), coords\n                    self.x += self.step_size\n                self.x = 0\n                self.y += self.step_size\n\n\nclass GridSearcher:\n    def __init__(\n            self,\n            paths_to_wsis: List[str],\n            step_size: Optional[int] = 512,\n            white_area_score: Optional[int] = 240,\n            max_white_area_mean: Optional[int] = 0.9,\n            path_to_masks: Optional[str] = None,\n    ):\n        self.paths_to_wsis = paths_to_wsis\n        self.path_to_masks = path_to_masks\n        self.step_size = step_size\n        self.white_area_score = white_area_score\n        self.max_white_area_mean = max_white_area_mean\n\n    def make_grid_mp(self):\n        cpu_count = os.cpu_count()\n        with Pool(processes=cpu_count) as p:\n            dfs = list(tqdm(p.imap(self.make_grid, self.paths_to_wsis), total=len(self.paths_to_wsis)))\n        df = pd.concat(dfs)\n        return df\n\n    def make_grid(self, path_to_wsi):\n        path_to_mask = None\n        wsi_filename = path_to_wsi.split('/')[-1]\n        if self.path_to_masks is not None:\n            mask_filename = wsi_filename.replace(\".\", \"_mask.\")\n            path_to_mask = os.path.join(self.path_to_masks, mask_filename)\n            if not os.path.exists(path_to_mask):\n                path_to_mask = None\n        if path_to_mask:\n            df = self.make_grid_from_wsi_and_mask(\n                path_to_wsi=path_to_wsi,\n                path_to_mask=path_to_mask,\n                step_size=self.step_size,\n                white_area_score=self.white_area_score,\n                max_white_area_mean=self.max_white_area_mean,\n                wsi_filename=wsi_filename,\n            )\n        else:\n            df = self.make_grid_from_wsi_only(\n                path_to_wsi=path_to_wsi,\n                step_size=self.step_size,\n                white_area_score=self.white_area_score,\n                max_white_area_mean=self.max_white_area_mean,\n                wsi_filename=wsi_filename\n            )\n        return df\n\n    @staticmethod\n    def make_grid_from_wsi_only(\n            path_to_wsi: str,\n            step_size: int,\n            white_area_score: int,\n            max_white_area_mean: int,\n            wsi_filename: str,\n    ):\n        patch_slicer = PatchSlicer(path_to_wsi=path_to_wsi, step_size=step_size)\n        patches_generator = patch_slicer.patch_generator()\n        good_patches_coords = []\n        for patch, coords in patches_generator:\n            patch_array = np.array(patch)[:, :, :3]\n            patch_mean_pixel_value = patch_array.mean(axis=2)\n            if (patch_mean_pixel_value > white_area_score).mean() > max_white_area_mean:\n                continue\n            else:\n                good_patches_coords.extend([coords])\n        df = GridSearcher.patches_coords_and_targets_to_df(\n            good_patches_coords=good_patches_coords,\n            wsi_filename=wsi_filename,\n        )\n        return df\n\n    @staticmethod\n    def make_grid_from_wsi_and_mask(\n            path_to_wsi: str,\n            path_to_mask: str,\n            step_size: int,\n            white_area_score: int,\n            max_white_area_mean: int,\n            wsi_filename: str\n    ):\n        wsi_patch_slicer = PatchSlicer(path_to_wsi=path_to_wsi, step_size=step_size)\n        wsi_patches_generator = wsi_patch_slicer.patch_generator()\n        mask_patch_slicer = PatchSlicer(path_to_wsi=path_to_mask, step_size=step_size)\n        mask_patches_generator = mask_patch_slicer.patch_generator()\n        good_patches_coords = []\n        targets = []\n        for (wsi_patch, wsi_coords), (mask_patch, _) in zip(wsi_patches_generator, mask_patches_generator):\n            patch_array = np.array(wsi_patch)[:, :, :3]\n            mask_array = np.array(mask_patch)[:, :, 0]\n            non_zero_mask_array = (mask_array > 0).astype(int)\n            if non_zero_mask_array.sum():\n                patch_mean_pixel_value = patch_array.mean(axis=2)\n                if (patch_mean_pixel_value > white_area_score).mean() > max_white_area_mean:\n                    continue\n                good_patches_coords.extend([wsi_coords])\n                unique_mask_values = np.unique(mask_array)\n                targets.extend([unique_mask_values])\n        if not targets:\n            targets = None\n        df = GridSearcher.patches_coords_and_targets_to_df(\n            good_patches_coords=good_patches_coords,\n            targets=targets,\n            wsi_filename=wsi_filename,\n        )\n        return df\n\n    @staticmethod\n    def patches_coords_and_targets_to_df(\n            good_patches_coords: List[Tuple[int, int]],\n            wsi_filename: str,\n            targets: Optional[List[List[int]]] = None,\n    ):\n        df = pd.DataFrame({\"patches_coords\": good_patches_coords})\n        df[\"targets\"] = targets\n        df['wsi_filename'] = wsi_filename\n        return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path_to_wsis = \"../input/prostate-cancer-grade-assessment/train_images/\"\npath_to_masks = \"../input/prostate-cancer-grade-assessment/train_label_masks/\"\n\nwsi_filenames = os.listdir(path_to_wsis)\n\n\ngs = GridSearcher(\n    paths_to_wsis = [os.path.join(path_to_wsis, wsi_filename) for wsi_filename in wsi_filenames[:8]],\n    path_to_masks = path_to_masks\n)\ndf = gs.make_grid_mp()\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df['targets'].astype(str).value_counts()","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}