{"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":"markdown","source":"# Install Libraries","metadata":{}},{"cell_type":"code","source":"!pip install -q python-gdcm\n!pip install -q pylibjpeg\n!pip install -q dicomsdl","metadata":{"execution":{"iopub.status.busy":"2022-12-21T06:00:15.71768Z","iopub.status.idle":"2022-12-21T06:00:15.718143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Import Libraries","metadata":{}},{"cell_type":"code","source":"import os,shutil, pandas as pd\nfrom glob import glob\nfrom tqdm import tqdm\nimport zipfile\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt","metadata":{"execution":{"iopub.status.busy":"2022-12-21T06:00:15.719239Z","iopub.status.idle":"2022-12-21T06:00:15.719709Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Path","metadata":{}},{"cell_type":"code","source":"ROOT_PATH = '/kaggle/input/rsna-breast-cancer-detection'\nIMG_DIR = '/tmp/Dataset/rsna-bcd'","metadata":{"execution":{"iopub.status.busy":"2022-12-21T06:00:15.720621Z","iopub.status.idle":"2022-12-21T06:00:15.721074Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Exp. Config","metadata":{}},{"cell_type":"code","source":"SIZE = 300 # number dicom to process for test\nNUM_SAMPLES = 10 # number of samples to show\nDATA = {}","metadata":{"execution":{"iopub.status.busy":"2022-12-21T06:00:37.684373Z","iopub.execute_input":"2022-12-21T06:00:37.684739Z","iopub.status.idle":"2022-12-21T06:00:37.689503Z","shell.execute_reply.started":"2022-12-21T06:00:37.684709Z","shell.execute_reply":"2022-12-21T06:00:37.688428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Meta Data","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv(f'{ROOT_PATH}/train.csv')\ntrain_df['image_path'] = f'{ROOT_PATH}/train_images' + '/' + train_df.patient_id.astype(str) + '/' + train_df.image_id.astype(str) + '.dcm'\nprint('Train:')\nprint(f'# Size: {len(train_df)}')\ndisplay(train_df.head())\n\ntest_df = pd.read_csv(f'{ROOT_PATH}/test.csv')\ntest_df['image_path'] = f'{ROOT_PATH}/test_images' + '/' + test_df.patient_id.astype(str) + '/' + test_df.image_id.astype(str) + '.dcm'\nprint('Test:')\nprint(f'# Size: {len(test_df)}')\ndisplay(test_df.head())","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:38.155284Z","iopub.execute_input":"2022-12-21T06:00:38.155679Z","iopub.status.idle":"2022-12-21T06:00:38.425278Z","shell.execute_reply.started":"2022-12-21T06:00:38.155647Z","shell.execute_reply":"2022-12-21T06:00:38.424201Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Filepaths","metadata":{}},{"cell_type":"code","source":"file_paths = train_df.groupby(['patient_id']).sample(1).image_path[:SIZE].tolist()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:40.415876Z","iopub.execute_input":"2022-12-21T06:00:40.416225Z","iopub.status.idle":"2022-12-21T06:00:59.402681Z","shell.execute_reply.started":"2022-12-21T06:00:40.416195Z","shell.execute_reply":"2022-12-21T06:00:59.401035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# `.dcm` to `.png`","metadata":{}},{"cell_type":"code","source":"!rm -r {IMG_DIR}\nos.makedirs(f'{IMG_DIR}/train_images', exist_ok = True)\nos.makedirs(f'{IMG_DIR}/test_images', exist_ok = True)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:59.406177Z","iopub.execute_input":"2022-12-21T06:00:59.406617Z","iopub.status.idle":"2022-12-21T06:01:00.536567Z","shell.execute_reply.started":"2022-12-21T06:00:59.406582Z","shell.execute_reply":"2022-12-21T06:01:00.53493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utils","metadata":{}},{"cell_type":"code","source":"import dicomsdl\nimport cv2\nimport time\nfrom scipy import stats\n\ndef read_img(path):\n    return cv2.imread(path)\n\ndef read_xray(path, fix_monochrome = True):\n    dicom = dicomsdl.open(path)\n    h0, w0 = dicom.Rows, dicom.Columns\n    data = dicom.pixelData(storedvalue=False)  # storedvalue = True for int16 return otherwise float32\n    data = data - np.min(data)\n    data = data / np.max(data)\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = 1.0 - data\n    data = (data * 255).astype(np.uint8)\n    return data, h0, w0\n\ndef get_img2roi(roi_fn, roi_algo):\n    def img2roi(file_path):\n        # read dicom\n        img, h0, w0 = read_xray(file_path)\n\n        # extract roi\n        img = roi_fn(img)\n        \n        # get new size\n        h, w = img.shape[:2]\n\n        # process path\n        sub_path = file_path.split(\"/\",4)[-1].split('.dcm')[0] + '.png'\n        infos = sub_path.split('/')\n        pid = infos[-2]\n        iid = infos[-1]; iid = iid.replace('.png','')\n        new_path = os.path.join(IMG_DIR, roi_algo, f'{pid}_{iid}.png')\n        os.makedirs(new_path.rsplit('/',1)[0], exist_ok=True)\n\n        # save img\n        cv2.imwrite(new_path, img)\n        return new_path, h, w, h0, w0\n    return img2roi\n\ndef check(roi_fn, roi_algo):\n    img2roi = get_img2roi(roi_fn, roi_algo)\n    \n    tick = time.time()\n    infos = [img2roi(file_path) for file_path in tqdm(file_paths, leave=True, position=0)]\n    tock = time.time()\n    \n    new_path, h, w, h0, w0 = list(zip(*infos))\n    df = pd.DataFrame({'image_path':new_path,\n                           'width':w,\n                           'height':h,\n                           'width0':w0,\n                           'height0':h0})\n    \n    duration = (tock - tick)\n    return df, duration\n\ndef plot_size(df, name):\n    plt.figure(figsize=(8,6))\n    \n    res0 = stats.linregress(df.width0, df.height0)\n    plt.plot(df.width0, df.height0, 'bo')\n    plt.plot(df.width0, res0.intercept + res0.slope*df.width0, '-.b',)\n    \n    res = stats.linregress(df.width, df.height)\n    plt.plot(df.width, df.height, 'ro')\n    plt.plot(df.width, res.intercept + res.slope*df.width, '-.r',)\n    \n    plt.xlabel('Width', fontsize=12)\n    plt.ylabel('Height', fontsize=12)\n    plt.title(f\"Original Vs {name}-ROI\\nR = (H/W) = {res.slope:0.3f}\", fontsize=13)\n    plt.legend(['original',\n                'original fitted',\n                f'{name}-roi',\n                f'roi-{name} fitted'],\n               fontsize=12)\n    plt.tight_layout()\n    plt.show()\n    \ndef show_roi(df, num=NUM_SAMPLES):\n    plt.figure(figsize=(num*3,8*2))\n    for i, path in enumerate(df.image_path.iloc[:num*2]):\n        plt.subplot(2, num, i+1)\n        img = read_img(path)\n        plt.imshow(img)\n    plt.tight_layout()\n    plt.show()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:01:00.538527Z","iopub.execute_input":"2022-12-21T06:01:00.538859Z","iopub.status.idle":"2022-12-21T06:01:00.567318Z","shell.execute_reply.started":"2022-12-21T06:01:00.538825Z","shell.execute_reply":"2022-12-21T06:01:00.566224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Non Zero (NZ)","metadata":{}},{"cell_type":"markdown","source":"## code","metadata":{}},{"cell_type":"code","source":"def nz(img):\n    \"\"\"Remove zero part using min-max\"\"\"\n    # binarize timage\n    bin_img = cv2.threshold(img, 20, 255, cv2.THRESH_BINARY)[1]\n    \n    # non-zero position\n    ys, xs = np.nonzero(bin_img)\n    # extact roi\n    roi =  img[np.min(ys):np.max(ys), np.min(xs):np.max(xs)]\n    \n    return roi","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:01:00.569172Z","iopub.execute_input":"2022-12-21T06:01:00.569538Z","iopub.status.idle":"2022-12-21T06:01:00.596254Z","shell.execute_reply.started":"2022-12-21T06:01:00.569502Z","shell.execute_reply":"2022-12-21T06:01:00.594608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## test","metadata":{}},{"cell_type":"code","source":"name = 'nz'\nDATA[name] = {}\nDATA[name]['df'], DATA[name]['time'] = check(eval(name), 'nz')\nprint('\\n# Name: {} | Time: {:0.3f} s'.format(name, DATA[name]['time']))\nplot_size(DATA[name]['df'], name)\nprint('# Samples')\nshow_roi(DATA[name]['df'])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:01:00.59839Z","iopub.execute_input":"2022-12-21T06:01:00.598752Z","iopub.status.idle":"2022-12-21T06:07:28.414586Z","shell.execute_reply.started":"2022-12-21T06:01:00.598718Z","shell.execute_reply":"2022-12-21T06:07:28.413605Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Binary Erode Largest Contour (BELC)","metadata":{}},{"cell_type":"markdown","source":"## code","metadata":{}},{"cell_type":"code","source":"def belc(img):\n    \"\"\"Binary Erode Largest Contour\"\"\"\n    # binarize timage\n    bin_img = cv2.threshold(img, 20, 255, cv2.THRESH_BINARY)[1]\n    \n    # remove straighnt line\n#     bin_img = cv2.medianBlur(bin_img, 11)\n    bin_img = cv2.erode(bin_img, np.ones((11, 11)))\n\n    # create contours and keep only the largest one\n    contours, _ = cv2.findContours(bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)\n    contour = max(contours, key=cv2.contourArea)\n\n   # Find ROI from largest contour\n    xs = contour.squeeze()[:, 0]\n    ys = contour.squeeze()[:, 1]\n    roi =  img[np.min(ys):np.max(ys), np.min(xs):np.max(xs)]\n    \n    return roi","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:08:56.909347Z","iopub.execute_input":"2022-12-21T06:08:56.909744Z","iopub.status.idle":"2022-12-21T06:08:56.918807Z","shell.execute_reply.started":"2022-12-21T06:08:56.909715Z","shell.execute_reply":"2022-12-21T06:08:56.917664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## test","metadata":{}},{"cell_type":"code","source":"name = 'belc'\nDATA[name] = {}\nDATA[name]['df'], DATA[name]['time'] = check(eval(name), 'nz')\nprint('# Name: {} | Time: {:0.3f} s'.format(name, DATA[name]['time']))\nplot_size(DATA[name]['df'], name)\nprint(\"# Samples:\")\nshow_roi(DATA[name]['df'])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:08:57.911467Z","iopub.execute_input":"2022-12-21T06:08:57.91181Z","iopub.status.idle":"2022-12-21T06:14:12.749407Z","shell.execute_reply.started":"2022-12-21T06:08:57.911779Z","shell.execute_reply":"2022-12-21T06:14:12.748171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# AvgPixelValue (APV)","metadata":{}},{"cell_type":"markdown","source":"## code","metadata":{}},{"cell_type":"code","source":"def apv(img):\n    \"\"\"https://www.kaggle.com/code/michaelgartsbein/save-cropped-images\"\"\"\n    av = np.mean(img, axis=0)\n    mi = np.min(img, axis=0)\n    ma = np.max(img, axis=0)\n    img = img[:, (((av - mi) > 2) + ((av - ma) > 2))]\n    return img","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:33:13.369783Z","iopub.execute_input":"2022-12-21T06:33:13.370087Z","iopub.status.idle":"2022-12-21T06:33:13.376313Z","shell.execute_reply.started":"2022-12-21T06:33:13.370057Z","shell.execute_reply":"2022-12-21T06:33:13.375304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## test","metadata":{}},{"cell_type":"code","source":"name = 'apv'\nDATA[name] = {}\nDATA[name]['df'], DATA[name]['time'] = check(eval(name), 'nz')\nprint('# Name: {} | Time: {:0.3f} s'.format(name, DATA[name]['time']))\nplot_size(DATA[name]['df'], name)\nprint(\"# Samples:\")\nshow_roi(DATA[name]['df'])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:27:34.088188Z","iopub.execute_input":"2022-12-21T06:27:34.088723Z","iopub.status.idle":"2022-12-21T06:33:13.367763Z","shell.execute_reply.started":"2022-12-21T06:27:34.088687Z","shell.execute_reply":"2022-12-21T06:33:13.366382Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Binary Masked Largest Contour (BMLC)","metadata":{}},{"cell_type":"code","source":"def bmlc(img):\n    \"\"\"https://www.kaggle.com/code/fabiendaniel/dicom-cropped-resized-png-jpg\"\"\"\n    # Binarize the image\n    bin_pixels = cv2.threshold(img, 20, 255, cv2.THRESH_BINARY)[1]\n   \n    # Make contours around the binarized image, keep only the largest contour\n    contours, _ = cv2.findContours(bin_pixels, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)\n    contour = max(contours, key=cv2.contourArea)\n\n    # Create a mask from the largest contour\n    mask = np.zeros(img.shape, np.uint8)\n    cv2.drawContours(mask, [contour], -1, 255, cv2.FILLED)\n   \n    # Use bitwise_and to get masked part of the original image\n    out = cv2.bitwise_and(img, mask)\n    \n    # get bounding box of contour\n    y1, y2 = np.min(contour[:, :, 1]), np.max(contour[:, :, 1])\n    x1, x2 = np.min(contour[:, :, 0]), np.max(contour[:, :, 0])\n    \n    x1 = int(0.99 * x1)\n    x2 = int(1.01 * x2)\n    y1 = int(0.99 * y1)\n    y2 = int(1.01 * y2)\n\n    return out[y1:y2, x1:x2]","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:15.739084Z","iopub.status.idle":"2022-12-21T06:00:15.739565Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## test","metadata":{}},{"cell_type":"code","source":"name = 'bmlc'\nDATA[name] = {}\nDATA[name]['df'], DATA[name]['time'] = check(eval(name), 'nz')\nprint('# Name: {} | Time: {:0.3f} s'.format(name, DATA[name]['time']))\nplot_size(DATA[name]['df'], name)\nprint(\"# Samples:\")\nshow_roi(DATA[name]['df'])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:15.740835Z","iopub.status.idle":"2022-12-21T06:00:15.741633Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Standard Deviation (STD)","metadata":{}},{"cell_type":"markdown","source":"## code","metadata":{}},{"cell_type":"code","source":"def std(img):\n    \"\"\"https://www.kaggle.com/code/salmanahmedtamu/faster-dicom-loading-and-cropping\"\"\"\n    img = img[:,img.std(axis=0) > 20]\n    img = img[img.std(axis=1) > 20, :]\n    return img","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:15.742818Z","iopub.status.idle":"2022-12-21T06:00:15.743298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## test","metadata":{}},{"cell_type":"code","source":"name = 'std'\nDATA[name] = {}\nDATA[name]['df'], DATA[name]['time'] = check(eval(name), 'nz')\nprint('# Name: {} | Time: {:0.3f} s'.format(name, DATA[name]['time']))\nplot_size(DATA[name]['df'], name)\nprint(\"# Samples:\")\nshow_roi(DATA[name]['df'])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:15.744695Z","iopub.status.idle":"2022-12-21T06:00:15.745239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Blur Binary Largest Contour (BBLC)","metadata":{}},{"cell_type":"markdown","source":"## code","metadata":{}},{"cell_type":"code","source":"def bblc(img):\n    \"\"\"\n    https://www.kaggle.com/code/snnclsr/roi-extraction-using-opencv\n    \"\"\"\n    # Otsu's thresholding after Gaussian filtering\n    blur = cv2.GaussianBlur(img, (5, 5), 0)\n    _, breast_mask = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n    \n    cnts, _ = cv2.findContours(breast_mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n    cnt = max(cnts, key = cv2.contourArea)\n    x, y, w, h = cv2.boundingRect(cnt)\n    return img[y:y+h, x:x+w]","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:15.746633Z","iopub.status.idle":"2022-12-21T06:00:15.747105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## test","metadata":{}},{"cell_type":"code","source":"name = 'bblc'\nDATA[name] = {}\nDATA[name]['df'], DATA[name]['time'] = check(eval(name), 'nz')\nprint('# Name: {} | Time: {:0.3f} s'.format(name, DATA[name]['time']))\nplot_size(DATA[name]['df'], name)\nprint(\"# Samples:\")\nshow_roi(DATA[name]['df'])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-21T06:00:15.748114Z","iopub.status.idle":"2022-12-21T06:00:15.74863Z"},"trusted":true},"execution_count":null,"outputs":[]}]}