{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":11119618,"sourceType":"datasetVersion","datasetId":6933898},{"sourceId":11119748,"sourceType":"datasetVersion","datasetId":6934002}],"dockerImageVersionId":30918,"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\n# import numpy as np # linear algebra\n# import 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\n# import 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":"2025-03-24T07:36:36.879545Z","iopub.execute_input":"2025-03-24T07:36:36.879917Z","iopub.status.idle":"2025-03-24T07:36:36.883823Z","shell.execute_reply.started":"2025-03-24T07:36:36.879884Z","shell.execute_reply":"2025-03-24T07:36:36.882993Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install pydicom -q\n!pip uninstall torch torchvision -y\n!pip install torch==2.1 torchvision==0.16 -q\n!pip install -qU pycocotools\n!pip install -qU wandb","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T07:36:36.884588Z","iopub.execute_input":"2025-03-24T07:36:36.884913Z","iopub.status.idle":"2025-03-24T07:39:27.498984Z","shell.execute_reply.started":"2025-03-24T07:36:36.884891Z","shell.execute_reply":"2025-03-24T07:39:27.498187Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for name in list(globals()):\n    if not name.startswith(\"_\"):  # Avoid deleting built-in and special variables\n        del globals()[name]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:05:50.573917Z","iopub.execute_input":"2025-04-01T16:05:50.574240Z","iopub.status.idle":"2025-04-01T16:05:50.578659Z","shell.execute_reply.started":"2025-04-01T16:05:50.574216Z","shell.execute_reply":"2025-04-01T16:05:50.577607Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CONDITION = 'LeftNeuralForaminalNarrowing'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:05:50.791409Z","iopub.execute_input":"2025-04-01T16:05:50.791759Z","iopub.status.idle":"2025-04-01T16:05:50.796243Z","shell.execute_reply.started":"2025-04-01T16:05:50.791731Z","shell.execute_reply":"2025-04-01T16:05:50.795185Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport time\nimport random\nfrom datetime import datetime\nimport numpy as np\nimport collections\n\nfrom matplotlib import animation, rc\nimport pandas as pd\n\nimport matplotlib.patches as patches\nimport matplotlib.pyplot as plt\n\nimport tqdm\nimport sys\nimport torch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:05:50.987058Z","iopub.execute_input":"2025-04-01T16:05:50.987410Z","iopub.status.idle":"2025-04-01T16:05:56.040903Z","shell.execute_reply.started":"2025-04-01T16:05:50.987360Z","shell.execute_reply":"2025-04-01T16:05:56.040012Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Reproducibility","metadata":{}},{"cell_type":"code","source":"# Ensure deterministic behavior\ntorch.backends.cudnn.deterministic = True\nrandom.seed(hash(\"setting random seeds\") % 2**32 - 1)\nnp.random.seed(hash(\"improves reproducibility\") % 2**32 - 1)\ntorch.manual_seed(hash(\"by removing stochasticity\") % 2**32 - 1)\ntorch.cuda.manual_seed_all(hash(\"so runs are repeatable\") % 2**32 - 1)\n\n# Device configuration\ndevice = torch.device('cuda:0') if torch.cuda.is_available() else torch.device('cpu')\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:05:56.949357Z","iopub.execute_input":"2025-04-01T16:05:56.949863Z","iopub.status.idle":"2025-04-01T16:05:56.968913Z","shell.execute_reply.started":"2025-04-01T16:05:56.949834Z","shell.execute_reply":"2025-04-01T16:05:56.968047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Directories\n# PROJECT_DIR = '/home/jupyter'\nDATA_DIR = os.path.join('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification', 'train_images')\nSRC_DIR = os.path.join('/kaggle/input/rsna-code/rsna-2024-main', 'src')\n\nCROP_DIR = '/kaggle/working'\nos.makedirs(CROP_DIR, exist_ok=True)\nMODEL_DIR = os.path.join('/kaggle/working/', 'models', '02_train_disc_detection', CONDITION)\nos.makedirs(MODEL_DIR, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:05:59.317066Z","iopub.execute_input":"2025-04-01T16:05:59.317482Z","iopub.status.idle":"2025-04-01T16:05:59.323518Z","shell.execute_reply.started":"2025-04-01T16:05:59.317453Z","shell.execute_reply":"2025-04-01T16:05:59.322509Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(DATA_DIR)\nprint(SRC_DIR)\nprint(CROP_DIR)\nprint(MODEL_DIR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:00.675134Z","iopub.execute_input":"2025-04-01T16:06:00.675519Z","iopub.status.idle":"2025-04-01T16:06:00.682017Z","shell.execute_reply.started":"2025-04-01T16:06:00.675491Z","shell.execute_reply":"2025-04-01T16:06:00.680891Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/engine.py\")\nos.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/utils.py\")\nos.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/coco_utils.py\")\nos.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/coco_eval.py\")\nos.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/transforms.py\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:02.800851Z","iopub.execute_input":"2025-04-01T16:06:02.801218Z","iopub.status.idle":"2025-04-01T16:06:03.865749Z","shell.execute_reply.started":"2025-04-01T16:06:02.801189Z","shell.execute_reply":"2025-04-01T16:06:03.864695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torchvision\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\n\n# ----------- Functions for disc detection -----------\n\ndef load_model_disc_detection(state_dict=None):\n    \n    # We use the lastest\n    model = torchvision.models.detection.fasterrcnn_resnet50_fpn_v2(weights=\"DEFAULT\")\n\n    # Replace the classifier with a new one, that has Num_classes\n    num_classes = 6  # 5 classes (discs) + background\n\n    # Get number of input features for the classifier\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n\n    # Replace the pre-trained head with a new one\n    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\n    \n    # Import parameters if available\n    if state_dict:\n        model.load_state_dict(state_dict)\n    \n    return model\n\n\n# ----------- Functions for severity classification -----------\n\ndef load_model_severity_classification(state_dict=None):\n    \n    # We use the lastest\n    model = torchvision.models.swin_v2_t(weights=\"DEFAULT\")\n\n    # Replace the classifier with a new one, that has Num_classes\n    num_classes = 3 \n\n    # Get number of input features for the classifier\n    in_features = model.head.in_features\n\n    # Replace the pre-trained head with a new one\n    model.head = nn.Linear(in_features, num_classes)\n    \n    # Import parameters if available\n    if state_dict:\n        model.load_state_dict(state_dict)\n    \n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:03.867062Z","iopub.execute_input":"2025-04-01T16:06:03.867440Z","iopub.status.idle":"2025-04-01T16:06:07.076919Z","shell.execute_reply.started":"2025-04-01T16:06:03.867413Z","shell.execute_reply":"2025-04-01T16:06:07.075919Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file_disc_detection = \"\"\"\n# # ONLY NEED TO RUN ONCE\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/engine.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/utils.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/coco_utils.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/coco_eval.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/transforms.py\")\nimport os\nimport utils\nfrom engine import train_one_epoch, evaluate\n\n# Util functions\nwith open(os.path.join(SRC_DIR, 'src/data.py')) as file:\n    exec(file.read())\nwith open(os.path.join(SRC_DIR, 'models.py')) as file:\n    exec(file.read())\n    \n    \n# LEVEL_LABELS = {\n#     \"L1/L2\": 1,\n#     \"L2/L3\": 2,\n#     \"L3/L4\": 3,\n#     \"L4/L5\": 4,\n#     \"L5/S1\": 5\n# }\n\ndef model_pipeline(config, model, model_dir, train_df, val_df):\n\n    # make the model, data, and optimization problem\n    model, train_loader, val_loader, optimizer, lr_scheduler = make(config, model, train_df, val_df)\n\n    # and use them to train the model\n    train_and_validate(model, model_dir, train_loader, val_loader, optimizer, lr_scheduler, config)\n\n    return\n\n\ndef make(config, model, train_df, val_df):\n    \n    # Make training set\n    dataset = RSNAMultipleBBoxesDataset(train_df, w = config['box_w'], h_l1_l4 = config['box_h_l1_l4'], h_l5 = config['box_h_l5'])\n    train_loader = torch.utils.data.DataLoader(\n        dataset,\n        batch_size=config['batch_size'],\n        shuffle=True,\n        collate_fn=utils.collate_fn,\n        num_workers=os.cpu_count()\n    )\n    \n    # Make validation set\n    dataset_val = RSNAMultipleBBoxesDataset(val_df, w = config['box_w'], h_l1_l4 = config['box_h_l1_l4'], h_l5 = config['box_h_l5'])\n    val_loader = torch.utils.data.DataLoader(\n        dataset_val,\n        batch_size=config['batch_size'],\n        shuffle=True,\n        collate_fn=utils.collate_fn,\n        num_workers=os.cpu_count()\n    )\n\n    # Make model\n    model.to(device)\n\n    # construct an optimizer\n    params = [p for p in model.parameters() if p.requires_grad]\n    optimizer = torch.optim.Adam(\n        params,\n        lr=config['lr'],\n    )\n\n    # and a learning rate scheduler\n    lr_scheduler = torch.optim.lr_scheduler.StepLR(\n        optimizer,\n        step_size=config['lr_step_size'],\n        gamma=config['lr_gamma']\n    )\n    \n    return model, train_loader, val_loader, optimizer, lr_scheduler\n\ndef train_and_validate(model, model_dir, train_loader, val_loader, optimizer, lr_scheduler, config):\n    \n    for epoch in tqdm.tqdm(range(config['num_epochs']), desc=\"Training Epochs\"):\n        \n        # train for one epoch, printing every 30 iterations\n        train_one_epoch(model, optimizer, train_loader, device, epoch, print_freq=30)\n        # update the learning rate\n        lr_scheduler.step()\n        # evaluate on the validation dataset\n        evaluate(model, val_loader, device=device)\n        \n        # Save model after every epoch\n        dirname = f'{model_dir}/epoch_{epoch}'\n        os.makedirs(dirname, exist_ok=True,)\n        fname = f'{dirname}/model_dict.pt'\n        torch.save(model.state_dict(), fname)\n\"\"\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:07.983971Z","iopub.execute_input":"2025-04-01T16:06:07.984541Z","iopub.status.idle":"2025-04-01T16:06:07.989677Z","shell.execute_reply.started":"2025-04-01T16:06:07.984510Z","shell.execute_reply":"2025-04-01T16:06:07.988629Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SRC_DIR = '/kaggle/input/rsna-code/rsna-2024-main'\nexec(file_disc_detection)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:10.207599Z","iopub.execute_input":"2025-04-01T16:06:10.207928Z","iopub.status.idle":"2025-04-01T16:06:11.255833Z","shell.execute_reply.started":"2025-04-01T16:06:10.207901Z","shell.execute_reply":"2025-04-01T16:06:11.254786Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CONFIG = dict(\n    num_epochs=3,\n    batch_size=10,\n    lr=0.0001,\n    lr_step_size=3,\n    lr_gamma=0.1,\n    box_w = 70, # width of the bounding boxes\n    box_h_l1_l4 = 30, # height of the boxes for levels from L1/L2 to L4/L5\n    box_h_l5 = 40 # width of the boxes for level L5/S1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:12.499806Z","iopub.execute_input":"2025-04-01T16:06:12.500137Z","iopub.status.idle":"2025-04-01T16:06:12.504919Z","shell.execute_reply.started":"2025-04-01T16:06:12.500112Z","shell.execute_reply":"2025-04-01T16:06:12.503778Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(os.path.join('/kaggle/input/rsna-metadata/kaggle 4/working', CONDITION, 'train.csv'))\ntest_df = pd.read_csv(os.path.join('/kaggle/input/rsna-metadata/kaggle 4/working', CONDITION, 'test.csv'))\nval_df = pd.read_csv(os.path.join('/kaggle/input/rsna-metadata/kaggle 4/working', CONDITION, 'val.csv'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:12.686436Z","iopub.execute_input":"2025-04-01T16:06:12.686765Z","iopub.status.idle":"2025-04-01T16:06:12.784179Z","shell.execute_reply.started":"2025-04-01T16:06:12.686739Z","shell.execute_reply":"2025-04-01T16:06:12.783118Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:06:18.607536Z","iopub.execute_input":"2025-04-01T16:06:18.608015Z","iopub.status.idle":"2025-04-01T16:06:18.640415Z","shell.execute_reply.started":"2025-04-01T16:06:18.607975Z","shell.execute_reply":"2025-04-01T16:06:18.639234Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Example of bounding boxes","metadata":{}},{"cell_type":"code","source":"# This is coming because we executed the code of file_disc_detection which in turn executed the src/data.py which contains the implementation of RSNAMultipleBBoxesDataset class.\ntmp_ds = RSNAMultipleBBoxesDataset(train_df, w = CONFIG['box_w'], h_l1_l4 = CONFIG['box_h_l1_l4'], h_l5 = CONFIG['box_h_l5'])\ntmp_dl = torch.utils.data.DataLoader(\n  tmp_ds,\n  batch_size=1,\n  shuffle=False,\n  collate_fn=utils.collate_fn\n)\n\nfig, ax = plt.subplots(nrows=1, ncols=2, figsize=(14,16))\nfor i, (img, target) in enumerate(tmp_dl):\n    if i == 2: break\n    img = img[0]\n    target = target[0]\n    print(img.shape)\n    print(target.keys())\n    y = img.squeeze().numpy()\n    ax[i].imshow(y, cmap=plt.cm.bone)\n    for j, box in enumerate(target['boxes']):\n        x0, y0, x1, y1 = box.numpy()\n        w = x1 - x0\n        h = y1 - y0\n        ax[i].add_patch(patches.Rectangle((x0, y0), w, h, linewidth=1, edgecolor='r', facecolor='none'))\ndel tmp_ds, tmp_dl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:11:29.059062Z","iopub.execute_input":"2025-04-01T16:11:29.059595Z","iopub.status.idle":"2025-04-01T16:11:29.713913Z","shell.execute_reply.started":"2025-04-01T16:11:29.059547Z","shell.execute_reply":"2025-04-01T16:11:29.712753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"model = load_model_disc_detection()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:21:04.963601Z","iopub.execute_input":"2025-04-01T16:21:04.963934Z","iopub.status.idle":"2025-04-01T16:21:07.247158Z","shell.execute_reply.started":"2025-04-01T16:21:04.963909Z","shell.execute_reply":"2025-04-01T16:21:07.246320Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def count_trainable_parameters(model):\n    return sum(p.numel() for p in model.parameters() if p.requires_grad)\n\nprint(f\"Number of trainable parameters in the model: {count_trainable_parameters(model)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:21:07.248543Z","iopub.execute_input":"2025-04-01T16:21:07.248905Z","iopub.status.idle":"2025-04-01T16:21:07.255731Z","shell.execute_reply.started":"2025-04-01T16:21:07.248870Z","shell.execute_reply":"2025-04-01T16:21:07.254635Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Test forward Pass","metadata":{}},{"cell_type":"code","source":"# Create dataset and dataloader\ndataset = RSNAMultipleBBoxesDataset(train_df, w = CONFIG['box_w'], h_l1_l4 = CONFIG['box_h_l1_l4'], h_l5 = CONFIG['box_h_l5'])\ntrain_loader = torch.utils.data.DataLoader(\n  dataset,\n  batch_size=2,\n  shuffle=True,\n  collate_fn=utils.collate_fn\n)\n\n# Get first input from dataloader\nimages, targets = next(iter(train_loader))\n# print(images[0])\nprint((images))\n# print((target))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T16:21:10.839893Z","iopub.execute_input":"2025-04-01T16:21:10.840275Z","iopub.status.idle":"2025-04-01T16:21:10.948344Z","shell.execute_reply.started":"2025-04-01T16:21:10.840245Z","shell.execute_reply":"2025-04-01T16:21:10.947250Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create dataset and dataloader\ndataset = RSNAMultipleBBoxesDataset(train_df, w = CONFIG['box_w'], h_l1_l4 = CONFIG['box_h_l1_l4'], h_l5 = CONFIG['box_h_l5'])\ntrain_loader = torch.utils.data.DataLoader(\n  dataset,\n  batch_size=2,\n  shuffle=True,\n  collate_fn=utils.collate_fn\n)\n\n# Get first input from dataloader\nimages, targets = next(iter(train_loader))\nimages = list(image for image in images)\ntargets = [{k: v for k, v in t.items()} for t in targets]\n\n# Inference\nmodel.to('cpu')\nmodel.eval()\nwith torch.inference_mode():\n    predictions = model(images)\n    \n# Inspect output\nprint(f\"Keys in the prediction: {predictions[0].keys()}\")\nprint(f\"Shape of the predicted 'boxes' object: {predictions[0]['boxes'].shape}\")  # [100 boxes, 4 dim (x0, y0, x1, y1)]\nprint(f\"Coordinates of the first box: {torch.round(predictions[0]['boxes'][0]).tolist()}\") # (x0, y0, x1, y1) of the first box\nprint(f\"Label predictions of the first 5 boxes: {predictions[0]['labels'][:5]}\")\nprint(f\"Prediction scores of the first 5 boxes: {predictions[0]['scores'][:5]}\") ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T07:40:02.548913Z","iopub.execute_input":"2025-03-24T07:40:02.549231Z","iopub.status.idle":"2025-03-24T07:40:10.706913Z","shell.execute_reply.started":"2025-03-24T07:40:02.549204Z","shell.execute_reply":"2025-03-24T07:40:10.706096Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_predicted = predictions[0]\ntargets_predicted = predictions[1]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T07:40:10.708047Z","iopub.execute_input":"2025-03-24T07:40:10.708444Z","iopub.status.idle":"2025-03-24T07:40:10.711725Z","shell.execute_reply.started":"2025-03-24T07:40:10.708408Z","shell.execute_reply":"2025-03-24T07:40:10.710881Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"targets_predicted","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T07:40:10.713245Z","iopub.execute_input":"2025-03-24T07:40:10.713457Z","iopub.status.idle":"2025-03-24T07:40:10.731914Z","shell.execute_reply.started":"2025-03-24T07:40:10.713437Z","shell.execute_reply":"2025-03-24T07:40:10.731040Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Train and Validate","metadata":{}},{"cell_type":"code","source":"# torch.cuda.empty_cache()\n# gc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T07:40:10.732838Z","iopub.execute_input":"2025-03-24T07:40:10.733045Z","iopub.status.idle":"2025-03-24T07:40:10.735853Z","shell.execute_reply.started":"2025-03-24T07:40:10.733026Z","shell.execute_reply":"2025-03-24T07:40:10.735218Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_pipeline(config=CONFIG, model=model, model_dir=MODEL_DIR, train_df=train_df, val_df=val_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T07:40:16.704343Z","iopub.execute_input":"2025-03-24T07:40:16.704620Z","iopub.status.idle":"2025-03-24T08:21:46.657139Z","shell.execute_reply.started":"2025-03-24T07:40:16.704599Z","shell.execute_reply":"2025-03-24T08:21:46.656322Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Examples of predection","metadata":{}},{"cell_type":"code","source":"trained_model = load_model_disc_detection(state_dict=torch.load(os.path.join(f\"{MODEL_DIR}/epoch_{CONFIG['num_epochs']-1}/model_dict.pt\")))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:34:01.372134Z","iopub.execute_input":"2025-03-24T08:34:01.372454Z","iopub.status.idle":"2025-03-24T08:34:02.354107Z","shell.execute_reply.started":"2025-03-24T08:34:01.372427Z","shell.execute_reply":"2025-03-24T08:34:02.353149Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!zip -r disc_detection_left_neural_foraminal_narrowing.zip /kaggle/working/models/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:37:07.784154Z","iopub.execute_input":"2025-03-24T08:37:07.784506Z","iopub.status.idle":"2025-03-24T08:37:34.353245Z","shell.execute_reply.started":"2025-03-24T08:37:07.784478Z","shell.execute_reply":"2025-03-24T08:37:34.352198Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import FileLink\nFileLink(r'disc_detection_left_neural_foraminal_narrowing.zip')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:42:19.474697Z","iopub.execute_input":"2025-03-24T08:42:19.475083Z","iopub.status.idle":"2025-03-24T08:42:19.480964Z","shell.execute_reply.started":"2025-03-24T08:42:19.475035Z","shell.execute_reply":"2025-03-24T08:42:19.480145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"LABELS_DICT = {\n    1: \"L1_L2\",\n    2: \"L2_L3\",\n    3: \"L3_L4\",\n    4: \"L4_L5\",\n    5: \"L5_S1\"\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:43:41.084473Z","iopub.execute_input":"2025-03-24T08:43:41.084811Z","iopub.status.idle":"2025-03-24T08:43:41.088874Z","shell.execute_reply.started":"2025-03-24T08:43:41.084788Z","shell.execute_reply":"2025-03-24T08:43:41.088092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_best_boxes(pred):\n    best_boxes = {}\n\n    for box, label, score in zip(pred['boxes'], pred['labels'], pred['scores']):\n        if label.item() not in best_boxes or score > best_boxes[label.item()]['score']:\n            best_boxes[label.item()] = {'box': box.tolist(), 'score': score.item()}\n\n    result = {\n        'boxes': [entry['box'] for entry in best_boxes.values()],\n        'labels': list(best_boxes.keys()),\n        'scores': [entry['score'] for entry in best_boxes.values()]\n    }\n\n    return result\n\ndef plot_prediction(x, pred):\n    x = x[0, :]\n    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(12,8))\n    ax.imshow(x, cmap=\"bone\")\n    pred = get_best_boxes(pred)\n    for i in range(len(pred['boxes'])):\n        x0, y0, x1, y1 = pred['boxes'][i]\n        label = pred['labels'][i]\n        score = pred['scores'][i]\n        h = y1 - y0\n        w = x1 - x0\n        ax.add_patch(patches.Rectangle((x0, y0), w, h, linewidth=1, edgecolor='r', facecolor='none'))\n        ax.text(x0+w+10, y0+h/2, f\"{LABELS_DICT[label]} ({'{:.2f}'.format(score)})\", color='r',fontsize=14)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:43:41.529939Z","iopub.execute_input":"2025-03-24T08:43:41.530241Z","iopub.status.idle":"2025-03-24T08:43:41.537972Z","shell.execute_reply.started":"2025-03-24T08:43:41.530215Z","shell.execute_reply":"2025-03-24T08:43:41.537034Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot predictions for a few validation samples\ndataset_val = RSNAMultipleBBoxesDataset(val_df, w = CONFIG['box_w'], h_l1_l4 = CONFIG['box_h_l1_l4'], h_l5 = CONFIG['box_h_l5'])\nval_loader = torch.utils.data.DataLoader(\n    dataset_val,\n    batch_size=1,\n    shuffle=True,\n    collate_fn=utils.collate_fn,\n    num_workers=os.cpu_count()\n)\n\nfor i, (images, targets) in enumerate(val_loader):\n    if i == 5: break\n    images = list(image.to(device) for image in images)\n    targets = [{k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in t.items()} for t in targets]\n\n    trained_model.to(device)\n    trained_model.eval()\n    with torch.inference_mode():\n        predictions = trained_model(images)\n\n    for i in range(len(images)):\n        plot_prediction(images[i].cpu(), predictions[i])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:43:50.887195Z","iopub.execute_input":"2025-03-24T08:43:50.887500Z","iopub.status.idle":"2025-03-24T08:43:53.397603Z","shell.execute_reply.started":"2025-03-24T08:43:50.887466Z","shell.execute_reply":"2025-03-24T08:43:53.396519Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Crop images with true labels and save","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"def get_true_boxes(target):\n\n    result = {\n        'boxes': target['boxes'].tolist(),\n        'labels': target['labels'].tolist()\n    }\n\n    return result\n\ndef crop_bbox(image, bbox):\n    x0, y0, x1, y1 = bbox\n\n    cropped_img = torchvision.transforms.functional.crop(\n        image,\n        top=round(int(y0)),\n        left=round(int(x0)),\n        height=round(int(y1 - y0)),\n        width=round(int(x1 - x0))\n    )\n    return cropped_img\n\n\ndef plot_crop(image, bboxes):\n    fig, ax = plt.subplots(nrows=5, ncols=1, figsize=(4,3))\n    plt.subplots_adjust(top=2)\n\n    for i in range(len(bboxes['boxes'])):\n        label_i = bboxes['labels'][i] - 1\n        label = LABELS_DICT[label_i + 1]\n        score = bboxes['scores'][i]\n        bbox = bboxes['boxes'][i]\n\n        cropped_img = crop_bbox(image, bbox)\n        cropped_img = cropped_img[0, :]\n        # print(cropped_img.shape)\n\n        ax[label_i].set_axis_off()\n        ax[label_i].imshow(cropped_img, cmap=\"bone\")\n        ax[label_i].set_title(f\"{label} ({'{:.2f}'.format(score)})\")\n        \n\ndef save_crop(image, bboxes, target):\n    series_id = target['series_id']\n    study_id = target['study_id']\n    instance_number = target['instance_number']\n\n    for i in range(len(bboxes['boxes'])):\n        label = LABELS_DICT[bboxes['labels'][i]]\n\n        dirname = f'{CROP_DIR}/{study_id}/{series_id}/{label}'\n        os.makedirs(dirname, exist_ok=True)\n        filepath = os.path.join(dirname, f'{instance_number}.pt')\n\n        bbox = bboxes['boxes'][i]\n\n        cropped_img = crop_bbox(image, bbox)\n        torch.save(cropped_img, filepath)\n\n    return","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:44:21.891076Z","iopub.execute_input":"2025-03-24T08:44:21.891407Z","iopub.status.idle":"2025-03-24T08:44:21.900324Z","shell.execute_reply.started":"2025-03-24T08:44:21.891378Z","shell.execute_reply":"2025-03-24T08:44:21.899569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def crop_and_save_true_boxes(df, limit = None):\n    \n    dataset = RSNAMultipleBBoxesDataset(df, w = CONFIG['box_w'], h_l1_l4 = CONFIG['box_h_l1_l4'], h_l5 = CONFIG['box_h_l5'], limit=limit)\n    train_loader = torch.utils.data.DataLoader(\n        dataset,\n        batch_size=10,\n        shuffle=True,\n        collate_fn=utils.collate_fn\n    )\n\n    for i, (images, targets) in enumerate(tqdm.tqdm(train_loader)):\n        images = list(image.to(device) for image in images)\n        targets = [{k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in t.items()} for t in targets]\n\n        for i in range(len(images)):\n            bboxes = get_true_boxes(targets[i])\n            save_crop(images[i].cpu(), bboxes, targets[i])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:44:22.471177Z","iopub.execute_input":"2025-03-24T08:44:22.471463Z","iopub.status.idle":"2025-03-24T08:44:22.477936Z","shell.execute_reply.started":"2025-03-24T08:44:22.471442Z","shell.execute_reply":"2025-03-24T08:44:22.476767Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"crop_and_save_true_boxes(df = train_df, limit = None)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:44:42.064917Z","iopub.execute_input":"2025-03-24T08:44:42.065242Z","iopub.status.idle":"2025-03-24T08:45:36.082939Z","shell.execute_reply.started":"2025-03-24T08:44:42.065216Z","shell.execute_reply":"2025-03-24T08:45:36.082081Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"crop_and_save_true_boxes(df = val_df, limit = None)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:45:37.960197Z","iopub.execute_input":"2025-03-24T08:45:37.960481Z","iopub.status.idle":"2025-03-24T08:45:47.470416Z","shell.execute_reply.started":"2025-03-24T08:45:37.960458Z","shell.execute_reply":"2025-03-24T08:45:47.469498Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Checkout few crops with different conditions","metadata":{}},{"cell_type":"code","source":"def load_crop(row, plot=True, title=None, output=True):\n    file = f\"{CROP_DIR}/{row['study_id']}/{row['series_id']}/{LABELS_DICT[row['level_code']]}/{row['instance_number']}.pt\"\n    crop = torch.load(file).squeeze(0)\n    if plot:\n        plt.imshow(crop, cmap=\"bone\")\n        if title: plt.title(title)\n        plt.show()\n    if output:\n        return crop","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:46:48.951505Z","iopub.execute_input":"2025-03-24T08:46:48.951809Z","iopub.status.idle":"2025-03-24T08:46:48.956406Z","shell.execute_reply.started":"2025-03-24T08:46:48.951785Z","shell.execute_reply":"2025-03-24T08:46:48.955590Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x = load_crop(train_df.iloc[0], plot=False, output=True)\ntrans = T.ToDtype(torch.float, scale=True)\ny = trans(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:46:49.440027Z","iopub.execute_input":"2025-03-24T08:46:49.440350Z","iopub.status.idle":"2025-03-24T08:46:49.445945Z","shell.execute_reply.started":"2025-03-24T08:46:49.440322Z","shell.execute_reply":"2025-03-24T08:46:49.445289Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[train_df.level_code == 1].groupby('severity').head(n=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:46:49.782918Z","iopub.execute_input":"2025-03-24T08:46:49.783224Z","iopub.status.idle":"2025-03-24T08:46:49.800424Z","shell.execute_reply.started":"2025-03-24T08:46:49.783195Z","shell.execute_reply":"2025-03-24T08:46:49.799577Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"load_crop(train_df.iloc[0], plot=True, title='Normal/Mild', output=False)\nload_crop(train_df.iloc[350], plot=True, title='Moderate', output=False)\nload_crop(train_df.iloc[185], plot=True, title='Severe', output=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:47:32.675339Z","iopub.execute_input":"2025-03-24T08:47:32.675629Z","iopub.status.idle":"2025-03-24T08:47:33.109684Z","shell.execute_reply.started":"2025-03-24T08:47:32.675606Z","shell.execute_reply":"2025-03-24T08:47:33.108935Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[train_df.level_code == 2].groupby('severity').head(n=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:47:52.085260Z","iopub.execute_input":"2025-03-24T08:47:52.085567Z","iopub.status.idle":"2025-03-24T08:47:52.099268Z","shell.execute_reply.started":"2025-03-24T08:47:52.085542Z","shell.execute_reply":"2025-03-24T08:47:52.098580Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"load_crop(train_df.iloc[1], plot=True, title='Normal/Mild', output=False)\nload_crop(train_df.iloc[51], plot=True, title='Moderate', output=False)\nload_crop(train_df.iloc[86], plot=True, title='Severe', output=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:47:58.735666Z","iopub.execute_input":"2025-03-24T08:47:58.735975Z","iopub.status.idle":"2025-03-24T08:47:59.157076Z","shell.execute_reply.started":"2025-03-24T08:47:58.735945Z","shell.execute_reply":"2025-03-24T08:47:59.156233Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[train_df.level_code == 5].groupby('severity').head(n=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:48:04.415250Z","iopub.execute_input":"2025-03-24T08:48:04.415533Z","iopub.status.idle":"2025-03-24T08:48:04.429751Z","shell.execute_reply.started":"2025-03-24T08:48:04.415510Z","shell.execute_reply":"2025-03-24T08:48:04.428854Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"load_crop(train_df.iloc[4], plot=True, title='Normal/Mild', output=False)\nload_crop(train_df.iloc[496], plot=True, title='Moderate', output=False)\nload_crop(train_df.iloc[1955], plot=True, title='Severe', output=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-24T08:48:04.691995Z","iopub.execute_input":"2025-03-24T08:48:04.692315Z","iopub.status.idle":"2025-03-24T08:48:05.127805Z","shell.execute_reply.started":"2025-03-24T08:48:04.692288Z","shell.execute_reply":"2025-03-24T08:48:05.126891Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}