{"cells":[{"metadata":{},"cell_type":"markdown","source":"I belive that standardize the dataset could help to increase the accuracy of any train so I wrote this script to crop the x-ray images, I also prepared a bboxes coordinate shifting script.\n\n[Cropped and Resized Images Dataset](https://www.kaggle.com/anhlv2312/vinbigdata-xray-cropped-512)\n\n![](https://i.ibb.co/fk3xT47/Screenshot-from-2021-01-13-00-21-24.png)\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport sys\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nfrom skimage import exposure\nfrom skimage import transform\nimport warnings\nfrom tqdm.notebook import tqdm\nimport imageio\n\npd.set_option('display.max_rows', 500)\npd.set_option('display.max_columns', 500)\npd.set_option('display.width', 1000)\nwarnings.filterwarnings('ignore')\n\nGRAY_SCALE = 1\n\n# Some constants\nHIP_THRESHOLD = [0.3, 0.4]\nNECK_THRESHOLD = 0.2\nLUNG_THRESHOLD = 0.7\nOUTPUT_DIM = 1024\n\ndataset_dir = '../input/vinbigdata-chest-xray-abnormalities-detection/'\n\n# Read dicom images into numpy array (https://www.kaggle.com/raddar/convert-dicom-to-np-array-the-correct-way)\ndef read_xray(path, voi_lut=True, monochrome=True, normalize=True):\n    dicom = pydicom.read_file(path)\n\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n\n    if monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n\n    if normalize:\n        data = exposure.equalize_hist(data)\n        \n    data = data - np.min(data)\n    data = data / np.max(data)\n\n    return data\n  \n\n# Calculate the margin of the chest\ndef calculate_margins(data):\n    height = data.shape[0]\n    width = data.shape[1]\n\n    # Get the median brighness value along with x and y axis\n    x_scale = np.median(data, axis=1)\n    y_scale = np.median(data, axis=0)\n\n    # Initialise default margin value [top, bottom, left, right]\n    margins = [0, height, 0, width]\n\n    # Calculate the left margin\n    if y_scale[0] < HIP_THRESHOLD[0]:\n        for i in range(width // 3):\n            if y_scale[i] < HIP_THRESHOLD[1] * GRAY_SCALE < y_scale[i + 1]:\n                margins[2] = i\n                break\n\n    # Calculate the left margin \n    if y_scale[-1] < HIP_THRESHOLD[0]:\n        for i in range(width - 1, width // 3 * 2, -1):\n            if y_scale[i] < HIP_THRESHOLD[1] * GRAY_SCALE < y_scale[i - 1]:\n                margins[3] = i\n                break\n    \n    # Calculate the top magin, looping until the median brighness reach the neck threshold\n    for i in range((margins[3]-margins[2]) // 2):\n            if x_scale[i] < NECK_THRESHOLD * GRAY_SCALE < x_scale[i + 1]:\n                margins[0] = i\n                break\n   \n    # Crop out the left hip and right hip to get the middle part\n    middle_part = data[:, margins[2]:margins[3]]\n    \n    # Initialise the bottom margin for left lung and right lung\n    margin_bottom_left = margins[0]\n    margin_bottom_right = margins[0]\n\n    # Crop out the lungs\n    left_lung = middle_part[:, (margins[3]-margins[2])//6:(margins[3]-margins[2])//6*3]\n    right_lung = middle_part[:, (margins[3]-margins[2])//6*3:(margins[3]-margins[2])//6*5]\n    \n    # Normalise the brightneess\n    left_lung = exposure.equalize_hist(left_lung)\n    right_lung = exposure.equalize_hist(right_lung)\n    \n    # Calculate the median brightness\n    x_scale_left = np.median(left_lung, axis=1)\n    x_scale_right = np.median(right_lung, axis=1)\n\n    # Calculate the margin based on the brightness of the left lung\n    for i in range(margins[0]+(margins[3]-margins[2])//2, height - 1):\n        if x_scale_left[i] < LUNG_THRESHOLD * GRAY_SCALE < x_scale_left[i + 1]:\n            margin_bottom_left = i\n            break\n\n    # Calculate the margin based on the brightness of the right lung\n    for i in range(margins[0]+(margins[3]-margins[2])//2, height - 1):\n        if x_scale_right[i] < LUNG_THRESHOLD * GRAY_SCALE < x_scale_right[i + 1]:\n            margin_bottom_right = i\n            break\n\n    # Set the bottom margin max of left lung and right lung\n    margins[1] = max(margin_bottom_left, margin_bottom_right)\n    # If the bottom margin is higher than the height of square then scrop at the part that make the image a square\n    margins[1] = max(margins[1], margins[0] + (margins[3] - margins[2]))\n    # if the square is higher than the original image then margin bottom is set to height of the original image\n    margins[1] = min(margins[1], height)\n\n    # Crop out the chest\n    cropped = middle_part[margins[0]:margins[1], :]\n    # Normalise the image last time    \n    cropped = exposure.equalize_hist(cropped)\n\n    \n    return margins, cropped\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_xray_data_cropped(image_id, data, cropped):\n    fig=plt.figure(figsize=(15, 3))\n    fig.suptitle(image_id, y=0)\n    \n    fig.add_subplot(1, 4, 1) \n    plt.imshow(data, cmap=plt.cm.bone)\n    plt.title('Original')\n \n    fig.add_subplot(1, 4, 2)\n    plt.imshow(cropped, cmap=plt.cm.bone)\n    plt.title('Cropped')\n    \n    fig.add_subplot(1, 4, 3)\n    plt.title('Brightness Scale by X')\n    plt.ylim([0, GRAY_SCALE])\n    plt.plot(np.median(data, axis=1))\n    plt.plot(data.mean(1))\n    \n    fig.add_subplot(1, 4, 4)\n    plt.title('Brightness Scale by Y')\n    plt.ylim([0, GRAY_SCALE])\n    plt.plot(np.median(data, axis=0))\n    plt.plot(data.mean(0))\n    \n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(dataset_dir + 'train.csv')\ndf = df[df['class_id'] != 14]\nimage_ids = df['image_id'].unique()\n\nsamples = []\n# samples.extend(['363dc405e14ed95659d88707f54730de','414ae85a6ec97db19ed913bde0062b11','ef63342a9d28339d09338c16573066c6'])\n# samples.extend(['a398135fec0dd0d8239d5b6d8d24454b','cefc63f9ff49d9da82c49144f05a13cd','2be0cff9073424bcaf946885d1c1adf5'])\n# samples.extend(['cb9658d61c84a99ba31665f40cb0788d','4b33db392748079f75a5250a15840b74','1d8f4d5daf11f2b01695b71a862aa813'])\n# samples.extend(['d16b67fee07971da41a3d08707ccd864','66396f621903b00a1b7e1f54c8e5e8b3','eebb4b0a4472b69b72b1004b5b4bfcaa'])\n# samples.extend(['6c79f2551808438721052023e043ab4d'])\nsamples.extend(np.random.choice(image_ids, 10))\n    \nfor image_id in samples:\n    data = read_xray(os.path.join(dataset_dir, 'train', image_id + '.dicom'))\n    margins, cropped = calculate_margins(data)\n    show_xray_data_cropped(image_id, data, cropped)\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def generate_output(image_ids, input_dir, ouput_dir_name):\n\n    os.makedirs(ouput_dir_name, exist_ok=True)\n    dimensions = {}\n    margins = {}\n\n    for image_id in tqdm(image_ids):\n\n        data = read_xray(os.path.join(input_dir, image_id + '.dicom'))\n\n        dimensions[image_id] = data.shape\n\n        margins[image_id], cropped = calculate_margins(data)\n        resized = transform.resize(cropped, [OUTPUT_DIM, OUTPUT_DIM])\n        output = (resized * 255).astype(np.uint8)\n        \n        imageio.imwrite(os.path.join(ouput_dir_name, image_id + '.png'), output)\n\n    output_df = pd.DataFrame(image_ids, columns=['image_id'])\n        \n    output_df['dimensions'] = output_df.apply(lambda row: dimensions.get(row['image_id']), axis=1)\n    output_df['margins'] = output_df.apply(lambda row: margins.get(row['image_id']), axis=1)\n    \n    output_df.to_csv(ouput_dir_name + '.csv')\n    print(output_df)\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def shift_bboxes():\n    \n    train = pd.read_csv(os.path.join(dataset_dir, 'train.csv'))\n    cropped = pd.read_csv('../input/vinbigdata-xray-cropped-512/train.cropped.csv', index_col='image_id')\n    train = pd.merge(train, cropped, on='image_id', how='left')\n\n    print(train)\n\n    train['x_min'] = train['x_min'] - train['left']\n    train['x_max'] = train['x_max'] - train['left']\n\n    train['y_min'] = train['y_min'] - train['top']\n    train['y_max'] = train['y_max'] - train['top']\n\n    train['x_min'] = train.apply(lambda row: 0 if row.x_min < 0 else row.x_min, axis=1)\n    train['y_min'] = train.apply(lambda row: 0 if row.y_min < 0 else row.y_min, axis=1)\n\n    train['x_max'] = train.apply(lambda row: row.right - row.left if row.x_max > row.right - row.left else row.x_max, axis=1)\n    train['y_max'] = train.apply(lambda row: row.bottom - row.top if row.y_max > row.bottom - row.top else row.y_max, axis=1)\n\n    print(train)\n    train.to_csv('train.shifted.csv', index=None)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# train_df = pd.read_csv(dataset_dir + 'train.csv')\n# train_df = train_df[train_df['class_id'] != 14]\n# train_ids = train_df['image_id'].unique()\n# generate_output(train_ids, os.path.join(dataset_dir,'train'), 'train_cropped_' + str(OUTPUT_DIM))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test_df = pd.read_csv(dataset_dir + 'sample_submission.csv')\n# test_ids = test_df['image_id'].unique()\n# generate_output(test_ids, os.path.join(dataset_dir,'test'), 'test_cropped_' + str(OUTPUT_DIM))","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}