{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.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":117682,"databundleVersionId":15062069,"sourceType":"competition"}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:38.422723Z","iopub.execute_input":"2026-01-14T08:14:38.422986Z","iopub.status.idle":"2026-01-14T08:14:39.892851Z","shell.execute_reply.started":"2026-01-14T08:14:38.422960Z","shell.execute_reply":"2026-01-14T08:14:39.891880Z"},"_kg_hide-output":true,"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Just a preview of data\nThis notebook is not intended to be a comprehensive Exploratory Data Analysis (EDA). Instead, it serves as a focused visual preview of the training dataset to establish a baseline understanding of the imagery and the quality of the annotations.\n\nThe primary goal is to compare two specific versions of the training data:\n\nStandard Training Set: The current, active version of the images and labels.\n\n\"Deprecated\" Training Set: An older or alternative version of the dataset.\n\nBy overlaying the masks and inspecting these files side-by-side, we aim to gain a visual intuition regarding:\n\nFeature Alignment: Ensuring the labels correctly map to the biological structures in the scans.\n\nAnnotation Completeness: Identifying why specific data was deprecated (e.g., sparse labeling or noise).\n\nData Consistency: Checking for shifts in resolution, intensity, or metadata between versions.\n\nTechnical Breakdown of the Inspection\n\nTo perform this inspection, we leverage the tifffile library to access the metadata and image arrays directly. As seen in our initial inspection of the XResolution tag (ID 282), the data is stored in a Rational format (Type 5), which allows us to verify the physical scaling of the scans.\n\nMetadata Inspection: We extract the tag.value, tag.code, and tag.dtype to ensure the file integrity.\n\nVisualization: We use matplotlib to render the images and labels with transparency (α), allowing for a precise quality check of the segmentation boundaries.","metadata":{}},{"cell_type":"code","source":"!pip install -U tifffile[all]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:39.894590Z","iopub.execute_input":"2026-01-14T08:14:39.895164Z","iopub.status.idle":"2026-01-14T08:14:49.567087Z","shell.execute_reply.started":"2026-01-14T08:14:39.895129Z","shell.execute_reply":"2026-01-14T08:14:49.565799Z"},"_kg_hide-output":true,"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# A single file to explore\nsample_train_image='/kaggle/input/vesuvius-challenge-surface-detection/train_images/1004283650.tif'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:49.568854Z","iopub.execute_input":"2026-01-14T08:14:49.569322Z","iopub.status.idle":"2026-01-14T08:14:49.575022Z","shell.execute_reply.started":"2026-01-14T08:14:49.569253Z","shell.execute_reply":"2026-01-14T08:14:49.573570Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Library for tiff https://pypi.org/project/tifffile/","metadata":{}},{"cell_type":"code","source":"import tifffile","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:49.576221Z","iopub.execute_input":"2026-01-14T08:14:49.576803Z","iopub.status.idle":"2026-01-14T08:14:49.618509Z","shell.execute_reply.started":"2026-01-14T08:14:49.576761Z","shell.execute_reply":"2026-01-14T08:14:49.617347Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import math\nimport random\nimport json","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:49.621428Z","iopub.execute_input":"2026-01-14T08:14:49.621737Z","iopub.status.idle":"2026-01-14T08:14:49.626820Z","shell.execute_reply.started":"2026-01-14T08:14:49.621711Z","shell.execute_reply":"2026-01-14T08:14:49.625457Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#help(tifffile)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:49.628433Z","iopub.execute_input":"2026-01-14T08:14:49.629248Z","iopub.status.idle":"2026-01-14T08:14:49.645007Z","shell.execute_reply.started":"2026-01-14T08:14:49.629216Z","shell.execute_reply":"2026-01-14T08:14:49.643929Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tifffile import TiffFile","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:49.646549Z","iopub.execute_input":"2026-01-14T08:14:49.646818Z","iopub.status.idle":"2026-01-14T08:14:49.667594Z","shell.execute_reply.started":"2026-01-14T08:14:49.646795Z","shell.execute_reply":"2026-01-14T08:14:49.666496Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:49.668833Z","iopub.execute_input":"2026-01-14T08:14:49.669650Z","iopub.status.idle":"2026-01-14T08:14:49.688416Z","shell.execute_reply.started":"2026-01-14T08:14:49.669605Z","shell.execute_reply":"2026-01-14T08:14:49.687081Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### One .tif file pages\n\nVisualize multi-page TIFF files by organizing every individual frame into a clean grid.\n\nMulti-Page TIFF Grid Visualization\n\nDynamic Grid Scaling: It uses math.ceil to automatically calculate the number of rows needed based on a fixed column width (20), ensuring all pages are captured regardless of file size.\n\n","metadata":{}},{"cell_type":"code","source":"with TiffFile(sample_train_image) as tif:\n    num_images = len(tif.pages)\n    \n    # 1. Setup the Grid Dimensions\n    cols = 20  # Number of images per row (increase to make images smaller)\n    rows = math.ceil(num_images / cols)\n    \n    # 2. Create the figure with a large height to accommodate all rows\n    # figsize=(width, height). We keep width fixed, height scales with rows.\n    fig, axes = plt.subplots(rows, cols, figsize=(20, rows * 1.5))\n    \n    # Flatten the 2D grid of axes into a 1D list for easy looping\n    axes = axes.flatten() \n\n    # 3. Loop through pages and plot\n    for i, page in enumerate(tif.pages):\n        image = page.asarray()\n        \n        # Plot on the specific subplot\n        axes[i].imshow(image, cmap='gray')\n        axes[i].axis('off')  # Hide axis ticks for a cleaner view\n        axes[i].set_title(f\"{i}\", fontsize=8) # Optional: Add index number\n\n    # 4. Hide any unused empty subplots at the end\n    for j in range(i + 1, len(axes)):\n        axes[j].axis('off')\n\n    plt.tight_layout() # Adjusts spacing to prevent overlap\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:14:49.689795Z","iopub.execute_input":"2026-01-14T08:14:49.690121Z","iopub.status.idle":"2026-01-14T08:15:11.870957Z","shell.execute_reply.started":"2026-01-14T08:14:49.690083Z","shell.execute_reply":"2026-01-14T08:15:11.869535Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Sample page\nShow one random page","metadata":{}},{"cell_type":"code","source":"with TiffFile(sample_train_image) as tif:\n    # 1. Get the total number of pages\n    num_pages = len(tif.pages)\n    \n    # 2. Pick a random index\n    random_idx = random.randint(0, num_pages - 1)\n    \n    # 3. Access the random page and convert to array\n    target_page = tif.pages[random_idx]\n    image = target_page.asarray()\n    \n    # 4. Display the image\n    plt.figure(figsize=(8, 8))\n    \n    # Check if the image has color channels (usually 3 or 4 channels)\n    # If image.ndim is 2, it's grayscale; if 3, it's likely RGB/RGBA\n    if image.ndim == 2:\n        plt.imshow(image, cmap='gray')\n        plt.title(f\"Random Page: {random_idx} (Grayscale)\")\n    else:\n        plt.imshow(image)\n        plt.title(f\"Random Page: {random_idx} (Color)\")\n        \n    plt.axis('off')\n    plt.show()\n\n# Optional: Print metadata for the selected page\nprint(f\"Image Shape: {image.shape}\")\nprint(f\"Data Type: {image.dtype}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:11.872629Z","iopub.execute_input":"2026-01-14T08:15:11.873199Z","iopub.status.idle":"2026-01-14T08:15:12.179987Z","shell.execute_reply.started":"2026-01-14T08:15:11.873156Z","shell.execute_reply":"2026-01-14T08:15:12.178981Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\nMedatada extraction and visualisation using TiffFile\n","metadata":{},"attachments":{"53dd3d9e-bc75-482f-a2ef-e0ced6daf5fc.png":{"image/png":"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qJ+6w7lTPtqzmOjfCzx8/D3xvxHm28SKgPQd844I30SwEy7ISwDa5D3Q5I/1LPn60mqb0n5Kzzam1bVGqod+oM+5H4Kijb/ZiCBeVgX/ptNn4Xkj1atjIiACIiACWYdAeumRaETSxVPkTx7+ptkf99vwH6H0x/2W/xSDq1j5437LgJo3xZEskcFdSgd/8doVqT1n61istqSGPyI0XIjGEkP++hLJQ97g6oW+7NnYRroPookS5jfxhpqFKhBFGIM3Bm0+3Cs1beR+jXbu1NQbLJso/2AZvx9c6c4fS6stPKM9q5wjkWcx1t+LaC9WmF+EWGI+kRdM9CX3drggj/UspRUHX09K+yk92+jbeja2PFPBl2X+HMz5Cq406I9HyuvTtBUBERABERCBlBBIV1GUkgaqjAhkFAF+N+njjz92C3wwuZ3BNPOPmKTPCmqyrEeAJeARe4TKEbrGgJuFGvDaBZchz3pXphaLgAiIgAiIgAikhkC6hs9FaighNExoZy5HMGwiUl4dE4H0JsBCEIsWLXJhbnjaWAaeOVayjCGQFn8vCANlQRf6Fo8V81nCfy8rY65OZxUBERABERCBnEsgo8PnMlwU5dyu15WLgAiIgAiIgAiIgAiIgAhAIKNFUW51gwiIgAiIgAiIgAiIgAiIgAjkZAISRTm593XtIiACIiACIiACIiACIiACJlGkm0AEREAEREAEREAEREAERCBHE5AoytHdr4sXAREQAREQAREQAREQARGQKNI9IAIiIAIiIAIiIAIiIAIikKMJSBTl6O7XxYuACIiACIiACIiACIiACEgU6R4QAREQAREQAREQAREQARHI0QQkinJ09+viRUAEREAEREAEREAEREAEJIp0D4iACIiACIiACIiACIiACORoAhJFObr7dfEiIAIiIAIiIAIiIAIiIAISRboHREAEREAEREAEREAEREAEcjQBiaIc3f26eBEQAREQAREQAREQAREQAYki3QMiIAIiIAIiIAIiIAIiIAI5moBEUY7ufl28CIiACIiACIiACIiACIiARJHuAREQAREQAREQAREQAREQgRxNQKIoR3e/Ll4EREAEREAEREAEREAERECiSPeACIiACIiACIiACIiACIhAjiYgUZSju18XLwIiIAIiIAIiIAIiIAIiIFGke0AEREAEREAEREAEREAERCBHE5AoytHdr4sXAREQAREQAREQAREQARGQKNI9IAIiIAIiIAIiIAIiIAIikKMJSBTl6O7XxYuACIiACIiACIiACIiACEgU6R4QAREQAREQAREQAREQARHI0QQkinJ09+viRUAEREAEREAEREAEREAEJIp0D4iACIiACIiACIiACIiACORoAhJFObr7dfEiIAIiIAIiIAIiIAIiIAISRboHREAEREAEREAEREAEREAEcjQBiaIc3f26eBEQAREQAREQAREQAREQAYki3QMiIAIiIAIiIAIiIAIiIAI5moBEUY7ufl28CIiACIiACIiACIiACIiARJHuAREQAREQAREQAREQAREQgRxNQKIoR3e/Ll4EREAEREAEREAEREAERCBvchGsWLEiuUWUXwREQAREQAREQAREQAREIAcSaNSoUZa46mSLIq6qXr16WeLi1EgREAEREAEREAEREAEREIGMIbBmzZqMOXEKzqrwuRRAUxEREAEREAEREAEREAEREIHsQ0CiKPv0pa5EBERABERABERABERABEQgBQQkilIATUVEQAREQAREQAREQAREQASyDwGJouzTl7oSERABERABERABERABERCBFBCQKEoBNBURAREQAREQAREQAREQARHIPgQkirJPX+pKREAEREAEREAEREAEREAEUkBAoigF0FREBERABERABERABERABEQg+xCQKMo+fakrEQEREAEREAEREAEREAERSAEBiaIUQFMRERABERABERABERABERCB7ENAoij79KWuRAREQAREQAREQAREQAREIAUEJIpSAE1FREAEREAEREAEREAEREAEsg8BiaLs05e6EhEQAREQAREQAREQAREQgRQQkChKATQVEQEREAEREAEREAEREAERyD4EJIqyT1/qSkRABERABERABERABERABFJAQKIoBdBURAREQAREQAREQAREQAREIPsQkCjKPn2pKxEBERABERABERABERABEUgBAYmiFEBTEREQAREQAREQAREQAREQgexDQKIoTl+eOHHCjh8/HidXxiYn2saTJ0/GbGi89FiFjx49Gis5TdK4zp9++ilN6sqqlSTCOSX9eOjQoayK5Ix2J8KIQinhdMbJdEAEREAEREAERCBbEMibLa7iLF0Eg6b777/fDcT//ve/W8GCBRM601dffWVjx461fv36Wd26dRMqE8x08ODB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C/VEul/iiSFfPlo/+fRY20TbGKuOSGnJuRcjldcxERABERABEcjMBDLUU5SZwWT2thFOxmRzfnyUwTLCiIE9CwpEExCZ/ZqS2z7CmHgD8fvf/z65RV1+5gSxeh/eT+/hIESPFeWCP5KbosrDCnEuPHreGPRG8ij5dL9FDDEY9UaZSALap5+NbVrda3h2+XjLLi81/PVk921muBezO2NdnwiIgAhkNwJZyVMkUZTd7j5djwiIgAiIgAiIgAiIgAhkAgJZSRT9sh5tJgCnJoiACIiACIiACIiACIiACIhAehOQKEpv4jqfCIiACIiACIiACIiACIhApiIgUZSpukONEQEREAEREAEREAEREAERSG8CEkXpTVznEwEREAEREAEREAEREAERyFQEJIoyVXeoMSIgAiIgAiIgAiIgAiIgAulNQKIovYnrfCIgAiIgAiIgAiIgAiIgApmKgERRpuoONUYEREAEREAEREAEREAERCC9CUgUpTdxnU8EREAEREAEREAEREAERCBTEZAoylTdocaIgAiIgAiIgAiIgAiIgAikNwGJovQmrvOJgAiIgAiIgAiIgAiIgAhkKgISRZmqO9QYERABERABERABERABERCB9CYgUZTexHU+ERABERABERABERABERCBTEVAoihTdYcaIwIiIAIiIAIiIAIiIAIikN4EMkwUHTx40N59911bvXp1xGuOl06hBQsW2KxZs+z48eMR69BBERABERABERABERABERABEYhHIG+8DGcrfe7cuTZ9+nTLly+fDR8+3G2D54qX/v3339uECRNckdKlS1uTJk2CxbUvAiIgAiIgAiIgAiIgAiIgAgkRyDBPUcWKFS1XrlxWrly5MwQRLY+XXrx4cStcuLArSx0yERABERABERABERABERABEUgJgQzzFOHZefzxx61gwYIR2x0vnXKPPPKInThxwgoUKBCxDh0UAREQAREQAREQAREQAREQgXgEMkwU0TA8PbEsXjqhd3xkIiACIiACIiACIiACIiACIpBSAhkWPpfSBqucCIiACIiACIiACIiACIiACKQlAYmitKSpukRABERABERABERABERABLIcAYmiLNdlarAIiIAIiIAIiIAIiIAIiEBaEpAoSkuaqksEREAEREAEREAEREAERCDLEZAoynJdpgaLgAiIgAiIgAiIgAiIgAikJQGJorSkqbpEQAREQAREQAREQAREQASyHAGJoizXZWqwCIiACIiACIiACIiACIhAWhKQKEpLmqpLBERABERABERABERABEQgyxHI0B9vzXK0MlGDn3vuOfv2229di/LmzWv3339/JmqdmiICIiACIiACIiACIiACWYdAunuKTp48aXPnzrVTp05FpBQvnUKLFy+2AwcORCyfnIPDhg2z2bNnJ6dIpsnbpEkTa9OmjRUvXty2b9+eadqlhoiACIiACIiACIiACIhAViOQIaLojTfesCeeeMJ27NhxBi9EUax0CqxYscIefPBBW7Zs2Rnlk3Ng79699vPPP8csQntiWbz0WGUTTYt0jubNm1v79u3tvPPOS7SaqPki1R81sxJEQAREQAREQAREQAREIJsRSPfwOUK97rnnHnvllVfs4Ycftp49e1q7du0sV65cDm28dDJdf/31NmnSJHvhhRfss88+s379+lmRIkUS7pqnn37a9uzZYydOnLAPP/zQ5s+f78rifenVq5ft27fPZs6cacuXL7f9+/dbqVKlXBtpp7evvvrKJk6caNu2bbPChQvbOeec45I6d+5srVu39tkibleuXOnaf+ONN1qNGjVcnp9++skeeeQRu+SSS6xr1662YcMG++CDD9z2+PHjVrlyZRswYIBVqVIlYp3hB8eMGeMODR482G2///57GzlypLs+rhP7/PPPbcaMGfb1119b6dKlrVWrVtaxY0eXpn9EQAREQAREQAREQAREIKcQSHdPEWAZ2A8dOtQ6depkU6ZMcV4jhIi3eOkFCxa0QYMG2W233WaIkwceeMBWr17ti8fdNmvWzFq0aOHyVatWzXlc8LrUrVvXHSMcbdWqVS48DeGCKMJ7tXnzZpdO6N+oUaMsT548duedd1qdOnWcyKJO6otnNWvWtIMHD9qCBQtCWZcsWWI7d+60Bg0auGNLly61Y8eOWZ8+faxv376u/tGjR4fyx9tBzPHxhgBECB49etQdWrt2rSGc8uXLZwMHDrTzzz/f3nrrLRea6MtoKwIiIAIiIAIiIAIiIAI5gUCGiCLA4hHq3r27EyeIDcRN0OKlk7devXrOa4TA+PTTT4PFY+7jEeGDIWLwAPFB3GC1a9e2hx56yOUpWrSoVa9e3R3ftGmT2+7evduJGkQQnh4EFYawwKMTzxB1hL/h5cJDhCGQqlatGiqP9+tPf/qTq//cc891YXIIR4RNWtiiRYuMsLnevXu7dnfr1s1Vi/dIJgIiIAIiIAIiIAIiIAI5iUC6h895uLt27bLx48cbQqNLly4hD0mi6XhRpk2b5hZKqFWrll111VW+aKq3hw4dsnHjxjnvU8mSJZ2niEqPHDni6sZzVKxYMZs3b55b6GDhwoWWO3duJy4SPTkhdgghPEQIM0LYCAv09uWXX9qECROcCCpXrpzzGpF2+PBho02ptS1btrgqCGEM2g8//BD8qn0REAEREAEREAEREAERyPYEMkQUzZkzx6ZOnerExpAhQ84QE/HS8SwxJwnPCeFlCAw/JynRHiNsDPNCJ1iOkL5169a50Dg8QXhz7rjjjlAWBFCFChVcuBthdWXKlHGhfHiVgoYHi5X28ADVr18/mORCCPEsIagQiAUKFDDC+jDC8whtQwwRnocAQ0AhksKNED6MBSP8vCa+s084nrfw66xYsaILr0MU+TrIy7XJREAEREAEREAEREAERCAnEUh3UYSHZ/LkyW5Cf48ePVwYXRB4vHTysggCQoE5RSwQkBJDhJQoUcKYu0PYHN+Zb0PoHAICccD3NWvWuMUYguf48ccfbf369dahQwe7+OKLnajInz+/EzNBcYYgmj59upu3M3z4cLcN1nP55Zc7cffdd9+5BRa8qGH+DxwQK4TLMf/n3XffDRYN7V9wwQWufZyHcELCDvE8cZzFGiiHZ+vtt98OlWGnadOmxoIPeMRYXAGRSDhfy5YtneBLkllfREAEREAEREAEREAERCAbE0h3UcRAn9Xnoq2iFi+dvmD+C+WDAiQlfdS/f38bO3asjRgxwhVHSCCKEAmEr7FaG2IB4RP0ArFfvnx5J0ZYvVNeewIAAEAASURBVM4bIuu+++5zq9FxDG8MbcTj4z1TPi9bPENvvvmmC4m77LLLQkl+PtU777xjjz32mBOAjRs3dl6nUKb/7iDoWJyBdsyaNcvNVUIUMWcJ4YZYYg4TAhQx6o10PGDvvfeePfXUU+5w2bJlXV14wWQiIAIiIAIiIAIiIAIikFMI5DrtlTiV95aJdvz5/gldM78RhEciuxiLDTCPBk9PUPgQwkZ4HseC4WVcNz8ey3you+66ywoVKuQWLCDcjmXCCedr27ZtCA/zkxAlKQlLIySOOUT8QGs84zws3Y0HLWh4tViuPNb5ycM1srS4TAREQAREQAREQAREQATSggARV40aNUqoquTokYQqTGamdPcUJbN9Zz07YoE5QeGGhwfPTyQjvA4BghDCu4R4wStDXXiagpYaoUE4nQ+pC9YZaT/aeYJCL1I5jiWSJ1pZHRcBERABERABERABERCBrE4gx4uilHQgP7C6Y8cOt/occ3bwzlSqVMmFBSr0LCVEVUYEREAEREAEREAEREAEMo6ARFEK2BNqR5gcH5kIiIAIiIAIiIAIiIAIiEDWJqD1l7N2/6n1IiACIiACIiACIiACIiACqSQgUZRKgCouAiIgAiIgAiIgAiIgAiKQtQlIFGXt/lPrRUAEREAEREAEREAEREAEUklAoiiVAFVcBERABERABERABERABEQgaxOQKMra/afWi4AIiIAIiIAIiIAIiIAIpJJAphBF/PBoZrPM2KbMxkjtEQEREAEREAEREAEREIHsQCDDRRG/dPuXv/zFdu/enWl4Hjt2zB588EH76KOPMk2b1BAREAEREAEREAEREAEREIGzQyBDf6do+/bt9uKLL1qTJk2sdOnSoSvctm2brV271pYuXWpFihSx22+/PZSWVjurVq2yL774wti2b9/e2rRpE6o6X7587tibb75p5cuXtzp16oTStCMCIiACIiACIiACIiACIpC9CGSYp+jkyZM2ZswYK1WqlPXv3z8J1fnz59u8efPs+++/tz179iRJS6svCB4EER6qgwcPnlFtx44d7cILL7QXXnghYvoZBXRABERABERABERABERABEQgSxLIMFG0fv1627Fjh/Xu3dvwzAStb9++LnytevXqwcNpuk943L333hu1zly5clm/fv3s8OHDtnjx4qj5lCACIiACIiACIiACIiACIpC1CWSYKFq3bp0hPM4///xMS7Bo0aJWsmRJo60yERABERABERABERABERCB7Ekgw0TRvn373Hyd/PnzZ2qyVatWtb1792bqNqpxIiACIiACIiACIiACIiACKSeQYaLoxIkTds4556S85elUktA+2ioTAREQAREQAREQAREQARHIngQyTBQVK1bMWGUuNYJj+fLlbkGG48ePn7Xe2bp1qxUvXvys1a+KRUAEREAEREAEREAEREAEMpZAhi3JXa1aNZs7d64TRlWqVAlROHLkiLEIA3bgwAG38hviJ0+ePG41OJ9x//799vzzz9upU6fc3KRWrVr5pLhbVrVjOfCjR4+6vN98841xDoQa7fJGOvkaNWrkD2krAiIgAiIgAiIgAiIgAiKQzQhkmChq2LChFSpUyGbMmGG/+93vQlhZgvvf//536Ds7/vu//vUvy5v3/5rM7xfVqFHDNm7c6IRVkgJxvixZssSd12djaW4+iJ9gW95//30nuC655BKfVVsREAEREAEREAEREAEREIFsRiDDRBHziW6++WZ79tlnbdasWdapUyeHtlKlSjZ69Oi4mPEc3XrrrXb33Xdb7dq14+YPZrjyyiuNTyxbuXKlzZw50y0ZXrZs2VhZlSYCIiACIiACIiACIiACIpCFCWTYnCKY1a1b16655hqbOnWq+xHV5HDkN46efPJJK1iwoKsnOWXj5T127JiNHz/eLr30Umvbtm287EoXAREQAREQAREQAREQARHIwgQyzFPkmbVv396YU1S6dGl/KKHt7t27rVy5cvb73//+jB9/TaiCGJlYce7222+3ypUrx8ilJBEQAREQAREQAREQAREQgexAIMNFERAvuOCCZLOsV6+e8Tlblpl/VPZsXbPqFQEREAEREAEREAEREIGcSCBDw+dyInBdswiIgAiIgAiIgAiIgAiIQOYiIFGUufpDrREBERABERABERABERABEUhnAhJF6QxcpxMBERABERABERABERABEchcBCSKMld/qDUiIAIiIAIiIAIiIAIiIALpTECiKJ2B63QiIAIiIAIiIAIiIAIiIAKZi4BEUebqD7VGBERABERABERABERABEQgnQlIFKUzcJ1OBERABERABERABERABEQgcxFId1F08uRJmzt3rp06dSoiiXjpFFq8eLEdOHAgYvnkHBw2bJjNnj07OUWUVwREQAREQAREQAREQAREIJsRyBBR9MYbb9gTTzxhO3bsOAMnoihWOgVWrFhhDz74oC1btuyM8sk5sHfvXvv5559jFqE9sSxeeqyyPi2aQPTpsc4RTAvu+7Jsox0P5tG+CIiACIiACIiACIiACORUArlOnDhxKu8tE+348/0TYoAgqVevXkJ5o2XaunWrvfLKK7Zz507r2bOntWvXznLlyhXKHi/9yJEjNmnSJFu0aJE1adLE+vXrZ0WKFAmVj7fz9NNP2549e2zXrl1WqFAhK1CggCtCXb169bJ9+/bZzJkzbfny5bZ//34rVaqUayPt9PbVV1/ZxIkTbdu2bVa4cGE755xzXFLnzp2tdevWPlvU7WOPPWaNGjVy5VetWmXFixc3yl5yySWuzIYNG+yDDz4wtsePH7fKlSvbgAEDrEqVKnb06FF76KGHrFKlSrZu3TqrVauWawMisVOnTtajRw9Xx+eff24zZsywr7/+2kqXLm2tWrWyjh07Rm2TEkRABERABERABERABEQgrQisWbPGjXcTqS85eiSR+pKbJ909RTSQgf3QoUPdAH7KlCnOa4QQ8RYvvWDBgjZo0CC77bbbDHHywAMP2OrVq33xuNtmzZpZixYtXL5q1apZ+/bt3adu3bru2Pbt2w2h0qZNG7vxxhudKMJ7tXnzZpeOZ2fUqFGWJ08eu/POO61OnTpOZFEn9SViCDKuHdGFkDl27Ji9/vrrIa/O0qVL3bE+ffpY3759Xf2jR492VSOSdu/e7a69YcOGhvhBYFasWNHmzJnj6li7dq2NGTPG8uXLZwMHDrTzzz/f3nrrLRd6mEj7lEcEREAEREAEREAEREAEcgqBDBFFwM2bN691797diRPEBuImaPHSyYvH6vrrr7eDBw/ap59+Giwecx+PCR8MEYMHiA/iBqtdu7bzxJCnaNGiVr16dXd806ZNbosg4ZyIoBo1ajhBRQLCA49OolazZk374x//aF27dnV1/PTTT86rQ3m8X3/6059c/eeee66dd955zoOFh8sbHqkOHTq4r3i4GjdubHjRDh065LxohM317t3btatbt24uHwJKJgIiIAIiIAIiIAIiIAIi8AuBvL/spu8enpLx48cbQqNLly7WoEGDJA2Il45nZdq0aW6hBMLHrrrqqiTlU/MFUTFu3DjnfSpZsqTzFFEfggMjnK5YsWI2b948F/a2cOFCy507txMfLkOC/yC4fNhgmTJlXCl/ji+//NImTJjgPETlypVzXiMyHD582PLnz+/y4qnivFhwn+9btmxhYw8//LDb+n9++OEHv6utCIiACIiACIiACIiACIjAaQIZIooI8Zo6daoTF0OGDDlDTMRLx7PEnCRC7ggvw2PixUWivUpYGeZFSLAcYW3M1SE0Dk8QHpw77rgjlAUhUqFCBReyRlgdgoZQPkRO0PAmsdJe1apVrX79+sGkmPuE5xH6hhiiDQiwBQsWOJEUs2AgkVA6QvMQRQgmb15E+e/aioAIiIAIiIAIiIAIiEBOJ5DuoggPz+TJk92EfxYEIEwuaPHSycsiCAgFhAgLCKTEWFyhRIkSxtwdwub4zgIGhM4hlBAPfGeC2IcffpjkFD/++KOtX7/eha5dfPHFTnTgvUHMBMUZgmj69OluXs/w4cPdNklFUb6cXvzCeYYQM4TLMT/o3XffjZI78uGmTZvaypUrnceLxRUQgZ999pm1bNnSCbrIpXRUBERABERABERABERABHIegaSKJB2un4H+Pffc4xZbiHS6eOmUYX4MizEEBUikuuId69+/v40dO9ZGjBjhsl5wwQVOFCEiCF8bOXKkExMIn6AXiP3y5cs7sRQUTIis++67z60ER4V4a2gjHh/vmYrWpqAHx8+neuedd4xV6hCAzBdCZIVbNAbNmzd3Hq733nvPnnrqKVesbNmyLkwRL5dMBERABERABERABERABETg/whkyJLcmQk+ixEwzwZPT1D44PUhPI9jwfAz2s6PxzIf6q677nJLelMH4XYsE044X9u2bUOXyPwkVssLip5QYpwdfkOJOUQs150aw7PFNbB0uEwEREAEREAEREAEREAE0oNAVlqSO909RenRAck5B2LFL3IQLIcHBs9PJCO8jmWxEUJ4lxAvhNNRF+F3QUuNEOG3j/zvHwXrTO5+UOwlt6zyi4AIiIAIiIAIiIAIiEB2J5DjRVFKOpgfWN2xY4dbfY65PoS38UOqhAUqNC0lRFVGBERABERABERABERABDKOgERRCtgTakeYHB+ZCIiACIiACIiACIiACIhA1iaQYT/emrWxqfUiIAIiIAIiIAIiIAIiIALZhYBEUXbpSV2HCIiACIiACIiACIiACIhAighIFKUImwqJgAiIgAiIgAiIgAiIgAhkFwISRdmlJ3UdIiACIiACIiACIiACIiACKSIgUZQibCokAiIgAiIgAiIgAiIgAiKQXQhIFGWBnjx69GgWaKWaKAIiIAIiIAIiIAIiIAJZk0CGLcm9Z88emzNnjlWuXNn43R9vhw8fNj4lS5Z0P4bK8UOHDhk/mMqPqebJk8f27t1rJ06ccEX4cdP0/nHSBQsW2Pvvv++bbAMGDDjjR1tDianc2bhxoz3zzDPWuHFju/nmm1NZ25nFV61aZRs2bHB9QF/IREAEREAEREAEREAERCCnEcgwUXTw4EGbPXu2XXjhhUlE0aeffmqTJk2yfv36WevWre3UqVP25JNPOqH0wAMPOFH01FNP2a5du0J9VaBAAevWrZt17NgxdOxs7px33nnWpk0b2717t82dO9e1LSXn27x5s3Etw4YNs7Jly0asIm/evE4csj0btmnTJtcPVapUcQL1bJxDdYqACIiACIiACIiACIhAZiZwdkbaCVwxP4CK+a0vghD65JNPbPr06XbxxRfbypUr7bvvvrNBgwYZXiFv1apVs/79+9vOnTvdoP6tt96yevXqWcWKFX0Wtz158mTI45Qk4b9fUpKOgODz5ZdfOlEUqd5Ejh04cMCOHz/uhF+0/L/61a+ccMJDFs3iXQPlouVBUGLh/eAO6h8REAEREAEREAEREAERyAEEMkwUER6HlSpVKglmBv/XXnutjRw50j744ANbvHixEyAIpKAxiK9UqZL75M6d2/B4fPXVV04U4V165513bOnSpbZv3z5XvlevXoaQ8vbuu+86wbVt2zYXltegQQPr0aOHFSlSxGX5+OOPjc+OHTsMz1D37t2NPInY/PnzbcaMGTZkyBArVqyYKzJixAi3j7j75ptv7OWXXzZEEYa3yIuegQMHWq1atezRRx8NpZOnadOmds0117Abslht5PxbtmyxmjVr2kcffeS8WYQpXn311ZYvX75QHb4f/DaUoB0REAEREAEREAEREAERyCEEMmyhBQbmzAWKNBjH48MHbxFhcr1797ZcuXJF7BLmJiGcMMQL9tprr7k5P40aNbK+ffvazz//7ESWFyHLli1zddeuXdtuu+02u/zyy+3rr7923hTKz5s3z9WBYOvTpw+H7LnnnnNzb9yXOP8w/4l2+XlPZCfUzp//3HPPtUsvvdRq1Kjharrooousffv27lO+fHl3rGXLlqFjP/74oxFuGLR4bdy/f78xX4gQRc5VtWpVJ46++OKLYDUh/uHiNEkmfREBERABERABERABERCBbEwgwzxFMCX8zQuZcMZ169a1NWvWOOEU9PD4fOvWrbNbb73Vf7VWrVq5ughHI/yuevXqTgyQAUE1YcIEF+7GggWFCxd25bZv3+4WSGjXrp116tQpVNfChQutTJkyNnjwYOfBwUtz7733OvGF5yW1Vrx4ceOceIeWL1/u2lmuXLkk1RJG6A2vV7gl0kau+5577nEeKkTW0KFDDVFUv379UHUsroD3qlChQqFj2hEBERABERABERABERCBnEQgQ0VRw4YNI7L+6aefbObMmc6LgccFrwjenKCxMAHC4c0333RzhliYARGA0MEIp2NhhqBRF4aH6Morr3Sr3xGmRygebbnuuuuMOTaEt7EAhA9pI6SudOnStnXr1mB1GbZPeGCibfThe2yZk8XKfkErWLBgkoUugmnaFwEREAEREAEREAEREIGcQCBDRVE0wCx3TajZX/7yF5s4caJNmzbNmjVrFvLwUI5wL0LOWJWNcDnm17Rt29at4oY4Ij9CKWjBuTRdu3a1Ll26OHHB3CPmLxHOx9wjQtiYS+Tt2LFjLhyOcLygedFEeF7Q/IIQzGciPJBFDqgj3PyKcojA5BjXl2gbk1Ov8oqACIiACIiACIiACIhATiSQYXOKosFm7g3zYJo3b+6WiEak8DtFCKNIdtlllzmBwMIJ5EOQ4OVB6LDAAKvTsUocIWhexCCiRo8e7cLzEEo+dM2LFELsWICBfKtXr7YXXnjBzUsKF0WE/uFZYmEFfk/os88+c030IXbvvfeeMX8JbxQCKdz8Snl4xVgkYu3atUmWGg/PH/yeaBuDZbQvAiIgAiIgAiIgAiIgAiJwJoFM5yny82d69uzpWsvcIAQAIXR4ghAweEpYcQ5jy6pso0aNciu+sXIdP3LKHCLEBmKK/CzcQOgY84nYZ3U2hJFfqrpOnTqufurEi8RiCYgdPFCEz+F1oh1BQ0SRd9asWe63lAhFo+4KFSoYc3iWLFlizH1CTDGPh7C3oLHcdps2bdwcKOYW0U4WleA6w81frz+eSBvDy1A20jFfp7YiIAIiIAIiIAIiIAIikBMJ5Dq9QtqpvLdMtOPP90/o+lesWOEG/gllzuBMCB48NKz2Fgyd881iUQZWaWO+jfcS+TS2lCe9RIkSwcNn7FMPK8RRj/dGkcmHxfnfAjqj4H8PIMDwctHO5P5eUKJtjHZuHRcBERABERABERABERCBs0GARdPCI62inSc5eiRaHak5nuk8Ram5mPCyeEUiLfnt8yGEYi1FTfl4goi6qCfSeeKJId8OPEx8UmKJtjEldauMCIiACIiACIiACIiACOQEApluTlFOgK5rFAEREAEREAEREAEREAERyDwEJIoyT1+oJSIgAiIgAiIgAiIgAiIgAhlAQKIoA6DrlCIgAiIgAiIgAiIgAiIgApmHgERR5ukLtUQEREAEREAEREAEREAERCADCEgUZQB0nVIEREAEREAEREAEREAERCDzEJAoyjx9oZaIgAiIgAiIgAiIgAiIgAhkAAGJogyArlOKgAiIgAiIgAiIgAiIgAhkHgISRZmnL9QSERABERABERABERABERCBDCAgUZQB0M/GKXft2mXDhg2zxYsXn43qVacIiIAIiIAIiIAIiIAIZFsC6S6KTp48aXPnzrVTp05FhBovnUIM/A8cOBCxfHIOIiJmz56dnCKZNu+JEyds9+7d9tNPP2XaNqphIiACIiACIiACIiACIpAZCWSIKHrjjTfsiSeesB07dpzBBFEUK50CK1assAcffNCWLVt2RvnkHNi7d6/9/PPPMYvQnlgWLz1WWZ8Wqw7EYzQBSfl46eSJVT/pMhEQAREQAREQAREQARHIyQTypvfF582b1+655x575ZVX7OGHH7aePXtau3btLFeuXK4p8dLJdP3119ukSZPshRdesM8++8z69etnRYoUSfhSnn76aduzZ4/hXfnwww9t/vz5rmyTJk2sV69etm/fPps5c6YtX77c9u/fb6VKlXJtpJ3evvrqK5s4caJt27bNChcubOecc45L6ty5s7Vu3dpni7ldsmSJffTRR/bNN99YgQIF7MILL7Rrr73WChYsaEePHrXXX3/dPv/8czt+/LjVrFnT+vfvb8WKFQvVOX36dJs3b54dOXLEatSoETrudyg7Y8YM+/rrr6106dLWqlUr69ixo0/WVgREQAREQAREQAREQARE4DSBdPcUQb1KlSo2dOhQ69Spk02ZMsV5jRAi3uKlIxoGDRpkt912myFOHnjgAVu9erUvHnfbrFkza9GihctXrVo1a9++vfvUrVvXHdu+fbutWrXK2rRpYzfeeKMTRXivNm/e7NLxzowaNcry5Mljd955p9WpU8eJLOqkvkQMMTN27Fjn6UEIIbg2bNjgBA7lx4wZY59++qkTMl27drWNGzfa8OHDQ54twv7effddq1y5shNS4R6vtWvXujry5ctnAwcOtPPPP9/eeustzTlKpHOURwREQAREQAREQAREIEcRyBBRBGE8Qt27d3fiBLGBuAlavHTy1qtXz3mNDh486AREsHysfTwmfDBEDIKED+IGq127tj300EMuT9GiRa169eru+KZNm9yWuTucExGEhwZRhSE8ECmJ2Mcff2xlypSxO+64wy6//HJD+Pz973+3kiVLGmF969evd/XiSUM8IpxYTMG3gXlVeH8GDx5sl112mQ0YMCDJaRctWuTC5nr37u3a1a1bN5eO90gmAiIgAiIgAiIgAiIgAiLwC4F0D5/zp2aAP378eDfI79KlizVo0MAnuW289GPHjtm0adPcQgm1atWyq666Kkn51Hw5dOiQjRs3znmfECmEz2GEqWF8J4wNb0/x4sVt4cKFljt3bic+XIY4/+DV+e677+ziiy8Ohd0Fi2zdutV9rVq1auiw3ycUDtFG+fr16ztxGcoU2NmyZYv7Rohi0H744YfgV+2LgAiIgAiIgAiIgAiIQI4nkCGiaM6cOTZ16lQnLoYMGXKGmIiXjmeJOUmE3PXp08fN4fFzkhLtUcLKMC90guUI6Vu3bp0LjcMTxIpueHS8IYAqVKhgO3fudItC4PEhlA+vUtDwJrHSHoIGAeON+UcIK+YjEYoX3nbqxoILUfj9ihUruvx4pbx4Im/4YgzkYz4UoogwP2+0XSYCIiACIiACIiACIiACIvALgXQXRXh4Jk+e7Cb89+jR4wxPR7x0ms4iCHhqECKEkKXEWNigRIkStnTpUhc2x3cWN8ALg1BCPPB9zZo1bjGG4Dl+/PFHF97WoUMH5+1BdOTPn/8MgYMgYjEEBBjzgbwQo66mTZu662CxCEL58E7NmjXLhcEheCpVqmTvv/++80QhomDGYhIXXHCBa0rDhg3dHKFXX33VhRGyYETQqH/lypXO48XiCpybRSlatmzpBF0wr/ZFQAREQAREQAREQAREICcTSHdRhIBg9TkWU4hk8dIpw/wYyod7WCLVF+sYq7mx2MGIESNcNgQHoggR8eWXX9rIkSOdmCDMLegFYr98+fJOLAXFCCLrvvvuc6vRUaH36pQrVy6JICKNuUIYK98hVrgW5jSxkh32hz/8wa2uN2HCBDc3CJF00003uZXpSGc+E6KH8gsWLHAiCyHnPUHNmzd3Hq733nvPnnrqKYpY2bJlXZii90S5g/pHBERABERABERABERABHI4gVynl6U+lfeWiXb8+f4JoeA3gljgILsYv+HDPBs8PUHhQzga4XkcC4afcd0scsB8qLvuussKFSrkRAvhdiwTTjhf27ZtQ3jwALFanhcroYTADgsrUA9tCDdC91g63Iul8HRC9Ggf54hmeLbIE62OaOV0XAREQAREQAREQAREQARSSoCIq0aNGiVUPDl6JKEKk5kp3T1FyWzfWc+OWGFOULjhucHzE8kIr+O3gxBCeJdYOIHV4qgLT1PQEhEi0c5DPYT1xbJEfp8pKPZi1aU0ERABERABERABERABEciJBHK8KEpJp19yySVuEQRWn+O3gpjfRHgbYYEKTUsJUZURAREQAREQAREQAREQgYwjIFGUAvaEuREmx0cmAiIgAiIgAiIgAiIgAiKQtQlofeas3X9qvQiIgAiIgAiIgAiIgAiIQCoJSBSlEqCKi4AIiIAIiIAIiIAIiIAIZG0CEkVZu//UehEQAREQAREQAREQAREQgVQSkChKJUAVFwEREAEREAEREAEREAERyNoEJIqydv+p9SIgAiIgAiIgAiIgAiIgAqkkIFGUSoAqLgIiIAIiIAIiIAIiIAIikLUJZKgoOnnyZEx68dJPnDgRs7wSRUAEREAEREAEREAEREAERCAegXT/nSKEzvTp023x4sW2Z88eK1WqlHXr1s34QVRv3377rU2YMMG+++4798OoTZo0sZ49e1ru3P+n4dasWWPTpk0z8hUoUMAaN25sffv2tbx50/1yfJO1FQEREAEREAEREAEREAERyKIE0t1T9J///MdmzZplzZs3twEDBljhwoVt3Lhxtm3bthDCsWPH2pEjR+y3v/2tde7c2WbPnm0rV6506fv27bN///vfVrx4cRs4cKA1atTI5s+fb++//36ovHZEQAREQAREQAREQAREQAREIFEC6e5aadWqldWpU8d5iGhk+fLl7fHHH7dNmzZZpUqVDE/Srl27rG3btla/fn13HXPnzrW1a9c6jxBiaNiwYVa6dGmXdtFFF7m0jRs3uu/6RwREQAREQAREQAREQAREQASSQyDdRRGNI2TO244dO9xuxYoV3ZYQOcLhEEJFihRxeb///nvr1auXLxISRBw4fPiwHThwwHmMQhm0IwIiIAIiIAIiIAIiIAIiIAIJEsgQUeTbdvDgQXvvvfcMQVS9enV/2Fq2bGlLly61KVOmuGNVq1a1atWqhdKDO5MnT7ZTp05ZixYtgoe1LwIiIAIiIAIiIAIiIAIiIAIJEUj3OUW+VUePHrVnn33WeXoGDx5suXLlcklbt251x6+++mr75z//aVdeeaVbUOGll17yRUNbFmxgjlKfPn3svPPOCx3XjgiIgAiIgAiIgAiIgAiIgAgkSiBDPEV4iBBE27dvt9tvv93KlCkTau+qVauc56dDhw5OKHXt2tWtUof4Yb6RX4Hu7bfftpkzZ1r37t2tdevWofLaEQEREAEREAEREAEREAEREIHkEEh3UcQy3CNGjDDmErVv39527tzpPvny5bOmTZu6xRb4/aF33nnHhdH98MMPtnr1aitXrpwTRAgjluteuHChValSxQmqRYsWuWuuW7euFS1aNDnXr7wiIAIiIAIiIAIiIAIiIAI5nEC6i6IFCxY4QQR3ltr2xqIKiKKGDRsa3qE5c+a4ZbYJq2PO0aBBg1xWRBSCCCPU7uWXX3b7/HPXXXdJFIVoaEcEREAEREAEREAEREAERCARAukuiq644grjE80Ij2MeEXn4TaKCBQu6H2j1+VnCe/To0f6rtiIgAiIgAiIgAiIgAiIgAiKQKgLpLooSbS0eohIlSiSaXflEQAREQAREQAREQAREQAREIEUEMmz1uRS1VoVEQAREQAREQAREQAREQAREII0JSBSlMVBVJwIiIAIiIAIiIAIiIAIikLUISBRlrf5Sa0VABERABERABERABERABNKYgERRGgNVdSIgAiIgAiIgAiIgAiIgAlmLgERR1uovtVYEREAEREAEREAEREAERCCNCUgUpTFQVScCIiACIiACIiACIiACIpC1CEgUZa3+UmtFQAREQAREQAREQAREQATSmIBEURoDVXUiIAIiIAIiIAIiIAIiIAJZi4BEUdbqL7VWBERABERABERABERABEQgjQmkuyg6efKkzZ07106dOhXxUuKlU2jx4sV24MCBiOWTc3DYsGE2e/bs5BTJdnnFINt1qS5IBERABERABERABEQgmQQyRBS98cYb9sQTT9iOHTvOaC6iKFY6BVasWGEPPvigLVu27IzyyTmwd+9e+/nnn2MWoT2xLF56rLI+LVYdiMdoAtKXT2Qb7RyJMEikfuURAREQAREQAREQAREQgaxKINeJEydO5b1loh1/vn9C14AgqVevXkJ5o2XaunWrvfLKK7Zz507r2bOntWvXznLlyhXKHi/9yJEjNmnSJFu0aJE1adLE+vXrZ0WKFAmVj7fz9NNP2549e2zXrl1WqFAhK1CggCtCXb169bJ9+/bZzJkzbfny5bZ//34rVaqUayPt9PbVV1/ZxIkTbdu2bVa4cGE755xzXFLnzp2tdevWPlvM7ZIlS+yjjz6yb775xrXhwgsvtGuvvdYKFixoR48etddff90+//xzO378uNWsWdP69+9vxYoVc3WOGTPGbQcPHuy233//vY0cOdK1n+uYMWOGbdmyxZXjHIcPH7ZLLrnErr76asuXL5/FYxCz4UoUAREQAREQAREQAREQgTgE1qxZY40aNYqT6/+Sk6NHEqowmZnS3VNE+6pUqWJDhw61Tp062ZQpU5zXCCHiLV46omHQoEF22223GeLkgQcesNWrV/vicbfNmjWzFi1auHzVqlWz9u3bu0/dunXdse3bt9uqVausTZs2duONNzpRhPdq8+bNLh3PzahRoyxPnjx25513Wp06dZzIok7qS8TmzZtnY8eOdV4ghBCCa8OGDYbgwxA9n376qbVq1cq6du1qGzdutOHDh4c8W4g1Pt5Oi1vXBsQURhrXQHjgpZdealWrVnUC7IsvvnDp8Ri4TPpHBERABERABERABERABHIAgbwZdY158+a17t27u8H7/Pnznbhp3LhxqDnx0smIx+r666+3ESNGOAFRv379UPlYOwgN5iS98847TsQEPUCUq127tj300EN26NAh+/bbb6169epOsGzatMnl3717tx08eNB69OhhNWrUcJ4XvD7nn3++Va5cOdapQ2kff/yxlSlTxu64446QlwnxgxHStn79euvYsaPzpHEMT9j48eONNiDCEjG8b/fcc4/zLrVs2dIJUUQRnOIxSKR+5REBERABERABERABERCB7EAgw0QRoWt+kN+lSxdr0KBBEp7x0o8dO2bTpk1znpBatWrZVVddlaR8ar4ghsaNG+e8TyVLlnSeIurzXhzC6Qhjw9tTvHhxW7hwoeXOnduJokTOyzym7777zi6++OKQIAqWI3wQw7vjze9//fXXCYsiyvpwO7aE+BFGJxMBERABERABERABERABEfiFQIaIojlz5tjUqVOd2BgyZMgZYiJeOmFszEki5K5Pnz5uDk9wTtIvlxd9j3k1mBc6wZyE9K1bt86FxuEJ+umnn5xHx+dBAFWoUMHNiSKsDo8PoXxFixb1WdwWbxIr7SFogl4sxAnCivlIhOKFt526seBCFH6/YsWKLo06mJPlLdJ1+LRo21gMopXRcREQAREQAREQAREQARHIbgTSXRTh4Zk8ebILDSP8jDC5oMVLJy+LIOD5QIiULl06WDzhfRZXKFGihC1dutR5XvjOfBxC5xAYCB++M0Hsww8/TFLvjz/+6MLbOnTo4Lw9zC3Knz//GQIHQTR9+v9v787Do6ruP45/Q0IIEBYBZVNwA7QIgoAWKyhYEMEFl4JIEevP5VFxR0F96vYTbdX6e9C6IdWKFhekoFAKFhAsiCCbCogbKIqCCAYIBEhCfvdzwh1uJplMZohJmHmf5xlm5t5z7z3ndfPH/fI958y/3PA6zQfygxCdrFOnTq4fY8eOdUPZlJ165513bPDgwS5IbN68uU2fPt1lohQAyUxD6Fq1auXaonfNQZo6daoLsCZPnlykjWX5UppBWY6nDgIIIIAAAggggAACiSBQNCKpgB4pgNA8Fy2mUFKJtl/H9O3b1x0fnmEp6XylbdNqblrsQHOSVBRoKCjSXJ4vv/zSreamQEbD3IJZIH1u0qSJC5aCAZOCrLvvvtutRqfzKaujNjZu3LhIQKR9WnVPRfOpli5d6upprpBWslO58cYbTQHTK6+8YlpOW0HSFVdc4Vam0/4uXbq4wExBlxaeUICpwClYFNiFl/BtkQzCj+M7AggggAACCCCAAAKJKlApS3JXJUwFHJs3b3aZnmDgo2FtGp6nbQrUgkU/Hqv5ULfddptb0lvn0HA7LROu4Xw9evQIVVcGSEFLeDASquB90MIKWhpc2abwoqF7WlnOD5bC9ytrpQxSaecPPyb8eySD8Hp8RwABBBBAAAEEEECgrAIH05LcFZ4pKitiRdVTMKE5QeFFGR5lfkoqGl6n3w5SIKTskhZO0GpxOpcyTcESKZgJ1ol0HdXxf0MpWD/4ORjIBbfH8jmSQSznoC4CCCCAAAIIIIAAAgerQNIHRfHcOP0IqhY+0OpzmtOj+U0a3qZhgf4iCfGcl2MQQAABBBBAAAEEEECg4gUIiuIw1zA3DZPTi4IAAggggAACCCCAAAIHt0DxmfgHd39oPQIIIIAAAggggAACCCAQkwBBUUxcVEYAAQQQQAABBBBAAIFEEyAoSrQ7Sn8QQAABBBBAAAEEEEAgJgGCopi4qIwAAggggAACCCCAAAKJJkBQlGh3lP4ggAACCCCAAAIIIIBATAIERTFxURkBBBBAAAEEEEAAAQQSTYCgKNHuKP1BAAEEEEAAAQQQQACBmAQq5XeK1q9fb6tWrbLFixdbZmam3XDDDTE1msoIIIAAAggggAACCCCAQHkJVEpQNG/ePFu5cqVlZWVZw4YNy6svnAcBBBBAAAEEEEAAAQQQiFmgUoKigQMHuoaOHj3aBUYxt5oDEEAAAQQQQAABBBBAAIFyEmBOUTlBchoEEEAAAQQQQAABBBA4OAUIig7O+0arEUAAAQQQQAABBBBAoJwECIrKCZLTIIAAAggggAACCCCAwMEpQFB0cN43Wo0AAggggAACCCCAAALlJFDhCy3k5OTY6tWrXfO3b99u2dnZtmzZMktNTbX27duXU7c4DQIIIIAAAggggAACCCBQNoEKD4q2bNliY8aMKdI6//uTTz5paWkV3qQibeELAggggAACCCCAAAIIJJdAhUcgzZs3t2eeeSa5lOktAggggAACCCCAAAIIVFkB5hRV2VtDwxBAAAEEEEAAAQQQQKAiBAiKKkKZayCAAAIIIIAAAggggECVFSAoqrK3hoYhgAACCCCAAAIIIIBARQhU+JyiiuhUaddIX9G3tN3s2yew54RpWCCAAAIIIIAAAgggkBQCZIqS4jbTSQQQQAABBBBAAAEEEIgkQFAUSYbtCCCAAAIIIIAAAgggkBQCBEVJcZvpJAIIIIAAAggggAACCEQSICiKJMN2BBBAAAEEEEAAAQQQSAoBgqKkuM10EgEEEEAAAQQQQAABBCIJEBRFkqng7eM+qGEn/6m+7c5LqeArczkEEEAAAQQQQAABBJJboMKDor1799qcOXOsoKCgRPlo+3XQokWLbPv27SUeH8vG3zxa356flxHLIb9Y3Q3bqtnib9Isf+/+S6z4PtWajWhg67Mq/DbtbwSfEEAAAQQQQAABBBBIcIEKf9pW0DNhwgR77LHHbOPGjcV4o+3XAcuXL7cHHnjAlixZUuz4WDas+iHVtu2qupmZ77dWMwVLwUAplv5RFwEEEEAAAQQQQAABBKILVPiPt6alpdmIESNs3LhxNmrUKDv//POtZ8+elpJSGJxE268uDRkyxN544w0bO3asLV261AYNGmSZmZnRe7uvxqV/q2Nrfkq1rTkp9n8za9r4RTXcnotO2mN39dlpWd72J2bXtInL0u2bzanW6rB8u+W3OXZpl92ha3y2MdVueC3TlqxLs8Pq7LVa6YWZr9t759glnffXCx0Q9mHnnhS7+Y3aNvWTdGuUWWBHNcoP1dC5r/lHZihD1O+v9Sw9rfD8zw7OtvbN863H4/VsgHedm3vmhI574F+1XLbp7eu22RPv1rRFa9Pcef/h9S891Wxo1112Z5/99d9fU90e+ndNW7i2uuvjNd1zbOivo7c9dEE+IIAAAggggAACCCCQAAIVnimSWYsWLWzkyJHWu3dvmzRpkssaZWVlhTij7a9Zs6YNHTrUhg0bZmvXrrX777/fVqxYETo+2offHp9rF3QsfPjv3DLPLvMCAb1+fVSuO3TVD2mmOT6Xnrzbnrk02xrWLrDLXqxjGs7ml75/rWs795hNuX6bdT0615Z9m+aClLZN9wc3ft2S3gd5gdmLCzLsyt/ssiu8l4bO+aV+zQK7oMMe6+K1TeWCDoXtUxsPr7/XangB0gnN8uzPM2ransIqbi7SaC+Q63hE4YZvt1Sz8R/WsFmr023EWTn2K69dd79V215bXBgALvQCprNG17W8/BQbPTDb2jTOtyvG1bFpK9L9ZvCOAAIIIIAAAggggEBSCFRKUCRZZYT69etnXbt2tTVr1rjgJigebb/qtm3b1mWNsrOzbeHChcHDS/18xam77Lruu1ydbq1y7SYv26JXzzaFQdGpXpDzxQM/2009dtlxTfKtX7s9ttdL1Pz3y+rumB+8YW1rvUzTVd12meoO71WYfVEQ0675viillBZsyq5mUz5Otzu8rNID5+102R5d3y+N6+517VHwpnLlabtCbWxar3DS0W3eNX/cXs1eX1IY5Ez+KN1lvv7HC7D8UrN6gc29Lcuu8dr52pXb7NDMvTbVu67Ki+9nWL7Xp+eHbLffHpdrf75wh9XzgrHJywmKfD/eEUAAAQQQQAABBJJDYH96ooL7u2nTJnv55Zftq6++sj59+li7du2KtCDa/tzcXJsyZYrNmjXL2rRpY/379y9y/IF80dC2G18vzKooQDn20MJAZPOOwhiyibftyIb59sL8DG/Y2V77+/s1rLqXRPKzNNGuvfzbwozTKfsyU9Hql7RfmZ3z2u+xJ71hckNO2e3a0KftHmvRYP9KDdW8EYlql0q6d6ePbLjX1nrDAVUWfp1mu3JTrOVdDdx3/5+vNu07wN/AOwIIIIAAAggggAACCS5QKUHR7Nmz7a233rKGDRvaHXfcYS1btizCHG2/Mkuak6QhdwMGDLDu3buH5iQVOVEpXxQkKGjILmGhhXun1HLDzGbevNUbUpdnud6IuBrDGpm/JIOmP53ibdeQtyEvZrqgacaNW+2QWoXzfvzLZu9OcRmZzi1zvSF2+zNIHY/It1QvvlruDblTYBOpZHiZHhWdp6Si+UunPVrPDXmb6Q2Tm3jNtpKquW15Xqz0pRfwXLhv2KCG0ynjtfr+n0P9UsW0SssdRmw6OxBAAAEEEEAAAQQQ+EUFKjwoUoZn4sSJ1qtXLzvnnHPcMLpgD6PtV90ZM2ZYvXr13JyiRo0aBQ8v82fNyznm0Hw3/OxcLzCpk1Fg270A6aQWeabhbVo4QYsTaCEFLcYQLKr3ujc35+ELdrjFF5SNqb1voYVgvTHect/D36xtDbw5SRsf2ewCIe1v5A1j07C7sV6mSW2o7wVTT80peg3VU+CiokUfhnsBUNbOFGvmzSlqtm8Inc6h12V/r2MaVtfvhKIBVo6XCXp4ek3r5Q3De3puhv3sHa+hgCoXecGR5hfdOqG23XzmLtNQOy0socUkatcoGty5A/gHAQQQQAABBBBAAIEEFajwoCg1NdWtPqfFFEoq0fbrmL59+7rFGvwV60o6T1m2PT0o2wY8X9c6P1zfVT/bG372r2Hb3PydmZ9Wd9sVHP2h6263ipsfKiiA+s0xuXbnpNru5V9Lw+f+O3xraCW61t6qdcoIafU6vQfL/3pziQaOreNlmuq4uTzKGL28sHB+kF+vw+F5dpu36p0WUFCApczWc97qc8F5Q5pbdNFzde2GM3KKXSPNC9YmLa/hFlhQmx/qv8P6n1gYFF3YcY/9bUi2KSv2d2/BB5XjvflTp3tzrIJD8Py28I4AAggggAACCCCAQKIKVHhQVK1aNRfQRAKNtl/HhQ+3i3SuaNvP9BYY+PHRzfa1N89GQYOW1lZRcPPtw1vsO+9HU5WB0ZCyJwMn+/fKdPtkfZotvjPLHaehafO/qm5Xv5LpMk9/8Ja+VjnHy8ps8DJE4cPqtK+7F3x8/6fCayjzo6DppcuL/yDtoxftsHv67XSLKmguU3gWR3OANBRQizGEl/TUAls0Msst7a2++fOL/HqXe+3US7+FpMxZSe306/KOAAIIIIAAAggggECiClR4UFTVIBWMaAhbeNG8oSMOKQySwvcpiNA8Hw2tO+3YXNvhfV7wVZoLOrp534NFy3lHKqVdI3iMArY6GUXbOGFpDZvm/cbRq94QuJt65Fhzb1hdeCnYd+mS9gXrKtiiIIAAAggggAACCCCQrAJJHxTFc+P146yrvN8senBaLfveW6xAvx3U3hvq9qGXOTq2hAArnmtEO+a9z6t7GZ4U+5M3r0lBUXhp7a1O1/tXRQO08Dp8RwABBBBAAAEEEEAAAW+xMRBiF9CiBBrWpldllScvyS710ld5w+n0oiCAAAIIIIAAAggggEDpAkkXFO05YVrpIuxFAAEEEEAAAQQQQACBpBIIWxMtqfpOZxFAAAEEEEAAAQQQQAABIyjijwABBBBAAAEEEEAAAQSSWoCgKKlvP51HAAEEEEAAAQQQQAABgiL+BhBAAAEEEEAAAQQQQCCpBQiKkvr203kEEEAAAQQQQAABBBCocqvP5efn288//+zuTO3ata1mzZoR75LqqX6jRo1CdbKzs23XrpKXoq5evbrVq1cvVHfnzp2mV506daxGjRpue25urm3dujVUJ/xDw4YNLSsry1JTU61u3bqh3X67a9WqZXr99NNPoX3B8/sbI7Wzfv36lpYW/bbs3r3btm/f7k5XrVo103F6Dy/x9lH9UT/DS3p6erF+f/311yYXtcEvBd4vx27evNm5qv8qvlF4H0u6j/55tmzZYhkZGc7U38Y7AggggAACCCCAAALlKRD96bs8rxbhXBs2bHBBxAknnGArVqywZ5991tVMSUmxxo0b28knn2xnn312kaNzcnLsnnvusby8PLv77rvt8MMPd/unTp1qc+fOLVLX/9K6dWu75ZZb/K/20ksv2ccff2x9+vSx888/323XA/7jjz8eqhP+4bHHHrPnnnvOPfA/+OCDoWBK15wwYYJdf/31duihh9p9991X5FBtGzx4sLVp08Ztnzhxon3wwQdF6ujLnXfeaS1atCi2PXzD9OnTTS+/KCA68cQT3TUUTPol3j7++OOP9sgjj/inCb23bdvWhg0b5r4vWrTIXnvtNdO9UGnVqpXbp8BJQdsf//hHF0DJSQGpf29HjBhhRx55pDsm0n3UTp1D9zZ4f9xB5fiP/tbWrVtX5Ixnnnmm6aWiPh5//PEucC5SiS8IIIAAAggggAACCSNQqUGRsgkzZ860KVOmWO/evU1BkbapDBw40D2IKnB4++23XZbhnHPOCcF/8sknbpsetpcsWRIKis466yzr0qWLq6cgRVmIq6++2n0PZp2UTVq1apXpAX7p0qWhoEgP68OHD3f19RCvwOP3v/+9NWnSJHQOteOpp56y9957z3r16uXaoX4cddRRrg8bN250ddWWjh072nfffef6OGbMGFNQpWBPRW2/6aab3Gf/HwWBZSm+0+233+4Cw+XLl9u7775rzZo1M9/pQPp4xBFHOIc9e/bYE0884UxPP/30UMZm/fr19sorrziXCy64wL755ht76623bPz48Xb55ZeHurBt2zabN2+e9ejRI3RvQzu9D5Huo+r4fQzWL+mz+rl27VoXlJUlyxY8h+6VgshOnTqFNrds2TL0Wa76O7rkkkuK1AlV4AMCCCCAAAIIIIDAQS9QaUGRHkbHjRtnyswoE6BXsCgIOe6446xDhw4uc/POO+9Y3759Q0PEFMgogDnkkEOKBDX6rpdKZmamaZjaMcccEzy1+6wMkbJMF198sb355psucFG2SYGKX1/ZEhVtDz4oK3g7+uij7T//+Y+dccYZtnjxYhd8XXbZZa6+/4+G6uk4vTScTgHWDz/84AIX1VFw5F/LPybWdwVBGl527LHH2vz5811w4J/jQPqozJPapmyNioa8BduqQFRDDYcOHWrNmzd32RTdyw8//NAFkX4b5Kl7161bN39TkfdI97FIpShfnn76afviiy+se/fuNmjQoCi1i+5WpkqBa/jfn19ryJAh9sYbb9jYsWPd35nOr78rCgIIIIAAAggggEDiCBSfhFIBfdNQs1GjRpmyEBoudu6550acR6O5OxoWpgdwBRQqfgZEQ7kUoCh4UeYilqKH8aZNm9qpp57qAi19j6WozZrToyyIgh0NHVMQV1JR8LVmzRo3D0lzb/yi/s+aNSv00oN9PEUeaofOpwyPXw60j/55SnqXt+ZOKSDyi4Yn7t271zQc0i9du3Z1c7Tef/99f1PovTzuo04m3+C7+1LGfzTnavXq1S7zpyA9/O9A2UUFfhoyqGzU/fff74YBlvH0VEMAAQQQQAABBBA4CAQqJVOkYWcKdq688ko3Zyiak5/5UbZFD+EacqUgqV27di6DoYyLHmaDD+ilnVPZDw2d05AuPfQqA7Js2TI777zzSjusyD4FQAqElGVSIKAhduFFw9l0Xg0tU8Ci6/kLOvh1g/OCNDxN54ylaH6Ozq2i/itbolIefXQnivCPhiX6Cyj4Vfzv2ucvfqF7p2zfjBkz7KKLLvKruvcDvY/+ya655hr7/PPPXYDsbyvLu4bnNWjQwC0SofauXLnSFixYYL/73e+sZ8+eRU6hAFxZIw0lXLhwYczXKnIyviCAAAIIIIAAAghUKYFKCYo0zEwLADz00EPWv39/NwTNn2dTko4/jE1DxVTmzJnj3jWkSUUPt9OmTXMZJ7chyj/+w7iGdSlo2bRpkzvi+++/Dw1ti3IKt1tB1F/+8heXISopmFGQoDlCCqA03C48k6T5TI8++mhZLhWxzoUXXugWO1AFLeSgh3yV8uqjO1kJ/xx22GHuGsFdCoZUtC9YNOxRmcGPPvoouPmA76N/Mg1T9OeR+dvK8q6/ueCCGAoulbnU4grBoEgBuOa9KaunhTL0N0tBAAEEEEAAAQQQSByBShk+pzk2d911l8ucKNOi1d78wCScVnM+9JCqjI7+N18ZEA1FU1FmJTj53x9eF36O8O/+ECkFFDr+tNNOc1X87eH1I333h6r57+H1Onfu7AIVBQXhAVF43UjfNU9IwZs/RCy83imnnOICDs3d+ec//xna7fflQPsYOmHYB2WlFERo6JmKAlMFPWqHVtoLFs3Jat++vbuP/vbyuI/+uTSXafLkyRH/hvx64e/K8AVdFaQqqFQQ5Bf9rSmgU3ZzwIABbmGM4BBIvx7vCCCAAAIIIIAAAgevQKVkisSlVcL0P+4aWqWskeac+Mtia/9nn33m5nBoQr8CJq1opv/Z14pwKtdee6170NZnzWVR1kmBQL9+/bQpYtHDuM6hZb61cpxfdD0d76/c5m//Jd/1UB6+LLf64md7FORphTcVBYQnnXRSic1RfS0hraF48vKXNj+QPmqBCjn5Q/PUFrVVCy4owNNcodmzZ9vLL79sWmVPgYkCCC2dXtLvJSkw1MIPfonlPiqDF3TSQgfqo180F0jt0/BKDcksa9Hy7VpdTm3WMD/ZaaVA9ccvGvanTJTmFPlDAv19vCOAAAIIIIAAAggkhkClBUU+35HeCnL6LZrwTJEe8DWRX8POgsts+9mIYOZFmRo9rGtfMChSEBX+gK7MhjIBylwEi+Yn6SFf7QjPdATrlddntU1ZCgWEwXLVVVeFgiL1SctFKzCJtlS3jJRVUrZI87UOtI/64dVg2xTE6KW5NbJX29RWrcz26quvuhXwlHHTAhQq6p9eflF2UMdq3o7uSSzsibNjAAAFY0lEQVT3UcFUMKDSuYJBkbJWCorKOqfMb5MyeQrkXnjhBbdJWS7NyfL7oI0K5vS7UcG++MfzjgACCCCAAAIIIJAYAin5+fkFaVeNt7znLy1Tj/Q/63q4pVSMgIIb7x65oKNirhj7VbQKn4Y3xvobQbFfKfIRaoO/0EPkWiXvUfZQmTFli8KD6JKPYCsCCCCAAAIIIIBANAH9Z7hGhZWlxBKPlOV8sdap9ExRrA1OtvrKXuhVlUu8wUh59ulA2qAVAcNXBSzPtnEuBBBAAAEEEEAAgaotUCkLLVRtElqHAAIIIIAAAggggAACySRAUJRMd5u+IoAAAggggAACCCCAQDEBgqJiJGxAAAEEEEAAAQQQQACBZBIgKEqmu01fEUAAAQQQQAABBBBAoJgAQVExEjYggAACCCCAAAIIIIBAMgkQFCXT3aavCCCAAAIIIIAAAgggUEyAoKgYCRsQQAABBBBAAAEEEEAgmQQIipLpbtNXBBBAAAEEEEAAAQQQKCZAUFSMhA0IIIAAAggggAACCCCQTAIERcl0t+krAggggAACCCCAAAIIFBMgKCpGwgYEEEAAAQQQQAABBBBIJgGComS62/QVAQQQQAABBBBAAAEEigkQFBUjYQMCCCCAAAIIIIAAAggkkwBBUTLdbfqKAAIIIIAAAggggAACxQQIioqRsAEBBBBAAAEEEEAAAQSSSYCgKJnuNn1FAAEEEEAAAQQQQACBYgIERcVI2IAAAggggAACCCCAAALJJEBQlEx3m74igAACCCCAAAIIIIBAMQGComIkbEAAAQQQQAABBBBAAIFkEkiLp7MrV66M5zCOQQABBBBAAAEEEEAAAQSqnEDMQVGHDh2qXCdoEAIIIIAAAggggAACCCAQrwDD5+KV4zgEEEAAAQQQQAABBBBICAGCooS4jXQCAQQQQAABBBBAAAEE4hUgKIpXjuMQQAABBBBAAAEEEEAgIQQIihLiNtIJBBBAAAEEEEAAAQQQiFeAoCheOY5DAAEEEEAAAQQQQACBhBAgKEqI20gnEEAAAQQQQAABBBBAIF4BgqJ45TgOAQQQQAABBBBAAAEEEkKAoCghbiOdQAABBBBAAAEEEEAAgXgFCIrileM4BBBAAAEEEEAAAQQQSAgBgqKEuI10AgEEEEAAAQQQQAABBOIVICiKV47jEEAAAQQQQAABBBBAICEECIoS4jbSCQQQQAABBBBAAAEEEIhXgKAoXjmOQwABBBBAAAEEEEAAgYQQIChKiNtIJxBAAAEEEEAAAQQQQCBeAYKieOU4DgEEEEAAAQQQQAABBBJCgKAoIW4jnUAAAQQQQAABBBBAAIF4BQiK4pXjOAQQQAABBBBAAAEEEEgIAYKihLiNdAIBBBBAAAEEEEAAAQTiFSAoileO4xBAAAEEEEAAAQQQQCAhBAiKqthtfP31123y5MmhVoV/D+2o5A87duywTz/9tFxbkZ2dbaNHj7bPPvusXM9bmSdTX0aNGmW33nqrLViwwNavX28bNmwINamq3t9QA/mAAAIIIIAAAggkgUBaPH2899577YwzzrAePXq4w+fPn28ZGRnWqVOneE5X5mPWrVtnixYtsosvvrjMxxxsFefOnWuZmZnWv39/1/Tw78H+FBQUmB6qu3fvbs2aNQvu+sU/P/vsszZlyhQbM2aMtW7dOubrldT2nTt32qRJk6xVq1bWpk2bmM8ZfsB9991n+fn54Zvd9wYNGtgtt9ziArvx48fbddddZ02bNg19Dz+odu3aNnLkyKj7g8fl5eW5Y9LS0qxbt25Wv359u/baa9391TVVSru/wXPxGQEEEEAAAQQQQOCXE/h/Ix9dG9Ya8fQAAAAASUVORK5CYII="}}},{"cell_type":"code","source":"with TiffFile(sample_train_image) as tif:\n    print(f\"--- File Overview ---\")\n    print(f\"Number of pages: {len(tif.pages)}\")\n    \n    # Check if it's a specific type of TIFF (BigTIFF, OME, etc)\n    print(f\"Is BigTIFF: {tif.is_bigtiff}\")\n    print(f\"Is MDG (Metadata): {tif.is_mdgel}\") # Just as an example\n    \n    # Get metadata from the first page\n    first_page = tif.pages[0]\n    \n    print(f\"\\n--- Detailed Metadata (Page 0) ---\")\n    all_tags = {}\n    for tag in first_page.tags:\n        # We use .name and .value; if value is bytes, we decode it\n        name = tag.name\n        value = tag.value\n        \n        # Handle byte strings for better readability\n        if isinstance(value, bytes):\n            try:\n                value = value.decode('utf-8').strip('\\x00')\n            except:\n                pass\n                \n        all_tags[name] = value\n    \n    # Print as a nice JSON block\n    print(json.dumps(all_tags, indent=4, default=str))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.181340Z","iopub.execute_input":"2026-01-14T08:15:12.181603Z","iopub.status.idle":"2026-01-14T08:15:12.197624Z","shell.execute_reply.started":"2026-01-14T08:15:12.181579Z","shell.execute_reply":"2026-01-14T08:15:12.196641Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Specific metadata","metadata":{}},{"cell_type":"code","source":"with TiffFile(sample_train_image) as tif:\n    # Check for specialized metadata formats\n    if tif.is_geotiff:\n        print(\"GeoTIFF Metadata:\", tif.geotiff_metadata)\n        \n    if tif.is_imagej:\n        print(\"ImageJ Metadata:\", tif.imagej_metadata)\n        \n    # Print the shape and axes (e.g., 'CYX' for Color, Y-axis, X-axis)\n    print(\"\\nData Layout:\")\n    for series in tif.series:\n        print(f\"Shape: {series.shape}, Axes: {series.axes}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.198754Z","iopub.execute_input":"2026-01-14T08:15:12.199096Z","iopub.status.idle":"2026-01-14T08:15:12.240989Z","shell.execute_reply.started":"2026-01-14T08:15:12.199059Z","shell.execute_reply":"2026-01-14T08:15:12.239730Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Labels\nExplore Labels ( one label)","metadata":{}},{"cell_type":"code","source":"sample_train_label='/kaggle/input/vesuvius-challenge-surface-detection/train_labels/1004283650.tif'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.242370Z","iopub.execute_input":"2026-01-14T08:15:12.242697Z","iopub.status.idle":"2026-01-14T08:15:12.248539Z","shell.execute_reply.started":"2026-01-14T08:15:12.242670Z","shell.execute_reply":"2026-01-14T08:15:12.247557Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with TiffFile(sample_train_label) as tif:\n    # 1. Get the total number of pages\n    num_pages = len(tif.pages)\n    \n    # 2. Pick a random index\n    random_idx = random.randint(0, num_pages - 1)\n    \n    # 3. Access the random page and convert to array\n    target_page = tif.pages[random_idx]\n    image = target_page.asarray()\n    \n    # 4. Display the image\n    plt.figure(figsize=(8, 8))\n    \n    # Check if the image has color channels (usually 3 or 4 channels)\n    # If image.ndim is 2, it's grayscale; if 3, it's likely RGB/RGBA\n    if image.ndim == 2:\n        plt.imshow(image, cmap='gray')\n        plt.title(f\"Random Page: {random_idx} (Grayscale)\")\n    else:\n        plt.imshow(image)\n        plt.title(f\"Random Page: {random_idx} (Color)\")\n        \n    plt.axis('off')\n    plt.show()\n\n# Optional: Print metadata for the selected page\nprint(f\"Image Shape: {image.shape}\")\nprint(f\"Data Type: {image.dtype}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.253430Z","iopub.execute_input":"2026-01-14T08:15:12.253811Z","iopub.status.idle":"2026-01-14T08:15:12.447742Z","shell.execute_reply.started":"2026-01-14T08:15:12.253784Z","shell.execute_reply":"2026-01-14T08:15:12.446845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with TiffFile(sample_train_label) as tif:\n    # 1. Get the total number of pages\n    num_pages = len(tif.pages)\n    \n    # 2. Pick a random index\n    random_idx = random.randint(0, num_pages - 1)\n    \n    # 3. Access the random page and convert to array\n    target_page = tif.pages[random_idx]\n    image = target_page.asarray()\n    \n    # 4. Display the image\n    plt.figure(figsize=(8, 8))\n    \n    # Check if the image has color channels (usually 3 or 4 channels)\n    # If image.ndim is 2, it's grayscale; if 3, it's likely RGB/RGBA\n    if image.ndim == 2:\n        plt.imshow(image, cmap='gray')\n        plt.title(f\"Random Page: {random_idx} (Grayscale)\")\n    else:\n        plt.imshow(image)\n        plt.title(f\"Random Page: {random_idx} (Color)\")\n        \n    plt.axis('off')\n    plt.show()\n\n# Optional: Print metadata for the selected page\nprint(f\"Image Shape: {image.shape}\")\nprint(f\"Data Type: {image.dtype}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.449028Z","iopub.execute_input":"2026-01-14T08:15:12.449411Z","iopub.status.idle":"2026-01-14T08:15:12.619600Z","shell.execute_reply.started":"2026-01-14T08:15:12.449381Z","shell.execute_reply":"2026-01-14T08:15:12.618324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with TiffFile(sample_train_label) as tif:\n    print(f\"--- File Overview ---\")\n    print(f\"Number of pages: {len(tif.pages)}\")\n    \n    # Check if it's a specific type of TIFF (BigTIFF, OME, etc)\n    print(f\"Is BigTIFF: {tif.is_bigtiff}\")\n    print(f\"Is MDG (Metadata): {tif.is_mdgel}\") # Just as an example\n    \n    # Get metadata from the first page\n    first_page = tif.pages[0]\n    \n    print(f\"\\n--- Detailed Metadata (Page 0) ---\")\n    all_tags = {}\n    for tag in first_page.tags:\n        # We use .name and .value; if value is bytes, we decode it\n        name = tag.name\n        value = tag.value\n        \n        # Handle byte strings for better readability\n        if isinstance(value, bytes):\n            try:\n                value = value.decode('utf-8').strip('\\x00')\n            except:\n                pass\n                \n        all_tags[name] = value\n    \n    # Print as a nice JSON block\n    print(json.dumps(all_tags, indent=4, default=str))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.620698Z","iopub.execute_input":"2026-01-14T08:15:12.621040Z","iopub.status.idle":"2026-01-14T08:15:12.640337Z","shell.execute_reply.started":"2026-01-14T08:15:12.621011Z","shell.execute_reply":"2026-01-14T08:15:12.639311Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with TiffFile(sample_train_label) as tif:\n    # Check for specialized metadata formats\n    if tif.is_geotiff:\n        print(\"GeoTIFF Metadata:\", tif.geotiff_metadata)\n        \n    if tif.is_imagej:\n        print(\"ImageJ Metadata:\", tif.imagej_metadata)\n        \n    # Print the shape and axes (e.g., 'CYX' for Color, Y-axis, X-axis)\n    print(\"\\nData Layout:\")\n    for series in tif.series:\n        print(f\"Shape: {series.shape}, Axes: {series.axes}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.641504Z","iopub.execute_input":"2026-01-14T08:15:12.641781Z","iopub.status.idle":"2026-01-14T08:15:12.668988Z","shell.execute_reply.started":"2026-01-14T08:15:12.641754Z","shell.execute_reply":"2026-01-14T08:15:12.668030Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Pages of one label file","metadata":{}},{"cell_type":"code","source":"with TiffFile(sample_train_label) as tif:\n    num_images = len(tif.pages)\n    \n    # 1. Setup the Grid Dimensions\n    cols = 20  # Number of images per row (increase to make images smaller)\n    rows = math.ceil(num_images / cols)\n    \n    # 2. Create the figure with a large height to accommodate all rows\n    # figsize=(width, height). We keep width fixed, height scales with rows.\n    fig, axes = plt.subplots(rows, cols, figsize=(20, rows * 1.5))\n    \n    # Flatten the 2D grid of axes into a 1D list for easy looping\n    axes = axes.flatten() \n\n    # 3. Loop through pages and plot\n    for i, page in enumerate(tif.pages):\n        image = page.asarray()\n        \n        # Plot on the specific subplot\n        axes[i].imshow(image, cmap='gray')\n        axes[i].axis('off')  # Hide axis ticks for a cleaner view\n        axes[i].set_title(f\"{i}\", fontsize=8) # Optional: Add index number\n\n    # 4. Hide any unused empty subplots at the end\n    for j in range(i + 1, len(axes)):\n        axes[j].axis('off')\n\n    plt.tight_layout() # Adjusts spacing to prevent overlap\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:12.670091Z","iopub.execute_input":"2026-01-14T08:15:12.670409Z","iopub.status.idle":"2026-01-14T08:15:30.704457Z","shell.execute_reply.started":"2026-01-14T08:15:12.670381Z","shell.execute_reply":"2026-01-14T08:15:30.703298Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"SIde by side Training and Labels","metadata":{}},{"cell_type":"code","source":"with TiffFile(sample_train_image) as tif_img, TiffFile(sample_train_label) as tif_lbl:\n    num_pages = len(tif_img.pages)\n    \n    # 1. Pick 8 random indices\n    indices = random.sample(range(num_pages), 8)\n    \n    # 2. Setup the plot (2 rows, 8 columns)\n    fig, axes = plt.subplots(2, 8, figsize=(20, 6))\n    \n    for i, idx in enumerate(indices):\n        # Load image and label for the same index\n        img_array = tif_img.pages[idx].asarray()\n        lbl_array = tif_lbl.pages[idx].asarray()\n        \n        # --- Top Row: Images ---\n        axes[0, i].imshow(img_array, cmap='gray' if img_array.ndim == 2 else None)\n        axes[0, i].set_title(f\"Img Index: {idx}\", fontsize=10)\n        axes[0, i].axis('off')\n        \n        # --- Bottom Row: Labels ---\n        # We often use 'jet' or 'viridis' for labels to make classes stand out\n        axes[1, i].imshow(lbl_array, cmap='nipy_spectral') \n        axes[1, i].set_title(f\"Label Index: {idx}\", fontsize=10)\n        axes[1, i].axis('off')\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:30.705662Z","iopub.execute_input":"2026-01-14T08:15:30.705930Z","iopub.status.idle":"2026-01-14T08:15:32.122371Z","shell.execute_reply.started":"2026-01-14T08:15:30.705906Z","shell.execute_reply":"2026-01-14T08:15:32.120461Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Overlaying labels with transparency is the gold standard for verifying segmentation quality. It allows you to see exactly where a mask might be \"bleeding\" over an edge or missing a feature.\n\nThe key technique here is to plot the image first, then plot the label immediately after with an alpha parameter (transparency) on the same axis.\n\nOverlaying Masks with Alpha\n\nIn this code, we'll pick 4 random samples to show them at a larger, clearer size.with TiffFile(sample_train_image) as tif_img, ","metadata":{}},{"cell_type":"markdown","source":"Issue with Ground Truth Completeness in TIFF Segmentation Dataset\n\nWe are visualizing a multi-page TIFF dataset (images and corresponding semantic labels) using tifffile and matplotlib. Upon overlaying the masks with transparency (α=0.5), I’ve noticed a consistent discrepancy in the labeling completeness.\n\nAs seen in the attached overlays, the segmentation masks appear \"under-segmented\" or incomplete:\n\n(Yellow Masks): Large regions of high-intensity features are left unmasked, while only a small strip is labeled.\n\n Questions:\n\nDataset Intent: Is this a \"sparse labeling\" strategy where only specific instances are labeled, or should every visible feature be captured in the ground truth?\n\nPreprocessing Error: Could this be a result of a misalignment in the TIFF pages between the sample_train_image and sample_train_label files?\n\nModel Impact: If I train a model on these partially labeled images, how should I handle the \"unlabeled\" features to prevent the model from learning them as \"background\"?\n\n#### Explanation:\n**train_labels**/ Label masks encoded such that **0 = background, 1 = foreground, 2 = unlabeled.** Most masks only cover a subset of the volume.","metadata":{}},{"cell_type":"code","source":"with TiffFile(sample_train_image) as tif_img, TiffFile(sample_train_label) as tif_lbl:\n    num_pages = len(tif_img.pages)\n    indices = random.sample(range(num_pages), 4) # Showing 4 for better detail\n    \n    fig, axes = plt.subplots(1, 4, figsize=(20, 5))\n    \n    for i, idx in enumerate(indices):\n        img = tif_img.pages[idx].asarray()\n        lbl = tif_lbl.pages[idx].asarray()\n        \n        # 1. Display the base image\n        axes[i].imshow(img, cmap='gray')\n        \n        # 2. Overlay the label\n        # alpha=0.5 makes it 50% transparent\n        # cmap='jet' or 'rainbow' helps distinguish different classes\n        # we mask the '0' values (background) so they remain fully transparent\n        import numpy as np\n        masked_lbl = np.ma.masked_where(lbl == 0, lbl) \n        \n        axes[i].imshow(masked_lbl, cmap='spring', alpha=0.5)\n        \n        axes[i].set_title(f\"Overlay Page: {idx}\")\n        axes[i].axis('off')\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:32.123814Z","iopub.execute_input":"2026-01-14T08:15:32.124118Z","iopub.status.idle":"2026-01-14T08:15:33.253759Z","shell.execute_reply.started":"2026-01-14T08:15:32.124093Z","shell.execute_reply":"2026-01-14T08:15:33.252177Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"This code snippet demonstrates how to access and inspect specific metadata tags within a TIFF file using the Python tifffile library. In this specific example, the code is extracting the XResolution tag from the first page of the file temp.tif.\n\nTIFF files are container formats that store image data alongside a rich set of metadata tags (like resolution, dimensions, and color space). This snippet breaks down the properties of a single tag object.","metadata":{}},{"cell_type":"markdown","source":"A short glimpse in deprecated dataset.","metadata":{}},{"cell_type":"code","source":"sample_train_image='/kaggle/input/vesuvius-challenge-surface-detection/deprecated_train_images/1407735.tif'\nsample_train_label='/kaggle/input/vesuvius-challenge-surface-detection/deprecated_train_labels/1407735.tif'\nwith TiffFile(sample_train_image) as tif_img, TiffFile(sample_train_label) as tif_lbl:\n    num_pages = len(tif_img.pages)\n    indices = random.sample(range(num_pages), 4) # Showing 4 for better detail\n    \n    fig, axes = plt.subplots(1, 4, figsize=(20, 5))\n    \n    for i, idx in enumerate(indices):\n        img = tif_img.pages[idx].asarray()\n        lbl = tif_lbl.pages[idx].asarray()\n        \n        # 1. Display the base image\n        axes[i].imshow(img, cmap='gray')\n        \n        # 2. Overlay the label\n        # alpha=0.5 makes it 50% transparent\n        # cmap='jet' or 'rainbow' helps distinguish different classes\n        # we mask the '0' values (background) so they remain fully transparent\n        import numpy as np\n        masked_lbl = np.ma.masked_where(lbl == 0, lbl) \n        \n        axes[i].imshow(masked_lbl, cmap='spring', alpha=0.5)\n        \n        axes[i].set_title(f\"Overlay Page: {idx}\")\n        axes[i].axis('off')\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:33.255198Z","iopub.execute_input":"2026-01-14T08:15:33.255821Z","iopub.status.idle":"2026-01-14T08:15:34.876612Z","shell.execute_reply.started":"2026-01-14T08:15:33.255788Z","shell.execute_reply":"2026-01-14T08:15:34.874055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with TiffFile(sample_train_image) as tif:\n        tag = tif.pages[0].tags['XResolution']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:34.878696Z","iopub.execute_input":"2026-01-14T08:15:34.879160Z","iopub.status.idle":"2026-01-14T08:15:34.890000Z","shell.execute_reply.started":"2026-01-14T08:15:34.879118Z","shell.execute_reply":"2026-01-14T08:15:34.887714Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tag.value","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:34.892406Z","iopub.execute_input":"2026-01-14T08:15:34.892911Z","iopub.status.idle":"2026-01-14T08:15:34.920444Z","shell.execute_reply.started":"2026-01-14T08:15:34.892866Z","shell.execute_reply":"2026-01-14T08:15:34.919379Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tag.name","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:34.921663Z","iopub.execute_input":"2026-01-14T08:15:34.921969Z","iopub.status.idle":"2026-01-14T08:15:34.948858Z","shell.execute_reply.started":"2026-01-14T08:15:34.921943Z","shell.execute_reply":"2026-01-14T08:15:34.947543Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tag.code","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:34.950980Z","iopub.execute_input":"2026-01-14T08:15:34.951554Z","iopub.status.idle":"2026-01-14T08:15:34.985331Z","shell.execute_reply.started":"2026-01-14T08:15:34.951509Z","shell.execute_reply":"2026-01-14T08:15:34.983871Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tag.count","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:34.986589Z","iopub.execute_input":"2026-01-14T08:15:34.986879Z","iopub.status.idle":"2026-01-14T08:15:35.012810Z","shell.execute_reply.started":"2026-01-14T08:15:34.986854Z","shell.execute_reply":"2026-01-14T08:15:35.011358Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Image Processing -experiment with some imagej like filters","metadata":{}},{"cell_type":"code","source":"from skimage import filters, segmentation, morphology, exposure, feature, measure\nfrom scipy import ndimage as ndi\n\ndef apply_imagej_filter(image, filter_name):\n    \"\"\"Maps ImageJ filter names to Python implementations.\"\"\"\n    filter_name = filter_name.lower().replace(\" \", \"_\")\n    \n    if filter_name == \"convert_to_mask\":\n        # ImageJ 'Make Binary' style\n        thresh = filters.threshold_otsu(image)\n        return (image > thresh).astype(np.uint8) * 255\n    \n    elif filter_name == \"distance_map\":\n        # Binary must be inverted for distance map in some workflows\n        thresh = image > filters.threshold_otsu(image)\n        return ndi.distance_transform_edt(thresh)\n    \n    elif filter_name == \"watershed\":\n        # Simplified ImageJ Watershed (Distance Map + Watershed)\n        thresh = image > filters.threshold_otsu(image)\n        distance = ndi.distance_transform_edt(thresh)\n        coords = feature.peak_local_max(distance, footprint=np.ones((3, 3)), labels=thresh)\n        mask = np.zeros(distance.shape, dtype=bool)\n        mask[tuple(coords.T)] = True\n        markers, _ = ndi.label(mask)\n        labels = segmentation.watershed(-distance, markers, mask=thresh)\n        return labels\n    \n    elif filter_name == \"enhance_contrast\":\n        # Equivalent to ImageJ 'Enhance Contrast' with 0.3% saturated pixels\n        p2, p98 = np.percentile(image, (0.3, 99.7))\n        return exposure.rescale_intensity(image, in_range=(p2, p98))\n    \n    elif filter_name == \"find_maxima\":\n        # Returns a coordinate map of local peaks\n        coords = feature.peak_local_max(image, min_distance=20)\n        mask = np.zeros_like(image)\n        mask[tuple(coords.T)] = 255\n        return mask\n\n    elif filter_name == \"find_edges\":\n        return filters.sobel(image)\n    \n    elif filter_name == \"sharpen\":\n        return filters.unsharp_mask(image, radius=1, amount=1)\n    \n    else:\n        print(f\"Filter {filter_name} not recognized. Returning original.\")\n        return image\n\ndef process_tiff_stack(file_path, filter_choice=\"find_edges\", num_to_show=6):\n    with tifffile.TiffFile(file_path) as tif:\n        num_to_show = min(num_to_show, len(tif.pages))\n        rows = math.ceil(num_to_show / 3)\n        fig, axes = plt.subplots(rows, 3, figsize=(18, rows * 6))\n        axes = axes.flatten()\n\n        for i in range(num_to_show):\n            raw_image = tif.pages[i].asarray()\n            processed = apply_imagej_filter(raw_image, filter_choice)\n            \n            axes[i].imshow(processed, cmap='magma' if filter_choice == 'distance_map' else 'gray')\n            axes[i].set_title(f\"Page {i}: {filter_choice}\")\n            axes[i].axis('off')\n\n        for j in range(i + 1, len(axes)):\n            axes[j].axis('off')\n        \n        plt.tight_layout()\n        plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:35.014786Z","iopub.execute_input":"2026-01-14T08:15:35.015294Z","iopub.status.idle":"2026-01-14T08:15:37.085811Z","shell.execute_reply.started":"2026-01-14T08:15:35.015231Z","shell.execute_reply":"2026-01-14T08:15:37.084465Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Watershed filter","metadata":{}},{"cell_type":"code","source":"\nprocess_tiff_stack(sample_train_image, filter_choice=\"Watershed\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:37.087692Z","iopub.execute_input":"2026-01-14T08:15:37.088436Z","iopub.status.idle":"2026-01-14T08:15:38.372977Z","shell.execute_reply.started":"2026-01-14T08:15:37.088331Z","shell.execute_reply":"2026-01-14T08:15:38.371669Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"convert_to_mask","metadata":{}},{"cell_type":"code","source":"process_tiff_stack(sample_train_image, filter_choice=\"convert_to_mask\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:38.374375Z","iopub.execute_input":"2026-01-14T08:15:38.374956Z","iopub.status.idle":"2026-01-14T08:15:39.385892Z","shell.execute_reply.started":"2026-01-14T08:15:38.374915Z","shell.execute_reply":"2026-01-14T08:15:39.384458Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"distance_map","metadata":{}},{"cell_type":"code","source":"process_tiff_stack(sample_train_image, filter_choice=\"distance_map\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:39.387721Z","iopub.execute_input":"2026-01-14T08:15:39.388258Z","iopub.status.idle":"2026-01-14T08:15:40.536072Z","shell.execute_reply.started":"2026-01-14T08:15:39.388213Z","shell.execute_reply":"2026-01-14T08:15:40.534824Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"enhance_contrast","metadata":{}},{"cell_type":"code","source":"process_tiff_stack(sample_train_image, filter_choice=\"enhance_contrast\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:40.537438Z","iopub.execute_input":"2026-01-14T08:15:40.537859Z","iopub.status.idle":"2026-01-14T08:15:42.056938Z","shell.execute_reply.started":"2026-01-14T08:15:40.537828Z","shell.execute_reply":"2026-01-14T08:15:42.055788Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"find_maxima","metadata":{}},{"cell_type":"code","source":"process_tiff_stack(sample_train_image, filter_choice=\"find_maxima\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:42.058117Z","iopub.execute_input":"2026-01-14T08:15:42.058430Z","iopub.status.idle":"2026-01-14T08:15:42.979294Z","shell.execute_reply.started":"2026-01-14T08:15:42.058403Z","shell.execute_reply":"2026-01-14T08:15:42.978354Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"sharpen","metadata":{}},{"cell_type":"code","source":"process_tiff_stack(sample_train_image, filter_choice=\"sharpen\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:42.980632Z","iopub.execute_input":"2026-01-14T08:15:42.981032Z","iopub.status.idle":"2026-01-14T08:15:45.173545Z","shell.execute_reply.started":"2026-01-14T08:15:42.980971Z","shell.execute_reply":"2026-01-14T08:15:45.172061Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"find_edges","metadata":{}},{"cell_type":"code","source":"process_tiff_stack(sample_train_image, filter_choice=\"find_edges\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:45.174734Z","iopub.execute_input":"2026-01-14T08:15:45.175026Z","iopub.status.idle":"2026-01-14T08:15:46.719399Z","shell.execute_reply.started":"2026-01-14T08:15:45.174999Z","shell.execute_reply":"2026-01-14T08:15:46.718382Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Compare a page and filters with training mask","metadata":{}},{"cell_type":"code","source":"def apply_ij_filter(image, filter_name):\n    \"\"\"Core logic for ImageJ-style filters.\"\"\"\n    img = image.astype(float)\n    \n    if filter_name == \"Original\":\n        return image\n    elif filter_name == \"Watershed\":\n        thresh = img > filters.threshold_otsu(img)\n        distance = ndi.distance_transform_edt(thresh)\n        coords = feature.peak_local_max(distance, min_distance=3, labels=thresh)\n        mask = np.zeros(distance.shape, dtype=bool)\n        mask[tuple(coords.T)] = True\n        markers, _ = ndi.label(mask)\n        return segmentation.watershed(-distance, markers, mask=thresh)\n    elif filter_name == \"Distance Map\":\n        thresh = img > filters.threshold_otsu(img)\n        return ndi.distance_transform_edt(thresh)\n    elif filter_name == \"Convert to Mask\":\n        return (img > filters.threshold_otsu(img)).astype(np.uint8) * 255\n    elif filter_name == \"Enhance Contrast\":\n        return exposure.equalize_adapthist(image / np.max(image)) if np.max(image) > 0 else image\n    elif filter_name == \"Find Maxima\":\n        # Returns a point map of local maxima\n        coords = feature.peak_local_max(img, min_distance=10, threshold_rel=0.05)\n        out = np.zeros_like(img)\n        for c in coords: out[tuple(c)] = 255\n        return out\n    elif filter_name == \"Find Edges\":\n        return filters.sobel(img)\n    elif filter_name == \"Sharpen\":\n        return filters.unsharp_mask(img, radius=2, amount=2)\n    return image\n\ndef compare_tiff_filters(file1, file2, page_index=0):\n    filter_list = [\n        \"Original\", \"Watershed\", \"Distance Map\", \"Convert to Mask\", \n        \"Enhance Contrast\", \"Find Maxima\", \"Find Edges\", \"Sharpen\"\n    ]\n    \n    files = [file1, file2]\n    fig, axes = plt.subplots(2, len(filter_list), figsize=(24, 8))\n    \n    for row, file_path in enumerate(files):\n        with tifffile.TiffFile(file_path) as tif:\n            if page_index >= len(tif.pages):\n                print(f\"Error: {file_path} only has {len(tif.pages)} pages.\")\n                continue\n            \n            raw_img = tif.pages[page_index].asarray()\n            \n            for col, f_name in enumerate(filter_list):\n                processed = apply_ij_filter(raw_img, f_name)\n                \n                ax = axes[row, col]\n                # Use 'magma' for Distance/Watershed to see depth, 'gray' for others\n                cmap = 'magma' if f_name in [\"Distance Map\", \"Watershed\"] else 'gray'\n                ax.imshow(processed, cmap=cmap)\n                \n                if row == 0:\n                    ax.set_title(f_name, fontsize=12, fontweight='bold')\n                if col == 0:\n                    ax.set_ylabel(f\"File {row+1}\\nPage {page_index}\", fontsize=10)\n                ax.axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\ndef compare_filtered_vs_raw(file1, file2, page_index=0):\n    filter_list = [\n        \"Original\", \"Watershed\", \"Distance Map\", \"Convert to Mask\", \n        \"Enhance Contrast\", \"Find Maxima\", \"Find Edges\", \"Sharpen\"\n    ]\n    \n    fig, axes = plt.subplots(2, len(filter_list), figsize=(24, 10))\n    \n    with tifffile.TiffFile(file1) as tif1, tifffile.TiffFile(file2) as tif2:\n        # Load the specific pages\n        img1 = tif1.pages[page_index].asarray()\n        img2 = tif2.pages[page_index].asarray()\n        \n        for col, f_name in enumerate(filter_list):\n            # ROW 1: Apply Filters to File 1\n            processed1 = apply_ij_filter(img1, f_name)\n            ax1 = axes[0, col]\n            cmap1 = 'magma' if f_name in [\"Distance Map\", \"Watershed\"] else 'gray'\n            ax1.imshow(processed1, cmap=cmap1)\n            ax1.set_title(f_name, fontsize=12, fontweight='bold')\n            if col == 0: ax1.set_ylabel(f\"FILE 1 (Filtered)\\nPage {page_index}\", fontsize=10)\n            ax1.axis('off')\n            \n            # ROW 2: Unmodified File 2 (No Filters)\n            ax2 = axes[1, col]\n            ax2.imshow(img2, cmap='gray')\n            if col == 0: ax2.set_ylabel(f\"FILE 2 (Unmodified)\\nPage {page_index}\", fontsize=10)\n            ax2.set_title(\"Unmodified Reference\", fontsize=9, alpha=0.7)\n            ax2.axis('off')\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:46.720631Z","iopub.execute_input":"2026-01-14T08:15:46.720941Z","iopub.status.idle":"2026-01-14T08:15:46.741576Z","shell.execute_reply.started":"2026-01-14T08:15:46.720912Z","shell.execute_reply":"2026-01-14T08:15:46.740077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"compare_tiff_filters(sample_train_image, sample_train_label, page_index=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:46.742834Z","iopub.execute_input":"2026-01-14T08:15:46.743137Z","iopub.status.idle":"2026-01-14T08:15:48.321922Z","shell.execute_reply.started":"2026-01-14T08:15:46.743111Z","shell.execute_reply":"2026-01-14T08:15:48.320892Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"compare_filtered_vs_raw(sample_train_image, sample_train_label, page_index=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:48.323247Z","iopub.execute_input":"2026-01-14T08:15:48.323640Z","iopub.status.idle":"2026-01-14T08:15:49.970289Z","shell.execute_reply.started":"2026-01-14T08:15:48.323599Z","shell.execute_reply":"2026-01-14T08:15:49.968582Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"compare_filtered_vs_raw(sample_train_image, sample_train_label, page_index=100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:49.972011Z","iopub.execute_input":"2026-01-14T08:15:49.972784Z","iopub.status.idle":"2026-01-14T08:15:51.473736Z","shell.execute_reply.started":"2026-01-14T08:15:49.972750Z","shell.execute_reply":"2026-01-14T08:15:51.472685Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"compare_filtered_vs_raw(sample_train_image, sample_train_label, page_index=200)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:51.474984Z","iopub.execute_input":"2026-01-14T08:15:51.475413Z","iopub.status.idle":"2026-01-14T08:15:53.138749Z","shell.execute_reply.started":"2026-01-14T08:15:51.475362Z","shell.execute_reply":"2026-01-14T08:15:53.137628Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"compare_filtered_vs_raw(sample_train_image, sample_train_label, page_index=300)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:53.140080Z","iopub.execute_input":"2026-01-14T08:15:53.140494Z","iopub.status.idle":"2026-01-14T08:15:54.602876Z","shell.execute_reply.started":"2026-01-14T08:15:53.140462Z","shell.execute_reply":"2026-01-14T08:15:54.601962Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"fitered image and mask operations","metadata":{}},{"cell_type":"code","source":"def image_calculator(img1, img2, operation):\n    \"\"\"Performs ImageJ-style mathematical and logical operations.\"\"\"\n    # Ensure images are floating point for math to avoid overflow/underflow\n    i1 = img1.astype(float)\n    i2 = img2.astype(float)\n    \n    op = operation.lower().replace(\" \", \"\")\n\n    if op == 'add':        return i1 + i2\n    if op == 'subtract':   return i1 - i2\n    if op == 'multiply':   return i1 * i2\n    if op == 'divide':     return np.divide(i1, i2, out=np.zeros_like(i1), where=i2!=0)\n    if op == 'average':    return (i1 + i2) / 2\n    if op == 'difference': return np.abs(i1 - i2)\n    if op == 'min':        return np.minimum(i1, i2)\n    if op == 'max':        return np.maximum(i1, i2)\n    if op == 'copy':       return i2\n    \n    # Logical operations require binary masks\n    mask1 = i1 > filters.threshold_otsu(i1) if np.max(i1) > 0 else i1.astype(bool)\n    mask2 = i2 > filters.threshold_otsu(i2) if np.max(i2) > 0 else i2.astype(bool)\n    \n    if op == 'and': return np.logical_and(mask1, mask2)\n    if op == 'or':  return np.logical_or(mask1, mask2)\n    if op == 'xor': return np.logical_xor(mask1, mask2)\n    \n    return i1\n\ndef compare_operations(file1, file2, page_index=0, filter_to_apply=\"Find Edges\"):\n    \"\"\"Applies a filter to File 1, then compares it to File 2 using all operations.\"\"\"\n    \n    ops = [\n        'Add', 'Subtract', 'Multiply', 'Divide', 'AND', 'OR', \n        'XOR', 'Min', 'Max', 'Average', 'Difference', 'Copy'\n    ]\n    \n    with tifffile.TiffFile(file1) as t1, tifffile.TiffFile(file2) as t2:\n        # 1. Get the images\n        raw1 = t1.pages[page_index].asarray()\n        raw2 = t2.pages[page_index].asarray()\n        \n        # 2. Apply the initial filter to Image 1 (using the function from previous steps)\n        # Assuming apply_ij_filter is defined in your notebook\n        filtered1 = apply_ij_filter(raw1, filter_to_apply)\n        \n        # 3. Setup Grid\n        cols = 4\n        rows = 3\n        fig, axes = plt.subplots(rows, cols, figsize=(20, 15))\n        axes = axes.flatten()\n        \n        fig.suptitle(f\"Comparison: [{filter_to_apply} File 1] vs [Raw File 2]\", fontsize=16, y=1.02)\n\n        for i, op_name in enumerate(ops):\n            result = image_calculator(filtered1, raw2, op_name)\n            \n            axes[i].imshow(result, cmap='gray')\n            axes[i].set_title(op_name, fontweight='bold')\n            axes[i].axis('off')\n\n        plt.tight_layout()\n        plt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:54.608888Z","iopub.execute_input":"2026-01-14T08:15:54.609237Z","iopub.status.idle":"2026-01-14T08:15:54.623821Z","shell.execute_reply.started":"2026-01-14T08:15:54.609206Z","shell.execute_reply":"2026-01-14T08:15:54.622725Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#compare_operations(sample_train_image, sample_train_label, page_index=100, filter_to_apply=\"Enhance Contrast\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:54.625634Z","iopub.execute_input":"2026-01-14T08:15:54.626055Z","iopub.status.idle":"2026-01-14T08:15:54.650571Z","shell.execute_reply.started":"2026-01-14T08:15:54.626016Z","shell.execute_reply":"2026-01-14T08:15:54.649306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# compare_operations(sample_train_image, sample_train_label, page_index=100, filter_to_apply=\"Watershed\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:54.651871Z","iopub.execute_input":"2026-01-14T08:15:54.652231Z","iopub.status.idle":"2026-01-14T08:15:54.670282Z","shell.execute_reply.started":"2026-01-14T08:15:54.652184Z","shell.execute_reply":"2026-01-14T08:15:54.669249Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# compare_operations(sample_train_image, sample_train_label, page_index=100, filter_to_apply=\"Find Maxima\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:54.671644Z","iopub.execute_input":"2026-01-14T08:15:54.672012Z","iopub.status.idle":"2026-01-14T08:15:54.687479Z","shell.execute_reply.started":"2026-01-14T08:15:54.671976Z","shell.execute_reply":"2026-01-14T08:15:54.686292Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\nGaussian Difference\n\nThe Difference of Gaussians (DoG) is a powerful feature enhancement algorithm that acts as a band-pass filter. In ImageJ, it is frequently used for spot detection and edge enhancement by subtracting a heavily blurred version of an image from a less blurred version.Mathematically, it is defined as:$$DoG(I) = G(I, \\sigma_1) - G(I, \\sigma_2)$$where $\\sigma_1$ is the standard deviation for the narrow (sharper) Gaussian and $\\sigma_2$ is the standard deviation for the wide (blurrier) Gaussian.Difference of Gaussians FunctionThis function allows you to tune the \"sharpness\" and \"scale\" of the features you want to extract.Pythonimport tifffile","metadata":{}},{"cell_type":"code","source":"\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom skimage import filters, exposure\n\ndef apply_dog_filter(image, sigma_low=1.0, sigma_high=2.0):\n    \"\"\"\n    Applies Difference of Gaussians filter.\n    sigma_low: Blur for the first Gaussian (smaller = sharper)\n    sigma_high: Blur for the second Gaussian (larger = more background subtraction)\n    \"\"\"\n    # Convert to float for subtraction\n    img_float = image.astype(float)\n    \n    # Apply the two blurs\n    gaussian_1 = filters.gaussian(img_float, sigma=sigma_low)\n    gaussian_2 = filters.gaussian(img_float, sigma=sigma_high)\n    \n    # Calculate the difference\n    dog = gaussian_1 - gaussian_2\n    \n    # Normalize result for display (0 to 1 range)\n    dog_normalized = exposure.rescale_intensity(dog)\n    \n    return dog_normalized\n\ndef show_dog_comparison(file_path, page_index=0, sigmas=[(1, 2), (2, 4), (3, 6), (1, 6)]):\n    \"\"\"Displays original vs different DoG settings for a specific page.\"\"\"\n    with tifffile.TiffFile(file_path) as tif:\n        img = tif.pages[page_index].asarray()\n        \n    fig, axes = plt.subplots(1, len(sigmas) + 1, figsize=(20, 5))\n    \n    # Show Original\n    axes[0].imshow(img, cmap='gray')\n    axes[0].set_title(\"Original\", fontweight='bold')\n    axes[0].axis('off')\n    \n    # Show various DoG scales\n    for i, (s1, s2) in enumerate(sigmas):\n        dog_img = apply_dog_filter(img, sigma_low=s1, sigma_high=s2)\n        axes[i+1].imshow(dog_img, cmap='gray')\n        axes[i+1].set_title(f\"DoG (σ1={s1}, σ2={s2})\", fontweight='bold')\n        axes[i+1].axis('off')\n        \n    plt.tight_layout()\n    plt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:54.689084Z","iopub.execute_input":"2026-01-14T08:15:54.689659Z","iopub.status.idle":"2026-01-14T08:15:54.710002Z","shell.execute_reply.started":"2026-01-14T08:15:54.689619Z","shell.execute_reply":"2026-01-14T08:15:54.708748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nshow_dog_comparison(sample_train_image, page_index=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:54.711566Z","iopub.execute_input":"2026-01-14T08:15:54.711958Z","iopub.status.idle":"2026-01-14T08:15:55.470184Z","shell.execute_reply.started":"2026-01-14T08:15:54.711920Z","shell.execute_reply":"2026-01-14T08:15:55.468858Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_dog_comparison(sample_train_image, page_index=100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:55.471519Z","iopub.execute_input":"2026-01-14T08:15:55.471895Z","iopub.status.idle":"2026-01-14T08:15:56.219513Z","shell.execute_reply.started":"2026-01-14T08:15:55.471856Z","shell.execute_reply":"2026-01-14T08:15:56.218211Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_dog_comparison(sample_train_image, page_index=312)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:56.220765Z","iopub.execute_input":"2026-01-14T08:15:56.221043Z","iopub.status.idle":"2026-01-14T08:15:57.125541Z","shell.execute_reply.started":"2026-01-14T08:15:56.221012Z","shell.execute_reply":"2026-01-14T08:15:57.124642Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Minimal EDA\nSome statistics about data","metadata":{}},{"cell_type":"code","source":"# def explore_tiff_folder(folder_path):\n#     data = []\n#     valid_extensions = ('.tif', '.tiff')\n\n#     for file in os.listdir(folder_path):\n#         if file.lower().endswith(valid_extensions):\n#             file_path = os.path.join(folder_path, file)\n            \n#             try:\n#                 with tifffile.TiffFile(file_path) as tif:\n#                     # Get the first page for general metadata\n#                     first_page = tif.pages[0]\n                    \n#                     data.append({\n#                         'File Name': file,\n#                         'Pages': len(tif.pages),\n#                         'Width': first_page.shape[1],\n#                         'Height': first_page.shape[0],\n#                         'Dtype': str(first_page.dtype),\n#                         'Resolution': first_page.tags.get('XResolution', 'N/A'),\n#                         'Compression': first_page.compression.name\n#                     })\n#             except Exception as e:\n#                 print(f\"Error processing {file}: {e}\")\n\n#     return pd.DataFrame(data)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:57.126636Z","iopub.execute_input":"2026-01-14T08:15:57.126917Z","iopub.status.idle":"2026-01-14T08:15:57.132930Z","shell.execute_reply.started":"2026-01-14T08:15:57.126888Z","shell.execute_reply":"2026-01-14T08:15:57.131667Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# df = explore_tiff_folder('/kaggle/input/vesuvius-challenge-surface-detection/train_images/')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:57.134299Z","iopub.execute_input":"2026-01-14T08:15:57.134720Z","iopub.status.idle":"2026-01-14T08:15:57.153883Z","shell.execute_reply.started":"2026-01-14T08:15:57.134682Z","shell.execute_reply":"2026-01-14T08:15:57.152892Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import time","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:57.156064Z","iopub.execute_input":"2026-01-14T08:15:57.156409Z","iopub.status.idle":"2026-01-14T08:15:57.169655Z","shell.execute_reply.started":"2026-01-14T08:15:57.156380Z","shell.execute_reply":"2026-01-14T08:15:57.168569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from concurrent.futures import ProcessPoolExecutor\nfrom tqdm.notebook import tqdm\n\ndef get_single_tiff_metadata(file_path):\n    \"\"\"Worker function to process one file.\"\"\"\n    try:\n        with tifffile.TiffFile(file_path) as tif:\n            page = tif.pages[0]\n            return {\n                'File Name': os.path.basename(file_path),\n                'Pages': len(tif.pages),\n                'Width': page.shape[1],\n                'Height': page.shape[0],\n                'Dtype': str(page.dtype),\n                'Compression': page.compression.name,\n                'Size (MB)': round(os.path.getsize(file_path) / (1024 * 1024), 2)\n            }\n    except Exception:\n        return None\n\ndef fast_explore_folder(folder_path):\n    files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) \n             if f.lower().endswith(('.tif', '.tiff'))]\n    \n    # Use ProcessPoolExecutor to use all CPU cores\n    with ProcessPoolExecutor() as executor:\n        # tqdm adds a progress bar\n        results = list(tqdm(executor.map(get_single_tiff_metadata, files), \n                           total=len(files), desc=\"Processing TIFFs\"))\n    \n    # Filter out failed reads and return DataFrame\n    return pd.DataFrame([r for r in results if r is not None])\n\n# RUN THIS\nfolder_path = '/kaggle/input/vesuvius-challenge-surface-detection/train_images/' # Change this\ndf = fast_explore_folder(folder_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:15:57.170713Z","iopub.execute_input":"2026-01-14T08:15:57.171016Z","iopub.status.idle":"2026-01-14T08:17:21.955572Z","shell.execute_reply.started":"2026-01-14T08:15:57.170985Z","shell.execute_reply":"2026-01-14T08:17:21.954436Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Display the first 10 files and basic stats\ndisplay(df.head())\ndisplay(df.describe())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:17:21.957059Z","iopub.execute_input":"2026-01-14T08:17:21.957448Z","iopub.status.idle":"2026-01-14T08:17:22.004960Z","shell.execute_reply.started":"2026-01-14T08:17:21.957415Z","shell.execute_reply":"2026-01-14T08:17:22.003987Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Resolution and Size for training files","metadata":{}},{"cell_type":"code","source":"%%time\nfig, ax = plt.subplots(1, 2, figsize=(14, 5))\n\n# Plot 1: Number of Pages per File\ndf['Pages'].value_counts().sort_index().plot(kind='bar', ax=ax[0], color='skyblue')\nax[0].set_title('Distribution of Page Counts')\nax[0].set_xlabel('Number of Pages')\nax[0].set_ylabel('File Count')\n\n# Plot 2: Resolution (Width x Height)\nax[1].scatter(df['Width'], df['Height'], alpha=0.5, c='coral')\nax[1].set_title('Image Resolutions (Width vs Height)')\nax[1].set_xlabel('Width (px)')\nax[1].set_ylabel('Height (px)')\nax[1].grid(True, linestyle='--', alpha=0.6)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:17:22.006052Z","iopub.execute_input":"2026-01-14T08:17:22.006725Z","iopub.status.idle":"2026-01-14T08:17:22.434190Z","shell.execute_reply.started":"2026-01-14T08:17:22.006694Z","shell.execute_reply":"2026-01-14T08:17:22.433253Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Bimodal Distribution: Two distinct peaks (one for 18 MB and the other for 35 MB).Two different types of data or two different acquisition settings in the same folder.\n\nThe Main Cluster: The vast majority of your files are concentrated between 30 MB and 37 MB. \n\nA few \"outliers\" on the far left (5–15 MB)\n\n2. Proportion of Pages (The Pie Chart)\n\nThe labels on chart (320, 256, 384) represent the number of pages (or frames) found inside the TIFF files.\n\nThe 320-Page Standard: 93.9% of your files contain exactly 320 pages. 6.0% of the files have 256 pages. 0.1% (likely just 1 or 2 files) have 384 pages.\n\n3. Resolution Summary\n\nMost common resolution: (320, 320) This means your images are square—specifically 320 x 320 pixels.\n","metadata":{}},{"cell_type":"markdown","source":"### Number of pages per file","metadata":{}},{"cell_type":"code","source":"import seaborn as sns\n\nplt.figure(figsize=(12, 6))\n\n# 1. Distribution of File Sizes (Memory Utilization)\nplt.subplot(1, 2, 1)\nsns.histplot(df['Size (MB)'], kde=True, color='green')\nplt.title('File Size Distribution')\n\n# 2. Page Count Breakdown\nplt.subplot(1, 2, 2)\ndf['Pages'].value_counts().plot(kind='pie', autopct='%1.1f%%')\nplt.title('Proportion of Multi-page vs Single-page')\n\nplt.tight_layout()\nplt.show()\n\n# Print the most common resolution found\nprint(f\"Most common resolution: {df.groupby(['Width', 'Height']).size().idxmax()}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:17:22.435711Z","iopub.execute_input":"2026-01-14T08:17:22.436114Z","iopub.status.idle":"2026-01-14T08:17:23.669122Z","shell.execute_reply.started":"2026-01-14T08:17:22.436079Z","shell.execute_reply":"2026-01-14T08:17:23.668358Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Mask (label files) minimal EDA analysis","metadata":{}},{"cell_type":"code","source":"def analyze_mask_file(file_path):\n    try:\n        with tifffile.TiffFile(file_path) as tif:\n            pages_data = []\n            for i, page in enumerate(tif.pages):\n                img = page.asarray()\n                total_pixels = img.size\n                \n                # Count occurrences of each label\n                counts = np.bincount(img.ravel(), minlength=3)\n                bg_count = counts[0]\n                fg_count = counts[1]\n                unlabeled_count = counts[2]\n                \n                # Logic for labeling status\n                is_fully_labeled = (unlabeled_count == 0)\n                is_partially_unlabeled = (unlabeled_count > 0)\n                unlabeled_percent = (unlabeled_count / total_pixels) * 100\n                \n                pages_data.append({\n                    'File': os.path.basename(file_path),\n                    'Page_Index': i,\n                    'Fully_Labeled': is_fully_labeled,\n                    'Partially_Unlabeled': is_partially_unlabeled,\n                    'Unlabeled_Percent': unlabeled_percent,\n                    'Foreground_Percent': (fg_count / total_pixels) * 100\n                })\n            return pages_data\n    except Exception as e:\n        return None\n\ndef run_mask_analysis(folder_path):\n    files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) \n             if f.lower().endswith(('.tif', '.tiff'))]\n    \n    all_results = []\n    with ProcessPoolExecutor() as executor:\n        results = list(tqdm(executor.map(analyze_mask_file, files), \n                           total=len(files), desc=\"Analyzing Mask Pixels\"))\n        \n    # Flatten the list of lists\n    flat_results = [item for sublist in results if sublist for item in sublist]\n    return pd.DataFrame(flat_results)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:17:23.670393Z","iopub.execute_input":"2026-01-14T08:17:23.671015Z","iopub.status.idle":"2026-01-14T08:17:23.680720Z","shell.execute_reply.started":"2026-01-14T08:17:23.670979Z","shell.execute_reply":"2026-01-14T08:17:23.679604Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Execute\ndf_masks = run_mask_analysis('/kaggle/input/vesuvius-challenge-surface-detection/train_labels/')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:17:23.682008Z","iopub.execute_input":"2026-01-14T08:17:23.682507Z","iopub.status.idle":"2026-01-14T08:18:41.161744Z","shell.execute_reply.started":"2026-01-14T08:17:23.682478Z","shell.execute_reply":"2026-01-14T08:18:41.160632Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_masks.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:41.163349Z","iopub.execute_input":"2026-01-14T08:18:41.163666Z","iopub.status.idle":"2026-01-14T08:18:41.177194Z","shell.execute_reply.started":"2026-01-14T08:18:41.163636Z","shell.execute_reply":"2026-01-14T08:18:41.176395Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a summary figure\nfig, ax = plt.subplots(1, 2, figsize=(16, 6))\n\n# 1. Labeling Status Summary\nstatus_counts = [df_masks['Fully_Labeled'].sum(), df_masks['Partially_Unlabeled'].sum()]\nax[0].bar(['Fully Labeled (0 or 1)', 'Partially Unlabeled (Contains 2)'], status_counts, color=['#4CAF50', '#FF9800'])\nax[0].set_title('Page-Level Annotation Status')\nax[0].set_ylabel('Total Number of Pages')\n\n# 2. Distribution of Unlabeled Surface Area\nsns.histplot(df_masks[df_masks['Unlabeled_Percent'] > 0]['Unlabeled_Percent'], \n             bins=30, kde=True, ax=ax[1], color='purple')\nax[1].set_title('Distribution of Unlabeled Surface %')\nax[1].set_xlabel('% of Page covered by \"Value 2\"')\nax[1].set_ylabel('Page Count')\n\nplt.tight_layout()\nplt.show()\n\n# Quick Statistics Report\nprint(f\"Total Pages Scanned: {len(df_masks)}\")\nprint(f\"Average Unlabeled Surface per Page: {df_masks['Unlabeled_Percent'].mean():.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:41.178845Z","iopub.execute_input":"2026-01-14T08:18:41.179493Z","iopub.status.idle":"2026-01-14T08:18:42.600471Z","shell.execute_reply.started":"2026-01-14T08:18:41.179462Z","shell.execute_reply":"2026-01-14T08:18:42.599556Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Mask overaly visualisation\n Question is is possible that valuable data for training is masked ?\n A possible augmentation would be to drag the mask a few pixels.?\n \n TBI- it is possible that the image mask covers useful information to \"calculate/discover\" a line (surface section)","metadata":{}},{"cell_type":"code","source":"def visualize_mask_overlay(image_path, mask_path, page_num=0):\n    # Load the specific page from both files\n    image = tifffile.imread(image_path, key=page_num)\n    mask = tifffile.imread(mask_path, key=page_num)\n    \n    # Normalize image for display if it's 16-bit\n    if image.dtype == np.uint16:\n        image = (image / 65535.0 * 255).astype(np.uint8)\n    \n    plt.figure(figsize=(18, 6))\n    \n    # 1. Original Image\n    plt.subplot(1, 3, 1)\n    plt.imshow(image, cmap='gray')\n    plt.title(f\"Original Image (Page {page_num})\")\n    plt.axis('off')\n    \n    # 2. Raw Mask (Showing Labels 0, 1, 2)\n    plt.subplot(1, 3, 2)\n    plt.imshow(mask, cmap='viridis') # Viridis makes 0, 1, 2 distinct\n    plt.title(\"Raw Mask (0:BG, 1:FG, 2:Unlabeled)\")\n    plt.axis('off')\n    \n    # 3. Targeted Overlay\n    # Create an RGB version of the grayscale image\n    overlay = np.stack([image]*3, axis=-1) \n    \n    # Highlight Unlabeled (Value 2) in Bright Red\n    overlay[mask == 2] = [255, 0, 0] \n    # Highlight Foreground (Value 1) in Bright Green\n    overlay[mask == 1] = [0, 255, 0] \n    \n    plt.subplot(1, 3, 3)\n    plt.imshow(overlay)\n    plt.title(\"Overlay (Red=Unlabeled, Green=Foreground)\")\n    plt.axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:42.601720Z","iopub.execute_input":"2026-01-14T08:18:42.602049Z","iopub.status.idle":"2026-01-14T08:18:42.610247Z","shell.execute_reply.started":"2026-01-14T08:18:42.602021Z","shell.execute_reply":"2026-01-14T08:18:42.609356Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_tiff='/kaggle/input/vesuvius-challenge-surface-detection/train_images/1448189335.tif'\nlabel_tiff='/kaggle/input/vesuvius-challenge-surface-detection/train_labels/1448189335.tif'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:42.611589Z","iopub.execute_input":"2026-01-14T08:18:42.611992Z","iopub.status.idle":"2026-01-14T08:18:42.632946Z","shell.execute_reply.started":"2026-01-14T08:18:42.611965Z","shell.execute_reply":"2026-01-14T08:18:42.632023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay(train_tiff, label_tiff, page_num=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:42.634998Z","iopub.execute_input":"2026-01-14T08:18:42.635353Z","iopub.status.idle":"2026-01-14T08:18:43.148423Z","shell.execute_reply.started":"2026-01-14T08:18:42.635323Z","shell.execute_reply":"2026-01-14T08:18:43.147388Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay(train_tiff, label_tiff, page_num=3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:43.149512Z","iopub.execute_input":"2026-01-14T08:18:43.149792Z","iopub.status.idle":"2026-01-14T08:18:43.687915Z","shell.execute_reply.started":"2026-01-14T08:18:43.149758Z","shell.execute_reply":"2026-01-14T08:18:43.686347Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay(train_tiff, label_tiff, page_num=4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:43.689369Z","iopub.execute_input":"2026-01-14T08:18:43.689858Z","iopub.status.idle":"2026-01-14T08:18:44.228048Z","shell.execute_reply.started":"2026-01-14T08:18:43.689817Z","shell.execute_reply":"2026-01-14T08:18:44.226815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay(train_tiff, label_tiff, page_num=9)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:44.229396Z","iopub.execute_input":"2026-01-14T08:18:44.229726Z","iopub.status.idle":"2026-01-14T08:18:44.762166Z","shell.execute_reply.started":"2026-01-14T08:18:44.229699Z","shell.execute_reply":"2026-01-14T08:18:44.761078Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Masked train images","metadata":{}},{"cell_type":"code","source":"def visualize_mask_overlay_extended(image_path, mask_path, page_num=0):\n    # Load data\n    image = tifffile.imread(image_path, key=page_num)\n    mask = tifffile.imread(mask_path, key=page_num)\n    \n    # Normalize for 16-bit images\n    if image.dtype == np.uint16:\n        img_disp = (image / 65535.0 * 255).astype(np.uint8)\n    else:\n        img_disp = image.copy()\n    \n    fig, axes = plt.subplots(2, 2, figsize=(16, 12))\n    \n    # 1. Original Image (Top Left)\n    axes[0,0].imshow(img_disp, cmap='gray')\n    axes[0,0].set_title(f\"Original Image (Page {page_num})\")\n    axes[0,0].axis('off')\n    \n    # 2. Raw Mask Labels (Top Right)\n    # Using a discrete color map to see 0, 1, and 2 clearly\n    axes[0,1].imshow(mask, cmap='viridis')\n    axes[0,1].set_title(\"Raw Mask Labels (0, 1, 2)\")\n    axes[0,1].axis('off')\n    \n    # 3. Full RGB Overlay (Bottom Left)\n    overlay = np.stack([img_disp]*3, axis=-1)\n    overlay[mask == 2] = [255, 0, 0] # Unlabeled = Red\n    overlay[mask == 1] = [0, 255, 0] # Foreground = Green\n    axes[1,0].imshow(overlay)\n    axes[1,0].set_title(\"Full Overlay (Red=Unlabeled, Green=FG)\")\n    axes[1,0].axis('off')\n    \n    # 4. Background/Unlabeled Highlight (Bottom Right)\n    # This masks the original image to only show what is marked 'Unlabeled'\n    masked_background = np.zeros_like(img_disp)\n    masked_background[mask == 2] = img_disp[mask == 2]\n    \n    axes[1,1].imshow(masked_background, cmap='magma')\n    axes[1,1].set_title(\"Pixels currently marked 'Unlabeled' (Value 2)\")\n    axes[1,1].axis('off')\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:44.763418Z","iopub.execute_input":"2026-01-14T08:18:44.763768Z","iopub.status.idle":"2026-01-14T08:18:44.773807Z","shell.execute_reply.started":"2026-01-14T08:18:44.763741Z","shell.execute_reply":"2026-01-14T08:18:44.772835Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay_extended(train_tiff, label_tiff, page_num=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:44.775559Z","iopub.execute_input":"2026-01-14T08:18:44.775968Z","iopub.status.idle":"2026-01-14T08:18:45.583599Z","shell.execute_reply.started":"2026-01-14T08:18:44.775926Z","shell.execute_reply":"2026-01-14T08:18:45.582322Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay_extended(train_tiff, label_tiff, page_num=4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:45.584744Z","iopub.execute_input":"2026-01-14T08:18:45.585040Z","iopub.status.idle":"2026-01-14T08:18:46.352100Z","shell.execute_reply.started":"2026-01-14T08:18:45.585013Z","shell.execute_reply":"2026-01-14T08:18:46.351054Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay_extended(train_tiff, label_tiff, page_num=9)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:46.353230Z","iopub.execute_input":"2026-01-14T08:18:46.353576Z","iopub.status.idle":"2026-01-14T08:18:47.320561Z","shell.execute_reply.started":"2026-01-14T08:18:46.353549Z","shell.execute_reply":"2026-01-14T08:18:47.319276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_mask_overlay_extended(train_tiff, label_tiff, page_num=100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:47.321891Z","iopub.execute_input":"2026-01-14T08:18:47.322212Z","iopub.status.idle":"2026-01-14T08:18:48.088531Z","shell.execute_reply.started":"2026-01-14T08:18:47.322184Z","shell.execute_reply":"2026-01-14T08:18:48.087090Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### start and end labels\nAll label files have the first 3 pages and the last 3 unlabeled.","metadata":{}},{"cell_type":"code","source":"def analyze_tiff_extremities(folder_path):\n    results = []\n    files = [f for f in os.listdir(folder_path) if f.lower().endswith(('.tif', '.tiff'))]\n    \n    for file_name in tqdm(files, desc=\"Analiză fișiere\"):\n        file_path = os.path.join(folder_path, file_name)\n        \n        try:\n            with tifffile.TiffFile(file_path) as tif:\n                n_pages = len(tif.pages)\n                \n                # Analizăm primele pagini (Start)\n                first_unlabeled_count = 0\n                for page in tif.pages:\n                    data = page.asarray()\n                    # Verificăm dacă TOATĂ pagina este formată din 2\n                    if np.all(data == 2):\n                        first_unlabeled_count += 1\n                    else:\n                        break # Ne oprim la prima pagină care are și altceva (0 sau 1)\n                \n                # Analizăm ultimele pagini (End)\n                last_unlabeled_count = 0\n                for i in range(n_pages - 1, -1, -1):\n                    data = tif.pages[i].asarray()\n                    if np.all(data == 2):\n                        last_unlabeled_count += 1\n                    else:\n                        break # Ne oprim când găsim date utile\n                \n                results.append({\n                    'Fișier': file_name,\n                    'Total_Pagini': n_pages,\n                    'Pagini_Start_Neetichetate': first_unlabeled_count,\n                    'Pagini_End_Neetichetate': last_unlabeled_count,\n                    'Are_Start_Gol': first_unlabeled_count > 0,\n                    'Are_End_Gol': last_unlabeled_count > 0\n                })\n        except Exception as e:\n            print(f\"Eroare la {file_name}: {e}\")\n            \n    return pd.DataFrame(results)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:48.089668Z","iopub.execute_input":"2026-01-14T08:18:48.089939Z","iopub.status.idle":"2026-01-14T08:18:48.099786Z","shell.execute_reply.started":"2026-01-14T08:18:48.089916Z","shell.execute_reply":"2026-01-14T08:18:48.098726Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Executare\ndf_extremities = analyze_tiff_extremities('/kaggle/input/vesuvius-challenge-surface-detection/train_labels/')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:48.101070Z","iopub.execute_input":"2026-01-14T08:18:48.101493Z","iopub.status.idle":"2026-01-14T08:18:52.394466Z","shell.execute_reply.started":"2026-01-14T08:18:48.101452Z","shell.execute_reply":"2026-01-14T08:18:52.393386Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. Câte fișiere au pagini goale la început/sfârșit?\nfisiere_start_gol = df_extremities['Are_Start_Gol'].sum()\nfisiere_end_gol = df_extremities['Are_End_Gol'].sum()\n\n# 2. Media de pagini goale\navg_start = df_extremities['Pagini_Start_Neetichetate'].mean()\navg_end = df_extremities['Pagini_End_Neetichetate'].mean()\n\nprint(f\"--- REZUMAT ETICHETARE ---\")\nprint(f\"Fișiere cu început neetichetat (2): {fisiere_start_gol} (Media: {avg_start:.1f} pagini)\")\nprint(f\"Fișiere cu sfârșit neetichetat (2): {fisiere_end_gol} (Media: {avg_end:.1f} pagini)\")\n\n# Afișare top 10 fișiere cu cele mai multe pagini goale la final\ndisplay(df_extremities.sort_values(by='Pagini_End_Neetichetate', ascending=False).head(10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:18:52.395834Z","iopub.execute_input":"2026-01-14T08:18:52.396227Z","iopub.status.idle":"2026-01-14T08:18:52.415331Z","shell.execute_reply.started":"2026-01-14T08:18:52.396196Z","shell.execute_reply":"2026-01-14T08:18:52.414349Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### one page statistics","metadata":{}},{"cell_type":"code","source":"from PIL import Image\n\ndef analyze_tiff_page(file_path, page_number=0):\n    \"\"\"\n    Analyzes a specific page of a TIFF file.\n    \n    Parameters:\n    - file_path: str, path to the .tiff file\n    - page_number: int, the index of the page to analyze (starts at 0)\n    \"\"\"\n    try:\n        # Open the image\n        with Image.open(file_path) as img:\n            # Seek to the specific page (frame)\n            img.seek(page_number)\n            \n            # 1. Resolution\n            # info.get('dpi') returns a tuple (x_dpi, y_dpi)\n            dpi = img.info.get('dpi', \"Resolution not found\")\n            \n            # 2. Greyscale Check\n            # 'L' is 8-bit greyscale, '1' is black and white, 'I' is 32-bit integer pixels\n            mode = img.mode\n            is_greyscale = mode in ['L', '1', 'I', 'F', 'I;16']\n            \n            # 3. Pixel Statistics\n            # Convert to NumPy array for efficient calculation\n            pixel_data = np.array(img)\n            \n            stats = {\n                \"Page\": page_number,\n                \"Dimensions\": img.size, # (Width, Height)\n                \"Resolution (DPI)\": dpi,\n                \"Mode\": mode,\n                \"Is Greyscale\": is_greyscale,\n                \"Pixel Stats\": {\n                    \"Min\": np.min(pixel_data),\n                    \"Max\": np.max(pixel_data),\n                    \"Mean\": round(np.mean(pixel_data), 2),\n                    \"Std Dev\": round(np.std(pixel_data), 2)\n                }\n            }\n            \n            return stats\n\n    except EOFError:\n        return f\"Error: Page {page_number} does not exist in this file.\"\n    except FileNotFoundError:\n        return \"Error: File not found.\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:19:12.737117Z","iopub.execute_input":"2026-01-14T08:19:12.737534Z","iopub.status.idle":"2026-01-14T08:19:12.745760Z","shell.execute_reply.started":"2026-01-14T08:19:12.737502Z","shell.execute_reply":"2026-01-14T08:19:12.744584Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train sample","metadata":{}},{"cell_type":"markdown","source":"First page has no info ","metadata":{}},{"cell_type":"code","source":"\nresult = analyze_tiff_page('/kaggle/input/vesuvius-challenge-surface-detection/train_images/1006462223.tif', page_number=0)\nprint(result)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:19:17.422129Z","iopub.execute_input":"2026-01-14T08:19:17.422501Z","iopub.status.idle":"2026-01-14T08:19:17.696941Z","shell.execute_reply.started":"2026-01-14T08:19:17.422471Z","shell.execute_reply":"2026-01-14T08:19:17.695881Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Label of the sample ","metadata":{}},{"cell_type":"code","source":"result = analyze_tiff_page('/kaggle/input/vesuvius-challenge-surface-detection/train_labels/1006462223.tif', page_number=0)\nprint(result)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:19:21.069970Z","iopub.execute_input":"2026-01-14T08:19:21.070924Z","iopub.status.idle":"2026-01-14T08:19:21.079393Z","shell.execute_reply.started":"2026-01-14T08:19:21.070890Z","shell.execute_reply":"2026-01-14T08:19:21.078506Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Random Page","metadata":{}},{"cell_type":"code","source":"result = analyze_tiff_page('/kaggle/input/vesuvius-challenge-surface-detection/train_images/1006462223.tif', page_number=65)\nprint(result)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:21:10.297590Z","iopub.execute_input":"2026-01-14T08:21:10.299085Z","iopub.status.idle":"2026-01-14T08:21:10.343819Z","shell.execute_reply.started":"2026-01-14T08:21:10.299029Z","shell.execute_reply":"2026-01-14T08:21:10.342752Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"result = analyze_tiff_page('/kaggle/input/vesuvius-challenge-surface-detection/train_labels/1006462223.tif', page_number=65)\nprint(result)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-14T08:21:11.663645Z","iopub.execute_input":"2026-01-14T08:21:11.664011Z","iopub.status.idle":"2026-01-14T08:21:11.677666Z","shell.execute_reply.started":"2026-01-14T08:21:11.663980Z","shell.execute_reply":"2026-01-14T08:21:11.676391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}