{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"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\nfor 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\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:43:15.700449Z","iopub.execute_input":"2026-06-16T08:43:15.700704Z","iopub.status.idle":"2026-06-16T08:43:25.877935Z","shell.execute_reply.started":"2026-06-16T08:43:15.700673Z","shell.execute_reply":"2026-06-16T08:43:25.877089Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Important\nWe havent fix duplicate and wrong annotaion in Messidor\n","metadata":{}},{"cell_type":"code","source":"%cd /kaggle/working\n\nfrom pathlib import Path\n\ncar_repo = Path('/kaggle/working/CAR')\nif car_repo.exists():\n    print(f'Using existing CAR repository: {car_repo}')\nelse:\n    !git clone https://github.com/sunwj/CAR.git\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:43:25.879460Z","iopub.execute_input":"2026-06-16T08:43:25.879854Z","iopub.status.idle":"2026-06-16T08:43:26.506524Z","shell.execute_reply.started":"2026-06-16T08:43:25.879832Z","shell.execute_reply":"2026-06-16T08:43:26.505508Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Replace the old extension build configuration:\n","metadata":{}},{"cell_type":"code","source":"%%writefile /kaggle/working/CAR/adaptive_gridsampler/setup.py\nfrom setuptools import setup\nfrom torch.utils.cpp_extension import BuildExtension, CUDAExtension\n\nsetup(\n    name=\"adaptive_gridsampler_cuda\",\n    ext_modules=[\n        CUDAExtension(\n            \"adaptive_gridsampler_cuda\",\n            [\"adaptive_gridsampler_cuda.cpp\",\n             \"adaptive_gridsampler_kernel.cu\"],\n            extra_compile_args={\n                \"cxx\": [\"-O3\", \"-std=c++17\"],\n                \"nvcc\": [\"-O3\"]\n            }\n        )\n    ],\n    cmdclass={\"build_ext\": BuildExtension},\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:43:26.507903Z","iopub.execute_input":"2026-06-16T08:43:26.508194Z","iopub.status.idle":"2026-06-16T08:43:26.514132Z","shell.execute_reply.started":"2026-06-16T08:43:26.508167Z","shell.execute_reply":"2026-06-16T08:43:26.513439Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Compile it:","metadata":{}},{"cell_type":"code","source":"import os\nimport torch\n\nassert torch.cuda.is_available(), \"Enable a Kaggle GPU accelerator\"\n\nmajor, minor = torch.cuda.get_device_capability()\nos.environ[\"TORCH_CUDA_ARCH_LIST\"] = f\"{major}.{minor}\"\nos.environ[\"MAX_JOBS\"] = \"2\"\n\n%cd /kaggle/working/CAR/adaptive_gridsampler\n!python setup.py build_ext --inplace\n%cd /kaggle/working/CAR","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:43:26.515294Z","iopub.execute_input":"2026-06-16T08:43:26.515942Z","iopub.status.idle":"2026-06-16T08:44:35.482666Z","shell.execute_reply.started":"2026-06-16T08:43:26.515919Z","shell.execute_reply":"2026-06-16T08:44:35.481880Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Test the extension:","metadata":{}},{"cell_type":"code","source":"from adaptive_gridsampler.gridsampler import Downsampler\nprint(\"CAR extension loaded successfully\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:44:35.484070Z","iopub.execute_input":"2026-06-16T08:44:35.484831Z","iopub.status.idle":"2026-06-16T08:44:35.493283Z","shell.execute_reply.started":"2026-06-16T08:44:35.484798Z","shell.execute_reply":"2026-06-16T08:44:35.492670Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Load CAR","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"import sys\nimport torch\nfrom pathlib import Path\n\nsys.path.insert(0, \"/kaggle/working/CAR\")\n\nfrom modules import DSN\nfrom adaptive_gridsampler.gridsampler import Downsampler\n\nSCALE = 4\nKSIZE = 3 * SCALE + 1\nWEIGHT = Path(\"/kaggle/input/datasets/khangcancode/car-trained-model-from-github/models/4x/kgn.pth\")\n\nkgn = DSN(k_size=KSIZE, scale=SCALE).cuda().eval()\nstate = torch.load(WEIGHT, map_location=\"cuda\", weights_only=True)\nstate = {k.removeprefix(\"module.\"): v for k, v in state.items()}\nkgn.load_state_dict(state)\n\nsampler = Downsampler(SCALE, KSIZE).cuda().eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:44:35.494203Z","iopub.execute_input":"2026-06-16T08:44:35.495025Z","iopub.status.idle":"2026-06-16T08:44:36.548772Z","shell.execute_reply.started":"2026-06-16T08:44:35.494988Z","shell.execute_reply":"2026-06-16T08:44:36.548118Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Create a downscaling function:","metadata":{}},{"cell_type":"code","source":"import numpy as np\n\n@torch.inference_mode()\ndef car_downscale(rgb_image):\n    h, w = rgb_image.shape[:2]\n    rgb_image = rgb_image[:h // 8 * 8, :w // 8 * 8]\n\n    x = torch.from_numpy(rgb_image).permute(2, 0, 1)\n    x = x.float().div(255).unsqueeze(0).cuda()\n\n    kernels, offset_h, offset_v = kgn(x)\n\n    # Downsampler.forward(img, kernels, offsets_h, offsets_v, offset_unit)\n    output = sampler(x, kernels, offset_h, offset_v, SCALE)\n\n    output = output.clamp(0, 1)\n\n    return (\n        output[0].permute(1, 2, 0)\n        .mul(255)\n        .byte()\n        .cpu()\n        .numpy()\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:44:36.550753Z","iopub.execute_input":"2026-06-16T08:44:36.551104Z","iopub.status.idle":"2026-06-16T08:44:36.556919Z","shell.execute_reply.started":"2026-06-16T08:44:36.551075Z","shell.execute_reply":"2026-06-16T08:44:36.556125Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Reproducible CAR/LID Downscaling Check\n\nThis section uses one fixed APTOS image, one fixed input size (`1280 x 1280`), and one fixed output size (`320 x 320`). It compares five OpenCV downscaling methods with CAR/LID and prints deterministic hashes so reruns can be checked exactly.\n","metadata":{}},{"cell_type":"code","source":"import cv2\nimport hashlib\nimport random\nfrom pathlib import Path\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport torch\n\n# Reproducibility settings for this visual check.\nSEED = 20260616\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)\ntorch.cuda.manual_seed_all(SEED)\ncv2.setNumThreads(1)\n\nTEST_IMAGE_PATH = Path(\n    \"/kaggle/input/competitions/aptos2019-blindness-detection/test_images/0111b949947e.png\"\n)\nINPUT_SIZE = 1280\nOUTPUT_SIZE = 320\nEXPECTED_CAR_SCALE = 4\n\nassert SCALE == EXPECTED_CAR_SCALE, f\"Expected CAR SCALE={EXPECTED_CAR_SCALE}, got SCALE={SCALE}\"\nassert TEST_IMAGE_PATH.exists(), f\"Missing test image: {TEST_IMAGE_PATH}\"\n\nimage_bgr = cv2.imread(str(TEST_IMAGE_PATH), cv2.IMREAD_COLOR)\nassert image_bgr is not None, f\"cv2.imread failed for: {TEST_IMAGE_PATH}\"\n\nimage_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)\nimage_1280_rgb = cv2.resize(\n    image_rgb,\n    (INPUT_SIZE, INPUT_SIZE),\n    interpolation=cv2.INTER_LINEAR,\n)\n\nassert image_1280_rgb.shape == (INPUT_SIZE, INPUT_SIZE, 3)\nassert image_1280_rgb.dtype == np.uint8\n\nopencv_methods = {\n    \"Nearest\": cv2.INTER_NEAREST,\n    \"Area\": cv2.INTER_AREA,\n    \"Bilinear\": cv2.INTER_LINEAR,\n    \"Bicubic\": cv2.INTER_CUBIC,\n    \"Lanczos4\": cv2.INTER_LANCZOS4,\n}\n\nresults = {}\nfor name, interpolation in opencv_methods.items():\n    results[name] = cv2.resize(\n        image_1280_rgb,\n        (OUTPUT_SIZE, OUTPUT_SIZE),\n        interpolation=interpolation,\n    )\n\nresults[\"CAR / LID\"] = car_downscale(image_1280_rgb)\n\nfor name, img in results.items():\n    assert img.shape == (OUTPUT_SIZE, OUTPUT_SIZE, 3), f\"{name} shape mismatch: {img.shape}\"\n    assert img.dtype == np.uint8, f\"{name} dtype mismatch: {img.dtype}\"\n\n\ndef image_md5(img):\n    return hashlib.md5(np.ascontiguousarray(img).tobytes()).hexdigest()\n\n\ndef laplacian_variance(img):\n    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n    return float(cv2.Laplacian(gray, cv2.CV_64F).var())\n\nsummary = pd.DataFrame(\n    [\n        {\n            \"method\": name,\n            \"shape\": str(img.shape),\n            \"dtype\": str(img.dtype),\n            \"mean_pixel\": round(float(img.mean()), 4),\n            \"std_pixel\": round(float(img.std()), 4),\n            \"sharpness_laplacian_var\": round(laplacian_variance(img), 4),\n            \"md5\": image_md5(img),\n        }\n        for name, img in results.items()\n    ]\n)\n\ndisplay(summary)\nprint(f\"Input image: {TEST_IMAGE_PATH}\")\nprint(f\"Input shape: {image_1280_rgb.shape}; CAR/LID output shape: {results['CAR / LID'].shape}\")\nprint(\"If the CAR / LID md5 stays the same on rerun, the function is producing the same output.\")\n\nfig, axes = plt.subplots(1, 2, figsize=(10, 5))\naxes[0].imshow(image_1280_rgb)\naxes[0].set_title(\"Before: resized input 1280 x 1280\")\naxes[0].axis(\"off\")\n\naxes[1].imshow(results[\"CAR / LID\"])\naxes[1].set_title(\"After: CAR / LID 320 x 320\")\naxes[1].axis(\"off\")\n\nplt.tight_layout()\nplt.show()\n\nfig, axes = plt.subplots(2, 3, figsize=(15, 10))\naxes = axes.ravel()\nfor ax, (name, img) in zip(axes, results.items()):\n    ax.imshow(img)\n    ax.set_title(name)\n    ax.axis(\"off\")\n\nplt.suptitle(\"Downscaling comparison: 1280 x 1280 to 320 x 320\", fontsize=16)\nplt.tight_layout()\nplt.show()\n\n# Zoom the same center region from each 320 x 320 output to make vessel/detail differences easier to inspect.\nfig, axes = plt.subplots(2, 3, figsize=(15, 10))\naxes = axes.ravel()\nfor ax, (name, img) in zip(axes, results.items()):\n    crop = img[80:240, 80:240]\n    zoom = cv2.resize(crop, (OUTPUT_SIZE, OUTPUT_SIZE), interpolation=cv2.INTER_NEAREST)\n    ax.imshow(zoom)\n    ax.set_title(f\"{name} center zoom\")\n    ax.axis(\"off\")\n\nplt.suptitle(\"Zoomed center crop comparison\", fontsize=16)\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-16T08:58:19.736323Z","iopub.execute_input":"2026-06-16T08:58:19.736845Z","iopub.status.idle":"2026-06-16T08:58:22.646538Z","shell.execute_reply.started":"2026-06-16T08:58:19.736812Z","shell.execute_reply":"2026-06-16T08:58:22.645621Z"}},"outputs":[],"execution_count":null}]}