{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"},{"sourceId":9235934,"sourceType":"datasetVersion","datasetId":5586514},{"sourceId":9238129,"sourceType":"datasetVersion","datasetId":5587999},{"sourceId":9238150,"sourceType":"datasetVersion","datasetId":5588015},{"sourceId":9409759,"sourceType":"datasetVersion","datasetId":5577143},{"sourceId":192715298,"sourceType":"kernelVersion"},{"sourceId":193161758,"sourceType":"kernelVersion"},{"sourceId":193298353,"sourceType":"kernelVersion"},{"sourceId":193335312,"sourceType":"kernelVersion"},{"sourceId":193338638,"sourceType":"kernelVersion"},{"sourceId":193417638,"sourceType":"kernelVersion"},{"sourceId":100132,"sourceType":"modelInstanceVersion","modelInstanceId":64905,"modelId":89293},{"sourceId":111113,"sourceType":"modelInstanceVersion","modelInstanceId":85952,"modelId":84065}],"dockerImageVersionId":30775,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -q --no-index --find-links /kaggle/input/ultralytics ultralytics","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:10:48.007663Z","iopub.execute_input":"2024-10-01T14:10:48.008062Z","iopub.status.idle":"2024-10-01T14:11:01.090528Z","shell.execute_reply.started":"2024-10-01T14:10:48.008024Z","shell.execute_reply":"2024-10-01T14:11:01.089467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport pydicom\nfrom PIL import Image\nimport numpy as np\nfrom multiprocessing import Pool, cpu_count\n\nimport sklearn.metrics\nimport torch\nimport cv2\nimport numpy as np \nimport pandas as pd \nfrom tqdm.auto import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-10-01T14:11:01.092547Z","iopub.execute_input":"2024-10-01T14:11:01.092879Z","iopub.status.idle":"2024-10-01T14:11:10.699398Z","shell.execute_reply.started":"2024-10-01T14:11:01.092844Z","shell.execute_reply":"2024-10-01T14:11:10.698404Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EVAL = False # Change to True to compute the validation score\nIMG_DIR = '/images'\nFOLD = 0\nSAMPLE = False # True for quick debugging\nSEVERITIES = ['Normal/Mild', 'Moderate', 'Severe']\nLEVELS = ['l1_l2', 'l2_l3', 'l3_l4', 'l4_l5', 'l5_s1']\n\nSCS_WEIGHTS = ['/kaggle/input/lsdc-train-yolo-scs/lsdc_yolov8/train/weights/best.pt']\n\nSS_WEIGHTS = ['/kaggle/input/lsdc-train-yolo-ss/lsdc_yolov8/train/weights/best.pt',\n             '/kaggle/input/lsdc-yolo-ssv3/best.pt']\n\nNFN_WEIGHTS = ['/kaggle/input/lsdc-train-yolo-nfn/lsdc_yolov8/train/weights/best.pt',\n              '/kaggle/input/lsdc-yolo-nfnv3/best.pt']","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:10.700501Z","iopub.execute_input":"2024-10-01T14:11:10.700979Z","iopub.status.idle":"2024-10-01T14:11:10.706716Z","shell.execute_reply.started":"2024-10-01T14:11:10.700944Z","shell.execute_reply":"2024-10-01T14:11:10.705872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    import sys\n    sys.path.append('/kaggle/input/lsdc-utils')\n    from metrics import score as lsdc_scoring","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:10.708798Z","iopub.execute_input":"2024-10-01T14:11:10.709086Z","iopub.status.idle":"2024-10-01T14:11:10.718183Z","shell.execute_reply.started":"2024-10-01T14:11:10.709056Z","shell.execute_reply":"2024-10-01T14:11:10.717428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_val_df = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train.csv')","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:10.719109Z","iopub.execute_input":"2024-10-01T14:11:10.719381Z","iopub.status.idle":"2024-10-01T14:11:10.765006Z","shell.execute_reply.started":"2024-10-01T14:11:10.719351Z","shell.execute_reply":"2024-10-01T14:11:10.764189Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    train_xy = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_label_coordinates.csv')\n    des = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_series_descriptions.csv')\nelse:    \n    des = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_series_descriptions.csv')","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:10.766069Z","iopub.execute_input":"2024-10-01T14:11:10.766378Z","iopub.status.idle":"2024-10-01T14:11:10.775467Z","shell.execute_reply.started":"2024-10-01T14:11:10.766346Z","shell.execute_reply":"2024-10-01T14:11:10.774597Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def read_dcm(src_path):\n    dicom_data = pydicom.dcmread(src_path)\n    image = dicom_data.pixel_array\n    image = (image - image.min()) / (image.max() - image.min() +1e-6) * 255\n    return image\n\ndef convert_dcm_to_jpg(file_path):\n    try:\n        # Read the DICOM file\n        image_array = read_dcm(file_path)\n        \n        # Define the output path\n        relative_path = os.path.relpath(file_path, start=input_directory)\n        output_path = os.path.join(output_directory, relative_path)\n        output_path = output_path.replace('.dcm', '.jpg')\n                \n        # Create the output directory if it doesn't exist\n        os.makedirs(os.path.dirname(output_path), exist_ok=True)\n        \n        # Save the image as a JPEG file\n        cv2.imwrite(output_path, image_array)\n        \n        return output_path\n    except Exception as e:\n        print(f\"Error processing file {file_path}: {e}\")\n        return None\n\ndef process_files(dcm_files):\n    with Pool(cpu_count()) as pool:\n        # Wrap pool.map with tqdm to show the progress bar\n        list(tqdm(pool.imap(convert_dcm_to_jpg, dcm_files), total=len(dcm_files)))\n\ndef get_dcm_files(directory):\n    dcm_files = []\n    for root, dirs, files in os.walk(directory):\n        for file in files:\n            if file.endswith('.dcm'):\n                dcm_files.append(os.path.join(root, file))\n    return dcm_files    ","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:10.776599Z","iopub.execute_input":"2024-10-01T14:11:10.776959Z","iopub.status.idle":"2024-10-01T14:11:10.788224Z","shell.execute_reply.started":"2024-10-01T14:11:10.776911Z","shell.execute_reply":"2024-10-01T14:11:10.787215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Replace these with your input and output directories\nif not EVAL:\n    input_directory = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images'\n\n    output_directory = IMG_DIR\n\n    # Get all .dcm files in the input directory\n    dcm_files = get_dcm_files(input_directory)\n\n    # Process the files using multiprocessing\n    process_files(dcm_files)\n\n    print(f\"Conversion completed. Images saved to {output_directory}\")\nelse:\n    if not os.path.exists(IMG_DIR):\n        print('Unziping data..')\n        !unzip -q -d / /kaggle/input/lsdc-get-all-images/images.zip\n        print('Done unziping data')","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:10.789543Z","iopub.execute_input":"2024-10-01T14:11:10.790160Z","iopub.status.idle":"2024-10-01T14:11:11.790861Z","shell.execute_reply.started":"2024-10-01T14:11:10.790114Z","shell.execute_reply":"2024-10-01T14:11:11.789951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    fold_df = pd.read_csv('/kaggle/input/lsdc-fold-split/5folds.csv')\n    test_df = fold_df[fold_df.fold == FOLD]\n    \nelse:\n    test_df = os.listdir('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images')\n    test_df = pd.DataFrame(test_df, columns=['study_id'])\n    test_df['study_id'] = test_df['study_id'].astype(int)\n    \ntest_df = test_df.merge(des, on=['study_id'])","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:11.792100Z","iopub.execute_input":"2024-10-01T14:11:11.792411Z","iopub.status.idle":"2024-10-01T14:11:11.816520Z","shell.execute_reply.started":"2024-10-01T14:11:11.792380Z","shell.execute_reply":"2024-10-01T14:11:11.815720Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def gen_label_map(CONDITIONS):\n    label2id = {}\n    id2label = {}\n    i = 0\n    for cond in CONDITIONS:\n        for level in LEVELS:\n            for severity in SEVERITIES:\n                cls_ = f\"{cond.lower().replace(' ', '_')}_{level}_{severity.lower()}\"\n                label2id[cls_] = i\n                id2label[i] = cls_\n                i+=1\n    return label2id, id2label\n                \nscs_label2id, scs_id2label = gen_label_map(['Spinal Canal Stenosis'])\nss_label2id, ss_id2label = gen_label_map(['Left Subarticular Stenosis', 'Right Subarticular Stenosis'])\nnfn_label2id, nfn_id2label = gen_label_map(['Left Neural Foraminal Narrowing', 'Right Neural Foraminal Narrowing'])","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:11.820623Z","iopub.execute_input":"2024-10-01T14:11:11.820936Z","iopub.status.idle":"2024-10-01T14:11:11.827605Z","shell.execute_reply.started":"2024-10-01T14:11:11.820906Z","shell.execute_reply":"2024-10-01T14:11:11.826621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ultralytics import YOLO\n\n# Load YOLO Model\nscs_models = []\nfor weight in SCS_WEIGHTS:\n    scs_models.append(YOLO(weight))\n    \nss_models = []\nfor weight in SS_WEIGHTS:\n    ss_models.append(YOLO(weight))\n    \nnfn_models = []\nfor weight in NFN_WEIGHTS:\n    nfn_models.append(YOLO(weight))","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:11.828615Z","iopub.execute_input":"2024-10-01T14:11:11.828926Z","iopub.status.idle":"2024-10-01T14:11:14.474916Z","shell.execute_reply.started":"2024-10-01T14:11:11.828894Z","shell.execute_reply":"2024-10-01T14:11:14.473794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_label_set = train_val_df.iloc[0, 1:].index.tolist()\nscs_label_set = all_label_set[:5]\nnfn_label_set = all_label_set[5:15]\nss_label_set = all_label_set[15:]","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:14.476253Z","iopub.execute_input":"2024-10-01T14:11:14.476773Z","iopub.status.idle":"2024-10-01T14:11:14.482245Z","shell.execute_reply.started":"2024-10-01T14:11:14.476722Z","shell.execute_reply":"2024-10-01T14:11:14.481184Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"settings = [\n    ( 'Sagittal T2/STIR', scs_models, scs_id2label, scs_label_set, 0.01),\n    ( 'Axial T2', ss_models, ss_id2label, ss_label_set, 0.01),\n    ( 'Sagittal T1', nfn_models, nfn_id2label, nfn_label_set, 0.1)\n]","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:14.483437Z","iopub.execute_input":"2024-10-01T14:11:14.483736Z","iopub.status.idle":"2024-10-01T14:11:14.496541Z","shell.execute_reply.started":"2024-10-01T14:11:14.483704Z","shell.execute_reply":"2024-10-01T14:11:14.495601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import defaultdict","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:14.497968Z","iopub.execute_input":"2024-10-01T14:11:14.498681Z","iopub.status.idle":"2024-10-01T14:11:14.507482Z","shell.execute_reply.started":"2024-10-01T14:11:14.498639Z","shell.execute_reply":"2024-10-01T14:11:14.506807Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_rows = []\n\nfor modality, models, id2label, label_set, thresh in settings:\n    mod_df = test_df[test_df.series_description == modality]\n    \n    if SAMPLE:\n        mod_df = mod_df.sample(20, random_state=610)\n    \n    # for each study, at each level and condition, get the maximum probability score\n    for study_id, group in tqdm(mod_df.groupby('study_id')):\n        predictions = defaultdict(list)\n        for i, row in group.iterrows():\n            # predict on all images from all the series\n            series_dir = os.path.join(IMG_DIR, str(row['study_id']), str(row['series_id']))\n            for model in models:\n                results = model(series_dir, conf=thresh, verbose=False)\n                for res in results:\n                    for pred_class, conf in zip(res.boxes.cls, res.boxes.conf):\n                        pred_class = pred_class.item()\n                        conf = conf.item()\n                        _class = id2label[pred_class]\n                        predictions[_class].append(conf)\n        \n        # aggregate the result on images to obtain study-level prediction\n        for condition in label_set:\n            res_dict = {'row_id': f'{study_id}_{condition}' }\n\n            score_vec = []\n            for severity in SEVERITIES:\n                severity = severity.lower()\n                key = f'{condition}_{severity}'\n                if len(predictions[key]) > 0:\n                    score = np.max(predictions[key])\n                else:\n                    score = thresh\n                score_vec.append(score)\n                \n            # normalize score to sum to 1\n            score_vec = torch.tensor(score_vec)\n            score_vec = score_vec / score_vec.sum()\n\n            for idx, severity in enumerate(SEVERITIES):\n                res_dict[severity.replace('/', '_').lower()] = score_vec[idx].item()\n\n            pred_rows.append(res_dict)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:14.508737Z","iopub.execute_input":"2024-10-01T14:11:14.509054Z","iopub.status.idle":"2024-10-01T14:11:20.365931Z","shell.execute_reply.started":"2024-10-01T14:11:14.509023Z","shell.execute_reply":"2024-10-01T14:11:20.364715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_df = pd.DataFrame(pred_rows)\npred_df","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:20.367299Z","iopub.execute_input":"2024-10-01T14:11:20.367750Z","iopub.status.idle":"2024-10-01T14:11:20.389151Z","shell.execute_reply.started":"2024-10-01T14:11:20.367716Z","shell.execute_reply":"2024-10-01T14:11:20.388158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_df.to_csv('submission_1.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:20.390527Z","iopub.execute_input":"2024-10-01T14:11:20.390923Z","iopub.status.idle":"2024-10-01T14:11:20.402688Z","shell.execute_reply.started":"2024-10-01T14:11:20.390890Z","shell.execute_reply":"2024-10-01T14:11:20.401815Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def sample_weight(row):\n    if row['normal_mild'] == 1:\n        return 1\n    if row['moderate'] == 1:\n        return 2\n    if row['severe'] == 1:\n        return 4\n    raise ValueError('No such value')\n    \ndef get_class(row):\n    return np.argmax([row['normal_mild'], row['moderate'], row['severe']])","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:20.403997Z","iopub.execute_input":"2024-10-01T14:11:20.404702Z","iopub.status.idle":"2024-10-01T14:11:20.410680Z","shell.execute_reply.started":"2024-10-01T14:11:20.404658Z","shell.execute_reply":"2024-10-01T14:11:20.409730Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    gt_df = train_val_df.dropna().melt(id_vars=['study_id'], value_vars=all_label_set)\n    gt_df['row_id'] = gt_df['study_id'].astype(str) + '_' + gt_df['variable']\n    gt_df= gt_df[['row_id', 'value']]\n    gt_df = pd.get_dummies(gt_df, columns=['value'], dtype=int)\n    gt_df.columns = ['row_id', 'moderate', 'normal_mild', 'severe']\n    gt_df = gt_df[['row_id', 'normal_mild', 'moderate', 'severe']]\n    gt_df['sample_weight'] = gt_df.apply(sample_weight, axis=1)\n\n    gt_df1 = gt_df.merge(pred_df['row_id'], how='inner', on='row_id').sort_values('row_id').reset_index(drop=True)\n    pred_df1 = pred_df.merge(gt_df1['row_id'], how='inner', on='row_id').sort_values('row_id').reset_index(drop=True)\n    gt_df1['pred_cls'] = gt_df1.apply(get_class, axis=1)\n    pred_df1['pred_cls'] = pred_df1.apply(get_class, axis=1)\n\n    gt_df1[(gt_df1['pred_cls'] != pred_df1['pred_cls'])]\n    pred_df1[(gt_df1['pred_cls'] != pred_df1['pred_cls'])]\n    print('Label count:\\n', gt_df1['pred_cls'].value_counts(normalize=True))\n    print('Prediction accuracy:', (gt_df1['pred_cls'] == pred_df1['pred_cls']).mean())\n    print()\n\n    target_levels = ['normal_mild', 'moderate', 'severe']\n    loss = lsdc_scoring(gt_df1.drop(['pred_cls'], axis=1), pred_df1.drop(['pred_cls'], axis=1), row_id_column_name='row_id', any_severe_scalar=1)\n    print('Total weighted log loss:', loss)","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:20.411742Z","iopub.execute_input":"2024-10-01T14:11:20.412040Z","iopub.status.idle":"2024-10-01T14:11:20.427736Z","shell.execute_reply.started":"2024-10-01T14:11:20.412010Z","shell.execute_reply":"2024-10-01T14:11:20.426910Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%pip install --quiet /kaggle/input/timm_3d_deps/other/initial/9/pydicom/pydicom/pydicom-2.4.4-py3-none-any.whl\n%pip install timm_3d --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/timm_3d/\n%pip install torchio --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/torchio/\n%pip install itk --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/itk/itk\n%pip install skorch --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/skorch/skorch\n%pip install spacecutter --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/spacecutter/\n%pip install open3d --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/open3d","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:11:20.428946Z","iopub.execute_input":"2024-10-01T14:11:20.429550Z","iopub.status.idle":"2024-10-01T14:13:15.291026Z","shell.execute_reply.started":"2024-10-01T14:11:20.429508Z","shell.execute_reply":"2024-10-01T14:13:15.289829Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_path = \"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/\"","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:15.292663Z","iopub.execute_input":"2024-10-01T14:13:15.293037Z","iopub.status.idle":"2024-10-01T14:13:15.298187Z","shell.execute_reply.started":"2024-10-01T14:13:15.293002Z","shell.execute_reply":"2024-10-01T14:13:15.297141Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\n\ndef retrieve_test_data(data_path):\n    test_df = pd.read_csv(data_path + 'test_series_descriptions.csv')\n\n    return test_df\n\nretrieve_test_data(data_path)","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:15.299292Z","iopub.execute_input":"2024-10-01T14:13:15.299573Z","iopub.status.idle":"2024-10-01T14:13:15.321131Z","shell.execute_reply.started":"2024-10-01T14:13:15.299543Z","shell.execute_reply":"2024-10-01T14:13:15.320270Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\ndef retrieve_image_paths(base_path, study_id, series_id):\n    series_dir = os.path.join(base_path, str(study_id), str(series_id))\n    images = os.listdir(series_dir)\n    image_paths = [os.path.join(series_dir, img) for img in images]\n    return image_paths","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:15.322446Z","iopub.execute_input":"2024-10-01T14:13:15.322871Z","iopub.status.idle":"2024-10-01T14:13:15.328912Z","shell.execute_reply.started":"2024-10-01T14:13:15.322827Z","shell.execute_reply":"2024-10-01T14:13:15.327933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import open3d as o3d\nfrom pydicom import dcmread\nimport math\nimport numpy as np\nimport cv2\nimport copy\n\ndef read_study_as_pcd(dir_path, series_types_dict=None, downsampling_factor=1, img_size=(256, 256)):\n    pcd_overall = o3d.geometry.PointCloud()\n\n    for path in glob.glob(os.path.join(dir_path, \"**/*.dcm\"), recursive=True):\n        dicom_slice = dcmread(path)\n\n        series_id = os.path.basename(os.path.dirname(path))\n        study_id = os.path.basename(os.path.dirname(os.path.dirname(path)))\n        if series_types_dict is None or int(series_id) not in series_types_dict:\n            series_desc = dicom_slice.SeriesDescription\n        else:\n            series_desc = series_types_dict[int(series_id)]\n            series_desc = series_desc.split(\" \")[-1]\n\n        x_orig, y_orig = dicom_slice.pixel_array.shape\n        img = np.expand_dims(cv2.resize(dicom_slice.pixel_array, img_size, interpolation=cv2.INTER_AREA), -1)\n        x, y, z = np.where(img)\n\n        downsampling_factor_iter = max(downsampling_factor, int(math.ceil(len(x) / 6e6)))\n\n        index_voxel = np.vstack((x, y, z))[:, ::downsampling_factor_iter]\n        grid_index_array = index_voxel.T\n        pcd = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(grid_index_array.astype(np.float64)))\n\n        vals = np.expand_dims(img[x, y, z][::downsampling_factor_iter], -1)\n        if series_desc == \"T1\":\n            vals = np.pad(vals, ((0, 0), (0, 2)))\n        elif series_desc == \"T2\":\n            vals = np.pad(vals, ((0, 0), (1, 1)))\n        elif series_desc == \"T2/STIR\":\n            vals = np.pad(vals, ((0, 0), (2, 0)))\n        else:\n            raise ValueError(f\"Unknown series desc: {series_desc}\")\n\n        pcd.colors = o3d.utility.Vector3dVector(vals.astype(np.float64))\n\n        dX, dY = dicom_slice.PixelSpacing\n        dZ = dicom_slice.SliceThickness\n\n        X = np.array(list(dicom_slice.ImageOrientationPatient[:3]) + [0]) * dX\n        Y = np.array(list(dicom_slice.ImageOrientationPatient[3:]) + [0]) * dY\n\n        for z in range(int(dZ)):\n            pos = list(dicom_slice.ImagePositionPatient)\n            if series_desc == \"T2\":\n                pos[-1] += z\n            else:\n                pos[0] += z\n            S = np.array(pos + [1])\n\n            transform_matrix = np.array([X, Y, np.zeros(len(X)), S]).T\n            transform_matrix = transform_matrix @ np.matrix(\n                [[0, y_orig / img_size[1], 0, 0],\n                 [x_orig / img_size[0], 0, 0, 0],\n                 [0, 0, 1, 0],\n                 [0, 0, 0, 1]]\n            )\n\n            pcd_overall += copy.deepcopy(pcd).transform(transform_matrix)\n\n    return pcd_overall\n\n\n\ndef read_study_as_voxel_grid(dir_path, series_type_dict=None, downsampling_factor=1, img_size=(256, 256)):\n    pcd_overall = read_study_as_pcd(dir_path,\n                                    series_types_dict=series_type_dict,\n                                    downsampling_factor=downsampling_factor,\n                                    img_size=img_size)\n    box = pcd_overall.get_axis_aligned_bounding_box()\n\n    max_b = np.array(box.get_max_bound())\n    min_b = np.array(box.get_min_bound())\n\n    pts = (np.array(pcd_overall.points) - (min_b)) * (\n                (img_size[0] - 1, img_size[0] - 1, img_size[0] - 1) / (max_b - min_b))\n    coords = np.round(pts).astype(np.int32)\n    vals = np.array(pcd_overall.colors, dtype=np.float16)\n\n    grid = np.zeros((3, img_size[0], img_size[0], img_size[0]), dtype=np.float16)\n    indices = coords[:, 0], coords[:, 1], coords[:, 2]\n\n    np.maximum.at(grid[0], indices, vals[:, 0])\n    np.maximum.at(grid[1], indices, vals[:, 1])\n    np.maximum.at(grid[2], indices, vals[:, 2])\n\n\n    return grid","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:15.330118Z","iopub.execute_input":"2024-10-01T14:13:15.330472Z","iopub.status.idle":"2024-10-01T14:13:17.382831Z","shell.execute_reply.started":"2024-10-01T14:13:15.330441Z","shell.execute_reply":"2024-10-01T14:13:17.381804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfrom torch.utils.data import Dataset, DataLoader\nimport torchio as tio\nimport torch.nn as nn\nimport pydicom\n\nCONDITIONS = {\n    \"Sagittal T2/STIR\": [\"Spinal Canal Stenosis\"],\n    \"Axial T2\": [\"Left Subarticular Stenosis\", \"Right Subarticular Stenosis\"],\n    \"Sagittal T1\": [\"Left Neural Foraminal Narrowing\", \"Right Neural Foraminal Narrowing\"],\n}\n\n\nclass PatientLevelTestset(Dataset):\n    def __init__(self,\n                 base_path: str,\n                 dataframe: pd.DataFrame,\n                 transform_3d=None):\n        self.base_path = base_path\n\n        self.dataframe = (dataframe[['study_id', \"series_id\", \"series_description\"]]\n                          .drop_duplicates())\n\n        self.subjects = self.dataframe[['study_id']].drop_duplicates().reset_index(drop=True)\n        self.series_descs = {e[0]: e[1] for e in self.dataframe[[\"series_id\", \"series_description\"]].drop_duplicates().values}\n\n        self.transform_3d = transform_3d\n\n    def __len__(self):\n        return len(self.subjects)\n\n    def __getitem__(self, index):\n        curr = self.subjects.iloc[index]\n        study_path = os.path.join(self.base_path, str(curr[\"study_id\"]))\n\n        study_images = read_study_as_voxel_grid(study_path, self.series_descs)\n\n        if self.transform_3d is not None:\n            study_images = self.transform_3d(torch.FloatTensor(study_images))  # .data\n            return study_images.to(torch.half), str(curr[\"study_id\"])\n\n        return torch.HalfTensor(study_images), str(curr[\"study_id\"])","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:17.384131Z","iopub.execute_input":"2024-10-01T14:13:17.384656Z","iopub.status.idle":"2024-10-01T14:13:17.928598Z","shell.execute_reply.started":"2024-10-01T14:13:17.384621Z","shell.execute_reply":"2024-10-01T14:13:17.927809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transform_3d = tio.Compose([\n    tio.RescaleIntensity([0, 1]),\n])","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:17.929870Z","iopub.execute_input":"2024-10-01T14:13:17.930177Z","iopub.status.idle":"2024-10-01T14:13:17.934730Z","shell.execute_reply.started":"2024-10-01T14:13:17.930145Z","shell.execute_reply":"2024-10-01T14:13:17.933733Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_subject_level_testset_and_loader(df: pd.DataFrame,\n                                             transform_3d,\n                                             base_path: str,\n                                             batch_size=1,\n                                             num_workers=0):\n    testset = PatientLevelTestset(base_path, df, transform_3d=transform_3d)\n    test_loader = DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=num_workers)\n\n    return testset, test_loader","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:17.935943Z","iopub.execute_input":"2024-10-01T14:13:17.936253Z","iopub.status.idle":"2024-10-01T14:13:17.946852Z","shell.execute_reply.started":"2024-10-01T14:13:17.936219Z","shell.execute_reply":"2024-10-01T14:13:17.946021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\ndata = retrieve_test_data(data_path)\ndataset, dataloader = create_subject_level_testset_and_loader(data, transform_3d, os.path.join(data_path, \"test_images\"))","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:17.952839Z","iopub.execute_input":"2024-10-01T14:13:17.953127Z","iopub.status.idle":"2024-10-01T14:13:17.971734Z","shell.execute_reply.started":"2024-10-01T14:13:17.953084Z","shell.execute_reply":"2024-10-01T14:13:17.970796Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport glob \nimport torch\n\ngrid = dataset[0][0]\n\nfig, axs = plt.subplots(3, 3)\n\naxs[0, 0].imshow(grid[0, 128])\naxs[1, 0].imshow(grid[1, 128])\naxs[2, 0].imshow(grid[2, 128])\n\naxs[0, 1].imshow(grid[0, :, 128])\naxs[1, 1].imshow(grid[1, :, 128])\naxs[2, 1].imshow(grid[2, :, 128])\n\naxs[0, 2].imshow(grid[0, :, :, 128])\naxs[1, 2].imshow(grid[1, :, :, 128])\naxs[2, 2].imshow(grid[2, :, :, 128])\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:17.973046Z","iopub.execute_input":"2024-10-01T14:13:17.973395Z","iopub.status.idle":"2024-10-01T14:13:38.294931Z","shell.execute_reply.started":"2024-10-01T14:13:17.973361Z","shell.execute_reply":"2024-10-01T14:13:38.294020Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\ndevice = torch.device(\"cuda\") if torch.cuda.is_available() else \"cpu\"","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:38.296178Z","iopub.execute_input":"2024-10-01T14:13:38.296496Z","iopub.status.idle":"2024-10-01T14:13:38.300979Z","shell.execute_reply.started":"2024-10-01T14:13:38.296457Z","shell.execute_reply":"2024-10-01T14:13:38.300041Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch.nn as nn\nimport timm_3d\nfrom spacecutter import *\nfrom spacecutter.losses import *\nfrom spacecutter.models import *\nfrom spacecutter.callbacks import *\n\n\nclass CNN_Model_3D_Multihead(nn.Module):\n    def __init__(self,\n                 backbone=\"efficientnet_lite0\",\n                 in_chans=1,\n                 out_classes=5,\n                 cutpoint_margin=0.15,\n                 pretrained=False):\n        super(CNN_Model_3D_Multihead, self).__init__()\n        self.out_classes = out_classes\n\n        self.encoder = timm_3d.create_model(\n            backbone,\n            features_only=False,\n            drop_rate=0,\n            drop_path_rate=0,\n            pretrained=pretrained,\n            in_chans=in_chans,\n            global_pool=\"max\"\n        )\n        if \"efficientnet\" in backbone:\n            head_in_dim = self.encoder.classifier.in_features\n            self.encoder.classifier = nn.Sequential(\n                nn.LayerNorm(head_in_dim),\n                nn.Dropout(0),\n            )\n\n        elif \"vit\" in backbone:\n            self.encoder.head.drop = nn.Dropout(0)\n            head_in_dim = self.encoder.head.fc.in_features\n            self.encoder.head.fc = nn.Identity()\n\n        self.heads = nn.ModuleList(\n            [nn.Sequential(\n                nn.Linear(head_in_dim, 1),\n                LogisticCumulativeLink(3)\n            ) for i in range(out_classes)]\n        )\n\n        self.ascension_callback = AscensionCallback(margin=cutpoint_margin)\n\n    def forward(self, x):\n        feat = self.encoder(x)\n        return torch.swapaxes(torch.stack([head(feat) for head in self.heads]), 0, 1)\n\n    def _ascension_callback(self):\n        for head in self.heads:\n            self.ascension_callback.clip(head[-1])","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:38.301999Z","iopub.execute_input":"2024-10-01T14:13:38.302330Z","iopub.status.idle":"2024-10-01T14:13:39.824869Z","shell.execute_reply.started":"2024-10-01T14:13:38.302296Z","shell.execute_reply":"2024-10-01T14:13:39.824033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = CNN_Model_3D_Multihead(backbone=\"maxvit_rmlp_tiny_rw_256\", in_chans=3, out_classes=25).to(device)\nmodel.load_state_dict(torch.load(\"/kaggle/input/rsna-2024/pytorch/vit_voxel_v2/6/maxvit_rmlp_tiny_rw_256_256_v2_fold_3_32.pt\"))","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:39.826324Z","iopub.execute_input":"2024-10-01T14:13:39.826704Z","iopub.status.idle":"2024-10-01T14:13:41.671077Z","shell.execute_reply.started":"2024-10-01T14:13:39.826669Z","shell.execute_reply":"2024-10-01T14:13:41.670097Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"CONDITIONS = {\n    \"Sagittal T2/STIR\": [\"spinal_canal_stenosis\"],\n    \"Axial T2\": [\"left_subarticular_stenosis\", \"right_subarticular_stenosis\"],\n    \"Sagittal T1\": [\"left_neural_foraminal_narrowing\", \"right_neural_foraminal_narrowing\"],\n}\n\nALL_CONDITIONS = sorted([\"spinal_canal_stenosis\", \"left_subarticular_stenosis\", \"right_subarticular_stenosis\", \"left_neural_foraminal_narrowing\", \"right_neural_foraminal_narrowing\"])\nLEVELS = [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]\n\nresults_df = pd.DataFrame({\"row_id\":[], \"normal_mild\": [], \"moderate\": [], \"severe\": []})\n\nALL_CONDITIONS","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:41.672217Z","iopub.execute_input":"2024-10-01T14:13:41.672540Z","iopub.status.idle":"2024-10-01T14:13:41.681510Z","shell.execute_reply.started":"2024-10-01T14:13:41.672508Z","shell.execute_reply":"2024-10-01T14:13:41.680554Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\nimport os\n\nstudy_ids = glob.glob(\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/*\")\nstudy_ids = [os.path.basename(e) for e in study_ids]\n\nresults_df = pd.DataFrame({\"row_id\":[], \"normal_mild\": [], \"moderate\": [], \"severe\": []})\nfor study_id in study_ids:\n    for condition in ALL_CONDITIONS:\n        for level in LEVELS:\n            row_id = f\"{study_id}_{condition}_{level}\"\n            results_df = results_df._append({\"row_id\": row_id, \"normal_mild\": 1/3, \"moderate\": 1/3, \"severe\": 1/3}, ignore_index=True)","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:41.682991Z","iopub.execute_input":"2024-10-01T14:13:41.683382Z","iopub.status.idle":"2024-10-01T14:13:41.724763Z","shell.execute_reply.started":"2024-10-01T14:13:41.683337Z","shell.execute_reply":"2024-10-01T14:13:41.724049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset.dataframe","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:41.725786Z","iopub.execute_input":"2024-10-01T14:13:41.726123Z","iopub.status.idle":"2024-10-01T14:13:41.735034Z","shell.execute_reply.started":"2024-10-01T14:13:41.726082Z","shell.execute_reply":"2024-10-01T14:13:41.734161Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torch.cuda.amp import autocast\nimport time\n\nstart_time = time.time()\n\nwith torch.no_grad():\n    with autocast(dtype=torch.float16):\n        model.eval()\n\n        for images, study_id in dataloader:\n            output = model(images.to(device))\n            for i, batch_out in enumerate(output):\n                batch_out = output.cpu().numpy()[i]\n                for index, level in enumerate(batch_out):\n                    row_id = f\"{study_id[i]}_{ALL_CONDITIONS[index // 5]}_{LEVELS[index % 5]}\"\n                    results_df.loc[results_df.row_id == row_id,'normal_mild'] = level[0]\n                    results_df.loc[results_df.row_id == row_id,'moderate'] = level[1]\n                    results_df.loc[results_df.row_id == row_id,'severe'] = level[2]\n                \nprint(\"--- %s seconds ---\" % (time.time() - start_time))","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:13:41.736015Z","iopub.execute_input":"2024-10-01T14:13:41.736320Z","iopub.status.idle":"2024-10-01T14:14:01.233098Z","shell.execute_reply.started":"2024-10-01T14:13:41.736289Z","shell.execute_reply":"2024-10-01T14:14:01.232143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results_df","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:14:01.234408Z","iopub.execute_input":"2024-10-01T14:14:01.235239Z","iopub.status.idle":"2024-10-01T14:14:01.249862Z","shell.execute_reply.started":"2024-10-01T14:14:01.235194Z","shell.execute_reply":"2024-10-01T14:14:01.248784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results_df.to_csv('submission_2.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:14:01.251110Z","iopub.execute_input":"2024-10-01T14:14:01.251454Z","iopub.status.idle":"2024-10-01T14:14:01.264283Z","shell.execute_reply.started":"2024-10-01T14:14:01.251420Z","shell.execute_reply":"2024-10-01T14:14:01.263448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sm1 = pd.read_csv('submission_1.csv')\nsm2 = pd.read_csv('submission_2.csv')\ndisplay(sm1,sm2)\nsms = pd.merge(sm1,sm2, on=['row_id'])\nsms['normal_mild'] = sms['normal_mild_x'] *0.407 + 0.593* sms['normal_mild_y']\nsms['moderate']    = sms['moderate_x']    *0.407 + 0.593* sms['moderate_y']\nsms['severe']      = sms['severe_x']      *0.407 + 0.593* sms['severe_y']","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:14:01.265319Z","iopub.execute_input":"2024-10-01T14:14:01.265670Z","iopub.status.idle":"2024-10-01T14:14:01.308215Z","shell.execute_reply.started":"2024-10-01T14:14:01.265638Z","shell.execute_reply":"2024-10-01T14:14:01.307521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = sms[['row_id','normal_mild','moderate','severe']]\nsub.to_csv('submission.csv', index=False, float_format='%.7f')\ndisplay(sub)","metadata":{"execution":{"iopub.status.busy":"2024-10-01T14:14:01.309178Z","iopub.execute_input":"2024-10-01T14:14:01.309496Z","iopub.status.idle":"2024-10-01T14:14:01.326455Z","shell.execute_reply.started":"2024-10-01T14:14:01.309458Z","shell.execute_reply":"2024-10-01T14:14:01.325746Z"},"trusted":true},"execution_count":null,"outputs":[]}]}