{"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":"markdown","source":"# Create respective images for Sagittal, Coronal and Axial\n\nRefer to this [notebook by vaillant](https://www.kaggle.com/code/vaillant/cross-reference-images-in-different-mri-planes/notebook) for more information","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"import os, gc, sys, copy\nfrom pathlib import Path\nimport glob\nfrom tqdm.auto import tqdm\ntqdm.pandas()\n\nfrom joblib import Parallel, delayed\nimport multiprocessing as mp\n\nimport math\nimport random\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nimport cv2\ncv2.setNumThreads(0)\nimport PIL\n\nimport pydicom\nfrom pydicom.filebase import DicomBytesIO\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:45.931096Z","iopub.execute_input":"2024-07-02T02:01:45.932082Z","iopub.status.idle":"2024-07-02T02:01:46.802891Z","shell.execute_reply.started":"2024-07-02T02:01:45.932043Z","shell.execute_reply":"2024-07-02T02:01:46.801876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_PATH = Path(\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification\")\nos.listdir(DATA_PATH)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.804631Z","iopub.execute_input":"2024-07-02T02:01:46.805053Z","iopub.status.idle":"2024-07-02T02:01:46.814425Z","shell.execute_reply.started":"2024-07-02T02:01:46.805026Z","shell.execute_reply":"2024-07-02T02:01:46.813445Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_df = pd.read_csv(DATA_PATH/\"sample_submission.csv\")\ntest_desc = pd.read_csv(DATA_PATH/\"test_series_descriptions.csv\")\ntrain_desc = pd.read_csv(DATA_PATH/\"train_series_descriptions.csv\")\ntrain_main = pd.read_csv(DATA_PATH/\"train.csv\")","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.815550Z","iopub.execute_input":"2024-07-02T02:01:46.815861Z","iopub.status.idle":"2024-07-02T02:01:46.867880Z","shell.execute_reply.started":"2024-07-02T02:01:46.815836Z","shell.execute_reply":"2024-07-02T02:01:46.867035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train_melted = train_main.melt(id_vars='study_id', var_name=\"condition\", value_name=\"severity\")\n# train_melted['target'] = 0\n# # train_main.head()","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.870349Z","iopub.execute_input":"2024-07-02T02:01:46.870981Z","iopub.status.idle":"2024-07-02T02:01:46.875048Z","shell.execute_reply.started":"2024-07-02T02:01:46.870946Z","shell.execute_reply":"2024-07-02T02:01:46.874078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train_images = glob.glob(f\"{DATA_PATH}/train_images/*/*/*.dcm\")\n# len(train_images)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.876539Z","iopub.execute_input":"2024-07-02T02:01:46.877168Z","iopub.status.idle":"2024-07-02T02:01:46.884974Z","shell.execute_reply.started":"2024-07-02T02:01:46.877135Z","shell.execute_reply":"2024-07-02T02:01:46.884119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # train_main.melt(id_vars=\"study_id\")\n# # train_desc['series_description'].value_counts()\n# img = pydicom.dcmread(train_images[0])\n# plt.imshow(img.pixel_array, cmap='gray');","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.886324Z","iopub.execute_input":"2024-07-02T02:01:46.886979Z","iopub.status.idle":"2024-07-02T02:01:46.895649Z","shell.execute_reply.started":"2024-07-02T02:01:46.886919Z","shell.execute_reply":"2024-07-02T02:01:46.894760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# img.ImagePositionPatient, img.ImageOrientationPatient\n# img.ImagePositionPatient","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.896631Z","iopub.execute_input":"2024-07-02T02:01:46.896899Z","iopub.status.idle":"2024-07-02T02:01:46.906162Z","shell.execute_reply.started":"2024-07-02T02:01:46.896876Z","shell.execute_reply":"2024-07-02T02:01:46.905317Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Examples\n\nCredit to [theoviel](https://www.kaggle.com/code/theoviel/dicom-resized-png-jpg) for his notebook, which inspired this one","metadata":{}},{"cell_type":"code","source":"# for f in tqdm(train_images[:3]):\n#     patient = f.split('/')[-2]\n#     image = f.split('/')[-1][:-4]\n    \n#     dicom = pydicom.dcmread(f)\n#     img = dicom.pixel_array\n#     img = (img - img.min()) / (img.max() - img.min())\n    \n#     if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n#         img = 1 - img\n# #     pass\n#     plt.figure(figsize=(15, 15))\n#     plt.imshow(img, cmap=\"gray\")\n#     plt.title(f\"{patient} {image}\")\n#     plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.907193Z","iopub.execute_input":"2024-07-02T02:01:46.907508Z","iopub.status.idle":"2024-07-02T02:01:46.915877Z","shell.execute_reply.started":"2024-07-02T02:01:46.907484Z","shell.execute_reply":"2024-07-02T02:01:46.915084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Code adopted from [shubhamcodez](https://www.kaggle.com/code/shubhamcodez/rsna-resnet-starter-notebook) wonderful notebook","metadata":{}},{"cell_type":"code","source":"# base_path = f\"{str(DATA_PATH)}/train_images/\"\n\n# def get_image_paths(row):\n#     series_path = os.path.join(base_path, str(row['study_id']), str(row['series_id']))\n#     if os.path.exists(series_path):\n#         return [\n#             os.path.join(series_path, f) for f in os.listdir(series_path) if os.path.isfile(os.path.join(series_path, f))\n#         ]\n#     return []\n\n# # Mapping of series_description to conditions\n# condition_mapping = {\n#     'Sagittal T1': {'left': 'left_neural_foraminal_narrowing', 'right': 'right_neural_foraminal_narrowing'},\n#     'Axial T2': {'left': 'left_subarticular_stenosis', 'right': 'right_subarticular_stenosis'},\n#     'Sagittal T2/STIR': 'spinal_canal_stenosis'\n# }\n\n# # Create a list to store the expanded rows\n# expanded_rows = []\n\n# # Expand the dataframe by adding new rows for each file path\n# for index, row in tqdm(train_desc.iterrows(), total=len(train_desc)):\n#     image_paths = get_image_paths(row)\n#     conditions = condition_mapping.get(row['series_description'], {})\n#     if isinstance(conditions, str):  # Single condition\n#         conditions = {'left': conditions, 'right': conditions}\n#     for side, condition in conditions.items():\n#         for image_path in image_paths:\n#             expanded_rows.append({\n#                 'study_id': row['study_id'],\n#                 'series_id': row['series_id'],\n#                 'series_description': row['series_description'],\n#                 'image_path': image_path,\n#                 'condition': condition,\n#                 'row_id': f\"{row['study_id']}_{condition}\"\n#             })\n\n# # Create a new dataframe from the expanded rows\n# expanded_train_desc = pd.DataFrame(expanded_rows)\n\n# # Display the resulting dataframe\n# expanded_train_desc.head(5)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.916990Z","iopub.execute_input":"2024-07-02T02:01:46.917287Z","iopub.status.idle":"2024-07-02T02:01:46.926800Z","shell.execute_reply.started":"2024-07-02T02:01:46.917243Z","shell.execute_reply":"2024-07-02T02:01:46.925968Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Save the processed data\n\n**Images are quite big so resizing them is necessary**\n\n- Use 256 to train models first, or if you don't have a lot of compute\n\n- Use 512 to have competitive models","metadata":{"execution":{"iopub.status.busy":"2024-06-04T09:19:27.227605Z","iopub.execute_input":"2024-06-04T09:19:27.228003Z","iopub.status.idle":"2024-06-04T09:19:27.235637Z","shell.execute_reply.started":"2024-06-04T09:19:27.227973Z","shell.execute_reply":"2024-06-04T09:19:27.234289Z"}}},{"cell_type":"code","source":"SAVE_FOLDER = \"output/\"\nSIZE = 256\nEXTENSION = \"png\"\n\nos.makedirs(SAVE_FOLDER, exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.930599Z","iopub.execute_input":"2024-07-02T02:01:46.930937Z","iopub.status.idle":"2024-07-02T02:01:46.940143Z","shell.execute_reply.started":"2024-07-02T02:01:46.930907Z","shell.execute_reply":"2024-07-02T02:01:46.938903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def process(f, size=256, save_folder=\"\", extension=\"png\"):\n    study_id = f.split('/')[-3]\n    patient = f.split('/')[-2]\n    image = f.split('/')[-1][:-4]\n    \n    dicom = pydicom.dcmread(f)\n    img = dicom.pixel_array\n\n    img = (img - img.min()) / (img.max() - img.min())\n\n    if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        img = 1 - img\n\n    img = cv2.resize(img, (size, size))\n    \n    cv2.imwrite(save_folder + f\"{study_id}_{patient}_{image}.{extension}\", (img * 255).astype(np.uint8))","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.941432Z","iopub.execute_input":"2024-07-02T02:01:46.941906Z","iopub.status.idle":"2024-07-02T02:01:46.951648Z","shell.execute_reply.started":"2024-07-02T02:01:46.941875Z","shell.execute_reply":"2024-07-02T02:01:46.950505Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def convert_to_8bit(x, size=SIZE):\n    lower, upper = np.percentile(x, (1, 99))\n    x = np.clip(x, lower, upper)\n    x = x - np.min(x)\n    x = x / np.max(x)\n    x = cv2.resize(x, (size, size))\n    return (x * 255).astype(\"uint8\")\n\n\ndef convert2png(dicom_folder, plane, reverse_sort=False):\n    dicom_files = glob.glob(os.path.join(dicom_folder, \"*.dcm\"))\n#     dicom_files = sorted(dicom_files, key=lambda x: int(x.split('/')[-1].split('.')[0]))\n    \n    png_files = []\n    for i in range(len(dicom_files)):\n        folder = dicom_files[i]\n        folder = folder.replace(\".dcm\", \".png\").split(\"/\")\n        folder.insert(-1, plane)\n        png_files.append(\"/\".join(folder[4:]))\n    \n    dicoms = [pydicom.dcmread(f) for f in dicom_files]\n    plane = {\"sagittal\": 0, \"coronal\": 1, \"axial\": 2}[plane.lower()]\n    positions = np.array([float(d.ImagePositionPatient[plane]) for d in dicoms])\n    idx = np.argsort(-positions if reverse_sort else positions)\n    ipp = np.asarray([d.ImagePositionPatient for d in dicoms]).astype(\"uint8\")[idx]\n    array = np.stack([convert_to_8bit(d.pixel_array.astype(\"float32\")) for d in dicoms])\n    array = array[idx]\n#     return png_files\n    for i in range(len(png_files)):\n        img = array[i]\n#         img = cv2.resize(img, (SIZE, SIZE))\n        dst_path = png_files[i].replace(\"/\", \"_\")\n        cv2.imwrite(dst_path, img)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.952978Z","iopub.execute_input":"2024-07-02T02:01:46.953550Z","iopub.status.idle":"2024-07-02T02:01:46.969738Z","shell.execute_reply.started":"2024-07-02T02:01:46.953519Z","shell.execute_reply":"2024-07-02T02:01:46.968417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_dir = \"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images/\"\n\nfor i in tqdm(range(train_desc.shape[0])):\n    study = train_desc.loc[train_desc.study_id == train_desc.study_id.iloc[i]]\n    for row in study.itertuples():\n        if row.series_description == \"Sagittal T2/STIR\":\n            convert2png(os.path.join(image_dir, str(row.study_id), str(row.series_id)), plane='sagittal')\n        elif row.series_description == \"Sagittal T1\":\n            convert2png(os.path.join(image_dir, str(row.study_id), str(row.series_id)), plane='coronal')\n        elif row.series_description == \"Axial T2\":\n            convert2png(os.path.join(image_dir, str(row.study_id), str(row.series_id)), plane='axial')","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:01:46.971747Z","iopub.execute_input":"2024-07-02T02:01:46.972117Z","iopub.status.idle":"2024-07-02T02:02:48.478937Z","shell.execute_reply.started":"2024-07-02T02:01:46.972085Z","shell.execute_reply":"2024-07-02T02:02:48.477529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # process(expanded_train_desc.loc[0, 'image_path'])\n# train_images = expanded_train_desc.loc[:, 'image_path']","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:02:48.479720Z","iopub.status.idle":"2024-07-02T02:02:48.480117Z","shell.execute_reply.started":"2024-07-02T02:02:48.479933Z","shell.execute_reply":"2024-07-02T02:02:48.479949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# _ = Parallel(n_jobs=mp.cpu_count())(\n#     delayed(process)(uid, size=SIZE, save_folder=SAVE_FOLDER, extension=EXTENSION)\n#     for uid in tqdm(train_images)\n# )","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:02:48.481927Z","iopub.status.idle":"2024-07-02T02:02:48.482309Z","shell.execute_reply.started":"2024-07-02T02:02:48.482109Z","shell.execute_reply":"2024-07-02T02:02:48.482125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# study_id = 4003253\n# series_id = 702807833\n\n# glob.glob(f\"/kaggle/working/output/{study_id}_{series_id}_*.png\")","metadata":{"execution":{"iopub.status.busy":"2024-07-02T02:02:48.483756Z","iopub.status.idle":"2024-07-02T02:02:48.484092Z","shell.execute_reply.started":"2024-07-02T02:02:48.483929Z","shell.execute_reply":"2024-07-02T02:02:48.483943Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}