melage 0.0.65__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- melage/__init__.py +16 -0
- melage/cli.py +4 -0
- melage/graphics/GLGraphicsItem.py +286 -0
- melage/graphics/GLViewWidget.py +595 -0
- melage/graphics/Transform3D.py +55 -0
- melage/graphics/__init__.py +8 -0
- melage/graphics/functions.py +101 -0
- melage/graphics/items/GLAxisItem.py +149 -0
- melage/graphics/items/GLGridItem.py +178 -0
- melage/graphics/items/GLPolygonItem.py +77 -0
- melage/graphics/items/GLScatterPlotItem.py +135 -0
- melage/graphics/items/GLVolumeItem.py +280 -0
- melage/graphics/items/GLVolumeItem_b.py +237 -0
- melage/graphics/items/__init__.py +0 -0
- melage/graphics/shaders.py +202 -0
- melage/main.py +270 -0
- melage/requirements22.txt +25 -0
- melage/requirements_old.txt +28 -0
- melage/resource/0circle.png +0 -0
- melage/resource/0circle_faded.png +0 -0
- melage/resource/3d.png +0 -0
- melage/resource/3d.psd +0 -0
- melage/resource/3dFaded.png +0 -0
- melage/resource/Eraser.png +0 -0
- melage/resource/EraserFaded.png +0 -0
- melage/resource/EraserX.png +0 -0
- melage/resource/EraserXFaded.png +0 -0
- melage/resource/Eraser_icon.svg +79 -0
- melage/resource/Hand.png +0 -0
- melage/resource/HandIcons_0.png +0 -0
- melage/resource/Hand_IX.png +0 -0
- melage/resource/Hand_IXFaded.png +0 -0
- melage/resource/Handsqueezed.png +0 -0
- melage/resource/Handwriting (copy).png +0 -0
- melage/resource/Handwriting.png +0 -0
- melage/resource/HandwritingMinus.png +0 -0
- melage/resource/HandwritingMinusX.png +0 -0
- melage/resource/HandwritingPlus.png +0 -0
- melage/resource/HandwritingPlusX.png +0 -0
- melage/resource/Move_icon.svg +8 -0
- melage/resource/PngItem_2422924.png +0 -0
- melage/resource/about.png +0 -0
- melage/resource/about_logo.png +0 -0
- melage/resource/about_logo0.png +0 -0
- melage/resource/action_check.png +0 -0
- melage/resource/action_check_OFF.png +0 -0
- melage/resource/arrow).png +0 -0
- melage/resource/arrow.png +0 -0
- melage/resource/arrowFaded.png +0 -0
- melage/resource/arrow_org.png +0 -0
- melage/resource/arrow_org.png.png +0 -0
- melage/resource/arrows.png +0 -0
- melage/resource/authors.mp4 +0 -0
- melage/resource/box.png +0 -0
- melage/resource/check-image-icon-0.jpg +0 -0
- melage/resource/circle.png +0 -0
- melage/resource/circle_faded.png +0 -0
- melage/resource/circle_or.png +0 -0
- melage/resource/close.png +0 -0
- melage/resource/close_bg.png +0 -0
- melage/resource/color/Simple.txt +18 -0
- melage/resource/color/Tissue.txt +24 -0
- melage/resource/color/Tissue12.txt +27 -0
- melage/resource/color/albert_LUT.txt +102 -0
- melage/resource/color/mcrib_LUT.txt +102 -0
- melage/resource/color/pediatric1.txt +29 -0
- melage/resource/color/pediatric1_old.txt +27 -0
- melage/resource/color/pediatric2.txt +87 -0
- melage/resource/color/pediatric3.txt +29 -0
- melage/resource/color/pediatrics (copy).csv +103 -0
- melage/resource/color/tissue_seg.txt +4 -0
- melage/resource/contour.png +0 -0
- melage/resource/contour.svg +2 -0
- melage/resource/contourFaded.png +0 -0
- melage/resource/contourX.png +0 -0
- melage/resource/contourXFaded.png +0 -0
- melage/resource/dti.png +0 -0
- melage/resource/dti0.png +0 -0
- melage/resource/dti222.png +0 -0
- melage/resource/dti_or.png +0 -0
- melage/resource/eco.png +0 -0
- melage/resource/eco22.png +0 -0
- melage/resource/eco_old.png +0 -0
- melage/resource/eco_or.png +0 -0
- melage/resource/eco_or2.png +0 -0
- melage/resource/eco_seg.png +0 -0
- melage/resource/eco_seg_old.png +0 -0
- melage/resource/export.png +0 -0
- melage/resource/hand-grab-icon-10.jpg +0 -0
- melage/resource/hand-grab-icon-25.jpg +0 -0
- melage/resource/info.png +0 -0
- melage/resource/line.png +0 -0
- melage/resource/linefaded.png +0 -0
- melage/resource/load.png +0 -0
- melage/resource/main.ico +0 -0
- melage/resource/manual_images/3D_rightc.png +0 -0
- melage/resource/manual_images/3D_rightc_goto.png +0 -0
- melage/resource/manual_images/3D_rightc_paint.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_draw1.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_draw2.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_render.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_render2.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_render3.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_render4.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_render5.png +0 -0
- melage/resource/manual_images/3D_rightc_paint_render6.png +0 -0
- melage/resource/manual_images/3D_rightc_seg.png +0 -0
- melage/resource/manual_images/exit_toolbar.png +0 -0
- melage/resource/manual_images/load_image_file.png +0 -0
- melage/resource/manual_images/load_image_file_openp.png +0 -0
- melage/resource/manual_images/main_page.png +0 -0
- melage/resource/manual_images/menu_file.png +0 -0
- melage/resource/manual_images/menu_file_export.png +0 -0
- melage/resource/manual_images/menu_file_import.png +0 -0
- melage/resource/manual_images/menu_file_settings.png +0 -0
- melage/resource/manual_images/menu_file_ss.png +0 -0
- melage/resource/manual_images/open_save_load.png +0 -0
- melage/resource/manual_images/panning_toolbar.png +0 -0
- melage/resource/manual_images/segmentation_toolbar.png +0 -0
- melage/resource/manual_images/tab_mri.png +0 -0
- melage/resource/manual_images/tab_us.png +0 -0
- melage/resource/manual_images/tabs.png +0 -0
- melage/resource/manual_images/toolbar_tools.png +0 -0
- melage/resource/manual_images/tools_basic.png +0 -0
- melage/resource/manual_images/tools_bet.png +0 -0
- melage/resource/manual_images/tools_cs.png +0 -0
- melage/resource/manual_images/tools_deepbet.png +0 -0
- melage/resource/manual_images/tools_imageinfo.png +0 -0
- melage/resource/manual_images/tools_maskO.png +0 -0
- melage/resource/manual_images/tools_masking.png +0 -0
- melage/resource/manual_images/tools_n4b.png +0 -0
- melage/resource/manual_images/tools_resize.png +0 -0
- melage/resource/manual_images/tools_ruler.png +0 -0
- melage/resource/manual_images/tools_seg.png +0 -0
- melage/resource/manual_images/tools_threshold.png +0 -0
- melage/resource/manual_images/tools_tools.png +0 -0
- melage/resource/manual_images/widget_color.png +0 -0
- melage/resource/manual_images/widget_color_add.png +0 -0
- melage/resource/manual_images/widget_color_add2.png +0 -0
- melage/resource/manual_images/widget_color_additional.png +0 -0
- melage/resource/manual_images/widget_images.png +0 -0
- melage/resource/manual_images/widget_images2.png +0 -0
- melage/resource/manual_images/widget_images3.png +0 -0
- melage/resource/manual_images/widget_marker.png +0 -0
- melage/resource/manual_images/widget_mri.png +0 -0
- melage/resource/manual_images/widget_mri2.png +0 -0
- melage/resource/manual_images/widget_segintensity.png +0 -0
- melage/resource/manual_images/widget_tab_mutualview.png +0 -0
- melage/resource/manual_images/widget_tab_mutualview2.png +0 -0
- melage/resource/manual_images/widget_table.png +0 -0
- melage/resource/manual_images/widget_table2.png +0 -0
- melage/resource/manual_images/widget_us.png +0 -0
- melage/resource/melage_top.ico +0 -0
- melage/resource/melage_top.png +0 -0
- melage/resource/melage_top0.png +0 -0
- melage/resource/melage_top1.png +0 -0
- melage/resource/melage_top4.png +0 -0
- melage/resource/mri (copy).png +0 -0
- melage/resource/mri.png +0 -0
- melage/resource/mri0.png +0 -0
- melage/resource/mri000.png +0 -0
- melage/resource/mri22.png +0 -0
- melage/resource/mri_big.png +0 -0
- melage/resource/mri_old.png +0 -0
- melage/resource/mri_seg.png +0 -0
- melage/resource/mri_seg_old.png +0 -0
- melage/resource/new.png +0 -0
- melage/resource/open.png +0 -0
- melage/resource/open2.png +0 -0
- melage/resource/pan.png +0 -0
- melage/resource/pencil.png +0 -0
- melage/resource/pencilFaded.png +0 -0
- melage/resource/points.png +0 -0
- melage/resource/pointsFaded.png +0 -0
- melage/resource/rotate.png +0 -0
- melage/resource/ruler.png +0 -0
- melage/resource/rulerFaded.png +0 -0
- melage/resource/s.png +0 -0
- melage/resource/s.psd +0 -0
- melage/resource/save.png +0 -0
- melage/resource/saveas.png +0 -0
- melage/resource/seg_mri.png +0 -0
- melage/resource/seg_mri2.png +0 -0
- melage/resource/settings.png +0 -0
- melage/resource/synch.png +0 -0
- melage/resource/synchFaded.png +0 -0
- melage/resource/theme/rc/.keep +1 -0
- melage/resource/theme/rc/arrow_down.png +0 -0
- melage/resource/theme/rc/arrow_down@2x.png +0 -0
- melage/resource/theme/rc/arrow_down_disabled.png +0 -0
- melage/resource/theme/rc/arrow_down_disabled@2x.png +0 -0
- melage/resource/theme/rc/arrow_down_focus.png +0 -0
- melage/resource/theme/rc/arrow_down_focus@2x.png +0 -0
- melage/resource/theme/rc/arrow_down_pressed.png +0 -0
- melage/resource/theme/rc/arrow_down_pressed@2x.png +0 -0
- melage/resource/theme/rc/arrow_left.png +0 -0
- melage/resource/theme/rc/arrow_left@2x.png +0 -0
- melage/resource/theme/rc/arrow_left_disabled.png +0 -0
- melage/resource/theme/rc/arrow_left_disabled@2x.png +0 -0
- melage/resource/theme/rc/arrow_left_focus.png +0 -0
- melage/resource/theme/rc/arrow_left_focus@2x.png +0 -0
- melage/resource/theme/rc/arrow_left_pressed.png +0 -0
- melage/resource/theme/rc/arrow_left_pressed@2x.png +0 -0
- melage/resource/theme/rc/arrow_right.png +0 -0
- melage/resource/theme/rc/arrow_right@2x.png +0 -0
- melage/resource/theme/rc/arrow_right_disabled.png +0 -0
- melage/resource/theme/rc/arrow_right_disabled@2x.png +0 -0
- melage/resource/theme/rc/arrow_right_focus.png +0 -0
- melage/resource/theme/rc/arrow_right_focus@2x.png +0 -0
- melage/resource/theme/rc/arrow_right_pressed.png +0 -0
- melage/resource/theme/rc/arrow_right_pressed@2x.png +0 -0
- melage/resource/theme/rc/arrow_up.png +0 -0
- melage/resource/theme/rc/arrow_up@2x.png +0 -0
- melage/resource/theme/rc/arrow_up_disabled.png +0 -0
- melage/resource/theme/rc/arrow_up_disabled@2x.png +0 -0
- melage/resource/theme/rc/arrow_up_focus.png +0 -0
- melage/resource/theme/rc/arrow_up_focus@2x.png +0 -0
- melage/resource/theme/rc/arrow_up_pressed.png +0 -0
- melage/resource/theme/rc/arrow_up_pressed@2x.png +0 -0
- melage/resource/theme/rc/base_icon.png +0 -0
- melage/resource/theme/rc/base_icon@2x.png +0 -0
- melage/resource/theme/rc/base_icon_disabled.png +0 -0
- melage/resource/theme/rc/base_icon_disabled@2x.png +0 -0
- melage/resource/theme/rc/base_icon_focus.png +0 -0
- melage/resource/theme/rc/base_icon_focus@2x.png +0 -0
- melage/resource/theme/rc/base_icon_pressed.png +0 -0
- melage/resource/theme/rc/base_icon_pressed@2x.png +0 -0
- melage/resource/theme/rc/branch_closed.png +0 -0
- melage/resource/theme/rc/branch_closed@2x.png +0 -0
- melage/resource/theme/rc/branch_closed_disabled.png +0 -0
- melage/resource/theme/rc/branch_closed_disabled@2x.png +0 -0
- melage/resource/theme/rc/branch_closed_focus.png +0 -0
- melage/resource/theme/rc/branch_closed_focus@2x.png +0 -0
- melage/resource/theme/rc/branch_closed_pressed.png +0 -0
- melage/resource/theme/rc/branch_closed_pressed@2x.png +0 -0
- melage/resource/theme/rc/branch_end.png +0 -0
- melage/resource/theme/rc/branch_end@2x.png +0 -0
- melage/resource/theme/rc/branch_end_disabled.png +0 -0
- melage/resource/theme/rc/branch_end_disabled@2x.png +0 -0
- melage/resource/theme/rc/branch_end_focus.png +0 -0
- melage/resource/theme/rc/branch_end_focus@2x.png +0 -0
- melage/resource/theme/rc/branch_end_pressed.png +0 -0
- melage/resource/theme/rc/branch_end_pressed@2x.png +0 -0
- melage/resource/theme/rc/branch_line.png +0 -0
- melage/resource/theme/rc/branch_line@2x.png +0 -0
- melage/resource/theme/rc/branch_line_disabled.png +0 -0
- melage/resource/theme/rc/branch_line_disabled@2x.png +0 -0
- melage/resource/theme/rc/branch_line_focus.png +0 -0
- melage/resource/theme/rc/branch_line_focus@2x.png +0 -0
- melage/resource/theme/rc/branch_line_pressed.png +0 -0
- melage/resource/theme/rc/branch_line_pressed@2x.png +0 -0
- melage/resource/theme/rc/branch_more.png +0 -0
- melage/resource/theme/rc/branch_more@2x.png +0 -0
- melage/resource/theme/rc/branch_more_disabled.png +0 -0
- melage/resource/theme/rc/branch_more_disabled@2x.png +0 -0
- melage/resource/theme/rc/branch_more_focus.png +0 -0
- melage/resource/theme/rc/branch_more_focus@2x.png +0 -0
- melage/resource/theme/rc/branch_more_pressed.png +0 -0
- melage/resource/theme/rc/branch_more_pressed@2x.png +0 -0
- melage/resource/theme/rc/branch_open.png +0 -0
- melage/resource/theme/rc/branch_open@2x.png +0 -0
- melage/resource/theme/rc/branch_open_disabled.png +0 -0
- melage/resource/theme/rc/branch_open_disabled@2x.png +0 -0
- melage/resource/theme/rc/branch_open_focus.png +0 -0
- melage/resource/theme/rc/branch_open_focus@2x.png +0 -0
- melage/resource/theme/rc/branch_open_pressed.png +0 -0
- melage/resource/theme/rc/branch_open_pressed@2x.png +0 -0
- melage/resource/theme/rc/checkbox_checked.png +0 -0
- melage/resource/theme/rc/checkbox_checked0.png +0 -0
- melage/resource/theme/rc/checkbox_checked@2x.png +0 -0
- melage/resource/theme/rc/checkbox_checked@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_checked@2x000.png.png +0 -0
- melage/resource/theme/rc/checkbox_checked_disabled.png +0 -0
- melage/resource/theme/rc/checkbox_checked_disabled0.png +0 -0
- melage/resource/theme/rc/checkbox_checked_disabled@2x.png +0 -0
- melage/resource/theme/rc/checkbox_checked_disabled@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_checked_focus.png +0 -0
- melage/resource/theme/rc/checkbox_checked_focus0.png +0 -0
- melage/resource/theme/rc/checkbox_checked_focus@2x.png +0 -0
- melage/resource/theme/rc/checkbox_checked_focus@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_checked_pressed.png +0 -0
- melage/resource/theme/rc/checkbox_checked_pressed0.png +0 -0
- melage/resource/theme/rc/checkbox_checked_pressed@2x.png +0 -0
- melage/resource/theme/rc/checkbox_checked_pressed@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate@2x.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate_disabled.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate_disabled@2x.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate_focus.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate_focus@2x.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate_pressed.png +0 -0
- melage/resource/theme/rc/checkbox_indeterminate_pressed@2x.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked00.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked@2x.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked@2x00.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_disabled.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_disabled0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_disabled00.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_disabled@2x.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_disabled@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_disabled@2x00.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_focus.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_focus0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_focus00.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_focus@2x.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_focus@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_focus@2x00.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_pressed.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_pressed0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_pressed00.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_pressed@2x.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_pressed@2x0.png +0 -0
- melage/resource/theme/rc/checkbox_unchecked_pressed@2x00.png +0 -0
- melage/resource/theme/rc/line_horizontal.png +0 -0
- melage/resource/theme/rc/line_horizontal@2x.png +0 -0
- melage/resource/theme/rc/line_horizontal_disabled.png +0 -0
- melage/resource/theme/rc/line_horizontal_disabled@2x.png +0 -0
- melage/resource/theme/rc/line_horizontal_focus.png +0 -0
- melage/resource/theme/rc/line_horizontal_focus@2x.png +0 -0
- melage/resource/theme/rc/line_horizontal_pressed.png +0 -0
- melage/resource/theme/rc/line_horizontal_pressed@2x.png +0 -0
- melage/resource/theme/rc/line_vertical.png +0 -0
- melage/resource/theme/rc/line_vertical@2x.png +0 -0
- melage/resource/theme/rc/line_vertical_disabled.png +0 -0
- melage/resource/theme/rc/line_vertical_disabled@2x.png +0 -0
- melage/resource/theme/rc/line_vertical_focus.png +0 -0
- melage/resource/theme/rc/line_vertical_focus@2x.png +0 -0
- melage/resource/theme/rc/line_vertical_pressed.png +0 -0
- melage/resource/theme/rc/line_vertical_pressed@2x.png +0 -0
- melage/resource/theme/rc/radio_checked.png +0 -0
- melage/resource/theme/rc/radio_checked@2x.png +0 -0
- melage/resource/theme/rc/radio_checked_disabled.png +0 -0
- melage/resource/theme/rc/radio_checked_disabled@2x.png +0 -0
- melage/resource/theme/rc/radio_checked_focus.png +0 -0
- melage/resource/theme/rc/radio_checked_focus@2x.png +0 -0
- melage/resource/theme/rc/radio_checked_pressed.png +0 -0
- melage/resource/theme/rc/radio_checked_pressed@2x.png +0 -0
- melage/resource/theme/rc/radio_unchecked.png +0 -0
- melage/resource/theme/rc/radio_unchecked@2x.png +0 -0
- melage/resource/theme/rc/radio_unchecked_disabled.png +0 -0
- melage/resource/theme/rc/radio_unchecked_disabled@2x.png +0 -0
- melage/resource/theme/rc/radio_unchecked_focus.png +0 -0
- melage/resource/theme/rc/radio_unchecked_focus@2x.png +0 -0
- melage/resource/theme/rc/radio_unchecked_pressed.png +0 -0
- melage/resource/theme/rc/radio_unchecked_pressed@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal_disabled.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal_disabled@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal_focus.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal_focus@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal_pressed.png +0 -0
- melage/resource/theme/rc/toolbar_move_horizontal_pressed@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical_disabled.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical_disabled@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical_focus.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical_focus@2x.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical_pressed.png +0 -0
- melage/resource/theme/rc/toolbar_move_vertical_pressed@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal_disabled.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal_disabled@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal_focus.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal_focus@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal_pressed.png +0 -0
- melage/resource/theme/rc/toolbar_separator_horizontal_pressed@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical_disabled.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical_disabled@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical_focus.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical_focus@2x.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical_pressed.png +0 -0
- melage/resource/theme/rc/toolbar_separator_vertical_pressed@2x.png +0 -0
- melage/resource/theme/rc/transparent.png +0 -0
- melage/resource/theme/rc/transparent@2x.png +0 -0
- melage/resource/theme/rc/transparent_disabled.png +0 -0
- melage/resource/theme/rc/transparent_disabled@2x.png +0 -0
- melage/resource/theme/rc/transparent_focus.png +0 -0
- melage/resource/theme/rc/transparent_focus@2x.png +0 -0
- melage/resource/theme/rc/transparent_pressed.png +0 -0
- melage/resource/theme/rc/transparent_pressed@2x.png +0 -0
- melage/resource/theme/rc/window_close.png +0 -0
- melage/resource/theme/rc/window_close@2x.png +0 -0
- melage/resource/theme/rc/window_close_disabled.png +0 -0
- melage/resource/theme/rc/window_close_disabled@2x.png +0 -0
- melage/resource/theme/rc/window_close_focus.png +0 -0
- melage/resource/theme/rc/window_close_focus@2x.png +0 -0
- melage/resource/theme/rc/window_close_pressed.png +0 -0
- melage/resource/theme/rc/window_close_pressed@2x.png +0 -0
- melage/resource/theme/rc/window_grip.png +0 -0
- melage/resource/theme/rc/window_grip@2x.png +0 -0
- melage/resource/theme/rc/window_grip_disabled.png +0 -0
- melage/resource/theme/rc/window_grip_disabled@2x.png +0 -0
- melage/resource/theme/rc/window_grip_focus.png +0 -0
- melage/resource/theme/rc/window_grip_focus@2x.png +0 -0
- melage/resource/theme/rc/window_grip_pressed.png +0 -0
- melage/resource/theme/rc/window_grip_pressed@2x.png +0 -0
- melage/resource/theme/rc/window_minimize.png +0 -0
- melage/resource/theme/rc/window_minimize@2x.png +0 -0
- melage/resource/theme/rc/window_minimize_disabled.png +0 -0
- melage/resource/theme/rc/window_minimize_disabled@2x.png +0 -0
- melage/resource/theme/rc/window_minimize_focus.png +0 -0
- melage/resource/theme/rc/window_minimize_focus@2x.png +0 -0
- melage/resource/theme/rc/window_minimize_pressed.png +0 -0
- melage/resource/theme/rc/window_minimize_pressed@2x.png +0 -0
- melage/resource/theme/rc/window_undock.png +0 -0
- melage/resource/theme/rc/window_undock@2x.png +0 -0
- melage/resource/theme/rc/window_undock_disabled.png +0 -0
- melage/resource/theme/rc/window_undock_disabled@2x.png +0 -0
- melage/resource/theme/rc/window_undock_focus.png +0 -0
- melage/resource/theme/rc/window_undock_focus@2x.png +0 -0
- melage/resource/theme/rc/window_undock_pressed.png +0 -0
- melage/resource/theme/rc/window_undock_pressed@2x.png +0 -0
- melage/resource/theme/style.qss +2223 -0
- melage/resource/tract.png +0 -0
- melage/resource/view1.png +0 -0
- melage/resource/view1_eco.png +0 -0
- melage/resource/view1_mri.png +0 -0
- melage/resource/view1_seg.png +0 -0
- melage/resource/view2.png +0 -0
- melage/resource/view2_seg.png +0 -0
- melage/resource/w.png +0 -0
- melage/resource/zoom_in.png +0 -0
- melage/resource/zoom_inFaded.png +0 -0
- melage/resource/zoom_out.png +0 -0
- melage/resource/zoom_outFaded.png +0 -0
- melage/some_notes.txt +3 -0
- melage/utils/DispalyIm.py +2788 -0
- melage/utils/GMM.py +720 -0
- melage/utils/Shaders_120.py +257 -0
- melage/utils/Shaders_330.py +314 -0
- melage/utils/Shaders_bu.py +314 -0
- melage/utils/__init__0.py +7 -0
- melage/utils/brain_extraction_helper.py +234 -0
- melage/utils/custom_QScrollBar.py +61 -0
- melage/utils/glScientific.py +1554 -0
- melage/utils/glScientific_bc.py +1585 -0
- melage/utils/readData.py +1061 -0
- melage/utils/registration.py +512 -0
- melage/utils/source_folder.py +18 -0
- melage/utils/utils.py +3808 -0
- melage/version.txt +1 -0
- melage/widgets/ApplyMask.py +212 -0
- melage/widgets/ChangeSystem.py +152 -0
- melage/widgets/DeepLModels/InfantSegment/Unet.py +464 -0
- melage/widgets/DeepLModels/NPP/dataset/mri_dataset_affine.py +149 -0
- melage/widgets/DeepLModels/NPP/models/checkpoints/npp_v1.pth.py +0 -0
- melage/widgets/DeepLModels/NPP/models/losses.py +146 -0
- melage/widgets/DeepLModels/NPP/models/model.py +272 -0
- melage/widgets/DeepLModels/NPP/models/utils.py +303 -0
- melage/widgets/DeepLModels/NPP/npp.py +116 -0
- melage/widgets/DeepLModels/NPP/requirements.txt +8 -0
- melage/widgets/DeepLModels/NPP/train/train.py +116 -0
- melage/widgets/DeepLModels/Unet3DAtt.py +657 -0
- melage/widgets/DeepLModels/Unet3D_basic.py +648 -0
- melage/widgets/DeepLModels/new_unet.py +652 -0
- melage/widgets/DeepLModels/new_unet_old.py +639 -0
- melage/widgets/DeepLModels/new_unet_old2.py +658 -0
- melage/widgets/HistImage.py +153 -0
- melage/widgets/ImageThresholding.py +222 -0
- melage/widgets/MaskOperations.py +147 -0
- melage/widgets/N4Dialog.py +241 -0
- melage/widgets/Segmentation/FCM.py +1553 -0
- melage/widgets/Segmentation/__init__.py +588 -0
- melage/widgets/Segmentation/utils.py +417 -0
- melage/widgets/SemiAutoSeg.py +666 -0
- melage/widgets/Synthstrip.py +141 -0
- melage/widgets/__init__0.py +5 -0
- melage/widgets/about.py +246 -0
- melage/widgets/activation.py +437 -0
- melage/widgets/activator.py +147 -0
- melage/widgets/be_dl.py +409 -0
- melage/widgets/be_dl_unet3d.py +441 -0
- melage/widgets/brain_extraction.py +855 -0
- melage/widgets/brain_extraction_dl.py +887 -0
- melage/widgets/brain_extraction_dl_bu.py +869 -0
- melage/widgets/colorwidget.py +100 -0
- melage/widgets/dockWidgets.py +2005 -0
- melage/widgets/enhanceImWidget.py +109 -0
- melage/widgets/fileDialog_widget.py +275 -0
- melage/widgets/iminfo.py +346 -0
- melage/widgets/mainwindow_widget.py +6775 -0
- melage/widgets/melageAbout.py +123 -0
- melage/widgets/openglWidgets.py +556 -0
- melage/widgets/registrationWidget.py +342 -0
- melage/widgets/repeat_widget.py +74 -0
- melage/widgets/screenshot_widget.py +138 -0
- melage/widgets/settings_widget.py +77 -0
- melage/widgets/tranformationWidget.py +275 -0
- melage-0.0.65.dist-info/METADATA +742 -0
- melage-0.0.65.dist-info/RECORD +501 -0
- melage-0.0.65.dist-info/WHEEL +5 -0
- melage-0.0.65.dist-info/entry_points.txt +2 -0
- melage-0.0.65.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,855 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main BrainExtractor class
|
|
3
|
+
"""
|
|
4
|
+
__AUTHOR__ = 'Bahram Jafrasteh'
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import warnings
|
|
8
|
+
import numpy as np
|
|
9
|
+
import nibabel as nib
|
|
10
|
+
import trimesh
|
|
11
|
+
from numba import jit, prange
|
|
12
|
+
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
13
|
+
|
|
14
|
+
from numba.typed import List
|
|
15
|
+
from melage.utils.brain_extraction_helper import sphere, closest_integer_point, bresenham3d, l2norm, l2normarray, diagonal_dot
|
|
16
|
+
|
|
17
|
+
from PyQt5.QtCore import pyqtSignal
|
|
18
|
+
import sys
|
|
19
|
+
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
|
|
20
|
+
from PyQt5.QtCore import Qt
|
|
21
|
+
|
|
22
|
+
def to_str(val):
|
|
23
|
+
return '{:.2f}'.format(val)
|
|
24
|
+
|
|
25
|
+
class BET(QDialog):
|
|
26
|
+
closeSig = pyqtSignal()
|
|
27
|
+
betcomp = pyqtSignal()
|
|
28
|
+
datachange = pyqtSignal()
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
https://github.com/vanandrew/brainextractor
|
|
32
|
+
Implemenation of the FSL Brain Extraction Tool
|
|
33
|
+
Smith SM. Fast robust automated brain extraction. Hum Brain Mapp.
|
|
34
|
+
2002 Nov;17(3):143-55. doi: 10.1002/hbm.10062. PMID: 12391568; PMCID: PMC6871816.
|
|
35
|
+
This class takes in a Nifti1Image class and generates
|
|
36
|
+
the brain surface and mask.
|
|
37
|
+
"""
|
|
38
|
+
def __init__(self, parent=None
|
|
39
|
+
):
|
|
40
|
+
super(BET, self).__init__(parent)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
Initialization of Brain Extractor
|
|
45
|
+
|
|
46
|
+
Computes image range/thresholds and
|
|
47
|
+
estimates the brain radius
|
|
48
|
+
"""
|
|
49
|
+
def set_pars(self, t02t: float = 0.02,
|
|
50
|
+
t98t: float = 0.98,
|
|
51
|
+
bt: float = 0.5,
|
|
52
|
+
d1: float = 20.0, # mm
|
|
53
|
+
d2: float = 10.0, # mm
|
|
54
|
+
rmin: float = 3.33, # mm
|
|
55
|
+
rmax: float = 10.0,# mm
|
|
56
|
+
n_iter = 1000):
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
#print("Initializing...")
|
|
60
|
+
self.ht_min = t02t
|
|
61
|
+
self.ht_max = t98t
|
|
62
|
+
self.ft = bt
|
|
63
|
+
self.sd_min = d2
|
|
64
|
+
self.sd_max = d1
|
|
65
|
+
self.r_min = rmin
|
|
66
|
+
self.r_max = rmax
|
|
67
|
+
self.n_iter = n_iter
|
|
68
|
+
# get image resolution
|
|
69
|
+
# store brain extraction parameters
|
|
70
|
+
self.setupUi()
|
|
71
|
+
def setData(self, img, res):
|
|
72
|
+
self.res = res
|
|
73
|
+
# store the image
|
|
74
|
+
self.img = img
|
|
75
|
+
|
|
76
|
+
# store conveinent references
|
|
77
|
+
self.data = img # 3D data
|
|
78
|
+
self.rdata = img.ravel() # flattened data
|
|
79
|
+
self.shape = img.shape # 3D shape
|
|
80
|
+
self.rshape = np.multiply.reduce(img.shape) # flattened shape
|
|
81
|
+
|
|
82
|
+
def get_params(self):
|
|
83
|
+
state = self.checkbox_thresholding.isChecked()
|
|
84
|
+
if state:
|
|
85
|
+
from melage.utils.utils import Threshold_MultiOtsu
|
|
86
|
+
numc = int(self.histogram_threshold_min.value())
|
|
87
|
+
thresholds = Threshold_MultiOtsu(self.img, numc)
|
|
88
|
+
t02t = thresholds[0]
|
|
89
|
+
t98t = thresholds[-1]
|
|
90
|
+
else:
|
|
91
|
+
t02t = self.histogram_threshold_min.value()/100
|
|
92
|
+
t98t = self.histogram_threshold_max.value()/100
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
bt = self.fractional_threshold.value()/100
|
|
96
|
+
d1 = self.search_distance_max.value()
|
|
97
|
+
d2 = self.search_distance_min.value()
|
|
98
|
+
rmin = self.radcurv_min.value()
|
|
99
|
+
rmax = self.radcurv_max.value()
|
|
100
|
+
n_iter = self.iteration.value()
|
|
101
|
+
return [t02t, t98t, bt, d1, d2, rmin, rmax,n_iter]
|
|
102
|
+
def update_params(self):
|
|
103
|
+
[t02t, t98t, bt, d1, d2, rmin, rmax,n_iter] = self.get_params()
|
|
104
|
+
# store brain extraction parameters
|
|
105
|
+
#print("Parameters: bt=%f, d1=%f, d2=%f, rmin=%f, rmax=%f" % (bt, d1, d2, rmin, rmax))
|
|
106
|
+
self.bt = bt
|
|
107
|
+
self.d1 = d1 / self.res
|
|
108
|
+
self.d2 = d2 / self.res
|
|
109
|
+
self.rmin = rmin / self.res
|
|
110
|
+
self.rmax = rmax / self.res
|
|
111
|
+
|
|
112
|
+
# compute E, F constants
|
|
113
|
+
self.E = (1.0 / rmin + 1.0 / rmax) / 2.0
|
|
114
|
+
self.F = 6.0 / (1.0 / rmin - 1.0 / rmax)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
self.n_iter = n_iter
|
|
118
|
+
# get thresholds from histogram
|
|
119
|
+
sorted_data = np.sort(self.rdata)
|
|
120
|
+
self.tmin = np.min(sorted_data)
|
|
121
|
+
state = self.checkbox_thresholding.isChecked()
|
|
122
|
+
if state:
|
|
123
|
+
self.t2 = t02t
|
|
124
|
+
self.t98 = t98t
|
|
125
|
+
else:
|
|
126
|
+
self.t2 = sorted_data[np.ceil(t02t * self.rshape).astype(np.int64) + 1]
|
|
127
|
+
self.t98 = sorted_data[np.ceil(t98t * self.rshape).astype(np.int64) + 1]
|
|
128
|
+
self.tmax = np.max(sorted_data)
|
|
129
|
+
self.t = (self.t98 - self.t2) * 0.1 + self.t2 #brain/background threshold
|
|
130
|
+
#print("tmin: %f, t2: %f, t: %f, t98: %f, tmax: %f" % (self.tmin, self.t2, self.t, self.t98, self.tmax))
|
|
131
|
+
|
|
132
|
+
def activate_advanced(self, value):
|
|
133
|
+
self.widget.setEnabled(value)
|
|
134
|
+
|
|
135
|
+
def setupUi(self):
|
|
136
|
+
Dialog = self.window()
|
|
137
|
+
Dialog.setObjectName("N4")
|
|
138
|
+
Dialog.resize(310, 220)
|
|
139
|
+
self.grid_main = QtWidgets.QGridLayout(self)
|
|
140
|
+
self.grid_main.setContentsMargins(0, 0, 0, 0)
|
|
141
|
+
self.grid_main.setObjectName("gridLayout")
|
|
142
|
+
|
|
143
|
+
self.hbox = QtWidgets.QHBoxLayout()
|
|
144
|
+
self.hbox.setContentsMargins(0, 0, 0, 0)
|
|
145
|
+
self.hbox.setObjectName("gridLayout")
|
|
146
|
+
self.grid_main.addLayout(self.hbox, 0, 0)
|
|
147
|
+
|
|
148
|
+
self.checkBox = QtWidgets.QCheckBox()
|
|
149
|
+
self.hbox.addWidget(self.checkBox, 0)
|
|
150
|
+
self.checkBox.setObjectName("checkBox")
|
|
151
|
+
self.checkBox.stateChanged.connect(self.activate_advanced)
|
|
152
|
+
self.comboBox_image = QtWidgets.QComboBox()
|
|
153
|
+
self.comboBox_image.setGeometry(QtCore.QRect(20, 10, 321, 25))
|
|
154
|
+
self.comboBox_image.setObjectName("comboBox")
|
|
155
|
+
self.comboBox_image.addItem("")
|
|
156
|
+
self.comboBox_image.addItem("")
|
|
157
|
+
self.comboBox_image.currentIndexChanged.connect(self.datachange)
|
|
158
|
+
self.hbox.addWidget(self.comboBox_image, 1)
|
|
159
|
+
self.pushButton = QtWidgets.QPushButton()
|
|
160
|
+
|
|
161
|
+
self.pushButton.setObjectName("pushButton")
|
|
162
|
+
self.pushButton.pressed.connect(self.accepted_emit)
|
|
163
|
+
|
|
164
|
+
self.progressBar = QtWidgets.QProgressBar()
|
|
165
|
+
self.progressBar.setEnabled(False)
|
|
166
|
+
self.progressBar.setProperty("value", 0)
|
|
167
|
+
self.progressBar.setAlignment(QtCore.Qt.AlignCenter)
|
|
168
|
+
self.progressBar.setObjectName("progressBar")
|
|
169
|
+
self.progressBar.setMaximum(100)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
self.hbox2 = QtWidgets.QHBoxLayout()
|
|
174
|
+
self.hbox2.setContentsMargins(0, 0, 0, 0)
|
|
175
|
+
self.hbox2.setObjectName("gridLayout")
|
|
176
|
+
self.hbox2.addWidget(self.progressBar, 1)
|
|
177
|
+
self.hbox2.addWidget(self.pushButton, 0)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
self.widget = QtWidgets.QWidget()
|
|
181
|
+
self.widget.setObjectName("widget")
|
|
182
|
+
self.gridLayout = QtWidgets.QGridLayout(self.widget)
|
|
183
|
+
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
|
184
|
+
self.gridLayout.setObjectName("gridLayout")
|
|
185
|
+
self.splitter_3 = QtWidgets.QSplitter(self.widget)
|
|
186
|
+
self.splitter_3.setOrientation(QtCore.Qt.Horizontal)
|
|
187
|
+
self.splitter_3.setObjectName("splitter_3")
|
|
188
|
+
self.label_2 = QtWidgets.QLabel(self.splitter_3)
|
|
189
|
+
self.label_2.setAlignment(QtCore.Qt.AlignCenter)
|
|
190
|
+
self.label_2.setObjectName("label_2")
|
|
191
|
+
self.iteration = QtWidgets.QSpinBox(self.splitter_3)
|
|
192
|
+
self.iteration.setObjectName("iteration")
|
|
193
|
+
|
|
194
|
+
self.iteration.setMaximum(10000)
|
|
195
|
+
self.iteration.setMinimum(1)
|
|
196
|
+
self.iteration.setValue(self.n_iter)
|
|
197
|
+
self.gridLayout.addWidget(self.splitter_3, 0, 0, 1, 1)
|
|
198
|
+
self.splitter = QtWidgets.QSplitter(self.widget)
|
|
199
|
+
self.splitter.setOrientation(QtCore.Qt.Horizontal)
|
|
200
|
+
self.splitter.setObjectName("splitter")
|
|
201
|
+
self.checkbox_thresholding = QtWidgets.QCheckBox(self.splitter)
|
|
202
|
+
#self.checkbox_thresholding.setAlignment(QtCore.Qt.AlignCenter)
|
|
203
|
+
self.checkbox_thresholding.setObjectName("checkbox_thresholding")
|
|
204
|
+
self.checkbox_thresholding.setChecked(True)
|
|
205
|
+
self.checkbox_thresholding.stateChanged.connect(self.change_hist_threshold)
|
|
206
|
+
|
|
207
|
+
self.histogram_threshold_min = QtWidgets.QDoubleSpinBox(self.splitter)
|
|
208
|
+
self.histogram_threshold_min.setObjectName("histogram_threshold_min")
|
|
209
|
+
self.histogram_threshold_min.setValue(6)#(self.ht_min)*100)
|
|
210
|
+
self.histogram_threshold_min.setMaximum(10)
|
|
211
|
+
self.histogram_threshold_min.setMinimum(0)
|
|
212
|
+
|
|
213
|
+
self.histogram_threshold_max = QtWidgets.QDoubleSpinBox(self.splitter)
|
|
214
|
+
self.histogram_threshold_max.setObjectName("histogram_threshold_max")
|
|
215
|
+
self.histogram_threshold_max.setValue((self.ht_max) * 100)
|
|
216
|
+
self.histogram_threshold_max.setMaximum(100)
|
|
217
|
+
self.histogram_threshold_max.setMinimum(0)
|
|
218
|
+
|
|
219
|
+
self.histogram_threshold_min.setEnabled(True)
|
|
220
|
+
self.histogram_threshold_max.setEnabled(False)
|
|
221
|
+
|
|
222
|
+
self.gridLayout.addWidget(self.splitter, 1, 0, 1, 1)
|
|
223
|
+
self.splitter_2 = QtWidgets.QSplitter(self.widget)
|
|
224
|
+
self.splitter_2.setOrientation(QtCore.Qt.Horizontal)
|
|
225
|
+
self.splitter_2.setObjectName("splitter_2")
|
|
226
|
+
self.label = QtWidgets.QLabel(self.splitter_2)
|
|
227
|
+
self.label.setAlignment(QtCore.Qt.AlignCenter)
|
|
228
|
+
self.label.setObjectName("label")
|
|
229
|
+
self.fractional_threshold = QtWidgets.QDoubleSpinBox(self.splitter_2)
|
|
230
|
+
self.fractional_threshold.setObjectName("fractional_threshold")
|
|
231
|
+
self.fractional_threshold.setValue((self.ft) * 100)
|
|
232
|
+
self.fractional_threshold.setMaximum(100)
|
|
233
|
+
self.fractional_threshold.setMinimum(0)
|
|
234
|
+
self.gridLayout.addWidget(self.splitter_2, 2, 0, 1, 1)
|
|
235
|
+
self.splitter_4 = QtWidgets.QSplitter(self.widget)
|
|
236
|
+
self.splitter_4.setOrientation(QtCore.Qt.Horizontal)
|
|
237
|
+
self.splitter_4.setObjectName("splitter_4")
|
|
238
|
+
self.label_4 = QtWidgets.QLabel(self.splitter_4)
|
|
239
|
+
self.label_4.setAlignment(QtCore.Qt.AlignCenter)
|
|
240
|
+
self.label_4.setObjectName("label_4")
|
|
241
|
+
self.search_distance_min = QtWidgets.QDoubleSpinBox(self.splitter_4)
|
|
242
|
+
self.search_distance_min.setObjectName("search_distance_min")
|
|
243
|
+
self.search_distance_min.setValue(self.sd_min)
|
|
244
|
+
self.search_distance_min.setMaximum(10000)
|
|
245
|
+
self.search_distance_min.setMinimum(0)
|
|
246
|
+
self.search_distance_max = QtWidgets.QDoubleSpinBox(self.splitter_4)
|
|
247
|
+
self.search_distance_max.setObjectName("search_distance_max")
|
|
248
|
+
self.search_distance_max.setValue(self.sd_max)
|
|
249
|
+
self.search_distance_max.setMaximum(10000)
|
|
250
|
+
self.search_distance_max.setMinimum(0)
|
|
251
|
+
self.gridLayout.addWidget(self.splitter_4, 3, 0, 1, 1)
|
|
252
|
+
self.splitter_5 = QtWidgets.QSplitter(self.widget)
|
|
253
|
+
self.splitter_5.setOrientation(QtCore.Qt.Horizontal)
|
|
254
|
+
self.splitter_5.setObjectName("splitter_5")
|
|
255
|
+
self.label_5 = QtWidgets.QLabel(self.splitter_5)
|
|
256
|
+
self.label_5.setAlignment(QtCore.Qt.AlignCenter)
|
|
257
|
+
self.label_5.setObjectName("label_5")
|
|
258
|
+
self.radcurv_min = QtWidgets.QDoubleSpinBox(self.splitter_5)
|
|
259
|
+
self.radcurv_min.setObjectName("radcurv_min")
|
|
260
|
+
self.radcurv_min.setValue(self.r_min)
|
|
261
|
+
self.radcurv_min.setMaximum(10000)
|
|
262
|
+
self.radcurv_min.setMinimum(0)
|
|
263
|
+
self.radcurv_max = QtWidgets.QDoubleSpinBox(self.splitter_5)
|
|
264
|
+
self.radcurv_max.setObjectName("radcurv_max")
|
|
265
|
+
self.radcurv_max.setValue(self.r_max)
|
|
266
|
+
self.radcurv_max.setMaximum(10000)
|
|
267
|
+
self.radcurv_max.setMinimum(0)
|
|
268
|
+
self.widget.setEnabled(False)
|
|
269
|
+
self.gridLayout.addWidget(self.splitter_5, 4, 0, 1, 1)
|
|
270
|
+
|
|
271
|
+
self.grid_main.addWidget(self.widget)
|
|
272
|
+
self.grid_main.addLayout(self.hbox2, 20, 0)
|
|
273
|
+
|
|
274
|
+
self.label_pr = QtWidgets.QLabel()
|
|
275
|
+
self.label_pr.setAlignment(QtCore.Qt.AlignCenter)
|
|
276
|
+
self.label_pr.setObjectName("label_2")
|
|
277
|
+
self.label_pr.setText('fdfdf')
|
|
278
|
+
self.label_pr.setVisible(False)
|
|
279
|
+
|
|
280
|
+
self.grid_main.addWidget(self.label_pr)
|
|
281
|
+
self.retranslateUi(Dialog)
|
|
282
|
+
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
|
283
|
+
|
|
284
|
+
def change_hist_threshold(self, val):
|
|
285
|
+
value = not val
|
|
286
|
+
if not val:
|
|
287
|
+
self.histogram_threshold_max.setEnabled(value)
|
|
288
|
+
self.histogram_threshold_min.setMaximum(100)
|
|
289
|
+
self.histogram_threshold_min.setMinimum(0)
|
|
290
|
+
self.histogram_threshold_min.setValue(2)
|
|
291
|
+
else:
|
|
292
|
+
self.histogram_threshold_max.setEnabled(value)
|
|
293
|
+
vl = self.histogram_threshold_min.value()
|
|
294
|
+
if vl>10:
|
|
295
|
+
self.histogram_threshold_min.setValue(10)
|
|
296
|
+
elif vl<4:
|
|
297
|
+
self.histogram_threshold_min.setValue(6)
|
|
298
|
+
self.histogram_threshold_min.setMaximum(10)
|
|
299
|
+
self.histogram_threshold_min.setMinimum(4)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def clear(self):
|
|
303
|
+
self.res = None
|
|
304
|
+
# store the image
|
|
305
|
+
self.img = None
|
|
306
|
+
|
|
307
|
+
# store conveinent references
|
|
308
|
+
self.data = None # 3D data
|
|
309
|
+
self.rdata = None # flattened data
|
|
310
|
+
self.shape = None # 3D shape
|
|
311
|
+
self.rshape = None # flattened shape
|
|
312
|
+
self.mask = None
|
|
313
|
+
|
|
314
|
+
def retranslateUi(self, Dialog):
|
|
315
|
+
_translate = QtCore.QCoreApplication.translate
|
|
316
|
+
Dialog.setWindowTitle(_translate("Dialog", "BET"))
|
|
317
|
+
self.checkBox.setText(_translate("Dialog", "Advanced"))
|
|
318
|
+
self.comboBox_image.setItemText(0, _translate("Dialog", "View 1"))
|
|
319
|
+
self.comboBox_image.setItemText(1, _translate("Dialog", "View 2"))
|
|
320
|
+
self.pushButton.setText(_translate("Dialog", "Apply"))
|
|
321
|
+
self.label_2.setText(_translate("Dialog", "Iterations"))
|
|
322
|
+
self.checkbox_thresholding.setText(_translate("Dialog", "Adaptive Thresholding"))
|
|
323
|
+
self.label.setText(_translate("Dialog", "Fractional Threshold"))
|
|
324
|
+
self.label_4.setText(_translate("Dialog", "Search Distance (mm)"))
|
|
325
|
+
self.label_5.setText(_translate("Dialog", "Radius of curvature"))
|
|
326
|
+
|
|
327
|
+
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
|
328
|
+
self.closeSig.emit()
|
|
329
|
+
super(BET, self).closeEvent(a0)
|
|
330
|
+
|
|
331
|
+
def accepted_emit(self):
|
|
332
|
+
try:
|
|
333
|
+
self.label_pr.setVisible(True)
|
|
334
|
+
self.label_pr.setText('Initialization...')
|
|
335
|
+
self.progressBar.setValue(0)
|
|
336
|
+
self.update_params()
|
|
337
|
+
self.progressBar.setValue(5)
|
|
338
|
+
self._progress = 5
|
|
339
|
+
self.initialization()
|
|
340
|
+
self.label_pr.setText('preparation...')
|
|
341
|
+
self.run()
|
|
342
|
+
self.progressBar.setValue(98)
|
|
343
|
+
self.label_pr.setText('Computing mask...')
|
|
344
|
+
self.mask = self.compute_mask()
|
|
345
|
+
self.label_pr.setVisible(False)
|
|
346
|
+
self._progress = 100
|
|
347
|
+
self.progressBar.setValue(self._progress)
|
|
348
|
+
self._progress =0
|
|
349
|
+
self.betcomp.emit()
|
|
350
|
+
except Exception as e:
|
|
351
|
+
print(e)
|
|
352
|
+
|
|
353
|
+
def initialization(self):
|
|
354
|
+
pv = self._progress
|
|
355
|
+
self.progressBar.setValue(pv+1)
|
|
356
|
+
# find the center of mass of image
|
|
357
|
+
ic, jc, kc = np.meshgrid(
|
|
358
|
+
np.arange(self.shape[0]), np.arange(self.shape[1]), np.arange(self.shape[2]), indexing="ij", copy=False
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
cdata = np.clip(self.rdata, self.t2, self.t98) * (self.rdata > self.t)
|
|
362
|
+
ci = np.average(ic.ravel(), weights=cdata)
|
|
363
|
+
cj = np.average(jc.ravel(), weights=cdata)
|
|
364
|
+
ck = np.average(kc.ravel(), weights=cdata)
|
|
365
|
+
self.c = np.array([ci, cj, ck])
|
|
366
|
+
#print("Center-of-Mass: {}".format(self.c))
|
|
367
|
+
|
|
368
|
+
# compute 1/2 head radius with spherical formula
|
|
369
|
+
self.r = 0.5 * np.cbrt(3 * np.sum(self.rdata > self.t) / (4 * np.pi))
|
|
370
|
+
#print("Head Radius: %f" % (2 * self.r))
|
|
371
|
+
|
|
372
|
+
# get median value within estimated head sphere
|
|
373
|
+
self.tm = np.median(self.data[sphere(self.shape, 2 * self.r, self.c)]) #media of all point within a sphere of the estimated radius
|
|
374
|
+
#print("Median Intensity within Head Radius: %f" % self.tm)
|
|
375
|
+
|
|
376
|
+
# generate initial surface
|
|
377
|
+
#print("Initializing surface...")
|
|
378
|
+
self.label_pr.setText('Initializing surface...')
|
|
379
|
+
self.surface = trimesh.creation.icosphere(subdivisions=4, radius=self.r)
|
|
380
|
+
self.surface = self.surface.apply_transform([[1, 0, 0, ci], [0, 1, 0, cj], [0, 0, 1, ck], [0, 0, 0, 1]])
|
|
381
|
+
|
|
382
|
+
# update the surface attributes
|
|
383
|
+
self.num_vertices = self.surface.vertices.shape[0]
|
|
384
|
+
self.num_faces = self.surface.faces.shape[0]
|
|
385
|
+
self.vertices = np.array(self.surface.vertices)
|
|
386
|
+
self.faces = np.array(self.surface.faces)
|
|
387
|
+
self.vertex_neighbors_idx = List([np.array(i) for i in self.surface.vertex_neighbors])
|
|
388
|
+
# compute location of vertices in face array
|
|
389
|
+
self.face_vertex_idxs = np.zeros((self.num_vertices, 6, 2), dtype=np.int32)
|
|
390
|
+
self.progressBar.setValue(pv+4)
|
|
391
|
+
for v in range(self.num_vertices):
|
|
392
|
+
f, i = np.asarray(self.faces == v).nonzero()
|
|
393
|
+
self.face_vertex_idxs[v, : i.shape[0], 0] = f
|
|
394
|
+
self.face_vertex_idxs[v, : i.shape[0], 1] = i
|
|
395
|
+
if i.shape[0] == 5:
|
|
396
|
+
self.face_vertex_idxs[v, 5, 0] = -1
|
|
397
|
+
self.face_vertex_idxs[v, 5, 1] = -1
|
|
398
|
+
self.progressBar.setValue(pv + 7)
|
|
399
|
+
self._progress += 12
|
|
400
|
+
self.update_surface_attributes()
|
|
401
|
+
#print("Brain extractor initialization complete!")
|
|
402
|
+
|
|
403
|
+
@staticmethod
|
|
404
|
+
@jit(nopython=True, cache=True)
|
|
405
|
+
def compute_face_normals(num_faces, faces, vertices):
|
|
406
|
+
"""
|
|
407
|
+
Compute face normals
|
|
408
|
+
"""
|
|
409
|
+
face_normals = np.zeros((num_faces, 3))
|
|
410
|
+
for i, f in enumerate(faces):
|
|
411
|
+
local_v = vertices[f]
|
|
412
|
+
a = local_v[1] - local_v[0]
|
|
413
|
+
b = local_v[2] - local_v[0]
|
|
414
|
+
face_normals[i] = np.array(
|
|
415
|
+
(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0])
|
|
416
|
+
)
|
|
417
|
+
#vec = face_normals[i]
|
|
418
|
+
#l2n = np.sqrt(vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2)
|
|
419
|
+
#face_normals[i] /= l2n
|
|
420
|
+
face_normals[i] /= l2norm(face_normals[i])
|
|
421
|
+
return face_normals
|
|
422
|
+
|
|
423
|
+
@staticmethod
|
|
424
|
+
def compute_face_angles(triangles: np.ndarray):
|
|
425
|
+
"""
|
|
426
|
+
Compute angles in triangles of each face
|
|
427
|
+
"""
|
|
428
|
+
# don't copy triangles
|
|
429
|
+
triangles = np.asanyarray(triangles, dtype=np.float64)
|
|
430
|
+
|
|
431
|
+
# get a unit vector for each edge of the triangle
|
|
432
|
+
u = triangles[:, 1] - triangles[:, 0]
|
|
433
|
+
u /= l2normarray(u)[:, np.newaxis]
|
|
434
|
+
v = triangles[:, 2] - triangles[:, 0]
|
|
435
|
+
v /= l2normarray(v)[:, np.newaxis]
|
|
436
|
+
w = triangles[:, 2] - triangles[:, 1]
|
|
437
|
+
w /= l2normarray(w)[:, np.newaxis]
|
|
438
|
+
|
|
439
|
+
# run the cosine and per-row dot product
|
|
440
|
+
result = np.zeros((len(triangles), 3), dtype=np.float64)
|
|
441
|
+
# clip to make sure we don't float error past 1.0
|
|
442
|
+
result[:, 0] = np.arccos(np.clip(diagonal_dot(u, v), -1, 1))
|
|
443
|
+
result[:, 1] = np.arccos(np.clip(diagonal_dot(-u, w), -1, 1))
|
|
444
|
+
# the third angle is just the remaining
|
|
445
|
+
result[:, 2] = np.pi - result[:, 0] - result[:, 1]
|
|
446
|
+
|
|
447
|
+
# a triangle with any zero angles is degenerate
|
|
448
|
+
# so set all of the angles to zero in that case
|
|
449
|
+
result[(result < 1e-8).any(axis=1), :] = 0.0
|
|
450
|
+
return result
|
|
451
|
+
|
|
452
|
+
@staticmethod
|
|
453
|
+
@jit(nopython=True, cache=True)
|
|
454
|
+
def compute_vertex_normals(
|
|
455
|
+
num_vertices: int,
|
|
456
|
+
faces: np.ndarray,
|
|
457
|
+
face_normals: np.ndarray,
|
|
458
|
+
face_angles: np.ndarray,
|
|
459
|
+
face_vertex_idxs: np.ndarray,
|
|
460
|
+
):
|
|
461
|
+
"""
|
|
462
|
+
Computes vertex normals
|
|
463
|
+
|
|
464
|
+
Sums face normals connected to vertex, weighting
|
|
465
|
+
by the angle the vertex makes with the face
|
|
466
|
+
"""
|
|
467
|
+
vertex_normals = np.zeros((num_vertices, 3))
|
|
468
|
+
for vertex_idx in range(num_vertices):
|
|
469
|
+
face_idxs = np.asarray([f for f in face_vertex_idxs[vertex_idx, :, 0] if f != -1])
|
|
470
|
+
inface_idxs = np.asarray([f for f in face_vertex_idxs[vertex_idx, :, 1] if f != -1])
|
|
471
|
+
surrounding_angles = face_angles.ravel()[face_idxs * 3 + inface_idxs]
|
|
472
|
+
vertex_normals[vertex_idx] = np.dot(surrounding_angles / surrounding_angles.sum(), face_normals[face_idxs])
|
|
473
|
+
vertex_normals[vertex_idx] /= l2norm(vertex_normals[vertex_idx])
|
|
474
|
+
return vertex_normals
|
|
475
|
+
|
|
476
|
+
def rebuild_surface(self):
|
|
477
|
+
"""
|
|
478
|
+
Rebuilds the surface mesh for given updated vertices
|
|
479
|
+
"""
|
|
480
|
+
self.update_surface_attributes()
|
|
481
|
+
self.surface = trimesh.Trimesh(vertices=self.vertices, faces=self.faces)
|
|
482
|
+
|
|
483
|
+
@staticmethod
|
|
484
|
+
@jit(nopython=True, cache=True)
|
|
485
|
+
def update_surf_attr(vertices: np.ndarray, neighbors_idx: list):
|
|
486
|
+
# the neighbors array is tricky because it doesn't
|
|
487
|
+
# have the structure of a nice rectangular array
|
|
488
|
+
# we initialize it to be the largest size (6) then we
|
|
489
|
+
# can make a check for valid vertices later with neighbors size
|
|
490
|
+
neighbors = np.zeros((vertices.shape[0], 6, 3))
|
|
491
|
+
neighbors_size = np.zeros(vertices.shape[0], dtype=np.int8)
|
|
492
|
+
for i, ni in enumerate(neighbors_idx):
|
|
493
|
+
for j, vi in enumerate(ni):
|
|
494
|
+
neighbors[i, j, :] = vertices[vi]
|
|
495
|
+
neighbors_size[i] = j + 1
|
|
496
|
+
|
|
497
|
+
# compute centroids
|
|
498
|
+
centroids = np.zeros((vertices.shape[0], 3))
|
|
499
|
+
for i, (n, s) in enumerate(zip(neighbors, neighbors_size)):
|
|
500
|
+
centroids[i, 0] = np.mean(n[:s, 0])
|
|
501
|
+
centroids[i, 1] = np.mean(n[:s, 1])
|
|
502
|
+
centroids[i, 2] = np.mean(n[:s, 2])
|
|
503
|
+
|
|
504
|
+
# return optimized surface attributes
|
|
505
|
+
return neighbors, neighbors_size, centroids
|
|
506
|
+
|
|
507
|
+
def update_surface_attributes(self):
|
|
508
|
+
"""
|
|
509
|
+
Updates attributes related to the surface
|
|
510
|
+
"""
|
|
511
|
+
init = self._progress
|
|
512
|
+
self.progressBar.setValue(init+1)
|
|
513
|
+
self.triangles = self.vertices[self.faces]
|
|
514
|
+
self.face_normals = self.compute_face_normals(self.num_faces, self.faces, self.vertices)
|
|
515
|
+
self.progressBar.setValue(init+1)
|
|
516
|
+
self.face_angles = self.compute_face_angles(self.triangles)
|
|
517
|
+
self.progressBar.setValue(init+1)
|
|
518
|
+
self.vertex_normals = self.compute_vertex_normals(
|
|
519
|
+
self.num_vertices, self.faces, self.face_normals, self.face_angles, self.face_vertex_idxs
|
|
520
|
+
)
|
|
521
|
+
self.progressBar.setValue(init+1)
|
|
522
|
+
self.vertex_neighbors, self.vertex_neighbors_size, self.vertex_neighbors_centroids = self.update_surf_attr(
|
|
523
|
+
self.vertices, self.vertex_neighbors_idx
|
|
524
|
+
)
|
|
525
|
+
self.progressBar.setValue(init+1)
|
|
526
|
+
self.l = self.get_mean_intervertex_distance(self.vertices, self.vertex_neighbors, self.vertex_neighbors_size)
|
|
527
|
+
self.progressBar.setValue(init+1)
|
|
528
|
+
self._progress += 6
|
|
529
|
+
@staticmethod
|
|
530
|
+
@jit(nopython=True, cache=True)
|
|
531
|
+
def get_mean_intervertex_distance(vertices: np.ndarray, neighbors: np.ndarray, sizes: np.ndarray):
|
|
532
|
+
"""
|
|
533
|
+
Computes the mean intervertex distance across the entire surface
|
|
534
|
+
"""
|
|
535
|
+
mivd = np.zeros(vertices.shape[0])
|
|
536
|
+
for v in range(vertices.shape[0]):
|
|
537
|
+
vecs = vertices[v] - neighbors[v, : sizes[v]]
|
|
538
|
+
vd = np.zeros(vecs.shape[0])
|
|
539
|
+
for i in range(vecs.shape[0]):
|
|
540
|
+
vd[i] = l2norm(vecs[i])
|
|
541
|
+
mivd[v] = np.mean(vd)
|
|
542
|
+
return np.mean(mivd)
|
|
543
|
+
|
|
544
|
+
def run(self):
|
|
545
|
+
"""
|
|
546
|
+
Runs the extraction step.
|
|
547
|
+
|
|
548
|
+
This deforms the surface based on the method outlined in"
|
|
549
|
+
|
|
550
|
+
Smith SM. Fast robust automated brain extraction. Hum Brain Mapp.
|
|
551
|
+
2002 Nov;17(3):143-55. doi: 10.1002/hbm.10062. PMID: 12391568;
|
|
552
|
+
PMCID: PMC6871816.
|
|
553
|
+
|
|
554
|
+
"""
|
|
555
|
+
iterations = self.n_iter
|
|
556
|
+
#print("Running surface deformation...")
|
|
557
|
+
# initialize s_vectors
|
|
558
|
+
s_vectors = np.zeros(self.vertices.shape)
|
|
559
|
+
|
|
560
|
+
# initialize s_vector normal/tangent
|
|
561
|
+
s_n = np.zeros(self.vertices.shape)
|
|
562
|
+
s_t = np.zeros(self.vertices.shape)
|
|
563
|
+
|
|
564
|
+
# initialize u components
|
|
565
|
+
u1 = np.zeros(self.vertices.shape)
|
|
566
|
+
u2 = np.zeros(self.vertices.shape)
|
|
567
|
+
u3 = np.zeros(self.vertices.shape)
|
|
568
|
+
u = np.zeros(self.vertices.shape)
|
|
569
|
+
pv = self._progress
|
|
570
|
+
self.progressBar.setValue(pv+1)
|
|
571
|
+
self._progress += 20
|
|
572
|
+
|
|
573
|
+
# surface deformation loop
|
|
574
|
+
for i in range(iterations):
|
|
575
|
+
#print("Iteration: %d" % i, end="\r")
|
|
576
|
+
self.label_pr.setText('{}/{}'.format(i+1, iterations))
|
|
577
|
+
self.progressBar.setValue(self._progress)
|
|
578
|
+
|
|
579
|
+
self._progress += 1
|
|
580
|
+
if self._progress>100:
|
|
581
|
+
self._progress = 0
|
|
582
|
+
# run one step of deformation
|
|
583
|
+
|
|
584
|
+
self.step_of_deformation(
|
|
585
|
+
self.data,
|
|
586
|
+
self.vertices,
|
|
587
|
+
self.vertex_normals,
|
|
588
|
+
self.vertex_neighbors_centroids,
|
|
589
|
+
self.l,
|
|
590
|
+
self.t2,
|
|
591
|
+
self.t,
|
|
592
|
+
self.tm,
|
|
593
|
+
self.t98,
|
|
594
|
+
self.E,
|
|
595
|
+
self.F,
|
|
596
|
+
self.bt,
|
|
597
|
+
self.d1,
|
|
598
|
+
self.d2,
|
|
599
|
+
s_vectors,
|
|
600
|
+
s_n,
|
|
601
|
+
s_t,
|
|
602
|
+
u1,
|
|
603
|
+
u2,
|
|
604
|
+
u3,
|
|
605
|
+
u,
|
|
606
|
+
)
|
|
607
|
+
# update vertices
|
|
608
|
+
self.vertices += u
|
|
609
|
+
|
|
610
|
+
# just update the surface attributes
|
|
611
|
+
self.update_surface_attributes()
|
|
612
|
+
|
|
613
|
+
self._progress = 90
|
|
614
|
+
self.progressBar.setValue(self._progress)
|
|
615
|
+
# update the surface
|
|
616
|
+
self.rebuild_surface()
|
|
617
|
+
self.label_pr.setText('Post Processing...')
|
|
618
|
+
|
|
619
|
+
#print("")
|
|
620
|
+
#print("Complete.")
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
@staticmethod
|
|
628
|
+
@jit(nopython=True, cache=True)
|
|
629
|
+
def step_of_deformation(
|
|
630
|
+
data: np.ndarray,
|
|
631
|
+
vertices: np.ndarray,
|
|
632
|
+
normals: np.ndarray,
|
|
633
|
+
neighbors_centroids: np.ndarray,
|
|
634
|
+
l: float, # mean invertext distance
|
|
635
|
+
t2: float,
|
|
636
|
+
t: float,
|
|
637
|
+
tm: float,
|
|
638
|
+
t98: float,
|
|
639
|
+
E: float,
|
|
640
|
+
F: float,
|
|
641
|
+
bt: float,
|
|
642
|
+
d1: float,
|
|
643
|
+
d2: float,
|
|
644
|
+
s_vectors: np.ndarray,
|
|
645
|
+
s_n: np.ndarray,
|
|
646
|
+
s_t: np.ndarray,
|
|
647
|
+
u1: np.ndarray,
|
|
648
|
+
u2: np.ndarray,
|
|
649
|
+
u3: np.ndarray,
|
|
650
|
+
u: np.ndarray,
|
|
651
|
+
):
|
|
652
|
+
"""
|
|
653
|
+
Finds a single displacement step for the surface
|
|
654
|
+
"""
|
|
655
|
+
for i, vertex in enumerate(vertices):
|
|
656
|
+
# compute s vector
|
|
657
|
+
s_vectors[i] = neighbors_centroids[i] - vertex
|
|
658
|
+
|
|
659
|
+
# split s vector into normal and tangent components
|
|
660
|
+
s_n[i] = np.dot(s_vectors[i], normals[i]) * normals[i]
|
|
661
|
+
s_t[i] = s_vectors[i] - s_n[i]
|
|
662
|
+
|
|
663
|
+
# set component u1
|
|
664
|
+
u1[i] = 0.5 * s_t[i]
|
|
665
|
+
|
|
666
|
+
# compute local radius of curvature (eq 4)
|
|
667
|
+
r = (l ** 2) / (2 * l2norm(s_n[i]))
|
|
668
|
+
|
|
669
|
+
# compute f2 # fractional update constant
|
|
670
|
+
f2 = (1 + np.tanh(F * (1 / r - E))) / 2
|
|
671
|
+
|
|
672
|
+
# set component u2
|
|
673
|
+
u2[i] = f2 * s_n[i]
|
|
674
|
+
|
|
675
|
+
# get endpoints directed interior (distance set by d1 and d2)
|
|
676
|
+
e1 = closest_integer_point(vertex - d1 * normals[i])
|
|
677
|
+
e2 = closest_integer_point(vertex - d2 * normals[i])
|
|
678
|
+
|
|
679
|
+
# get lines created by e1/e2
|
|
680
|
+
c = closest_integer_point(vertex)
|
|
681
|
+
i1 = bresenham3d(c, e1, data.shape)
|
|
682
|
+
i2 = bresenham3d(c, e2, data.shape)
|
|
683
|
+
|
|
684
|
+
# get Imin/Imax
|
|
685
|
+
|
|
686
|
+
linedata1 = [data[d[0], d[1], d[2]] for d in i1]
|
|
687
|
+
linedata1.append(tm)
|
|
688
|
+
linedata1 = np.asarray(linedata1)
|
|
689
|
+
Imin = np.max(np.asarray([t2, np.min(linedata1)])) # min intensity from far distance
|
|
690
|
+
|
|
691
|
+
linedata2 = [data[d[0], d[1], d[2]] for d in i2]
|
|
692
|
+
|
|
693
|
+
linedata2.append(t)
|
|
694
|
+
linedata2 = np.asarray(linedata2)
|
|
695
|
+
# in the original paper is tm but here we use t98 which is correct
|
|
696
|
+
Imax = np.min(np.asarray([t98, np.max(linedata2)])) # max intensity from close distance
|
|
697
|
+
|
|
698
|
+
# get tl #locally appropriate intensity threshold
|
|
699
|
+
tl = (Imax - t2) * bt + t2
|
|
700
|
+
|
|
701
|
+
# compute f3
|
|
702
|
+
f3 = 0.05 * 2 * (Imin - tl) / (Imax - t2) * l
|
|
703
|
+
|
|
704
|
+
# get component u3
|
|
705
|
+
u3[i] = f3 * normals[i]
|
|
706
|
+
# get displacement vector
|
|
707
|
+
u[:, :] = u1 + u2 + u3
|
|
708
|
+
|
|
709
|
+
@staticmethod
|
|
710
|
+
def check_bound(img_min: int, img_max: int, img_start: int, img_end: int, vol_start: int, vol_end: int):
|
|
711
|
+
if img_min < img_start:
|
|
712
|
+
vol_start = vol_start + (img_start - img_min)
|
|
713
|
+
img_min = 0
|
|
714
|
+
if img_max > img_end:
|
|
715
|
+
vol_end = vol_end - (img_max - img_end)
|
|
716
|
+
img_max = img_end
|
|
717
|
+
return img_min, img_max, img_start, img_end, vol_start, vol_end
|
|
718
|
+
|
|
719
|
+
def compute_mask(self):
|
|
720
|
+
"""
|
|
721
|
+
Convert surface mesh to volume
|
|
722
|
+
"""
|
|
723
|
+
vol = self.surface.voxelized(1)
|
|
724
|
+
vol = vol.fill()
|
|
725
|
+
self.mask = np.zeros(self.shape)
|
|
726
|
+
bounds = vol.bounds
|
|
727
|
+
|
|
728
|
+
# adjust bounds to handle data outside the field of view
|
|
729
|
+
|
|
730
|
+
# get the bounds of the volumized surface mesh
|
|
731
|
+
x_min = int(vol.bounds[0, 0]) if vol.bounds[0, 0] > 0 else int(vol.bounds[0, 0]) - 1
|
|
732
|
+
x_max = int(vol.bounds[1, 0]) if vol.bounds[1, 0] > 0 else int(vol.bounds[1, 0]) - 1
|
|
733
|
+
y_min = int(vol.bounds[0, 1]) if vol.bounds[0, 1] > 0 else int(vol.bounds[0, 1]) - 1
|
|
734
|
+
y_max = int(vol.bounds[1, 1]) if vol.bounds[1, 1] > 0 else int(vol.bounds[1, 1]) - 1
|
|
735
|
+
z_min = int(vol.bounds[0, 2]) if vol.bounds[0, 2] > 0 else int(vol.bounds[0, 2]) - 1
|
|
736
|
+
z_max = int(vol.bounds[1, 2]) if vol.bounds[1, 2] > 0 else int(vol.bounds[1, 2]) - 1
|
|
737
|
+
|
|
738
|
+
# get the extents of the original image
|
|
739
|
+
x_start = 0
|
|
740
|
+
y_start = 0
|
|
741
|
+
z_start = 0
|
|
742
|
+
x_end = int(self.shape[0])
|
|
743
|
+
y_end = int(self.shape[1])
|
|
744
|
+
z_end = int(self.shape[2])
|
|
745
|
+
|
|
746
|
+
# get the extents of the volumized surface
|
|
747
|
+
x_vol_start = 0
|
|
748
|
+
y_vol_start = 0
|
|
749
|
+
z_vol_start = 0
|
|
750
|
+
x_vol_end = int(vol.matrix.shape[0])
|
|
751
|
+
y_vol_end = int(vol.matrix.shape[1])
|
|
752
|
+
z_vol_end = int(vol.matrix.shape[2])
|
|
753
|
+
|
|
754
|
+
# if the volumized surface mesh is outside the extents of the original image
|
|
755
|
+
# we need to crop this volume to fit the image
|
|
756
|
+
x_min, x_max, x_start, x_end, x_vol_start, x_vol_end = self.check_bound(
|
|
757
|
+
x_min, x_max, x_start, x_end, x_vol_start, x_vol_end
|
|
758
|
+
)
|
|
759
|
+
y_min, y_max, y_start, y_end, y_vol_start, y_vol_end = self.check_bound(
|
|
760
|
+
y_min, y_max, y_start, y_end, y_vol_start, y_vol_end
|
|
761
|
+
)
|
|
762
|
+
z_min, z_max, z_start, z_end, z_vol_start, z_vol_end = self.check_bound(
|
|
763
|
+
z_min, z_max, z_start, z_end, z_vol_start, z_vol_end
|
|
764
|
+
)
|
|
765
|
+
self.mask[x_min:x_max, y_min:y_max, z_min:z_max] = vol.matrix[
|
|
766
|
+
x_vol_start:x_vol_end, y_vol_start:y_vol_end, z_vol_start:z_vol_end
|
|
767
|
+
]
|
|
768
|
+
return self.mask
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def save_surface(self, filename: str):
|
|
773
|
+
"""
|
|
774
|
+
Save surface in .stl
|
|
775
|
+
"""
|
|
776
|
+
self.surface.export(filename)
|
|
777
|
+
def step_of_deformation_python(self,
|
|
778
|
+
data: np.ndarray,
|
|
779
|
+
vertices: np.ndarray,
|
|
780
|
+
normals: np.ndarray,
|
|
781
|
+
neighbors_centroids: np.ndarray,
|
|
782
|
+
l: float,
|
|
783
|
+
t2: float,
|
|
784
|
+
t: float,
|
|
785
|
+
tm: float,
|
|
786
|
+
t98: float,
|
|
787
|
+
E: float,
|
|
788
|
+
F: float,
|
|
789
|
+
bt: float,
|
|
790
|
+
d1: float,
|
|
791
|
+
d2: float,
|
|
792
|
+
s_vectors: np.ndarray,
|
|
793
|
+
s_n: np.ndarray,
|
|
794
|
+
s_t: np.ndarray,
|
|
795
|
+
u1: np.ndarray,
|
|
796
|
+
u2: np.ndarray,
|
|
797
|
+
u3: np.ndarray,
|
|
798
|
+
u: np.ndarray,
|
|
799
|
+
):
|
|
800
|
+
"""
|
|
801
|
+
Finds a single displacement step for the surface
|
|
802
|
+
"""
|
|
803
|
+
#aaa = self.new_test(data,vertices,normals,neighbors_centroids,l,t2,t,tm,t98,E,F,bt,d1,d2,s_vectors,s_n,s_t,u1,u2,u3,u)
|
|
804
|
+
# loop over vertices
|
|
805
|
+
s_vectors = neighbors_centroids - vertices
|
|
806
|
+
s_n = (s_vectors[:, 0] * normals[:, 0] + s_vectors[:, 1] * normals[:, 1] + s_vectors[:, 2] * normals[:, 2]).reshape(
|
|
807
|
+
-1, 1) * normals
|
|
808
|
+
s_t = s_vectors - s_n
|
|
809
|
+
u1 = 0.5*s_t
|
|
810
|
+
# compute local radius of curvature
|
|
811
|
+
lnormsfdf = np.sqrt(s_n[:,0] ** 2 + s_n[:,1] ** 2 + s_n[:,2] ** 2)
|
|
812
|
+
r = (l ** 2) / (2 * lnormsfdf)
|
|
813
|
+
# compute f2
|
|
814
|
+
f2 = (1 + np.tanh(F * (1 / r - E))) / 2
|
|
815
|
+
# set component u2
|
|
816
|
+
u2 = f2.reshape(-1,1) * s_n
|
|
817
|
+
|
|
818
|
+
# closest integer point can be used instead
|
|
819
|
+
# get endpoints directed interior (distance set by d1 and d2)
|
|
820
|
+
e1 = np.int32(vertices - d1 * normals)
|
|
821
|
+
e2 = np.int32(vertices - d2 * normals)
|
|
822
|
+
# get lines created by e1/e2
|
|
823
|
+
c = vertices.astype('int')
|
|
824
|
+
Imin = bresenhamlines_getdata(c, e1, data, tm, t2, 'min')
|
|
825
|
+
Imax = bresenhamlines_getdata(c, e2, data, t, tm, 'max')
|
|
826
|
+
# get tl
|
|
827
|
+
tl = (Imax - t2) * bt + t2
|
|
828
|
+
|
|
829
|
+
# compute f3
|
|
830
|
+
f3 = 0.05 * 2 * (Imin - tl) / (Imax - t2) * l
|
|
831
|
+
|
|
832
|
+
# get component u3
|
|
833
|
+
u3 = f3.reshape(-1,1) * normals
|
|
834
|
+
# get displacement vector
|
|
835
|
+
u[:, :] = u1 + u2 + u3
|
|
836
|
+
def run():
|
|
837
|
+
import sys
|
|
838
|
+
app = QtWidgets.QApplication(sys.argv)
|
|
839
|
+
file = '/home/binibica/Paper_seg/results/prep/2_X_20180202_X_t1_withoutmask.nii.gz'
|
|
840
|
+
import nibabel as nib
|
|
841
|
+
m = nib.load(file)
|
|
842
|
+
res = m.header["pixdim"][1]
|
|
843
|
+
nibf = m.get_fdata()
|
|
844
|
+
from melage.utils.utils import standardize
|
|
845
|
+
#nibf = standardize(nibf)
|
|
846
|
+
window = BET()
|
|
847
|
+
window.setData(nibf, res)
|
|
848
|
+
window.set_pars()
|
|
849
|
+
window.show()
|
|
850
|
+
sys.exit(app.exec_())
|
|
851
|
+
|
|
852
|
+
if __name__ == '__main__':
|
|
853
|
+
|
|
854
|
+
run()
|
|
855
|
+
|