{"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":"none","dataSources":[{"sourceType":"competition","sourceId":71549,"databundleVersionId":8561470}],"dockerImageVersionId":30786,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Data Preprocessing","metadata":{}},{"cell_type":"code","source":"!pip -q install natsort","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport glob\nimport pydicom\nimport cv2\nfrom natsort import natsorted","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-28T15:56:23.610944Z","iopub.execute_input":"2024-11-28T15:56:23.611292Z","iopub.status.idle":"2024-11-28T15:56:23.643067Z","shell.execute_reply.started":"2024-11-28T15:56:23.611261Z","shell.execute_reply":"2024-11-28T15:56:23.641886Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_KAGGLE_DIR = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-28T15:56:23.644368Z","iopub.execute_input":"2024-11-28T15:56:23.644735Z","iopub.status.idle":"2024-11-28T15:56:23.649186Z","shell.execute_reply.started":"2024-11-28T15:56:23.644706Z","shell.execute_reply":"2024-11-28T15:56:23.648091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class dotdict(dict):\n    __setattr__ = dict.__setitem__\n    __delattr__ = dict.__delitem__\n\n    def __getattr__(self, name):\n        try:\n            return self[name]\n        except KeyError:\n            raise AttributeError(name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-28T15:59:13.639565Z","iopub.execute_input":"2024-11-28T15:59:13.639968Z","iopub.status.idle":"2024-11-28T15:59:13.645488Z","shell.execute_reply.started":"2024-11-28T15:59:13.639933Z","shell.execute_reply":"2024-11-28T15:59:13.644380Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Normalize image to 8-bit grayscale format\ndef normalise_to_8bit(x, lower = 0.1, upper = 99.9):\n    # Get 0.1 and 99.9 percentile\n    lower, upper = np.percentile(x, (lower, upper))\n    # Clip values below 0.1 and above 99.9 percentile\n    x = np.clip(x, lower, upper)\n    # Shift x so min becomes 0\n    x = x - np.min(x)\n    # Scale values between 0 and 1\n    x = x / np.max(x)\n    return (x * 255).astype(np.uint8)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-28T15:56:30.407203Z","iopub.execute_input":"2024-11-28T15:56:30.407595Z","iopub.status.idle":"2024-11-28T15:56:30.413562Z","shell.execute_reply.started":"2024-11-28T15:56:30.407562Z","shell.execute_reply":"2024-11-28T15:56:30.412520Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Read series\ndef read_series(stuy_id, series_id, series_description):\n    error_code = ''\n\n    dicom_dir = f'{DATA_KAGGLE_DIR}/train_images/{study_id}/{series_id}'\n\n    # Read dicom file\n    dicom_file = natsorted(glob.glob(f'{dicom_dir}/*.dcm'))\n    instance_number = [int(d.split('/')[-1].split('.')[0]) for d in dicom_file]\n    dicom = [pydicom.dcmread(d) for d in dicom_file]\n\n    # Make dicom header df\n    dicom_df = []\n    for i, d in zip(instance_number, dicom):\n        dicom_df.append(\n            dotdict(\n                study_id = study_id,\n                series_id = series_id,\n                series_description = series_description,\n                instance_number = i,\n                ImagePositionPatient = [float(v) for v in d.ImagePositionPatient],\n                ImageOrientationPatient = [float(v) for v in d.ImageOrientationPatient],\n                PixelSpacing = [float(v) for v in d.PixelSpacing],\n                SpacingBetweenSlices = float(d.SpacingBetweenSlices),\n                SliceThickness = float(d.SliceThickness),\n                grouping = str([round(float(v), 3) for v in d.ImageOrientationPatient]),\n                H = d.pixel_array.shape[0],\n                W = d.pixel_array.shape[1],\n            )\n        )\n\n    dicom_df = pd.DataFrame(dicom_df)\n    # If slices are of different dimensions for a given study-series\n    if dicom_df.W.nunique() != 1 or dicom_df.H.nunique() != 1:\n        error_code = 'multi-shape'\n\n    Wmax = dicom_df.W.max()\n    Hmax = dicom_df.H.max()\n\n    # Sort slices by ImageOrientationPatient\n    dicom_df = [g for _, g in dicom_df.groupby('grouping')]\n\n    data = []\n    sorted_data_by_group = []\n\n    # Find projection\n    for df in dicom_df:\n        position = np.array(df.ImagePositionPatient.values.tolist())\n        orientation = np.array(df.ImageOrientationPatient.values.tolist())\n        normal = np.cross(orientation[:, :3], orientation[:, 3:])\n        projection = np.sum(normal*position, 1)\n        df.loc[:, 'projection'] = projection\n        df = df.sort_values('projection')\n\n        assert len(df.SliceThickness.unique()) == 1\n\n        # Stack the images to create volume\n        volume = []\n        \n    ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"study_id = 267842058\nseries_id = 894248358\nseries_description = 'sagittal_t1'\nerror_code = \"\"\ndicom_dir = f'{DATA_KAGGLE_DIR}/train_images/{study_id}/{series_id}'\n\n# Read dicom file\ndicom_file = natsorted(glob.glob(f'{dicom_dir}/*.dcm'))\ninstance_number = [int(d.split('/')[-1].split('.')[0]) for d in dicom_file]\ndicom = [pydicom.dcmread(d) for d in dicom_file]\n\n# Make dicom header df\ndicom_df = []\nfor i, d in zip(instance_number, dicom):\n    dicom_df.append(\n        dotdict(\n            study_id = study_id,\n            series_id = series_id,\n            series_description = series_description,\n            instance_number = i,\n            ImagePositionPatient = [float(v) for v in d.ImagePositionPatient],\n            ImageOrientationPatient = [float(v) for v in d.ImageOrientationPatient],\n            PixelSpacing = [float(v) for v in d.PixelSpacing],\n            SpacingBetweenSlices = float(d.SpacingBetweenSlices),\n            SliceThickness = float(d.SliceThickness),\n            grouping = str([round(float(v), 3) for v in d.ImageOrientationPatient]),\n            H = d.pixel_array.shape[0],\n            W = d.pixel_array.shape[1],\n        )\n    )\n\ndicom_df = pd.DataFrame(dicom_df)\ndicom_df.head(10)\n# If slices are of different dimensions for a given study-series\nif dicom_df.W.nunique() != 1 or dicom_df.H.nunique() != 1:\n    error_code = 'multi-shape'\n\nWmax = dicom_df.W.max()\nHmax = dicom_df.H.max()\n\n# Sort slices by ImageOrientationPatient\ndicom_df = [g for _, g in dicom_df.groupby('grouping')]\n\ndata = []\nsorted_data_by_group = []\n\nfor df in dicom_df:\n    position = np.array(df.ImagePositionPatient.values.tolist())\n    orientation = np.array(df.ImageOrientationPatient.values.tolist())\n    normal = np.cross(orientation[:, :3], orientation[:, 3:])\n    projection = np.sum(normal*position, 1)\n    df.loc[:, 'projection'] = projection\n    df = df.sort_values('projection')\n\n    assert len(df.SliceThickness.unique()) == 1\n\n    # Stack the images to create volume\n    volume = []\n    for i in df.instance_number:\n        v = dicom[instance_number.index(i)].pixel_array\n        if error_code.find('multi-shape') != -1:\n            H, W = v.shape\n            v = np.pad(v, [(0, Hmax-H), (0, Wmax-W)], 'reflect')\n        volume.append(v)\n\n    volume = np.stack(volume)\n    volume = normalise_to_8bit(volume)\n\n    data.append(dotdict(\n        df = df,\n        volume = volume\n    ))\n\n    print(df)\n    print(position[0, 0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-28T16:53:07.888200Z","iopub.execute_input":"2024-11-28T16:53:07.888627Z","iopub.status.idle":"2024-11-28T16:53:08.101180Z","shell.execute_reply.started":"2024-11-28T16:53:07.888592Z","shell.execute_reply":"2024-11-28T16:53:08.100171Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for df in dicom_df:\n    position = np.array(df.ImagePositionPatient.values.tolist())\n    print(type(position))\n    print(position)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-28T16:10:57.247236Z","iopub.execute_input":"2024-11-28T16:10:57.247626Z","iopub.status.idle":"2024-11-28T16:10:57.253948Z","shell.execute_reply.started":"2024-11-28T16:10:57.247592Z","shell.execute_reply":"2024-11-28T16:10:57.252868Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"normal = np.array([[1, 0, 0], [0, 1, 0]])  # Unit vectors along x and y axes\nposition = np.array([[3, 4, 0], [5, 6, 0]])  # Arbitrary 3D vectors\n\nprojection = np.sum(normal * position, 1)  # Compute projection\nprint(projection)  # Output: [3, 6]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-28T16:19:20.203117Z","iopub.execute_input":"2024-11-28T16:19:20.203529Z","iopub.status.idle":"2024-11-28T16:19:20.210280Z","shell.execute_reply.started":"2024-11-28T16:19:20.203495Z","shell.execute_reply":"2024-11-28T16:19:20.209187Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}