{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -qU \"python-gdcm\" pydicom pylibjpeg \"opencv-python-headless\"","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-08-01T22:32:55.152952Z","iopub.execute_input":"2022-08-01T22:32:55.153445Z","iopub.status.idle":"2022-08-01T22:33:17.338725Z","shell.execute_reply.started":"2022-08-01T22:32:55.153345Z","shell.execute_reply":"2022-08-01T22:33:17.337692Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%matplotlib inline\n\nimport os\nimport glob\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nPATH_DATASET = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection\"","metadata":{"execution":{"iopub.status.busy":"2022-08-01T22:33:17.341213Z","iopub.execute_input":"2022-08-01T22:33:17.341604Z","iopub.status.idle":"2022-08-01T22:33:17.351343Z","shell.execute_reply.started":"2022-08-01T22:33:17.341569Z","shell.execute_reply":"2022-08-01T22:33:17.350058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Image sizes","metadata":{}},{"cell_type":"code","source":"import pydicom\nfrom tqdm.auto import tqdm\n\nls_study = [d for d in glob.glob(os.path.join(PATH_DATASET, \"train_images\", \"*\")) if os.path.isdir(d)]\n\nsizes = []\nfor p_dir in tqdm(ls_study):\n    ls_imgs = glob.glob(os.path.join(p_dir, \"*.dcm\"))\n    dicom = pydicom.dcmread(ls_imgs[0])\n    im_size = dicom.pixel_array.shape\n    sizes.append({\"width\": im_size[1], \"height\": im_size[0], \"depth\": len(ls_imgs)})","metadata":{"execution":{"iopub.status.busy":"2022-08-01T22:33:17.353303Z","iopub.execute_input":"2022-08-01T22:33:17.356299Z","iopub.status.idle":"2022-08-01T22:35:47.866011Z","shell.execute_reply.started":"2022-08-01T22:33:17.356234Z","shell.execute_reply":"2022-08-01T22:35:47.86403Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sizes = pd.DataFrame(sizes).groupby([\"width\", \"height\"], as_index=False).size()\ndisplay(df_sizes.head())\n\n# import plotly.express as px\n# fig = px.scatter(df_sizes, x=\"width\", y=\"height\", size=\"size\", height=400, width=400)\n# fig.update_yaxes(scaleanchor=\"x\", scaleratio=1)\n# fig.show() ","metadata":{"execution":{"iopub.status.busy":"2022-08-01T22:35:47.870894Z","iopub.execute_input":"2022-08-01T22:35:47.871748Z","iopub.status.idle":"2022-08-01T22:35:47.942286Z","shell.execute_reply.started":"2022-08-01T22:35:47.871692Z","shell.execute_reply":"2022-08-01T22:35:47.940947Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ax = pd.DataFrame(sizes)[\"depth\"].hist(bins=50, figsize=(10, 5))\nax.set_xlabel(\"image depth\")\nax.set_ylabel(\"counts\")","metadata":{"execution":{"iopub.status.busy":"2022-08-01T22:35:47.943514Z","iopub.execute_input":"2022-08-01T22:35:47.943842Z","iopub.status.idle":"2022-08-01T22:35:48.528478Z","shell.execute_reply.started":"2022-08-01T22:35:47.943792Z","shell.execute_reply":"2022-08-01T22:35:48.527005Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load 🤖 convert DICOM to PNG + denoising + equalizing\n\nNOTE: to reduce redundant image information I scaled the omage to 40% and there is also option to crop\n\n> If croped, for ploting bouding box annotations you need to adjust postions too","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"import cv2\nfrom PIL import Image\nfrom pydicom.pixel_data_handlers import apply_voi_lut\n\ndef conver_image(dicom_path: str, output_dir: str = \"train_images\", scale: float = 0.50, crop: float = 0):\n    dicom = pydicom.dcmread(dicom_path)\n    img = apply_voi_lut(dicom.pixel_array, dicom)\n    \n    if 0 < crop < 1:\n        crop = int(min(img.shape) * crop)\n    if crop:\n        img = img[crop:-crop, crop:-crop]\n    \n    if scale is not None:\n        dim = int(img.shape[1] * scale), int(img.shape[0] * scale)\n        img = cv2.resize(img, dim, interpolation=cv2.INTER_LINEAR)\n\n    img = (img - img.min()) / float(img.max() - img.min())\n    img = np.clip(img * 255, 0, 255).astype(np.uint8)\n    \n    # denoising of image saving it into dst image\n    img = cv2.fastNlMeansDenoising(img, h=4)\n\n    # create a CLAHE object (Arguments are optional).\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))\n    img = clahe.apply(img)\n\n    study_id = os.path.basename(os.path.dirname(dicom_path))\n    out_dir = os.path.join(output_dir, study_id)\n    os.makedirs(out_dir, exist_ok=True)\n    img_name, _ = os.path.splitext(os.path.basename(dicom_path))\n    png_path = os.path.join(out_dir, f\"{img_name}.png\")\n\n    img = Image.fromarray(img)\n    img.save(png_path, \"PNG\", optimize=False, compress_level=9)","metadata":{"execution":{"iopub.status.busy":"2022-08-01T22:40:27.760174Z","iopub.execute_input":"2022-08-01T22:40:27.760676Z","iopub.status.idle":"2022-08-01T22:40:27.776849Z","shell.execute_reply.started":"2022-08-01T22:40:27.760636Z","shell.execute_reply":"2022-08-01T22:40:27.775614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Process all images 🤖","metadata":{}},{"cell_type":"code","source":"from joblib import Parallel, delayed\n\n! rm -rf /tmp/train_images\n! mkdir /tmp/train_images\n\nls_imgs = glob.glob(os.path.join(PATH_DATASET, \"train_images\", \"**\", \"*.dcm\"), recursive=True)\nprint(f\"images: {len(ls_imgs)}\")\n\n_= Parallel(n_jobs=5)(\n    delayed(conver_image)(p_img, output_dir=\"/tmp/train_images\", scale=0.4)\n    for p_img in tqdm(ls_imgs)\n)\n\n# !ls -hl train_images/**/","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-08-01T22:40:50.352404Z","iopub.execute_input":"2022-08-01T22:40:50.352826Z","iopub.status.idle":"2022-08-01T22:42:19.719864Z","shell.execute_reply.started":"2022-08-01T22:40:50.352776Z","shell.execute_reply":"2022-08-01T22:42:19.717996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"! rm -rf train_images\n! mkdir train_images\n\nimport shutil\n\nls_dir = [d for d in glob.glob(\"/tmp/train_images/*\") if os.path.isdir(d)]\nfor p_dir in tqdm(ls_dir):\n    zip_file = os.path.join(\"train_images\", f\"{os.path.basename(p_dir)}.zip\")\n    shutil.make_archive(zip_file, 'zip', p_dir)\n\n! ls -lh train_images","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-08-01T22:42:22.215838Z","iopub.execute_input":"2022-08-01T22:42:22.217401Z","iopub.status.idle":"2022-08-01T22:42:26.200767Z","shell.execute_reply.started":"2022-08-01T22:42:22.217291Z","shell.execute_reply":"2022-08-01T22:42:26.199227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Show 🔎 few samples","metadata":{}},{"cell_type":"code","source":"ls_imgs = glob.glob(os.path.join(\"/tmp/train_images/\", \"**\", \"*.png\"), recursive=True)\nnp.random.shuffle(ls_imgs)\n\nfig, axarr = plt.subplots(nrows=3, ncols=2, figsize=(8, 10))\nfor i, p_img in enumerate(ls_imgs[:6]):\n    img = plt.imread(p_img)\n    print(f\"{p_img} >> {img.shape}\")\n    axarr[i // 2, i % 2].imshow(img, cmap=\"gray\")\nfig.tight_layout()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}