{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"}],"dockerImageVersionId":30715,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport os\nimport random\nimport numpy as np\nimport pandas as pd\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import datasets, transforms\nimport torch\nimport pydicom\nimport multiprocessing as mp\nimport torch.nn.functional as F\nimport torch.nn as nn\nfrom tqdm import tqdm\nimport glob\nimport pydicom\nimport matplotlib.pyplot as plt\nimport cv2\n\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n #   for filename in filenames:\n  #      pass\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","scrolled":true,"execution":{"iopub.status.busy":"2024-06-06T10:23:17.131062Z","iopub.execute_input":"2024-06-06T10:23:17.132528Z","iopub.status.idle":"2024-06-06T10:23:25.518807Z","shell.execute_reply.started":"2024-06-06T10:23:17.132476Z","shell.execute_reply":"2024-06-06T10:23:25.517635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"NUM_WORKERS = mp.cpu_count()\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\ndata_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification'\nprint(f\"Using device: {device}\")\nprint(NUM_WORKERS)","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:25.520853Z","iopub.execute_input":"2024-06-06T10:23:25.521221Z","iopub.status.idle":"2024-06-06T10:23:25.529047Z","shell.execute_reply.started":"2024-06-06T10:23:25.521186Z","shell.execute_reply":"2024-06-06T10:23:25.527814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train.csv')\ntotal_classes = len(train)\nprint(total_classes)","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:25.530417Z","iopub.execute_input":"2024-06-06T10:23:25.530773Z","iopub.status.idle":"2024-06-06T10:23:25.590733Z","shell.execute_reply.started":"2024-06-06T10:23:25.530744Z","shell.execute_reply":"2024-06-06T10:23:25.589571Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.head()","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:25.593794Z","iopub.execute_input":"2024-06-06T10:23:25.594269Z","iopub.status.idle":"2024-06-06T10:23:25.636748Z","shell.execute_reply.started":"2024-06-06T10:23:25.594228Z","shell.execute_reply":"2024-06-06T10:23:25.635635Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* we'll use predefined code for visualization\n* modifying a bit to simplify","metadata":{}},{"cell_type":"code","source":"figure, axis = plt.subplots(1,3, figsize=(20,5)) \nfor idx, d in enumerate(['foraminal', 'subarticular', 'canal']):\n  diagnosis = list(filter(lambda x: x.find(d) > -1, train.columns))\n  dff = train[diagnosis]\n  value_counts = dff.apply(lambda x: x.value_counts()).fillna(0).T\n  value_counts.plot(kind='bar', stacked=True, ax=axis[idx])\n  axis[idx].set_title(f'{d} distribution')\n\n# most patients have normal","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:25.637988Z","iopub.execute_input":"2024-06-06T10:23:25.638347Z","iopub.status.idle":"2024-06-06T10:23:27.133072Z","shell.execute_reply.started":"2024-06-06T10:23:25.638310Z","shell.execute_reply":"2024-06-06T10:23:27.131758Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"study_folders = [f for f in os.listdir('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images') if '.DS' not in f]\ndf_meta = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_series_descriptions.csv')\n\n# Create metadata object\nmeta_obj = {study: {'folder_path': f\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/{study}\"} for study in study_folders}\n\n# Populate SeriesInstanceUIDs and SeriesDescriptions\nfor study in meta_obj:\n  series_uids = [uid for uid in os.listdir(meta_obj[study]['folder_path']) if '.DS' not in uid]\n  meta_obj[study]['SeriesInstanceUIDs'] = series_uids\n  meta_obj[study]['SeriesDescriptions'] = [\n    df_meta.loc[(df_meta['study_id'] == int(study)) & (df_meta['series_id'] == int(uid)), 'series_description'].values[0]\n    for uid in series_uids\n  ]\n\npatient_id = '4646740' \npatient_data = meta_obj[patient_id]\n\nim_list_dcm = {}\nfor idx, series_uid in enumerate(patient_data['SeriesInstanceUIDs']):\n  series_description = patient_data['SeriesDescriptions'][idx]\n  images = sorted(\n    glob.glob(f\"{patient_data['folder_path']}/{series_uid}/*.dcm\"),\n    key=lambda x: int(os.path.basename(x).replace('.dcm', '').split('\\\\')[-1])\n  )\n  im_list_dcm[series_uid] = {\n    'images': [{'SOPInstanceUID': os.path.basename(img).replace('.dcm', ''), 'dicom': pydicom.dcmread(img)} for img in images],\n    'description': series_description\n  }\n\n\ndef display_images(images, title, max_images_per_row=4):\n  num_images = len(images)\n  num_rows = (num_images + max_images_per_row - 1) // max_images_per_row\n\n  fig, axes = plt.subplots(num_rows, max_images_per_row, figsize=(5 * max_images_per_row, 1.5 * num_rows))\n  for idx, image in enumerate(images):\n    ax = axes.flatten()[idx]\n    ax.imshow(image, cmap='gray')\n    ax.axis('off')\n  for idx in range(num_images, len(axes.flatten())):\n    axes.flatten()[idx].axis('off')\n  fig.suptitle(title, fontsize=16)\n  plt.tight_layout()\n","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:27.134580Z","iopub.execute_input":"2024-06-06T10:23:27.135029Z","iopub.status.idle":"2024-06-06T10:23:33.033924Z","shell.execute_reply.started":"2024-06-06T10:23:27.134991Z","shell.execute_reply":"2024-06-06T10:23:33.032862Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for series_uid, series_data in im_list_dcm.items():\n  display_images([img['dicom'].pixel_array for img in series_data['images']], series_data['description'])","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:33.035758Z","iopub.execute_input":"2024-06-06T10:23:33.036233Z","iopub.status.idle":"2024-06-06T10:23:40.431354Z","shell.execute_reply.started":"2024-06-06T10:23:33.036192Z","shell.execute_reply":"2024-06-06T10:23:40.429602Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_coor = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_label_coordinates.csv')\ndf_coor","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:40.432875Z","iopub.execute_input":"2024-06-06T10:23:40.433323Z","iopub.status.idle":"2024-06-06T10:23:40.591419Z","shell.execute_reply.started":"2024-06-06T10:23:40.433263Z","shell.execute_reply":"2024-06-06T10:23:40.590328Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def display_images_grid(images, titles, max_images_per_row=4):\n    num_images = len(images)\n    num_rows = (num_images + max_images_per_row - 1) // max_images_per_row\n    fig, axes = plt.subplots(num_rows, max_images_per_row, figsize=(10 * max_images_per_row, 5 * num_rows))\n    \n    for idx, (image, title) in enumerate(zip(images, titles)):\n        ax = axes.flatten()[idx]\n        ax.imshow(image, cmap='gray')\n        ax.axis('off')\n        ax.set_title(title, fontsize=12)\n    \n    for idx in range(num_images, len(axes.flatten())):\n        axes.flatten()[idx].axis('off')\n    \n    fig.tight_layout()\n    plt.show()\n\ndef display_coor_on_img_grid(c, images, titles):\n    center_coordinates = (int(c['x']), int(c['y']))\n    radius = 10\n    color = (255, 0, 0)  \n    thickness = 2\n    \n    img_with_circles = []\n    for img in images:\n        img_normalized = cv2.normalize(img, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)\n        img_rgb = cv2.cvtColor(img_normalized, cv2.COLOR_GRAY2RGB)\n        img_with_circles.append(cv2.circle(img_rgb, center_coordinates, radius, color, thickness))\n    \n    display_images_grid(img_with_circles, titles)\n    \n    display_images_grid(img_with_circles, titles)","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:40.592762Z","iopub.execute_input":"2024-06-06T10:23:40.593102Z","iopub.status.idle":"2024-06-06T10:23:40.606923Z","shell.execute_reply.started":"2024-06-06T10:23:40.593073Z","shell.execute_reply":"2024-06-06T10:23:40.605544Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"patient = train.iloc[1]\ncoor_entries = df_coor[df_coor['study_id'] == int(patient['study_id'])]\n\nprint(\"Showing all cases for this patient:\")\npatient_images = []\npatient_titles = []\nfor idc, c in coor_entries.iterrows():\n    for i in im_list_dcm[str(c['series_id'])]['images']:\n        if int(i['SOPInstanceUID']) == int(c['instance_number']):\n            try:\n                patient_severity = patient[\n                    f\"{c['condition'].lower().replace(' ', '_')}_{c['level'].lower().replace('/', '_')}\"\n                ]\n            except Exception as e:\n                patient_severity = \"unknown severity\"\n            title = f\"{i['SOPInstanceUID']} \\n{c['level']}, {c['condition']}: {patient_severity} \\n{c['x']}, {c['y']}\"\n            patient_images.append(i['dicom'].pixel_array)\n            patient_titles.append(title)\n\ndisplay_coor_on_img_grid(c, patient_images, patient_titles)","metadata":{"execution":{"iopub.status.busy":"2024-06-06T10:23:40.610152Z","iopub.execute_input":"2024-06-06T10:23:40.610570Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"***add code from here***","metadata":{}},{"cell_type":"code","source":"img = pydicom.dcmread('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/100206310/1012284084/1.dcm')\nimg = img.pixel_array\nimg.shape","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nIMG_SIZE = (256,256)  \nBATCH_SIZE = 16 \nEPOCHS = 1\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\ndata_dir = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification'\ntrain_dir = os.path.join(data_dir, 'train_images')\ntest_dir = os.path.join(data_dir, 'test_images')\n\nlabels = pd.read_csv(os.path.join(data_dir, 'train.csv'))\n\nlabel_mapping = {'normal/mild': 0, 'moderate': 1, 'severe': 2}\nlabels = labels.map(lambda x: x.strip().lower() if isinstance(x, str) else x)\nfor col in labels.columns[1:]: \n  labels[col] = labels[col].map(label_mapping)\nlabels","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ct = pd.DataFrame()\nfor c in labels.columns[1:]:\n  ct = pd.concat([ct, labels[c].value_counts()], axis=1)\n  ct = ct.reset_index(drop=True)\nct.columns = labels.columns[1:]\nct","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNADataset(Dataset):\n\n    def __init__(self, dcm_dir, labels_df, tran=None):\n        self.dcm_dir = dcm_dir\n\n        self.transform = tran\n        self.labels_df = labels_df\n        self.dcm_files = [os.path.join(root, f)\n                          for root, _, files in os.walk(dcm_dir)\n                          for f in files if f.endswith('.dcm')]\n\n        if len(self.dcm_files) == 0:\n            raise ValueError(f\"No DICOM files found in directory {dcm_dir}\")\n\n    def __len__(self):\n        return len(self.dcm_files)\n\n    def __getitem__(self, idx):\n        dcm_file = self.dcm_files[idx]\n        dicom = pydicom.dcmread(str(dcm_file))\n        print(type(dcm_file), dcm_file)\n        image = dicom.pixel_array\n        image = (image - np.min(image)) / (np.max(image) - np.min(image))  # normalize image\n        image = np.expand_dims(image, axis=0)\n\n        # Extract the study_id from the DICOM filename\n        study_id = os.path.basename(dcm_file).split('.')[0]\n        labels = np.zeros(NUM_CLASSES, dtype=np.int64)\n        try:\n            labels_data = self.labels_df.loc[self.labels_df.columns[1:]].values.astype(np.int64)\n            labels[:labels_data.shape[0]] = labels_data\n        except KeyError as e:\n            print(f\"KeyError: {e} occurred. Using default labels.\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, torch.tensor(labels)\n\n\n\ntransform = transforms.Compose([\n  transforms.ToTensor(),\n  transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n  transforms.Resize((IMG_SIZE[0], IMG_SIZE[1])),\n# add more later\n])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = RSNADataset(train_dir,labels_df=labels, transforms=transform)\ndataloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=mp.cpu_count())","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RsnaClassifier(nn.Module):\n    def __init__(self,\n                 input_shape: int,\n                 hidden_units: int,\n                 output_shape: int) -> None:\n        super().__init__()\n        self.conv_block_1 = nn.Sequential(\n            nn.Conv2d(in_channels=input_shape,\n                      out_channels=hidden_units,\n                      kernel_size=3,\n                      stride=1,\n                      padding=0),\n            nn.ReLU(),\n            nn.Conv2d(in_channels=hidden_units,\n                      out_channels=hidden_units,\n                      kernel_size=3,\n                      stride=1,\n                      padding=0),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2,\n                         stride=2)\n        )\n        self.conv_block_2 = nn.Sequential(\n            nn.Conv2d(in_channels=hidden_units,\n                      out_channels=hidden_units,\n                      kernel_size=3,\n                      stride=1,\n                      padding=0),\n            nn.ReLU(),\n            nn.Conv2d(in_channels=hidden_units,\n                      out_channels=hidden_units,\n                      kernel_size=3,\n                      stride=1,\n                      padding=0),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2,\n                         stride=2)\n        )\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(in_features=hidden_units * 13 * 13,\n                      out_features=output_shape)\n        )\n\n    def forward(self, x):\n        x = self.conv_block_1(x)\n        # print(x.shape)\n        x = self.conv_block_2(x)\n        # print(x.shape)\n        x = self.classifier(x)\n        # print(x.shape)\n        return x","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = RsnaClassifier(input_shape=256,output_shape=NUM_CLASSES,hidden_units=32)\nmodel.to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001) ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS = 1","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_step(images, labels):\n  model.train()\n  optimizer.zero_grad()\n  outputs = model(images)\n  loss = criterion(outputs, labels)\n  loss.backward()\n  optimizer.step()\n  return loss.item()\n\ndef test_step(images, labels):\n  model.eval()\n  with torch.no_grad():\n    outputs = model(images)\n    loss = criterion(outputs, labels)\n  return loss.item()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for epoch in range(EPOCHS):\n  train_loss = 0\n  for images, labels in dataloader:\n    images, labels = images.to(device), labels.to(device)\n    train_loss += train_step(images, labels)\n  print(f'Epoch {epoch + 1}/{EPOCHS}, Loss: {train_loss/len(dataloader)}')","metadata":{},"execution_count":null,"outputs":[]}]}