{"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":30698,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Used perplexity.ai to create dicom to png code\n- refined prompt to add window and level\n- refined prompt by asking for window and level values to be read from DICOM\n- for MRI, you want to use the DICOM values (CT can give different information with different window and levels)\n- asked it to handle multivalues for window and level\n- refined prompt to include PhotometricInterpretation\n- refined prompt to include scale and slope\n- modified code to use dicomsdl https://pypi.org/project/dicomsdl/ which is faster than pydicom\n- fixed mistake in PhotometricInterpretation","metadata":{}},{"cell_type":"code","source":"!pip install dicomsdl -q","metadata":{"execution":{"iopub.status.busy":"2024-05-29T00:10:58.560457Z","iopub.execute_input":"2024-05-29T00:10:58.561430Z","iopub.status.idle":"2024-05-29T00:11:10.274626Z","shell.execute_reply.started":"2024-05-29T00:10:58.561381Z","shell.execute_reply":"2024-05-29T00:11:10.273506Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#https://www.perplexity.ai/ with edits\n\nimport numpy as np\nimport dicomsdl\n\ndef mri_dicom_to_array(dicom_file):\n    \"\"\"\n    Convert a DICOM image to a PNG format with specified window center and width.\n\n    Args:\n        dicom_file (str): Path to the DICOM file.\n        window_center (int or float): Window center value.\n        window_width (int or float): Window width value.\n\n    Returns:\n        PIL Image\n    \"\"\"\n    ## Load the DICOM file\n    ds = dicomsdl.open(dicom_file)\n\n    ## Extract the pixel data from the DICOM file\n    #pixel_data = ds.pixel_array\n    pixel_data = ds.pixelData()\n    \n    # Apply RescaleIntercept and RescaleSlope if present\n    rescale_intercept = ds.RescaleIntercept if 'RescaleIntercept' in ds else 0\n    rescale_slope = ds.RescaleSlope if 'RescaleSlope' in ds else 1\n    pixel_data = pixel_data * rescale_slope + rescale_intercept\n    \n    # Handle different PhotometricInterpretations\n    photometric_interpretation = ds.PhotometricInterpretation\n    if photometric_interpretation == \"MONOCHROME1\":\n        pixel_data = np.invert(pixel_data)\n    elif photometric_interpretation == \"MONOCHROME2\":\n        pass  # No need to invert\n    else:\n        raise ValueError(f\"Unsupported PhotometricInterpretation: {photometric_interpretation}\")\n\n    ## Get window center and width from DICOM\n    window_center, window_width = get_window_values(ds)\n    \n    ## Apply window center and width\n    pixel_data = window_image(pixel_data, window_center, window_width)\n\n    ## Rescale the pixel data to the appropriate range for PNG\n    pixel_data = (pixel_data - pixel_data.min()) / (pixel_data.max() - pixel_data.min())\n    pixel_data = (pixel_data * 255).astype(np.uint8)\n\n    return pixel_data\n\ndef window_image(pixel_data, window_center, window_width):\n    \"\"\"\n    Apply window center and width to the pixel data.\n\n    Args:\n        pixel_data (numpy.ndarray): Pixel data from the DICOM file.\n        window_center (int or float): Window center value.\n        window_width (int or float): Window width value.\n\n    Returns:\n        numpy.ndarray: Windowed pixel data.\n    \"\"\"\n    img_min = window_center - window_width // 2\n    img_max = window_center + window_width // 2\n    pixel_data = np.clip(pixel_data, img_min, img_max)\n    pixel_data = (pixel_data - img_min) / (img_max - img_min)\n    return pixel_data\n\ndef get_window_values(ds):\n    \"\"\"\n    Get the window center and window width values from a DICOM file.\n    If the values are sequences, return the first item in the sequence.\n\n    Args:\n        dicom_file (str): Path to the DICOM file.\n\n    Returns:\n        tuple: A tuple containing the window center and window width values.\n    \"\"\"\n\n    ## Get the window center and window width values\n    window_center = ds.WindowCenter\n    window_width = ds.WindowWidth\n\n    ## Handle sequences\n    if isinstance(window_center, (list,tuple)):\n        window_center = window_center[0]\n\n    if isinstance(window_width, (list,tuple)):\n        window_width = window_width[0]\n\n    return window_center, window_width","metadata":{"execution":{"iopub.status.busy":"2024-05-29T00:28:20.632732Z","iopub.execute_input":"2024-05-29T00:28:20.633571Z","iopub.status.idle":"2024-05-29T00:28:20.644033Z","shell.execute_reply.started":"2024-05-29T00:28:20.633534Z","shell.execute_reply":"2024-05-29T00:28:20.642909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nfn = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/44036939/2828203845/15.dcm'\nim = mri_dicom_to_array(fn)\n\nplt.imshow(im,cmap='gray')","metadata":{"execution":{"iopub.status.busy":"2024-05-29T00:28:21.172358Z","iopub.execute_input":"2024-05-29T00:28:21.173191Z","iopub.status.idle":"2024-05-29T00:28:21.539682Z","shell.execute_reply.started":"2024-05-29T00:28:21.173152Z","shell.execute_reply":"2024-05-29T00:28:21.538830Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create volume from sequence","metadata":{}},{"cell_type":"code","source":"from pathlib import Path\n\ntrain_dir = Path('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images')\n\n#sort file_paths\ndef get_files(directory):\n    file_paths = []\n    for path in directory.rglob('*.dcm'):\n        if path.is_file():\n            file_paths.append(str(path))\n    #Sort images by filename\n    file_paths.sort(key = lambda o: int(o.split('/')[-1][:-4]))\n    return file_paths\n\n# read sorted files and create 3d array\ndef stack_series(directory):\n    file_paths = get_files(directory)\n    stack = np.array([mri_dicom_to_array(fn) for fn in file_paths])   \n    return stack\n","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:31:11.041852Z","iopub.execute_input":"2024-05-29T01:31:11.043084Z","iopub.status.idle":"2024-05-29T01:31:11.049792Z","shell.execute_reply.started":"2024-05-29T01:31:11.043037Z","shell.execute_reply":"2024-05-29T01:31:11.049005Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#get all studies\nstudies_paths = Path(train_dir).iterdir()\nstudies_paths = [o for o in studies_paths]\n\n#test one study\nstudy_i = 0\n#test one series\nseries_i = 0\n\nstudy_id = studies_paths[study_i]\nstudy_path = train_dir/study_id\n\nseries_paths = Path(study_path).iterdir()\nseries_paths = [o for o in series_paths]\n\nseries_path = series_paths[series_i]","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:31:12.595317Z","iopub.execute_input":"2024-05-29T01:31:12.596021Z","iopub.status.idle":"2024-05-29T01:31:12.609777Z","shell.execute_reply.started":"2024-05-29T01:31:12.595972Z","shell.execute_reply":"2024-05-29T01:31:12.608652Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"stack = stack_series(series_path)                 \nstack.shape","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:31:13.241515Z","iopub.execute_input":"2024-05-29T01:31:13.241918Z","iopub.status.idle":"2024-05-29T01:31:13.312724Z","shell.execute_reply.started":"2024-05-29T01:31:13.241865Z","shell.execute_reply":"2024-05-29T01:31:13.311567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"im = stack[2]\nplt.imshow(im, cmap = 'gray')","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:31:13.915629Z","iopub.execute_input":"2024-05-29T01:31:13.916277Z","iopub.status.idle":"2024-05-29T01:31:14.201853Z","shell.execute_reply.started":"2024-05-29T01:31:13.916245Z","shell.execute_reply":"2024-05-29T01:31:14.200860Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Show images ( and masks if available)\n#https://stackoverflow.com/questions/41071947/how-to-remove-the-space-between-subplots-in-matplotlib-pyplot\nfrom matplotlib import gridspec\n\ndef show_images(ims, masks=None, cols = 8):\n    rows = int(np.ceil(len(ims)/cols))\n    img_count = 0\n    \n    fig = plt.figure(figsize=(cols+1, rows+1)) \n\n    gs = gridspec.GridSpec(rows, cols,\n         wspace=0.0, hspace=0.0, \n         top=1.-0.5/(rows+1), bottom=0.5/(rows+1), \n         left=0.5/(cols+1), right=1-0.5/(cols+1)) \n    \n    for i in range(rows):\n        for j in range(cols):\n            if img_count < len(ims):\n                ax= plt.subplot(gs[i,j])\n                ax.imshow(ims[img_count], cmap='gray')\n                if masks is not None:\n                    ax.imshow(masks[img_count], alpha=0.5)\n                ax.set_xticklabels([])\n                ax.set_yticklabels([])\n                ax.set_axis_off()\n                \n                img_count+=1\n        \n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:27.490310Z","iopub.execute_input":"2024-05-29T01:33:27.491273Z","iopub.status.idle":"2024-05-29T01:33:27.499888Z","shell.execute_reply.started":"2024-05-29T01:33:27.491238Z","shell.execute_reply":"2024-05-29T01:33:27.498786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"study_i = 0\n#series_i = 0\n\nstudy_id = studies_paths[study_i]\nstudy_path = train_dir/study_id\n\nseries_paths = Path(study_path).iterdir()\nseries_paths = [o for o in series_paths]\n\nfor path in series_paths:\n    #print(path)\n    stack = stack_series(path)                 \n    show_images(stack)","metadata":{"execution":{"iopub.status.busy":"2024-05-29T01:33:30.201246Z","iopub.execute_input":"2024-05-29T01:33:30.201597Z","iopub.status.idle":"2024-05-29T01:33:36.398710Z","shell.execute_reply.started":"2024-05-29T01:33:30.201571Z","shell.execute_reply":"2024-05-29T01:33:36.395237Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}