{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**在这次比赛中，你将自动定位和分类14种胸片胸椎异常类型。您将使用由经验丰富的放射科医生注释的18000个扫描组成的数据集。您可以使用15000个独立标记的图像训练模型，并将在3000个图像的测试集上进行评估。这些注释是通过VinBigData的基于web的平台VinLab收集的。关于构建数据集的详细信息可以在我们最近的论文“VinDr CXR：一个开放的胸部X射线数据集和放射科医生的注释”中找到。**"},{"metadata":{},"cell_type":"markdown","source":"# 1.Dicom to Numpy array"},{"metadata":{},"cell_type":"markdown","source":"**将dicom数据转换成png/jpg看起来很简单，但是，您必须考虑到，原始dicom数据实际上并不能线性转换为“人性化”的png/jpg。事实上，大多数DICOM的存储像素值在指数级。因此，为了得到放射学家最初在工作区看到的jpg/png，您需要应用一些转换。DICOM元数据存储了如何进行这种“人性化”转换的信息。\n下面是我使用的示例代码：**"},{"metadata":{"trusted":true},"cell_type":"code","source":"import pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nfrom glob import glob\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\nfrom skimage import exposure\nimport cv2\nimport warnings\nwarnings.filterwarnings('ignore')\n%matplotlib inline\n\n\ndef read_xray(path, voi_lut = True, fix_monochrome = True):\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT（如果DICOM设备可用）用于将原始DICOM数据转换为“人性化”视图\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n               \n    # 根据这个值，X射线可能看起来是反向的修复这个问题:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n        \n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n        \n    return data","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img = read_xray('../input/vinbigdata-chest-xray-abnormalities-detection/train/0108949daa13dc94634a7d650a05c0bb.dicom')\nplt.figure(figsize = (12,12))\nplt.imshow(img, 'gray')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"没有解决单色问题,设置fix_monochrome = False"},{"metadata":{"trusted":true},"cell_type":"code","source":"img = read_xray('../input/vinbigdata-chest-xray-abnormalities-detection/train/0108949daa13dc94634a7d650a05c0bb.dicom',fix_monochrome = False)\nplt.figure(figsize = (12,12))\nplt.imshow(img, 'gray')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset_dir = '../input/vinbigdata-chest-xray-abnormalities-detection'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def dicom2array(path, voi_lut=True, fix_monochrome=True):\n    dicom = pydicom.read_file(path)\n    # VOI LUT (if available by DICOM device) is used to\n    # transform raw DICOM data to \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    return data\n        \n    \ndef plot_img(img, size=(7, 7), is_rgb=True, title=\"\", cmap='gray'):\n    plt.figure(figsize=size)\n    plt.imshow(img, cmap=cmap)\n    plt.suptitle(title)\n    plt.show()\n\n\ndef plot_imgs(imgs, cols=4, size=7, is_rgb=True, title=\"\", cmap='gray', img_size=(500,500)):\n    rows = len(imgs)//cols + 1\n    fig = plt.figure(figsize=(cols*size, rows*size))\n    for i, img in enumerate(imgs):\n        if img_size is not None:\n            img = cv2.resize(img, img_size)\n        fig.add_subplot(rows, cols, i+1)\n        plt.imshow(img, cmap=cmap)\n    plt.suptitle(title)\n    plt.show()\n    \n# def draw_bboxes(img, boxes, thickness=10, color=(255, 0, 0), img_size=(500,500)):\n#     img_copy = img.copy()\n#     if len(img_copy.shape) == 2:\n#         img_copy = np.stack([img_copy, img_copy, img_copy], axis=-1)\n#     for box in boxes:\n#         img_copy = cv2.rectangle(\n#             img_copy,\n#             (int(box[0]), int(box[1])),\n#             (int(box[2]), int(box[3])),\n#             color, thickness)\n#     if img_size is not None:\n#         img_copy = cv2.resize(img_copy, img_size)\n#     return img_copy","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dicom_paths = glob(f'{dataset_dir}/train/*.dicom')\nimgs = [dicom2array(path) for path in dicom_paths[:4]]\nplot_imgs(imgs)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"可以试试均衡直方图处理，对比差异"},{"metadata":{"trusted":true},"cell_type":"code","source":"imgs = [exposure.equalize_hist(img) for img in imgs]\nplot_imgs(imgs)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2.EDA csv"},{"metadata":{"trusted":true},"cell_type":"code","source":"from bokeh.plotting import figure as bokeh_figure\nfrom bokeh.io import output_notebook, show, output_file\nfrom bokeh.models import ColumnDataSource, HoverTool, Panel\nfrom bokeh.models.widgets import Tabs\nimport pandas as pd\nfrom PIL import Image\nfrom sklearn import preprocessing\nimport random\nfrom random import randint","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"下面开始提取重要特征，预处理数据"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_bbox_area(row):\n    return (row['x_max']-row['x_min'])*(row['y_max']-row['y_min'])\n\ntrain_df = pd.read_csv(f'{dataset_dir}/train.csv')\nle = preprocessing.LabelEncoder()  # encode rad_id\ntrain_df['rad_label'] = le.fit_transform(train_df['rad_id'])\n\nfinding_df = train_df[train_df['class_name'] != 'No finding']\nfinding_df['bbox_area'] = finding_df.apply(get_bbox_area, axis=1)\nfinding_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2.1 Plot bounding box"},{"metadata":{"trusted":true},"cell_type":"code","source":"imgs = []\nimg_ids = finding_df['image_id'].values\nclass_ids = finding_df['class_id'].unique()\n\n# map label_id to specify color\nlabel2color = {class_id:[randint(0,255) for i in range(3)] for class_id in class_ids}\nthickness = 3\nscale = 5\n\n\nfor i in range(8):\n    img_id = random.choice(img_ids)\n    img_path = f'{dataset_dir}/train/{img_id}.dicom'\n    img = dicom2array(path=img_path)\n    img = cv2.resize(img, None, fx=1/scale, fy=1/scale)\n    img = np.stack([img, img, img], axis=-1)\n    \n    boxes = finding_df.loc[finding_df['image_id'] == img_id, ['x_min', 'y_min', 'x_max', 'y_max']].values/scale\n    labels = finding_df.loc[finding_df['image_id'] == img_id, ['class_id']].values.squeeze()\n    \n    for label_id, box in zip(labels, boxes):\n        color = label2color[label_id]\n        img = cv2.rectangle(\n            img,\n            (int(box[0]), int(box[1])),\n            (int(box[2]), int(box[3])),\n            color, thickness\n    )\n    img = cv2.resize(img, (500,500))\n    imgs.append(img)\n    \nplot_imgs(imgs, cmap=None)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"您可以看到：在每个图像中，都有许多重叠的框。请注意，这场比赛的一个关键部分是从多个放射科医生的地面真相工作。我想如果你处理得好的话，在这场比赛中获得最好的名次是关键。"},{"metadata":{},"cell_type":"markdown","source":"# 2.2 Plot histogram"},{"metadata":{},"cell_type":"markdown","source":"**我们将尝试绘制一些直方图。**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def hist_hover(dataframe, column, color=[\"#94c8d8\", \"#ea5e51\"], bins=30, title=\"\", value_range=None):\n    \"\"\"\n    Plot histogram\n    \"\"\"\n    hist, edges = np.histogram(dataframe[column], bins=bins, range=value_range)\n    hist_frame = pd.DataFrame({\n        column: hist,\n        \"left\": edges[:-1],\n        \"right\": edges[1:]\n    })\n    hist_frame[\"interval\"] = [\"%d to %d\" %\n                              (left, right) for left, right in zip(edges[:-1], edges[1:])]\n    src = ColumnDataSource(hist_frame)\n    plot = bokeh_figure(\n        plot_height=400, plot_width=600,\n        title=title, x_axis_label=column,\n        y_axis_label=\"Count\"\n    )\n    plot.quad(\n        bottom=0, top=column, left=\"left\", right=\"right\",\n        source=src, fill_color=color[0], line_color=\"#35838d\",\n        fill_alpha=0.7, hover_fill_alpha=0.7,\n        hover_fill_color=color[1]\n    )\n    hover = HoverTool(\n        tooltips=[(\"Interval\", \"@interval\"), (\"Count\", str(f\"@{column}\"))]\n    )\n    plot.add_tools(hover)\n    output_notebook()\n    show(plot)\n    \n    \nhist_hover(train_df, column='class_id')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**可以看到每个类的图像质量之间的不平衡**"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Note that a key part of this competition is working with ground truth from multiple radiologists.\nhist_hover(train_df, column='rad_label')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**每个放射科医生的图像质量之间的不平衡**"},{"metadata":{"trusted":true},"cell_type":"code","source":"## histogram of bbox area\nhist_hover(finding_df, column='bbox_area')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**经过一些EDA步骤，我们认识到数据集在许多方面相当不平衡。也许，我们需要用一些增广的方法来解决这个问题。数据增广还没处理，以后会继续更新**"},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}