{"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"},{"sourceId":183798121,"sourceType":"kernelVersion"},{"sourceId":186724508,"sourceType":"kernelVersion"}],"dockerImageVersionId":30732,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# !cp -r /kaggle/input/rsna2024-interactive/* .","metadata":{"execution":{"iopub.status.busy":"2024-07-03T17:52:52.842659Z","iopub.execute_input":"2024-07-03T17:52:52.843044Z","iopub.status.idle":"2024-07-03T17:52:54.093409Z","shell.execute_reply.started":"2024-07-03T17:52:52.843012Z","shell.execute_reply":"2024-07-03T17:52:54.091851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nimport numpy as np \nimport pandas as pd\nimport os\nimport pydicom\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\nimport PIL.Image\nfrom io import BytesIO\nimport cv2\n\nfrom IPython.display import display\nfrom ipywidgets import interact_manual, interact\nfrom ipywidgets import GridBox, Box, Layout\nfrom ipywidgets import Dropdown, Label, Text, Image, Button, HTML, ToggleButton, IntSlider, FloatLogSlider, Checkbox\n\n","metadata":{"execution":{"iopub.status.busy":"2024-07-03T17:52:54.096125Z","iopub.execute_input":"2024-07-03T17:52:54.096657Z","iopub.status.idle":"2024-07-03T17:52:55.151972Z","shell.execute_reply.started":"2024-07-03T17:52:54.096609Z","shell.execute_reply":"2024-07-03T17:52:55.150808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Observations\nMore detailed information will be updated on https://www.kaggle.com/code/followjohn/rsna2024-check-incorrectly-labeled-data.\n* Dtype of pixel_array is uint16 or uint16\n* Some Sagittal T1 are left to right. In these cases, ImagePositionPatient[0] are decreasing instead of increasing. Study ID: 71973234, Series ID: 1433976441\n* Some images have extreme values, so normalize them to [0, 255] might not work. Study ID: 44060036, Series ID: 1034241723\n* Series ID with missing coordinates: 542282425, 3636216534, 3892989905\n* Some not all 5 coordinates of all Series are given. Please refer to missing_coordinates.csv for each Study ID and Series.\n* Not all sutdies have three modalities. Study ID: 2492114990, 2780132468, 3008676218\n* The size (mm) of images are described below:\n|     | Rows (mm) | Columns (mm) |\n|  ----  | ----  | ----  |\n| Axial T2  | 137.78 $\\le$ 187.56 $\\pm$ 19.43 $\\le$ 300.0 | 150.0 $\\le$ 188.11 $\\pm$ 19.87 $\\le$ 301.16 |\n| Sagittal T2/STIR  | 199.99 $\\le$ 280.22 $\\pm$ 27.81 $\\le$ 521.09 | 190.0 $\\le$ 274.43 $\\pm$ 28.25 $\\le$ 410.01 |\n| Sagittal T1  | 199.99 $\\le$ 296.62 $\\pm$ 33.05 $\\le$ 410.01 | 190.0 $\\le$ 291.17 $\\pm$ 35.35 $\\le$ 410.01 |\n* ImageOrientationPatient might change the content of Axial T2.\n* No coordinate in Study ID: 3008676218","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Functions","metadata":{}},{"cell_type":"code","source":"def FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs):\n    from IPython.display import HTML, display\n    import matplotlib.animation as animation\n    ani = animation.FuncAnimation(fig, func, frames=frames, init_func=init_func, fargs=fargs, save_count=save_count, cache_frame_data=cache_frame_data, **kwargs)\n    video = ani.to_html5_video()\n    plt.close(fig)\n    return video\n\ndef clip_extreme_values(image, low_percentile=1, high_percentile=99):\n    lower_bound = np.percentile(image, low_percentile).astype(image.dtype)\n    upper_bound = np.percentile(image, high_percentile).astype(image.dtype)\n    clipped_image = np.clip(image, lower_bound, upper_bound)\n    return clipped_image\n\ndef histogram_equalization(img, precision=2048): \n    img = (img.astype(np.float32) - float(img.min())) / float(img.max() - float(img.min()))\n    hist, bins = np.histogram(img.flatten(), precision, [0,1])\n    hist[0] = 0\n    cdf = hist.cumsum()\n    cdf_m = np.ma.masked_equal(cdf, 0)\n    cdf_m = (cdf_m - cdf_m.min()) / (cdf_m.max() - cdf_m.min())\n    cdf = np.ma.filled(cdf_m, 0)\n    img = cdf[((precision - 1) * img).astype(np.int64)]\n    return img\n\ndef dicom_preprocess(dicom_obj, apply_he=True):\n    img = dicom_obj.pixel_array\n    val_max, val_min = img.max(), img.min()\n    if dicom_obj.PhotometricInterpretation == 'MONOCHROME1':\n        img = val_max + val_min - img # invert\n    img = img - img.min()\n    img = img.astype(np.uint16)\n    img = clip_extreme_values(img)\n    if apply_he:\n        img = cv2.createCLAHE().apply(img)\n#         img = histogram_equalization(img)\n    img = (img.astype(np.float32) - float(img.min())) / float(img.max() - float(img.min()))\n    img = (255 * img).round().astype(np.uint8)\n    return img\n\n\ndef build_label_table_html(labels):\n    row_names = ['spinal_canal_stenosis', \n                 'left_neural_foraminal_narrowing', 'right_neural_foraminal_narrowing',\n                 'left_subarticular_stenosis', 'right_subarticular_stenosis']\n    col_names = ['l1_l2', 'l2_l3', 'l3_l4', 'l4_l5', 'l5_s1']\n    label_set = {'Normal/Mild': 0, 'Moderate': 1, 'Severe': 2}\n    \n    rows = []\n    \n    header = [f'<td style=\"border-bottom: 1px solid; border-top: 1px solid; width: 50px;\">{d}</td>' \n              for d in [''] + col_names]\n    header = '<tr>' + ''.join(header) + '</tr>'\n    rows.append(header)\n    \n    for i, condition in enumerate(row_names):\n        cols = [condition]\n        for level in col_names:\n            name = f'{condition}_{level}'\n            v = label_set.get(labels[name], -1)\n            cols.append(v)\n        if i == len(row_names) - 1:\n            cols = [f'<td style=\"border-bottom: 1px solid;\">{d}</td>' \n                    if j == 0 \n                    else f'<td style=\"border-bottom: 1px solid; text-align: center;\">{d}</td>'\n                    for j, d in enumerate(cols)]\n        else:\n            cols = [f'<td>{d}</td>' if j == 0 else f'<td style=\"text-align: center;\">{d}</td>'\n                    for j, d in enumerate(cols)]\n        row = '<tr>' + ''.join(cols) + '</tr>'\n        rows.append(row)\n    table = '<table>' + ''.join(rows) + '</table>'\n    return table\n\ndef build_meta_table_html(metas, max_cols=3):\n    counter = 0\n    rows = []\n    \n    while counter < len(metas):\n        cols = (2 * max_cols) * ['<td></td>']\n        for i in range(max_cols):\n            if counter >= len(metas):\n                break\n            cols[2 * i] = '<td style=\"text-align: left; border-style: solid; border-width:0 0 0 1px;\">' + str(metas[counter].keyword) + '</td>'\n            cols[2 * i + 1] = '<td style=\"text-align: center;\">' + str(metas[counter].value) + '</td>'\n            counter += 1\n#         cols = [f'<td style=\"text-align: left;\">{d}</td>' for d in cols]\n        cols = '<tr>' + ''.join(cols) + '</tr>'\n        rows.append(cols)\n    table = '<table>' + ''.join(rows) + '</table>'\n    return table","metadata":{"execution":{"iopub.status.busy":"2024-07-03T17:52:55.880508Z","iopub.execute_input":"2024-07-03T17:52:55.881047Z","iopub.status.idle":"2024-07-03T17:52:55.909008Z","shell.execute_reply.started":"2024-07-03T17:52:55.881015Z","shell.execute_reply":"2024-07-03T17:52:55.907687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load DataFrames","metadata":{}},{"cell_type":"code","source":"meta_df = pd.read_csv('/kaggle/input/rsna2024-file-meta-to-dataframe/dicom_meta.csv')\ntrain_df = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train.csv')\nlabel_coordinates_df = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_label_coordinates.csv')\nseries_descriptions_df = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_series_descriptions.csv')\n\n","metadata":{"execution":{"iopub.status.busy":"2024-07-03T17:53:00.715843Z","iopub.execute_input":"2024-07-03T17:53:00.716269Z","iopub.status.idle":"2024-07-03T17:53:02.752742Z","shell.execute_reply.started":"2024-07-03T17:53:00.716237Z","shell.execute_reply":"2024-07-03T17:53:02.751277Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"series_description_dict = {str(row['series_id']):row['series_description'] \n                           for _, row in series_descriptions_df.iterrows()}\nseries_id2study_id = {str(row['series_id']):str(row['study_id']) \n                      for _, row in series_descriptions_df.iterrows()}\nseries_id2coords_index = dict()\nfor i, row in tqdm(label_coordinates_df.iterrows(), total=len(label_coordinates_df)):\n    key = str(row['series_id'])\n    item = series_id2coords_index.get(key, [])\n    series_id2coords_index[key] = item\n    item.append(i)\n    \nto_meta_dict = dict()\nfor i, row in tqdm(meta_df.iterrows(), total=len(meta_df)):\n    study_id, series_id, fn = row[[\"study_id\", \"series_id\", \"fn\"]]\n    study_id, series_id, instance_id = str(study_id), str(series_id), os.path.splitext(fn)[0]\n    study_item = to_meta_dict.get(study_id, dict())\n    series_item = study_item.get(series_id, dict())\n    to_meta_dict[study_id] = study_item\n    study_item[series_id] = series_item\n    series_item[instance_id] = row\n","metadata":{"execution":{"iopub.status.busy":"2024-07-03T17:53:12.607053Z","iopub.execute_input":"2024-07-03T17:53:12.607483Z","iopub.status.idle":"2024-07-03T17:54:33.498706Z","shell.execute_reply.started":"2024-07-03T17:53:12.607447Z","shell.execute_reply":"2024-07-03T17:54:33.497488Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Interactive\n* \"Show Labels\" button shows the labels of current Study ID in train.csv.\n* \"Show Meta\" button shows information of the current instance in dicom file.\n* \"Show Image\" button shows the image of the current instance. \n    * \"Apply HE\" option applies `cv2.createCLAHE()`.\n    * \"Show Coordinates\" option shows the coordinates in train_label_coordinates.csv.\n    * \"Filter Instance\" option filters out the coordinates if these coordinates are not on the current instance.\n* \"Cross Ref\" button shows the cross reference between Sagittal and Axial images. You can drag the slider under images.\n* \"Show Video\" button creates a video.","metadata":{}},{"cell_type":"code","source":"\ntrain_root = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_images'\nstudy_ids = os.listdir(train_root)\nstudy_ids.sort(key=lambda i: int(os.path.splitext(os.path.basename(i))[0]))\nglobal_dicom = None\nnext_adaptive_width = False\n\n\nstudy_id_dropdown = Dropdown(options=study_ids,\n                             value=study_ids[0],\n                             description='Study ID:',)\nseries_id_dropdown = Dropdown(options=[], description='Series ID:',)\nfn_dropdown = Dropdown(options=[], description='Instance ID:',)\n\nstudy_id_text = Text(value=study_ids[0], description='Study ID:', continuous_update=False)\nseries_id_text = Text(description='Series ID:', continuous_update=False)\nfn_slider = IntSlider(value=1, min=1, max=1, step=1, description='Instance ID:', continuous_update=False)\n\nshow_label_button = ToggleButton(description='Show Labels')\nshow_meta_button = ToggleButton(description='Show Meta')\nshow_image_button = ToggleButton(value=True, description='Show Image')\ncross_ref_button = ToggleButton(value=False, description='Cross Ref')\nshow_video_button = ToggleButton(description='Show Video')\n\nequalize_checkbox = Checkbox(value=True, description='Apply HE')\nshow_coord_checkbox = Checkbox(value=True, description='Show Coordinates')\nfilter_instance_checkbox = Checkbox(value=True, description='Filter Instance')\n\nzoom_slider = FloatLogSlider(base=2, value=1, min=-3, max=3, step=0.01, description='Zoom in/out:', continuous_update=False)\nlabel_table = HTML()\nmeta_table = HTML()\ndicom_image = Image(format='png', width=512, height=512)\ndicom_with_coord_image = Image(format='png', width=512, height=512)\ncoord_legend_image = Image(format='png', width=128, height=512)\ndicom_video = HTML()\n\nref_sag_dropdown = Dropdown(options=[], description='Source Series ID:',)\nref_axi_dropdown = Dropdown(options=[], description='Target Series ID:',)\nsag_slider = IntSlider(value=1, min=1, max=1, step=1, description='', continuous_update=False)\naxi_slider = IntSlider(value=1, min=1, max=1, step=1, description='', continuous_update=False)\nref_sag_image = Image(format='png', width=512, height=512)\nref_axi_image = Image(format='png', width=512, height=512)\n\ndebug_elem = HTML()\ndef debug(*msg):\n    debug.msgs.append(' '.join([repr(m) for m in msg]))\n    msgs = [f'<tr><td>{i}</td><td style=\"background-color: red\">{m}</td></tr>'\n            for i, m in enumerate(debug.msgs)]\n    msgs = '<table>' + ' '.join(msgs) + '</table>'\n    debug_elem.value = msgs\ndebug.msgs = []\n\n\ndef update_study_id(study_id):\n    if study_id_dropdown.value != study_id:\n        study_id_dropdown.value = study_id\n    if study_id_text.value != study_id:\n        study_id_text.value = study_id\n    \n    series_ids = os.listdir(os.path.join(train_root, \n                                         study_id))\n    series_ids.sort(key=lambda i: int(os.path.splitext(os.path.basename(i))[0]))\n    series_ids = [(' --- '.join([series_description_dict[sid], sid]),\n                   sid) \n                  for sid in series_ids]\n    series_id_dropdown.options = series_ids\n    \n        \n    update_series_id(study_id, series_ids[0][1])\n    toggle_label_table()\n    toggle_cross_ref()\n\ndef update_series_id(study_id, series_id):\n    if series_id_dropdown.value != series_id:\n        series_id_dropdown.value = series_id\n    if series_id_text.value != series_id:\n        series_id_text.value = series_id\n        \n    fn_list = os.listdir(os.path.join(train_root, \n                                      study_id,\n                                      series_id))\n    fn_list.sort(key=lambda i: int(os.path.splitext(os.path.basename(i))[0]))\n    fn_dropdown.options = fn_list\n    fn_slider.max = len(fn_list)\n  \n    global next_adaptive_width\n    next_adaptive_width = True\n    update_fn(fn_list[0])\n    next_adaptive_width = False\n\ndef update_fn(fn):\n    idx = fn_dropdown.options.index(fn)\n    if fn_dropdown.value != fn:\n        fn_dropdown.value = fn\n    if fn_slider.value != idx + 1:\n        fn_slider.value = idx + 1 \n    toggle_dicom_image(adaptive_width=next_adaptive_width)\n    toggle_meta_table()\n    \ndef update_study_id_dropdown(*args):\n    update_study_id(study_id_dropdown.value)\n\ndef update_series_id_dropdown(*args):\n    update_series_id(study_id_dropdown.value, series_id_dropdown.value)\n    \ndef update_fn_dropdown(*args):\n    update_fn(fn_dropdown.value)\n    \ndef update_study_id_text(*args):\n    update_study_id(study_id_text.value)\n\ndef update_series_id_text(*args):\n    study_id = series_id2study_id[series_id_text.value]\n    series_id = series_id_text.value\n    study_id_dropdown.value = study_id\n    study_id_text.value = study_id\n    update_series_id(study_id, series_id)\n    \ndef update_fn_slider(*args):\n    update_fn(fn_dropdown.options[fn_slider.value - 1])\n    \ndef toggle_label_table(*args):\n    if show_label_button.value:\n        label_table.layout.display = 'block'\n        i = np.argwhere(train_df['study_id'] == int(study_id_dropdown.value))[0][0]\n        table = build_label_table_html(train_df.iloc[i])\n        label_table.value = table\n    else:\n        label_table.layout.display = 'none'\n        \ndef toggle_meta_table(*args):\n    if show_meta_button.value:\n        meta_table.layout.display = 'block'\n        study_id = study_id_dropdown.value\n        series_id = series_id_dropdown.value\n        fn = fn_dropdown.value\n        dicom_path = os.path.join(train_root, study_id, series_id, fn)\n        dicom = load_dicom(dicom_path)\n        exclude_tags = ['SOPInstanceUID', 'PatientID', 'StudyInstanceUID', 'SeriesInstanceUID', 'InstanceNumber', 'FrameOfReferenceUID']\n        elem_list = [dicom[elem.tag] \n                     for elem in dicom.elements() \n                     if elem.VR != 'OB' and elem.tag not in exclude_tags]\n        \n        table = build_meta_table_html(elem_list)\n        meta_table.value = table\n    else:\n        meta_table.layout.display = 'none'\n        \ndef draw_coordinates(arr, scale, series_id, curr_instance_number=None):\n    LEGEND_BG_COLOR = (0, 0, 0)\n    LEGEND_TEXT_COLOR = (200, 200, 200)\n    radius = 7.5 * min(arr.shape[:2]) / 640\n    thickness = max(radius / 2.5, 1)\n    font_scale = min(arr.shape[:2]) / 1280\n    font_thickness = max(1, int(round(2 * font_scale)))\n    radius = int(round(radius))\n    thickness = int(round(thickness))\n    \n    indices = series_id2coords_index[series_id]\n    rows = [label_coordinates_df.iloc[i][['instance_number', 'condition', 'level', 'x', 'y']] for i in indices]\n    instance_numbers, conditions, levels, xs, ys = list(zip(*rows))\n    # filter out not curr_instance_number\n    instance_mask = [curr_instance_number is None or instance_number == curr_instance_number \n                     for instance_number in instance_numbers]\n    \n    # legends\n    ## compute max number of chars for alignment\n    num_char_legends = [(len(str(instance_number)), len(condition), len(level)) \n                        for instance_number, condition, level in zip(instance_numbers, conditions, levels)]\n    num_char_legends.append((len('instance_number'), len('condition'), len('level')))\n    instance_number_len, condition_len, level_len = list(zip(*num_char_legends))\n    instance_number_len, condition_len, level_len = max(instance_number_len), max(condition_len), max(level_len)\n    legend_format = '{:<' + str(instance_number_len) + '}  {:<' + str(condition_len) + '}  {:<' + str(level_len) + '}'\n    ## build legends and header\n    legends = [legend_format.format(instance_number, condition, level) for instance_number, condition, level in zip(instance_numbers, conditions, levels)]\n    legend_header = legend_format.format('instance_number', 'condition', 'level')\n\n    legend_fontsizes = [cv2.getTextSize(legend, cv2.FONT_HERSHEY_SIMPLEX, font_scale, font_thickness)[0] for legend in legends]\n    legends_width = max([fontsize[0] for fontsize in legend_fontsizes])\n    legend_arr = np.ones((arr.shape[0], \n                          int(legends_width + 4 * radius), \n                          3), np.uint8)\n    legend_arr[:] = LEGEND_BG_COLOR\n\n    # different colors for each coordinate\n    colors = np.array([[i, 255, 255] for i in np.linspace(120, 180+120, sum(instance_mask) + 1)])\n    colors = colors[None].astype(np.uint8)\n    colors = cv2.cvtColor(colors, cv2.COLOR_HSV2BGR)[0, :-1].tolist()\n    _indices = np.cumsum(instance_mask) - 1\n    colors = [colors[_indices[i]] if is_instance else LEGEND_BG_COLOR\n              for i, is_instance in enumerate(instance_mask)]\n\n    header_fontsize = cv2.getTextSize(legend_header, cv2.FONT_HERSHEY_SIMPLEX, font_scale, font_thickness)[0][1]\n    fontsize_counter = int(1.5 * header_fontsize) # text top margin\n    legend_arr = cv2.putText(legend_arr, legend_header, (2, fontsize_counter),\n                             cv2.FONT_HERSHEY_SIMPLEX , font_scale, LEGEND_TEXT_COLOR, font_thickness)\n    for i, (is_instance, legend, x, y, color, fontsize) in enumerate(zip(instance_mask, legends, xs, ys, colors, legend_fontsizes)):\n        x *= scale\n        y *= scale\n        if is_instance:\n            arr = cv2.circle(arr, (int(x), int(y)), radius, color, thickness)\n\n        # draw legend\n        fontsize_counter += int(1.5 * fontsize[1]) # text top margin\n        legend_arr = cv2.putText(legend_arr, legend, (2, fontsize_counter),\n                                 cv2.FONT_HERSHEY_SIMPLEX , font_scale, LEGEND_TEXT_COLOR, font_thickness)\n        legend_arr = cv2.circle(legend_arr, (legend_arr.shape[1] - int(2 * radius), fontsize_counter - fontsize[1] // 2), \n                                radius, color, -1)\n        \n    \n    return arr, legend_arr\n\ndef draw_image(image_elem, arr):\n    f = BytesIO()\n    PIL.Image.fromarray(arr).save(f, 'png')\n    image_elem.width = arr.shape[1]\n    image_elem.height = arr.shape[0]\n    image_elem.value = f.getvalue()\n\ndef load_dicom(dicom_path):\n    global global_dicom\n    dicom = pydicom.dcmread(dicom_path)\n    global_dicom = dicom\n    return dicom\n\ndef get_zoom_scale(width, adaptive_width=False, update_zoom_slider=True):\n    if adaptive_width:\n        zoom_scale = 350 / width\n        if update_zoom_slider:\n            zoom_slider.value = zoom_scale\n    else:\n        zoom_scale = zoom_slider.value\n    return zoom_scale\n\ndef load_and_process_dicom_image(dicom_path, adaptive_width=False, update_zoom_slider=True):\n    dicom = load_dicom(dicom_path)\n    arr = dicom_preprocess(dicom, equalize_checkbox.value)\n    arr = cv2.cvtColor(arr, cv2.COLOR_GRAY2RGB)\n    zoom_scale = get_zoom_scale(arr.shape[1], adaptive_width, update_zoom_slider)\n    if zoom_scale != 1:\n        arr = cv2.resize(arr, None, fx=zoom_scale, fy=zoom_scale, \n                         interpolation=(cv2.INTER_CUBIC if zoom_slider.value > 1 else cv2.INTER_AREA))\n    return arr, zoom_scale\n\ndef toggle_dicom_image(*args, adaptive_width=False):\n    if show_image_button.value:\n        dicom_image.value = b''\n        dicom_with_coord_image.value = b''\n        coord_legend_image.value = b''\n        dicom_image.layout.display = 'block'\n        study_id = study_id_dropdown.value\n        series_id = series_id_dropdown.value\n        fn = fn_dropdown.value\n        dicom_path = os.path.join(train_root, study_id, series_id, fn)\n        arr, zoom_scale = load_and_process_dicom_image(dicom_path, adaptive_width=adaptive_width)\n\n        draw_image(dicom_image, arr)\n        \n        if show_coord_checkbox.value:\n            dicom_with_coord_image.layout.display = 'block'\n            coord_legend_image.layout.display = 'block'\n            \n            curr_instance_num = None\n            if filter_instance_checkbox.value:\n                curr_instance_num = int(os.path.splitext(fn_dropdown.value)[0])\n            arr_c, legend_arr = draw_coordinates(arr, zoom_scale, series_id_dropdown.value, curr_instance_num)\n            draw_image(dicom_with_coord_image, arr_c)\n            draw_image(coord_legend_image, legend_arr)\n            \n        else:\n            dicom_with_coord_image.layout.display = 'none'\n            coord_legend_image.layout.display = 'none'\n    else:\n        dicom_image.layout.display = 'none'\n        dicom_with_coord_image.layout.display = 'none'\n        coord_legend_image.layout.display = 'none'\n\ndef toggle_cross_ref(*args):\n    if cross_ref_button.value:\n        cross_ref_row.layout.display = 'block'\n        \n        # set options of dropdown\n        ref_sag_dropdown.options = [opt for opt in series_id_dropdown.options if opt[0].startswith('Sagittal')]\n        ref_axi_dropdown.options = [opt for opt in series_id_dropdown.options if opt[0].startswith('Axial')]\n    else:\n        cross_ref_row.layout.display = 'none'\n        ref_sag_dropdown.options = []\n        ref_axi_dropdown.options = []\n\ndef update_sag_dropdown(*args): \n    sag_slider.max = len(os.listdir(os.path.join(train_root, \n                                                 study_id_dropdown.value, \n                                                 ref_axi_dropdown.value)))\n    sag_slider.unobserve(update_sag_slider, 'value')\n    sag_slider.value = 1\n    sag_slider.observe(update_sag_slider, 'value')\n    update_sag_slider(draw_line=False, update_other=False)\n\ndef update_axi_dropdown(*args): \n    axi_slider.max = len(os.listdir(os.path.join(train_root, \n                                                 study_id_dropdown.value, \n                                                 ref_sag_dropdown.value))) \n    axi_slider.unobserve(update_axi_slider, 'value')\n    axi_slider.value = 1\n    axi_slider.observe(update_axi_slider, 'value')\n    update_axi_slider(draw_line=False, update_other=False)\n    \n    \ndef image_to_realword_coordinate(coords, positions, orientations, pixel_spacing):\n    mat = np.array([\n        [orientations[0] * pixel_spacing[0], orientations[3] * pixel_spacing[1], 0, positions[0]],\n        [orientations[1] * pixel_spacing[0], orientations[4] * pixel_spacing[1], 0, positions[1]],\n        [orientations[2] * pixel_spacing[0], orientations[5] * pixel_spacing[1], 0, positions[2]],\n        [0, 0, 0, 1],\n    ])\n    coords = np.array([coords[0], coords[1], 0, 1]).reshape(4, 1)\n    new_coords = mat @ coords\n    return new_coords.ravel()[:3]\n\ndef realword_to_image_coordinate(coords, positions, orientations, pixel_spacing):\n    mat = np.array([\n        [orientations[0] / pixel_spacing[0], orientations[3] / pixel_spacing[1]],\n        [orientations[1] / pixel_spacing[0], orientations[4] / pixel_spacing[1]],\n        [orientations[2] / pixel_spacing[0], orientations[5] / pixel_spacing[1]],\n    ]).T\n    offset = np.array(positions).reshape(3, 1)\n    coords = np.array([coords[0], coords[1], coords[2]]).reshape(3, 1)\n    new_coords = mat @ (coords - offset)\n    return new_coords.ravel()\n\ndef update_sag_slider(*args, draw_line=True, update_other=True):\n    dicom_path = os.path.join(train_root, study_id_dropdown.value, ref_axi_dropdown.value,\n                              f'{sag_slider.value}.dcm')\n    arr, zoom_scale = load_and_process_dicom_image(dicom_path, adaptive_width=True, update_zoom_slider=False)\n        \n    if show_coord_checkbox.value:\n        curr_instance_num = None\n        if filter_instance_checkbox.value:\n            curr_instance_num = sag_slider.value\n        arr, _ = draw_coordinates(arr, zoom_scale, ref_axi_dropdown.value, curr_instance_num)\n    \n    # draw cross reference line\n    if draw_line:\n        sag_meta = to_meta_dict[study_id_dropdown.value][ref_sag_dropdown.value][str(axi_slider.value)]\n        axi_meta = to_meta_dict[study_id_dropdown.value][ref_axi_dropdown.value][str(sag_slider.value)]\n#         x = (sag_meta['ImagePositionPatient_0'] - axi_meta['ImagePositionPatient_0']) / axi_meta['PixelSpacing_0']\n        x1, y1 = realword_to_image_coordinate(\n            sag_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            axi_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            axi_meta[[f'ImageOrientationPatient_{i}' for i in range(6)]].tolist(),\n            axi_meta[['PixelSpacing_0', 'PixelSpacing_1']].tolist(),\n        )\n\n        x2, y2 = sag_meta['Columns'], sag_meta['Rows']\n        x2, y2, z2 = image_to_realword_coordinate(\n            [x2, y2],\n            sag_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            sag_meta[[f'ImageOrientationPatient_{i}' for i in range(6)]].tolist(),\n            sag_meta[['PixelSpacing_0', 'PixelSpacing_1']].tolist(),\n        )\n        x2, y2 = realword_to_image_coordinate(\n            [x2, y2, z2],\n            axi_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            axi_meta[[f'ImageOrientationPatient_{i}' for i in range(6)]].tolist(),\n            axi_meta[['PixelSpacing_0', 'PixelSpacing_1']].tolist(),\n        )\n        x1, y1, x2, y2 = np.array([x1, y1, x2, y2]) * zoom_scale\n        arr = cv2.line(arr, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2) \n    \n    draw_image(ref_axi_image, arr)\n    \n    if update_other:\n        update_axi_slider(draw_line=draw_line, update_other=False)\n    \n        \ndef update_axi_slider(*args, draw_line=True, update_other=True):\n    dicom_path = os.path.join(train_root, study_id_dropdown.value, ref_sag_dropdown.value,\n                              f'{axi_slider.value}.dcm')\n    arr, zoom_scale = load_and_process_dicom_image(dicom_path, adaptive_width=True, update_zoom_slider=False)\n        \n    if show_coord_checkbox.value:\n        curr_instance_num = None\n        if filter_instance_checkbox.value:\n            curr_instance_num = axi_slider.value\n        arr, _ = draw_coordinates(arr, zoom_scale, ref_sag_dropdown.value, curr_instance_num)\n        \n    # draw cross reference line\n    if draw_line:\n        sag_meta = to_meta_dict[study_id_dropdown.value][ref_sag_dropdown.value][str(axi_slider.value)]\n        axi_meta = to_meta_dict[study_id_dropdown.value][ref_axi_dropdown.value][str(sag_slider.value)]\n#         y = (sag_meta['ImagePositionPatient_2'] - axi_meta['ImagePositionPatient_2']) / sag_meta['PixelSpacing_1']\n        x1, y1 = realword_to_image_coordinate(\n            axi_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            sag_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            sag_meta[[f'ImageOrientationPatient_{i}' for i in range(6)]].tolist(),\n            sag_meta[['PixelSpacing_0', 'PixelSpacing_1']].tolist(),\n        )\n\n        x2, y2 = axi_meta['Columns'], axi_meta['Rows']\n        x2, y2, z2 = image_to_realword_coordinate(\n            [x2, y2],\n            axi_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            axi_meta[[f'ImageOrientationPatient_{i}' for i in range(6)]].tolist(),\n            axi_meta[['PixelSpacing_0', 'PixelSpacing_1']].tolist(),\n        )\n        x2, y2 = realword_to_image_coordinate(\n            [x2, y2, z2],\n            sag_meta[['ImagePositionPatient_0', 'ImagePositionPatient_1', 'ImagePositionPatient_2']].tolist(),\n            sag_meta[[f'ImageOrientationPatient_{i}' for i in range(6)]].tolist(),\n            sag_meta[['PixelSpacing_0', 'PixelSpacing_1']].tolist(),\n        )\n        x1, y1, x2, y2 = np.array([x1, y1, x2, y2]) * zoom_scale\n        arr = cv2.line(arr, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2) \n        \n    draw_image(ref_sag_image, arr)\n    \n    if update_other:\n        update_sag_slider(draw_line=draw_line, update_other=False)\n    \n    \ndef toggle_dicom_video(*args):\n    if show_video_button.value:\n        dicom_video.layout.display = 'block'\n        study_id = study_id_dropdown.value\n        series_id = series_id_dropdown.value\n\n        fig = plt.figure(figsize=(6, 6))\n        def func(fn):\n            fig.clf()\n            dicom_path = os.path.join(train_root, study_id, series_id, fn)\n            arr = load_and_process_dicom_image(dicom_path)\n\n            plt.title(fn)\n            plt.axis('off')\n            fig.tight_layout()\n            plt.imshow(arr, cmap='gray')\n            return fig\n\n        video = FuncAnimation(fig, func, fn_dropdown.options, \n                              blit=False, interval=1000, repeat=True)\n        dicom_video.value = video\n    else:\n        dicom_video.layout.display = 'none'\n\nstudy_id_dropdown.observe(update_study_id_dropdown, 'value')\nseries_id_dropdown.observe(update_series_id_dropdown, 'value')\nfn_dropdown.observe(update_fn_dropdown, 'value')\nstudy_id_text.observe(update_study_id_text, 'value')\nseries_id_text.observe(update_series_id_text, 'value')\nfn_slider.observe(update_fn_slider, 'value')\n\nshow_label_button.observe(toggle_label_table, 'value')\nshow_meta_button.observe(toggle_meta_table, 'value')\nshow_image_button.observe(toggle_dicom_image, 'value')\nshow_video_button.observe(toggle_dicom_video, 'value')\n\nshow_coord_checkbox.observe(toggle_dicom_image, 'value')\nequalize_checkbox.observe(toggle_dicom_image, 'value')\nzoom_slider.observe(toggle_dicom_image, 'value')\nfilter_instance_checkbox.observe(toggle_dicom_image, 'value')\n\ncross_ref_button.observe(toggle_cross_ref, 'value')\nref_sag_dropdown.observe(update_axi_dropdown, 'value')\nref_axi_dropdown.observe(update_sag_dropdown, 'value')\nsag_slider.observe(update_sag_slider, 'value')\naxi_slider.observe(update_axi_slider, 'value')\n\n\nlabel_table.layout.display = 'none'\ndicom_image.layout.display = 'none'\ndicom_with_coord_image.layout.display = 'none'\ndicom_video.layout.display = 'none'\n\nstudy_id_row = GridBox(children=[study_id_dropdown, study_id_text],\n                        layout=Layout(width='100%',\n                                      grid_template_rows='auto',\n                                      grid_template_columns='auto auto',)\n                       )\n\nseries_id_row = GridBox(children=[series_id_dropdown, series_id_text],\n                        layout=Layout(width='100%',\n                                      grid_template_rows='auto',\n                                      grid_template_columns='auto auto',)\n                       )\nfn_row = GridBox(children=[fn_dropdown, fn_slider],\n                        layout=Layout(width='100%',\n                                      grid_template_rows='auto',\n                                      grid_template_columns='auto auto',)\n                       )\n\nbutton_row = GridBox(children=[show_label_button, show_meta_button, show_image_button, cross_ref_button, show_video_button],\n                      layout=Layout(width='100%',\n                                    grid_template_rows='auto',\n                                    grid_template_columns='auto auto auto auto auto',)\n                       )\nimage_info_row = GridBox(children=[zoom_slider, equalize_checkbox, show_coord_checkbox, filter_instance_checkbox],\n                         layout=Layout(width='100%',\n                                       grid_template_rows='auto',\n                                       grid_template_columns='30% 15% 15% 15%',)\n                       )\nimage_row = Box(children=[dicom_image, dicom_with_coord_image, coord_legend_image],\n                layout=Layout(display='flex',\n                              flex_flow='row',\n                              align_items='stretch',\n                              width='100%')\n               )\ncross_ref_row = GridBox(children=[\n    GridBox(children=[ref_sag_dropdown, ref_axi_dropdown],\n            layout=Layout(width='100%', grid_template_rows='auto', grid_template_columns='auto auto')),\n    GridBox(children=[GridBox(children=[ref_sag_image, sag_slider],\n                              layout=Layout(width='100%', grid_template_rows='auto auto auto', grid_template_columns='auto')), \n                      GridBox(children=[ref_axi_image, axi_slider],\n                              layout=Layout(width='100%', grid_template_rows='auto auto auto', grid_template_columns='auto'))],\n            layout=Layout(width='100%', grid_template_rows='auto', grid_template_columns='auto auto'))\n    ], \n    layout=Layout(display='none',\n                  width='100%',\n                  grid_template_rows='auto auto',\n                  grid_template_columns='auto',)\n)\n\ndisplay(GridBox(children=[study_id_row, series_id_row, fn_row, button_row, \n                          image_info_row, label_table, meta_table, image_row, \n                          cross_ref_row, dicom_video,debug_elem],\n                layout=Layout(\n                    width='100%',\n                    grid_template_rows='auto auto auto auto auto auto auto auto auto auto',\n                    grid_template_columns='auto',)\n               ))\n\nupdate_study_id_dropdown()\nupdate_series_id_dropdown()\ntoggle_dicom_image()\n\n","metadata":{"execution":{"iopub.status.busy":"2024-07-03T18:09:09.568727Z","iopub.execute_input":"2024-07-03T18:09:09.569170Z","iopub.status.idle":"2024-07-03T18:09:11.101151Z","shell.execute_reply.started":"2024-07-03T18:09:09.569133Z","shell.execute_reply":"2024-07-03T18:09:11.099768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Find Coordinates Interactive","metadata":{}},{"cell_type":"code","source":"\n_img_elem = Image(format='png', width=512, height=512)\n_x_slider = IntSlider(value=0, min=0, max=0, step=1, description='x:', continuous_update=False)\n_y_slider = IntSlider(value=0, min=0, max=0, step=1, description='y:', continuous_update=False)\n_text = Text(value='', description='', disable=True)\n\n\ndef _get_image():\n    dicom_path = os.path.join(train_root, study_id_dropdown.value, series_id_dropdown.value, fn_dropdown.value)\n    arr, scale = load_and_process_dicom_image(dicom_path, adaptive_width=True, update_zoom_slider=False)\n    _y_slider.max = arr.shape[0] / scale\n    _x_slider.max = arr.shape[1] / scale\n    return arr, scale\n\ndef _update_slider(*args):\n    y = _y_slider.value\n    x = _x_slider.value\n    _text.value = f'{x}, {y}'\n    arr = arr0.copy()\n    y *= scale\n    x *= scale\n    arr = cv2.line(arr, (0, int(y)), (arr.shape[1], int(y)), (255, 0, 0), 2) \n    arr = cv2.line(arr, (int(x), 0), (int(x), arr.shape[0]), (255, 0, 0), 2) \n    draw_image(_img_elem, arr)\n\narr0, scale = _get_image()\ndraw_image(_img_elem, arr0)\n_x_slider.observe(_update_slider, 'value')\n_y_slider.observe(_update_slider, 'value')\n\ndisplay(GridBox(children=[_img_elem, _y_slider, _x_slider, _text],\n                layout=Layout(\n                    width='100%',\n                    grid_template_rows='auto auto auto auto',\n                    grid_template_columns='auto',)\n               ))","metadata":{"execution":{"iopub.status.busy":"2024-07-03T17:57:41.223550Z","iopub.execute_input":"2024-07-03T17:57:41.224048Z","iopub.status.idle":"2024-07-03T17:57:41.364021Z","shell.execute_reply.started":"2024-07-03T17:57:41.224011Z","shell.execute_reply":"2024-07-03T17:57:41.362637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}