-
Notifications
You must be signed in to change notification settings - Fork 8
Sparse bitmask #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tpietzsch
wants to merge
27
commits into
master
Choose a base branch
from
sparse-bitmask
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Sparse bitmask #44
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
21b2933
A bitmask stored as a quadtree (or n-dimensional equivalent).
tpietzsch 933b6be
Add SparseBitmask:
tpietzsch 3d53a1f
Add SparseBitmask usage examples.
tpietzsch 64cfdcc
Make Bitmask a outer class
maarzt 5eb9114
WIP: add tiny tests + THERE'S A BUG
maarzt e9e97ca
"Fix" circular test dependency. Thanks @imagejan for this solution!!!
tpietzsch 525a222
bugfix
tpietzsch ad4ad4e
Renamings:
maarzt 388e956
Rename bbox to boundingBox
maarzt 64ae2ae
Add FIXME comment for DefaultInterval
maarzt 20f4377
Important to avoid wrong usage of this method
maarzt fee32f6
TODO comment implement Intervals.wrap()
maarzt f8f8d98
Rename BitMask to LeafBitmask
maarzt 643ac4a
Tree: introduce method getChildIndex
maarzt 76ed17a
Add Benchmark to compare performance of NTree agains SparseBitmask
maarzt d956e54
Add Benchmark against Labkit's sparse bitmaps.
maarzt 5f1381f
Tree: move operations that modify NodeData into NodeData
maarzt 0778e5d
Make NodeData an outer class
maarzt 802463c
NodeData: rename data to bitmask
maarzt 49c2187
Add final
maarzt d58b8c4
Improve thread-safety of Tree.get and Tree.getNode
maarzt 87244a5
Make tree a tree with fixed height
maarzt 34994a6
WIP
maarzt bc2b8f2
GrowableTree: some renamings
maarzt d4e2def
Make GrowableTree thread safe
maarzt 820ea7a
Add read benchmark
maarzt 6dfc807
Remove ReentrantReadWriteLock
maarzt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,293 @@ | ||
| package net.imglib2.roi.sparse; | ||
|
|
||
| import java.util.function.Predicate; | ||
|
|
||
| import net.imglib2.Interval; | ||
| import net.imglib2.roi.sparse.util.DefaultInterval; | ||
|
|
||
| /** | ||
| * A unbounded {@link SparseBitmaskNTree}, based on a {@link Tree} that grows | ||
| * when out-of-bounds pixels are set. | ||
| * <p> | ||
| * This class is thread-safe! | ||
| * | ||
| * @author Tobias Pietzsch | ||
| */ | ||
| public class GrowableTree implements SparseBitmaskNTree | ||
| { | ||
| private TreeAndOffset treeAndOffset; | ||
|
|
||
| /** | ||
| * @param leafDims | ||
| * Dimensions of a leaf bit-mask. <em>Every element must be a | ||
| * power of 2!</em> | ||
| */ | ||
| public GrowableTree( final int[] leafDims ) | ||
| { | ||
| treeAndOffset = new TreeAndOffset( new Tree( leafDims, 0 ), new long[ leafDims.length ] ); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean get( final long[] position ) | ||
| { | ||
| return get( position, new long[ numDimensions() ] ); | ||
| } | ||
|
|
||
| @Override | ||
| public void set( final long[] position, final boolean value ) | ||
| { | ||
| set( position, new long[ numDimensions() ], value ); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Set the value at the specified position. If necessary, new nodes will be | ||
| * created. If possible, nodes will be merged. | ||
| * <p> | ||
| * {@code position} must be within bounds of this tree, i.e., within the | ||
| * interval covered by the root node. | ||
| * | ||
| * @param position | ||
| * coordinates within bounds of this tree. | ||
| * @param tmp | ||
| * pre-allocated array to store translated coordinates. | ||
| * @param value | ||
| * value to store at {@code position}. | ||
| */ | ||
| public synchronized void set( final long[] position, final long[] tmp, final boolean value ) | ||
| { | ||
| final TreeAndOffset o = treeAndOffset; | ||
| final int n = o.tree.numDimensions(); | ||
| int childindex = 0; | ||
| boolean outOfBounds = false; | ||
| long[] newOffset = null; | ||
| for ( int d = 0; d < n; ++d ) | ||
| { | ||
| final long p = position[ d ] - o.offset[ d ]; | ||
| tmp[ d ] = p; | ||
| outOfBounds = outOfBounds || p < 0 || p > o.tree.bounds().max( d ); | ||
| if ( p < 0 ) | ||
| { | ||
| if(newOffset == null) | ||
| newOffset = new long[n]; | ||
| childindex |= 1 << d; | ||
| newOffset[ d ] -= o.tree.bounds().dimension( d ); | ||
| } | ||
| } | ||
| if ( outOfBounds ) | ||
| { | ||
| if ( value ) | ||
| { | ||
| if(newOffset == null) | ||
| newOffset = new long[n]; | ||
| treeAndOffset = new TreeAndOffset( Tree.newParentTree( o.tree, childindex ), newOffset ); | ||
| set( position, tmp, value ); | ||
| } | ||
| } | ||
| else | ||
| o.tree.set( tmp, value ); | ||
| } | ||
|
|
||
| /** | ||
| * Get the value at the specified position. | ||
| * <p> | ||
| * {@code position} must be within bounds of this tree, i.e., within the | ||
| * interval covered by the root node. | ||
| * | ||
| * @param position | ||
| * coordinates within bounds of this tree. | ||
| * @param tmp | ||
| * pre-allocated array to store translated coordinates. | ||
| * @return the value at {@code position}. | ||
| */ | ||
| public boolean get( final long[] position, final long[] tmp ) | ||
| { | ||
| TreeAndOffset o = treeAndOffset; | ||
| final int n = o.tree.numDimensions(); | ||
| for ( int d = 0; d < n; ++d ) | ||
| { | ||
| final long p = position[ d ] - o.offset[ d ]; | ||
| if ( p < 0 || p > o.tree.bounds().max( d ) ) | ||
| return false; | ||
| tmp[ d ] = p; | ||
| } | ||
| return o.tree.get( tmp ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the current height of the tree. | ||
| * <p> | ||
| * The height is the length of the path from the root to a leaf (containing | ||
| * a bit mask). E.g., a tree comprising only a root nodeData has | ||
| * {@code height = 0}. | ||
| * | ||
| * @return the current height of the tree. | ||
| */ | ||
| @Override | ||
| public int height() | ||
| { | ||
| return treeAndOffset.tree.height(); | ||
| } | ||
|
|
||
| @Override | ||
| public int numDimensions() | ||
| { | ||
| return treeAndOffset.tree.numDimensions(); | ||
| } | ||
|
|
||
| @Override | ||
| public void forEach( final Predicate< Node > op ) | ||
| { | ||
| TreeAndOffset o = treeAndOffset; | ||
| final TranslatedNode w = new TranslatedNode( o.offset ); | ||
| o.tree.forEach( ( final Node nd ) -> op.test( w.wrap( nd ) ) ); | ||
| } | ||
|
|
||
| @Override | ||
| public NodeIterator iterator() | ||
| { | ||
| return new TranslatedNodeIterator( treeAndOffset ); | ||
| } | ||
|
|
||
| private static class TreeAndOffset | ||
| { | ||
| private final Tree tree; | ||
| private final long[] offset; | ||
|
|
||
| private TreeAndOffset( Tree tree, long[] offset ) | ||
| { | ||
| this.tree = tree; | ||
| this.offset = offset; | ||
| } | ||
| } | ||
|
|
||
| private static class TranslatedNodeIterator implements NodeIterator | ||
| { | ||
| private final TranslatedNode w; | ||
|
|
||
| private final NodeIterator wi; | ||
|
|
||
| private TranslatedNodeIterator( TreeAndOffset tree ) | ||
| { | ||
| w = new TranslatedNode( tree.offset ); | ||
| wi = tree.tree.iterator(); | ||
| } | ||
|
|
||
| private TranslatedNodeIterator( final TranslatedNodeIterator other ) | ||
| { | ||
| w = new TranslatedNode( other.w ); | ||
| wi = other.wi.copy(); | ||
| } | ||
|
|
||
| @Override | ||
| public void reset() | ||
| { | ||
| wi.reset(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean hasNext() | ||
| { | ||
| return wi.hasNext(); | ||
| } | ||
|
|
||
| @Override | ||
| public Node next() | ||
| { | ||
| return w.wrap( wi.next() ); | ||
| } | ||
|
|
||
| @Override | ||
| public Node current() | ||
| { | ||
| return w.wrap( wi.current() ); | ||
| } | ||
|
|
||
| @Override | ||
| public NodeIterator copy() | ||
| { | ||
| return new TranslatedNodeIterator( this ); | ||
| } | ||
| } | ||
|
|
||
| private static class TranslatedNode implements Node | ||
| { | ||
| private Node source; | ||
|
|
||
| private final long[] offset; | ||
|
|
||
| private TranslatedNode( final long[] offset ) | ||
| { | ||
| this.offset = offset; | ||
| } | ||
|
|
||
| private TranslatedNode( final TranslatedNode other ) | ||
| { | ||
| this.source = other.source; | ||
| this.offset = other.offset; | ||
| } | ||
|
|
||
| Node wrap( final Node source ) | ||
| { | ||
| this.source = source; | ||
| return this; | ||
| } | ||
|
|
||
| private final Interval interval = new DefaultInterval() | ||
| { | ||
| @Override | ||
| public long min( final int d ) | ||
| { | ||
| return source.interval().min( d ) + offset[ d ]; | ||
| } | ||
|
|
||
| @Override | ||
| public long max( final int d ) | ||
| { | ||
| return source.interval().max( d ) + offset[ d ]; | ||
| } | ||
|
|
||
| @Override | ||
| public long dimension( final int d ) | ||
| { | ||
| return source.interval().dimension( d ); | ||
| } | ||
|
|
||
| @Override | ||
| public int numDimensions() | ||
| { | ||
| return source.interval().numDimensions(); | ||
| } | ||
| }; | ||
|
|
||
| @Override | ||
| public boolean hasChildren() | ||
| { | ||
| return source.hasChildren(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean value() | ||
| { | ||
| return source.value(); | ||
| } | ||
|
|
||
| @Override | ||
| public LeafBitmask bitmask() | ||
| { | ||
| return source.bitmask(); | ||
| } | ||
|
|
||
| @Override | ||
| public Interval interval() | ||
| { | ||
| return interval; | ||
| } | ||
|
|
||
| @Override | ||
| public int level() | ||
| { | ||
| return source.level(); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Maven build on Travis now fails because
bigdataviewer-vistoolscontains a transient dependency onimglib2-roi, creating a circular dependency.As this is a
testscope dependency only, the issue can be avoided by adding an exclusion:I'm not sure though if it is the best way to address this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting hack—I didn't know that worked.Personally, I'd vote for the usage examples requiring vistools to move downstream. Perhaps into vistools itself, since it already depends on imglib2-roi transitively.