{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"},{"sourceId":9311546,"sourceType":"datasetVersion","datasetId":5392962}],"dockerImageVersionId":30762,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# RSNA 2024 Lumbar Spine Degenerative Classification","metadata":{}},{"cell_type":"markdown","source":"# 1. Introduction\n\nTo have a general overview of medical conditions you can consult this notebook:\n\nhttps://www.kaggle.com/code/abhinavsuri/anatomy-image-visualization-overview-rsna-raidshttps://www.kaggle.com/code/abhinavsuri/anatomy-image-visualization-overview-rsna-raids\n \nThe following proposal consists of training a model by images to be able to estimate the probability of *pain* at each intervertebral *level* and for each of the following *conditions*: \n\nspinal_canal_stenosis_l1_l2                 \nspinal_canal_stenosis_l2_l3                 \nspinal_canal_stenosis_l3_l4                 \nspinal_canal_stenosis_l4_l5                 \nspinal_canal_stenosis_l5_s1                 \nleft_neural_foraminal_narrowing_l1_l2       \nleft_neural_foraminal_narrowing_l2_l3       \nleft_neural_foraminal_narrowing_l3_l4       \nleft_neural_foraminal_narrowing_l4_l5       \nleft_neural_foraminal_narrowing_l5_s1       \nright_neural_foraminal_narrowing_l1_l2      \nright_neural_foraminal_narrowing_l2_l3      \nright_neural_foraminal_narrowing_l3_l4      \nright_neural_foraminal_narrowing_l4_l5      \nright_neural_foraminal_narrowing_l5_s1      \nleft_subarticular_stenosis_l1_l2          \nleft_subarticular_stenosis_l2_l3          \nleft_subarticular_stenosis_l3_l4          \nleft_subarticular_stenosis_l4_l5          \nleft_subarticular_stenosis_l5_s1           \nright_subarticular_stenosis_l1_l2         \nright_subarticular_stenosis_l2_l3          \nright_subarticular_stenosis_l3_l4           \nright_subarticular_stenosis_l4_l5           \nright_subarticular_stenosis_l5_s1           \n       \nThe general strategy is to group all intervertebral levels by medical condition and train five individual Neural Models (NM)\n\n$\\mathcal{M}_1$: spinal_canal_stenosis\n\n$\\mathcal{M}_2$: left_neural_foraminal_narrowing\n\n$\\mathcal{M}_3$: right_neural_foraminal_narrowing\n\n$\\mathcal{M}_4$: left_subarticular_stenosis\n\n$\\mathcal{M}_5$: right_subarticular_stenosis\n\nThe first step is to explore the databases and image files. We have to perform several tasks such as identifying the type of data, verifying the order and consistency of the records, identifying if there is incorrect, missing or mislabeled data.\n\nThe next step was to debug and clean the database. In the process, we recognized the correlations between the data that have allowed us to define a structure to address the objective of this project, which consists of training an image machine learning model to estimate the level of pain for each medical condition and for each level intervertebral.   \n\n","metadata":{}},{"cell_type":"markdown","source":"# 2. $\\mathcal{M}_1$: spinal_canal_stenosis","metadata":{}},{"cell_type":"markdown","source":"The first step is to explore the set of data referring to this first condition. As we will see below, there are missing and mislabeled data as well as misclassified images. \n\nIn particular, we need coordinates of some intervertebral levels for which we have a diagnosis and the series of images for them. The need then arises to train a model that can help us estimate these coordinates so that we can integrate these into the training of the submodels. Since we have a small amount of training data we think it is important not to ignore them. To train the model we have derived a database with images from the **Sagittal T2/STIR** series that have the coordinates of all intervertebral levels of interest and we have also eliminated images that have labeling errors. This model will later allow us to label the coordinates that we need and correct those that are mislabeled and finally will allow us to estimate the coordinates of our prediction images. This is particularly useful because we train a neural model ($\\mathcal{M}_{scs}$) to predict the condition **Spinal Canal Stenosis** that takes as a basis the coordinates of the intervertebral levels to segment the images. \n\nWe developed four models to detect the coordinates of the intervertebral levels using: ResNet18, ViT, ResNet50 and YOLO. By comparing the results we obtain that ResNet50 has the best results. In general, we will use ResNet50 for the remaining models.\n \nOnce we have the model to predict the coordinates of the intervertebral levels, we design the $\\mathcal{M}_{scs}$ model to be able to predict the pain of all intervertebral levels [Normal/Mild, Moderate, Severe]. To develop this neural model we use two types of models: a local model $(\\mathcal{M}_{scs}^L)$ that predicts the pain of each intervertebral level and that is trained with the images segmented in a neighborhood of the coordinates; and a general model $(\\mathcal{M}_{scs}^G)$ that is trained with the segmented images coming from all the local models and that predicts pain with respect to all levels and that is based on the assumption that pain at all intervertebral levels is related in some way or another. In order to segment the images for training, prediction and imputation of missing or mislabeled coordinates we designed a model $(\\mathcal{M}_{scs}^V)$ to predict the coordinates of all intervertebral levels.\n\nIn conclusion, the first model $\\mathcal{M}_{scs}$ takes an image of the *Sagittal T2/STIR* series and predicts the coordinates $(\\mathcal{M}_{scs}^V)$ of the five levels intervertebral, then segments the images and makes five local predictions $(\\mathcal{M}_{scs}^L)$ then makes another 5 predictions with the general model $(\\mathcal{M}_{scs}^G)$ with which it carries out a weighting to estimate the pain of each intervertebral level of the spinal canal stenosis condition.\n\nIn symbolic terms, given an image $\\bf{z}$ of the Sagittal T2/STIR series we predict the coordinates of the 5 intervertebral levels $\\mathcal{M}_{scs}^V(\\bf{z}) = \\{z_1,z_2,z_3,z_4, z_5\\}$. Then we form the prediction matrix that has the form\n\n$$\n\\mathcal{M}_{scs}(\\bf{z}) =  \\begin{pmatrix}\n\\mathcal{M}_{scs}^L(z_1) & \\mathcal{M}_{scs}^G(z_1)\\\\\n\\mathcal{M}_{scs}^L(z_2) & \\mathcal{M}_{scs}^G(z_2)\\\\\n\\mathcal{M}_{scs}^L(z_3) & \\mathcal{M}_{scs}^G(z_3)\\\\\n\\mathcal{M}_{scs}^L(z_4) & \\mathcal{M}_{scs}^G(z_4)\\\\\n\\mathcal{M}_{scs}^L(z_5) & \\mathcal{M}_{scs}^G(z_5)\\\\\n\\end{pmatrix} \n\\begin{pmatrix}\na \\\\\nb\n\\end{pmatrix} =\n\\begin{pmatrix}\na\\cdot\\mathcal{M}_{scs}^L(z_1) + b\\cdot\\mathcal{M}_{scs}^G(z_1)\\\\\na\\cdot\\mathcal{M}_{scs}^L(z_2) + b\\cdot\\mathcal{M}_{scs}^G(z_2)\\\\\na\\cdot\\mathcal{M}_{scs}^L(z_3) + b\\cdot\\mathcal{M}_{scs}^G(z_3)\\\\\na\\cdot\\mathcal{M}_{scs}^L(z_4) + b\\cdot\\mathcal{M}_{scs}^G(z_4)\\\\\na\\cdot\\mathcal{M}_{scs}^L(z_5) + b\\cdot\\mathcal{M}_{scs}^G(z_5)\\\\\n\\end{pmatrix}\n$$\n\nIn this case, $a$ and $b$ must be optimized, for $a,b \\in \\mathbb{R}$. \n","metadata":{}},{"cell_type":"markdown","source":"## 2.1 Afirmations\n\nThroughout the development of this notebook we will demonstrate the following statements:\n\n#### 🟡 **Afirmation 1**:\nData is missing on the *train*\n\n#### 🟡 **Afirmation 2**:\n*'study_id = '3008676218'* is present in *train* and *train_series_descriptions* but not in *train_label_coordinates*. Therefore, we eliminates this entrie in train and *train_label_coordinates*.\n\n#### 🟡 **Afirmation 3**:\n*'Sagittal T2/STIR'* is used to diagnose all levels the condition *'Spinal Canal Stenosis'* except in case *'study_id = '3637444890'*. Therefore, we can exclude the *'study_id='3637444890'* in the case of *'Spinal Canal Stenosis'* and use *train_merge_T2* for our analysis.\n\n#### 🟡 **Afirmation 4**:\nGenerally there are three *'series_id'* which implies that there are three subdirectories. However, there are *'study_id'* that have more than three directories associated with them.\n\n#### 🟡 **Afirmation 5**:\nWhen we look at the *'level'* in *train_label_descriptions* for *Spinal Canal Stenosis* we have *'study_id'* cases that exist in *train* and *train_series_descriptions* but not in *train_label_coordinates*. In other words, there is the *pain level* (*'Normal/Mild', 'Moderate', 'Severe'*) in *'train'* in relation to *'Spinal Canal Stenosis'* and we have the * 'series_descriptions'* (*' Sagittal T2/STIR'*) but there are no records in *'train_label_coordinates'*. For example: a) there are 70 cases in *'spinal_canal_stenosis_l1_l2'*, b) 30 cases in *'spinal_canal_stenosis_l2_l3'*, and 2, 3 and 5 in the other cases.\n\n#### 🟡 **Afirmation 6**:\nExists cases where the coordinates are bad labeled.\n\n#### 🟡 **Afirmation 7**:\nWith 20 epochs of ResNet50 model predicts much better than ResNet18, ViT and ResNet18.\n\n#### 🟡 **Afirmation 8**:\nThe case *study_id = 3637444890* has an incorrect label with respect to *series_id = 3892989905*.\n\nThe case *study_id = 2905025904* and *series_id = 816381378* is mislabeled with respect to coordinates.\n\nThe case *study_id = 1438760543* and *series_id = 737753815* is mislabeled with respect to coordinates.\n\n#### 🟡 **Afirmation 9**:\n \nThe databases *df_scs_l1_l2, df_scs_l2_l3,df_scs_l3_l4,df_scs_l4_l5,df_scs_l5_s1 * that combine the referring data by intervertebral level are purified and imputed.\n\n#### 🟡 **Afirmation 10**:\nEl modelo ResNet_scs_\n\n","metadata":{}},{"cell_type":"markdown","source":"## 2.2 Import libraries and datasets","metadata":{}},{"cell_type":"markdown","source":"In general, we need the following libraries. However, in each model the necessary libraries are imported again to avoid compilation errors due to unexecuted cells.","metadata":{}},{"cell_type":"code","source":"#!pip install transformers torch torchvision torchaudio torchtext accelerate -U\nimport os\nimport cv2\nimport shutil\nimport pandas as pd\nimport numpy as np\nimport pydicom\nimport torch\nimport torch.nn as nn\nimport matplotlib.pyplot as plt\n\nfrom tqdm import tqdm\nfrom PIL import Image, ImageOps\nfrom ipywidgets import interact, IntSlider\nfrom tqdm import tqdm\nfrom torch.utils.data import DataLoader, WeightedRandomSampler, Subset, Dataset\nfrom torchvision import transforms\nfrom transformers import ViTConfig, ViTForImageClassification\nfrom sklearn.model_selection import StratifiedKFold, KFold\nfrom sklearn.preprocessing import LabelEncoder","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-09-27T20:03:34.700473Z","iopub.execute_input":"2024-09-27T20:03:34.700891Z","iopub.status.idle":"2024-09-27T20:03:42.719369Z","shell.execute_reply.started":"2024-09-27T20:03:34.70084Z","shell.execute_reply":"2024-09-27T20:03:42.718573Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train.csv')\ntrain_label_coordinates = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_label_coordinates.csv')\ntrain_series_descriptions = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_series_descriptions.csv')","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.721002Z","iopub.execute_input":"2024-09-27T20:03:42.721445Z","iopub.status.idle":"2024-09-27T20:03:42.876066Z","shell.execute_reply.started":"2024-09-27T20:03:42.721404Z","shell.execute_reply":"2024-09-27T20:03:42.875215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.3 Explore the datasets","metadata":{}},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 1**: \nData is missing on the *train*\n\n**Proof:**","metadata":{}},{"cell_type":"code","source":"train.isna().sum()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.877278Z","iopub.execute_input":"2024-09-27T20:03:42.877572Z","iopub.status.idle":"2024-09-27T20:03:42.898728Z","shell.execute_reply.started":"2024-09-27T20:03:42.87754Z","shell.execute_reply":"2024-09-27T20:03:42.897911Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*train_label_coordinates* and *train_series_descriptions* don´t have missing data","metadata":{}},{"cell_type":"code","source":"train_label_coordinates.isna().sum()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.900851Z","iopub.execute_input":"2024-09-27T20:03:42.901157Z","iopub.status.idle":"2024-09-27T20:03:42.917742Z","shell.execute_reply.started":"2024-09-27T20:03:42.901123Z","shell.execute_reply":"2024-09-27T20:03:42.916804Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_series_descriptions.isna().sum()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.918856Z","iopub.execute_input":"2024-09-27T20:03:42.919144Z","iopub.status.idle":"2024-09-27T20:03:42.92777Z","shell.execute_reply.started":"2024-09-27T20:03:42.919103Z","shell.execute_reply":"2024-09-27T20:03:42.926718Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This demostrate **Afirmation 1** 🟨","metadata":{}},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 2**: \n*'study_id = '3008676218'* is present in *train* and *train_series_descriptions* but not in *train_label_coordinates*.\n\n**Proof:**","metadata":{}},{"cell_type":"markdown","source":"Now we compare the datasets if have the same entries with respect to the column *study_id*, ","metadata":{}},{"cell_type":"code","source":"def compare_column(dataframe1, dataframe2, column_name):\n\n    column1 = dataframe1[column_name]\n    column2 = dataframe2[column_name]\n\n    set1 = set(column1)\n    set2 = set(column2)\n\n    unique_to_dataframe1 = set1 - set2\n    unique_to_dataframe2 = set2 - set1\n\n    return unique_to_dataframe1, unique_to_dataframe2","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.929107Z","iopub.execute_input":"2024-09-27T20:03:42.929401Z","iopub.status.idle":"2024-09-27T20:03:42.938295Z","shell.execute_reply.started":"2024-09-27T20:03:42.929351Z","shell.execute_reply":"2024-09-27T20:03:42.937365Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(compare_column(train,train_label_coordinates,'study_id'))\nprint(compare_column(train_label_coordinates,train_series_descriptions,'study_id'))\nprint(compare_column(train,train_series_descriptions,'study_id'))","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.939428Z","iopub.execute_input":"2024-09-27T20:03:42.939777Z","iopub.status.idle":"2024-09-27T20:03:42.967051Z","shell.execute_reply.started":"2024-09-27T20:03:42.939743Z","shell.execute_reply":"2024-09-27T20:03:42.966189Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(train[train.study_id == 3008676218].T)\nprint(train_series_descriptions[train_series_descriptions.study_id == 3008676218])","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.968273Z","iopub.execute_input":"2024-09-27T20:03:42.969091Z","iopub.status.idle":"2024-09-27T20:03:42.990333Z","shell.execute_reply.started":"2024-09-27T20:03:42.969048Z","shell.execute_reply":"2024-09-27T20:03:42.989383Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This implies that there are no images to diagnose the condition 'Spinal Canal Stenosis'. Therefore, we eliminates this entrie in *train* and *train_label_coordinates*","metadata":{}},{"cell_type":"code","source":"train.drop(1378,inplace=True)\ntrain_series_descriptions.drop(4398, inplace = True)\ntrain_series_descriptions.drop(4397, inplace = True)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:42.991449Z","iopub.execute_input":"2024-09-27T20:03:42.991757Z","iopub.status.idle":"2024-09-27T20:03:43.002307Z","shell.execute_reply.started":"2024-09-27T20:03:42.991726Z","shell.execute_reply":"2024-09-27T20:03:43.001425Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This demostrate **Afirmation 2** 🟨","metadata":{}},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 3**: \n*'Sagittal T2/STIR'* is used to diagnose all levels the condition *'Spinal Canal Stenosis'* except in case *'study_id = '3637444890'*\n\n**Proof:**","metadata":{}},{"cell_type":"markdown","source":"Now combine the datasets *train_label_coordinates* and *train_series_descriptions* to show there is an unique case where the condition 'Spinal Canal Stenosis' use other 'series_descriptions' diferent to *'Sagittal T2/STIR'* ","metadata":{}},{"cell_type":"code","source":"train_merge = pd.merge(left=train_label_coordinates,right=train_series_descriptions,how='left',on=['study_id','series_id']).reset_index(drop = True)\ntrain_merge.head()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.00692Z","iopub.execute_input":"2024-09-27T20:03:43.007546Z","iopub.status.idle":"2024-09-27T20:03:43.049468Z","shell.execute_reply.started":"2024-09-27T20:03:43.007512Z","shell.execute_reply":"2024-09-27T20:03:43.048585Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_merge_T2 = train_merge[train_merge.series_description == 'Sagittal T2/STIR']\ntrain_merge_T2.head()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.050409Z","iopub.execute_input":"2024-09-27T20:03:43.050719Z","iopub.status.idle":"2024-09-27T20:03:43.073143Z","shell.execute_reply.started":"2024-09-27T20:03:43.050678Z","shell.execute_reply":"2024-09-27T20:03:43.072314Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_merge_scs = train_merge[train_merge.condition == 'Spinal Canal Stenosis']\ntrain_merge_scs.head()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.074295Z","iopub.execute_input":"2024-09-27T20:03:43.074694Z","iopub.status.idle":"2024-09-27T20:03:43.097502Z","shell.execute_reply.started":"2024-09-27T20:03:43.074634Z","shell.execute_reply":"2024-09-27T20:03:43.096576Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"compare_column(train_merge_scs,train_merge_T2,'study_id')","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.098578Z","iopub.execute_input":"2024-09-27T20:03:43.098905Z","iopub.status.idle":"2024-09-27T20:03:43.108756Z","shell.execute_reply.started":"2024-09-27T20:03:43.098872Z","shell.execute_reply":"2024-09-27T20:03:43.107747Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_merge_scs[train_merge_scs.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.10986Z","iopub.execute_input":"2024-09-27T20:03:43.11014Z","iopub.status.idle":"2024-09-27T20:03:43.126432Z","shell.execute_reply.started":"2024-09-27T20:03:43.11011Z","shell.execute_reply":"2024-09-27T20:03:43.125449Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def visualize_dicom_image(file_path):\n    dicom_data = pydicom.dcmread(file_path)\n    pixel_array = dicom_data.pixel_array\n\n    plt.imshow(pixel_array, cmap=plt.cm.bone)\n    plt.title('DICOM Image')\n    plt.axis('on')\n    plt.show()\n\ndicom_file_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3951475160/8.dcm'\nvisualize_dicom_image(dicom_file_path)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.127482Z","iopub.execute_input":"2024-09-27T20:03:43.127788Z","iopub.status.idle":"2024-09-27T20:03:43.476298Z","shell.execute_reply.started":"2024-09-27T20:03:43.127756Z","shell.execute_reply":"2024-09-27T20:03:43.475369Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Note in this case exists four directories and have a problem with the images of *'Sagittal T2/STIR'*","metadata":{}},{"cell_type":"code","source":"def get_subdir(path,my_study):\n    directorio = os.path.join(path, str(my_study))\n    for ruta, dirs, archivos in os.walk(directorio):\n        for dir in dirs:\n            print(f\"{ruta}/{dir}\")","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.478433Z","iopub.execute_input":"2024-09-27T20:03:43.47876Z","iopub.status.idle":"2024-09-27T20:03:43.48402Z","shell.execute_reply.started":"2024-09-27T20:03:43.478727Z","shell.execute_reply":"2024-09-27T20:03:43.48294Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\nmy_study = 3637444890\nget_subdir(path,my_study)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.634472Z","iopub.execute_input":"2024-09-27T20:03:43.634792Z","iopub.status.idle":"2024-09-27T20:03:43.663852Z","shell.execute_reply.started":"2024-09-27T20:03:43.634759Z","shell.execute_reply":"2024-09-27T20:03:43.662996Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_merge_scs[train_merge_scs.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:43.908424Z","iopub.execute_input":"2024-09-27T20:03:43.90878Z","iopub.status.idle":"2024-09-27T20:03:43.922614Z","shell.execute_reply.started":"2024-09-27T20:03:43.908746Z","shell.execute_reply":"2024-09-27T20:03:43.921747Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dicom_file_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3892989905/9.dcm'\n\nvisualize_dicom_image(dicom_file_path)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:44.212817Z","iopub.execute_input":"2024-09-27T20:03:44.213171Z","iopub.status.idle":"2024-09-27T20:03:44.54505Z","shell.execute_reply.started":"2024-09-27T20:03:44.213137Z","shell.execute_reply":"2024-09-27T20:03:44.544116Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for f in ['series_description']:\n    print(train_merge_scs[f].value_counts())\nprint(\" \")\nfor f in ['series_description']:\n    print(train_merge_T2[f].value_counts())","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:44.546828Z","iopub.execute_input":"2024-09-27T20:03:44.547604Z","iopub.status.idle":"2024-09-27T20:03:44.559793Z","shell.execute_reply.started":"2024-09-27T20:03:44.547561Z","shell.execute_reply":"2024-09-27T20:03:44.558697Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Therefore, we can exclude the *'study_id='3637444890'* in the case of *'Spinal Canal Stenosis'* and use *train_merge_T2* for our analysis.","metadata":{}},{"cell_type":"markdown","source":"This demostrate **Afirmation 3** 🟨","metadata":{}},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 4**: \nGenerally there are three *'series_id'* which implies that there are three subdirectories. However, there are *'study_id'* that have more than three directories associated with them. \n\n**Proof:**","metadata":{}},{"cell_type":"markdown","source":"Let's count the total number of directories *'train_images'* has.","metadata":{}},{"cell_type":"code","source":"def count_subdirectories(path):\n    entries = os.listdir(path)\n    directories = [entry for entry in entries if os.path.isdir(os.path.join(path, entry))]\n    subdir_counts = []\n    for directory in directories:\n        dir_path = os.path.join(path, directory)\n        subdirs = [d for d in os.listdir(dir_path) if os.path.isdir(os.path.join(dir_path, d))]\n        subdir_count = len(subdirs)\n        subdir_counts.append((directory, subdir_count))\n    df = pd.DataFrame(subdir_counts, columns=['study_id', 'number_series_id'])\n    return df\n\npath = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\nsubdir_df = count_subdirectories(path)\nsubdir_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:44.896017Z","iopub.execute_input":"2024-09-27T20:03:44.896584Z","iopub.status.idle":"2024-09-27T20:03:50.901066Z","shell.execute_reply.started":"2024-09-27T20:03:44.896546Z","shell.execute_reply":"2024-09-27T20:03:50.900046Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we can view the frecuency of this classes","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nsubdir_df['number_series_id'] = subdir_df['number_series_id'].replace([np.inf, -np.inf], np.nan)\nsubdir_df = subdir_df.dropna(subset=['number_series_id'])\n\nbins = np.arange(0.5, 7.5, 1) \nlabels = ['1', '2', '3', '4', '5', '6']  \n\nplt.figure(figsize=(5, 3))\nplt.hist(subdir_df['number_series_id'], bins=bins, edgecolor='black')\n\nplt.xticks(ticks=np.arange(1, 7), labels=labels)\n\nplt.xlabel('Class')\nplt.ylabel('Frecuency')\nplt.title('Histogram of Number of Subdirectories per Class')\nplt.grid(True)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:50.90268Z","iopub.execute_input":"2024-09-27T20:03:50.903004Z","iopub.status.idle":"2024-09-27T20:03:51.157263Z","shell.execute_reply.started":"2024-09-27T20:03:50.902969Z","shell.execute_reply":"2024-09-27T20:03:51.156415Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This implies that for most 'series_id' we have three directories. Below we can compare with the classes present in 'train_series_descriptions' and conclude that in general we have three directories corresponding to 'Axial T2', 'Sagittal T1' and 'Sagittal T2/STIR'","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(5, 2))\nplt.hist(train_series_descriptions['series_description'], color='skyblue', edgecolor='black', orientation='horizontal')\nplt.title('Histogram of Series Description')\nplt.xlabel('Frequency')\nplt.ylabel('Series Description')\nplt.grid(True)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.158443Z","iopub.execute_input":"2024-09-27T20:03:51.158792Z","iopub.status.idle":"2024-09-27T20:03:51.372859Z","shell.execute_reply.started":"2024-09-27T20:03:51.158759Z","shell.execute_reply":"2024-09-27T20:03:51.371937Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We have repetitions of *'Axial T2'* because exists *'study_id'* that uses diferents *'series_id'* asociated to this class.","metadata":{}},{"cell_type":"code","source":"print(\"Cases with 4 subdirectories:\")\nprint(subdir_df[subdir_df.number_series_id == 4].count())\nprint(\"Cases with 5 subdirectories:\")\nprint(subdir_df[subdir_df.number_series_id == 5].count())\nprint(\"Cases with 6 subdirectories:\")\nprint(subdir_df[subdir_df.number_series_id == 6].count())","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.375581Z","iopub.execute_input":"2024-09-27T20:03:51.376074Z","iopub.status.idle":"2024-09-27T20:03:51.386816Z","shell.execute_reply.started":"2024-09-27T20:03:51.376027Z","shell.execute_reply":"2024-09-27T20:03:51.385842Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Show the case *'study_id'* asociates to six subdirectories","metadata":{}},{"cell_type":"code","source":"subdir_df[subdir_df.number_series_id == 6].iloc[0,0]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.387892Z","iopub.execute_input":"2024-09-27T20:03:51.388218Z","iopub.status.idle":"2024-09-27T20:03:51.399236Z","shell.execute_reply.started":"2024-09-27T20:03:51.388185Z","shell.execute_reply":"2024-09-27T20:03:51.398225Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\nmy_study = subdir_df[subdir_df.number_series_id == 6].iloc[0,0]\nget_subdir(path,my_study)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.400449Z","iopub.execute_input":"2024-09-27T20:03:51.400793Z","iopub.status.idle":"2024-09-27T20:03:51.45758Z","shell.execute_reply.started":"2024-09-27T20:03:51.400759Z","shell.execute_reply":"2024-09-27T20:03:51.456719Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_series_descriptions","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.458702Z","iopub.execute_input":"2024-09-27T20:03:51.458985Z","iopub.status.idle":"2024-09-27T20:03:51.469468Z","shell.execute_reply.started":"2024-09-27T20:03:51.458954Z","shell.execute_reply":"2024-09-27T20:03:51.468556Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_series_descriptions[train_series_descriptions.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.470805Z","iopub.execute_input":"2024-09-27T20:03:51.471584Z","iopub.status.idle":"2024-09-27T20:03:51.483265Z","shell.execute_reply.started":"2024-09-27T20:03:51.471537Z","shell.execute_reply":"2024-09-27T20:03:51.482181Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dicom_file_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/4096820034/2602265508/10.dcm'\n\nvisualize_dicom_image(dicom_file_path)\n\ntrain_series_descriptions[train_series_descriptions.study_id == 4096820034 ]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.484639Z","iopub.execute_input":"2024-09-27T20:03:51.485034Z","iopub.status.idle":"2024-09-27T20:03:51.777922Z","shell.execute_reply.started":"2024-09-27T20:03:51.484994Z","shell.execute_reply":"2024-09-27T20:03:51.777092Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_label_coordinates[train_label_coordinates.study_id == 4096820034]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.781392Z","iopub.execute_input":"2024-09-27T20:03:51.781842Z","iopub.status.idle":"2024-09-27T20:03:51.802161Z","shell.execute_reply.started":"2024-09-27T20:03:51.781801Z","shell.execute_reply":"2024-09-27T20:03:51.801238Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This demostrate **Afirmation 4** 🟨","metadata":{}},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 5**:\nWhen we look at the *'level'* in *train_label_descriptions* for *Spinal Canal Stenosis* we have *'study_id'* cases that exist in *train* and *train_series_descriptions* but not in *train_label_coordinates*. In other words, there is the *pain level* (*'Normal/Mild', 'Moderate', 'Severe'*) in *'train'* in relation to *'Spinal Canal Stenosis'* and we have the * 'series_descriptions'* (*' Sagittal T2/STIR'*) but there are no records in *'train_label_coordinates'*. For example: a) there are 70 cases in *'spinal_canal_stenosis_l1_l2'*, b) 30 cases in *'spinal_canal_stenosis_l2_l3'*, and 2, 3 and 5 in the other cases.\n\n**Proof:**","metadata":{}},{"cell_type":"markdown","source":"Now to compare *train['spinal_canal_stenosis_l1_l2']* with *train_merge_T2[train_merge_T2.level=='L1/L2'] and the others cases to observe that exists some diferences","metadata":{}},{"cell_type":"code","source":"print(\"scs_l1_l2 :\",compare_column(train.loc[:, ['study_id', 'spinal_canal_stenosis_l1_l2']],train_merge_T2[train_merge_T2.level == 'L1/L2'],'study_id'))\nprint(\" \")\nprint(\"scs_l2_l3 :\",compare_column(train.loc[:, ['study_id', 'spinal_canal_stenosis_l2_l3']],train_merge_T2[train_merge_T2.level == 'L2/L3'],'study_id'))\nprint(\" \")\nprint(\"scs_l3_l4 :\",compare_column(train.loc[:, ['study_id', 'spinal_canal_stenosis_l3_l4']],train_merge_T2[train_merge_T2.level == 'L3/L4'],'study_id'))\nprint(\" \")\nprint(\"scs_l4_l5 :\",compare_column(train.loc[:, ['study_id', 'spinal_canal_stenosis_l4_l5']],train_merge_T2[train_merge_T2.level == 'L4/L5'],'study_id'))\nprint(\" \")\nprint(\"scs_l5_s1 :\",compare_column(train.loc[:, ['study_id', 'spinal_canal_stenosis_l5_s1']],train_merge_T2[train_merge_T2.level == 'L5/S1'],'study_id'))","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.8038Z","iopub.execute_input":"2024-09-27T20:03:51.804245Z","iopub.status.idle":"2024-09-27T20:03:51.834559Z","shell.execute_reply.started":"2024-09-27T20:03:51.804199Z","shell.execute_reply":"2024-09-27T20:03:51.833706Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This demostrate **Afirmation 4** 🟨","metadata":{}},{"cell_type":"markdown","source":"## 2.4 Prepare the data","metadata":{}},{"cell_type":"markdown","source":"We build the database with the coordinates of each vertebra","metadata":{}},{"cell_type":"code","source":"def pivot_dataframe(df):\n    \n    df_pivot = df.pivot_table(index=['study_id', 'series_id', 'instance_number'], columns='level', values=['x', 'y']).reset_index()\n\n    df_pivot.columns = ['study_id', 'series_id', 'instance_number', 'L12x', 'L23x', 'L34x', 'L45x', 'L51x',\n                        'L12y', 'L23y', 'L34y', 'L45y', 'L51y']\n\n    df_pivot = df_pivot[['study_id', 'series_id', 'instance_number', 'L12x', 'L12y',\n                         'L23x', 'L23y', 'L34x', 'L34y', 'L45x', 'L45y', 'L51x', 'L51y']]\n\n    return df_pivot\n\npivoted_df = pivot_dataframe(train_merge_T2)\npivoted_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.835681Z","iopub.execute_input":"2024-09-27T20:03:51.836062Z","iopub.status.idle":"2024-09-27T20:03:51.874925Z","shell.execute_reply.started":"2024-09-27T20:03:51.835992Z","shell.execute_reply":"2024-09-27T20:03:51.873916Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Eliminate the cases where there isn´t all coordinates","metadata":{}},{"cell_type":"code","source":"pivoted_df = pivoted_df.dropna()\npivoted_df.reset_index(drop=True) ","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.876167Z","iopub.execute_input":"2024-09-27T20:03:51.876592Z","iopub.status.idle":"2024-09-27T20:03:51.900248Z","shell.execute_reply.started":"2024-09-27T20:03:51.876509Z","shell.execute_reply":"2024-09-27T20:03:51.899445Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 5**: \n\nExists cases where the coordinates are bad labeled.\n\n**Proof:**","metadata":{}},{"cell_type":"code","source":"columns_to_check = ['L12x', 'L12y', 'L23x', 'L23y', 'L34x', 'L34y', 'L45x', 'L45y', 'L51x', 'L51y']\nindices_to_drop = pivoted_df[pivoted_df[columns_to_check].lt(43).any(axis=1)].index\npivoted_df.loc[indices_to_drop]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.901799Z","iopub.execute_input":"2024-09-27T20:03:51.902353Z","iopub.status.idle":"2024-09-27T20:03:51.925866Z","shell.execute_reply.started":"2024-09-27T20:03:51.902305Z","shell.execute_reply":"2024-09-27T20:03:51.924958Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can eliminates this cases","metadata":{}},{"cell_type":"code","source":"pivoted_df = pivoted_df.drop(indices_to_drop)\npivoted_df.reset_index(drop=True, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.926866Z","iopub.execute_input":"2024-09-27T20:03:51.927123Z","iopub.status.idle":"2024-09-27T20:03:51.93533Z","shell.execute_reply.started":"2024-09-27T20:03:51.927093Z","shell.execute_reply":"2024-09-27T20:03:51.934563Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This demostrate **Afirmation 5** 🟨","metadata":{}},{"cell_type":"markdown","source":"## 2.5 Visualize and transform the images","metadata":{}},{"cell_type":"markdown","source":"Now we are going to transform all images to size $224x224$ and visualize the coordinates","metadata":{}},{"cell_type":"code","source":"input_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\ndef process_and_visualize_image(index):\n    try:\n        row = pivoted_df.iloc[index]\n        study_id = int(row['study_id'])\n        series_id = int(row['series_id'])\n        instance_number = int(row['instance_number'])\n\n        L12x, L12y = int(row['L12x']), int(row['L12y'])\n        L23x, L23y = int(row['L23x']), int(row['L23y'])\n        L34x, L34y = int(row['L34x']), int(row['L34y'])\n        L45x, L45y = int(row['L45x']), int(row['L45y'])\n        L51x, L51y = int(row['L51x']), int(row['L51y'])\n\n        dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n        ds = pydicom.dcmread(dicom_path)\n\n        img = ds.pixel_array\n        img_normalized = ((img - np.min(img)) / (np.max(img) - np.min(img))) * 255\n        img_normalized = img_normalized.astype(np.uint8)\n        img_resized = Image.fromarray(img_normalized).resize((224, 224), Image.LANCZOS)\n        img_resized_np = np.array(img_resized)\n\n        scale_x = 224 / img.shape[1]\n        scale_y = 224 / img.shape[0]\n\n        L12x_scaled, L12y_scaled = int(L12x * scale_x), int(L12y * scale_y)\n        L23x_scaled, L23y_scaled = int(L23x * scale_x), int(L23y * scale_y)\n        L34x_scaled, L34y_scaled = int(L34x * scale_x), int(L34y * scale_y)\n        L45x_scaled, L45y_scaled = int(L45x * scale_x), int(L45y * scale_y)\n        L51x_scaled, L51y_scaled = int(L51x * scale_x), int(L51y * scale_y)\n\n        plt.figure(figsize=(8, 8))\n        plt.subplot(2, 2, 1)\n        plt.imshow(img, cmap=plt.cm.bone)\n        plt.title(f'Original Image{study_id}_{series_id}_{instance_number}')\n        plt.scatter([L12x, L51x], [L12y, L51y], c='red', marker='x')\n\n        plt.subplot(2, 2, 2)\n        plt.imshow(img_resized_np, cmap=plt.cm.bone)\n        plt.title('Image Resized to 224x224')\n        plt.scatter([L12x_scaled, L51x_scaled], [L12y_scaled, L51y_scaled], c='red', marker='x')\n\n        # After a review we will make a cut using the minimums and maximums \n        y_min = 0 # L12y_scaled - 15 # 30 # max(0,0) # L12y_scaled - 15)\n        y_max = 224 # L51y_scaled + 25 #min(224,224)# L51y_scaled + 25)\n        x_min = 0#min(L12x_scaled,L23x_scaled,L34x_scaled,L45x_scaled,L51x_scaled) - 50 # 30 #max(0, 0)# L12x_scaled - 60)\n        x_max = 224#max(L12x_scaled,L23x_scaled,L34x_scaled,L45x_scaled,L51x_scaled) + 25 #min(224,224)# L51x_scaled + 20)\n\n        img_cropped = img_resized_np[y_min:y_max, x_min:x_max]\n\n        img_cropped_pil = Image.fromarray(img_cropped)\n        padding_top_bottom = (224 - img_cropped.shape[0]) // 2\n        padding_left_right = (224 - img_cropped.shape[1]) // 2\n        img_padded = ImageOps.expand(img_cropped_pil,\n                                     border=(padding_left_right, padding_top_bottom),\n                                     fill=0)\n        img_padded_np = np.array(img_padded)\n\n        plt.subplot(2, 2, 3)\n        plt.imshow(img_padded_np, cmap=plt.cm.bone)\n        plt.title('Cropped Image with Padding')\n\n        def adjust_coord(x, y, x_min, y_min):\n            return (x - x_min + padding_left_right, y - y_min + padding_top_bottom)\n\n        coords = [\n            adjust_coord(L12x_scaled, L12y_scaled, x_min, y_min),\n            adjust_coord(L23x_scaled, L23y_scaled, x_min, y_min),\n            adjust_coord(L34x_scaled, L34y_scaled, x_min, y_min),\n            adjust_coord(L45x_scaled, L45y_scaled, x_min, y_min),\n            adjust_coord(L51x_scaled, L51y_scaled, x_min, y_min)\n        ]\n\n        plt.subplot(2, 2, 4)\n        plt.imshow(img_padded_np, cmap=plt.cm.bone)\n        plt.title('Cropped Image with Coordinates')\n        for (x, y) in coords:\n            plt.scatter(x, y, c='red', marker='p')\n            plt.text(x, y, f'({x}, {y})', color='red')\n\n        plt.show()\n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\ntotal_images = len(pivoted_df)\ninteract(process_and_visualize_image, index=IntSlider(min=0, max=total_images-1, step=1, value=0))","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:51.936563Z","iopub.execute_input":"2024-09-27T20:03:51.937121Z","iopub.status.idle":"2024-09-27T20:03:52.920966Z","shell.execute_reply.started":"2024-09-27T20:03:51.937077Z","shell.execute_reply":"2024-09-27T20:03:52.920116Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tranforms the images and save new coordinates in *m1_pivoted_df.csv*","metadata":{}},{"cell_type":"code","source":"from tqdm import tqdm\n\ninput_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\noutput_base_path = '/kaggle/working/train_ima_vert_m1'\n\nif not os.path.exists(output_base_path):\n    os.makedirs(output_base_path)\n\ndef process_image(row):\n    study_id = int(row['study_id'])\n    series_id = int(row['series_id'])\n    instance_number = int(row['instance_number'])\n\n    L12x, L12y = int(row['L12x']), int(row['L12y'])\n    L23x, L23y = int(row['L23x']), int(row['L23y'])\n    L34x, L34y = int(row['L34x']), int(row['L34y'])\n    L45x, L45y = int(row['L45x']), int(row['L45y'])\n    L51x, L51y = int(row['L51x']), int(row['L51y'])\n\n    dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n    ds = pydicom.dcmread(dicom_path)\n\n    img = ds.pixel_array\n    img_normalized = ((img - np.min(img)) / (np.max(img) - np.min(img))) * 255\n    img_normalized = img_normalized.astype(np.uint8)\n    img_resized = Image.fromarray(img_normalized).resize((224, 224), Image.LANCZOS)\n    img_resized_np = np.array(img_resized)\n\n    scale_x = 224 / img.shape[1]\n    scale_y = 224 / img.shape[0]\n\n    L12x_scaled, L12y_scaled = int(L12x * scale_x), int(L12y * scale_y)\n    L23x_scaled, L23y_scaled = int(L23x * scale_x), int(L23y * scale_y)\n    L34x_scaled, L34y_scaled = int(L34x * scale_x), int(L34y * scale_y)\n    L45x_scaled, L45y_scaled = int(L45x * scale_x), int(L45y * scale_y)\n    L51x_scaled, L51y_scaled = int(L51x * scale_x), int(L51y * scale_y)\n\n     # After a review we will make a cut using the minimums and maximums \n    y_min = 0 # L12y_scaled - 15 # 30 # max(0,0) # L12y_scaled - 15)\n    y_max = 224 # L51y_scaled + 25 #min(224,224)# L51y_scaled + 25)\n    x_min = 0#min(L12x_scaled,L23x_scaled,L34x_scaled,L45x_scaled,L51x_scaled) - 50 # 30 #max(0, 0)# L12x_scaled - 60)\n    x_max = 224#max(L12x_scaled,L23x_scaled,L34x_scaled,L45x_scaled,L51x_scaled) + 25 #min(224,224)# L51x_scaled + 20)\n\n    img_cropped = img_resized_np[y_min:y_max, x_min:x_max]\n\n    img_cropped_pil = Image.fromarray(img_cropped)\n    padding_top_bottom = (224 - img_cropped.shape[0]) // 2\n    padding_left_right = (224 - img_cropped.shape[1]) // 2\n    img_padded = ImageOps.expand(img_cropped_pil,\n                                border=(padding_left_right, padding_top_bottom),\n                                fill=0)\n    img_padded_np = np.array(img_padded)\n\n    def adjust_coord(x, y, x_min, y_min):\n        return (x - x_min + padding_left_right, y - y_min + padding_top_bottom)\n\n    new_coords = {\n        'L12x': adjust_coord(L12x_scaled, L12y_scaled, x_min, y_min)[0],\n        'L12y': adjust_coord(L12x_scaled, L12y_scaled, y_min, x_min)[1],\n        'L23x': adjust_coord(L23x_scaled, L23y_scaled, x_min, y_min)[0],\n        'L23y': adjust_coord(L23x_scaled, L23y_scaled, y_min, x_min)[1],\n        'L34x': adjust_coord(L34x_scaled, L34y_scaled, x_min, y_min)[0],\n        'L34y': adjust_coord(L34x_scaled, L34y_scaled, y_min, x_min)[1],\n        'L45x': adjust_coord(L45x_scaled, L45y_scaled, x_min, y_min)[0],\n        'L45y': adjust_coord(L45x_scaled, L45y_scaled, y_min, x_min)[1],\n        'L51x': adjust_coord(L51x_scaled, L51y_scaled, x_min, y_min)[0],\n        'L51y': adjust_coord(L51x_scaled, L51y_scaled, y_min, x_min)[1],\n    }\n\n    output_filename = f'{study_id}_{series_id}_{instance_number}.jpg'\n    output_path = os.path.join(output_base_path, output_filename)\n    img_padded.save(output_path)\n\n    return new_coords, output_path\n\nnew_data = []\nfor idx, row in tqdm(pivoted_df.iterrows(), total=pivoted_df.shape[0], desc=\"Processing images\"):\n    new_coords, output_path = process_image(row)\n    new_row = row.to_dict()\n    new_row.update(new_coords)\n    new_row['image_path'] = output_path\n    new_data.append(new_row)\n\nnew_pivoted_df = pd.DataFrame(new_data)\n\nnew_pivoted_df.to_csv('/kaggle/working/m1_pivoted_df.csv', index=False)\n\nprint(\"Completed process. The transformed images have been saved and the new DataFrame has been created.\")","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:03:52.922851Z","iopub.execute_input":"2024-09-27T20:03:52.923286Z","iopub.status.idle":"2024-09-27T20:04:31.644193Z","shell.execute_reply.started":"2024-09-27T20:03:52.923236Z","shell.execute_reply":"2024-09-27T20:04:31.643228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"m1_pivoted_df = pd.read_csv('/kaggle/working/m1_pivoted_df.csv')\nm1_pivoted_df['study_id'] = m1_pivoted_df['study_id'].astype(int) \nm1_pivoted_df['series_id'] = m1_pivoted_df['series_id'].astype(int) \nm1_pivoted_df['instance_number'] = m1_pivoted_df['instance_number'].astype(int)\nm1_pivoted_df = m1_pivoted_df.drop(['image_path'], axis=1)\nm1_pivoted_df","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:04:31.645482Z","iopub.execute_input":"2024-09-27T20:04:31.645861Z","iopub.status.idle":"2024-09-27T20:04:31.67039Z","shell.execute_reply.started":"2024-09-27T20:04:31.645822Z","shell.execute_reply":"2024-09-27T20:04:31.669495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's see what the maximum and minimum values are for the coordinates and do a second transformation to make the training more efficient. These values ​​will allow us later to have a limit for the size of the rectangle in which each intervertebral level will be segmented.","metadata":{}},{"cell_type":"code","source":"columns_to_check_x = ['L12x', 'L23x', 'L34x', 'L45x', 'L51x']\ncolumns_to_check_y = ['L12y', 'L23y', 'L34y', 'L45y', 'L51y']\nminx = []\nmaxx = []\nminy = []\nmaxy = []\n\nfor i in columns_to_check_x:\n    min_x = min(m1_pivoted_df[i])\n    max_x = max(m1_pivoted_df[i])\n    minx.append(min_x)\n    maxx.append(max_x)\nprint(\"min(x) =\",min(minx), \"y max(x) =\",max(maxx))\n\nfor i in columns_to_check_y:\n    min_y = min(m1_pivoted_df[i])\n    max_y = max(m1_pivoted_df[i])\n    miny.append(min_y)\n    maxy.append(max_y)\nprint(\"min(y) =\",min(miny), \"y max(y) =\",max(maxy))","metadata":{"execution":{"iopub.status.busy":"2024-09-27T21:14:58.600584Z","iopub.execute_input":"2024-09-27T21:14:58.60101Z","iopub.status.idle":"2024-09-27T21:14:58.613647Z","shell.execute_reply.started":"2024-09-27T21:14:58.60097Z","shell.execute_reply":"2024-09-27T21:14:58.612634Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.6 Models to predict the intervertebral level","metadata":{}},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 7**:\nWith 20 epochs of ResNet50 model predicts much better than ResNet18, ViT and ResNet18.","metadata":{}},{"cell_type":"markdown","source":"Now we are going to design four models. All models are trained with 20 epochs, we use data augmentation, we apply the Adam optimizer and we use MSE to calculate the loss, we use 70% of the data for training, 15% for validation and the remaining 15% for testing. These values will allow us later to have a limit for the size of the rectangle in which each intervertebral level will be segmented. Finally their results are compared to choose the best option as for the model that minimizes the mean square error most quickly. By doing some tests we can see the performance and accuracy of each model.","metadata":{}},{"cell_type":"markdown","source":"## 2.6.1. ResNet18 ","metadata":{}},{"cell_type":"markdown","source":"We save the model as *spinenet_model.pth*","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nfrom torchvision.models import resnet18\nfrom torch.utils.data import DataLoader, Dataset, random_split\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\n\nclass SpineNet(nn.Module):\n    def __init__(self, pretrained=True):\n        super(SpineNet, self).__init__()\n        self.backbone = resnet18(weights='DEFAULT' if pretrained else None)\n        self.backbone.fc = nn.Linear(self.backbone.fc.in_features, 10)\n\n    def forward(self, x):\n        return self.backbone(x)\n\nclass SpineDataset(Dataset):\n    def __init__(self, dataframe, images_dir, transform=None):\n        self.data = dataframe\n        self.images_dir = images_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        study_id = int(row['study_id'])\n        series_id = int(row['series_id'])\n        instance_number = int(row['instance_number'])\n        image_path = os.path.join(self.images_dir, f'{study_id}_{series_id}_{instance_number}.jpg')\n        \n        image = Image.open(image_path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n        coords = torch.tensor(\n            [row[key] for key in ['L12x', 'L12y', 'L23x', 'L23y', 'L34x', 'L34y', 'L45x', 'L45y', 'L51x', 'L51y']],\n            dtype=torch.float32)\n        return image, coords\n\ndef collate_fn(batch):\n    batch = list(filter(lambda x: x is not None, batch))\n    if not batch:\n        return torch.zeros((1, 3, 224, 224)), torch.zeros((1, 10))\n    images, coords = zip(*batch)\n    images = torch.stack(images)\n    coords = torch.stack(coords)\n    return images, coords\n\n# Data augmentation transformations for the training images\ntrain_transform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),  # Random horizontal flip\n    transforms.RandomVerticalFlip(),    # Random vertical flip\n    transforms.RandomRotation(30),      # Random rotation\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),  # Random resized crop\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),  # Color jitter\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # Normalize\n])\n\n# Simple transformations for validation and testing\nval_transform = transforms.Compose([\n    #transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # Normalize\n])\n\n# Update the image path\nimage_path = '/kaggle/working/train_ima_vert_m1'\n\n# Create dataset\ndataset = SpineDataset(m1_pivoted_df, images_dir=image_path, transform=None)\n\n# Split dataset into train, validation, and test sets\ntrain_size = int(0.7 * len(dataset))\nval_size = int(0.15 * len(dataset))\ntest_size = len(dataset) - train_size - val_size\ntrain_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])\n\nprint(f'Number of images in training: {(train_size)}')\nprint(f'Number of images in validation: {(val_size)}')\nprint(f'Number of images under test: {(test_size)}')\n\n# Assign transformations\ntrain_dataset.dataset.transform = train_transform\nval_dataset.dataset.transform = val_transform\ntest_dataset.dataset.transform = val_transform\n\n# Create data loaders\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, collate_fn=collate_fn)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False, collate_fn=collate_fn)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, collate_fn=collate_fn)\n\nmodel = SpineNet(pretrained=True)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\ncriterion = nn.MSELoss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\ndef train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=20, device='cuda'):\n    model.to(device)\n    best_model_wts = model.state_dict()\n    best_loss = float('inf')\n\n    # Store losses for plotting\n    train_losses = []\n    val_losses = []\n\n    for epoch in range(num_epochs):\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                model.train()\n                dataloader = train_loader\n            else:\n                model.eval()\n                dataloader = val_loader\n\n            running_loss = 0.0\n\n            for inputs, labels in dataloader:\n                if inputs is None or labels is None:\n                    continue\n\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = model(inputs)\n                    loss = criterion(outputs, labels)\n\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                running_loss += loss.item() * inputs.size(0)\n\n            epoch_loss = running_loss / len(dataloader.dataset)\n\n            if phase == 'train':\n                train_losses.append(epoch_loss)\n            else:\n                val_losses.append(epoch_loss)\n\n            # Print the results in a compact format\n            if phase == 'val':\n                print(f'Epoch {epoch + 1}/{num_epochs} | '\n                      f'Train Loss: {train_losses[-1]:.4f} | '\n                      f'Val Loss: {val_losses[-1]:.4f}')\n\n            if phase == 'val' and epoch_loss < best_loss:\n                best_loss = epoch_loss\n                best_model_wts = model.state_dict()\n\n    print(f'Best val loss: {best_loss:4f}')\n    model.load_state_dict(best_model_wts)\n\n    # Plot the training and validation losses\n    plt.figure(figsize=(5, 5))\n    plt.plot(range(num_epochs), train_losses, label='Train Loss')\n    plt.plot(range(num_epochs), val_losses, label='Validation Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Losses')\n    plt.legend()\n    plt.show()\n\n    return model\n\nmodel = train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=20, device=device)\n\n# Evaluate the model on the test dataset\ndef evaluate_model(model, test_loader, criterion, device='cuda'):\n    model.eval()\n    model.to(device)\n    running_loss = 0.0\n\n    with torch.no_grad():\n        for inputs, labels in test_loader:\n            inputs = inputs.to(device)\n            labels = labels.to(device)\n\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item() * inputs.size(0)\n\n    test_loss = running_loss / len(test_loader.dataset)\n    print(f'Test Loss: {test_loss:.4f}')\n\n# Evaluate the trained model\nevaluate_model(model, test_loader, criterion, device=device)\n\n# Save the model to a file\nmodel_save_path = '/kaggle/working/spinenet_model.pth'  # Define the save path\ntorch.save(model.state_dict(), model_save_path)  # Save the model\nprint(f\"Model saved to {model_save_path}\")\n","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"let's make some predictions using this model","metadata":{}},{"cell_type":"code","source":"import torch\nfrom PIL import Image\nimport torchvision.transforms as transforms\nimport pydicom\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        return transform(img)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None\n\n# Function to make a prediction\ndef predict(image_path, model, device):\n    model.eval()\n    image = load_and_preprocess_dicom(image_path)\n    if image is None:\n        print(\"Failed to load and preprocess the image.\")\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        #print(f\"Model output: {output}\")\n        return output.cpu().numpy().flatten()  # Flatten the output for easier access\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Function to plot resized image and draw predicted rectangles\ndef plot_with_predictions(image_path, coordinates):\n    # Load the image for display\n    ds = pydicom.dcmread(image_path)\n    img = ds.pixel_array\n    img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n    # Convert to float32 and normalize for visualization\n    img = img.astype(np.float32)\n    img = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n    img = img.astype(np.uint8)\n\n    # Convert image to PIL and resize\n    img_pil = Image.fromarray(img)\n    img_resized = img_pil.resize((224, 224))\n\n    fig, ax = plt.subplots(1, figsize=(5, 5))\n    ax.imshow(img_resized)\n\n    # Draw rectangles centered at each predicted coordinate\n    for i in range(0, len(coordinates), 2):\n        x_center = coordinates[i]\n        y_center = coordinates[i + 1]\n        rect_width = 35  # Width of the rectangle\n        rect_height = 20  # Height of the rectangle\n        rect = patches.Rectangle((x_center - rect_width / 2, y_center - rect_height / 2 + 5),\n                                 rect_width, rect_height, linewidth=2, edgecolor='r', facecolor='none')\n        ax.add_patch(rect)\n\n    plt.axis('on')\n    plt.show()\n\n# Example usage\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/3844393089/11.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/100206310/2092806862/8.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1035170868/2727057862/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1557387235/2143604834/8.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/3844393089/12.dcm'\nimage_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1557387235/2143604834/8.dcm'\n\nmodel_path = '/kaggle/working/spinenet_model.pth'\n\n# Configure the device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# Load the trained model\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')\n\n# Plot the resized image with predicted rectangles\nif predicted_coordinates is not None:\n    plot_with_predictions(image_path, predicted_coordinates)\nelse:\n    print(\"Prediction failed.\")","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.6.2 VIT-Transformer ","metadata":{}},{"cell_type":"markdown","source":"The model is saved as *spinevit_model.pth*","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport torch\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\nfrom transformers import ViTModel\nfrom torch.optim import AdamW as PyTorchAdamW\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\n\n# Configure the device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# DataFrame with the training data\ndata_train = m1_pivoted_df\n\n# Divide the dataset into training, validation, and test sets\ntrain_df, test_df = train_test_split(data_train, test_size=0.3, random_state=42)\nval_df, test_df = train_test_split(test_df, test_size=0.5, random_state=42)\n\nprint(f'Number of images in training: {len(train_df)}')\nprint(f'Number of images in validation: {len(val_df)}')\nprint(f'Number of images under test: {len(test_df)}')\n\n# Custom Dataset\nclass ST1VertebralDataset(Dataset):\n    def __init__(self, images_dir, dataframe, transform=None):\n        self.data = dataframe\n        self.images_dir = images_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        study_id = int(row['study_id'])\n        series_id = int(row['series_id'])\n        instance_number = int(row['instance_number'])\n        image_path = os.path.join(self.images_dir, f'{study_id}_{series_id}_{instance_number}.jpg')\n        \n        image = Image.open(image_path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n        \n        # Obtain coordinates of the branches\n        coordinates = row[['L12x', 'L12y', 'L23x', 'L23y', 'L34x', 'L34y', 'L45x', 'L45y', 'L51x', 'L51y']].values\n        coordinates = coordinates.astype(np.float32)\n        \n        return image, coordinates\n\n# Data augmentation transformations for the images\ntransform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),  # Random horizontal flip\n    transforms.RandomVerticalFlip(),    # Random vertical flip\n    transforms.RandomRotation(30),      # Random rotation\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),  # Random resized crop\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),  # Color jitter\n    transforms.Resize((224, 224)),      # Resize to the input size of ViT\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Custom ViT model for regression\nclass ViTModelForRegression(nn.Module):\n    def __init__(self, num_coords=10):\n        super(ViTModelForRegression, self).__init__()\n        self.vit = ViTModel.from_pretrained('google/vit-base-patch16-224-in21k')\n        self.regression_head = nn.Linear(self.vit.config.hidden_size, num_coords)\n\n    def forward(self, x):\n        outputs = self.vit(x)\n        pooled_output = outputs.last_hidden_state[:, 0]\n        coords = self.regression_head(pooled_output)\n        return coords\n\ndef main():\n    # Create the dataset and DataLoader\n    images_dir = '/kaggle/working/train_ima_vert_m1'\n    train_dataset = ST1VertebralDataset(images_dir=images_dir, dataframe=train_df, transform=transform)\n    val_dataset = ST1VertebralDataset(images_dir=images_dir, dataframe=val_df, transform=transform)\n    test_dataset = ST1VertebralDataset(images_dir=images_dir, dataframe=test_df, transform=transform)\n\n    train_dataloader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=0)  # Set num_workers=0\n    val_dataloader = DataLoader(val_dataset, batch_size=16, shuffle=False, num_workers=0)    # Set num_workers=0\n    test_dataloader = DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=0)  # Set num_workers=0\n\n    # Initialize the model, optimizer, and loss functions\n    num_coords = 10  # Example: five key points with (x, y) coordinates\n    model = ViTModelForRegression(num_coords)\n    model.to(device)\n\n    optimizer = PyTorchAdamW(model.parameters(), lr=1e-4)\n    regression_loss_fn = nn.MSELoss()\n\n    num_epochs = 20\n\n    # Training function\n    def train_model(model, train_dataloader, val_dataloader, optimizer, num_epochs):\n        train_losses = []\n        val_losses = []\n        for epoch in range(num_epochs):\n            model.train()\n            total_regression_loss = 0.0\n\n            for images, coordinates in train_dataloader:#MODELtqdm(train_dataloader, desc=f'Epoch {epoch+1}/{num_epochs}'):\n                images = images.to(device)\n                coordinates = coordinates.to(device)\n\n                optimizer.zero_grad()\n\n                predicted_coords = model(images)\n                regression_loss = regression_loss_fn(predicted_coords, coordinates)\n\n                regression_loss.backward()\n                optimizer.step()\n\n                total_regression_loss += regression_loss.item()\n\n            avg_train_regression_loss = total_regression_loss / len(train_dataloader)\n            train_losses.append(avg_train_regression_loss)\n\n            # Evaluate the model on the validation set\n            model.eval()\n            val_regression_loss = 0.0\n            with torch.no_grad():\n                for images, coordinates in val_dataloader:\n                    images = images.to(device)\n                    coordinates = coordinates.to(device)\n\n                    predicted_coords = model(images)\n                    regression_loss = regression_loss_fn(predicted_coords, coordinates)\n\n                    val_regression_loss += regression_loss.item()\n\n            avg_val_regression_loss = val_regression_loss / len(val_dataloader)\n            val_losses.append(avg_val_regression_loss)\n\n            print(f'Epoch {epoch+1}/{num_epochs} | Train Loss: {avg_train_regression_loss:.4f} | Val Loss: {avg_val_regression_loss:.4f}')\n            #print(f'Epoch {epoch+1}/{num_epochs}, Validation Regression Loss: {avg_val_regression_loss:.4f}')\n\n        return train_losses, val_losses\n\n    # Train the model\n    train_losses, val_losses = train_model(model, train_dataloader, val_dataloader, optimizer, num_epochs)\n\n    # Save the model\n    model_save_path = '/kaggle/working/spinevit_model.pth'\n    torch.save(model.state_dict(), model_save_path)\n    print(f\"Modelo guardado en: {model_save_path}\")\n\n    # Plot training and validation losses\n    plt.figure(figsize=(5, 5))\n    plt.plot(train_losses, label='Train Regression Loss')\n    plt.plot(val_losses, label='Validation Regression Loss')\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.title('Regression Loss')\n    plt.legend()\n    plt.tight_layout()\n    plt.show()\n\n    # Evaluate the model on the test set\n    def evaluate_model(model, dataloader, device):\n        model.eval()\n        all_preds = []\n        all_labels = []\n        \n        with torch.no_grad():\n            for images, coordinates in tqdm(dataloader, desc='Evaluating'):\n                images = images.to(device)\n                coordinates = coordinates.to(device)\n                \n                predicted_coords = model(images)\n                all_preds.extend(predicted_coords.cpu().numpy())\n                all_labels.extend(coordinates.cpu().numpy())\n        \n        all_preds = np.array(all_preds)\n        all_labels = np.array(all_labels)\n        mse = np.mean((all_labels - all_preds) ** 2)\n        return mse\n\n    # Evaluate the model on the test set\n    test_mse = evaluate_model(model, test_dataloader, device)\n    print(f'Test Mean Squared Error: {test_mse:.4f}')\n\nif __name__ == '__main__':\n    main()\n","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we make a predicctions with this model","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport torch\nfrom torch import nn\nfrom torchvision import transforms\nfrom PIL import Image\nfrom transformers import ViTForImageClassification, ViTConfig\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport pydicom\nimport cv2\nfrom collections import OrderedDict\n\n# Define device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Define the class labels\nclass_labels = {0: 'L1/L2', 1: 'L2/L3', 2: 'L3/L4', 3: 'L4/L5', 4: 'L5/S1'}\n\n# Define transformations for the images\ntransform = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Define your model class if not already defined\nclass ViTModelWithCoordRegression(ViTForImageClassification):\n    def __init__(self, config):\n        super().__init__(config)\n        self.regressor = torch.nn.Linear(config.hidden_size, 10)  # 10 coordinates (5 points x 2 coordinates)\n        self.classifier = nn.Identity()  # Do not use the classification head\n\n    def forward(self, pixel_values, labels=None):\n        outputs = self.vit(pixel_values)\n        sequence_output = outputs.last_hidden_state[:, 0, :]  # [CLS] token\n        logits = self.regressor(sequence_output)\n        \n        loss = None\n        if labels is not None:\n            loss = torch.nn.MSELoss()(logits, labels)\n        \n        return (loss, logits) if loss is not None else logits\n\n# Load the configuration and model\nconfig = ViTConfig.from_pretrained('google/vit-base-patch16-224', num_labels=10)\nmodel = ViTModelWithCoordRegression(config)\nmodel.to(device)\n\n# Load the trained model state\nmodel_save_path = '/kaggle/working/spinevit_model.pth'\nstate_dict = torch.load(model_save_path, map_location=device, weights_only=True)\n\n# Fix the state dict keys\nnew_state_dict = OrderedDict()\nfor k, v in state_dict.items():\n    if 'regression_head' in k:\n        new_key = k.replace('regression_head', 'regressor')\n    elif 'classifier' in k:\n        continue  # Skip the classifier keys\n    else:\n        new_key = k\n    new_state_dict[new_key] = v\n\nmodel.load_state_dict(new_state_dict, strict=False)\nmodel.eval()\n\n# Function to load and transform a DICOM image\ndef load_and_transform_dicom(image_path):\n    ds = pydicom.dcmread(image_path)\n    img = ds.pixel_array\n    img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)  # Convert to RGB if necessary\n    img = cv2.resize(img, (224, 224))\n    img = cv2.convertScaleAbs(img, alpha=(255.0/65535.0))  # Convert to uint8\n    img = transform(img)\n    return img.unsqueeze(0)  # Add batch dimension\n\n# Function to predict coordinates\ndef predict_coordinates(model, image_path):\n    image = load_and_transform_dicom(image_path).to(device)\n    print(f'Image shape after transformation: {image.shape}')\n    print(\" \")\n    with torch.no_grad():\n        outputs = model(image)\n        print(f'Outputs from model: {outputs}')\n        print(\" \")\n        coordinates = outputs.cpu().numpy().flatten()  # Get the predicted coordinates\n        print(f'Predicted coordinates shape: {coordinates.shape}')\n        print(\" \")\n    return coordinates\n\n# Convert coordinates to readable format\ndef format_coordinates(coordinates):\n    formatted_coordinates = {}\n    for i in range(5):\n        formatted_coordinates[f'L{i+1}'] = (coordinates[2*i], coordinates[2*i + 1])\n    return formatted_coordinates\n\n# Function to visualize image and coordinates\ndef visualize_image_with_coordinates(image_path, coordinates):\n    ds = pydicom.dcmread(image_path)\n    img = ds.pixel_array\n    img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)\n    img = cv2.resize(img, (224, 224))\n    \n    fig, ax = plt.subplots()\n    ax.imshow(img)\n    ax.scatter(\n        [coordinates[f'L{i+1}'][0] for i in range(5)], \n        [coordinates[f'L{i+1}'][1] for i in range(5)], \n        color='red', marker='X'\n    )\n    \n    # Draw rectangles centered at each predicted coordinate\n    for i in range(5):\n        x_center = coordinates[f'L{i+1}'][0]\n        y_center = coordinates[f'L{i+1}'][1]\n        rect_width = 35  # Width of the rectangle\n        rect_height = 20  # Height of the rectangle\n        rect = patches.Rectangle((x_center - rect_width / 2, y_center - rect_height / 2),\n                                 rect_width, rect_height, linewidth=2, edgecolor='r', facecolor='none')\n        ax.add_patch(rect)\n        \n    plt.show()\n\n# Example prediction\n# Example usage\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/3844393089/11.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/100206310/2092806862/8.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1035170868/2727057862/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1557387235/2143604834/8.dcm'\nimage_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1557387235/2143604834/8.dcm'\n\npredicted_coordinates = predict_coordinates(model, image_path)\nformatted_coordinates = format_coordinates(predicted_coordinates)\nprint(f'Predicted coordinates for {image_path}:')\nprint(\" \")\nprint(formatted_coordinates)\n\n# Visualize the image with predicted coordinates\nvisualize_image_with_coordinates(image_path, formatted_coordinates)\n","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.6.3 ResNet50","metadata":{}},{"cell_type":"markdown","source":"We save the model like *spinenet20_model.pth*","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nfrom torchvision.models import resnet50  \nfrom torch.utils.data import DataLoader, Dataset, random_split\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\n\nclass SpineNet(nn.Module):\n    def __init__(self, pretrained=True):\n        super(SpineNet, self).__init__()\n        self.backbone = resnet50(weights='DEFAULT' if pretrained else None)  # Use resnet50\n        self.backbone.fc = nn.Linear(self.backbone.fc.in_features, 10)\n\n    def forward(self, x):\n        return self.backbone(x)\n\nclass SpineDataset(Dataset):\n    def __init__(self, dataframe, images_dir, transform=None):\n        self.data = dataframe\n        self.images_dir = images_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        study_id = int(row['study_id'])\n        series_id = int(row['series_id'])\n        instance_number = int(row['instance_number'])\n        image_path = os.path.join(self.images_dir, f'{study_id}_{series_id}_{instance_number}.jpg')\n        \n        image = Image.open(image_path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n        coords = torch.tensor(\n            [row[key] for key in ['L12x', 'L12y', 'L23x', 'L23y', 'L34x', 'L34y', 'L45x', 'L45y', 'L51x', 'L51y']],\n            dtype=torch.float32)\n        return image, coords\n\ndef collate_fn(batch):\n    batch = list(filter(lambda x: x is not None, batch))\n    if not batch:\n        return torch.zeros((1, 3, 224, 224)), torch.zeros((1, 10))\n    images, coords = zip(*batch)\n    images = torch.stack(images)\n    coords = torch.stack(coords)\n    return images, coords\n\n# Data augmentation transformations for the training images\ntrain_transform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),  # Random horizontal flip\n    transforms.RandomVerticalFlip(),    # Random vertical flip\n    transforms.RandomRotation(30),      # Random rotation\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),  # Random resized crop\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),  # Color jitter\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # Normalize\n])\n\n# Simple transformations for validation and testing\nval_transform = transforms.Compose([\n    #transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # Normalize\n])\n\n# Update the image path\nimage_path = '/kaggle/working/train_ima_vert_m1'\n\n# Create dataset\ndataset = SpineDataset(m1_pivoted_df, images_dir=image_path, transform=None)\n\n# Split dataset into train, validation, and test sets\ntrain_size = int(0.7 * len(dataset))\nval_size = int(0.15 * len(dataset))\ntest_size = len(dataset) - train_size - val_size\ntrain_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])\n\nprint(f'Number of images in training: {(train_size)}')\nprint(f'Number of images in validation: {(val_size)}')\nprint(f'Number of images under test: {(test_size)}')\n\n# Assign transformations\ntrain_dataset.dataset.transform = train_transform\nval_dataset.dataset.transform = val_transform\ntest_dataset.dataset.transform = val_transform\n\n# Create data loaders\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, collate_fn=collate_fn)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False, collate_fn=collate_fn)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, collate_fn=collate_fn)\n\nmodel = SpineNet(pretrained=True)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\ncriterion = nn.MSELoss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\ndef train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=20, device='cuda'):\n    model.to(device)\n    best_model_wts = model.state_dict()\n    best_loss = float('inf')\n\n    # Store losses for plotting\n    train_losses = []\n    val_losses = []\n\n    for epoch in range(num_epochs):\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                model.train()\n                dataloader = train_loader\n            else:\n                model.eval()\n                dataloader = val_loader\n\n            running_loss = 0.0\n\n            for inputs, labels in dataloader:\n                if inputs is None or labels is None:\n                    continue\n\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = model(inputs)\n                    loss = criterion(outputs, labels)\n\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                running_loss += loss.item() * inputs.size(0)\n\n            epoch_loss = running_loss / len(dataloader.dataset)\n\n            if phase == 'train':\n                train_losses.append(epoch_loss)\n            else:\n                val_losses.append(epoch_loss)\n\n            # Print the results in a compact format\n            if phase == 'val':\n                print(f'Epoch {epoch + 1}/{num_epochs} | '\n                      f'Train Loss: {train_losses[-1]:.4f} | '\n                      f'Val Loss: {val_losses[-1]:.4f}')\n\n            if phase == 'val' and epoch_loss < best_loss:\n                best_loss = epoch_loss\n                best_model_wts = model.state_dict()\n\n    print(f'Best val loss: {best_loss:4f}')\n    model.load_state_dict(best_model_wts)\n\n    # Plot the training and validation losses\n    plt.figure(figsize=(5, 5))\n    plt.plot(range(num_epochs), train_losses, label='Train Loss')\n    plt.plot(range(num_epochs), val_losses, label='Validation Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Losses')\n    plt.legend()\n    plt.show()\n\n    return model\n\nmodel = train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=20, device=device)\n\n# Save the model to a file\nmodel_save_path = '/kaggle/working/spinenet20_model.pth'  # Define the save path\ntorch.save(model.state_dict(), model_save_path)  # Save the model\nprint(f\"Model saved to {model_save_path}\")\n\n\n# Evaluate the model on the test dataset\ndef evaluate_model(model, test_loader, criterion, device='cuda'):\n    model.eval()\n    model.to(device)\n    running_loss = 0.0\n\n    with torch.no_grad():\n        for inputs, labels in test_loader:\n            inputs = inputs.to(device)\n            labels = labels.to(device)\n\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item() * inputs.size(0)\n\n    test_loss = running_loss / len(test_loader.dataset)\n    print(f'Test Loss: {test_loss:.4f}')\n\n# Evaluate the trained model\nevaluate_model(model, test_loader, criterion, device=device)\n","metadata":{"execution":{"iopub.status.busy":"2024-09-23T19:49:04.195249Z","iopub.execute_input":"2024-09-23T19:49:04.195754Z","iopub.status.idle":"2024-09-23T20:09:00.413786Z","shell.execute_reply.started":"2024-09-23T19:49:04.195691Z","shell.execute_reply":"2024-09-23T20:09:00.412713Z"},"jupyter":{"source_hidden":true,"outputs_hidden":true},"collapsed":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's now make predictions from some images","metadata":{}},{"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torchvision.models import resnet50\nfrom PIL import Image\nimport torchvision.transforms as transforms\nimport pydicom\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\n\n# Define the SpineNet class using ResNet50\nclass SpineNet(nn.Module):\n    def __init__(self, pretrained=True):\n        super(SpineNet, self).__init__()\n        self.backbone = resnet50(weights='DEFAULT' if pretrained else None)\n        self.backbone.fc = nn.Linear(self.backbone.fc.in_features, 10)\n\n    def forward(self, x):\n        return self.backbone(x)\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        return transform(img)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None\n\n# Function to make a prediction\ndef predict(image_path, model, device):\n    model.eval()\n    image = load_and_preprocess_dicom(image_path)\n    if image is None:\n        print(\"Failed to load and preprocess the image.\")\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        #print(f\"Model output: {output}\")\n        return output.cpu().numpy().flatten()  # Flatten the output for easier access\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Function to plot resized image and draw predicted rectangles\ndef plot_with_predictions(image_path, coordinates):\n    # Load the image for display\n    ds = pydicom.dcmread(image_path)\n    img = ds.pixel_array\n    img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n    # Convert to float32 and normalize for visualization\n    img = img.astype(np.float32)\n    img = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n    img = img.astype(np.uint8)\n\n    # Convert image to PIL and resize\n    img_pil = Image.fromarray(img)\n    img_resized = img_pil.resize((224, 224))\n\n    fig, ax = plt.subplots(1, figsize=(4, 4))\n    ax.imshow(img_resized)\n\n    # Draw rectangles centered at each predicted coordinate\n    for i in range(0, len(coordinates), 2):\n        x_center = coordinates[i]\n        y_center = coordinates[i + 1]\n        #rect_width = 40  # Width of the rectangle\n        #rect_height = 25  # Height of the rectangle\n        rect = patches.Rectangle((x_center , y_center ),\n                                 3, 3, linewidth=2, edgecolor='r', facecolor='none')\n        ax.add_patch(rect)\n\n    plt.axis('on')\n    plt.show()\n\n# Example usage\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/3844393089/11.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/100206310/2092806862/8.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1035170868/2727057862/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1557387235/2143604834/8.dcm'\nimage_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1557387235/2143604834/8.dcm'\n\nmodel_path = '/kaggle/working/spinenet20_model.pth'\n\n# Configure the device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# Load the trained model\nmodel = SpineNet(pretrained=False)\ntry:\n    #model.load_state_dict(torch.load(model_path, map_location=device))\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')\n\n# Plot the resized image with predicted rectangles\nif predicted_coordinates is not None:\n    plot_with_predictions(image_path, predicted_coordinates)\nelse:\n    print(\"Prediction failed.\")","metadata":{"execution":{"iopub.status.busy":"2024-09-27T13:25:20.019087Z","iopub.execute_input":"2024-09-27T13:25:20.019734Z","iopub.status.idle":"2024-09-27T13:25:20.978619Z","shell.execute_reply.started":"2024-09-27T13:25:20.019694Z","shell.execute_reply":"2024-09-27T13:25:20.977642Z"},"jupyter":{"source_hidden":true,"outputs_hidden":true},"collapsed":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.6.4 YOLO ","metadata":{}},{"cell_type":"markdown","source":"Let's define the model to train","metadata":{}},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import Dataset, DataLoader\nimport torch\nimport torch.nn as nn\nfrom torchvision import models, transforms\nfrom torch.optim import AdamW\nimport matplotlib.pyplot as plt\n\n# Configure the device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# DataFrame with the training data\ndata_train = pd.read_csv('/kaggle/working/m1_pivoted_df.csv')\n\n# Divide the dataset into training, validation, and test sets\ntrain_df, test_df = train_test_split(data_train, test_size=0.3, random_state=42)\nval_df, test_df = train_test_split(test_df, test_size=0.5, random_state=42)\n\nprint(f'Number of images in training: {len(train_df)}')\nprint(f'Number of images in validation: {len(val_df)}')\nprint(f'Number of images under test: {len(test_df)}')\n\nclass ST1VertebralDataset(Dataset):\n    def __init__(self, images_dir, labels_df, transform=None):\n        self.images_dir = images_dir\n        self.labels_df = labels_df\n        self.transform = transform\n        self.image_filenames = self.labels_df['image_path'].tolist()\n\n    def __len__(self):\n        return len(self.image_filenames)\n\n    def __getitem__(self, idx):\n        image_filename = self.image_filenames[idx]\n        image_path = os.path.join(self.images_dir, image_filename)\n        image = Image.open(image_path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n\n        # Read the tags corresponding to the current image from the DataFrame\n        label_row = self.labels_df[self.labels_df['image_path'] == image_filename]\n        labels = label_row[['L12x', 'L12y', 'L23x', 'L23y', 'L34x', 'L34y', 'L45x', 'L45y', 'L51x', 'L51y']].values[0]\n        labels = torch.tensor(labels, dtype=torch.float32)  # Convert labels to tensor\n\n        return image, labels\n\n# Data augmentation transformations for the images\ntransform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(30),\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# YOLO Head\nclass YOLOHead(nn.Module):\n    def __init__(self, num_outputs):\n        super(YOLOHead, self).__init__()\n        self.conv1 = nn.Conv2d(2048, 512, kernel_size=3, padding=1)\n        self.bn1 = nn.BatchNorm2d(512)\n        self.gelu = nn.GELU()\n        self.conv2 = nn.Conv2d(512, num_outputs, kernel_size=1)\n        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.gelu(x)\n        x = self.conv2(x)\n        x = self.avgpool(x)\n        x = x.view(x.size(0), -1)  # Adjust output to have the same size [batch_size, num_outputs]\n        return x\n\n# YOLO Model\nclass YOLOModel(nn.Module):\n    def __init__(self, num_outputs):\n        super(YOLOModel, self).__init__()\n        self.backbone = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)\n        self.backbone = nn.Sequential(*list(self.backbone.children())[:-2])  # Remove the last layers\n        self.yolo_head = YOLOHead(num_outputs=num_outputs)\n\n    def forward(self, x):\n        x = self.backbone(x)\n        outputs = self.yolo_head(x)\n        return outputs\n\ndef main():\n    # Create the dataset and DataLoader\n    images_dir = '/kaggle/working/train_ima_vert_m1'\n    \n    train_dataset = ST1VertebralDataset(\n        images_dir=images_dir,\n        labels_df=train_df,\n        transform=transform\n    )\n\n    val_dataset = ST1VertebralDataset(\n        images_dir=images_dir,\n        labels_df=val_df,\n        transform=transform\n    )\n\n    test_dataset = ST1VertebralDataset(\n        images_dir=images_dir,\n        labels_df=test_df,\n        transform=transform\n    )\n\n    train_dataloader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=0)\n    val_dataloader = DataLoader(val_dataset, batch_size=16, shuffle=False, num_workers=0)\n    test_dataloader = DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=0)\n\n    # Initialize the model, optimizer, and loss functions\n    num_outputs = 10  # Number of coordinates expected in labels\n    model = YOLOModel(num_outputs=num_outputs)\n    model.to(device)\n\n    optimizer = AdamW(model.parameters(), lr=0.001)\n    loss_fn = nn.MSELoss()\n\n    num_epochs = 20\n    train_losses = []  # List to store training losses\n    val_losses = []  # List to store validation losses\n\n    # Training loop\n    for epoch in range(num_epochs):\n        model.train()\n        total_train_loss = 0\n        for batch in train_dataloader:\n            images, labels = batch\n            images, labels = images.to(device), labels.to(device)\n            optimizer.zero_grad()\n            outputs = model(images)\n            \n            loss = loss_fn(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            total_train_loss += loss.item()\n        \n        avg_train_loss = total_train_loss / len(train_dataloader)\n        train_losses.append(avg_train_loss)\n\n        model.eval()\n        total_val_loss = 0\n        with torch.no_grad():\n            for batch in val_dataloader:\n                images, labels = batch\n                images, labels = images.to(device), labels.to(device)\n                outputs = model(images)\n                \n                loss = loss_fn(outputs, labels)\n                total_val_loss += loss.item()\n\n        avg_val_loss = total_val_loss / len(val_dataloader)\n        val_losses.append(avg_val_loss)\n\n        print(f'Epoch {epoch+1} | Train Loss: {avg_train_loss:.4f} | Val Loss: {avg_val_loss:.4f}')\n\n    # Plot the training and validation losses\n    plt.figure(figsize=(5, 5))\n    plt.plot(range(num_epochs), train_losses, label='Train Loss')\n    plt.plot(range(num_epochs), val_losses, label='Validation Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Losses')\n    plt.legend()\n    plt.show()\n\n    # Testing loop\n    model.eval()\n    with torch.no_grad():\n        total_test_loss = 0\n        for batch in test_dataloader:\n            images, labels = batch\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            \n            loss = loss_fn(outputs, labels)\n            total_test_loss += loss.item()\n\n        print(f'Test Loss: {total_test_loss / len(test_dataloader)}')\n\n    # Save the updated model\n    up_model_save_path = '/kaggle/working/model_yolo.pt'\n    torch.save(model.state_dict(), up_model_save_path)\n    print(f\"Model saved in: {up_model_save_path}\")\n\nif __name__ == '__main__':\n    main()\n","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's now make some predictions with the YOLO model\n","metadata":{}},{"cell_type":"code","source":"import os\nimport torch\nimport pydicom\nimport numpy as np\nfrom PIL import Image, ImageDraw\nfrom torchvision import models, transforms\nimport matplotlib.pyplot as plt\nimport torch.nn as nn\n\n# Configura el dispositivo\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Define el modelo\nclass YOLOHead(nn.Module):\n    def __init__(self, num_outputs):\n        super(YOLOHead, self).__init__()\n        self.conv1 = nn.Conv2d(2048, 512, kernel_size=3, padding=1)\n        self.bn1 = nn.BatchNorm2d(512)\n        self.gelu = nn.GELU()\n        #self.tanh = nn.Tanh()\n        #self.leaky_relu = nn.LeakyReLU(negative_slope=0.01)\n        #self.relu = nn.ReLU()\n        self.conv2 = nn.Conv2d(512, num_outputs, kernel_size=1)\n        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.gelu(x)\n        #x = self.leaky_relu(x)\n        #x = self.tanh(x)\n        #x = self.relu(x)\n        x = self.conv2(x)\n        x = self.avgpool(x)\n        x = x.view(x.size(0), -1)\n        return x\n\nclass YOLOModel(nn.Module):\n    def __init__(self, num_outputs):\n        super(YOLOModel, self).__init__()\n        self.backbone = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)\n        self.backbone = nn.Sequential(*list(self.backbone.children())[:-2])  # Remove the last layers\n        self.yolo_head = YOLOHead(num_outputs=num_outputs)\n\n    def forward(self, x):\n        x = self.backbone(x)\n        outputs = self.yolo_head(x)\n        return outputs\n\n# Cargar el modelo\nmodel_path = '/kaggle/working/model_yolo.pt'\nnum_outputs = 10  # Número de coordenadas esperadas en los labels\nmodel = YOLOModel(num_outputs=num_outputs)\nmodel.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\nmodel.to(device)\nmodel.eval()\n\n# Transformación para la imagen de entrada\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\ndef load_dicom_image(image_path):\n    \"\"\"Carga y convierte una imagen DICOM a formato RGB\"\"\"\n    dicom_image = pydicom.dcmread(image_path)\n    image_array = dicom_image.pixel_array\n\n    # Convertir la imagen a formato RGB\n    if len(image_array.shape) == 2:  # Imagen en escala de grises\n        image_array = np.stack([image_array] * 3, axis=-1)\n    else:  # Imagen en color (si es necesario, ajusta según el caso)\n        image_array = np.moveaxis(image_array, 0, -1)  # Convertir a formato [H, W, C]\n    \n    # Asegúrate de que el tipo de datos sea uint8\n    if image_array.dtype != np.uint8:\n        image_array = np.clip(image_array, 0, 255).astype(np.uint8)\n    \n    return image_array\n\ndef predict(image_path):\n    image_array = load_dicom_image(image_path)\n    image_pil = Image.fromarray(image_array)\n    image_transformed = transform(image_pil).unsqueeze(0).to(device)  # Añadir dimensión de batch\n    with torch.no_grad():\n        outputs = model(image_transformed)\n    return outputs\n\ndef visualize_prediction(image_path, outputs):\n    # Cargar la imagen original y transformarla a 224x224\n    image_array = load_dicom_image(image_path)\n    image_pil = Image.fromarray(image_array).resize((224, 224))  # Redimensionar a 224x224\n\n    # Ajustar según el formato de las predicciones\n    output_array = outputs.cpu().numpy().reshape(-1, 2)  # Ajusta según la salida de tu modelo\n\n    # Convertir la imagen a un formato que pueda ser modificado\n    draw = ImageDraw.Draw(image_pil)\n\n    # Colores para los rectángulos\n    colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255)]\n\n    # Dibujar las predicciones sobre la imagen\n    for i, (x, y) in enumerate(output_array):\n        x, y = int(x), int(y)  # Convertir coordenadas a enteros\n        # Asegúrate de que las coordenadas estén dentro del rango de la imagen\n        x = min(max(x, 0), 223)\n        y = min(max(y, 0), 223)\n        color = colors[i % len(colors)]  # Seleccionar el color para el rectángulo\n        draw.rectangle((x - 25, y - 25, x + 25, y+5), fill=None, outline=color, width=2)\n\n    # Mostrar la imagen\n    plt.figure(figsize=(5, 5))\n    plt.imshow(image_pil)\n    plt.title(\"Predicción\")\n    plt.axis('on')\n    plt.show()\n\n# Ejemplo de uso\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/3844393089/11.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/100206310/2092806862/8.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1035170868/2727057862/9.dcm'\n#image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1004726367/2526352865/9.dcm'\nimage_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1557387235/2143604834/8.dcm'\n\noutputs = predict(image_path)\nprint(\"Predicciones:\", outputs)\n\n# Visualizar la imagen con las predicciones\nvisualize_prediction(image_path, outputs)","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"By comparing the results obtained by each of the trained models we can conclude that the best model is *ResNet50*. Now let's use this model to estimate the missing and mislabeled coordinates.","metadata":{}},{"cell_type":"markdown","source":"From top to bottom in the following image we have the results of YOLO, ResNet50, ViT and ResNet18. In subsequent tests, with 35, 50 and 100 epochs, the performance of the ResNet model is the best in terms of loss and the speed with which it converges.","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n\n# Carga la imagen\nimg = mpimg.imread('/kaggle/input/model-scs-test/models_page-0001.jpg')\nplt.figure(figsize=(10, 20))\n# Muestra la imagen\nplt.imshow(img)\n\nplt.axis('off')  # Oculta los ejes\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-09-23T20:23:42.055271Z","iopub.execute_input":"2024-09-23T20:23:42.056188Z","iopub.status.idle":"2024-09-23T20:23:43.023966Z","shell.execute_reply.started":"2024-09-23T20:23:42.056134Z","shell.execute_reply":"2024-09-23T20:23:43.023011Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This demostrate **Afirmation 7** 🟨","metadata":{}},{"cell_type":"markdown","source":"We train a model using ResNet50 with 100 times","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nfrom torchvision.models import resnet50  \nfrom torch.utils.data import DataLoader, Dataset, random_split\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\n\nclass SpineNet(nn.Module):\n    def __init__(self, pretrained=True):\n        super(SpineNet, self).__init__()\n        self.backbone = resnet50(weights='DEFAULT' if pretrained else None)  # Use resnet50\n        self.backbone.fc = nn.Linear(self.backbone.fc.in_features, 10)\n\n    def forward(self, x):\n        return self.backbone(x)\n\nclass SpineDataset(Dataset):\n    def __init__(self, dataframe, images_dir, transform=None):\n        self.data = dataframe\n        self.images_dir = images_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        study_id = int(row['study_id'])\n        series_id = int(row['series_id'])\n        instance_number = int(row['instance_number'])\n        image_path = os.path.join(self.images_dir, f'{study_id}_{series_id}_{instance_number}.jpg')\n        \n        image = Image.open(image_path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n        coords = torch.tensor(\n            [row[key] for key in ['L12x', 'L12y', 'L23x', 'L23y', 'L34x', 'L34y', 'L45x', 'L45y', 'L51x', 'L51y']],\n            dtype=torch.float32)\n        return image, coords\n\ndef collate_fn(batch):\n    batch = list(filter(lambda x: x is not None, batch))\n    if not batch:\n        return torch.zeros((1, 3, 224, 224)), torch.zeros((1, 10))\n    images, coords = zip(*batch)\n    images = torch.stack(images)\n    coords = torch.stack(coords)\n    return images, coords\n\n# Data augmentation transformations for the training images\ntrain_transform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),  # Random horizontal flip\n    transforms.RandomVerticalFlip(),    # Random vertical flip\n    transforms.RandomRotation(30),      # Random rotation\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),  # Random resized crop\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),  # Color jitter\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # Normalize\n])\n\n# Simple transformations for validation and testing\nval_transform = transforms.Compose([\n    #transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # Normalize\n])\n\n# Update the image path\nimage_path = '/kaggle/working/train_ima_vert_m1'\n\n# Create dataset\ndataset = SpineDataset(m1_pivoted_df, images_dir=image_path, transform=None)\n\n# Split dataset into train, validation, and test sets\ntrain_size = int(0.7 * len(dataset))\nval_size = int(0.15 * len(dataset))\ntest_size = len(dataset) - train_size - val_size\ntrain_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])\n\nprint(f'Number of images in training: {(train_size)}')\nprint(f'Number of images in validation: {(val_size)}')\nprint(f'Number of images under test: {(test_size)}')\n\n# Assign transformations\ntrain_dataset.dataset.transform = train_transform\nval_dataset.dataset.transform = val_transform\ntest_dataset.dataset.transform = val_transform\n\n# Create data loaders\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, collate_fn=collate_fn)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False, collate_fn=collate_fn)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, collate_fn=collate_fn)\n\nmodel = SpineNet(pretrained=True)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\ncriterion = nn.MSELoss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\ndef train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=100, device='cuda'):\n    model.to(device)\n    best_model_wts = model.state_dict()\n    best_loss = float('inf')\n\n    # Store losses for plotting\n    train_losses = []\n    val_losses = []\n\n    for epoch in range(num_epochs):\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                model.train()\n                dataloader = train_loader\n            else:\n                model.eval()\n                dataloader = val_loader\n\n            running_loss = 0.0\n\n            for inputs, labels in dataloader:\n                if inputs is None or labels is None:\n                    continue\n\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = model(inputs)\n                    loss = criterion(outputs, labels)\n\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                running_loss += loss.item() * inputs.size(0)\n\n            epoch_loss = running_loss / len(dataloader.dataset)\n\n            if phase == 'train':\n                train_losses.append(epoch_loss)\n            else:\n                val_losses.append(epoch_loss)\n\n            # Print the results in a compact format\n            if phase == 'val':\n                print(f'Epoch {epoch + 1}/{num_epochs} | '\n                      f'Train Loss: {train_losses[-1]:.4f} | '\n                      f'Val Loss: {val_losses[-1]:.4f}')\n\n            if phase == 'val' and epoch_loss < best_loss:\n                best_loss = epoch_loss\n                best_model_wts = model.state_dict()\n\n    print(f'Best val loss: {best_loss:4f}')\n    model.load_state_dict(best_model_wts)\n\n    # Plot the training and validation losses\n    plt.figure(figsize=(5, 5))\n    plt.plot(range(num_epochs), train_losses, label='Train Loss')\n    plt.plot(range(num_epochs), val_losses, label='Validation Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Losses')\n    plt.legend()\n    plt.show()\n\n    return model\n\nmodel = train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=100, device=device)\n\n# Save the model to a file\nmodel_save_path = '/kaggle/working/spinenet_model.pth'  # Define the save path\ntorch.save(model.state_dict(), model_save_path)  # Save the model\nprint(f\"Model saved to {model_save_path}\")\n\n\n# Evaluate the model on the test dataset\ndef evaluate_model(model, test_loader, criterion, device='cuda'):\n    model.eval()\n    model.to(device)\n    running_loss = 0.0\n\n    with torch.no_grad():\n        for inputs, labels in test_loader:\n            inputs = inputs.to(device)\n            labels = labels.to(device)\n\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item() * inputs.size(0)\n\n    test_loss = running_loss / len(test_loader.dataset)\n    print(f'Test Loss: {test_loss:.4f}')\n\n# Evaluate the trained model\nevaluate_model(model, test_loader, criterion, device=device)\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:17:11.881158Z","iopub.execute_input":"2024-09-27T20:17:11.881737Z","iopub.status.idle":"2024-09-27T20:37:11.496607Z","shell.execute_reply.started":"2024-09-27T20:17:11.881699Z","shell.execute_reply":"2024-09-27T20:37:11.495631Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.7 Predict the intervertebral level","metadata":{}},{"cell_type":"markdown","source":"In the next section we are going to develop a model to predict the coordinates of the intervertebral levels using images from the *Sagittal T2/STIR* series. In this development we begin by imputing the missing data using the *ResNet50* model. First we obtain the image numbers by applying the missForest *spinet20.pth* method to predict the coordinates and expand the number of images to use in the different training.\n\nCon los datos imputados procederemos a segmentar las imágenes que servirán para los entrenamientos de los modelos locales. En este caso tendremos cinco lotes de imágenes por cada nivel. Posteriormente, reuniremos todas las imágenes segmentadas para entrenar un modelo general.   ","metadata":{}},{"cell_type":"markdown","source":"Let's now label the images without coordinates. However, first we must solve one more problem: To diagnose the condition *Spinal Canal Stenosis* we must choose an image from the directory associated with *Sagittal T2/STIR*. Below we can see that the most used image is $9$. ","metadata":{}},{"cell_type":"code","source":"subdir_df['number_series_id'] = subdir_df['number_series_id'].replace([np.inf, -np.inf], np.nan)\nsubdir_df = subdir_df.dropna(subset=['number_series_id'])\n\nm = train_merge_T2['instance_number'].min()\nM = train_merge_T2['instance_number'].max()\n\nbins = np.arange(m, M, 1) \nlabels = []\nfor i in range(m,M):\n    labs = i\n    labels.append(labs)\n\nplt.figure(figsize=(5, 3))\nplt.hist(train_merge_T2['instance_number'], bins=bins, edgecolor='black')\n\nplt.xticks(ticks=np.arange(m, M), labels=labels)\n\nplt.xlabel('Class')\nplt.ylabel('Frecuency')\nplt.title('Histogram of instance number')\nplt.grid(True)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-09-27T21:15:16.520521Z","iopub.execute_input":"2024-09-27T21:15:16.521368Z","iopub.status.idle":"2024-09-27T21:15:16.869316Z","shell.execute_reply.started":"2024-09-27T21:15:16.521327Z","shell.execute_reply":"2024-09-27T21:15:16.868417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Next we are going to design 5 databases of the condition *Spinal Canal Stenosis* by intervertebral level *L1/L2, L2/L3, L3/L4, L4/L5* and *L5/S1*. These databases will have imputable data.","metadata":{}},{"cell_type":"markdown","source":"One way to solve the problem is to apply the *MissForest* method. To do this, let's go by steps. From statements 5 and 6 we know the *level* without labels and also the mislabeled ones for the analysis of the condition *Spinal Canal Stenosis*:\n\n**scs_l1_l2**: {3906279426, 2232794498, 1133158151, 267842058, 3428426893, 1868615696, 344297746, 2256339732, 2548543893, 1292979992, 3151371929, 3637444890, 3515641631, 4072455711, 979209761, 2297295777, 1143209760, 893250212, 1722539301, 2279142182, 434488359, 2336516775, 4127969449, 693432872, 1745732011, 998688940, 3189076268, 267989673, 4137194670, 2839003053, 390498354, 74782131, 4232806580, 1567179188, 934686772, 2615694902, 1047914296, 2239199413, 3221995449, 3084269121, 2966999234, 3284652867, 3537214277, 3973705542, 953218250, 1681401548, 3966998094, 376723024, 1187463765, 4146959702, 1452830936, 3674744025, 1613634521, 296083289, 2213304029, 1395773918, 3850173026, 1557387235, 2566719718, 597329259, 305152236, 293713262, 2907745008, 2040217841, 3167888497, 3936691827, 2397650165, 1431195383, 3711891194, 1400326269, 3824720894}\n \n**scs_l2_l3**: {2232794498, 1133158151, 1868615696, 2256339732, 2548543893, 1292979992, 3637444890, 4072455711, 2297295777, 893250212, 1722539301, 2279142182, 434488359, 2336516775, 390498354, 1567179188, 4232806580, 934686772, 2615694902, 1047914296, 2239199413, 3221995449, 3294654272, 3084269121, 3537214277, 376723024, 1187463765, 3674744025, 1613634521, 2213304029, 3525503074, 1557387235, 3850173026, 2566719718, 293713262, 2907745008, 2040217841, 3167888497, 1431195383, 3711891194, 3824720894})\n \n**scs_l3_l4**: {3294654272, 3637444890}\n \n**scs_l4_l5**: {3294654272, 3637444890, 665627263}\n \n**scs_l5_s1**: {3294654272, 3225351618, 3024532039, 3637444890, 665627263}","metadata":{}},{"cell_type":"markdown","source":"Let us note that the two largest bases have differences so it is convenient to take each imputation separately.","metadata":{}},{"cell_type":"code","source":"\nscs_l1_l2 = {3906279426, 2232794498, 1133158151, 267842058, 3428426893, 1868615696, 344297746, 2256339732, \n             2548543893, 1292979992, 3151371929, 3637444890, 3515641631, 4072455711, 979209761, 2297295777, \n             1143209760, 893250212, 1722539301, 2279142182, 434488359, 2336516775, 4127969449, 693432872, \n             1745732011, 998688940, 3189076268, 267989673, 4137194670, 2839003053, 390498354, 74782131, \n             4232806580, 1567179188, 934686772, 2615694902, 1047914296, 2239199413, 3221995449, 3084269121, \n             2966999234, 3284652867, 3537214277, 3973705542, 953218250, 1681401548, 3966998094, 376723024, \n             1187463765, 4146959702, 1452830936, 3674744025, 1613634521, 296083289, 2213304029, 1395773918, \n             3850173026, 1557387235, 2566719718, 597329259, 305152236, 293713262, 2907745008, 2040217841, \n             3167888497, 3936691827, 2397650165, 1431195383, 3711891194, 1400326269, 3824720894}\n\nscs_l2_l3 = {2232794498, 1133158151, 1868615696, 2256339732, 2548543893, 1292979992, 3637444890, 4072455711, \n             2297295777, 893250212, 1722539301, 2279142182, 434488359, 2336516775, 390498354, 1567179188, \n             4232806580, 934686772, 2615694902, 1047914296, 2239199413, 3221995449, 3294654272, 3084269121, \n             3537214277, 376723024, 1187463765, 3674744025, 1613634521, 2213304029, 3525503074, 1557387235, \n             3850173026, 2566719718, 293713262, 2907745008, 2040217841, 3167888497, 1431195383, 3711891194, \n             3824720894}\n\ncommon_elements = scs_l1_l2.intersection(scs_l2_l3)\n\nunique_to_scs_l1_l2 = scs_l1_l2.difference(scs_l2_l3)\n\nunique_to_scs_l2_l3 = scs_l2_l3.difference(scs_l1_l2)\n\nsymmetric_difference = scs_l1_l2.symmetric_difference(scs_l2_l3)\n\nprint(\"Common elements:\", common_elements)\nprint(\"\")\nprint(\"Common elements in scs_l1_l2:\", unique_to_scs_l1_l2)\nprint(\"\")\nprint(\"Common elements in scs_l2_l3:\", unique_to_scs_l2_l3)\nprint(\"\")\nprint(\"Common elements:\", symmetric_difference)\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:12.132082Z","iopub.execute_input":"2024-09-27T20:41:12.13256Z","iopub.status.idle":"2024-09-27T20:41:12.145623Z","shell.execute_reply.started":"2024-09-27T20:41:12.132515Z","shell.execute_reply":"2024-09-27T20:41:12.144725Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The process that we will follow in the imputation of the bases *df_scs_l1_l2, df_scs_l2_l3, df_scs_l3_l4, df_scs_l4_l5, df_scs_l5_s1* is the following","metadata":{}},{"cell_type":"markdown","source":"🔸 **Step 1**: First, let's build a database with level *L1/L2* from *train_merge_T2* and add to this database in the *study_id* column the values of *scs_l1_l2*.\n\n🔸 **Step 2**: Since the column *spinal_canal_stenosis_l1_l2* of *train* has the diagnostic values of *scs_l1_l2* we can add it to *train_merge_T2*.\n\n🔸 **Step 3**: Since, in the case of the condition *spinal_canal_stenosis*, the *series_description* corresponding to *Sagittal T2/STIR* is used, we can search for the values corresponding to *scs_l1_l2* in the database *train_series_descriptions* and add it to *train_merge_T2*.\n\n🔸 **Step 4**: We apply the *missForest* method to estimate the value of the image used.\n\n🔸 **Step 5**: We apply our model to predict the coordinates of the images we have integrated.","metadata":{}},{"cell_type":"markdown","source":"## 2.7.1 spinal_canal_stenosis_l1_l2","metadata":{}},{"cell_type":"code","source":"df_scs_l1_l2 = train_merge_T2[train_merge_T2.level == 'L1/L2']\ndf_scs_l1_l2 = df_scs_l1_l2.reset_index(drop=True)\ndf_scs_l1_l2","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:14.228095Z","iopub.execute_input":"2024-09-27T20:41:14.228747Z","iopub.status.idle":"2024-09-27T20:41:14.24948Z","shell.execute_reply.started":"2024-09-27T20:41:14.228705Z","shell.execute_reply":"2024-09-27T20:41:14.248544Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scs_l1_l2 = [3906279426, 2232794498, 1133158151, 267842058, 3428426893, 1868615696, \n             344297746, 2256339732, 2548543893, 1292979992, 3151371929, 3637444890, \n             3515641631, 4072455711, 979209761, 2297295777, 1143209760, 893250212, \n             1722539301, 2279142182, 434488359, 2336516775, 4127969449, 693432872, \n             1745732011, 998688940, 3189076268, 267989673, 4137194670, 2839003053, \n             390498354, 74782131, 4232806580, 1567179188, 934686772, 2615694902, \n             1047914296, 2239199413, 3221995449, 3084269121, 2966999234, 3284652867, \n             3537214277, 3973705542, 953218250, 1681401548, 3966998094, 376723024, \n             1187463765, 4146959702, 1452830936, 3674744025, 1613634521, 296083289, \n             2213304029, 1395773918, 3850173026, 1557387235, 2566719718, 597329259, \n             305152236, 293713262, 2907745008, 2040217841, 3167888497, 3936691827, \n             2397650165, 1431195383, 3711891194, 1400326269, 3824720894]\nlen(scs_l1_l2)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:14.531153Z","iopub.execute_input":"2024-09-27T20:41:14.532265Z","iopub.status.idle":"2024-09-27T20:41:14.543649Z","shell.execute_reply.started":"2024-09-27T20:41:14.532199Z","shell.execute_reply":"2024-09-27T20:41:14.542797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_scs_l1_l2_sd = train_series_descriptions[train_series_descriptions.series_description == 'Sagittal T2/STIR'].reset_index(drop=True)\nscs_l1_l2_sd = []\nfor i in scs_l1_l2:\n    scs_sd = df_scs_l1_l2_sd[df_scs_l1_l2_sd.study_id == i]\n    scs_sd = scs_sd.iloc[0,1]\n    scs_l1_l2_sd.append(scs_sd)\nscs_l1_l2_sd[0:5] # first elements","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:14.792503Z","iopub.execute_input":"2024-09-27T20:41:14.792876Z","iopub.status.idle":"2024-09-27T20:41:14.830597Z","shell.execute_reply.started":"2024-09-27T20:41:14.792842Z","shell.execute_reply":"2024-09-27T20:41:14.829717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Calculate total number of rows\ntotal_rows = len(df_scs_l1_l2) + len(scs_l1_l2)\n\n# Add missing rows to all columns\ndf_scs_l1_l2 = df_scs_l1_l2.reindex(range(total_rows))\n\n# Add the values to column 'study_id'\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 0] = scs_l1_l2\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 1] = scs_l1_l2_sd\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 2] = np.nan\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 3] = 'Spinal Canal Stenosis'\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 4] = 'L1/L2'\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 5] = np.nan\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 6] = np.nan\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 7] = 'Sagittal T2/STIR'\n\ndf_scs_l1_l2['study_id'] = df_scs_l1_l2['study_id'].astype(int)\ndf_scs_l1_l2['series_id'] = df_scs_l1_l2['series_id'].astype(int)\n\ndf_scs_l1_l2","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:15.032157Z","iopub.execute_input":"2024-09-27T20:41:15.032587Z","iopub.status.idle":"2024-09-27T20:41:15.0684Z","shell.execute_reply.started":"2024-09-27T20:41:15.032537Z","shell.execute_reply":"2024-09-27T20:41:15.067163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"To impute the column *'instance_number'* we extract the following columns","metadata":{}},{"cell_type":"code","source":"df_scs_l1_l2_insNum = df_scs_l1_l2.loc[:,['study_id','series_id','instance_number']]\ndf_scs_l1_l2_insNum","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:15.239158Z","iopub.execute_input":"2024-09-27T20:41:15.239514Z","iopub.status.idle":"2024-09-27T20:41:15.252804Z","shell.execute_reply.started":"2024-09-27T20:41:15.23948Z","shell.execute_reply":"2024-09-27T20:41:15.251805Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We apply imputation","metadata":{}},{"cell_type":"code","source":"from sklearn.experimental import enable_iterative_imputer\nfrom sklearn.impute import IterativeImputer\n\n# Create an IterativeImputer object\nimputer = IterativeImputer()\n\n# Impute missing data\ndf_scs_l1_l2_insNum_imputed = imputer.fit_transform(df_scs_l1_l2_insNum)\n\n# Show imputed data\ndf_scs_l1_l2_insNum_imputed = df_scs_l1_l2_insNum_imputed.astype(int)\ndf_scs_l1_l2_insNum_imputed = pd.DataFrame(df_scs_l1_l2_insNum_imputed)\n\n# we add the imputed data to the database\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 2] = df_scs_l1_l2_insNum_imputed.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):,2]\ndf_scs_l1_l2['instance_number'] = df_scs_l1_l2['instance_number'].astype(int)\ndf_scs_l1_l2","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:15.465563Z","iopub.execute_input":"2024-09-27T20:41:15.466514Z","iopub.status.idle":"2024-09-27T20:41:15.692464Z","shell.execute_reply.started":"2024-09-27T20:41:15.466468Z","shell.execute_reply":"2024-09-27T20:41:15.691492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"With the proposed model we make the predictions of the last 71 records, extract the coordinates of the intervertebral level 'L1/L2' and add them to the data to impute the missing coordinates.","metadata":{}},{"cell_type":"code","source":"df_scs_l1_l2_imp_coordinates = df_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):,]\ndf_scs_l1_l2_imp_coordinates ","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:15.700043Z","iopub.execute_input":"2024-09-27T20:41:15.700375Z","iopub.status.idle":"2024-09-27T20:41:15.715678Z","shell.execute_reply.started":"2024-09-27T20:41:15.700341Z","shell.execute_reply":"2024-09-27T20:41:15.714728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's make the predictions with the ResNet50 model","metadata":{}},{"cell_type":"code","source":"# Define the SpineNet class using ResNet50\nclass SpineNet(nn.Module):\n    def __init__(self, pretrained=True):\n        super(SpineNet, self).__init__()\n        self.backbone = resnet50(weights='DEFAULT' if pretrained else None)\n        self.backbone.fc = nn.Linear(self.backbone.fc.in_features, 10)\n\n    def forward(self, x):\n        return self.backbone(x)\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        return transform(img)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None\n\n# Function to make a prediction\ndef predict(image_path, model, device):\n    model.eval()\n    image = load_and_preprocess_dicom(image_path)\n    if image is None:\n        print(\"Failed to load and preprocess the image.\")\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        return output.cpu().numpy().flatten()  # Flatten the output for easier access\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Function to plot resized image and draw predicted rectangles\ndef plot_with_predictions(image_path, coordinates):\n    # Load the image for display\n    ds = pydicom.dcmread(image_path)\n    img = ds.pixel_array\n    img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n    # Convert to float32 and normalize for visualization\n    img = img.astype(np.float32)\n    img = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n    img = img.astype(np.uint8)\n\n    # Convert image to PIL and resize\n    img_pil = Image.fromarray(img)\n    img_resized = img_pil.resize((224, 224))\n\n    fig, ax = plt.subplots(1, figsize=(4, 4))\n    ax.imshow(img_resized)\n\n    # Draw rectangles centered at each predicted coordinate\n    for i in range(0, len(coordinates), 2):\n        x_center = coordinates[i]\n        y_center = coordinates[i + 1]\n        rect = patches.Rectangle((x_center - 0, y_center - 0), 5, 5, linewidth=2, edgecolor='r', facecolor='none')\n        ax.add_patch(rect)\n\n    plt.axis('on')\n    plt.show()\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Load the database\ndf = df_scs_l1_l2_imp_coordinates\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\n# Obtain image paths from DataFrame\nimage_files = df.apply(lambda row: os.path.join(image_dir, f\"{row['study_id']}/{row['series_id']}/{row['instance_number']}.dcm\"), axis=1).tolist()\n\ndef process_and_visualize_image(index):\n    try:\n        image_path = image_files[index]  # Complete image path\n        outputs = predict(image_path, model, device)\n        print(f\"Predictions for image {image_path}: {outputs}\")\n        plot_with_predictions(image_path, outputs)\n    except Exception as e:\n        print(f\"Error processing image {image_path}: {e}\")\n\n# Interact for visualization\ninteract(process_and_visualize_image, index=IntSlider(min=0, max=len(image_files)-1, step=1, value=0))\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:18.287334Z","iopub.execute_input":"2024-09-27T20:41:18.287762Z","iopub.status.idle":"2024-09-27T20:41:19.539605Z","shell.execute_reply.started":"2024-09-27T20:41:18.28772Z","shell.execute_reply":"2024-09-27T20:41:19.538616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now let's save the coordinates by transforming them to the original size of each image","metadata":{}},{"cell_type":"code","source":"# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Save original size\n        original_size = img.shape[:2]  # (height, width)\n        \n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        \n        # Apply transformation\n        resized_image = transform(img)\n        \n        # Calculate scale factors\n        width_ratio = original_size[1] / 224\n        height_ratio = original_size[0] / 224\n        \n        return resized_image, original_size, (width_ratio, height_ratio)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None, None, None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None, None, None\n\n# Function to make a prediction and return scaled coordinates\ndef predict(image_path, model, device):\n    model.eval()\n    image, original_size, (width_ratio, height_ratio) = load_and_preprocess_dicom(image_path)\n    if image is None:\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        coordinates = output.cpu().numpy().flatten()  # Flatten the output for easier access\n\n        # Scale the coordinates back to original size\n        scaled_coordinates = []\n        for i in range(0, len(coordinates), 2):\n            x = coordinates[i] \n            y = coordinates[i + 1] \n            x_rescaled = x * width_ratio\n            y_rescaled = y * height_ratio\n            scaled_coordinates.append((x_rescaled, y_rescaled))\n        \n        return scaled_coordinates\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Load the database\ndf = df_scs_l1_l2_imp_coordinates\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\n# Obtain image paths from DataFrame\nimage_files = df.apply(lambda row: os.path.join(image_dir, f\"{row['study_id']}/{row['series_id']}/{row['instance_number']}.dcm\"), axis=1).tolist()\n\n# Initialize lists to store results\nresults = []\n\nfor image_path in image_files:\n    try:\n        scaled_coordinates = predict(image_path, model, device)\n        if scaled_coordinates is not None:\n            # Assuming `scaled_coordinates` contains the rescaled coordinates for L1-L2 vertebrae\n            # Adapt based on the specific structure of the output\n            results.append({\n                'study_id': image_path.split('/')[-3],\n                'series_id': image_path.split('/')[-2],\n                'L12x': scaled_coordinates[0][0],  # X coordinate of L1-L2\n                'L12y': scaled_coordinates[0][1]   # Y coordinate of L1-L2\n            })\n    except Exception as e:\n        print(f\"Error processing image {image_path}: {e}\")\n\n# Create DataFrame with results\ndf_results = pd.DataFrame(results)\nprint(df_results)\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:19.541435Z","iopub.execute_input":"2024-09-27T20:41:19.541731Z","iopub.status.idle":"2024-09-27T20:41:23.596919Z","shell.execute_reply.started":"2024-09-27T20:41:19.5417Z","shell.execute_reply":"2024-09-27T20:41:23.595894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now add this coordinates to the dataframe *df_scs_l1_l2*","metadata":{}},{"cell_type":"code","source":"df_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 5] = df_results['L12x']\ndf_scs_l1_l2.iloc[len(df_scs_l1_l2)-len(scs_l1_l2):, 6] = df_results['L12y']","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:23.598452Z","iopub.execute_input":"2024-09-27T20:41:23.598799Z","iopub.status.idle":"2024-09-27T20:41:23.605016Z","shell.execute_reply.started":"2024-09-27T20:41:23.598763Z","shell.execute_reply":"2024-09-27T20:41:23.603894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"and the final result is the following database","metadata":{}},{"cell_type":"code","source":"df_scs_l1_l2","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:23.608149Z","iopub.execute_input":"2024-09-27T20:41:23.608616Z","iopub.status.idle":"2024-09-27T20:41:23.627773Z","shell.execute_reply.started":"2024-09-27T20:41:23.608564Z","shell.execute_reply":"2024-09-27T20:41:23.626708Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 8**:\n \nThe case study_id = 3637444890 has an incorrect label with respect to series_id = 3892989905.\n\nThe case study_id = 2905025904 and series_id = 816381378 is mislabeled with respect to coordinates.\n\nThe case study_id = 1438760543 and series_id = 737753815 is mislabeled with respect to coordinates.","metadata":{}},{"cell_type":"code","source":"path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\nmy_study = 3637444890\nget_subdir(path,my_study)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:23.628928Z","iopub.execute_input":"2024-09-27T20:41:23.629268Z","iopub.status.idle":"2024-09-27T20:41:23.64389Z","shell.execute_reply.started":"2024-09-27T20:41:23.629228Z","shell.execute_reply":"2024-09-27T20:41:23.642874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_series_descriptions[train_series_descriptions.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:23.645046Z","iopub.execute_input":"2024-09-27T20:41:23.645708Z","iopub.status.idle":"2024-09-27T20:41:23.656557Z","shell.execute_reply.started":"2024-09-27T20:41:23.645653Z","shell.execute_reply":"2024-09-27T20:41:23.655411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_scs_l1_l2[df_scs_l1_l2.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:23.657942Z","iopub.execute_input":"2024-09-27T20:41:23.658778Z","iopub.status.idle":"2024-09-27T20:41:23.673312Z","shell.execute_reply.started":"2024-09-27T20:41:23.658731Z","shell.execute_reply":"2024-09-27T20:41:23.672295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In this case we can use *series_id = 3951475160*","metadata":{}},{"cell_type":"code","source":"dicom_file_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3951475160/8.dcm'\nvisualize_dicom_image(dicom_file_path)","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:23.674706Z","iopub.execute_input":"2024-09-27T20:41:23.675226Z","iopub.status.idle":"2024-09-27T20:41:24.016232Z","shell.execute_reply.started":"2024-09-27T20:41:23.675181Z","shell.execute_reply":"2024-09-27T20:41:24.015284Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"and now add the correct labels in the next cases.\n\nThis implies that we must review each database to avoid cases similar to those we have reviewed in the case of database *df_scs_l1_l2*","metadata":{}},{"cell_type":"markdown","source":"This demostrate **Afirmation 8** 🟨","metadata":{}},{"cell_type":"markdown","source":"#### 🟡 **Afirmation 9**:\n\nThe databases *df_scs_l1_l2, df_scs_l2_l3,df_scs_l3_l4,df_scs_l4_l5,df_scs_l5_s1 * that combine the referring data by intervertebral level are imputed.","metadata":{}},{"cell_type":"code","source":"for i in {737753815, 816381378}:\n    print(df_scs_l1_l2[df_scs_l1_l2.series_id == i])","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.017347Z","iopub.execute_input":"2024-09-27T20:41:24.017691Z","iopub.status.idle":"2024-09-27T20:41:24.031214Z","shell.execute_reply.started":"2024-09-27T20:41:24.017638Z","shell.execute_reply":"2024-09-27T20:41:24.030293Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/2905025904/816381378/11.dcm'\n#model_path = '/kaggle/working/spinenet50_model.pth'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.034994Z","iopub.execute_input":"2024-09-27T20:41:24.035326Z","iopub.status.idle":"2024-09-27T20:41:24.07829Z","shell.execute_reply.started":"2024-09-27T20:41:24.035295Z","shell.execute_reply":"2024-09-27T20:41:24.077376Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1438760543/737753815/9.dcm'\n#model_path = '/kaggle/working/spinenet50_model.pth'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.07938Z","iopub.execute_input":"2024-09-27T20:41:24.079729Z","iopub.status.idle":"2024-09-27T20:41:24.114394Z","shell.execute_reply.started":"2024-09-27T20:41:24.079688Z","shell.execute_reply":"2024-09-27T20:41:24.113463Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3951475160/8.dcm'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.11564Z","iopub.execute_input":"2024-09-27T20:41:24.11609Z","iopub.status.idle":"2024-09-27T20:41:24.148857Z","shell.execute_reply.started":"2024-09-27T20:41:24.116047Z","shell.execute_reply":"2024-09-27T20:41:24.148012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We update the database *df_scs_l1_l2*","metadata":{}},{"cell_type":"code","source":"T = df_scs_l1_l2\ndef update_entry(df_scs_l1_l2, study_id, new_series_id, new_instance_number, new_x, new_y):\n    \n    mask = (df_scs_l1_l2['study_id'] == study_id) \n    index = df_scs_l1_l2.index[mask].tolist()  #\n\n    if len(index) != 1:\n        raise ValueError(f\"No single entry was found for study_id {study_id}\")\n\n    # Update the values of 'instance_number', 'x' and 'y'\n    df_scs_l1_l2.loc[index[0], 'series_id'] = new_series_id\n    df_scs_l1_l2.loc[index[0], 'instance_number'] = new_instance_number\n    df_scs_l1_l2.loc[index[0], 'x'] = new_x\n    df_scs_l1_l2.loc[index[0], 'y'] = new_y\nif __name__ == '__main__':\n    update_entry(df_scs_l1_l2, T.iloc[1286].iloc[0], 816381378, 11, 214.2267412458147, 109.61395454406738)\n    update_entry(df_scs_l1_l2, T.iloc[626].iloc[0], 737753815, 9, 246.69515991210938, 126.09215545654297)\n    update_entry(df_scs_l1_l2, T.iloc[1914].iloc[0], 3951475160, 8, 124.27112, 61.668064 )","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.150231Z","iopub.execute_input":"2024-09-27T20:41:24.15056Z","iopub.status.idle":"2024-09-27T20:41:24.169834Z","shell.execute_reply.started":"2024-09-27T20:41:24.150525Z","shell.execute_reply":"2024-09-27T20:41:24.168852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#T = df_scs_l1_l2\n#def update_entry(df_scs_l1_l2, study_id, series_id, new_instance_number, new_x, new_y):\n    \n#    mask = (df_scs_l1_l2['study_id'] == study_id) & (df_scs_l1_l2['series_id'] == series_id)\n#    index = df_scs_l1_l2.index[mask].tolist()  #\n\n#    if len(index) != 1:\n#        raise ValueError(f\"No single entry was found for study_id {study_id} ans series_id {series_id}\")\n\n    # Update the values of 'instance_number', 'x' and 'y'\n#    df_scs_l1_l2.loc[index[0], 'instance_number'] = new_instance_number\n#    df_scs_l1_l2.loc[index[0], 'x'] = new_x\n#    df_scs_l1_l2.loc[index[0], 'y'] = new_y\n#if __name__ == '__main__':\n#    update_entry(df_scs_l1_l2, T.iloc[1286].iloc[0], T.iloc[1286].iloc[1], 11, 214.2267412458147, 109.61395454406738)\n#    update_entry(df_scs_l1_l2, T.iloc[626].iloc[0], T.iloc[626].iloc[1], 9, 246.69515991210938, 126.09215545654297)\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.172951Z","iopub.execute_input":"2024-09-27T20:41:24.173618Z","iopub.status.idle":"2024-09-27T20:41:24.181032Z","shell.execute_reply.started":"2024-09-27T20:41:24.173574Z","shell.execute_reply":"2024-09-27T20:41:24.180115Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_scs_l1_l2 = train.loc[:,['study_id','spinal_canal_stenosis_l1_l2']]\ndf_scs_l1_l2 = pd.merge( df_scs_l1_l2,train_scs_l1_l2, on='study_id', how='inner')\ndf_scs_l1_l2","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.182215Z","iopub.execute_input":"2024-09-27T20:41:24.182527Z","iopub.status.idle":"2024-09-27T20:41:24.207234Z","shell.execute_reply.started":"2024-09-27T20:41:24.182484Z","shell.execute_reply":"2024-09-27T20:41:24.206307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.7.2 spinal_canal_stenosis_l2_l3","metadata":{}},{"cell_type":"markdown","source":"Based on the above, let's now do the corresponding thing for the 'L2/L3' level","metadata":{}},{"cell_type":"code","source":"df_scs_l2_l3 = train_merge_T2[train_merge_T2.level == 'L2/L3']\ndf_scs_l2_l3 = df_scs_l2_l3.reset_index(drop=True)\n\nscs_l2_l3 = [2232794498, 1133158151, 1868615696, 2256339732, 2548543893, 1292979992, 3637444890, 4072455711, 2297295777, \n             893250212, 1722539301, 2279142182, 434488359, 2336516775, 390498354, 1567179188, 4232806580, 934686772, \n             2615694902, 1047914296, 2239199413, 3221995449, 3294654272, 3084269121, 3537214277, 376723024, 1187463765, \n             3674744025, 1613634521, 2213304029, 3525503074, 1557387235, 3850173026, 2566719718, 293713262, 2907745008, \n             2040217841, 3167888497, 1431195383, 3711891194, 3824720894]\n\ndf_scs_l2_l3_sd = train_series_descriptions[train_series_descriptions.series_description == 'Sagittal T2/STIR'].reset_index(drop=True)\nscs_l2_l3_sd = []\nfor i in scs_l2_l3:\n    scs_sd = df_scs_l2_l3_sd[df_scs_l2_l3_sd.study_id == i]\n    scs_sd = scs_sd.iloc[0,1]\n    scs_l2_l3_sd.append(scs_sd)\n    \n# Calculate total number of rows\ntotal_rows = len(df_scs_l2_l3) + len(scs_l2_l3)\n\n# Add missing rows to all columns\ndf_scs_l2_l3 = df_scs_l2_l3.reindex(range(total_rows))\n\n# Add the values to column 'study_id'\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 0] = scs_l2_l3\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 1] = scs_l2_l3_sd\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 2] = np.nan\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 3] = 'Spinal Canal Stenosis'\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 4] = 'L2/L3'\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 5] = np.nan\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 6] = np.nan\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 7] = 'Sagittal T2/STIR'\n\ndf_scs_l2_l3['study_id'] = df_scs_l2_l3['study_id'].astype(int)\ndf_scs_l2_l3['series_id'] = df_scs_l2_l3['series_id'].astype(int)\n\ndf_scs_l2_l3_insNum = df_scs_l2_l3.loc[:,['study_id','series_id','instance_number']]\n\n# Impute missing data\ndf_scs_l2_l3_insNum_imputed = imputer.fit_transform(df_scs_l2_l3_insNum)\n\n# Show imputed data\ndf_scs_l2_l3_insNum_imputed = df_scs_l2_l3_insNum_imputed.astype(int)\ndf_scs_l2_l3_insNum_imputed = pd.DataFrame(df_scs_l2_l3_insNum_imputed)\n\n# we add the imputed data to the database\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 2] = df_scs_l2_l3_insNum_imputed.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):,2]\ndf_scs_l2_l3['instance_number'] = df_scs_l2_l3['instance_number'].astype(int)\n\ndf_scs_l2_l3_imp_coordinates = df_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):,]\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Save original size\n        original_size = img.shape[:2]  # (height, width)\n        \n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        \n        # Apply transformation\n        resized_image = transform(img)\n        \n        # Calculate scale factors\n        width_ratio = original_size[1] / 224\n        height_ratio = original_size[0] / 224\n        \n        return resized_image, original_size, (width_ratio, height_ratio)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None, None, None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None, None, None\n\n# Function to make a prediction and return scaled coordinates\ndef predict(image_path, model, device):\n    model.eval()\n    image, original_size, (width_ratio, height_ratio) = load_and_preprocess_dicom(image_path)\n    if image is None:\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        coordinates = output.cpu().numpy().flatten()  # Flatten the output for easier access\n\n        # Scale the coordinates back to original size\n        scaled_coordinates = []\n        for i in range(0, len(coordinates), 2):\n            x = coordinates[i] \n            y = coordinates[i + 1] \n            x_rescaled = x * width_ratio\n            y_rescaled = y * height_ratio\n            scaled_coordinates.append((x_rescaled, y_rescaled))\n        \n        return scaled_coordinates\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Load the database\ndf = df_scs_l2_l3_imp_coordinates\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\n# Obtain image paths from DataFrame\nimage_files = df.apply(lambda row: os.path.join(image_dir, f\"{row['study_id']}/{row['series_id']}/{row['instance_number']}.dcm\"), axis=1).tolist()\n\n# Initialize lists to store results\nresults = []\n\nfor image_path in image_files:\n    try:\n        scaled_coordinates = predict(image_path, model, device)\n        if scaled_coordinates is not None and len(scaled_coordinates) > 1:\n            results.append({\n                'study_id': image_path.split('/')[-3],\n                'series_id': image_path.split('/')[-2],\n                'L23x': scaled_coordinates[1][0],  \n                'L23y': scaled_coordinates[1][1]   \n            })\n    except Exception as e:\n        print(f\"Error processing image {image_path}: {e}\")\n\ndf_results = pd.DataFrame(results)\n#print(df_results)\n\n# Now add this coordinates to the dataframe df_scs_l1_l2\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 5] = df_results['L23x']\ndf_scs_l2_l3.iloc[len(df_scs_l2_l3)-len(scs_l2_l3):, 6] = df_results['L23y']\n\ndf_scs_l2_l3","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:24.210026Z","iopub.execute_input":"2024-09-27T20:41:24.210339Z","iopub.status.idle":"2024-09-27T20:41:26.885307Z","shell.execute_reply.started":"2024-09-27T20:41:24.210305Z","shell.execute_reply":"2024-09-27T20:41:26.884182Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's view the results and notice that the image /3637444890/3892989905/8.dcm is incorrect and as in the previous case, apply a correction","metadata":{}},{"cell_type":"code","source":"# Define the SpineNet class using ResNet50\nclass SpineNet(nn.Module):\n    def __init__(self, pretrained=True):\n        super(SpineNet, self).__init__()\n        self.backbone = resnet50(weights='DEFAULT' if pretrained else None)\n        self.backbone.fc = nn.Linear(self.backbone.fc.in_features, 10)\n\n    def forward(self, x):\n        return self.backbone(x)\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        return transform(img)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None\n\n# Function to make a prediction\ndef predict(image_path, model, device):\n    model.eval()\n    image = load_and_preprocess_dicom(image_path)\n    if image is None:\n        print(\"Failed to load and preprocess the image.\")\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        return output.cpu().numpy().flatten()  # Flatten the output for easier access\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Function to plot resized image and draw predicted rectangles\ndef plot_with_predictions(image_path, coordinates):\n    # Load the image for display\n    ds = pydicom.dcmread(image_path)\n    img = ds.pixel_array\n    img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n    # Convert to float32 and normalize for visualization\n    img = img.astype(np.float32)\n    img = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n    img = img.astype(np.uint8)\n\n    # Convert image to PIL and resize\n    img_pil = Image.fromarray(img)\n    img_resized = img_pil.resize((224, 224))\n\n    fig, ax = plt.subplots(1, figsize=(4, 4))\n    ax.imshow(img_resized)\n\n    # Draw rectangles centered at each predicted coordinate\n    for i in range(0, len(coordinates), 2):\n        x_center = coordinates[i]\n        y_center = coordinates[i + 1]\n        rect = patches.Rectangle((x_center - 0, y_center - 0), 5, 5, linewidth=2, edgecolor='r', facecolor='none')\n        ax.add_patch(rect)\n\n    plt.axis('on')\n    plt.show()\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Load the database\ndf = df_scs_l2_l3_imp_coordinates\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\n# Obtain image paths from DataFrame\nimage_files = df.apply(lambda row: os.path.join(image_dir, f\"{row['study_id']}/{row['series_id']}/{row['instance_number']}.dcm\"), axis=1).tolist()\n\ndef process_and_visualize_image(index):\n    try:\n        image_path = image_files[index]  # Complete image path\n        outputs = predict(image_path, model, device)\n        print(f\"Predictions for image {image_path}: {outputs}\")\n        plot_with_predictions(image_path, outputs)\n    except Exception as e:\n        print(f\"Error processing image {image_path}: {e}\")\n\n# Interact for visualization\ninteract(process_and_visualize_image, index=IntSlider(min=0, max=len(image_files)-1, step=1, value=0))\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:26.887396Z","iopub.execute_input":"2024-09-27T20:41:26.888034Z","iopub.status.idle":"2024-09-27T20:41:27.7911Z","shell.execute_reply.started":"2024-09-27T20:41:26.887991Z","shell.execute_reply":"2024-09-27T20:41:27.790191Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":" image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/1820866003/131094096/7.dcm'\n#model_path = '/kaggle/working/spinenet50_model.pth'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:27.792169Z","iopub.execute_input":"2024-09-27T20:41:27.792476Z","iopub.status.idle":"2024-09-27T20:41:27.839057Z","shell.execute_reply.started":"2024-09-27T20:41:27.792444Z","shell.execute_reply":"2024-09-27T20:41:27.838099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":" print(df_scs_l2_l3[df_scs_l2_l3.series_id == 131094096])","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:27.840324Z","iopub.execute_input":"2024-09-27T20:41:27.840715Z","iopub.status.idle":"2024-09-27T20:41:27.850207Z","shell.execute_reply.started":"2024-09-27T20:41:27.840654Z","shell.execute_reply":"2024-09-27T20:41:27.849037Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3892989905/8.dcm'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')\n\ndf_scs_l2_l3[df_scs_l2_l3.study_id == 3637444890]\n","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:27.851365Z","iopub.execute_input":"2024-09-27T20:41:27.852141Z","iopub.status.idle":"2024-09-27T20:41:27.893709Z","shell.execute_reply.started":"2024-09-27T20:41:27.852103Z","shell.execute_reply":"2024-09-27T20:41:27.892657Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"T2 = df_scs_l2_l3\n\nif __name__ == '__main__':\n    update_entry(df_scs_l2_l3, T2.iloc[808].iloc[0], 131094096, 7, 273.9358956473214, 173.546142578125)\n    update_entry(df_scs_l2_l3, T2.iloc[1939].iloc[0], 3892989905, 8, 110.16017, 106.5738) ","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:27.894947Z","iopub.execute_input":"2024-09-27T20:41:27.895249Z","iopub.status.idle":"2024-09-27T20:41:27.904112Z","shell.execute_reply.started":"2024-09-27T20:41:27.895215Z","shell.execute_reply":"2024-09-27T20:41:27.903053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_scs_l2_l3 = train.loc[:,['study_id','spinal_canal_stenosis_l2_l3']]\ndf_scs_l2_l3 = pd.merge( df_scs_l2_l3,train_scs_l2_l3, on='study_id', how='inner')\ndf_scs_l2_l3","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:27.905386Z","iopub.execute_input":"2024-09-27T20:41:27.905748Z","iopub.status.idle":"2024-09-27T20:41:27.928253Z","shell.execute_reply.started":"2024-09-27T20:41:27.905712Z","shell.execute_reply":"2024-09-27T20:41:27.927311Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.7.3 spinal_canal_stenosis_l3_l4","metadata":{}},{"cell_type":"markdown","source":"We follow the same process outlined in the previous cases for the 'L3/L4' level.","metadata":{}},{"cell_type":"code","source":"df_scs_l3_l4 = train_merge_T2[train_merge_T2.level == 'L3/L4']\ndf_scs_l3_l4 = df_scs_l3_l4.reset_index(drop=True)\n\nscs_l3_l4 = [3294654272, 3637444890]\n\ndf_scs_l3_l4_sd = train_series_descriptions[train_series_descriptions.series_description == 'Sagittal T2/STIR'].reset_index(drop=True)\nscs_l3_l4_sd = []\nfor i in scs_l3_l4:\n    scs_sd = df_scs_l3_l4_sd[df_scs_l3_l4_sd.study_id == i]\n    scs_sd = scs_sd.iloc[0,1]\n    scs_l3_l4_sd.append(scs_sd)\n    \n# Calculate total number of rows\ntotal_rows = len(df_scs_l3_l4) + len(scs_l3_l4)\n\n# Add missing rows to all columns\ndf_scs_l3_l4 = df_scs_l3_l4.reindex(range(total_rows))\n\n# Add the values to column 'study_id'\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 0] = scs_l3_l4\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 1] = scs_l3_l4_sd\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 2] = np.nan\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 3] = 'Spinal Canal Stenosis'\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 4] = 'L3/L4'\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 5] = np.nan\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 6] = np.nan\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 7] = 'Sagittal T2/STIR'\n\ndf_scs_l3_l4['study_id'] = df_scs_l3_l4['study_id'].astype(int)\ndf_scs_l3_l4['series_id'] = df_scs_l3_l4['series_id'].astype(int)\n\ndf_scs_l3_l4_insNum = df_scs_l3_l4.loc[:,['study_id','series_id','instance_number']]\n\n# Impute missing data\ndf_scs_l3_l4_insNum_imputed = imputer.fit_transform(df_scs_l3_l4_insNum)\n\n# Show imputed data\ndf_scs_l3_l4_insNum_imputed = df_scs_l3_l4_insNum_imputed.astype(int)\ndf_scs_l3_l4_insNum_imputed = pd.DataFrame(df_scs_l3_l4_insNum_imputed)\n\n# we add the imputed data to the database\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 2] = df_scs_l3_l4_insNum_imputed.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):,2]\ndf_scs_l3_l4['instance_number'] = df_scs_l3_l4['instance_number'].astype(int)\n\ndf_scs_l3_l4_imp_coordinates = df_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):,]\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Save original size\n        original_size = img.shape[:2]  # (height, width)\n        \n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        \n        # Apply transformation\n        resized_image = transform(img)\n        \n        # Calculate scale factors\n        width_ratio = original_size[1] / 224\n        height_ratio = original_size[0] / 224\n        \n        return resized_image, original_size, (width_ratio, height_ratio)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None, None, None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None, None, None\n\n# Function to make a prediction and return scaled coordinates\ndef predict(image_path, model, device):\n    model.eval()\n    image, original_size, (width_ratio, height_ratio) = load_and_preprocess_dicom(image_path)\n    if image is None:\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        coordinates = output.cpu().numpy().flatten()  # Flatten the output for easier access\n\n        # Scale the coordinates back to original size\n        scaled_coordinates = []\n        for i in range(0, len(coordinates), 2):\n            x = coordinates[i] \n            y = coordinates[i + 1] \n            x_rescaled = x * width_ratio\n            y_rescaled = y * height_ratio\n            scaled_coordinates.append((x_rescaled, y_rescaled))\n        \n        return scaled_coordinates\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Load the database\ndf = df_scs_l3_l4_imp_coordinates\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\n# Obtain image paths from DataFrame\nimage_files = df.apply(lambda row: os.path.join(image_dir, f\"{row['study_id']}/{row['series_id']}/{row['instance_number']}.dcm\"), axis=1).tolist()\n\n# Initialize lists to store results\nresults = []\n\n\nfor image_path in image_files:\n    try:\n        scaled_coordinates = predict(image_path, model, device)\n        if scaled_coordinates is not None and len(scaled_coordinates) > 1:\n            results.append({\n                'study_id': image_path.split('/')[-3],\n                'series_id': image_path.split('/')[-2],\n                'L34x': scaled_coordinates[2][0],  \n                'L34y': scaled_coordinates[2][1]   \n            })\n    except Exception as e:\n        print(f\"Error procesando la imagen {image_path}: {e}\")\n\ndf_results = pd.DataFrame(results)\n#print(df_results)\n\n# Now add this coordinates to the dataframe \ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 5] = df_results['L34x']\ndf_scs_l3_l4.iloc[len(df_scs_l3_l4)-len(scs_l3_l4):, 6] = df_results['L34y']\n\ndf_scs_l3_l4","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:27.929535Z","iopub.execute_input":"2024-09-27T20:41:27.929846Z","iopub.status.idle":"2024-09-27T20:41:28.627624Z","shell.execute_reply.started":"2024-09-27T20:41:27.929813Z","shell.execute_reply":"2024-09-27T20:41:28.626694Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3892989905/8.dcm'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')\n\ndf_scs_l3_l4[df_scs_l3_l4.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:28.629052Z","iopub.execute_input":"2024-09-27T20:41:28.629389Z","iopub.status.idle":"2024-09-27T20:41:28.667136Z","shell.execute_reply.started":"2024-09-27T20:41:28.629357Z","shell.execute_reply":"2024-09-27T20:41:28.666197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"T2 = df_scs_l3_l4\n\nif __name__ == '__main__':\n    update_entry(df_scs_l3_l4, T2.iloc[1973].iloc[0], 3892989905, 8, 153.2284872872489, 168.61812046595983) ","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:28.668647Z","iopub.execute_input":"2024-09-27T20:41:28.669021Z","iopub.status.idle":"2024-09-27T20:41:28.675423Z","shell.execute_reply.started":"2024-09-27T20:41:28.66898Z","shell.execute_reply":"2024-09-27T20:41:28.674366Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_scs_l3_l4 = train.loc[:,['study_id','spinal_canal_stenosis_l3_l4']]\ndf_scs_l3_l4 = pd.merge( df_scs_l3_l4,train_scs_l3_l4, on='study_id', how='inner')\ndf_scs_l3_l4","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:28.676965Z","iopub.execute_input":"2024-09-27T20:41:28.677297Z","iopub.status.idle":"2024-09-27T20:41:28.698945Z","shell.execute_reply.started":"2024-09-27T20:41:28.677263Z","shell.execute_reply":"2024-09-27T20:41:28.698025Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.7.4 spinal_canal_stenosis_l4_l5","metadata":{}},{"cell_type":"markdown","source":"We follow the development of previous cases","metadata":{}},{"cell_type":"code","source":"df_scs_l4_l5 = train_merge_T2[train_merge_T2.level == 'L4/L5']\ndf_scs_l4_l5 = df_scs_l4_l5.reset_index(drop=True)\n\nscs_l4_l5 = [3294654272, 3637444890, 665627263]\n\ndf_scs_l4_l5_sd = train_series_descriptions[train_series_descriptions.series_description == 'Sagittal T2/STIR'].reset_index(drop=True)\nscs_l4_l5_sd = []\nfor i in scs_l4_l5:\n    scs_sd = df_scs_l4_l5_sd[df_scs_l4_l5_sd.study_id == i]\n    scs_sd = scs_sd.iloc[0,1]\n    scs_l4_l5_sd.append(scs_sd)\n    \n# Calculate total number of rows\ntotal_rows = len(df_scs_l4_l5) + len(scs_l4_l5)\n\n# Add missing rows to all columns\ndf_scs_l4_l5 = df_scs_l4_l5.reindex(range(total_rows))\n\n# Add the values to column 'study_id'\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 0] = scs_l4_l5\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 1] = scs_l4_l5_sd\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 2] = np.nan\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 3] = 'Spinal Canal Stenosis'\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 4] = 'L4/L5'\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 5] = np.nan\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 6] = np.nan\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 7] = 'Sagittal T2/STIR'\n\ndf_scs_l4_l5['study_id'] = df_scs_l4_l5['study_id'].astype(int)\ndf_scs_l4_l5['series_id'] = df_scs_l4_l5['series_id'].astype(int)\n\ndf_scs_l4_l5_insNum = df_scs_l4_l5.loc[:,['study_id','series_id','instance_number']]\n\n# Impute missing data\ndf_scs_l4_l5_insNum_imputed = imputer.fit_transform(df_scs_l4_l5_insNum)\n\n# Show imputed data\ndf_scs_l4_l5_insNum_imputed = df_scs_l4_l5_insNum_imputed.astype(int)\ndf_scs_l4_l5_insNum_imputed = pd.DataFrame(df_scs_l4_l5_insNum_imputed)\n\n# we add the imputed data to the database\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 2] = df_scs_l4_l5_insNum_imputed.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):,2]\ndf_scs_l4_l5['instance_number'] = df_scs_l4_l5['instance_number'].astype(int)\n\ndf_scs_l4_l5_imp_coordinates = df_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):,]\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Save original size\n        original_size = img.shape[:2]  # (height, width)\n        \n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        \n        # Apply transformation\n        resized_image = transform(img)\n        \n        # Calculate scale factors\n        width_ratio = original_size[1] / 224\n        height_ratio = original_size[0] / 224\n        \n        return resized_image, original_size, (width_ratio, height_ratio)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None, None, None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None, None, None\n\n# Function to make a prediction and return scaled coordinates\ndef predict(image_path, model, device):\n    model.eval()\n    image, original_size, (width_ratio, height_ratio) = load_and_preprocess_dicom(image_path)\n    if image is None:\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        coordinates = output.cpu().numpy().flatten()  # Flatten the output for easier access\n\n        # Scale the coordinates back to original size\n        scaled_coordinates = []\n        for i in range(0, len(coordinates), 2):\n            x = coordinates[i] \n            y = coordinates[i + 1] \n            x_rescaled = x * width_ratio\n            y_rescaled = y * height_ratio\n            scaled_coordinates.append((x_rescaled, y_rescaled))\n        \n        return scaled_coordinates\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Load the database\ndf = df_scs_l4_l5_imp_coordinates\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\n# Obtain image paths from DataFrame\nimage_files = df.apply(lambda row: os.path.join(image_dir, f\"{row['study_id']}/{row['series_id']}/{row['instance_number']}.dcm\"), axis=1).tolist()\n\n# Initialize lists to store results\nresults = []\n\n# Predecir y escalar las coordenadas para las imágenes\nfor image_path in image_files:\n    try:\n        scaled_coordinates = predict(image_path, model, device)\n        if scaled_coordinates is not None and len(scaled_coordinates) > 1:\n            results.append({\n                'study_id': image_path.split('/')[-3],\n                'series_id': image_path.split('/')[-2],\n                'L45x': scaled_coordinates[3][0], \n                'L45y': scaled_coordinates[3][1]   \n            })\n    except Exception as e:\n        print(f\"Error procesando la imagen {image_path}: {e}\")\n\n# Crear DataFrame con los resultados\ndf_results = pd.DataFrame(results)\n#print(df_results)\n\n# Now add this coordinates to the dataframe \ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 5] = df_results['L45x']\ndf_scs_l4_l5.iloc[len(df_scs_l4_l5)-len(scs_l4_l5):, 6] = df_results['L45y']\n\ndf_scs_l4_l5","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:28.70452Z","iopub.execute_input":"2024-09-27T20:41:28.704852Z","iopub.status.idle":"2024-09-27T20:41:29.517098Z","shell.execute_reply.started":"2024-09-27T20:41:28.704818Z","shell.execute_reply":"2024-09-27T20:41:29.516106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3892989905/8.dcm'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')\n\ndf_scs_l4_l5[df_scs_l4_l5.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:29.518888Z","iopub.execute_input":"2024-09-27T20:41:29.519336Z","iopub.status.idle":"2024-09-27T20:41:29.56024Z","shell.execute_reply.started":"2024-09-27T20:41:29.519289Z","shell.execute_reply":"2024-09-27T20:41:29.559193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"T2 = df_scs_l4_l5\n\nif __name__ == '__main__':\n    update_entry(df_scs_l4_l5, T2.iloc[1972].iloc[0], 3892989905, 8, 113.44343,135.64023) ","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:29.561757Z","iopub.execute_input":"2024-09-27T20:41:29.562203Z","iopub.status.idle":"2024-09-27T20:41:29.570336Z","shell.execute_reply.started":"2024-09-27T20:41:29.562149Z","shell.execute_reply":"2024-09-27T20:41:29.569128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_scs_l4_l5 = train.loc[:,['study_id','spinal_canal_stenosis_l4_l5']]\ndf_scs_l4_l5 = pd.merge( df_scs_l4_l5,train_scs_l4_l5, on='study_id', how='inner')\ndf_scs_l4_l5","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:29.571753Z","iopub.execute_input":"2024-09-27T20:41:29.572265Z","iopub.status.idle":"2024-09-27T20:41:29.595045Z","shell.execute_reply.started":"2024-09-27T20:41:29.572215Z","shell.execute_reply":"2024-09-27T20:41:29.593961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.7.5 spinal_canal_stenosis_l5_S1","metadata":{}},{"cell_type":"code","source":"df_scs_l5_s1 = train_merge_T2[train_merge_T2.level == 'L5/S1']\ndf_scs_l5_s1 = df_scs_l5_s1.reset_index(drop=True)\n\nscs_l5_s1 = [3294654272, 3225351618, 3024532039, 3637444890, 665627263]\n\ndf_scs_l5_s1_sd = train_series_descriptions[train_series_descriptions.series_description == 'Sagittal T2/STIR'].reset_index(drop=True)\nscs_l5_s1_sd = []\nfor i in scs_l5_s1:\n    scs_sd = df_scs_l5_s1_sd[df_scs_l5_s1_sd.study_id == i]\n    scs_sd = scs_sd.iloc[0,1]\n    scs_l5_s1_sd.append(scs_sd)\n    \n# Calculate total number of rows\ntotal_rows = len(df_scs_l5_s1) + len(scs_l5_s1)\n\n# Add missing rows to all columns\ndf_scs_l5_s1= df_scs_l5_s1.reindex(range(total_rows))\n\n# Add the values to column 'study_id'\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 0] = scs_l5_s1\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 1] = scs_l5_s1_sd\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 2] = np.nan\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 3] = 'Spinal Canal Stenosis'\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 4] = 'L5/S1'\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 5] = np.nan\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 6] = np.nan\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 7] = 'Sagittal T2/STIR'\n\ndf_scs_l5_s1['study_id'] = df_scs_l5_s1['study_id'].astype(int)\ndf_scs_l5_s1['series_id'] = df_scs_l5_s1['series_id'].astype(int)\n\ndf_scs_l5_s1_insNum = df_scs_l5_s1.loc[:,['study_id','series_id','instance_number']]\n\n# Impute missing data\ndf_scs_l5_s1_insNum_imputed = imputer.fit_transform(df_scs_l5_s1_insNum)\n\n# Show imputed data\ndf_scs_l5_s1_insNum_imputed = df_scs_l5_s1_insNum_imputed.astype(int)\ndf_scs_l5_s1_insNum_imputed = pd.DataFrame(df_scs_l5_s1_insNum_imputed)\n\n# we add the imputed data to the database\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 2] = df_scs_l5_s1_insNum_imputed.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):,2]\ndf_scs_l5_s1['instance_number'] = df_scs_l5_s1['instance_number'].astype(int)\n\ndf_scs_l5_s1_imp_coordinates = df_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):,]\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Save original size\n        original_size = img.shape[:2]  # (height, width)\n        \n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        \n        # Apply transformation\n        resized_image = transform(img)\n        \n        # Calculate scale factors\n        width_ratio = original_size[1] / 224\n        height_ratio = original_size[0] / 224\n        \n        return resized_image, original_size, (width_ratio, height_ratio)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None, None, None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None, None, None\n\n# Function to make a prediction and return scaled coordinates\ndef predict(image_path, model, device):\n    model.eval()\n    image, original_size, (width_ratio, height_ratio) = load_and_preprocess_dicom(image_path)\n    if image is None:\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        coordinates = output.cpu().numpy().flatten()  # Flatten the output for easier access\n\n        # Scale the coordinates back to original size\n        scaled_coordinates = []\n        for i in range(0, len(coordinates), 2):\n            x = coordinates[i] \n            y = coordinates[i + 1] \n            x_rescaled = x * width_ratio\n            y_rescaled = y * height_ratio\n            scaled_coordinates.append((x_rescaled, y_rescaled))\n        \n        return scaled_coordinates\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Load the database\ndf = df_scs_l5_s1_imp_coordinates\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\n\n# Obtain image paths from DataFrame\nimage_files = df.apply(lambda row: os.path.join(image_dir, f\"{row['study_id']}/{row['series_id']}/{row['instance_number']}.dcm\"), axis=1).tolist()\n\n# Initialize lists to store results\nresults = []\n\nfor image_path in image_files:\n    try:\n        scaled_coordinates = predict(image_path, model, device)\n        if scaled_coordinates is not None and len(scaled_coordinates) > 1:\n            results.append({\n                'study_id': image_path.split('/')[-3],\n                'series_id': image_path.split('/')[-2],\n                'L51x': scaled_coordinates[4][0],  \n                'L51y': scaled_coordinates[4][1]   \n            })\n    except Exception as e:\n        print(f\"Error procesando la imagen {image_path}: {e}\")\n\ndf_results = pd.DataFrame(results)\n#print(df_results)\n\n# Now add this coordinates to the dataframe \ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 5] = df_results['L51x']\ndf_scs_l5_s1.iloc[len(df_scs_l5_s1)-len(scs_l5_s1):, 6] = df_results['L51y']\n\ndf_scs_l5_s1","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:29.596314Z","iopub.execute_input":"2024-09-27T20:41:29.596604Z","iopub.status.idle":"2024-09-27T20:41:30.483866Z","shell.execute_reply.started":"2024-09-27T20:41:29.596571Z","shell.execute_reply":"2024-09-27T20:41:30.48285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/3637444890/3892989905/8.dcm'\n\n# Get the predicted coordinates\npredicted_coordinates = predict(image_path, model, device)\nprint(f'Predicted coordinates: {predicted_coordinates}')\n\ndf_scs_l5_s1[df_scs_l5_s1.study_id == 3637444890]","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:30.485265Z","iopub.execute_input":"2024-09-27T20:41:30.48561Z","iopub.status.idle":"2024-09-27T20:41:30.524521Z","shell.execute_reply.started":"2024-09-27T20:41:30.485576Z","shell.execute_reply":"2024-09-27T20:41:30.52353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"T2 = df_scs_l5_s1\n\nif __name__ == '__main__':\n    update_entry(df_scs_l5_s1, T2.iloc[1972].iloc[0], 3892989905, 8, 113.44343,135.64023) ","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:30.525777Z","iopub.execute_input":"2024-09-27T20:41:30.526091Z","iopub.status.idle":"2024-09-27T20:41:30.532601Z","shell.execute_reply.started":"2024-09-27T20:41:30.526057Z","shell.execute_reply":"2024-09-27T20:41:30.531535Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_scs_l5_s1 = train.loc[:,['study_id','spinal_canal_stenosis_l5_s1']]\ndf_scs_l5_s1 = pd.merge( df_scs_l5_s1,train_scs_l5_s1, on='study_id', how='inner')\ndf_scs_l5_s1","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:30.533978Z","iopub.execute_input":"2024-09-27T20:41:30.534288Z","iopub.status.idle":"2024-09-27T20:41:30.55907Z","shell.execute_reply.started":"2024-09-27T20:41:30.534255Z","shell.execute_reply":"2024-09-27T20:41:30.558175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.8 Pain prediction","metadata":{}},{"cell_type":"markdown","source":"The objective is to design five specialized models focused on each of the vertebrae and a general model that integrates all the images. From this we will design a model that makes two evaluations: one where each vertebra is evaluated using the specialized or local model and another the general one. Based on these evaluations, make a weighting.\n\nPrimero segmentamos las imágenes. Combinamos las bases de datos de datos para segmentar todas las imágenes. ","metadata":{}},{"cell_type":"code","source":"df_scs_l1_l2 = pd.merge(df_scs_l1_l2,train[['study_id','spinal_canal_stenosis_l1_l2']])\ndf_scs_l2_l3 = pd.merge(df_scs_l2_l3,train[['study_id','spinal_canal_stenosis_l2_l3']])\ndf_scs_l3_l4 = pd.merge(df_scs_l3_l4,train[['study_id','spinal_canal_stenosis_l3_l4']])\ndf_scs_l4_l5 = pd.merge(df_scs_l4_l5,train[['study_id','spinal_canal_stenosis_l4_l5']])\ndf_scs_l5_s1 = pd.merge(df_scs_l5_s1,train[['study_id','spinal_canal_stenosis_l5_s1']])","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:30.560261Z","iopub.execute_input":"2024-09-27T20:41:30.560561Z","iopub.status.idle":"2024-09-27T20:41:30.587802Z","shell.execute_reply.started":"2024-09-27T20:41:30.560528Z","shell.execute_reply":"2024-09-27T20:41:30.586918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's make a change in the nomenclature","metadata":{}},{"cell_type":"code","source":"df_scs_l1_l2['level'] = df_scs_l1_l2['level'].str.replace('L1/L2', 'l1_l2')\ndf_scs_l2_l3['level'] = df_scs_l2_l3['level'].str.replace('L2/L3', 'l2_l3')\ndf_scs_l3_l4['level'] = df_scs_l3_l4['level'].str.replace('L3/L4', 'l3_l4')\ndf_scs_l4_l5['level'] = df_scs_l4_l5['level'].str.replace('L4/L5', 'l4_l5')\ndf_scs_l5_s1['level'] = df_scs_l5_s1['level'].str.replace('L5/S1', 'l5_s1')\n\ndf_scs_l1_l2.to_csv('train_merge_spinal_canal_stenosis_l1_l2.csv')\ndf_scs_l2_l3.to_csv('train_merge_spinal_canal_stenosis_l2_l3.csv')\ndf_scs_l3_l4.to_csv('train_merge_spinal_canal_stenosis_l3_l4.csv')\ndf_scs_l4_l5.to_csv('train_merge_spinal_canal_stenosis_l4_l5.csv')\ndf_scs_l5_s1.to_csv('train_merge_spinal_canal_stenosis_l5_s1.csv')","metadata":{"execution":{"iopub.status.busy":"2024-09-27T20:41:30.590778Z","iopub.execute_input":"2024-09-27T20:41:30.591053Z","iopub.status.idle":"2024-09-27T20:41:30.707476Z","shell.execute_reply.started":"2024-09-27T20:41:30.591022Z","shell.execute_reply":"2024-09-27T20:41:30.706699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's do the segmentations with respect to the 'L1/L2, L2/L3, L3/L4, L4/L5, L5/S1' levels and save the segmented images","metadata":{}},{"cell_type":"code","source":"input_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\noutput_base_path = '/kaggle/working/train_ima_scsl1l2'\noutput_csv_path = '/kaggle/working/train_merge_spinal_canal_stenosis_l1_l2_transf.csv'\n\nos.makedirs(output_base_path, exist_ok=True)\nnew_coordinates_list = []\n\ndef process_and_save_image(row):\n    try:\n        study_id = row['study_id']\n        series_id = row['series_id']\n        instance_number = row['instance_number']\n        x = int(row['x'])  \n        y = int(row['y']) \n        level = str(row['level'])\n        pain = str(row['spinal_canal_stenosis_l1_l2'])\n        \n        dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n        ds = pydicom.dcmread(dicom_path)\n        \n        img = ds.pixel_array        \n        img_normalized = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n        img_normalized = img_normalized.astype(np.uint8)        \n        img_pil = Image.fromarray(img_normalized)        \n        img_resized = img_pil.resize((224, 224), Image.LANCZOS)        \n        \n        scale_x = 224 / img.shape[1]\n        scale_y = 224 / img.shape[0]\n        new_x = int(x * scale_x)\n        new_y = int(y * scale_y)        \n        \n        img_rotated = img_resized.rotate(0, center=(new_x, new_y))        \n        img_rotated_np = np.array(img_rotated)\n        \n        x_min = int(max(0, new_x - 37-3))\n        x_max = int(min(224, new_x + 37-3))\n        y_min = int(max(0, new_y - 20))\n        y_max = int(min(224, new_y + 20))     \n        \n        img_cropped = img_rotated_np[y_min:y_max, x_min:x_max]        \n        img_cropped_pil = Image.fromarray(img_cropped)        \n        padding_color = 0  \n        padded_img = ImageOps.expand(img_cropped_pil, border=((224 - img_cropped.shape[1]) // 2, (224 - img_cropped.shape[0]) // 2), fill=padding_color)        \n        \n        output_path = os.path.join(output_base_path, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n        padded_img.save(output_path, \"JPEG\")        \n        new_coordinates_list.append([study_id, series_id, instance_number, pain, level, new_x, new_y])\n        #print(f'transformation of image save on {output_path}')\n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\nfor _, row in tqdm(df_scs_l1_l2.iterrows(), total=df_scs_l1_l2.shape[0]):\n    process_and_save_image(row)\n    \ndf_new_coordinates = pd.DataFrame(new_coordinates_list, columns=['study_id', 'series_id', 'instance_number', 'pain', 'level', 'x', 'y'])\ndf_new_coordinates.to_csv(output_csv_path, index=False)\nprint(f'The new coordinates have been saved in {output_csv_path}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\noutput_base_path = '/kaggle/working/train_ima_scsl2l3'\noutput_csv_path = '/kaggle/working/train_merge_spinal_canal_stenosis_l2_l3_transf.csv'\n\nos.makedirs(output_base_path, exist_ok=True)\nnew_coordinates_list = []\n\ndef process_and_save_image(row):\n    try:\n        study_id = row['study_id']\n        series_id = row['series_id']\n        instance_number = row['instance_number']\n        x = int(row['x'])  \n        y = int(row['y']) \n        level = str(row['level'])\n        pain = str(row['spinal_canal_stenosis_l2_l3'])\n        \n        dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n        ds = pydicom.dcmread(dicom_path)\n        \n        img = ds.pixel_array        \n        img_normalized = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n        img_normalized = img_normalized.astype(np.uint8)        \n        img_pil = Image.fromarray(img_normalized)        \n        img_resized = img_pil.resize((224, 224), Image.LANCZOS)        \n        \n        scale_x = 224 / img.shape[1]\n        scale_y = 224 / img.shape[0]\n        new_x = int(x * scale_x)\n        new_y = int(y * scale_y)        \n        \n        img_rotated = img_resized.rotate(0, center=(new_x, new_y))        \n        img_rotated_np = np.array(img_rotated)\n        \n        x_min = int(max(0, new_x - 37-3))\n        x_max = int(min(224, new_x + 37-3))\n        y_min = int(max(0, new_y - 20))\n        y_max = int(min(224, new_y + 20))     \n        \n        img_cropped = img_rotated_np[y_min:y_max, x_min:x_max]        \n        img_cropped_pil = Image.fromarray(img_cropped)        \n        padding_color = 0  \n        padded_img = ImageOps.expand(img_cropped_pil, border=((224 - img_cropped.shape[1]) // 2, (224 - img_cropped.shape[0]) // 2), fill=padding_color)        \n        \n        output_path = os.path.join(output_base_path, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n        padded_img.save(output_path, \"JPEG\")        \n        new_coordinates_list.append([study_id, series_id, instance_number, pain, level, new_x, new_y])\n        #print(f'transformation of image save on {output_path}')\n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\nfor _, row in tqdm(df_scs_l2_l3.iterrows(), total=df_scs_l2_l3.shape[0]):\n    process_and_save_image(row)\n    \ndf_new_coordinates = pd.DataFrame(new_coordinates_list, columns=['study_id', 'series_id', 'instance_number', 'pain', 'level', 'x', 'y'])\ndf_new_coordinates.to_csv(output_csv_path, index=False)\nprint(f'The new coordinates have been saved in {output_csv_path}')","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\noutput_base_path = '/kaggle/working/train_ima_scsl3l4'\noutput_csv_path = '/kaggle/working/train_merge_spinal_canal_stenosis_l3_l4_transf.csv'\n\nos.makedirs(output_base_path, exist_ok=True)\nnew_coordinates_list = []\n\ndef process_and_save_image(row):\n    try:\n        study_id = row['study_id']\n        series_id = row['series_id']\n        instance_number = row['instance_number']\n        x = int(row['x'])  \n        y = int(row['y']) \n        level = str(row['level'])\n        pain = str(row['spinal_canal_stenosis_l3_l4'])\n        \n        dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n        ds = pydicom.dcmread(dicom_path)\n        \n        img = ds.pixel_array        \n        img_normalized = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n        img_normalized = img_normalized.astype(np.uint8)        \n        img_pil = Image.fromarray(img_normalized)        \n        img_resized = img_pil.resize((224, 224), Image.LANCZOS)        \n        \n        scale_x = 224 / img.shape[1]\n        scale_y = 224 / img.shape[0]\n        new_x = int(x * scale_x)\n        new_y = int(y * scale_y)        \n        \n        img_rotated = img_resized.rotate(0, center=(new_x, new_y))        \n        img_rotated_np = np.array(img_rotated)\n        \n        x_min = int(max(0, new_x - 37-3))\n        x_max = int(min(224, new_x + 37-3))\n        y_min = int(max(0, new_y - 20))\n        y_max = int(min(224, new_y + 20))     \n        \n        img_cropped = img_rotated_np[y_min:y_max, x_min:x_max]        \n        img_cropped_pil = Image.fromarray(img_cropped)        \n        padding_color = 0  \n        padded_img = ImageOps.expand(img_cropped_pil, border=((224 - img_cropped.shape[1]) // 2, (224 - img_cropped.shape[0]) // 2), fill=padding_color)        \n        \n        output_path = os.path.join(output_base_path, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n        padded_img.save(output_path, \"JPEG\")        \n        new_coordinates_list.append([study_id, series_id, instance_number, pain, level, new_x, new_y])\n        #print(f'transformation of image save on {output_path}')\n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\nfor _, row in tqdm(df_scs_l3_l4.iterrows(), total=df_scs_l3_l4.shape[0]):\n    process_and_save_image(row)\n    \ndf_new_coordinates = pd.DataFrame(new_coordinates_list, columns=['study_id', 'series_id', 'instance_number', 'pain', 'level', 'x', 'y'])\ndf_new_coordinates.to_csv(output_csv_path, index=False)\nprint(f'The new coordinates have been saved in {output_csv_path}')","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\noutput_base_path = '/kaggle/working/train_ima_scsl4l5'\noutput_csv_path = '/kaggle/working/train_merge_spinal_canal_stenosis_l4_l5_transf.csv'\n\nos.makedirs(output_base_path, exist_ok=True)\nnew_coordinates_list = []\n\ndef process_and_save_image(row):\n    try:\n        study_id = row['study_id']\n        series_id = row['series_id']\n        instance_number = row['instance_number']\n        x = int(row['x'])  \n        y = int(row['y']) \n        level = str(row['level'])\n        pain = str(row['spinal_canal_stenosis_l4_l5'])\n        \n        dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n        ds = pydicom.dcmread(dicom_path)\n        \n        img = ds.pixel_array        \n        img_normalized = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n        img_normalized = img_normalized.astype(np.uint8)        \n        img_pil = Image.fromarray(img_normalized)        \n        img_resized = img_pil.resize((224, 224), Image.LANCZOS)        \n        \n        scale_x = 224 / img.shape[1]\n        scale_y = 224 / img.shape[0]\n        new_x = int(x * scale_x)\n        new_y = int(y * scale_y)        \n        \n        img_rotated = img_resized.rotate(0, center=(new_x, new_y))        \n        img_rotated_np = np.array(img_rotated)\n        \n        x_min = int(max(0, new_x - 37-3))\n        x_max = int(min(224, new_x + 37-3))\n        y_min = int(max(0, new_y - 20))\n        y_max = int(min(224, new_y + 20))     \n        \n        img_cropped = img_rotated_np[y_min:y_max, x_min:x_max]        \n        img_cropped_pil = Image.fromarray(img_cropped)        \n        padding_color = 0  \n        padded_img = ImageOps.expand(img_cropped_pil, border=((224 - img_cropped.shape[1]) // 2, (224 - img_cropped.shape[0]) // 2), fill=padding_color)        \n        \n        output_path = os.path.join(output_base_path, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n        padded_img.save(output_path, \"JPEG\")        \n        new_coordinates_list.append([study_id, series_id, instance_number, pain, level, new_x, new_y])\n        #print(f'transformation of image save on {output_path}')\n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\nfor _, row in tqdm(df_scs_l4_l5.iterrows(), total=df_scs_l4_l5.shape[0]):\n    process_and_save_image(row)\n    \ndf_new_coordinates = pd.DataFrame(new_coordinates_list, columns=['study_id', 'series_id', 'instance_number', 'pain', 'level', 'x', 'y'])\ndf_new_coordinates.to_csv(output_csv_path, index=False)\nprint(f'The new coordinates have been saved in {output_csv_path}')","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\noutput_base_path = '/kaggle/working/train_ima_scsl5s1'\noutput_csv_path = '/kaggle/working/train_merge_spinal_canal_stenosis_l5_s1_transf.csv'\n\nos.makedirs(output_base_path, exist_ok=True)\nnew_coordinates_list = []\n\ndef process_and_save_image(row):\n    try:\n        study_id = row['study_id']\n        series_id = row['series_id']\n        instance_number = row['instance_number']\n        x = int(row['x'])  \n        y = int(row['y']) \n        level = str(row['level'])\n        pain = str(row['spinal_canal_stenosis_l5_s1'])\n        \n        dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n        ds = pydicom.dcmread(dicom_path)\n        \n        img = ds.pixel_array        \n        img_normalized = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n        img_normalized = img_normalized.astype(np.uint8)        \n        img_pil = Image.fromarray(img_normalized)        \n        img_resized = img_pil.resize((224, 224), Image.LANCZOS)        \n        \n        scale_x = 224 / img.shape[1]\n        scale_y = 224 / img.shape[0]\n        new_x = int(x * scale_x)\n        new_y = int(y * scale_y)        \n        \n        img_rotated = img_resized.rotate(0, center=(new_x, new_y))        \n        img_rotated_np = np.array(img_rotated)\n        \n        x_min = int(max(0, new_x - 37-3))\n        x_max = int(min(224, new_x + 37-3))\n        y_min = int(max(0, new_y - 20))\n        y_max = int(min(224, new_y + 20))     \n        \n        img_cropped = img_rotated_np[y_min:y_max, x_min:x_max]        \n        img_cropped_pil = Image.fromarray(img_cropped)        \n        padding_color = 0  \n        padded_img = ImageOps.expand(img_cropped_pil, border=((224 - img_cropped.shape[1]) // 2, (224 - img_cropped.shape[0]) // 2), fill=padding_color)        \n        \n        output_path = os.path.join(output_base_path, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n        padded_img.save(output_path, \"JPEG\")        \n        new_coordinates_list.append([study_id, series_id, instance_number, pain, level, new_x, new_y])\n        #print(f'transformation of image save on {output_path}')\n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\nfor _, row in tqdm(df_scs_l5_s1.iterrows(), total=df_scs_l5_s1.shape[0]):\n    process_and_save_image(row)\n    \ndf_new_coordinates = pd.DataFrame(new_coordinates_list, columns=['study_id', 'series_id', 'instance_number', 'pain', 'level', 'x', 'y'])\ndf_new_coordinates.to_csv(output_csv_path, index=False)\nprint(f'The new coordinates have been saved in {output_csv_path}')","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"csv_files = [\n    'train_merge_spinal_canal_stenosis_l5_s1_transf.csv',\n    'train_merge_spinal_canal_stenosis_l4_l5_transf.csv',\n    'train_merge_spinal_canal_stenosis_l3_l4_transf.csv',\n    'train_merge_spinal_canal_stenosis_l2_l3_transf.csv',\n    'train_merge_spinal_canal_stenosis_l1_l2_transf.csv']\n\ncsv_dir = '/kaggle/working/'\n\ndfs = []\nfor file in csv_files:\n    df = pd.read_csv(os.path.join(csv_dir, file))\n    df['database'] = os.path.splitext(file)[0]  \n    dfs.append(df)\n\nmerged_df = pd.concat(dfs, ignore_index=True)\nmerged_df.to_csv('/kaggle/working/merged_dataset.csv', index=False)\nprint(f\"Combined DataFrame Dimensions: {merged_df.shape}\")\nmerged_df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We generate the database *images_merged_dataset* that has the labels associated with all levels","metadata":{}},{"cell_type":"code","source":"merged_dataset = pd.read_csv('/kaggle/working/merged_dataset.csv')\nmerged_dataset = merged_dataset.drop(columns=['database'])\nmerged_dataset#.to_csv('/kaggle/working/merged_dataset_full.cvs')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\noutput_base_path = '/kaggle/working/images_merged_dataset'\noutput_csv_path = '/kaggle/working/merged_dataset_transf.csv'\n\nos.makedirs(output_base_path, exist_ok=True)\n\nnew_coordinates_list = []\n\ndef process_and_save_image(row):\n    try:\n        study_id = row['study_id']\n        series_id = row['series_id']\n        instance_number = row['instance_number']\n        x = int(row['x'])  \n        y = int(row['y']) \n        level = str(row['level'])\n        pain = str(row['pain'])\n\n        dicom_path = os.path.join(input_base_path, str(study_id), str(series_id), f'{instance_number}.dcm')\n\n        ds = pydicom.dcmread(dicom_path)\n\n        img = ds.pixel_array\n\n        img_normalized = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n        img_normalized = img_normalized.astype(np.uint8)\n\n        img_pil = Image.fromarray(img_normalized)\n\n        img_resized = img_pil.resize((224, 224), Image.LANCZOS)\n\n        scale_x = 224 / img.shape[1]\n        scale_y = 224 / img.shape[0]\n        new_x = int(x) #Don´t change coordinates\n        new_y = int(y)\n\n        img_rotated = img_resized.rotate(0, center=(new_x, new_y))\n\n        img_rotated_np = np.array(img_rotated)\n\n        x_min = int(max(0, new_x - 35-5))\n        x_max = int(min(224, new_x + 35-5))\n        y_min = int(max(0, new_y - 20))\n        y_max = int(min(224, new_y + 20))\n\n        img_cropped = img_rotated_np[y_min:y_max, x_min:x_max]\n\n        img_cropped_pil = Image.fromarray(img_cropped)\n\n        padding_color = 0  \n        padded_img = ImageOps.expand(img_cropped_pil, border=((224 - img_cropped.shape[1]) // 2, (224 - img_cropped.shape[0]) // 2), fill=padding_color)\n\n        output_path = os.path.join(output_base_path, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n        padded_img.save(output_path, \"JPEG\")\n\n        new_coordinates_list.append([study_id, series_id, instance_number, pain, level, new_x, new_y])\n\n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\nfor _, row in tqdm(merged_dataset.iterrows(), total=merged_dataset.shape[0]):\n    process_and_save_image(row)\n\ndf_new_coordinates = pd.DataFrame(new_coordinates_list, columns=['study_id', 'series_id', 'instance_number', 'pain', 'level', 'x', 'y'])\ndf_new_coordinates.to_csv(output_csv_path, index=False)\n\nprint(f'The new coordinates have been saved in {output_csv_path}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now let's define the database with the images of all intervertebral levels and what is necessary for training the general model","metadata":{}},{"cell_type":"code","source":"train_data = pd.read_csv('/kaggle/working/merged_dataset_transf.csv')\ntrain_data","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We now train a ResNet50 model to classify spine images, incorporating data augmentation, K-fold cross-validation, and class weighting techniques to handle class imbalance. We save the model as *resnet_scs_all.pth* for later use.","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nfrom torchvision import models  # Import the torchvision models module\nfrom torchvision.models import ResNet50_Weights\nfrom torch.utils.data import DataLoader, Dataset, Subset, random_split, WeightedRandomSampler\nimport numpy as np\nimport os\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.preprocessing import LabelEncoder\nimport matplotlib.pyplot as plt\n\n# Configure device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Entry and exit routes\ntrain_images_dir = '/kaggle/working/images_merged_dataset'\nmodel_save_path = '/kaggle/working/resnet_scs_all.pth'\ncheckpoint_path = '/kaggle/working/checkpointAll.pth'\n\n# Convert level labels to numeric\nlabel_encoder = LabelEncoder()\ntrain_data['pain_encoded'] = label_encoder.fit_transform(train_data['pain'])\n\n# Custom dataset for uploading JPG images\nclass SpineDataset(Dataset):\n    def __init__(self, images_dir, dataframe, transform=None):\n        self.data = dataframe\n        self.images_dir = images_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        study_id = str(self.data.iloc[idx]['study_id'])\n        series_id = str(self.data.iloc[idx]['series_id'])\n        instance_number = str(self.data.iloc[idx]['instance_number'])\n        level = str(self.data.iloc[idx]['level'])\n        condition_label = self.data.iloc[idx]['pain_encoded']\n\n        img_path = os.path.join(self.images_dir, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n\n        if os.path.exists(img_path):\n            image = Image.open(img_path).convert('RGB')\n            if self.transform:\n                image = self.transform(image)\n            return image, condition_label\n        else:\n            print(f'File not found: {img_path}')\n            return None, None\n\n# Transformations for images with data augmentation\ntrain_transform = transforms.Compose([\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Create the full dataset\ndataset = SpineDataset(train_images_dir, train_data)\n\n# Split dataset into train, validation, and test sets\ntrain_size = int(0.7 * len(dataset))\nval_size = int(0.15 * len(dataset))\ntest_size = len(dataset) - train_size - val_size\ntrain_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])\n\nprint(f'Number of images in training: {train_size}')\nprint(f'Number of images in validation: {val_size}')\nprint(f'Number of images in test: {test_size}')\n\n# Assign transformations\ntrain_dataset.dataset.transform = train_transform\nval_dataset.dataset.transform = val_transform\ntest_dataset.dataset.transform = val_transform\n\n# Create data loaders\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False)\n\n# Define ResNet50 model with Dropout\nclass ResNet50WithDropout(nn.Module):\n    def __init__(self, dropout_rate=0.3):\n        super(ResNet50WithDropout, self).__init__()\n        self.model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)\n        num_ftrs = self.model.fc.in_features\n        self.model.fc = nn.Sequential(\n            nn.Dropout(dropout_rate),\n            nn.Linear(num_ftrs, 3)  # Assuming 3 classes\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n# Function to save the checkpoint\ndef save_checkpoint(model, optimizer, epoch, loss, checkpoint_path):\n    torch.save({\n        'epoch': epoch,\n        'model_state_dict': model.state_dict(),\n        'optimizer_state_dict': optimizer.state_dict(),\n        'loss': loss,\n    }, checkpoint_path)\n\n# Function to load the checkpoint\ndef load_checkpoint(model, optimizer, checkpoint_path):\n    if os.path.isfile(checkpoint_path):\n        checkpoint = torch.load(checkpoint_path, weights_only=True)\n        model.load_state_dict(checkpoint['model_state_dict'])\n        optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n        epoch = checkpoint['epoch']\n        loss = checkpoint['loss']\n        return epoch, loss\n    else:\n        return 0, float('inf')\nN = 5\n\n# Function to train the model and store losses\ndef train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=N, checkpoint_path=checkpoint_path):\n    start_epoch, prev_loss = load_checkpoint(model, optimizer, checkpoint_path)\n    \n    train_losses = []\n    val_losses = []\n\n    for epoch in range(start_epoch, num_epochs):\n        model.train()\n        total_train_loss = 0.0\n        progress_bar = tqdm(enumerate(train_loader), total=len(train_loader))\n        \n        for i, (images, labels) in progress_bar:\n            if images is None or labels is None:\n                continue\n            images, labels = images.to(device), labels.to(device)\n            optimizer.zero_grad()\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            total_train_loss += loss.item()\n            progress_bar.set_description(f'Epoch {epoch+1}/{num_epochs}, Loss: {total_train_loss / (i + 1):.4f}')\n        \n        # Calcular la pérdida de entrenamiento promedio en la época actual\n        avg_train_loss = total_train_loss / len(train_loader)\n        train_losses.append(avg_train_loss)\n        \n        # Evaluar el modelo en el conjunto de validación\n        model.eval()\n        total_val_loss = 0.0\n        \n        with torch.no_grad():\n            for val_images, val_labels in val_loader:\n                val_images, val_labels = val_images.to(device), val_labels.to(device)\n                val_outputs = model(val_images)\n                val_loss = criterion(val_outputs, val_labels).item()\n                total_val_loss += val_loss\n\n        avg_val_loss = total_val_loss / len(val_loader)\n        val_losses.append(avg_val_loss)\n\n        print(f'Validation Loss: {avg_val_loss:.4f}')\n        \n        # Guardar el checkpoint\n        save_checkpoint(model, optimizer, epoch+1, avg_val_loss, checkpoint_path)\n\n    # Mostrar las gráficas de pérdida\n    plt.figure(figsize=(5, 3))\n    plt.plot(train_losses, label='Training Loss')\n    plt.plot(val_losses, label='Validation Loss')\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Loss')\n    plt.legend()\n    plt.show()\n\n# Configure the model, criterion, and optimizer\nmodel = ResNet50WithDropout(dropout_rate=0.3).to(device)\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)\nclass_counts = np.bincount(train_data['pain_encoded'])\nclass_weights = 1. / class_counts\ncriterion = torch.nn.CrossEntropyLoss(weight=torch.tensor(class_weights, dtype=torch.float).to(device))\n\n# Train the model and display the loss graph\ntrain_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=N, checkpoint_path=checkpoint_path)\n\n# Save the trained model\ntorch.save(model.state_dict(), model_save_path)\nprint(f\"Model trained and saved in: {model_save_path}\")\n\n# Mostrar características del modelo final\nprint(f'Model size: {sum(p.numel() for p in model.parameters()) / 1e6:.2f}M parameters')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"let's make a prediction","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.transforms as transforms\nfrom torchvision import models\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\nfrom ipywidgets import interact, IntSlider\nfrom sklearn.preprocessing import LabelEncoder\n\n# Configuración de paths y dispositivo\nimage_dir = '/kaggle/working/train_ima_scsl1l2/'\nmodel_save_path = '/kaggle/working/resnet_scs_all.pth'\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# Definir la arquitectura del modelo ResNet50 con capa de Dropout\nclass ResNet50WithDropout(nn.Module):\n    def __init__(self, dropout_rate=0.3):\n        super(ResNet50WithDropout, self).__init__()\n        self.model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)\n        num_ftrs = self.model.fc.in_features\n        self.model.fc = nn.Sequential(\n            nn.Dropout(dropout_rate),\n            nn.Linear(num_ftrs, 3)  # Suponiendo 3 clases\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n# Cargar el modelo entrenado\nmodel = ResNet50WithDropout(dropout_rate=0.5)\nmodel.load_state_dict(torch.load(model_save_path, map_location=device, weights_only=True))\nmodel.to(device)\nmodel.eval()\n\n# Codificador de etiquetas\nlabel_encoder = LabelEncoder()\nlabel_encoder.classes_ = np.array(['Moderate', 'Normal/Mild', 'Severe'])\n\n# Transformaciones para las imágenes\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Lista para almacenar las probabilidades predichas\nprobabilities_list = []\n\n# Función para procesar y visualizar una imagen\ndef process_and_visualize_image(index):\n    try:\n        image_path = os.path.join(image_dir, image_files[index])\n        img = Image.open(image_path)\n        if img.mode != 'RGB':\n            img = img.convert('RGB')\n        \n        img_transformed = transform(img).unsqueeze(0).to(device)\n        \n        with torch.no_grad():\n            outputs = model(img_transformed)\n            logits = outputs\n            probabilities = torch.nn.functional.softmax(logits, dim=1).cpu().numpy()\n        \n        predicted_class = logits.argmax(1).item()\n        predicted_label = label_encoder.inverse_transform([predicted_class])[0]\n        \n        plt.figure(figsize=(4, 4))\n        plt.imshow(img)\n        plt.title(f'Imagen: {image_files[index]}, ETIQUETA PREDICHA: {predicted_label}\\n')\n        plt.axis('off')\n        plt.show()\n        \n        for i, class_name in enumerate(label_encoder.classes_):\n            print(f'{class_name}: {100*probabilities[0][i]:.4f} %')\n        \n        condition_name = os.path.splitext(os.path.basename(image_path))[0]\n        probabilities_row = [condition_name] + [f\"{100*prob:.4f} %\" for prob in probabilities[0]]\n        probabilities_list.append(probabilities_row)\n\n    except Exception as e:\n        print(f\"Error al procesar la imagen {image_path}: {e}\")\n\n# Obtener la lista de archivos de imagen\nimage_files = sorted([f for f in os.listdir(image_dir) if f.endswith('.jpg')])\n\n# Interactuar con el slider para seleccionar imágenes\ninteract(process_and_visualize_image, index=IntSlider(min=0, max=len(image_files)-1, step=1, value=0))\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dat_a = pd.read_csv('/kaggle/working/train_merge_spinal_canal_stenosis_l1_l2_transf.csv')\ndat_a[dat_a.pain == 'Moderate']","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now let's generate the models for each level trained with images segmented around the coordinates of each intervertebral level","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nfrom torchvision import models\nfrom torchvision.models import ResNet50_Weights\nfrom torch.utils.data import DataLoader, Dataset, random_split\nimport numpy as np\nimport os\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom sklearn.preprocessing import LabelEncoder\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\n# Configurar dispositivo\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Directorio de imágenes y archivos CSV\ntrain_images_dir = '/kaggle/working/images_merged_dataset'\ncsv_files = [\n    '/kaggle/working/train_merge_spinal_canal_stenosis_l1_l2_transf.csv',\n    '/kaggle/working/train_merge_spinal_canal_stenosis_l2_l3_transf.csv',\n    '/kaggle/working/train_merge_spinal_canal_stenosis_l3_l4_transf.csv',\n    '/kaggle/working/train_merge_spinal_canal_stenosis_l4_l5_transf.csv',\n    '/kaggle/working/train_merge_spinal_canal_stenosis_l5_s1_transf.csv'\n]\nmodel_save_paths = [\n    '/kaggle/working/resnet50_scs_l1l2.pth',\n    '/kaggle/working/resnet50_scs_l2l3.pth',\n    '/kaggle/working/resnet50_scs_l3l4.pth',\n    '/kaggle/working/resnet50_scs_l4l5.pth',\n    '/kaggle/working/resnet50_scs_l5s1.pth'\n]\n\n# Dataset personalizado\nclass SpineDataset(Dataset):\n    def __init__(self, images_dir, dataframe, transform=None):\n        self.data = dataframe\n        self.images_dir = images_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        study_id = str(self.data.iloc[idx]['study_id'])\n        series_id = str(self.data.iloc[idx]['series_id'])\n        instance_number = str(self.data.iloc[idx]['instance_number'])\n        level = str(self.data.iloc[idx]['level'])\n        condition_label = self.data.iloc[idx]['pain_encoded']\n\n        img_path = os.path.join(self.images_dir, f'{study_id}_{series_id}_{instance_number}_{level}.jpg')\n\n        if os.path.exists(img_path):\n            image = Image.open(img_path).convert('RGB')\n            if self.transform:\n                image = self.transform(image)\n            return image, condition_label\n        else:\n            print(f'File not found: {img_path}')\n            return None, None\n\n# Transformaciones\ntrain_transform = transforms.Compose([\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Definir modelo ResNet50 con Dropout\nclass ResNet50WithDropout(nn.Module):\n    def __init__(self, dropout_rate=0.3):\n        super(ResNet50WithDropout, self).__init__()\n        self.model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)\n        num_ftrs = self.model.fc.in_features\n        self.model.fc = nn.Sequential(\n            nn.Dropout(dropout_rate),\n            nn.Linear(num_ftrs, 3)  # Suponiendo 3 clases\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n# Función para guardar el checkpoint\ndef save_checkpoint(model, optimizer, epoch, loss, checkpoint_path):\n    torch.save({\n        'epoch': epoch,\n        'model_state_dict': model.state_dict(),\n        'optimizer_state_dict': optimizer.state_dict(),\n        'loss': loss,\n    }, checkpoint_path)\n\n# Función para cargar el checkpoint\ndef load_checkpoint(model, optimizer, checkpoint_path):\n    if os.path.isfile(checkpoint_path):\n        checkpoint = torch.load(checkpoint_path, weights_only=True)\n        model.load_state_dict(checkpoint['model_state_dict'])\n        optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n        epoch = checkpoint['epoch']\n        loss = checkpoint['loss']\n        return epoch, loss\n    else:\n        return 0, float('inf')\n\n# Función para entrenar el modelo\ndef train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=5, checkpoint_path=None):\n    start_epoch, prev_loss = load_checkpoint(model, optimizer, checkpoint_path) if checkpoint_path else (0, float('inf'))\n    \n    train_losses = []\n    val_losses = []\n\n    for epoch in range(start_epoch, num_epochs):\n        model.train()\n        total_train_loss = 0.0\n        progress_bar = tqdm(enumerate(train_loader), total=len(train_loader))\n        \n        for i, (images, labels) in progress_bar:\n            if images is None or labels is None:\n                continue\n            images, labels = images.to(device), labels.to(device)\n            optimizer.zero_grad()\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            total_train_loss += loss.item()\n            progress_bar.set_description(f'Epoch {epoch+1}/{num_epochs}, Loss: {total_train_loss / (i + 1):.4f}')\n        \n        avg_train_loss = total_train_loss / len(train_loader)\n        train_losses.append(avg_train_loss)\n        \n        model.eval()\n        total_val_loss = 0.0\n        \n        with torch.no_grad():\n            for val_images, val_labels in val_loader:\n                val_images, val_labels = val_images.to(device), val_labels.to(device)\n                val_outputs = model(val_images)\n                val_loss = criterion(val_outputs, val_labels).item()\n                total_val_loss += val_loss\n\n        avg_val_loss = total_val_loss / len(val_loader)\n        val_losses.append(avg_val_loss)\n\n        print(f'Validation Loss: {avg_val_loss:.4f}')\n        \n        if checkpoint_path:\n            save_checkpoint(model, optimizer, epoch+1, avg_val_loss, checkpoint_path)\n\n    plt.figure(figsize=(5, 3))\n    plt.plot(train_losses, label='Training Loss')\n    plt.plot(val_losses, label='Validation Loss')\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Loss')\n    plt.legend()\n    plt.show()\n\n# Iterar sobre los archivos CSV\nfor csv_file, model_save_path in zip(csv_files, model_save_paths):\n    print(f\"Training model for: {csv_file}\")\n\n    # Cargar los datos\n    train_data = pd.read_csv(csv_file)\n\n    # Codificar las etiquetas de la condición\n    label_encoder = LabelEncoder()\n    train_data['pain_encoded'] = label_encoder.fit_transform(train_data['pain'])\n\n    # Crear el dataset completo\n    dataset = SpineDataset(train_images_dir, train_data)\n\n    # Dividir el dataset\n    train_size = int(0.7 * len(dataset))\n    val_size = int(0.15 * len(dataset))\n    test_size = len(dataset) - train_size - val_size\n    train_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])\n\n    # Asignar transformaciones\n    train_dataset.dataset.transform = train_transform\n    val_dataset.dataset.transform = val_transform\n    test_dataset.dataset.transform = val_transform\n\n    # Crear loaders\n    train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)\n    val_loader = DataLoader(val_dataset, batch_size=16, shuffle=False)\n    test_loader = DataLoader(test_dataset, batch_size=16, shuffle=False)\n\n    # Configurar el modelo, criterio y optimizador\n    model = ResNet50WithDropout(dropout_rate=0.3).to(device)\n    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)\n    class_counts = np.bincount(train_data['pain_encoded'])\n    class_weights = 1. / class_counts\n    criterion = torch.nn.CrossEntropyLoss(weight=torch.tensor(class_weights, dtype=torch.float).to(device))\n\n    # Entrenar el modelo\n    train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=5)\n\n    # Guardar el modelo entrenado\n    torch.save(model.state_dict(), model_save_path)\n    print(f\"Model trained and saved in: {model_save_path}\")\n\n    # Mostrar características del modelo final\n    print(f'Model size: {sum(p.numel() for p in model.parameters()) / 1e6:.2f}M parameters')\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's do some tests with the generated models","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.transforms as transforms\nfrom torchvision import models\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\nfrom ipywidgets import interact, IntSlider\nfrom sklearn.preprocessing import LabelEncoder\n\n# Configuración de paths y dispositivo\nimage_dir = '/kaggle/working/train_ima_scsl1l2/'\nmodel_save_path = '/kaggle/working/resnet_scs_l1l2.pth'\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# Definir la arquitectura del modelo ResNet50 con capa de Dropout\nclass ResNet50WithDropout(nn.Module):\n    def __init__(self, dropout_rate=0.5):\n        super(ResNet50WithDropout, self).__init__()\n        self.model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)\n        num_ftrs = self.model.fc.in_features\n        self.model.fc = nn.Sequential(\n            nn.Dropout(dropout_rate),\n            nn.Linear(num_ftrs, 3)  # Suponiendo 3 clases\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n# Cargar el modelo entrenado\nmodel = ResNet50WithDropout(dropout_rate=0.5)\nmodel.load_state_dict(torch.load(model_save_path, map_location=device, weights_only=True))\nmodel.to(device)\nmodel.eval()\n\n# Codificador de etiquetas\nlabel_encoder = LabelEncoder()\nlabel_encoder.classes_ = np.array(['Moderate', 'Normal/Mild', 'Severe'])\n\n# Transformaciones para las imágenes\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Lista para almacenar las probabilidades predichas\nprobabilities_list = []\n\n# Función para procesar y visualizar una imagen\ndef process_and_visualize_image(index):\n    try:\n        image_path = os.path.join(image_dir, image_files[index])\n        img = Image.open(image_path)\n        if img.mode != 'RGB':\n            img = img.convert('RGB')\n        \n        img_transformed = transform(img).unsqueeze(0).to(device)\n        \n        with torch.no_grad():\n            outputs = model(img_transformed)\n            logits = outputs\n            probabilities = torch.nn.functional.softmax(logits, dim=1).cpu().numpy()\n        \n        predicted_class = logits.argmax(1).item()\n        predicted_label = label_encoder.inverse_transform([predicted_class])[0]\n        \n        plt.figure(figsize=(4, 4))\n        plt.imshow(img)\n        plt.title(f'Imagen: {image_files[index]}, ETIQUETA PREDICHA: {predicted_label}\\n')\n        plt.axis('off')\n        plt.show()\n        \n        for i, class_name in enumerate(label_encoder.classes_):\n            print(f'{class_name}: {100*probabilities[0][i]:.4f} %')\n        \n        condition_name = os.path.splitext(os.path.basename(image_path))[0]\n        probabilities_row = [condition_name] + [f\"{100*prob:.4f} %\" for prob in probabilities[0]]\n        probabilities_list.append(probabilities_row)\n\n    except Exception as e:\n        print(f\"Error al procesar la imagen {image_path}: {e}\")\n\n# Obtener la lista de archivos de imagen\nimage_files = sorted([f for f in os.listdir(image_dir) if f.endswith('.jpg')])\n\n# Interactuar con el slider para seleccionar imágenes\ninteract(process_and_visualize_image, index=IntSlider(min=0, max=len(image_files)-1, step=1, value=0))\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Finally we are going to evaluate the images of the test_images directory. First we calculate the coordinates","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport pydicom\nimport os\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport torch\nimport torchvision.transforms as transforms\nfrom torchvision.models import resnet50\nimport torch.nn as nn\n\n# Define the SpineNet class using ResNet50\nclass SpineNet(nn.Module):\n    def __init__(self, pretrained=True):\n        super(SpineNet, self).__init__()\n        self.backbone = resnet50(weights='DEFAULT' if pretrained else None)\n        self.backbone.fc = nn.Linear(self.backbone.fc.in_features, 10)  # Example: 10 output classes\n\n    def forward(self, x):\n        return self.backbone(x)\n\n# Function to load and preprocess DICOM image for prediction\ndef load_and_preprocess_dicom(image_path):\n    try:\n        ds = pydicom.dcmread(image_path)\n        img = ds.pixel_array\n        img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n        # Convert to float32 and normalize to [0, 1]\n        img = img.astype(np.float32)\n        img = (img - np.min(img)) / (np.max(img) - np.min(img))\n\n        # Convert to uint8 for compatibility with ToPILImage\n        img = (img * 255).astype(np.uint8)\n\n        transform = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n        return transform(img)\n    except pydicom.errors.InvalidDicomError:\n        print(f\"Invalid DICOM file: {image_path}\")\n        return None\n    except Exception as e:\n        print(f\"Error processing {image_path}: {e}\")\n        return None\n\n# Function to make a prediction\ndef predict(image_path, model, device):\n    model.eval()\n    image = load_and_preprocess_dicom(image_path)\n    if image is None:\n        print(\"Failed to load and preprocess the image.\")\n        return None\n\n    image = image.unsqueeze(0).to(device)  # Add batch dimension and move to device\n\n    try:\n        with torch.no_grad():\n            output = model(image)\n        return output.cpu().numpy().flatten()  # Flatten the output for easier access\n    except Exception as e:\n        print(f\"Error during model prediction: {e}\")\n        return None\n\n# Function to plot resized image and draw predicted rectangles\ndef plot_with_predictions(image_path, coordinates):\n    # Load the image for display\n    ds = pydicom.dcmread(image_path)\n    img = ds.pixel_array\n    img = np.stack([img] * 3, axis=-1)  # Convert to RGB\n\n    # Convert to float32 and normalize for visualization\n    img = img.astype(np.float32)\n    img = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n    img = img.astype(np.uint8)\n\n    # Convert image to PIL and resize\n    img_pil = Image.fromarray(img)\n    img_resized = img_pil.resize((224, 224))\n\n    fig, ax = plt.subplots(1, figsize=(3, 3))\n    ax.imshow(img_resized)\n\n    # Draw rectangles centered at each predicted coordinate\n    for i in range(0, len(coordinates), 2):\n        x_center = coordinates[i]\n        y_center = coordinates[i + 1]\n        rect = patches.Rectangle((x_center - 0, y_center - 0), 5, 5, linewidth=2, edgecolor='r', facecolor='none')\n        ax.add_patch(rect)\n\n    plt.title(f\"Predictions for {os.path.basename(image_path)}\")\n    plt.axis('on')\n    plt.show()\n\n# Load the trained model\nmodel_path = '/kaggle/working/spinenet_model.pth'\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = SpineNet(pretrained=False)\ntry:\n    model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))\n    model.to(device)\nexcept Exception as e:\n    print(f\"Error loading model: {e}\")\n\n# Directory containing test images\nimage_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/3844393089/'\n\n# Obtain all image paths from the test directory\nimage_files = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith('.dcm')]\n\n# Prepare to save prediction results\nresults = []\n\nfor image_path in image_files:\n    try:\n        outputs = predict(image_path, model, device)\n        if outputs is not None:\n            print(f\"Predictions for image {image_path}: {outputs}\")\n            \n            # Plot the predictions (assuming they are coordinates)\n            plot_with_predictions(image_path, outputs)\n            \n            # Parse the instance number from the image path\n            instance_number = os.path.basename(image_path).split('.')[0]\n            \n            # Prepare the result dictionary\n            result_dict = {'instance_number': instance_number}\n            for i in range(0, len(outputs), 2):\n                level = f\"L{(i // 2) + 1}\"\n                result_dict[f\"{level}x\"] = outputs[i]\n                result_dict[f\"{level}y\"] = outputs[i + 1]\n\n            # Save the predictions to the results list\n            results.append(result_dict)\n    except Exception as e:\n        print(f\"Error processing image {image_path}: {e}\")\n\n# Save the results to a CSV file\nresults_df = pd.DataFrame(results)\nresults_csv_path = '/kaggle/working/predictions.csv'\nresults_df.to_csv(results_csv_path, index=False)\nprint(f\"Predictions saved to {results_csv_path}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results_csv_path = '/kaggle/working/predictions.csv'\nresults_df = pd.read_csv(results_csv_path)\n# Ordenar el DataFrame por la columna 'instance_number'\nsorted_results_df = results_df.sort_values(by='instance_number')\n\n# Resetear el índice del DataFrame\nsorted_results_df = sorted_results_df.reset_index(drop=True)\n\n# Imprimir el DataFrame con el índice reseteado\nsorted_results_df\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pydicom\nfrom PIL import Image, ImageOps\nimport pandas as pd\nfrom tqdm import tqdm\n\n# Define the base paths\ninput_base_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/3844393089'\noutput_base_path = '/kaggle/working/test_ima_scs'\nos.makedirs(output_base_path, exist_ok=True)\n\n# Define the output CSV path\noutput_csv_path = '/kaggle/working/new_coordinates.csv'\n\n# List to store new coordinates\nnew_coordinates_list = []\n\ndef process_and_save_segmented_images(filename, coordinates):\n    try:\n        dicom_path = os.path.join(input_base_path, f'{filename}.dcm')\n        ds = pydicom.dcmread(dicom_path)\n        \n        img = ds.pixel_array        \n        img_normalized = (img - np.min(img)) / (np.max(img) - np.min(img)) * 255\n        img_normalized = img_normalized.astype(np.uint8)\n        img_pil = Image.fromarray(img_normalized)        \n        img_resized = img_pil.resize((224, 224), Image.LANCZOS)        \n        \n        scale_x = 1#224 / img.shape[1]\n        scale_y = 1#224 / img.shape[0]\n        \n        for i, (x, y) in enumerate(coordinates):\n            new_x = int(x * scale_x)\n            new_y = int(y * scale_y)\n            \n            x_min = int(max(0, new_x - 37 - 3))\n            x_max = int(min(224, new_x + 37 - 3))\n            y_min = int(max(0, new_y - 20))\n            y_max = int(min(224, new_y + 20))\n            \n            img_cropped = img_resized.crop((x_min, y_min, x_max, y_max))\n            padded_img = ImageOps.expand(img_cropped, border=((224 - img_cropped.width) // 2, (224 - img_cropped.height) // 2), fill=0)\n            \n            output_path = os.path.join(output_base_path, f'{filename}_L{i+1}.jpg')\n            padded_img.save(output_path, \"JPEG\")\n            new_coordinates_list.append([filename, f'L{i+1}', new_x, new_y])\n           # print(f'Segment {i+1} saved to {output_path}')\n    \n    except Exception as e:\n        print(f\"Error processing file {dicom_path}: {e}\")\n\n# Iterate over sorted_results_df\nfor _, row in tqdm(sorted_results_df.iterrows(), total=sorted_results_df.shape[0]):\n    filename = int(row['instance_number'])\n    coordinates = [(row[f'L{i}x'], row[f'L{i}y']) for i in range(1, 6)]\n    process_and_save_segmented_images(filename, coordinates)\n\n# Save new coordinates to CSV\ndf_new_coordinates = pd.DataFrame(new_coordinates_list, columns=['instance_number', 'level', 'x', 'y'])\ndf_new_coordinates.to_csv(output_csv_path, index=False)\nprint(f'The new coordinates have been saved in {output_csv_path}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_new_coordinates","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"filtered_df = df_new_coordinates[df_new_coordinates['instance_number'] == 12]\nfiltered_df ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torchvision import transforms, models\nfrom PIL import Image\nimport os\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.preprocessing import LabelEncoder\nfrom ipywidgets import interact, IntSlider\n\n# Device configuration\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# Define the ResNet50 model\nmodel = models.resnet50(pretrained=False, num_classes=3)  # Adjust num_classes according to your case\nmodel_save_path = '/kaggle/working/resnet_scs_all.pth'\nif os.path.exists(model_save_path):\n    model.load_state_dict(torch.load(model_save_path, map_location=device))  # Load weights correctly\nelse:\n    raise FileNotFoundError(\"Model weights file not found.\")\nmodel = model.to(device)\nmodel.eval()\n\n# Label encoder for class names\nlabel_encoder = LabelEncoder()\nlabel_encoder.classes_ = np.array(['Moderate', 'Normal/Mild', 'Severe'])\n\n# Image transformations\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Function to process and visualize the image\ndef process_and_visualize_image(index):\n    try:\n        image_path = os.path.join(image_dir, image_files[index])\n        img = Image.open(image_path)\n        if img.mode != 'RGB':\n            img = img.convert('RGB')\n        img_transformed = transform(img).unsqueeze(0).to(device)\n        \n        with torch.no_grad():\n            outputs = model(img_transformed)\n            probabilities = torch.nn.functional.softmax(outputs, dim=1).cpu().numpy()\n        \n        predicted_class = outputs.argmax(1).item()\n        predicted_label = label_encoder.inverse_transform([predicted_class])[0]\n        \n        plt.figure(figsize=(6, 6))\n        plt.imshow(img)\n        plt.title(f'Image: {image_files[index]}, PREDICTED LABEL: {predicted_label}\\n')\n        plt.axis('off')\n        plt.show()\n        \n        for i, class_name in enumerate(label_encoder.classes_):\n            print(f'{class_name}: {100 * probabilities[0][i]:.4f} %')\n        \n        condition_name = os.path.splitext(os.path.basename(image_path))[0]\n        probabilities_row = [condition_name] + [f\"{100 * prob:.4f} %\" for prob in probabilities[0]]\n        probabilities_list.append(probabilities_row)\n\n    except Exception as e:\n        print(f\"Error processing image {image_path}: {e}\")\n\n# Directory and image files\nimage_dir = '/kaggle/working/test_ima_scs'\nimage_files = sorted([f for f in os.listdir(image_dir) if f.endswith('.jpg')])\n\n# Initialize list to store probabilities\nprobabilities_list = []\n\n# Interactive widget for image selection\ninteract(process_and_visualize_image, index=IntSlider(min=0, max=len(image_files)-1, step=1, value=0))\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Probemos el modelo scs_all","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.transforms as transforms\nfrom torchvision import models\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\nfrom ipywidgets import interact, IntSlider\nfrom sklearn.preprocessing import LabelEncoder\n\n# Configuración de paths y dispositivo\nimage_dir = '/kaggle/working/train_ima_scsl2l3/'\nmodel_save_path = '/kaggle/working/resnet_scs_l2l3.pth'\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# Definir la arquitectura del modelo ResNet50 con capa de Dropout\nclass ResNet50WithDropout(nn.Module):\n    def __init__(self, dropout_rate=0.5):\n        super(ResNet50WithDropout, self).__init__()\n        self.model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)\n        num_ftrs = self.model.fc.in_features\n        self.model.fc = nn.Sequential(\n            nn.Dropout(dropout_rate),\n            nn.Linear(num_ftrs, 3)  # Suponiendo 3 clases\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n# Cargar el modelo entrenado\nmodel = ResNet50WithDropout(dropout_rate=0.5)\nmodel.load_state_dict(torch.load(model_save_path, map_location=device, weights_only=True))\nmodel.to(device)\nmodel.eval()\n\n# Codificador de etiquetas\nlabel_encoder = LabelEncoder()\nlabel_encoder.classes_ = np.array(['Moderate', 'Normal/Mild', 'Severe'])\n\n# Transformaciones para las imágenes\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Lista para almacenar las probabilidades predichas\nprobabilities_list = []\n\n# Función para procesar y visualizar una imagen\ndef process_and_visualize_image(index):\n    try:\n        image_path = os.path.join(image_dir, image_files[index])\n        img = Image.open(image_path)\n        if img.mode != 'RGB':\n            img = img.convert('RGB')\n        \n        img_transformed = transform(img).unsqueeze(0).to(device)\n        \n        with torch.no_grad():\n            outputs = model(img_transformed)\n            logits = outputs\n            probabilities = torch.nn.functional.softmax(logits, dim=1).cpu().numpy()\n        \n        predicted_class = logits.argmax(1).item()\n        predicted_label = label_encoder.inverse_transform([predicted_class])[0]\n        \n        plt.figure(figsize=(4, 4))\n        plt.imshow(img)\n        plt.title(f'Imagen: {image_files[index]}, ETIQUETA PREDICHA: {predicted_label}\\n')\n        plt.axis('off')\n        plt.show()\n        \n        for i, class_name in enumerate(label_encoder.classes_):\n            print(f'{class_name}: {100*probabilities[0][i]:.4f} %')\n        \n        condition_name = os.path.splitext(os.path.basename(image_path))[0]\n        probabilities_row = [condition_name] + [f\"{100*prob:.4f} %\" for prob in probabilities[0]]\n        probabilities_list.append(probabilities_row)\n\n    except Exception as e:\n        print(f\"Error al procesar la imagen {image_path}: {e}\")\n\n# Obtener la lista de archivos de imagen\nimage_files = sorted([f for f in os.listdir(image_dir) if f.endswith('.jpg')])\n\n# Interactuar con el slider para seleccionar imágenes\ninteract(process_and_visualize_image, index=IntSlider(min=0, max=len(image_files)-1, step=1, value=0))","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}