{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":45867,"databundleVersionId":6924515,"sourceType":"competition"},{"sourceId":6774400,"sourceType":"datasetVersion","datasetId":3895136},{"sourceId":6984590,"sourceType":"datasetVersion","datasetId":4014175}],"dockerImageVersionId":30558,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Generate tiles for training","metadata":{"_uuid":"33deb012-9cb3-43a3-b193-783327918c42","_cell_guid":"84130fd3-6f12-470b-ba70-74109455e404","trusted":true}},{"cell_type":"markdown","source":"Code adapted from:\n\nhttps://www.kaggle.com/code/jirkaborovec/cancer-subtype-lightning-torch-inference-tiles\n\nhttps://www.kaggle.com/code/sakima/medical-whole-slide-processing-speed-up","metadata":{"_uuid":"df9c60d9-6f6e-4348-98ca-e312a9dd219e","_cell_guid":"d3aac531-a0f7-4616-82ad-ce01ca3df58b","trusted":true}},{"cell_type":"code","source":"# intall the deb packages\n!dpkg -i --force-depends /kaggle/input/pyvips-python-and-deb-package/linux_packages/archives/*.deb\n# install the python wrapper\n!pip install pyvips -f /kaggle/input/pyvips-python-and-deb-package/python_packages/ --no-index","metadata":{"_uuid":"04628543-3936-4c64-81e1-d18527d22960","_cell_guid":"39f530e4-7f85-4433-bdb6-d994c676d3cb","_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-11-22T01:22:31.451127Z","iopub.execute_input":"2023-11-22T01:22:31.451514Z","iopub.status.idle":"2023-11-22T01:23:39.267795Z","shell.execute_reply.started":"2023-11-22T01:22:31.451487Z","shell.execute_reply":"2023-11-22T01:23:39.266037Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configurations for tile generation","metadata":{"_uuid":"489ae044-25cc-40e4-a0b4-b8b7c77b4c12","_cell_guid":"9706949f-53f1-4c70-bf0e-140b90fbf335","trusted":true}},{"cell_type":"code","source":"BATCH_IDX = 6\nNUM_BATCHES = 7\nDATASET_IMAGES = \"/kaggle/input/UBC-OCEAN/train_images\"\nDATASET_ANNOTATIONS = \"/kaggle/input/ubc-ovarian-cancer-competition-supplemental-masks\"\nTILE_SIZE = 512 #224\nSCALE = 0.4 #0.175\nWHITE_THR = 240\nDROP_THR = 0.5 #0.6 #0.8\nTMA_THR = 5000 # TMA images usually have length around ~3000 pixels\nINDEX_TO_LABEL = [\"Tumor\", \"Stroma\", \"Necrosis\", \"Unlabeled\"]\n# LABEL_TO_INDEX = {\"Tumor\": 0, \"Stroma\": 1, \"Necrosis\": 2, \"Unlabeled\": 3}\nSAVE_FOLDER = f\"/kaggle/working/UBC-OCEAN_train_tiles_size{TILE_SIZE}_scale{SCALE}/batch{BATCH_IDX}\"","metadata":{"_uuid":"6b6012d4-0cdb-453d-8056-9f7acf5d4882","_cell_guid":"abeef334-760d-4384-a9c9-0a21e13806e4","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-11-22T01:23:39.271983Z","iopub.execute_input":"2023-11-22T01:23:39.272308Z","iopub.status.idle":"2023-11-22T01:23:39.279198Z","shell.execute_reply.started":"2023-11-22T01:23:39.272282Z","shell.execute_reply":"2023-11-22T01:23:39.277996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Functions for generating tiles","metadata":{"_uuid":"58e31f78-70ce-4990-b3ea-53b213273773","_cell_guid":"6b64b539-e03d-4041-8e83-d0a6d17b9763","trusted":true}},{"cell_type":"code","source":"import os\nimport pyvips\nimport numpy as np\nfrom typing import List, Tuple, Union\n\nos.environ['VIPS_DISC_THRESHOLD'] = '15gb'\nos.environ['VIPS_CONCURRENCY'] = '4'\n\n\ndef is_tma(p_img: pyvips.vimage.Image, tma_thr: int) -> bool:\n    # Determine whether image is TMA by checking its longer dimension\n    img = pyvips.Image.new_from_file(p_img)\n    return max(img.width, img.height) < tma_thr\n    \n\ndef load_and_resize_img(p_img: pyvips.vimage.Image, scale: float) -> pyvips.vimage.Image:\n    if not os.path.isfile(p_img):\n        return None\n    img = pyvips.Image.new_from_file(p_img)\n#     img = img.resize(scale, kernel='lanczos2')\n    img = pyvips.Image.thumbnail(p_img, int(scale * img.width)) # Faster than resize\n    return img.copy_memory() # Needed when opening image using thumbnail\n\n\ndef find_subintervals(\n    binary_list: List[Union[int, float]], \n    target_digit: int,\n) -> List[Tuple[int, int]]:\n    subintervals = []\n    start = None\n\n    for i, digit in enumerate(binary_list):\n        if digit == target_digit:\n            if start is None:\n                start = i\n        elif start is not None:\n            subintervals.append((start, i - 1))\n            start = None\n\n    if start is not None:\n        subintervals.append((start, len(binary_list) - 1))\n\n    return subintervals\n\n\ndef get_subimg_x_intervals(img: pyvips.vimage.Image) -> List[Tuple[int, int]]:\n    is_empty_column = (img.rot90().fliphor().bandfold().bandmean() == 0).numpy().squeeze() / 255\n    return find_subintervals(is_empty_column, target_digit=0)\n\n\ndef get_tile_img(\n    img: pyvips.vimage.Image,\n    x: int,\n    y: int,\n    tile_size: int,\n) -> pyvips.vimage.Image:\n    return img.crop(x, y, min(tile_size, img.width - x), min(tile_size, img.height - y)\n       ).gravity('north-west', tile_size, tile_size, extend='black')\n\n\ndef get_tile_pos(\n    x: int, \n    y: int,\n    x_start: int,\n    tile_size: int,\n) -> Tuple[int, int]:\n    return (\n        int((x - x_start) / tile_size + 1), \n        int(y / tile_size + 1),\n    ) \n    \n    \ndef get_tile_label(\n    seg: pyvips.vimage.Image, \n    x: int, \n    y: int, \n    tile_size: int,\n) -> str:\n    tile_seg = get_tile_img(seg, x, y, tile_size)\n    tile_seg = (tile_seg > 127) / 255\n    tumor_pct = tile_seg[0].avg()                                # Red: Tumor\n    stroma_pct = tile_seg[1].avg()                               # Green: Stroma\n    necrosis_pct = tile_seg[2].avg()                             # Blue: Necrosis\n    unlabeled_pct = 1 - tumor_pct - stroma_pct - necrosis_pct    # Black: Unlabeled\n    label_idx = np.argmax([tumor_pct, stroma_pct, necrosis_pct, unlabeled_pct])\n    return INDEX_TO_LABEL[label_idx]\n\n    \ndef is_background_tile(\n    tile: pyvips.vimage.Image,\n    white_thr: int,\n    drop_thr: float,\n) -> bool:\n    mean_tile = tile.bandmean()\n    mask_bg = (mean_tile == 0).bandjoin(mean_tile > white_thr).bandor()    \n    return (mask_bg.avg() / 255) > drop_thr\n\n\ndef save_tile(\n    tile_img: pyvips.vimage.Image,\n    folder: str,\n    subimg_idx: int, \n    label: str, \n    tile_idx: int,\n    pos: Tuple[int, int],\n):\n    fname = os.path.join(\n        folder, \n        f\"subimg{subimg_idx:02}\",\n        label, \n        f\"{tile_idx:06}_{pos[0]}-{pos[1]}.png\",\n    )\n    os.makedirs(os.path.dirname(fname), exist_ok=True)                        \n    tile_img.write_to_file(fname)\n\n\ndef get_tiles_wsi(\n    img: pyvips.vimage.Image,\n    seg: pyvips.vimage.Image,\n    x_intervals: List[Tuple[int, int]], \n    tile_size: int,\n    white_thr: int,\n    drop_thr: float,\n    folder: str,\n):\n    # Get tiles for each subimage. The empty columns in the image define and separate the image into subimages\n    subimg_idx = 0\n    for x_interval in x_intervals:\n        tile_idx = 0\n        for y in range(0, img.height, tile_size):\n            for x in range(x_interval[0], x_interval[1] + 1, tile_size):\n                tile_img = get_tile_img(img, x, y, tile_size)\n                if not is_background_tile(tile_img, white_thr, drop_thr):\n                    pos = get_tile_pos(x, y, x_interval[0], tile_size)\n                    if seg is None:\n                        label = \"Unlabeled\"\n                    else:\n                        label = get_tile_label(seg, x, y, tile_size)\n                    save_tile(tile_img, folder, subimg_idx, label, tile_idx, pos)                        \n                    tile_idx += 1\n        if tile_idx > 0:\n            subimg_idx += 1\n               \n\ndef get_tiles_tma(\n    img: pyvips.vimage.Image,\n    tile_size: int,\n    folder: str,\n):\n    # Get center crop of the image\n    w, h = img.width, img.height\n    x = 0 if w < tile_size else (w - tile_size) // 2\n    y = 0 if h < tile_size else (h - tile_size) // 2\n    tile_img = img.crop(x, y, min(tile_size, w - x), min(tile_size, h - y)\n        ).gravity('centre', tile_size, tile_size, extend='copy')\n    save_tile(tile_img, folder, subimg_idx=0, label=\"Tumor\", tile_idx=0, pos=(1, 1))\n            \n            \ndef make_tiles_png(\n    p_img: str, \n    p_seg: str,\n    folder: str, \n    tile_size: int,\n    scale: float, \n    white_thr: float, \n    drop_thr: float,\n    tma_thr: int,\n):\n    tma = is_tma(p_img, tma_thr)\n    if tma:\n        scale *= 0.5   # TMA has 40x magnification while WSI has 20x magnification\n        img = load_and_resize_img(p_img, scale)\n        get_tiles_tma(img, tile_size, folder)\n    else:\n        img = load_and_resize_img(p_img, scale)\n        seg = load_and_resize_img(p_seg, scale)\n        x_intervals = get_subimg_x_intervals(img)\n        get_tiles_wsi(img, seg, x_intervals, tile_size, white_thr, drop_thr, folder)","metadata":{"_uuid":"f475bec5-84f4-4a9b-990d-c33882875053","_cell_guid":"6b698f11-b947-4c63-b86f-f769e8f70640","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-11-22T01:23:39.281227Z","iopub.execute_input":"2023-11-22T01:23:39.281808Z","iopub.status.idle":"2023-11-22T01:23:39.659561Z","shell.execute_reply.started":"2023-11-22T01:23:39.281759Z","shell.execute_reply":"2023-11-22T01:23:39.657711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Function for visualising tiles","metadata":{"_uuid":"fc124700-128f-478c-a4e2-f2f2a9161d26","_cell_guid":"8865d0e2-7d26-46c8-8784-c5e05eadb477","trusted":true}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport glob\nfrom PIL import Image\n\ndef plot_tiles(folder):\n    LABEL_COLOR = {\"Tumor\": 0, \"Stroma\": 1, \"Necrosis\": 2, \"Unlabeled\": 3}\n    subimgs = os.listdir(folder)\n\n    for subimg in subimgs:\n        print(subimg)\n        tiles_img = glob.glob(os.path.join(folder, subimg, '*/*.png'))\n\n        names = [os.path.splitext(os.path.basename(p_img))[0] for p_img in tiles_img]\n        labels = [p_img.split('/')[-2] for p_img in tiles_img]\n        pos = [name.split(\"_\")[-1] for name in names]\n        idx_x, idx_y = zip(*[list(map(int, p.split(\"-\"))) for p in pos])\n        nb_rows = max(set(idx_y))\n        nb_cols = max(set(idx_x))\n        print(f\"{nb_rows=}\\n{nb_cols=}\")\n\n        is_single_img = (nb_rows == 1) and (nb_cols == 1)\n        fig, axes = plt.subplots(nrows=nb_rows, ncols=nb_cols,\n            figsize=(nb_cols * 0.5, nb_rows * 0.5) if not is_single_img else (5, 5)\n        )\n        for i in range(nb_rows):\n            for j in range(nb_cols):\n                ax = axes[i, j] if not is_single_img else axes\n                ax.set_facecolor(\"aqua\")\n                ax.set_xticklabels([])\n                ax.set_yticklabels([])\n\n        for p_img, x, y, label in zip(tiles_img, idx_x, idx_y, labels):\n            img = Image.open(p_img)\n            if label != \"Unlabeled\" and isinstance(axes, np.ndarray):\n                mask = np.zeros_like(img)\n                mask[:, :, LABEL_COLOR[label]] = 255\n                mask = Image.fromarray(mask)\n                img = Image.blend(img, mask, alpha=0.3)\n            ax = axes[y - 1, x - 1] if not is_single_img else axes\n            ax.imshow(img)\n        print(f\"tile size: {img.size}\")\n        print(f\"number of tiles: {len(tiles_img)}\")\n\n        plt.subplots_adjust(wspace=0, hspace=0)\n        # # fig.tight_layout()","metadata":{"_uuid":"87d0243a-7079-4b63-8ca7-29b362883335","_cell_guid":"a379a26b-e54e-462b-9241-8015ca0c5fe4","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-11-22T01:23:39.661578Z","iopub.execute_input":"2023-11-22T01:23:39.661864Z","iopub.status.idle":"2023-11-22T01:23:39.677267Z","shell.execute_reply.started":"2023-11-22T01:23:39.66184Z","shell.execute_reply":"2023-11-22T01:23:39.675284Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generate tile for one WSI sample","metadata":{"_uuid":"c5144a5e-e1f2-4b4c-8e02-cb136011d05c","_cell_guid":"84628300-c2d2-4058-b291-cbbe7ad6e112","trusted":true}},{"cell_type":"code","source":"%%time\n\n!rm -rf /kaggle/temp\n\n# image_id = \"10642\"\n# image_id = \"35239\"\n# image_id = \"63941\"\nimage_id = \"34277\"\n# image_id = \"35565\"  # TMA\n\nmake_tiles_png(\n    os.path.join(DATASET_IMAGES, f\"{image_id}.png\"), \n    os.path.join(DATASET_ANNOTATIONS, f\"{image_id}.png\"), \n    os.path.join(\"/kaggle/temp\", image_id),\n    TILE_SIZE, \n    SCALE, \n    WHITE_THR,\n    DROP_THR,\n    TMA_THR,\n)","metadata":{"_uuid":"ffab171a-3657-4c5f-b3e3-b1ec2780be1a","_cell_guid":"d2b56f66-d23c-4132-ae8b-f7baea29056f","collapsed":false,"execution":{"iopub.status.busy":"2023-11-21T16:43:11.550154Z","iopub.execute_input":"2023-11-21T16:43:11.55087Z","iopub.status.idle":"2023-11-21T16:46:55.935399Z","shell.execute_reply.started":"2023-11-21T16:43:11.55081Z","shell.execute_reply":"2023-11-21T16:46:55.933881Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualise the WSI tiles","metadata":{"_uuid":"faf5bdb0-cf07-4354-8beb-e510084c7333","_cell_guid":"50c2dc10-5d11-4f91-bef4-d19dd80054c2","trusted":true}},{"cell_type":"code","source":"plot_tiles(os.path.join('/kaggle/temp', image_id))","metadata":{"_uuid":"0c9fc811-67be-45e0-9d96-12f5a355c2a9","_cell_guid":"9d16a13b-3a05-4155-a1a7-7afdd1ec071c","collapsed":false,"execution":{"iopub.status.busy":"2023-11-21T16:46:55.939321Z","iopub.execute_input":"2023-11-21T16:46:55.939896Z","iopub.status.idle":"2023-11-21T16:48:34.216898Z","shell.execute_reply.started":"2023-11-21T16:46:55.939855Z","shell.execute_reply":"2023-11-21T16:48:34.215295Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualise one WSI tile","metadata":{"_uuid":"02406471-9836-440c-996e-041bae7727f4","_cell_guid":"3c88a76f-0ab2-4afe-8b15-e2e13369a407","trusted":true}},{"cell_type":"code","source":"p = glob.glob(f'/kaggle/temp/{image_id}/subimg00/Tumor/*.png')[0]\nplt.figure(figsize=(5, 5))\nplt.imshow(plt.imread(p));","metadata":{"_uuid":"3fe0a727-f60a-4dba-8a7e-f9a1025e56f7","_cell_guid":"7541b7b2-865a-4e75-bef6-802dfaee3fda","collapsed":false,"execution":{"iopub.status.busy":"2023-11-21T16:48:34.218954Z","iopub.execute_input":"2023-11-21T16:48:34.219805Z","iopub.status.idle":"2023-11-21T16:48:34.740164Z","shell.execute_reply.started":"2023-11-21T16:48:34.219754Z","shell.execute_reply":"2023-11-21T16:48:34.738921Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generate tile for one TMA sample","metadata":{"_uuid":"37529741-3032-42fa-b286-0073a8681f30","_cell_guid":"3c068951-929e-43b8-9279-26d3faa49bde","trusted":true}},{"cell_type":"code","source":"%%time\n\n!rm -rf /kaggle/temp\n\nimage_id = \"35565\"  # TMA\n\nmake_tiles_png(\n    os.path.join(DATASET_IMAGES, f\"{image_id}.png\"), \n    os.path.join(DATASET_ANNOTATIONS, f\"{image_id}.png\"), \n    os.path.join(\"/kaggle/temp\", image_id),\n    TILE_SIZE, \n    SCALE, \n    WHITE_THR,\n    DROP_THR,\n    TMA_THR,\n)","metadata":{"_uuid":"59ed3ba3-9b90-4d44-bcc3-f87a50ba5b4f","_cell_guid":"b9a5f9e0-3878-4ac6-91bd-e04b794d9ad6","collapsed":false,"execution":{"iopub.status.busy":"2023-11-21T16:48:34.743161Z","iopub.execute_input":"2023-11-21T16:48:34.74395Z","iopub.status.idle":"2023-11-21T16:48:36.528513Z","shell.execute_reply.started":"2023-11-21T16:48:34.743899Z","shell.execute_reply":"2023-11-21T16:48:36.527067Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualise one TMA tile","metadata":{"_uuid":"76286226-2ef4-491d-a49d-ef88f4c75326","_cell_guid":"f9398002-9fd6-47d9-9818-da6b420cf51e","trusted":true}},{"cell_type":"code","source":"plot_tiles(os.path.join('/kaggle/temp', image_id))","metadata":{"_uuid":"fdcdbd2c-16ba-4214-989a-6cd7ee94ec9c","_cell_guid":"a0768c07-1147-4462-9d16-de30c9c19534","collapsed":false,"execution":{"iopub.status.busy":"2023-11-21T16:48:36.531106Z","iopub.execute_input":"2023-11-21T16:48:36.535174Z","iopub.status.idle":"2023-11-21T16:48:36.961986Z","shell.execute_reply.started":"2023-11-21T16:48:36.535071Z","shell.execute_reply":"2023-11-21T16:48:36.960505Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!rm -rf /kaggle/temp","metadata":{"_uuid":"e900734a-728b-4f46-8e6f-b758e54580d8","_cell_guid":"4a1c70c0-d3c9-4f44-81bf-122fbe6c3e90","collapsed":false,"execution":{"iopub.status.busy":"2023-11-21T14:53:20.513058Z","iopub.execute_input":"2023-11-21T14:53:20.513515Z","iopub.status.idle":"2023-11-21T14:53:21.657873Z","shell.execute_reply.started":"2023-11-21T14:53:20.513474Z","shell.execute_reply":"2023-11-21T14:53:21.655871Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Export all image tiles¶","metadata":{"_uuid":"b9a5abc0-55d5-450a-b240-672444001a15","_cell_guid":"62c5b806-f8b2-4221-a9fc-897f0f0602ee","trusted":true}},{"cell_type":"code","source":"import gc, time\n\ndef extract_tiles(\n    idx_image_id: Tuple[int, str],\n    p_img: str = DATASET_IMAGES,\n    p_seg: str = DATASET_ANNOTATIONS,\n    folder: str = SAVE_FOLDER, \n    tile_size: int = TILE_SIZE,\n    scale: float = SCALE, \n    white_thr: float = WHITE_THR, \n    drop_thr: float = DROP_THR,\n    tma_thr: int = TMA_THR,\n) -> None:\n    idx, image_id = idx_image_id\n    print(f\"processing #{idx}: {image_id}\")\n    make_tiles_png(\n        os.path.join(p_img, f\"{image_id}.png\"), \n        os.path.join(p_seg, f\"{image_id}.png\"), \n        os.path.join(folder, image_id),\n        tile_size, \n        scale, \n        white_thr,\n        drop_thr,\n        tma_thr,\n    )\n    gc.collect()\n    time.sleep(1)","metadata":{"_uuid":"ac218062-801d-4014-9316-7abca107fc13","_cell_guid":"89ac92af-5c6f-48ad-8d37-6c615f94c7ee","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-11-22T01:23:39.678985Z","iopub.execute_input":"2023-11-22T01:23:39.679408Z","iopub.status.idle":"2023-11-22T01:23:39.696381Z","shell.execute_reply.started":"2023-11-22T01:23:39.67937Z","shell.execute_reply":"2023-11-22T01:23:39.694915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\nimport multiprocessing as mproc\nfrom tqdm.auto import tqdm\nfrom joblib import Parallel, delayed\n\nls = glob.glob(os.path.join(DATASET_IMAGES, '*.png'))\nls = sorted(ls)\nls = np.array_split(ls, NUM_BATCHES)[BATCH_IDX]\n\nimage_ids = [os.path.splitext(os.path.basename(p))[0] for p in ls]\nprint(f\"found images: {len(image_ids)}\")\n\n# pool = mproc.Pool(1)\n# tqdm_bar = tqdm(total=len(image_ids))\n# for _ in pool.imap_unordered(extract_tiles, enumerate(image_ids)):\n#     tqdm_bar.update()\n# pool.close()\n# pool.join()\n\n_ = Parallel(n_jobs=os.cpu_count())(\n    delayed(extract_tiles)\n    (idx_image_id)\n    for idx_image_id in tqdm(enumerate(image_ids))\n#     for idx_image_id in enumerate(image_ids)\n)\n\nprint(f\"All {len(image_ids)} images processed successfully!\")","metadata":{"_uuid":"97eea326-c629-4f88-ad2a-e03f39100bdf","_cell_guid":"0519a546-2e2d-4973-b767-1d0cffc9aba6","collapsed":false,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-11-21T20:44:07.103345Z","iopub.execute_input":"2023-11-21T20:44:07.10386Z","iopub.status.idle":"2023-11-21T20:57:11.244154Z","shell.execute_reply.started":"2023-11-21T20:44:07.103822Z","shell.execute_reply":"2023-11-21T20:57:11.242611Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualise some tiles","metadata":{"_uuid":"900f8348-02fd-4f80-a8ba-47803438ba3e","_cell_guid":"91cd8325-4b64-4757-b7e5-6dc45015ba7c","trusted":true}},{"cell_type":"code","source":"ls = [p for p in glob.glob(os.path.join(SAVE_FOLDER, '*')) if os.path.isdir(p)]\nprint(f\"found folders or images: {len(ls)}\")\nls = glob.glob(os.path.join(SAVE_FOLDER, '*/*/*/*.png'))\nprint(f\"found tiles: {len(ls)}\")\n\nfig, axes = plt.subplots(nrows=5, ncols=5, figsize=(10, 10))\nfor i, p_img in enumerate(ls[:25]):\n    img = plt.imread(p_img)\n    ax = axes[i // 5, i % 5]\n    ax.imshow(img)\n    #ax.set_axis_off()","metadata":{"_uuid":"e444fa5e-3ee4-4de7-b002-fc2d3724765a","_cell_guid":"8d743352-2812-4acb-abf7-df3aa882ec0a","collapsed":false,"execution":{"iopub.status.busy":"2023-11-21T18:44:35.87202Z","iopub.execute_input":"2023-11-21T18:44:35.87246Z","iopub.status.idle":"2023-11-21T18:44:40.050055Z","shell.execute_reply.started":"2023-11-21T18:44:35.872428Z","shell.execute_reply":"2023-11-21T18:44:40.04897Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]}]}