{"metadata":{"kaggle":{"accelerator":"none","dataSources":[{"sourceId":45867,"databundleVersionId":6924515,"sourceType":"competition"},{"sourceId":6774553,"sourceType":"datasetVersion","datasetId":3898019},{"sourceId":6774400,"sourceType":"datasetVersion","datasetId":3895136}],"dockerImageVersionId":30626,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"},"papermill":{"default_parameters":{},"duration":272.301432,"end_time":"2024-01-03T02:37:28.592037","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2024-01-03T02:32:56.290605","version":"2.4.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import shutil\nimport os\nimport torch\n\n# TEMP FIX FOR TMP USAGE FROM https://www.kaggle.com/discussions/product-feedback/461401#2562671\nif not os.path.exists('/kaggle/working/tmp'):\n    os.mkdir('/kaggle/working/tmp')\nos.environ['TMPDIR'] = '/kaggle/working/tmp'\n!export TMPDIR='/kaggle/working/tmp'\n\nimport PIL\nPIL.Image.MAX_IMAGE_PIXELS = None\n\n# Directories to store tiles\nfor path in [\"/tmp/images\"]:\n    if not os.path.exists(path):\n        os.mkdir(path)","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":8.079857,"end_time":"2024-01-03T02:33:07.92892","exception":false,"start_time":"2024-01-03T02:32:59.849063","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-04T14:49:39.911624Z","iopub.execute_input":"2024-01-04T14:49:39.912074Z","iopub.status.idle":"2024-01-04T14:49:44.525982Z","shell.execute_reply.started":"2024-01-04T14:49:39.912042Z","shell.execute_reply":"2024-01-04T14:49:44.524259Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## UBCO Tiling Code\n\nCode used to select Tiles from WSIs and TMAs in the [22nd Place Solution - UBC-OCEAN](https://www.kaggle.com/competitions/UBC-OCEAN/discussion/465356).","metadata":{}},{"cell_type":"code","source":"%%capture\nif torch.cuda.is_available():\n    # ------------------- GPU ---------------------------\n    # https://www.kaggle.com/datasets/jirkaborovec/pyvips-python-and-deb-package-gpu\n    # intall the deb packages\n    !yes | dpkg -i --force-depends /kaggle/input/pyvips-python-and-deb-package-gpu/linux_packages/archives/*.deb\n    # install the python wrapper\n    !pip install pyvips -f /kaggle/input/pyvips-python-and-deb-package-gpu/python_packages/ --no-index\n\nelse:\n    # ------------------- CPU ---------------------------\n    !ls /kaggle/input/pyvips-python-and-deb-package\n    # 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\n    !pip list | grep pyvips","metadata":{"papermill":{"duration":63.929894,"end_time":"2024-01-03T02:34:11.8654","exception":false,"start_time":"2024-01-03T02:33:07.935506","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-04T14:49:44.529143Z","iopub.execute_input":"2024-01-04T14:49:44.52996Z","iopub.status.idle":"2024-01-04T14:51:32.300272Z","shell.execute_reply.started":"2024-01-04T14:49:44.529917Z","shell.execute_reply":"2024-01-04T14:51:32.298441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Utils","metadata":{"papermill":{"duration":0.006048,"end_time":"2024-01-03T02:34:11.878595","exception":false,"start_time":"2024-01-03T02:34:11.872547","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def clean_working(directory_path: str = \"/kaggle/working/\"):\n    \"\"\"\n    Clean kaggle output directory.\n    \"\"\"\n    if os.path.exists(directory_path):\n        for item in os.listdir(directory_path):\n            if item == \"submission.csv\":\n                continue\n            item_path = os.path.join(directory_path, item)\n            os.remove(item_path) if os.path.isfile(item_path) else shutil.rmtree(item_path)\n        print(f\"All items in '{directory_path}' have been removed.\")\n    else:\n        print(f\"'{directory_path}' does not exist.\")","metadata":{"papermill":{"duration":0.01575,"end_time":"2024-01-03T02:34:11.900393","exception":false,"start_time":"2024-01-03T02:34:11.884643","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-04T14:51:32.302815Z","iopub.execute_input":"2024-01-04T14:51:32.303827Z","iopub.status.idle":"2024-01-04T14:51:32.313633Z","shell.execute_reply.started":"2024-01-04T14:51:32.303766Z","shell.execute_reply":"2024-01-04T14:51:32.311706Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Script 1: Select Tiles","metadata":{"papermill":{"duration":0.005854,"end_time":"2024-01-03T02:34:11.912247","exception":false,"start_time":"2024-01-03T02:34:11.906393","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%writefile tile_images.py\n\nimport os\nfrom functools import reduce\nos.environ['VIPS_DISC_THRESHOLD'] = '150gb'\nos.environ['VIPS_CONCURRENCY'] = '4'\n\nimport numpy as np\nimport pandas as pd\nimport skimage.io as io\nfrom scipy.ndimage import zoom\nimport albumentations as A\nimport cv2\n\nimport pyvips\nimport heapq\nimport gc\nimport os\nimport time\nfrom datetime import timedelta\nimport multiprocessing as mp\nfrom types import SimpleNamespace\n\nconfig = SimpleNamespace(\n    num_tiles = 14,\n    tile_size = 1280, # Fails on >= 742 (Need to change TMA crop function if we want larger)\n    benchmark_run = False,\n    test_image_n = 10,\n    image_path = \"/kaggle/input/UBC-OCEAN/train_images/{}.png\",\n    thumbnail_path = \"/kaggle/input/UBC-OCEAN/train_thumbnails/{}_thumbnail.png\",\n)\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    config.image_path = \"/kaggle/input/UBC-OCEAN/test_images/{}.png\"\n    config.thumbnail_path = \"/kaggle/input/UBC-OCEAN/test_thumbnails/{}_thumbnail.png\"\n\ndef load_idxs():\n    # Submit Run\n    if os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n        df = pd.read_csv(\"/kaggle/input/UBC-OCEAN/test.csv\")        \n        df[\"size\"] = df[\"image_height\"] * df[\"image_width\"]\n        df[\"is_tma\"] = df[\"size\"] < 9000*9000\n\n    # Regular Run\n    else:\n        df = pd.read_csv(\"/kaggle/input/UBC-OCEAN/train.csv\")\n        df[\"size\"] = df[\"image_height\"] * df[\"image_width\"]\n        df = df.sort_values([\"is_tma\", \"size\"], ascending=[True, True]).groupby(\"is_tma\").head(config.test_image_n)\n#         df = df[df.is_tma == True]\n            \n    return df[\"image_id\"].values, df[\"is_tma\"].values\n\ndef largest_nonzero_seq(arr):\n    \"\"\"\n    Find largest nonzero sequence in 1D numpy array.\n    \"\"\"\n    # Find non-zero sequences\n    non_zero_mask = (arr != 0).astype(int)\n    seq_arr = np.diff(np.concatenate(([0], non_zero_mask, [0])))\n    \n    # Find start + end idxs\n    seq_start = np.where(seq_arr == 1)[0]\n    seq_end = np.where(seq_arr == -1)[0]\n    \n    # Find longest seq\n    non_zero_lengths = seq_end - seq_start\n    seq_idx = np.argmax(non_zero_lengths)\n    \n    return seq_start[seq_idx], seq_end[seq_idx]\n\ndef crop_image(thumb_image, threshold=0.15, viz=False):\n    \"\"\"\n    Greedy method to crop largest object from WSI thumbnail image.\n    \"\"\"\n    # Load thumbnail\n    height, width = thumb_image.shape[0], thumb_image.shape[1]\n    \n    # Calculating X boundaries\n    vs = np.sum(np.sum(thumb_image, axis=0), axis=-1)\n    left, right = largest_nonzero_seq(vs)\n    \n    # Calculating Y boundaries\n    bottom, top = 0, 0\n    vs = np.sum(np.sum(thumb_image[:, left:right, :], axis=1), axis=-1)\n    top, bottom = largest_nonzero_seq(vs)\n    \n    # Calculate crop ratio (for loading larger image)\n    crop_ratios = (top/height, bottom/height, left/width, right/width)\n    return crop_ratios\n\ndef tune_crop_ratio(image, crop_ratios):\n    \"\"\"\n    Updates crop ratios to be divisible by tile size.\n    \"\"\"\n    # Range after Crop 1\n    top = int(image.height*crop_ratios[0])\n    bottom = int(image.height*crop_ratios[1])\n    left = int(image.width*crop_ratios[2])\n    right = int(image.width*crop_ratios[3])\n\n    # Crop 2: Crop to multiple of tile size\n    h_diff = (bottom - top) % config.tile_size\n    w_diff = (right - left) % config.tile_size\n\n    # Trim from each side\n    left += w_diff//2\n    right -= w_diff//2 + (w_diff%2)\n    top += h_diff//2\n    bottom -= h_diff//2 + (h_diff%2)\n    \n#     # DEBUG\n#     print(\"diffs\", h_diff, w_diff)\n#     print(top, bottom, left, right)\n#     print(\"should be zero: \", (bottom - top)%config.tile_size, (right - left)%config.tile_size)\n#     print(\"processed size: {:_} x {:_}\".format(right - left, bottom - top))\n    \n    # Updated ratios\n    crop_ratios = (top/image.height, bottom/image.height, left/image.width, right/image.width)\n    width_tiles_n = (right - left) / config.tile_size\n    height_tiles_n = (bottom - top) / config.tile_size\n    \n    return crop_ratios, width_tiles_n, height_tiles_n, top, bottom, left, right\n\ndef get_xy_pairs(img_id, thumb_image, crop_ratios, width_tiles_n, height_tiles_n, left, top, debug=False):\n    \n    # Crop to updated ratio\n    height, width = thumb_image.shape[0], thumb_image.shape[1]\n    crop = thumb_image[\n        int(crop_ratios[0]*height):int(crop_ratios[1]*height), \n        int(crop_ratios[2]*width):int(crop_ratios[3]*width), \n        :,\n    ]\n    \n    # Find black pixels and replace them with white\n    black_pixels = np.all(crop == [0, 0, 0], axis=-1) # Replace mask pixels w/ white\n    crop[black_pixels] = np.array([255, 255, 255])\n    \n    # Resize to a multiple of tiles in full image\n    new_height = next_largest_divisible(crop.shape[0], height_tiles_n)\n    new_width = next_largest_divisible(crop.shape[1], width_tiles_n)\n    \n    # Resize image\n    y_scale = new_height / crop.shape[0]\n    x_scale = new_width / crop.shape[1]\n    \n    crop = zoom(crop, (y_scale, x_scale, 1), order=0, mode=\"nearest\")\n    if debug: print(crop.shape, width_tiles_n, height_tiles_n, new_width, new_height, x_scale, y_scale)\n    \n    h_step = int(crop.shape[0] / height_tiles_n)\n    h_start = int(h_step // 2) + h_step\n    h_end = int(crop.shape[0] - h_start) - h_step\n    \n    w_step = int(crop.shape[1] / width_tiles_n) \n    w_start = int(w_step // 2) + w_step\n    w_end = int(crop.shape[1] - w_start) - w_step\n    \n    if debug: print(h_step, h_start, h_end, w_step, w_start, w_end)\n    \n    xy_pairs = []\n    c = 0\n    \n    # 1. Iteration w/ strict conditions\n    h_iters = range(h_start, h_end, h_step)\n    w_iters = range(w_start, w_end, w_step)\n    for i, h in enumerate(h_iters):\n        for j, w in enumerate(w_iters):\n            c+=1\n            # Skip white tile areas\n            if np.all(crop[h, w, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h+h_step, w, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h+h_step, w+w_step, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h+h_step, w-w_step, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h-h_step, w, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h-h_step, w+w_step, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h-h_step, w-w_step, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h, w+w_step, :] == np.array([255, 255, 255])) \\\n            or np.all(crop[h, w-w_step, :] == np.array([255, 255, 255])):\n                if debug: crop[h:h+20, w:w+20, :] = np.full((20, 20, 3), [100, 200, 50], dtype=np.uint8)\n                continue\n            \n            if debug: crop[h:h+20, w:w+20, :] = np.full((20, 20, 3), [220, 20, 50], dtype=np.uint8)\n            xy_pairs.append((j, i)) # (width, height)  \n            \n    # 2. If not enough tiles, do 2nd iteration w/ less strict conditions\n    h_iters = range(h_start-h_step, h_end+h_step, h_step)\n    w_iters = range(w_start-w_step, w_end+w_step, w_step)\n    if len(xy_pairs) < 4:\n        for i, h in enumerate(h_iters):\n            for j, w in enumerate(w_iters):\n                c+=1\n                # Skip white tile areas\n                if np.all(crop[h, w, :] == np.array([255, 255, 255])):\n                    continue\n                elif (j, i) not in xy_pairs:\n                    xy_pairs.append((j, i))\n            \n    if debug: io.imsave(\"{}.png\".format(img_id), crop)\n    \n    # Add offsets and convert to full image dimensions\n    xy_pairs = [(left+(x*config.tile_size), top+(y*config.tile_size)) for x,y in xy_pairs]\n    return xy_pairs\n\ndef next_largest_divisible(num, divisor):\n    \"\"\"\n    Returns next largest integer divisible by divisor.\n    \"\"\"\n    if num % divisor == 0:\n        return num\n    else:\n        return num + (divisor - num % divisor)\n    \ndef expand_tma(img, ts):\n    \"\"\"\n    Expand TMA image using mirroring.\n    \"\"\"\n    # Center crop\n    transform = A.CenterCrop(height=ts//2, width=ts//2)\n    img = transform(image=img)['image']\n    \n    # Double image size w/ mirroring\n    final_img = np.zeros((ts, ts, 3), dtype=np.uint8)    \n    final_img[:ts//2, :ts//2, :] = img[:, :, :]\n    final_img[ts//2:, :ts//2, :] = img[::-1, :, :]\n    final_img[:ts//2, ts//2:, :] = img[:, ::-1, :]\n    final_img[ts//2:, ts//2:, :] = img[::-1, ::-1, :] \n    return final_img\n\ndef equidistant_numbers(num, num_numbers, ts):\n    result = np.linspace(0,num, num_numbers+1).astype(int)[:-1]\n    result[-1] = num - ts\n    return result\n\ndef process_image(img_id):\n    # Load full img w/ pyvips\n    start = time.time()\n    print(\"-\"*5 + \" {} \".format(img_id) + \"-\"*5)\n    image = pyvips.Image.new_from_file(config.image_path.format(img_id))\n    print(\"original size: {:_} x {:_}\".format(image.width, image.height))\n    \n    # Create outdir\n    outdir = \"/tmp/images/{}\".format(img_id)\n    if not os.path.exists(outdir):\n        os.mkdir(outdir)\n    \n    # Convert black pixels to white\n    mask = (image == 0).bandand()\n    image = mask.ifthenelse([255, 255, 255], image)\n    del mask\n    \n    # Get thumbnail path\n    thumb_path = config.thumbnail_path.format(img_id)\n    if not os.path.exists(thumb_path): # allows loading of train thumbnails\n        thumb_path = config.image_path.format(img_id)\n    \n    # Load thumbnail + check if TMA\n    thumb_image = cv2.imread(thumb_path)\n    thumb_image = cv2.cvtColor(thumb_image, cv2.COLOR_BGR2RGB)\n    \n    pct_black_pixels = (thumb_image == 0).all(axis=-1).sum() / reduce(lambda x, y: x * y, thumb_image.shape)\n    if pct_black_pixels > 0.01:\n        is_tma = False\n    else:\n        is_tma = True\n        with open(os.path.join(outdir, \"is_tma.txt\"), \"w\") as f: pass # Create a .txt file to mark as tma\n\n    # WSI \n    if is_tma == False:\n        \n        # Update crop_ratios\n        crop_ratios = crop_image(thumb_image)\n        crop_ratios, width_tiles_n, height_tiles_n, top, bottom, left, right = tune_crop_ratio(image, crop_ratios)\n\n        # Get X,Y pairs from the thumbnail image\n        xy_pairs = get_xy_pairs(img_id, thumb_image, crop_ratios, width_tiles_n, height_tiles_n, left, top, debug=False)\n        \n        # Rank tiles\n        all_scores = []\n        \n        for x,y in xy_pairs:\n            tile = image.crop(x, y, config.tile_size, config.tile_size).numpy()\n            tile_score = score_tile(tile)\n            all_scores.append((x, y, score_tile(tile)))\n        \n        # Get top ranked tiles, remove obvious outliers\n        all_scores = sorted(all_scores, key=lambda x: x[-1])\n        start = 0\n        \n#         print(\"{} STATS 1:\".format(img_id), str({\"start\": start, \"len_scores\": len(all_scores), \"all_scores\": [z[2] for z in all_scores[:10]]}))\n        for i in range(0,10):\n            if i+10 >= len(all_scores):\n                break\n            if all_scores[i][2]*2 >= all_scores[i+10][2]:\n                break\n            else:\n                start += 1\n        \n#         print(\"{} STATS 2:\".format(img_id), str({\"start\": start, \"len_scores\": len(all_scores), \"all_scores\": [z[2] for z in all_scores[:10]]}))\n        for i, (x, y, tile_score) in enumerate(all_scores[start:start+config.num_tiles]):\n            # Create tile\n            tile = image.extract_area(x, y, config.tile_size, config.tile_size)\n            tile.write_to_file(\"{}/{}_{}_{}.png\".format(outdir, i, x, y))\n            \n        del image, tile\n        gc.collect()\n    \n    # TMA\n    else:\n        img = thumb_image\n        \n        # 2x tile size  matches WSI magnification\n        ts = config.tile_size*2\n        \n        # Score tiles w/ darkest median pixel if the TMA is large enough\n        thresh = 2.2\n        if img.shape[0] >= ts*thresh and img.shape[1] >= ts*thresh:\n            \n            w = img.shape[0]\n            h = img.shape[1]\n            w_numbers = (w // ts) + 1\n            h_numbers = (h // ts) + 1\n            if w%ts == 0: w_numbers-=1\n            if h%ts == 0: h_numbers-=1\n            \n            widths = equidistant_numbers(w, w_numbers, ts)\n            heights = equidistant_numbers(h, h_numbers, ts)\n            \n            all_scores = []\n            for x in widths:\n                for y in heights:\n                    tile = img[x:x+ts, y:y+ts, :]\n                    score = np.mean(img)\n                    all_scores.append((x, y, score_tile(tile)))\n            all_scores = sorted(all_scores, key=lambda x: x[-1])\n            \n            # Select top N tiles\n            for i, (x, y, tile_score) in enumerate(all_scores[:12]):\n                tile = img[x:x+ts, y:y+ts, :]\n                io.imsave(\"{}/{}_{}_{}.png\".format(outdir, i, x, y), tile)\n        \n        # Otherwise 5 central crops\n        else:\n            # Increase size of tiny imgs\n            if img.shape[0] < ts or img.shape[1] < ts:\n                transform = A.PadIfNeeded(min_height=ts, min_width=ts, border_mode=cv2.BORDER_CONSTANT, value=[255,255,255])\n                img = transform(image=img)[\"image\"]\n                expanded = True\n            else:\n                expanded = False\n\n            # 5 centered crops (h_start, h_end, w_start, w_end)\n            mp = (img.shape[0] // 2, img.shape[1] // 2) # img midpoint\n            crop_hws = [\n                (mp[0] - ts // 2, mp[0] + ts // 2, mp[1] - ts // 2, mp[1] + ts // 2, \"center crop\"),\n                (mp[0] - ts, mp[0], mp[1], mp[1] + ts, \"top-right\"),\n                (mp[0] - ts, mp[0], mp[1] - ts, mp[1], \"top-left\"),\n                (mp[0], mp[0] + ts, mp[1], mp[1] + ts, \"bottom-right\"),\n                (mp[0], mp[0] + ts, mp[1] - ts, mp[1], \"bottom-left\"),\n            ]\n\n            # Adjusts crops to fit in image\n            for i, (h_start, h_end, w_start, w_end, _) in enumerate(crop_hws):\n                if h_start < 0:\n                    h_end -= h_start\n                    h_start = 0\n                if h_end > img.shape[0]:\n                    h_start -= h_end - img.shape[0]\n                    h_end = img.shape[0]\n                if w_start < 0:\n                    w_end -= w_start\n                    w_start = 0\n                if w_end > img.shape[1]:\n                    w_start -= w_end - img.shape[1]\n                    w_end = img.shape[1]\n                crop_hws[i] = (h_start, h_end, w_start, w_end)\n\n            # Resize to match tile size\n            tma_resize = A.Compose([\n                A.Resize(\n                    height=config.tile_size, \n                    width=config.tile_size, \n                    interpolation=cv2.INTER_LANCZOS4\n                )\n            ])\n\n            # Make crops\n            for i, idxs in enumerate(crop_hws):\n                h_start, h_end, w_start, w_end = idxs\n                crop = img[h_start:h_end, w_start:w_end, :]\n                # Resize + Save\n                crop = tma_resize(image=crop)[\"image\"]\n                io.imsave(\"{}/{}_{}_{}.png\".format(outdir, i, w_start, h_start), crop)\n                if expanded == True:\n                    break\n            del crop, img\n    return\n\n\ndef main():\n    start = time.time()    \n    idxs, img_tmas = load_idxs()\n\n    # Process imgs\n    print(\"-\"*10 + \" Count: {} \".format(len(idxs)) + \"-\"*10)\n    all_pairs = [(img_id, ) for img_id, is_tma in zip(idxs, img_tmas)]\n    \n    # 4 processors = OOM\n    with mp.Pool(processes=3) as pool:\n        results = pool.starmap(process_image, all_pairs)\n        \n    # Summary\n    elapsed = time.time() - start\n    print(\"Elapsed: {:0>8}\".format(str(timedelta(seconds=int(elapsed)))))\n    \ndef score_tile(tile):\n    \"\"\"\n    Returns score for a tile. Lower is better.\n    \"\"\"\n    score = np.median(tile)\n    return score\n\nif __name__ == \"__main__\":\n    main()","metadata":{"papermill":{"duration":0.024613,"end_time":"2024-01-03T02:34:11.942929","exception":false,"start_time":"2024-01-03T02:34:11.918316","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-04T14:51:32.318224Z","iopub.execute_input":"2024-01-04T14:51:32.318789Z","iopub.status.idle":"2024-01-04T14:51:32.342168Z","shell.execute_reply.started":"2024-01-04T14:51:32.318748Z","shell.execute_reply":"2024-01-04T14:51:32.340803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    !KAGGLE_IS_COMPETITION_RERUN=\"true\" python tile_images.py\nelse:\n    !KAGGLE_IS_COMPETITION_RERUN=\"\" python tile_images.py","metadata":{"papermill":{"duration":45.291523,"end_time":"2024-01-03T02:34:57.240515","exception":false,"start_time":"2024-01-03T02:34:11.948992","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-04T14:51:32.344447Z","iopub.execute_input":"2024-01-04T14:51:32.344948Z","iopub.status.idle":"2024-01-04T14:54:33.210075Z","shell.execute_reply.started":"2024-01-04T14:51:32.344913Z","shell.execute_reply":"2024-01-04T14:54:33.208014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"clean_working(\"/tmp/images/\")\nclean_working(\"/kaggle/working/\")","metadata":{"papermill":{"duration":0.065249,"end_time":"2024-01-03T02:37:27.660452","exception":false,"start_time":"2024-01-03T02:37:27.595203","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-01-04T14:54:33.213753Z","iopub.execute_input":"2024-01-04T14:54:33.214267Z","iopub.status.idle":"2024-01-04T14:54:33.379817Z","shell.execute_reply.started":"2024-01-04T14:54:33.214211Z","shell.execute_reply":"2024-01-04T14:54:33.378565Z"},"trusted":true},"execution_count":null,"outputs":[]}]}