{"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":30746,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np \nimport pandas as pd \nimport matplotlib.pyplot as plt \nfrom tqdm import tqdm\nimport glob\nimport pydicom\nimport cv2\nimport os\nimport re","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-08-25T14:32:35.092110Z","iopub.execute_input":"2024-08-25T14:32:35.093371Z","iopub.status.idle":"2024-08-25T14:32:35.099257Z","shell.execute_reply.started":"2024-08-25T14:32:35.093335Z","shell.execute_reply":"2024-08-25T14:32:35.097892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# globally used constants\nBASE_PATH = \"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification\"\nTRAIN_SERIES_DESCRIPTION_PATH = \"train_series_descriptions.csv\"\nTRAIN_LABEL_COORDINATES_PATH = \"train_label_coordinates.csv\"\nIMAGE_X = 512\nIMAGE_y = 512","metadata":{"execution":{"iopub.status.busy":"2024-08-25T14:32:36.488645Z","iopub.execute_input":"2024-08-25T14:32:36.489045Z","iopub.status.idle":"2024-08-25T14:32:36.494506Z","shell.execute_reply.started":"2024-08-25T14:32:36.489018Z","shell.execute_reply":"2024-08-25T14:32:36.493239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_series_description_df = pd.read_csv(BASE_PATH+\"/\"+TRAIN_SERIES_DESCRIPTION_PATH)\ntrain_label_coordinates_df  = pd.read_csv(BASE_PATH+\"/\"+TRAIN_LABEL_COORDINATES_PATH)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T14:32:36.710702Z","iopub.execute_input":"2024-08-25T14:32:36.711112Z","iopub.status.idle":"2024-08-25T14:32:36.873299Z","shell.execute_reply.started":"2024-08-25T14:32:36.711079Z","shell.execute_reply":"2024-08-25T14:32:36.872053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_series_description_df.head(5)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T14:32:36.889681Z","iopub.execute_input":"2024-08-25T14:32:36.890097Z","iopub.status.idle":"2024-08-25T14:32:36.912705Z","shell.execute_reply.started":"2024-08-25T14:32:36.890052Z","shell.execute_reply":"2024-08-25T14:32:36.911428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_label_coordinates_df.head(5)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T14:32:37.026933Z","iopub.execute_input":"2024-08-25T14:32:37.027381Z","iopub.status.idle":"2024-08-25T14:32:37.045661Z","shell.execute_reply.started":"2024-08-25T14:32:37.027344Z","shell.execute_reply":"2024-08-25T14:32:37.044443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"study_ids = train_series_description_df['study_id'].unique()\nstudy_ids[:3], len(study_ids)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T14:32:37.142754Z","iopub.execute_input":"2024-08-25T14:32:37.143115Z","iopub.status.idle":"2024-08-25T14:32:37.155743Z","shell.execute_reply.started":"2024-08-25T14:32:37.143087Z","shell.execute_reply":"2024-08-25T14:32:37.154428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DICOMDatasetConvertor:\n    def __init__(self,base_path, train_series_description_path, train_label_coordinates_path, IMAGE_X, IMAGE_Y):\n        self.base_path = base_path\n        self.train_series_description_path = train_series_description_path\n        self.train_label_coordinates_path  = train_label_coordinates_path\n        self.IMAGE_X = IMAGE_X\n        self.IMAGE_Y = IMAGE_Y\n        self.desc = ['Sagittal T2/STIR', 'Sagittal T1', 'Axial T2']\n    \n    def atoi(self, text):\n        return int(text) if text.isdigit() else text\n\n    def natural_keys(self, text):\n        return [ self.atoi(c) for c in re.split(r'(\\d+)', text) ]\n\n    def read_dicom(self, filepath):\n        dicom = pydicom.dcmread(filepath)\n        image = dicom.pixel_array\n        return image\n    \n    def read_csv(self, filepath):\n        return pd.read_csv(filepath)\n    \n    def process_image(self, image):\n        image = (image - image.min()) / (image.max() - image.min() +1e-6) * 255\n        img = cv2.resize(image, (self.IMAGE_X, self.IMAGE_Y),interpolation=cv2.INTER_CUBIC)\n        return img\n    \n    def save_as_png(self, image, filepath):\n        cv2.imwrite(filepath, image)\n    \n    def search_imgs(self, df):\n        allImages = []\n        for i, row in df.iterrows():\n            pimgs = glob.glob(f'{BASE_PATH}/train_images/{row[\"study_id\"]}/{row[\"series_id\"]}/*.dcm')\n            pimgs = sorted(pimgs, key=self.natural_keys)\n            allImages.extend(pimgs)\n        return allImages\n    \n    def process_axial_images(self, allImages, study_id, ds):\n        for j, imgPath in enumerate(allImages):\n            dst = f'cvt_png/{study_id}/{ds}/{j:03d}.png'\n            self.save_as_png(self.process_image(self.read_dicom(imgPath)), dst)\n            \n    def process_saggital_t1_images(self, allImages, study_id, ds_):\n        steps = len(allImages)/10.0\n        start = len(allImages)/2.0 - 4.0*steps\n        end = len(allImages)+0.0001\n        for j, i in enumerate(np.arange(start, end, steps)):\n            dst = f'cvt_png/{study_id}/{ds_}/{j:03d}.png'\n            ind = max(0, int((i-0.5001).round()))\n            self.save_as_png(self.process_image(self.read_dicom(allImages[ind])), dst)\n        \n        \n    def process_saggital_t2_images(self, allImages, study_id, ds_):\n        steps = len(allImages)/10.0\n        start = len(allImages)/2.0 - 4.0*steps\n        end = len(allImages)+0.0001\n        for j, i in enumerate(np.arange(start, end, steps)):\n            dst = f'cvt_png/{study_id}/{ds_}/{j:03d}.png'\n            ind = max(0, int((i-0.5001).round()))\n            self.save_as_png(self.process_image(self.read_dicom(allImages[ind])), dst)\n        \n    def process_study_id(self, study_id):\n        #print(\"processing the study_id::\", study_id)\n        temp_df = self.train_series_description_df[self.train_series_description_df['study_id'] == study_id]\n        for description in self.desc:\n            modifiedDescription = description.replace('/', '_')\n            temp_df_ = temp_df[temp_df[\"series_description\"] == description]\n            os.makedirs(f'cvt_png/{study_id}/{modifiedDescription}', exist_ok=True)\n            allImages = self.search_imgs(temp_df_)\n            if(len(allImages)==0):\n                print(study_id, \"with \", description,\": has no images\")\n                continue\n            if description == \"Axial T2\":\n                self.process_axial_images(allImages, study_id , modifiedDescription)\n            elif description == \"Sagittal T1\":\n                self.process_saggital_t1_images(allImages, study_id , modifiedDescription)\n            elif description == \"Sagittal T2/STIR\":\n                self.process_saggital_t2_images(allImages,study_id,  modifiedDescription)\n        \n    def process(self):\n        self.train_series_description_df = self.read_csv(self.base_path + \"/\" + self.train_series_description_path)\n        self.train_label_coordinates_df  = self.read_csv(self.base_path + \"/\" + self.train_label_coordinates_path)\n        study_ids = self.train_series_description_df['study_id'].unique()\n        for index, study_id in enumerate(tqdm(study_ids, total = len(study_ids))):\n            self.process_study_id(study_id)\n        \n        \n        \n    ","metadata":{"execution":{"iopub.status.busy":"2024-08-25T14:32:37.659717Z","iopub.execute_input":"2024-08-25T14:32:37.660082Z","iopub.status.idle":"2024-08-25T14:32:37.686965Z","shell.execute_reply.started":"2024-08-25T14:32:37.660055Z","shell.execute_reply":"2024-08-25T14:32:37.685864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imageProcessor = DICOMDatasetConvertor(BASE_PATH, TRAIN_SERIES_DESCRIPTION_PATH, TRAIN_LABEL_COORDINATES_PATH, 512, 512)\nimageProcessor.process()","metadata":{"execution":{"iopub.status.busy":"2024-08-25T14:32:37.809276Z","iopub.execute_input":"2024-08-25T14:32:37.809665Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"INSPIRED FROM https://www.kaggle.com/code/itsuki9180/rsna2024-lsdc-making-dataset/notebook","metadata":{}}]}