{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":52254,"databundleVersionId":9674523},{"sourceType":"modelInstanceVersion","sourceId":992,"databundleVersionId":4905385,"modelInstanceId":846,"modelId":101},{"sourceType":"kernelVersion","sourceId":144565665},{"sourceType":"kernelVersion","sourceId":144629118}],"dockerImageVersionId":30559,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pydicom\n\nimport pandas as pd\nimport numpy as np\n\nimport torch\nimport torchvision\nfrom torch import nn\nimport torch.nn.functional as F\nfrom torch import optim\nfrom torchvision import transforms\n\nfrom torch.utils.data import Dataset, DataLoader\n\nimport matplotlib.pyplot as plt\n\nfrom joblib import Parallel, delayed\nimport sys\n\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.503479Z","iopub.execute_input":"2026-06-12T17:23:49.504498Z","iopub.status.idle":"2026-06-12T17:23:49.50991Z","shell.execute_reply.started":"2026-06-12T17:23:49.504465Z","shell.execute_reply":"2026-06-12T17:23:49.508792Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SEED = 42\n\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.511747Z","iopub.execute_input":"2026-06-12T17:23:49.512165Z","iopub.status.idle":"2026-06-12T17:23:49.522365Z","shell.execute_reply.started":"2026-06-12T17:23:49.512138Z","shell.execute_reply":"2026-06-12T17:23:49.521352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.523657Z","iopub.execute_input":"2026-06-12T17:23:49.524272Z","iopub.status.idle":"2026-06-12T17:23:49.532145Z","shell.execute_reply.started":"2026-06-12T17:23:49.524228Z","shell.execute_reply":"2026-06-12T17:23:49.531303Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TEST_ROOT = '/kaggle/input/rsna-2023-abdominal-trauma-detection/test_images'\nTEST_SERIES = '/kaggle/input/rsna-2023-abdominal-trauma-detection/test_series_meta.csv'\n\nVOLUME = (128, 64, 64)\n\nBATCH_SIZE = 32\n\nVOLUME = (128, 64, 64)\n\nWINDOW_STEP = 2\n# Odd Number Only\nWINDOW_WIDTH = 3\nPAD_ENDING = True\n\nSLICE_NUM = (128-1)//2 + 1\n\nTARGET_COLS  = [\n    \"bowel_injury\", \"extravasation_injury\",\n    \"kidney_healthy\", \"kidney_low\", \"kidney_high\",\n    \"liver_healthy\", \"liver_low\", \"liver_high\",\n    \"spleen_healthy\", \"spleen_low\", \"spleen_high\",\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.533784Z","iopub.execute_input":"2026-06-12T17:23:49.534173Z","iopub.status.idle":"2026-06-12T17:23:49.542918Z","shell.execute_reply.started":"2026-06-12T17:23:49.534147Z","shell.execute_reply":"2026-06-12T17:23:49.541757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SLICE_NUM","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.544496Z","iopub.execute_input":"2026-06-12T17:23:49.544816Z","iopub.status.idle":"2026-06-12T17:23:49.560194Z","shell.execute_reply.started":"2026-06-12T17:23:49.544791Z","shell.execute_reply":"2026-06-12T17:23:49.55897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Net(nn.Module):\n    \n    def __init__(self):\n        \n        super(Net, self).__init__()\n        \n        resnet = torchvision.models.resnet18()\n        self.backbone = nn.Sequential(*list(resnet.children())[:-1])\n        \n        self.ltsm = nn.LSTM(input_size=512, hidden_size=128, batch_first=True)\n        self.fc = nn.Linear(in_features=128*SLICE_NUM, out_features=11)\n    \n    def forward(self, x):\n        \n        batch_size = x.shape[0]\n        x = x.view(batch_size * SLICE_NUM, WINDOW_WIDTH, VOLUME[1], VOLUME[2])\n        x = self.backbone(x)\n        x = x.view(batch_size, SLICE_NUM, -1) \n        \n        x, _ = self.ltsm(x)\n        x = torch.flatten(x, start_dim=1)\n        \n        x = self.fc(x)\n        \n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.562517Z","iopub.execute_input":"2026-06-12T17:23:49.562875Z","iopub.status.idle":"2026-06-12T17:23:49.573847Z","shell.execute_reply.started":"2026-06-12T17:23:49.562842Z","shell.execute_reply":"2026-06-12T17:23:49.573052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = Net().to(device)\nsum(p.numel() for p in model.parameters() if p.requires_grad)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.575117Z","iopub.execute_input":"2026-06-12T17:23:49.575414Z","iopub.status.idle":"2026-06-12T17:23:49.794276Z","shell.execute_reply.started":"2026-06-12T17:23:49.575392Z","shell.execute_reply":"2026-06-12T17:23:49.793278Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(os.listdir('/kaggle/input'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.795525Z","iopub.execute_input":"2026-06-12T17:23:49.796178Z","iopub.status.idle":"2026-06-12T17:23:49.80171Z","shell.execute_reply.started":"2026-06-12T17:23:49.796148Z","shell.execute_reply":"2026-06-12T17:23:49.800778Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision.models import resnet18\n\nmodel = resnet18(weights=None)\nmodel = model.to(device)\n\nprint(\"MODEL LOADED\")\nprint(next(model.parameters()).device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:49.803067Z","iopub.execute_input":"2026-06-12T17:23:49.803451Z","iopub.status.idle":"2026-06-12T17:23:50.013963Z","shell.execute_reply.started":"2026-06-12T17:23:49.803424Z","shell.execute_reply":"2026-06-12T17:23:50.012808Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\n\nx = torch.randn(1, 3, 224, 224).to(device)\n\nwith torch.no_grad():\n    y = model(x)\n\nprint(y.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:50.015179Z","iopub.execute_input":"2026-06-12T17:23:50.015488Z","iopub.status.idle":"2026-06-12T17:23:50.027475Z","shell.execute_reply.started":"2026-06-12T17:23:50.015462Z","shell.execute_reply":"2026-06-12T17:23:50.026235Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# RSNA Abdominal Trauma — Fixed Inference Script\n# ============================================================\n# WHERE TO INSERT: Run this as a NEW cell immediately after\n# your last model-loading cell (the one that prints\n# \"MODEL LOADED\" and checks next(model.parameters()).device).\n#\n# It replaces / supersedes all subsequent cells in the notebook:\n#   - DICOM processing\n#   - Feature map extraction\n#   - CV-enhanced visualization\n#   - GradCAM heatmap\n#   - Accuracy evaluation (on any labeled split)\n# ============================================================\n\nimport os\nimport cv2\nimport numpy as np\nimport pydicom\nimport torch\nimport torch.nn.functional as F\nimport matplotlib.pyplot as plt\nimport matplotlib.gridspec as gridspec\nfrom torchvision.models import resnet18\n\n# ── Config ──────────────────────────────────────────────────\nDCM_PATH   = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/26501/22032/12.dcm'\nTRUE_LABEL = None          # set to int (0-999) if you have a ground-truth label\n\n# To evaluate accuracy over multiple files, set EVAL_DIR to a\n# directory of .dcm files and provide a dict of {filename: label}\nEVAL_DIR    = None         # e.g. '/kaggle/input/.../train_images/26501/22032'\nEVAL_LABELS = {}           # e.g. {'12.dcm': 3, '15.dcm': 7, ...}\n# ────────────────────────────────────────────────────────────\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n\n# ── 1. DICOM to normalised float32 image ─────────────────────\ndef dicom_to_image(dcm):\n    \"\"\"\n    Convert a pydicom dataset to a float32 numpy array in [0, 1].\n    Applies HU rescaling and soft-tissue windowing (WC=40, WW=400).\n    \"\"\"\n    img = dcm.pixel_array.astype(np.float32)\n\n    slope     = float(getattr(dcm, 'RescaleSlope',     1))\n    intercept = float(getattr(dcm, 'RescaleIntercept', 0))\n    img = img * slope + intercept\n\n    raw_wc = getattr(dcm, 'WindowCenter', 40)\n    raw_ww = getattr(dcm, 'WindowWidth',  400)\n    wc = float(raw_wc[0] if hasattr(raw_wc, '__len__') else raw_wc)\n    ww = float(raw_ww[0] if hasattr(raw_ww, '__len__') else raw_ww)\n\n    lower = wc - ww / 2\n    upper = wc + ww / 2\n    img   = np.clip(img, lower, upper)\n    img   = (img - lower) / (upper - lower)\n    return img\n\n\n# ── 2. Preprocessing pipeline ────────────────────────────────\ndef preprocess(img):\n    resized = cv2.resize(img, (224, 224))\n    rgb     = np.stack([resized] * 3, axis=-1)\n    tensor  = torch.tensor(rgb).permute(2, 0, 1).float()\n    return tensor.unsqueeze(0).to(device)\n\n\n# ── 3. Model ──────────────────────────────────────────────────\nmodel = resnet18(weights=None)\nmodel = model.to(device)\nmodel.eval()\nprint(f\"Model on: {next(model.parameters()).device}\")\n\n\n# ── 4. GradCAM ───────────────────────────────────────────────\nclass GradCAM:\n    def __init__(self, model, target_layer):\n        self.model  = model\n        self.grads  = None\n        self.acts   = None\n        self._hooks = [\n            target_layer.register_forward_hook(self._save_acts),\n            target_layer.register_full_backward_hook(self._save_grads),\n        ]\n\n    def _save_acts(self, _, __, output):\n        self.acts = output.detach()\n\n    def _save_grads(self, _, __, grad_output):\n        self.grads = grad_output[0].detach()\n\n    def __call__(self, x, class_idx=None):\n        self.model.zero_grad()\n        logits = self.model(x)\n        idx    = class_idx if class_idx is not None else logits.argmax(dim=1).item()\n        logits[0, idx].backward()\n\n        weights  = self.grads.mean(dim=(2, 3), keepdim=True)\n        cam_map  = (weights * self.acts).sum(dim=1, keepdim=True)\n        cam_map  = F.relu(cam_map)\n        cam_map  = cam_map.squeeze().cpu().numpy()\n        cam_map  = cv2.resize(cam_map, (224, 224))\n        if cam_map.max() > 0:\n            cam_map = cam_map / cam_map.max()\n        return cam_map, idx\n\n    def remove(self):\n        for h in self._hooks:\n            h.remove()\n\n\n# ── 5. Feature-map hook (layer1, channel 0) ──────────────────\n_activation = {}\n\ndef _hook_fn(module, inp, output):\n    _activation['feat'] = output.detach()\n\n_hook = model.layer1.register_forward_hook(_hook_fn)\n\n\n# ── 6. CLAHE + sharpening ────────────────────────────────────\ndef enhance(img_01):\n    u8     = (img_01 * 255).astype(np.uint8)\n    clahe  = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n    eq     = clahe.apply(u8)\n    kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])\n    return cv2.filter2D(eq, -1, kernel)\n\n\n# ── 7. Single-image inference + visualisation ─────────────────\ndef run_inference(dcm_path, true_label=None):\n    print(f\"\\n{'='*60}\")\n    print(f\"Processing: {os.path.basename(dcm_path)}\")\n\n    dcm    = pydicom.dcmread(dcm_path)\n    img    = dicom_to_image(dcm)\n    tensor = preprocess(img)\n\n    # GradCAM (needs gradients)\n    gcam         = GradCAM(model, model.layer4)\n    heatmap, idx = gcam(tensor)\n    gcam.remove()\n\n    # Forward pass for feature map hook + softmax\n    with torch.no_grad():\n        logits = model(tensor)\n\n    probs      = F.softmax(logits, dim=1)[0]\n    pred_class = logits.argmax(dim=1).item()\n    confidence = probs[pred_class].item() * 100\n\n    feat_map = _activation['feat'][0, 0].cpu().numpy()\n    img_u8   = (img * 255).astype(np.uint8)\n    enhanced = enhance(img)\n\n    # GradCAM overlay\n    img_rgb  = cv2.resize(img_u8, (224, 224))\n    img_rgb3 = cv2.cvtColor(img_rgb, cv2.COLOR_GRAY2BGR)\n    heat_u8  = (heatmap * 255).astype(np.uint8)\n    heat_col = cv2.applyColorMap(heat_u8, cv2.COLORMAP_JET)\n    overlay  = cv2.addWeighted(img_rgb3, 0.55, heat_col, 0.45, 0)\n    overlay  = cv2.cvtColor(overlay, cv2.COLOR_BGR2RGB)\n\n    if true_label is not None:\n        acc_str = \"Correct ✓\" if pred_class == true_label else f\"Wrong ✗  (true={true_label})\"\n    else:\n        acc_str = \"No ground-truth label supplied\"\n\n    # Plot\n    fig = plt.figure(figsize=(18, 9))\n    fig.patch.set_facecolor('#0d0d0d')\n    gs  = gridspec.GridSpec(2, 4, figure=fig, hspace=0.35, wspace=0.25)\n\n    def dark_ax(spec, data, cmap, title):\n        ax = fig.add_subplot(spec)\n        ax.set_facecolor('#0d0d0d')\n        ax.imshow(data, cmap=cmap)\n        ax.set_title(title, color='#e0e0e0', fontsize=9, pad=6)\n        ax.axis('off')\n\n    dark_ax(gs[0, 0], img_u8,   'gray',    \"Processed DICOM\")\n    dark_ax(gs[0, 1], enhanced, 'inferno', \"CLAHE + Sharpened\")\n    dark_ax(gs[0, 2], feat_map, 'viridis', \"ResNet layer1 Feature Map (ch 0)\")\n    dark_ax(gs[0, 3], heatmap,  'jet',     \"GradCAM Heatmap\")\n    dark_ax(gs[1, :2], overlay, None,      \"GradCAM Overlay\")\n\n    ax_txt = fig.add_subplot(gs[1, 2:])\n    ax_txt.set_facecolor('#111827')\n    ax_txt.axis('off')\n\n    top5    = probs.topk(5)\n    summary = (\n        f\"File:        {os.path.basename(dcm_path)}\\n\"\n        f\"Device:      {device}\\n\"\n        f\"─────────────────────────\\n\"\n        f\"Pred class:  {pred_class}\\n\"\n        f\"Confidence:  {confidence:.1f}%\\n\"\n        f\"─────────────────────────\\n\"\n        f\"Accuracy:    {acc_str}\\n\"\n        f\"\\nTop-5 predictions:\\n\"\n    )\n    for rank, (cls, p) in enumerate(zip(top5.indices.tolist(), top5.values.tolist()), 1):\n        summary += f\"  {rank}. class {cls:>4d}  {p*100:5.1f}%\\n\"\n\n    ax_txt.text(0.05, 0.95, summary,\n                transform=ax_txt.transAxes,\n                va='top', ha='left',\n                fontsize=9, family='monospace',\n                color='#a3e635')\n\n    plt.suptitle(\"RSNA Abdominal CT — ResNet18 Inference\",\n                 color='white', fontsize=13, y=1.01)\n    plt.savefig('rsna_inference_output.png', dpi=150,\n                bbox_inches='tight', facecolor='#0d0d0d')\n    plt.show()\n    print(f\"Predicted class : {pred_class}  ({confidence:.1f}%)\")\n    print(f\"Accuracy check  : {acc_str}\")\n    print(\"Saved → rsna_inference_output.png\")\n    return pred_class, confidence\n\n\n# ── 8. Batch accuracy evaluator ───────────────────────────────\ndef evaluate_accuracy(dcm_dir, label_map):\n    \"\"\"\n    label_map = {'filename.dcm': int_label, ...}\n    Returns top-1 accuracy as float 0-1.\n    \"\"\"\n    files   = [f for f in os.listdir(dcm_dir) if f.endswith('.dcm')]\n    correct = 0\n    total   = 0\n    for fname in files:\n        if fname not in label_map:\n            continue\n        path = os.path.join(dcm_dir, fname)\n        dcm  = pydicom.dcmread(path)\n        img  = dicom_to_image(dcm)\n        t    = preprocess(img)\n        with torch.no_grad():\n            pred = model(t).argmax(dim=1).item()\n        correct += int(pred == label_map[fname])\n        total   += 1\n        status   = '✓' if pred == label_map[fname] else '✗'\n        print(f\"  {fname}: pred={pred}, true={label_map[fname]}, {status}\")\n\n    acc = correct / total if total else 0.0\n    print(f\"\\nTop-1 Accuracy: {correct}/{total} = {acc*100:.1f}%\")\n    return acc\n\n\n# ── 9. Run ────────────────────────────────────────────────────\npred, conf = run_inference(DCM_PATH, true_label=TRUE_LABEL)\n\nif EVAL_DIR and EVAL_LABELS:\n    print(\"\\n── Batch evaluation ──\")\n    evaluate_accuracy(EVAL_DIR, EVAL_LABELS)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:50.030344Z","iopub.execute_input":"2026-06-12T17:23:50.030719Z","iopub.status.idle":"2026-06-12T17:23:51.731901Z","shell.execute_reply.started":"2026-06-12T17:23:50.030694Z","shell.execute_reply":"2026-06-12T17:23:51.731068Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(os.listdir('/kaggle/input/rsna-2023-abdominal-trauma-detection'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.733207Z","iopub.execute_input":"2026-06-12T17:23:51.733532Z","iopub.status.idle":"2026-06-12T17:23:51.73919Z","shell.execute_reply.started":"2026-06-12T17:23:51.733502Z","shell.execute_reply":"2026-06-12T17:23:51.738197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 2  ← RUN THIS NOW\nimport os\n\nprint(os.listdir('/kaggle/input/rsna-2023-abdominal-trauma-detection'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.740526Z","iopub.execute_input":"2026-06-12T17:23:51.740849Z","iopub.status.idle":"2026-06-12T17:23:51.749524Z","shell.execute_reply.started":"2026-06-12T17:23:51.740822Z","shell.execute_reply":"2026-06-12T17:23:51.74853Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(os.listdir('/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images')[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.750939Z","iopub.execute_input":"2026-06-12T17:23:51.751791Z","iopub.status.idle":"2026-06-12T17:23:51.765797Z","shell.execute_reply.started":"2026-06-12T17:23:51.751756Z","shell.execute_reply":"2026-06-12T17:23:51.764719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"patient_path = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/26501'\n\nimport os\nprint(os.listdir(patient_path)[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.766983Z","iopub.execute_input":"2026-06-12T17:23:51.767378Z","iopub.status.idle":"2026-06-12T17:23:51.775314Z","shell.execute_reply.started":"2026-06-12T17:23:51.767355Z","shell.execute_reply":"2026-06-12T17:23:51.774371Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"series_path = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/26501/22032'\n\nimport os\nprint(os.listdir(series_path)[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.776275Z","iopub.execute_input":"2026-06-12T17:23:51.776704Z","iopub.status.idle":"2026-06-12T17:23:51.785458Z","shell.execute_reply.started":"2026-06-12T17:23:51.776677Z","shell.execute_reply":"2026-06-12T17:23:51.784484Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pydicom\nimport matplotlib.pyplot as plt\n\ndcm_path = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/26501/22032/12.dcm'\n\ndcm = pydicom.dcmread(dcm_path)\n\nimg = dicom_to_image(dcm)\n\nprint(img.shape)\nprint(img.min(), img.max())\n\nplt.figure(figsize=(6,6))\nplt.imshow(img, cmap='gray')\nplt.title(\"Processed DICOM\")\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.786517Z","iopub.execute_input":"2026-06-12T17:23:51.787319Z","iopub.status.idle":"2026-06-12T17:23:51.954756Z","shell.execute_reply.started":"2026-06-12T17:23:51.787285Z","shell.execute_reply":"2026-06-12T17:23:51.953919Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport torch\nimport numpy as np\n\nimg_resized = cv2.resize(img, (224, 224))\n\nimg_3ch = np.stack([img_resized]*3, axis=-1)\n\ntensor = torch.tensor(img_3ch).permute(2,0,1).float()\n\ntensor = tensor.unsqueeze(0).to(device)\n\nprint(tensor.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.955737Z","iopub.execute_input":"2026-06-12T17:23:51.956067Z","iopub.status.idle":"2026-06-12T17:23:51.967782Z","shell.execute_reply.started":"2026-06-12T17:23:51.956035Z","shell.execute_reply":"2026-06-12T17:23:51.966313Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\n\nwith torch.no_grad():\n    output = model(tensor)\n\nprint(output.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.969124Z","iopub.execute_input":"2026-06-12T17:23:51.969408Z","iopub.status.idle":"2026-06-12T17:23:51.983939Z","shell.execute_reply.started":"2026-06-12T17:23:51.969385Z","shell.execute_reply":"2026-06-12T17:23:51.983142Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred_class = output.argmax(dim=1).item()\n\nprint(\"Predicted class:\", pred_class)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.985178Z","iopub.execute_input":"2026-06-12T17:23:51.985575Z","iopub.status.idle":"2026-06-12T17:23:51.990921Z","shell.execute_reply.started":"2026-06-12T17:23:51.985532Z","shell.execute_reply":"2026-06-12T17:23:51.989805Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# normalize image for visualization\nimg_vis = (img * 255).astype(np.uint8)\n\n# create enhanced version\nenhanced = cv2.equalizeHist(img_vis)\n\n# optional sharpening\nkernel = np.array([\n    [0, -1, 0],\n    [-1, 5,-1],\n    [0, -1, 0]\n])\n\nenhanced = cv2.filter2D(enhanced, -1, kernel)\n\n# side-by-side plot\nfig, ax = plt.subplots(1, 2, figsize=(12,6))\n\nax[0].imshow(img_vis, cmap='gray')\nax[0].set_title(\"Original CT Scan\")\nax[0].axis('off')\n\nax[1].imshow(enhanced, cmap='inferno')\nax[1].set_title(f\"AI-Enhanced Output | Predicted Class: {pred_class}\")\nax[1].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:51.992082Z","iopub.execute_input":"2026-06-12T17:23:51.992416Z","iopub.status.idle":"2026-06-12T17:23:52.518463Z","shell.execute_reply.started":"2026-06-12T17:23:51.992384Z","shell.execute_reply":"2026-06-12T17:23:52.517412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"activation = {}\n\ndef hook_fn(module, input, output):\n    activation['feat'] = output.detach()\n\nhook = model.layer1.register_forward_hook(hook_fn)\n\nwith torch.no_grad():\n    _ = model(tensor)\n\nfeature_map = activation['feat'][0, 0].cpu().numpy()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:52.519778Z","iopub.execute_input":"2026-06-12T17:23:52.52014Z","iopub.status.idle":"2026-06-12T17:23:52.532345Z","shell.execute_reply.started":"2026-06-12T17:23:52.520107Z","shell.execute_reply":"2026-06-12T17:23:52.531351Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(6,6))\nplt.imshow(feature_map, cmap='inferno')\nplt.title(\"ResNet Feature Map\")\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T17:23:52.533761Z","iopub.execute_input":"2026-06-12T17:23:52.534165Z","iopub.status.idle":"2026-06-12T17:23:52.717625Z","shell.execute_reply.started":"2026-06-12T17:23:52.534133Z","shell.execute_reply":"2026-06-12T17:23:52.716493Z"}},"outputs":[],"execution_count":null}]}