{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"},{"sourceId":189405102,"sourceType":"kernelVersion"}],"dockerImageVersionId":30747,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import os\n\nimport pandas as pd\nimport numpy as np\n\nimport pydicom\nimport cv2\nfrom math import floor\n\nimport torch\nimport torch.nn.functional as F\nimport pytorch_lightning as pl\nimport timm\n\nfrom joblib import Parallel, delayed\nimport pickle\n\nfrom torch.utils.data import Dataset, DataLoader","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-07-23T18:52:37.288367Z","iopub.execute_input":"2024-07-23T18:52:37.288714Z","iopub.status.idle":"2024-07-23T18:52:47.240271Z","shell.execute_reply.started":"2024-07-23T18:52:37.288687Z","shell.execute_reply":"2024-07-23T18:52:47.239509Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Config","metadata":{}},{"cell_type":"code","source":"!mkdir volumes","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:47.242206Z","iopub.execute_input":"2024-07-23T18:52:47.242509Z","iopub.status.idle":"2024-07-23T18:52:48.262785Z","shell.execute_reply.started":"2024-07-23T18:52:47.242484Z","shell.execute_reply":"2024-07-23T18:52:48.261518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Config():\n    \n    SEED = 454\n    set_label = 'test'\n    \n    n_slices_per_series = 10\n    image_size = (256, 256)\n    interpolation = cv2.INTER_CUBIC\n    \n    root = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification'\n    model_folder = '/kaggle/input/rsna-trial-1-training/'\n    volumes_folder = '/kaggle/working/volumes'\n    \n    MODEL_NAME = 'tf_efficientnet_b0.ns_jft_in1k'\n    IN_CHANNELS = 3 * n_slices_per_series\n    NUM_CLASSES = 75\n    NUM_LABELS = NUM_CLASSES // 3\n    GLOBAL_POOL = 'avg'\n    \n    BATCH_SIZE = 32","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.264438Z","iopub.execute_input":"2024-07-23T18:52:48.264836Z","iopub.status.idle":"2024-07-23T18:52:48.272390Z","shell.execute_reply.started":"2024-07-23T18:52:48.264801Z","shell.execute_reply":"2024-07-23T18:52:48.271357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Saving","metadata":{}},{"cell_type":"code","source":"test_path = os.path.join(Config.root, f'{Config.set_label}_images')\nseries_types = ['Sagittal T2/STIR', 'Sagittal T1', 'Axial T2']","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.275089Z","iopub.execute_input":"2024-07-23T18:52:48.275411Z","iopub.status.idle":"2024-07-23T18:52:48.283334Z","shell.execute_reply.started":"2024-07-23T18:52:48.275386Z","shell.execute_reply":"2024-07-23T18:52:48.282618Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_all_instances_df(series_df, image_path):\n    \n    files_df = []\n\n    for study_id in series_df.study_id.unique():\n\n        study_filter = series_df.study_id == study_id\n        series_ids = series_df.loc[study_filter, 'series_id']\n\n        for series_id in series_ids:\n\n            series_filter = series_df.series_id == series_id\n            series_description = series_df.loc[series_filter, 'series_description'].iloc[0]\n\n            series_folder = os.path.join(image_path, str(study_id), str(series_id))\n            files = os.listdir(series_folder)\n            files = [int(file[:-4]) for file in files]\n\n            for file in files:\n                files_df.append((study_id, series_id, file, series_description))\n\n    files_columns = ['study_id', 'series_id', 'instance', 'series_type']\n    files_df = pd.DataFrame(files_df, columns=files_columns)\n\n    files_df = files_df.sort_values(['study_id', 'series_id', 'instance'], ascending=True)\n    \n    return files_df","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.284622Z","iopub.execute_input":"2024-07-23T18:52:48.285051Z","iopub.status.idle":"2024-07-23T18:52:48.295780Z","shell.execute_reply.started":"2024-07-23T18:52:48.285001Z","shell.execute_reply":"2024-07-23T18:52:48.294900Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def shift_image_bits(img, dcm):\n    \n    if dcm.PixelRepresentation == 1:\n        bit_shift = dcm.BitsAllocated - dcm.BitsStored\n        dtype = img.dtype \n        img = (img << bit_shift).astype(dtype) >>  bit_shift\n        \n    return img","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.296958Z","iopub.execute_input":"2024-07-23T18:52:48.297271Z","iopub.status.idle":"2024-07-23T18:52:48.307045Z","shell.execute_reply.started":"2024-07-23T18:52:48.297247Z","shell.execute_reply":"2024-07-23T18:52:48.306198Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def apply_window(img, dcm):\n\n    center = int(dcm.WindowCenter)\n    width = int(dcm.WindowWidth)\n    low = center - width / 2\n    high = center + width / 2    \n    \n    # Some notebooks instead of clipping\n    # with window parameters use quantiles\n    img = np.clip(img, low, high)\n\n    return img","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.308208Z","iopub.execute_input":"2024-07-23T18:52:48.308464Z","iopub.status.idle":"2024-07-23T18:52:48.317317Z","shell.execute_reply.started":"2024-07-23T18:52:48.308429Z","shell.execute_reply":"2024-07-23T18:52:48.316477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def read_dcm_image(image_folder, study_id, series_id, instance):\n    \n    dcm_path = os.path.join(Config.root,\n                            image_folder, \n                            str(study_id),\n                            str(series_id),\n                            f'{instance}.dcm')\n    dcm = pydicom.dcmread(dcm_path)\n    \n    img = dcm.pixel_array\n    img = shift_image_bits(img, dcm)\n    img = apply_window(img, dcm)\n    \n    # Resizing performed before image normalization!\n    img = cv2.resize(img, \n                 Config.image_size, \n                 interpolation=Config.interpolation)\n    \n    # Maybe we should normalize not slice,\n    # but the whole series of slices\n    if img.max() != img.min():\n        img = (img - img.min()) / (img.max() - img.min())\n    else:\n        img = img - img\n\n    if dcm.PhotometricInterpretation == \"MONOCHROME1\":\n        img = 1 - img\n        \n    img = (img * 255).astype('uint8')\n    \n    return img","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.318461Z","iopub.execute_input":"2024-07-23T18:52:48.318762Z","iopub.status.idle":"2024-07-23T18:52:48.327267Z","shell.execute_reply.started":"2024-07-23T18:52:48.318739Z","shell.execute_reply":"2024-07-23T18:52:48.326336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_and_save_volume(study_id, image_folder):\n    \n    study_filter = files_df.study_id == study_id\n    \n    volume = []\n    \n    for series_type in series_types:\n        \n        type_filter = files_df.series_type == series_type\n        series_df = files_df[study_filter & type_filter]\n        \n        step = (series_df.shape[0]- 1) / (Config.n_slices_per_series - 1)\n        \n        for i in range(Config.n_slices_per_series):\n            \n            idx = floor(i * step)\n            img = read_dcm_image(image_folder, *series_df.iloc[idx, :3])\n            volume.append(img)\n    \n    volume = np.stack(volume)\n    np.save(f'volumes/{study_id}.npy', volume)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.328432Z","iopub.execute_input":"2024-07-23T18:52:48.328719Z","iopub.status.idle":"2024-07-23T18:52:48.338001Z","shell.execute_reply.started":"2024-07-23T18:52:48.328697Z","shell.execute_reply":"2024-07-23T18:52:48.337163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"series_df = pd.read_csv(os.path.join(Config.root, f'{Config.set_label}_series_descriptions.csv'))\nfiles_df = get_all_instances_df(series_df, test_path)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.340872Z","iopub.execute_input":"2024-07-23T18:52:48.341207Z","iopub.status.idle":"2024-07-23T18:52:48.401895Z","shell.execute_reply.started":"2024-07-23T18:52:48.341183Z","shell.execute_reply":"2024-07-23T18:52:48.401146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"enough_series_filter = (series_df.groupby('study_id').series_description.nunique() == 3)\nfiles_df = files_df[files_df.study_id.isin(enough_series_filter[enough_series_filter].index)]","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.402884Z","iopub.execute_input":"2024-07-23T18:52:48.403138Z","iopub.status.idle":"2024-07-23T18:52:48.412456Z","shell.execute_reply.started":"2024-07-23T18:52:48.403115Z","shell.execute_reply":"2024-07-23T18:52:48.411574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_studies = files_df.study_id.unique()\ntasks = [delayed(get_and_save_volume)(study_id, f'{Config.set_label}_images') for study_id in all_studies]\nParallel(n_jobs=-1, verbose=10)(tasks)\npass","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:48.413697Z","iopub.execute_input":"2024-07-23T18:52:48.414041Z","iopub.status.idle":"2024-07-23T18:52:50.552867Z","shell.execute_reply.started":"2024-07-23T18:52:48.413992Z","shell.execute_reply":"2024-07-23T18:52:50.551760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"volumes_files = os.listdir(Config.volumes_folder)\n\nvolumes_path = [os.path.join(Config.volumes_folder, file) for file in volumes_files]\npresent_studies = [int(file[:-4]) for file in volumes_files]\nvolumes_path = pd.Series(volumes_path, index=present_studies, name='path')","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:50.554497Z","iopub.execute_input":"2024-07-23T18:52:50.554829Z","iopub.status.idle":"2024-07-23T18:52:50.561605Z","shell.execute_reply.started":"2024-07-23T18:52:50.554801Z","shell.execute_reply":"2024-07-23T18:52:50.560602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model and Dataset","metadata":{}},{"cell_type":"code","source":"device = 'cuda' if torch.cuda.is_available() else 'cpu'\npl.seed_everything(Config.SEED)\nnp.random.seed(Config.SEED)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:52:50.563232Z","iopub.execute_input":"2024-07-23T18:52:50.563942Z","iopub.status.idle":"2024-07-23T18:52:50.600207Z","shell.execute_reply.started":"2024-07-23T18:52:50.563890Z","shell.execute_reply":"2024-07-23T18:52:50.599258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"state_dict = torch.load(os.path.join(Config.model_folder, 'best_model_0'))['model']\nstate_dict = {k[6:]:v for k, v,in state_dict.items()}\n\nmodel =  timm.create_model(\n    model_name = Config.MODEL_NAME,\n    pretrained = False,\n    features_only = False,\n    in_chans = Config.IN_CHANNELS,\n    num_classes = Config.NUM_CLASSES,\n    global_pool = Config.GLOBAL_POOL\n)\n\nmodel.load_state_dict(state_dict)\n\nmodel = model.to(device)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T18:56:00.304038Z","iopub.execute_input":"2024-07-23T18:56:00.304658Z","iopub.status.idle":"2024-07-23T18:56:00.650172Z","shell.execute_reply.started":"2024-07-23T18:56:00.304628Z","shell.execute_reply":"2024-07-23T18:56:00.649397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mean, std = pickle.load(open('/kaggle/input/rsna-trial-1-training/scaler_params_0.pkl', 'rb'))\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"execution":{"iopub.status.busy":"2024-07-23T06:56:34.182636Z","iopub.execute_input":"2024-07-23T06:56:34.183510Z","iopub.status.idle":"2024-07-23T06:56:34.193363Z","shell.execute_reply.started":"2024-07-23T06:56:34.183470Z","shell.execute_reply":"2024-07-23T06:56:34.192135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNADataset(Dataset):\n    \n    def __init__(self, volumes_path, mean, std):\n        \n        self.path = volumes_path\n        \n        self.mean = mean\n        self.std = std\n        \n    def __len__(self):\n        \n        return self.path.shape[0]\n    \n    def __getitem__(self, idx):\n        \n        volume = np.load(self.path.iloc[idx])\n        volume = volume.astype('float32')\n        volume = (volume - self.mean) / self.std\n        \n        return self.path.index[idx], volume","metadata":{"execution":{"iopub.status.busy":"2024-07-23T06:56:35.229045Z","iopub.execute_input":"2024-07-23T06:56:35.229840Z","iopub.status.idle":"2024-07-23T06:56:35.237235Z","shell.execute_reply.started":"2024-07-23T06:56:35.229803Z","shell.execute_reply":"2024-07-23T06:56:35.236055Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = RSNADataset(volumes_path, mean, std)\ndataloader = DataLoader(dataset, batch_size=Config.BATCH_SIZE, shuffle=False, drop_last=False)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T06:56:35.898709Z","iopub.execute_input":"2024-07-23T06:56:35.899525Z","iopub.status.idle":"2024-07-23T06:56:35.906393Z","shell.execute_reply.started":"2024-07-23T06:56:35.899487Z","shell.execute_reply":"2024-07-23T06:56:35.905133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_df = pd.read_csv(os.path.join(Config.root, 'train.csv'), index_col=0)\nlabel_names = list(label_df.columns)\nlabel_names[:5]","metadata":{"execution":{"iopub.status.busy":"2024-07-23T06:56:37.197879Z","iopub.execute_input":"2024-07-23T06:56:37.198321Z","iopub.status.idle":"2024-07-23T06:56:37.246159Z","shell.execute_reply.started":"2024-07-23T06:56:37.198288Z","shell.execute_reply":"2024-07-23T06:56:37.245128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"levels = ['normal_mild', 'moderate', 'severe']","metadata":{"execution":{"iopub.status.busy":"2024-07-23T06:56:37.948848Z","iopub.execute_input":"2024-07-23T06:56:37.949811Z","iopub.status.idle":"2024-07-23T06:56:37.955328Z","shell.execute_reply.started":"2024-07-23T06:56:37.949769Z","shell.execute_reply":"2024-07-23T06:56:37.953997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_submission(study_ids, labels, pred):\n    \n    study_col = pd.Series(study_ids.repeat(25))\n    label_col = pd.Series(labels * study_ids.shape[0])\n    row_id = study_col.astype('str') + '_' + label_col\n    row_id = row_id.rename('row_id')\n\n    y_pred = pd.DataFrame(pred, columns=levels)\n    \n    return pd.concat([row_id, y_pred], axis=1)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T06:56:38.872298Z","iopub.execute_input":"2024-07-23T06:56:38.873302Z","iopub.status.idle":"2024-07-23T06:56:38.881171Z","shell.execute_reply.started":"2024-07-23T06:56:38.873258Z","shell.execute_reply":"2024-07-23T06:56:38.878609Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm","metadata":{"execution":{"iopub.status.busy":"2024-07-23T07:10:52.866137Z","iopub.execute_input":"2024-07-23T07:10:52.866586Z","iopub.status.idle":"2024-07-23T07:10:52.871855Z","shell.execute_reply.started":"2024-07-23T07:10:52.866553Z","shell.execute_reply":"2024-07-23T07:10:52.870444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submissions = []\n\nmodel.eval()\nwith torch.no_grad():\n    \n    for study_ids, X in tqdm(dataloader):\n\n        X = X.to(device)\n\n        y_pred = model(X)\n\n        for i in range(25):\n            y_pred[:, i*3:(i+1)*3] = F.softmax(y_pred[:, i*3:(i+1)*3], dim=1)\n        \n        y_pred = y_pred.reshape((-1, 3))\n            \n        submissions.append(get_submission(study_ids, label_names, y_pred.detach().cpu()))","metadata":{"execution":{"iopub.status.busy":"2024-07-23T07:11:34.707686Z","iopub.execute_input":"2024-07-23T07:11:34.708097Z","iopub.status.idle":"2024-07-23T07:11:42.325417Z","shell.execute_reply.started":"2024-07-23T07:11:34.708064Z","shell.execute_reply":"2024-07-23T07:11:42.323800Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Not enough slices patients","metadata":{}},{"cell_type":"code","source":"study_ids = list(enough_series_filter[~enough_series_filter].index)\nstudy_ids = np.array(study_ids)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T07:11:58.070448Z","iopub.execute_input":"2024-07-23T07:11:58.071703Z","iopub.status.idle":"2024-07-23T07:11:58.077869Z","shell.execute_reply.started":"2024-07-23T07:11:58.071661Z","shell.execute_reply":"2024-07-23T07:11:58.076441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if len(study_ids) != 0:\n    mean_predict = label_df.apply(lambda col: col.value_counts(normalize=True))\n    mean_predict = mean_predict.values.T\n    mean_predict = np.tile(mean_predict, (len(study_ids), 1))\n    \n    submissions.append(get_submission(study_ids, label_names, mean_predict))","metadata":{"execution":{"iopub.status.busy":"2024-07-23T07:11:58.221814Z","iopub.execute_input":"2024-07-23T07:11:58.222240Z","iopub.status.idle":"2024-07-23T07:11:58.258367Z","shell.execute_reply.started":"2024-07-23T07:11:58.222191Z","shell.execute_reply":"2024-07-23T07:11:58.256839Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pd.concat(submissions, axis=0)\nsubmission.shape[0] // 25","metadata":{"execution":{"iopub.status.busy":"2024-07-23T07:12:06.032585Z","iopub.execute_input":"2024-07-23T07:12:06.033010Z","iopub.status.idle":"2024-07-23T07:12:06.041406Z","shell.execute_reply.started":"2024-07-23T07:12:06.032977Z","shell.execute_reply":"2024-07-23T07:12:06.040310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-07-23T07:12:12.414068Z","iopub.execute_input":"2024-07-23T07:12:12.414513Z","iopub.status.idle":"2024-07-23T07:12:12.433486Z","shell.execute_reply.started":"2024-07-23T07:12:12.414478Z","shell.execute_reply":"2024-07-23T07:12:12.432507Z"},"trusted":true},"execution_count":null,"outputs":[]}]}