{"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,"sourceType":"competition"},{"sourceId":10578301,"sourceType":"datasetVersion","datasetId":6546379}],"dockerImageVersionId":30839,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import PIL\nPIL.Image.open(\"/kaggle/input/rsna-2024-poster-overview/lumparspine-posteroverview.png\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T13:58:05.484469Z","iopub.execute_input":"2025-01-25T13:58:05.484856Z","iopub.status.idle":"2025-01-25T13:58:07.434724Z","shell.execute_reply.started":"2025-01-25T13:58:05.484816Z","shell.execute_reply":"2025-01-25T13:58:07.433112Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# I - Import lib","metadata":{}},{"cell_type":"code","source":"import warnings\nimport pydicom\nimport glob, os\nimport pandas as pd\nimport numpy as np\nimport cv2\nfrom tqdm import tqdm\nimport re\nimport matplotlib.patches as patches\nfrom multiprocessing import Pool\nimport matplotlib.pyplot as plt\nfrom matplotlib import animation, rc\nimport seaborn as sns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:34:51.493029Z","iopub.execute_input":"2025-01-25T12:34:51.493400Z","iopub.status.idle":"2025-01-25T12:34:54.923293Z","shell.execute_reply.started":"2025-01-25T12:34:51.493340Z","shell.execute_reply":"2025-01-25T12:34:54.921965Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# II - EDA","metadata":{}},{"cell_type":"code","source":"path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:34:54.925101Z","iopub.execute_input":"2025-01-25T12:34:54.925784Z","iopub.status.idle":"2025-01-25T12:34:54.931814Z","shell.execute_reply.started":"2025-01-25T12:34:54.925738Z","shell.execute_reply":"2025-01-25T12:34:54.930302Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1. train df","metadata":{}},{"cell_type":"code","source":"dfm = pd.read_csv(f'{path}/train.csv')\ndfm.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:06.354828Z","iopub.execute_input":"2025-01-25T12:35:06.357044Z","iopub.status.idle":"2025-01-25T12:35:06.456878Z","shell.execute_reply.started":"2025-01-25T12:35:06.356982Z","shell.execute_reply":"2025-01-25T12:35:06.455445Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"category_order = ['Normal/Mild', 'Moderate', 'Severe']\n\nfigure, axis = plt.subplots(1, 3, figsize=(20, 5)) \nfor idx, d in enumerate(['foraminal', 'subarticular', 'canal']):\n    diagnosis = list(filter(lambda x: x.find(d) > -1, dfm.columns))\n    dff = dfm[diagnosis]\n    with warnings.catch_warnings():\n        warnings.simplefilter(action='ignore', category=FutureWarning)\n        value_counts = dff.apply(pd.value_counts).fillna(0).T\n\n    value_counts = value_counts[category_order]\n   \n    value_counts.plot(kind='bar', stacked=True, ax=axis[idx])\n    axis[idx].set_title(f'{d} distribution')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:06.458745Z","iopub.execute_input":"2025-01-25T12:35:06.459176Z","iopub.status.idle":"2025-01-25T12:35:07.953044Z","shell.execute_reply.started":"2025-01-25T12:35:06.459144Z","shell.execute_reply":"2025-01-25T12:35:07.951254Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. train_label_coordinates df","metadata":{}},{"cell_type":"code","source":"dfc = pd.read_csv(f'{path}/train_label_coordinates.csv')\ndfc.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:07.955438Z","iopub.execute_input":"2025-01-25T12:35:07.955928Z","iopub.status.idle":"2025-01-25T12:35:08.094413Z","shell.execute_reply.started":"2025-01-25T12:35:07.955893Z","shell.execute_reply":"2025-01-25T12:35:08.093291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"folder_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/100206310/1012284084'\ndicom_files = [f for f in os.listdir(folder_path) if f.endswith('.dcm')]\n\nstudy_id = folder_path.split('/')[-2]\nstudy_label_coordinates = dfc[dfc['study_id'] == int(study_id)]\nfiltered_dicom_files = []\nfiltered_label_coordinates = []\n\nfor dicom_file in dicom_files:\n    instance_number = int(dicom_file.split('.')[0])\n    corresponding_coordinates = study_label_coordinates[study_label_coordinates['instance_number'] == instance_number]\n    if not corresponding_coordinates.empty:\n        filtered_dicom_files.append(dicom_file)\n        filtered_label_coordinates.append(corresponding_coordinates)\nfig, axs = plt.subplots(1, 4, figsize=(20, 5))\nsecond_row_index = 1\nsecond_row_images = filtered_dicom_files[second_row_index : second_row_index + 4]\nsecond_row_coordinates = filtered_label_coordinates[second_row_index : second_row_index + 4]\n\nfor i, (dicom_file, label_coordinates) in enumerate(zip(second_row_images, second_row_coordinates)):\n    if label_coordinates['condition'].values[0] == 'Spinal Canal Stenosis':\n        path_temp = folder_path.replace('1012284084', str(label_coordinates['series_id'].values[0]))\n        dicom_file_path = os.path.join(path_temp, dicom_file)\n    else:\n        dicom_file_path = os.path.join(folder_path, dicom_file)\n    dicom_data = pydicom.dcmread(dicom_file_path)\n    image = dicom_data.pixel_array   \n    axs[i].imshow(image, cmap='gray')\n    axs[i].set_title(f'DICOM Image - {dicom_file}')\n    axs[i].axis('off')   \n    for _, row in label_coordinates.iterrows():\n        axs[i].plot(row['x'], row['y'], 'ro', markersize=5) \n        \nplt.tight_layout()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:08.096238Z","iopub.execute_input":"2025-01-25T12:35:08.096980Z","iopub.status.idle":"2025-01-25T12:35:09.317283Z","shell.execute_reply.started":"2025-01-25T12:35:08.096620Z","shell.execute_reply":"2025-01-25T12:35:09.315397Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. train_series_descriptions df","metadata":{}},{"cell_type":"code","source":"dfd = pd.read_csv(f'{path}/train_series_descriptions.csv')\ndfd.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:09.319471Z","iopub.execute_input":"2025-01-25T12:35:09.320079Z","iopub.status.idle":"2025-01-25T12:35:09.362414Z","shell.execute_reply.started":"2025-01-25T12:35:09.320029Z","shell.execute_reply":"2025-01-25T12:35:09.360402Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"description_count = dfd['series_description'].value_counts().reset_index()\ndescription_count.columns = ['series_description', 'count']\n\nplt.figure(figsize=(8, 6))\nsns.barplot(x='count', y='series_description', data=description_count, palette='Set2')\nplt.title('Number of IDs by Series Description', fontsize=16)\nplt.xlabel('Number of IDs', fontsize=12)\nplt.ylabel('Series Description', fontsize=12)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:09.363900Z","iopub.execute_input":"2025-01-25T12:35:09.364310Z","iopub.status.idle":"2025-01-25T12:35:09.575637Z","shell.execute_reply.started":"2025-01-25T12:35:09.364280Z","shell.execute_reply":"2025-01-25T12:35:09.574281Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Combine","metadata":{}},{"cell_type":"code","source":"dfc_dfm = pd.merge(left=dfc, right=dfm, how='left', on='study_id').reset_index(drop=True)\nmerge_df = pd.merge(left=dfc_dfm, right=dfd, how='left', on=['study_id', 'series_id']).reset_index(drop=True)\nmerge_df.study_id = merge_df.study_id.astype('category')\nmerge_df.series_id = merge_df.series_id.astype('category')\nmerge_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:09.577185Z","iopub.execute_input":"2025-01-25T12:35:09.577604Z","iopub.status.idle":"2025-01-25T12:35:09.827483Z","shell.execute_reply.started":"2025-01-25T12:35:09.577543Z","shell.execute_reply":"2025-01-25T12:35:09.826021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rand_data = np.random.randint(0, 255, (100, 100))\nfig, axes = plt.subplots(1, 8, figsize=(15, 3))\n\naxes[0].imshow(rand_data, cmap ='viridis')\naxes[1].imshow(rand_data, cmap ='CMRmap')\naxes[2].imshow(rand_data, cmap ='binary')\naxes[3].imshow(rand_data, cmap ='plasma')\naxes[4].imshow(rand_data, cmap ='jet')\naxes[5].imshow(rand_data, cmap ='cividis')\naxes[6].imshow(rand_data, cmap ='inferno')\naxes[7].imshow(rand_data, cmap ='coolwarm')\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:09.830979Z","iopub.execute_input":"2025-01-25T12:35:09.831339Z","iopub.status.idle":"2025-01-25T12:35:11.063235Z","shell.execute_reply.started":"2025-01-25T12:35:09.831311Z","shell.execute_reply":"2025-01-25T12:35:11.062076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dfc['series_description'] = merge_df.series_description","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:11.064694Z","iopub.execute_input":"2025-01-25T12:35:11.065268Z","iopub.status.idle":"2025-01-25T12:35:11.072216Z","shell.execute_reply.started":"2025-01-25T12:35:11.065219Z","shell.execute_reply":"2025-01-25T12:35:11.070780Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def mrt(id, ser, inst):\n    lag=20\n    path2 = path+'/train_images/' + str(id) +'/' + str(ser)+'/' + str(inst) + '.dcm'\n\n    ds = pydicom.dcmread(path2)\n    fig, ax = plt.subplots(figsize=(16, 8))\n    from matplotlib.colors import LogNorm \n\n    ax.imshow(ds.pixel_array, cmap ='inferno')     # Display the image\n\n    # Create a legend\n    legend_elements = []\n\n    # Plot the coordinates for the current condition\n    ab = dfc[(dfc.study_id==id) & (dfc.instance_number==inst) & (dfc.series_id==ser)]\n\n    a = 25 * max(ds.pixel_array.shape)/640\n    for _, row in ab.iterrows():\n        x, y = row['x'], row['y']\n\n        rect2 = patches.Rectangle((x - a, y - a), 2*a, 2*a, linewidth=2, edgecolor='white', facecolor='none')\n        rect1 = patches.Rectangle((x - a, y - a), 2*a, 2*a, linewidth=2, facecolor='white', alpha = 0.25)\n\n        ax.add_patch(rect2)\n        ax.add_patch(rect1)\n\n        # Add the condition to the legend\n        legend_elements.append(patches.Patch(facecolor='none', edgecolor='r', ))\n\n    # Add title\n    title = f\"{ab.series_description.unique()}, Study: {id}, Series: {ser}, Instance: {inst}\"\n    ax.set_title(title, fontsize=20)\n\n    # Display additional columns:\n    for _, row in ab.iterrows():\n        text = f\"level {row['level']}, {row['condition']}\"\n        ax.text(row['x'] + lag, row['y']+np.random.randint(-15, 15), text, fontsize=10, color='white', verticalalignment='center_baseline')\n    \n    plt.show() \n    \ndef MR3d(id, ser):\n    \n    path_to_folder = path + \"/train_images/\"+str(id)+'/'+str(ser)\n    def load_dicom(path):\n        dicom = pydicom.read_file(path)\n        data = dicom.pixel_array\n        data = data - np.min(data)\n        if np.max(data) != 0:\n            data = data / np.max(data)\n        data = (data * 255).astype(np.uint8)\n        return data\n\n    rc('animation', html='jshtml')\n\n    def load_dicom(filename):\n        ds = pydicom.dcmread(filename)\n        return ds.pixel_array\n\n    def load_dicom_line(path):\n        t_paths = sorted(\n            glob.glob(os.path.join(path, \"*\")), \n            key=lambda x: int(os.path.splitext(os.path.basename(x))[0].split(\"-\")[-1]),\n        )\n        images = []\n        for filename in t_paths:\n            data = load_dicom(filename)\n            if data.max() == 0:\n                continue\n            images.append(data)\n        return images\n\n    def create_animation(ims):\n        fig = plt.figure(figsize=(6, 6))\n        plt.axis('off')\n        im = plt.imshow(ims[0], cmap=\"inferno\")\n        text = plt.text(0.05, 0.05, f'Slide {1}', transform=fig.transFigure, fontsize=16, color='darkblue')\n\n        def animate_func(i):\n            im.set_array(ims[i])\n            text.set_text(f'Slide {i+1}')  \n            return [im]\n        plt.title(f'id = {id}, series = {ser}')\n        \n        plt.close()  \n\n        return animation.FuncAnimation(fig, animate_func, frames=len(ims), interval=1000//10) #24\n\n    images = load_dicom_line(path_to_folder)\n    \n    return create_animation(images)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:11.073609Z","iopub.execute_input":"2025-01-25T12:35:11.073963Z","iopub.status.idle":"2025-01-25T12:35:11.097014Z","shell.execute_reply.started":"2025-01-25T12:35:11.073933Z","shell.execute_reply":"2025-01-25T12:35:11.095691Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"id = 4003253\nser= 702807833\ninst=8\n\nmrt(id, ser, inst)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:11.098347Z","iopub.execute_input":"2025-01-25T12:35:11.098743Z","iopub.status.idle":"2025-01-25T12:35:11.660158Z","shell.execute_reply.started":"2025-01-25T12:35:11.098707Z","shell.execute_reply":"2025-01-25T12:35:11.658864Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"id = 4003253\nser= 2448190387\ninst=11\n\nmrt(id, ser, inst)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:11.661255Z","iopub.execute_input":"2025-01-25T12:35:11.661642Z","iopub.status.idle":"2025-01-25T12:35:12.147483Z","shell.execute_reply.started":"2025-01-25T12:35:11.661602Z","shell.execute_reply":"2025-01-25T12:35:12.146172Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"id_ = 4003253\nser = 702807833\n\nMR3d(id_, ser)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:12.148760Z","iopub.execute_input":"2025-01-25T12:35:12.149137Z","iopub.status.idle":"2025-01-25T12:35:15.371969Z","shell.execute_reply.started":"2025-01-25T12:35:12.149108Z","shell.execute_reply":"2025-01-25T12:35:15.370678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"id_ = 100206310\nser = 1012284084\n\nMR3d(id_, ser)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-25T12:35:15.373194Z","iopub.execute_input":"2025-01-25T12:35:15.373705Z","iopub.status.idle":"2025-01-25T12:35:26.030410Z","shell.execute_reply.started":"2025-01-25T12:35:15.373654Z","shell.execute_reply":"2025-01-25T12:35:26.029254Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# References","metadata":{}},{"cell_type":"markdown","source":"\nhttps://www.kaggle.com/code/abhinavsuri/anatomy-image-visualization-overview-rsna-raids\n\nhttps://www.kaggle.com/code/allegich/lumbar-rsna-2024-visualizing-eda-sub#4.-MR-IMAGES\n\nhttps://www.kaggle.com/code/satyaprakashshukl/rsna-lumbar-spine-analysis","metadata":{}}]}