driftlessmap 1.6.0__tar.gz → 1.6.1__tar.gz
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.
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/CITATION.cff +2 -2
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/MANUAL.md +15 -8
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/PKG-INFO +5 -5
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/README.md +4 -4
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/UpdateLog.md +5 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/WhatsNew.md +30 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/app.py +6 -3
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/auto_registration.py +170 -37
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/slice_stacks.py +3 -1
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/version.py +1 -1
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap.egg-info/PKG-INFO +5 -5
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_atlas_view.py +9 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_auto_registration.py +73 -7
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_version.py +1 -1
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/AUTHOR.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/CONTRIBUTING.md +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/CONTRIBUTORS.md +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/LICENSE.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/MANIFEST.in +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/ORIGINS.md +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/THANKS.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/__init__.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/__main__.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/about.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/allen_downloader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/atlas_downloader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/atlas_loader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/atlas_matching.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/atlas_processor.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/atlas_transform.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/atlas_view.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/background.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/cell_detection.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/coordinate_validation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/czi_reader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/data/atlas_labels.pkl +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/data/query.csv +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/download_utils.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/app/driftlessmap.icns +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/app/driftlessmap.ico +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/app/driftlessmap.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/backward.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/dot.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/down-arrow.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/fast_backward.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/fast_forward.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/forward.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/layers/add.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/layers/ai.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/layers/eye_off.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/layers/eye_on.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/layers/eye_white.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/layers/hi.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/layers/trash.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/object.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/add.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/atlascontrol.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/atlascontrol.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/bnd.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/c_section.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/c_section2.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/cell.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/check.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/compare.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/contour.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/h_section.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/info.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/layers.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/layers.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/line.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/link.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/link_off.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/merge.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/object.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/probe.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/probe.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/rotation_horizontal.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/rotation_vertical.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/s_section.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/tool.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/tool.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/trash.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/tree_checked.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/treeview.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/treeview.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/treeview2.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/sidebar/virus.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/tdown.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/accept.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/accept2.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/aim.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/aim_not.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/anchor.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/anticlockwise_rotation.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/atlas_icon.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/boundary_register.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/cancel.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/cell_select.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/cell_select_not.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/check.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/clockwise_rotation.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/closed_path.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/closed_path2 copy.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/closed_path2.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/eraser.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/eraser.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/eye.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/eye_closed.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/fill.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/gps.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/h_flip.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/handle.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/image_icon.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/image_icon.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/info.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/inpart.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/inpart.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/lasso.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/lasso.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/left90.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/line.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/line_sites.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/linear_silicon.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/linear_silicon.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/list.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/location.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/magic-wand.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/magic_white.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/mask.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/match.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/matchbnd.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/matching.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/merge.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/move_down.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/move_left.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/move_right.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/move_up.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/moving.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/multi-probe.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/multi_pencil.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/multi_probe.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/object.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/open_path.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/outpart.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/outpart.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/pencil.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/pencil.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/plasso.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/probe.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/radar.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/rotation.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/rotation_clockwise.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/rotation_counter_clockwise.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/ruler.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/separate_sites.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/single_pencil.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/toa.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/toa_delete.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/toaa.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/toh.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/toh_delete.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/tohh.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/trans.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/trans.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/triangulation.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/two_window.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/unmerge.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/v_flip.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/virus.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/virus_register.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/vis2d.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/window2.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/window3.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/toolbar/window4.png +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/tree_close.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/tree_open.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/icons/up-arrow.svg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/image_curves.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/image_reader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/image_stacks.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/image_view.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/label_tree.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/landmarks.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/layer_geometry.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/layer_validation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/layers_control.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/main_window.ui +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/movable_points.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/obj_items.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/object_control.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/persistence.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/probe_csv.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/probe_reconstruction.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/probe_utiles.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/project_io.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/provenance.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/atlas_view_group_box.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/box_label.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/channel_selector.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/color_combo.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/decor_label.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/dialogs.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/hidden_line_edit.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/label_tree.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/main_window.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/menu_bar.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/multi_handle_slider.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/obj_ctrl_bottom_button.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/object_text_button.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/page_control.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/rotation_button.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/side_bar.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/spinbox.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/tabs.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/qss/tool_bar.qss +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/resources.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/roi_analysis.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/run_driftlessmap.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/section_suggestion_dialog.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/slice_validation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/toolbox.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/triangulation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/user_settings.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/utils.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/widgets_utils.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap/wtiles.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap.egg-info/SOURCES.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap.egg-info/dependency_links.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap.egg-info/entry_points.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap.egg-info/requires.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/driftlessmap.egg-info/top_level.txt +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/pyproject.toml +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/setup.cfg +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/setup.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/atlas_fixture.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/run_test_suite.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_allen_downloader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_atlas_cache_integrity.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_atlas_loader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_atlas_matching.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_atlas_transform.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_atlas_workers.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_background.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_cell_detection.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_coordinate_validation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_czi_reader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_download_utils.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_editing_tools.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_histogram_data.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_image_geometry.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_image_reader.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_label_tree.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_landmarks.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_layer_transforms.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_layer_validation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_layers_control.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_mesh_cache.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_object_control.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_packaging_metadata.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_persistence.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_probe_csv.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_probe_dialogs.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_probe_mapping.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_probe_reconstruction.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_project_io.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_project_persistence.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_provenance.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_roi_analysis.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_runtime_paths.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_slice_validation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_triangulation.py +0 -0
- {driftlessmap-1.6.0 → driftlessmap-1.6.1}/tests/test_widgets.py +0 -0
|
@@ -4,8 +4,8 @@ message: >-
|
|
|
4
4
|
HERBS paper listed under references.
|
|
5
5
|
title: DriftlessMap
|
|
6
6
|
type: software
|
|
7
|
-
version: 1.6.
|
|
8
|
-
date-released: 2026-09-
|
|
7
|
+
version: 1.6.1
|
|
8
|
+
date-released: 2026-09-28
|
|
9
9
|
authors:
|
|
10
10
|
- name: Mohebi & Associates
|
|
11
11
|
website: https://www.mohebi-associates.org/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# DriftlessMap User Manual
|
|
2
2
|
|
|
3
|
-
This manual applies to **DriftlessMap 1.6.
|
|
3
|
+
This manual applies to **DriftlessMap 1.6.1**.
|
|
4
4
|
|
|
5
5
|
DriftlessMap - Histological E-data Registration in Brain Space - is a desktop
|
|
6
6
|
application for aligning rodent histology with a reference atlas, reconstructing
|
|
@@ -107,7 +107,7 @@ purposes. See [Saving, loading, and exporting](#18-saving-loading-and-exporting)
|
|
|
107
107
|
|
|
108
108
|
### 2.1 Requirements
|
|
109
109
|
|
|
110
|
-
DriftlessMap 1.6.
|
|
110
|
+
DriftlessMap 1.6.1 requires:
|
|
111
111
|
|
|
112
112
|
- A 64-bit operating system and 64-bit Python 3.10 or newer.
|
|
113
113
|
- Python 3.10-3.14 for the core application.
|
|
@@ -126,7 +126,7 @@ page. The macOS DMG (`-macOS-arm64.dmg`) is built for Apple Silicon; on an Intel
|
|
|
126
126
|
Mac, use the developer installation below.
|
|
127
127
|
The desktop builds include Python and all runtime dependencies. Windows users
|
|
128
128
|
extract the entire ZIP and run `DriftlessMap.exe`; macOS users open the DMG and
|
|
129
|
-
drag `DriftlessMap.app` to Applications. The 1.6.
|
|
129
|
+
drag `DriftlessMap.app` to Applications. The 1.6.1 release is unsigned, so the
|
|
130
130
|
first launch may require Windows SmartScreen confirmation or Control-clicking
|
|
131
131
|
the macOS app and choosing **Open**.
|
|
132
132
|
|
|
@@ -174,7 +174,7 @@ python -m pip --version
|
|
|
174
174
|
python -c "import driftlessmap; print(driftlessmap.__version__)"
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
The final command should print `1.6.
|
|
177
|
+
The final command should print `1.6.1`.
|
|
178
178
|
|
|
179
179
|
### 2.5 Launching DriftlessMap
|
|
180
180
|
|
|
@@ -756,10 +756,13 @@ section and fills the Triangulation tool with paired landmarks:
|
|
|
756
756
|
2. Choose **Atlas > Propose Landmarks**. If landmarks already exist, you are
|
|
757
757
|
asked before they are replaced.
|
|
758
758
|
3. DriftlessMap fits the outlines, then refines the fit on the image
|
|
759
|
-
intensities where that improves the match. It
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
759
|
+
intensities where that improves the match. It suggests up to 10 landmarks:
|
|
760
|
+
up to 5 at the sharpest tips and notches of the outline, snapped onto the
|
|
761
|
+
section outline, and the rest on strong internal edges that both images
|
|
762
|
+
show, such as the corpus callosum. It moves the boundary anchors with the
|
|
763
|
+
same fit. The landmarks are suggestions to confirm and edit.
|
|
764
|
+
4. The status bar reports how many pairs are on the outline and how many are
|
|
765
|
+
on internal edges, and the outline overlap. The
|
|
763
766
|
Triangulation tool opens so you can check the mesh. Drag any landmark that
|
|
764
767
|
is off, add more where the anatomy needs them, and check mesh quality
|
|
765
768
|
(Section 8.5) before transferring.
|
|
@@ -768,6 +771,10 @@ The fit follows the outline and the overall shape well, but internal
|
|
|
768
771
|
boundaries can still be several voxels off. Treat the proposal as a first
|
|
769
772
|
draft of the manual landmarks, not as a finished registration.
|
|
770
773
|
|
|
774
|
+
The tutorial [Automatic Section Matching and Landmark Proposal](Tutorial/Registration_Related/automatic_section_and_landmarks.md)
|
|
775
|
+
walks through both steps on a real section, with screenshots, and explains
|
|
776
|
+
how each stage works.
|
|
777
|
+
|
|
771
778
|
### 8.3 Place paired landmarks
|
|
772
779
|
|
|
773
780
|
1. Activate Triangulation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: driftlessmap
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.1
|
|
4
4
|
Summary: Interactive histology registration and brain-atlas mapping
|
|
5
5
|
Author: Pearl Saldanha, Jacopo Paglia, Jonathan R. Whitlock, Mohebi & Associates, Ali Mohebi
|
|
6
6
|
Author-email: Jingyi Guo Fuglstad <jingyi.g.fuglstad@gmail.com>
|
|
@@ -104,7 +104,7 @@ See [CITATION.cff](CITATION.cff) for machine-readable citation metadata.
|
|
|
104
104
|
|
|
105
105
|
## Installation
|
|
106
106
|
|
|
107
|
-
DriftlessMap 1.6.
|
|
107
|
+
DriftlessMap 1.6.1 is available as a desktop application for end users and as
|
|
108
108
|
a Python package for developers.
|
|
109
109
|
|
|
110
110
|
### Mode 1: desktop application for end users
|
|
@@ -112,10 +112,10 @@ a Python package for developers.
|
|
|
112
112
|
No Python or Conda installation is required. Download the asset for your
|
|
113
113
|
computer from [GitHub Releases](https://github.com/mohebi-n-associates/DriftlessMap/releases):
|
|
114
114
|
|
|
115
|
-
- **Windows 64-bit:** download `DriftlessMap-1.6.
|
|
115
|
+
- **Windows 64-bit:** download `DriftlessMap-1.6.1-Windows-x64.zip`, extract
|
|
116
116
|
the complete folder, and double-click `DriftlessMap.exe`. Do not move the
|
|
117
117
|
executable out of its extracted folder.
|
|
118
|
-
- **macOS (Apple Silicon):** download `DriftlessMap-1.6.
|
|
118
|
+
- **macOS (Apple Silicon):** download `DriftlessMap-1.6.1-macOS-arm64.dmg`,
|
|
119
119
|
open it, and drag `DriftlessMap.app` to Applications. The application bundle
|
|
120
120
|
includes the DriftlessMap icon and all Python dependencies. Intel Macs are
|
|
121
121
|
not covered by the desktop build; use the Conda/pip installation below.
|
|
@@ -139,7 +139,7 @@ malware:
|
|
|
139
139
|
required only for the first launch.
|
|
140
140
|
|
|
141
141
|
Only use **Open Anyway** for a copy downloaded from the
|
|
142
|
-
[official DriftlessMap 1.6.
|
|
142
|
+
[official DriftlessMap 1.6.1 release](https://github.com/mohebi-n-associates/DriftlessMap/releases/tag/v1.6.1).
|
|
143
143
|
The official macOS DMG has this SHA-256 digest:
|
|
144
144
|
|
|
145
145
|
```text
|
|
@@ -46,7 +46,7 @@ See [CITATION.cff](CITATION.cff) for machine-readable citation metadata.
|
|
|
46
46
|
|
|
47
47
|
## Installation
|
|
48
48
|
|
|
49
|
-
DriftlessMap 1.6.
|
|
49
|
+
DriftlessMap 1.6.1 is available as a desktop application for end users and as
|
|
50
50
|
a Python package for developers.
|
|
51
51
|
|
|
52
52
|
### Mode 1: desktop application for end users
|
|
@@ -54,10 +54,10 @@ a Python package for developers.
|
|
|
54
54
|
No Python or Conda installation is required. Download the asset for your
|
|
55
55
|
computer from [GitHub Releases](https://github.com/mohebi-n-associates/DriftlessMap/releases):
|
|
56
56
|
|
|
57
|
-
- **Windows 64-bit:** download `DriftlessMap-1.6.
|
|
57
|
+
- **Windows 64-bit:** download `DriftlessMap-1.6.1-Windows-x64.zip`, extract
|
|
58
58
|
the complete folder, and double-click `DriftlessMap.exe`. Do not move the
|
|
59
59
|
executable out of its extracted folder.
|
|
60
|
-
- **macOS (Apple Silicon):** download `DriftlessMap-1.6.
|
|
60
|
+
- **macOS (Apple Silicon):** download `DriftlessMap-1.6.1-macOS-arm64.dmg`,
|
|
61
61
|
open it, and drag `DriftlessMap.app` to Applications. The application bundle
|
|
62
62
|
includes the DriftlessMap icon and all Python dependencies. Intel Macs are
|
|
63
63
|
not covered by the desktop build; use the Conda/pip installation below.
|
|
@@ -81,7 +81,7 @@ malware:
|
|
|
81
81
|
required only for the first launch.
|
|
82
82
|
|
|
83
83
|
Only use **Open Anyway** for a copy downloaded from the
|
|
84
|
-
[official DriftlessMap 1.6.
|
|
84
|
+
[official DriftlessMap 1.6.1 release](https://github.com/mohebi-n-associates/DriftlessMap/releases/tag/v1.6.1).
|
|
85
85
|
The official macOS DMG has this SHA-256 digest:
|
|
86
86
|
|
|
87
87
|
```text
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Update Log
|
|
2
2
|
|
|
3
|
+
### 28th September 2026
|
|
4
|
+
##### DriftlessMap 1.6.1
|
|
5
|
+
|
|
6
|
+
Fixes **Show Boundary**, which drew atlas region boundaries almost black and therefore invisibly. **Propose Landmarks** now suggests up to 10 landmarks on features both images show (outline tips and notches, shared internal edges) instead of about 36. Adds a tutorial for the automatic section and landmark suggestions.
|
|
7
|
+
|
|
3
8
|
### 27th September 2026
|
|
4
9
|
##### DriftlessMap 1.6.0
|
|
5
10
|
|
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
This cumulative release history is maintained as a single document. New
|
|
4
4
|
releases are added at the top; earlier release notes remain below them.
|
|
5
5
|
|
|
6
|
+
## DriftlessMap 1.6.1
|
|
7
|
+
|
|
8
|
+
Release date: 28 September 2026
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Show Boundary** now draws atlas region boundaries in white. Since the
|
|
13
|
+
boundary images were stored as one byte per pixel, they had been drawn
|
|
14
|
+
almost black, so the button appeared to do nothing.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Propose Landmarks** now suggests up to 10 landmarks instead of about 36,
|
|
19
|
+
each on a feature you can see and check in both images:
|
|
20
|
+
- up to 5 at the sharpest tips and notches of the brain outline, snapped
|
|
21
|
+
onto the section outline;
|
|
22
|
+
- the rest on strong internal edges that both images show, such as the
|
|
23
|
+
corpus callosum and the ventricles.
|
|
24
|
+
|
|
25
|
+
An edge seen in only one image is never chosen: an atlas region border in
|
|
26
|
+
uniform-looking tissue, or a dye track in the section. The status bar says
|
|
27
|
+
how many points are on the outline and how many are internal. Confirm or
|
|
28
|
+
drag each point, and add more by hand where the anatomy needs them.
|
|
29
|
+
|
|
30
|
+
### Documentation
|
|
31
|
+
|
|
32
|
+
- A new tutorial, [Automatic Section Matching and Landmark Proposal](Tutorial/Registration_Related/automatic_section_and_landmarks.md),
|
|
33
|
+
walks through **Suggest Atlas Section...** and **Propose Landmarks** on a
|
|
34
|
+
real sagittal section and explains how each stage works.
|
|
35
|
+
|
|
6
36
|
## DriftlessMap 1.6.0
|
|
7
37
|
|
|
8
38
|
Release date: 27 September 2026
|
|
@@ -1944,10 +1944,13 @@ class DriftlessMap(QMainWindow, FORM_Main):
|
|
|
1944
1944
|
else:
|
|
1945
1945
|
self._build_triangulation_registration(strict=False, show_error=False)
|
|
1946
1946
|
stage = "outline and intensity" if proposal.deformable_used else "outline"
|
|
1947
|
+
on_outline = sum(kind == "outline" for kind in proposal.kinds)
|
|
1947
1948
|
self.print_message(
|
|
1948
|
-
"
|
|
1949
|
-
"
|
|
1950
|
-
|
|
1949
|
+
"Suggested {} landmark pairs ({} on the outline, {} on internal edges) "
|
|
1950
|
+
"from the {} fit (outline overlap {:.2f}). Confirm or drag each one "
|
|
1951
|
+
"before transferring.".format(
|
|
1952
|
+
len(proposal.atlas_points), on_outline,
|
|
1953
|
+
len(proposal.atlas_points) - on_outline, stage, proposal.overlap_final
|
|
1951
1954
|
),
|
|
1952
1955
|
self.normal_color,
|
|
1953
1956
|
)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Automatic landmark proposal between an atlas slice and a histology section.
|
|
2
2
|
|
|
3
3
|
The atlas slice is the fixed image, so the fitted SimpleITK transform maps an
|
|
4
|
-
atlas point directly to the matching section point; sampling it at
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
atlas point directly to the matching section point; sampling it at the tips
|
|
5
|
+
and notches of the outline and at edges that both images show yields landmark
|
|
6
|
+
pairs for DriftlessMap's piecewise-affine registration. They are suggestions
|
|
7
|
+
that the user confirms and edits.
|
|
7
8
|
|
|
8
9
|
Both images are given physical coordinates in DriftlessMap's convention
|
|
9
10
|
(pixel ``k`` spans ``[k, k + 1)``; its centre is ``k + 0.5``), so points need
|
|
@@ -35,6 +36,7 @@ class LandmarkProposal:
|
|
|
35
36
|
deformable_used: bool
|
|
36
37
|
boundary_points: np.ndarray # extra atlas points mapped into the section,
|
|
37
38
|
# clamped to the section image
|
|
39
|
+
kinds: tuple = () # "outline" or "internal" for each landmark
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
def _as_image(array, scale):
|
|
@@ -131,29 +133,149 @@ def _bspline_on_intensities(fixed, moving, fixed_mask, affine, mesh_size):
|
|
|
131
133
|
return composite, after < before
|
|
132
134
|
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
136
|
+
OUTLINE_LANDMARKS = 5 # tips and notches of the outline
|
|
137
|
+
EDGE_MARGIN = 8 # atlas pixels; the slice may cut the brain at the image edge
|
|
138
|
+
MIN_AGREEMENT = 0.2 # internal edges must show clearly in both images
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _spacing(mask, count):
|
|
142
|
+
"""Typical distance between ``count`` points spread over ``mask``."""
|
|
143
|
+
return max(2.0, float(np.sqrt(mask.sum() / max(count, 1))))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _outer_contour(mask):
|
|
147
|
+
contours, _ = cv2.findContours(mask.astype(np.uint8), cv2.RETR_EXTERNAL,
|
|
148
|
+
cv2.CHAIN_APPROX_NONE)
|
|
149
|
+
if not contours:
|
|
150
|
+
return np.zeros((0, 2))
|
|
151
|
+
return max(contours, key=cv2.contourArea)[:, 0, :].astype(float)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _outline_points(atlas_mask, atlas_image, count):
|
|
155
|
+
"""The sharpest tips and notches of the atlas outline, most pronounced first.
|
|
156
|
+
|
|
157
|
+
Points where the slice cuts the brain at the image edge are skipped, as
|
|
158
|
+
are points where the labels extend past the visible template (the
|
|
159
|
+
template can be dark at the brain surface), because neither is an
|
|
160
|
+
outline that can be seen in the section.
|
|
161
|
+
"""
|
|
162
|
+
contour = _outer_contour(atlas_mask)
|
|
163
|
+
n = len(contour)
|
|
164
|
+
if n < 20 or count <= 0:
|
|
165
|
+
return np.zeros((0, 2))
|
|
166
|
+
def turning(step):
|
|
167
|
+
before = contour - np.roll(contour, step, axis=0)
|
|
168
|
+
after = np.roll(contour, -step, axis=0) - contour
|
|
169
|
+
return np.arctan2(before[:, 0] * after[:, 1] - before[:, 1] * after[:, 0],
|
|
170
|
+
(before * after).sum(axis=1))
|
|
171
|
+
|
|
172
|
+
# A wide window finds the pronounced tips and notches; a narrow one then
|
|
173
|
+
# places each at its sharpest point.
|
|
174
|
+
k = max(4, n // 40)
|
|
175
|
+
turn = turning(k)
|
|
176
|
+
turn = np.convolve(np.r_[turn[-3:], turn, turn[:3]], np.ones(7) / 7, "valid")
|
|
177
|
+
fine = turning(max(2, k // 3))
|
|
178
|
+
|
|
179
|
+
def sharpest(index):
|
|
180
|
+
window = (index + np.arange(-k, k + 1)) % n
|
|
181
|
+
same_sign = window[np.sign(fine[window]) == np.sign(turn[index])]
|
|
182
|
+
if not len(same_sign):
|
|
183
|
+
return index
|
|
184
|
+
return int(same_sign[np.argmax(np.abs(fine[same_sign]))])
|
|
185
|
+
height, width = atlas_mask.shape
|
|
186
|
+
visible = np.percentile(atlas_image[atlas_mask], 5)
|
|
187
|
+
chosen = []
|
|
188
|
+
for index in np.argsort(-np.abs(turn)):
|
|
189
|
+
if len(chosen) == count:
|
|
190
|
+
break
|
|
191
|
+
index = sharpest(int(index))
|
|
192
|
+
x, y = contour[index]
|
|
193
|
+
if not (EDGE_MARGIN <= x < width - EDGE_MARGIN and EDGE_MARGIN <= y < height - EDGE_MARGIN):
|
|
194
|
+
continue
|
|
195
|
+
if atlas_image[int(y) - 1:int(y) + 2, int(x) - 1:int(x) + 2].mean() < visible:
|
|
196
|
+
continue
|
|
197
|
+
if all(min(abs(index - j), n - abs(index - j)) > n / 10 for j in chosen):
|
|
198
|
+
chosen.append(index)
|
|
199
|
+
return contour[chosen] + 0.5
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _edge_magnitude(image):
|
|
203
|
+
blurred = cv2.GaussianBlur(np.asarray(image, dtype=np.float32), (0, 0), 1.0)
|
|
204
|
+
return np.hypot(cv2.Sobel(blurred, cv2.CV_32F, 1, 0), cv2.Sobel(blurred, cv2.CV_32F, 0, 1))
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _local_correlation(a, b, radius=5):
|
|
208
|
+
size = (2 * radius + 1, 2 * radius + 1)
|
|
209
|
+
mean_a, mean_b = cv2.boxFilter(a, -1, size), cv2.boxFilter(b, -1, size)
|
|
210
|
+
covariance = cv2.boxFilter(a * b, -1, size) - mean_a * mean_b
|
|
211
|
+
var_a = cv2.boxFilter(a * a, -1, size) - mean_a ** 2
|
|
212
|
+
var_b = cv2.boxFilter(b * b, -1, size) - mean_b ** 2
|
|
213
|
+
return covariance / np.sqrt(np.maximum(var_a * var_b, 1e-12))
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _shared_edge_score(atlas_image, section_in_atlas, inner):
|
|
217
|
+
"""Strong atlas edges that the registered section shows too, in [0, 1].
|
|
218
|
+
|
|
219
|
+
An edge seen in only one image (an atlas region border in uniform
|
|
220
|
+
tissue, or a dye track in the section) scores near zero.
|
|
221
|
+
"""
|
|
222
|
+
atlas_edges = _edge_magnitude(atlas_image)
|
|
223
|
+
section_edges = _edge_magnitude(section_in_atlas)
|
|
224
|
+
agreement = np.clip(_local_correlation(atlas_edges, section_edges), 0.0, 1.0)
|
|
225
|
+
top = np.percentile(atlas_edges[inner], 99) if inner.any() else 0.0
|
|
226
|
+
strength = np.clip(atlas_edges / top, 0.0, 1.0) if top > 0 else np.zeros_like(atlas_edges)
|
|
227
|
+
return np.where(inner, agreement * strength, 0.0)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _peaks(score, count, radius, taken=()):
|
|
231
|
+
"""Up to ``count`` score maxima above MIN_AGREEMENT, ``radius`` apart."""
|
|
232
|
+
score = score.copy()
|
|
233
|
+
for x, y in taken:
|
|
234
|
+
cv2.circle(score, (int(x), int(y)), int(radius), 0, -1)
|
|
235
|
+
points = []
|
|
236
|
+
while len(points) < count:
|
|
237
|
+
row, col = np.unravel_index(np.argmax(score), score.shape)
|
|
238
|
+
if score[row, col] < MIN_AGREEMENT:
|
|
239
|
+
break
|
|
240
|
+
points.append((col + 0.5, row + 0.5))
|
|
241
|
+
cv2.circle(score, (int(col), int(row)), int(radius), 0, -1)
|
|
145
242
|
return np.asarray(points, dtype=float).reshape(-1, 2)
|
|
146
243
|
|
|
147
244
|
|
|
148
|
-
def
|
|
245
|
+
def _section_in_atlas(section_gray, atlas_shape, transform):
|
|
246
|
+
"""The section resampled onto the atlas slice's pixels."""
|
|
247
|
+
reference = sitk.GetImageFromArray(np.zeros(atlas_shape, np.float32))
|
|
248
|
+
reference.SetOrigin((0.5, 0.5))
|
|
249
|
+
moving = sitk.GetImageFromArray(np.ascontiguousarray(section_gray, dtype=np.float32))
|
|
250
|
+
moving.SetOrigin((0.5, 0.5))
|
|
251
|
+
return sitk.GetArrayFromImage(sitk.Resample(moving, reference, transform, sitk.sitkLinear, 0.0))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _snap_to_outline(points, contour, limit):
|
|
255
|
+
"""Move each point to the nearest point of ``contour`` if within ``limit``."""
|
|
256
|
+
if not len(contour):
|
|
257
|
+
return points
|
|
258
|
+
snapped = points.copy()
|
|
259
|
+
for i, point in enumerate(points):
|
|
260
|
+
distances = np.linalg.norm(contour - point, axis=1)
|
|
261
|
+
nearest = int(np.argmin(distances))
|
|
262
|
+
if distances[nearest] <= limit:
|
|
263
|
+
snapped[i] = contour[nearest] + 0.5
|
|
264
|
+
return snapped
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def propose_landmarks(atlas_intensity, atlas_labels, section, count=10,
|
|
149
268
|
deformable=True, mesh_size=6, boundary_points=None):
|
|
150
269
|
"""Propose paired landmarks mapping an atlas slice onto a section.
|
|
151
270
|
|
|
152
271
|
``section`` must already be in the atlas orientation (see
|
|
153
|
-
``atlas_matching``). ``
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
272
|
+
``atlas_matching``). Up to ``count`` landmarks are proposed: the sharpest
|
|
273
|
+
tips and notches of the outline, snapped onto the section outline, then
|
|
274
|
+
internal points on strong edges that both images show. They are
|
|
275
|
+
suggestions for the user to confirm and edit. ``boundary_points`` are
|
|
276
|
+
further atlas points (such as the mesh's frame points) to carry through
|
|
277
|
+
the same transform; they are clamped into the section image so they
|
|
278
|
+
remain valid landmarks. Returns a :class:`LandmarkProposal`.
|
|
157
279
|
"""
|
|
158
280
|
atlas_mask = np.asarray(atlas_labels) > 0
|
|
159
281
|
if atlas_mask.sum() < 100:
|
|
@@ -189,27 +311,38 @@ def propose_landmarks(atlas_intensity, atlas_labels, section, count=36,
|
|
|
189
311
|
except RuntimeError:
|
|
190
312
|
pass # optimiser failure: fall back to the affine fit
|
|
191
313
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
314
|
+
def to_section(points):
|
|
315
|
+
return np.array([transform.TransformPoint((float(x), float(y)))
|
|
316
|
+
for x, y in points]).reshape(-1, 2)
|
|
317
|
+
|
|
195
318
|
height, width = section_mask.shape
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
319
|
+
spacing = _spacing(atlas_mask, count)
|
|
320
|
+
|
|
321
|
+
outline_atlas = _outline_points(atlas_mask, atlas_image, min(OUTLINE_LANDMARKS, count))
|
|
322
|
+
outline_section = _snap_to_outline(to_section(outline_atlas), _outer_contour(section_mask),
|
|
323
|
+
limit=0.05 * np.hypot(height, width))
|
|
324
|
+
|
|
325
|
+
inner = cv2.erode(atlas_mask.astype(np.uint8), np.ones((3, 3), np.uint8), iterations=4) > 0
|
|
326
|
+
score = _shared_edge_score(atlas_image, _section_in_atlas(section_gray / 255.0,
|
|
327
|
+
atlas_mask.shape, transform), inner)
|
|
328
|
+
radius = 0.45 * spacing # keeps internal points about a tenth of the section apart
|
|
329
|
+
internal_atlas = _peaks(score, count - len(outline_atlas), radius, taken=outline_atlas)
|
|
330
|
+
internal_section = to_section(internal_atlas)
|
|
331
|
+
if len(internal_section):
|
|
332
|
+
rows = np.clip(internal_section[:, 1].astype(int), 0, height - 1)
|
|
333
|
+
cols = np.clip(internal_section[:, 0].astype(int), 0, width - 1)
|
|
334
|
+
inside = ((internal_section[:, 0] >= 0) & (internal_section[:, 0] < width)
|
|
335
|
+
& (internal_section[:, 1] >= 0) & (internal_section[:, 1] < height))
|
|
336
|
+
keep = inside & section_mask[rows, cols]
|
|
337
|
+
internal_atlas, internal_section = internal_atlas[keep], internal_section[keep]
|
|
338
|
+
|
|
207
339
|
boundary = np.asarray(boundary_points if boundary_points is not None else [],
|
|
208
340
|
dtype=float).reshape(-1, 2)
|
|
209
|
-
carried =
|
|
210
|
-
for x, y in boundary]).reshape(-1, 2)
|
|
341
|
+
carried = to_section(boundary)
|
|
211
342
|
if len(carried):
|
|
212
343
|
carried[:, 0] = np.clip(carried[:, 0], 0, width - 1)
|
|
213
344
|
carried[:, 1] = np.clip(carried[:, 1], 0, height - 1)
|
|
214
|
-
|
|
215
|
-
|
|
345
|
+
kinds = ("outline",) * len(outline_atlas) + ("internal",) * len(internal_atlas)
|
|
346
|
+
return LandmarkProposal(np.vstack([outline_atlas, internal_atlas]),
|
|
347
|
+
np.vstack([outline_section, internal_section]),
|
|
348
|
+
overlap_affine, overlap_final, used, carried, kinds)
|
|
@@ -202,7 +202,9 @@ class SliceStacks(pg.GraphicsLayoutWidget):
|
|
|
202
202
|
self.set_boundary_data(contour)
|
|
203
203
|
|
|
204
204
|
def set_boundary_data(self, contour):
|
|
205
|
-
|
|
205
|
+
# Contours are 0/1; without explicit levels a uint8 image is drawn
|
|
206
|
+
# on a 0-255 scale and the boundary is invisible.
|
|
207
|
+
self.boundary.setImage(contour, autoLevels=False, levels=(0, 1))
|
|
206
208
|
|
|
207
209
|
def add_display_obj_to_view(self, obj_data, obj_color, obj_type):
|
|
208
210
|
if obj_type == 'probe':
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: driftlessmap
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.1
|
|
4
4
|
Summary: Interactive histology registration and brain-atlas mapping
|
|
5
5
|
Author: Pearl Saldanha, Jacopo Paglia, Jonathan R. Whitlock, Mohebi & Associates, Ali Mohebi
|
|
6
6
|
Author-email: Jingyi Guo Fuglstad <jingyi.g.fuglstad@gmail.com>
|
|
@@ -104,7 +104,7 @@ See [CITATION.cff](CITATION.cff) for machine-readable citation metadata.
|
|
|
104
104
|
|
|
105
105
|
## Installation
|
|
106
106
|
|
|
107
|
-
DriftlessMap 1.6.
|
|
107
|
+
DriftlessMap 1.6.1 is available as a desktop application for end users and as
|
|
108
108
|
a Python package for developers.
|
|
109
109
|
|
|
110
110
|
### Mode 1: desktop application for end users
|
|
@@ -112,10 +112,10 @@ a Python package for developers.
|
|
|
112
112
|
No Python or Conda installation is required. Download the asset for your
|
|
113
113
|
computer from [GitHub Releases](https://github.com/mohebi-n-associates/DriftlessMap/releases):
|
|
114
114
|
|
|
115
|
-
- **Windows 64-bit:** download `DriftlessMap-1.6.
|
|
115
|
+
- **Windows 64-bit:** download `DriftlessMap-1.6.1-Windows-x64.zip`, extract
|
|
116
116
|
the complete folder, and double-click `DriftlessMap.exe`. Do not move the
|
|
117
117
|
executable out of its extracted folder.
|
|
118
|
-
- **macOS (Apple Silicon):** download `DriftlessMap-1.6.
|
|
118
|
+
- **macOS (Apple Silicon):** download `DriftlessMap-1.6.1-macOS-arm64.dmg`,
|
|
119
119
|
open it, and drag `DriftlessMap.app` to Applications. The application bundle
|
|
120
120
|
includes the DriftlessMap icon and all Python dependencies. Intel Macs are
|
|
121
121
|
not covered by the desktop build; use the Conda/pip installation below.
|
|
@@ -139,7 +139,7 @@ malware:
|
|
|
139
139
|
required only for the first launch.
|
|
140
140
|
|
|
141
141
|
Only use **Open Anyway** for a copy downloaded from the
|
|
142
|
-
[official DriftlessMap 1.6.
|
|
142
|
+
[official DriftlessMap 1.6.1 release](https://github.com/mohebi-n-associates/DriftlessMap/releases/tag/v1.6.1).
|
|
143
143
|
The official macOS DMG has this SHA-256 digest:
|
|
144
144
|
|
|
145
145
|
```text
|
|
@@ -198,6 +198,15 @@ class AtlasViewPerformanceTests(unittest.TestCase):
|
|
|
198
198
|
view.cimg.boundary.image.shape, view.cimg.label_data.shape
|
|
199
199
|
)
|
|
200
200
|
|
|
201
|
+
def test_boundaries_are_drawn_at_full_brightness(self):
|
|
202
|
+
stack = SliceStacks()
|
|
203
|
+
contour = np.zeros((20, 20), dtype=np.uint8)
|
|
204
|
+
contour[5:15, 10] = 1
|
|
205
|
+
stack.set_boundary_data(contour)
|
|
206
|
+
stack.boundary.render()
|
|
207
|
+
self.assertEqual(stack.boundary.qimage.pixelColor(10, 10).getRgb()[:3],
|
|
208
|
+
(255, 255, 255))
|
|
209
|
+
|
|
201
210
|
def test_image_edge_hover_coordinates_are_rejected(self):
|
|
202
211
|
image = np.zeros((3, 4), dtype=np.uint8)
|
|
203
212
|
|
|
@@ -5,7 +5,11 @@ import unittest
|
|
|
5
5
|
import cv2
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
|
-
from driftlessmap.
|
|
8
|
+
from driftlessmap.atlas_matching import tissue_mask
|
|
9
|
+
from driftlessmap.auto_registration import (
|
|
10
|
+
EDGE_MARGIN, MIN_AGREEMENT, _outer_contour, _outline_points, _shared_edge_score,
|
|
11
|
+
propose_landmarks,
|
|
12
|
+
)
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
def synthetic_slice(size=(160, 240)):
|
|
@@ -54,14 +58,76 @@ class ProposeLandmarkTests(unittest.TestCase):
|
|
|
54
58
|
def test_landmarks_follow_a_known_deformation(self):
|
|
55
59
|
intensity, labels = synthetic_slice()
|
|
56
60
|
section = warped_section(intensity, labels)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
# Measure the fit on a fixed interior grid, independent of which
|
|
62
|
+
# landmarks are selected, by carrying the grid through the transform.
|
|
63
|
+
ys, xs = np.mgrid[10:160:12, 10:240:12]
|
|
64
|
+
grid = np.column_stack([xs.ravel(), ys.ravel()]).astype(float) + 0.5
|
|
65
|
+
inner = cv2.erode((labels > 0).astype(np.uint8), np.ones((9, 9), np.uint8)) > 0
|
|
66
|
+
grid = grid[inner[grid[:, 1].astype(int), grid[:, 0].astype(int)]]
|
|
67
|
+
proposal = propose_landmarks(intensity, labels, section, boundary_points=grid)
|
|
68
|
+
self.assertTrue(3 <= len(proposal.atlas_points) <= 10)
|
|
69
|
+
self.assertEqual(len(proposal.kinds), len(proposal.atlas_points))
|
|
70
|
+
errors = np.linalg.norm(proposal.boundary_points - known_warp(grid), axis=1)
|
|
71
|
+
# section pixels are 2.5x atlas pixels: within ~1.6 atlas pixels,
|
|
72
|
+
# including grid points close to the outline
|
|
73
|
+
self.assertLess(np.median(errors), 4.0, errors)
|
|
74
|
+
internal = np.array([kind == "internal" for kind in proposal.kinds])
|
|
75
|
+
if internal.any():
|
|
76
|
+
truth = known_warp(proposal.atlas_points[internal])
|
|
77
|
+
landmark_errors = np.linalg.norm(proposal.histology_points[internal] - truth, axis=1)
|
|
78
|
+
self.assertLess(np.median(landmark_errors), 6.0, landmark_errors)
|
|
63
79
|
self.assertGreater(proposal.overlap_final, 0.9)
|
|
64
80
|
|
|
81
|
+
def test_outline_points_lie_on_both_outlines(self):
|
|
82
|
+
intensity, labels = synthetic_slice()
|
|
83
|
+
section = warped_section(intensity, labels)
|
|
84
|
+
proposal = propose_landmarks(intensity, labels, section, deformable=False)
|
|
85
|
+
outline = np.array([kind == "outline" for kind in proposal.kinds])
|
|
86
|
+
self.assertGreaterEqual(outline.sum(), 2)
|
|
87
|
+
atlas_contour = _outer_contour(labels > 0) + 0.5
|
|
88
|
+
section_contour = _outer_contour(tissue_mask(section)) + 0.5
|
|
89
|
+
for a, h in zip(proposal.atlas_points[outline], proposal.histology_points[outline]):
|
|
90
|
+
self.assertLess(np.linalg.norm(atlas_contour - a, axis=1).min(), 1.0)
|
|
91
|
+
self.assertLess(np.linalg.norm(section_contour - h, axis=1).min(), 1.0)
|
|
92
|
+
# a notch where the two lobes of the synthetic slice meet is found
|
|
93
|
+
notch = np.array([[35.8, 117.4], [20.0, 84.0]]) # ellipse intersections
|
|
94
|
+
self.assertLess(min(np.linalg.norm(proposal.atlas_points[outline] - q, axis=1).min()
|
|
95
|
+
for q in notch), 12.0)
|
|
96
|
+
|
|
97
|
+
def test_internal_points_sit_on_edges_both_images_show(self):
|
|
98
|
+
intensity, labels = synthetic_slice()
|
|
99
|
+
section = warped_section(intensity, labels)
|
|
100
|
+
proposal = propose_landmarks(intensity, labels, section, deformable=False)
|
|
101
|
+
internal = proposal.atlas_points[[kind == "internal" for kind in proposal.kinds]]
|
|
102
|
+
self.assertGreaterEqual(len(internal), 1)
|
|
103
|
+
edges = cv2.Canny((intensity * 255).astype(np.uint8), 20, 60) > 0
|
|
104
|
+
ys, xs = np.nonzero(edges)
|
|
105
|
+
edge_points = np.column_stack([xs, ys]) + 0.5
|
|
106
|
+
for point in internal:
|
|
107
|
+
self.assertLess(np.linalg.norm(edge_points - point, axis=1).min(), 4.0)
|
|
108
|
+
|
|
109
|
+
def test_an_edge_in_only_one_image_scores_nothing(self):
|
|
110
|
+
atlas = np.full((80, 80), 0.4, np.float32)
|
|
111
|
+
atlas[:, 40:] = 0.9 # an edge in both images
|
|
112
|
+
section = atlas.copy()
|
|
113
|
+
section[20:60, 15] = 1.0 # a "dye track" only in the section
|
|
114
|
+
atlas_only = atlas.copy()
|
|
115
|
+
atlas_only[40:, 60] = 0.1 # a border only in the atlas
|
|
116
|
+
inner = np.ones_like(atlas, bool)
|
|
117
|
+
score = _shared_edge_score(atlas_only, section, inner)
|
|
118
|
+
self.assertGreater(score[40, 39:41].max(), MIN_AGREEMENT)
|
|
119
|
+
self.assertLess(score[40, 12:18].max(), MIN_AGREEMENT)
|
|
120
|
+
self.assertLess(score[60, 58:62].max(), MIN_AGREEMENT)
|
|
121
|
+
|
|
122
|
+
def test_outline_cut_by_the_image_edge_is_skipped(self):
|
|
123
|
+
intensity, labels = synthetic_slice()
|
|
124
|
+
cut = labels.copy()
|
|
125
|
+
cut[:, :40] = 0
|
|
126
|
+
cut[40:130, :40] = 1 # brain runs off the left edge
|
|
127
|
+
image = cv2.normalize(intensity + 0.4 * (cut > 0), None, 0, 1, cv2.NORM_MINMAX)
|
|
128
|
+
points = _outline_points(cut > 0, image, 5)
|
|
129
|
+
self.assertTrue(np.all(points[:, 0] >= EDGE_MARGIN), points)
|
|
130
|
+
|
|
65
131
|
def test_affine_only_mode_and_empty_atlas(self):
|
|
66
132
|
intensity, labels = synthetic_slice()
|
|
67
133
|
section = warped_section(intensity, labels)
|
|
@@ -22,7 +22,7 @@ class VersionTests(unittest.TestCase):
|
|
|
22
22
|
cls.app = QApplication.instance() or QApplication([])
|
|
23
23
|
|
|
24
24
|
def test_public_package_version_uses_the_canonical_value(self):
|
|
25
|
-
self.assertEqual(__version__, "1.6.
|
|
25
|
+
self.assertEqual(__version__, "1.6.1")
|
|
26
26
|
self.assertEqual(driftlessmap.__version__, __version__)
|
|
27
27
|
|
|
28
28
|
def test_main_window_display_strings_include_the_version(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|