Skip to content
Snippets Groups Projects
Commit 84cc71a5 authored by Tobias Pietzsch's avatar Tobias Pietzsch
Browse files

Dialog to select a bounding box

parent 4faa05c7
No related branches found
No related tags found
No related merge requests found
package bdv.viewer.box;
import java.util.ArrayList;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import mpicbg.spim.data.SpimDataException;
import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription;
import net.imglib2.Interval;
import net.imglib2.util.Intervals;
import bdv.BigDataViewer;
import bdv.ViewerImgLoader;
import bdv.spimdata.SpimDataMinimal;
import bdv.spimdata.WrapBasicImgLoader;
import bdv.spimdata.XmlIoSpimDataMinimal;
import bdv.tools.InitializeViewerState;
import bdv.tools.ToggleDialogAction;
import bdv.tools.VisibilityAndGroupingDialog;
import bdv.tools.brightness.BrightnessDialog;
import bdv.tools.brightness.ConverterSetup;
import bdv.tools.brightness.MinMaxGroup;
import bdv.tools.brightness.RealARGBColorConverterSetup;
import bdv.tools.brightness.SetupAssignments;
import bdv.util.AbstractNamedAction.NamedActionAdder;
import bdv.util.KeyProperties;
import bdv.util.KeyProperties.KeyStrokeAdder;
import bdv.viewer.DisplayMode;
import bdv.viewer.NavigationActions;
import bdv.viewer.SourceAndConverter;
import bdv.viewer.ViewerFrame;
import bdv.viewer.ViewerPanel;
public class BdvBoundingBoxExample
{
protected BdvBoundingBoxExample( final String xmlFilename ) throws SpimDataException
{
// =============== Load spimdata and create sources for display ==================
final SpimDataMinimal spimData = new XmlIoSpimDataMinimal().load( xmlFilename );
if ( WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ) )
{
System.err.println( "WARNING:\nOpening <SpimData> dataset that is not suited for suited for interactive browsing.\nConsider resaving as HDF5 for better performance." );
}
final AbstractSequenceDescription< ?, ?, ? > seq = spimData.getSequenceDescription();
final ArrayList< ConverterSetup > converterSetups = new ArrayList< ConverterSetup >();
final ArrayList< SourceAndConverter< ? > > sources = new ArrayList< SourceAndConverter< ? > >();
BigDataViewer.initSetups( spimData, converterSetups, sources );
final int width = 800;
final int height = 600;
final int numTimepoints = seq.getTimePoints().getTimePointsOrdered().size();
final ViewerFrame viewerFrame = new ViewerFrame( width, height, sources, numTimepoints,
( ( ViewerImgLoader< ?, ? > ) seq.getImgLoader() ).getCache() );
final ViewerPanel viewer = viewerFrame.getViewerPanel();
// =============== Create SetupAssignments, which encapsulate source color and brightness settings ==================
for ( final ConverterSetup cs : converterSetups )
if ( RealARGBColorConverterSetup.class.isInstance( cs ) )
( ( RealARGBColorConverterSetup ) cs ).setViewer( viewer );
final SetupAssignments setupAssignments = new SetupAssignments( converterSetups, 0, 65535 );
if ( setupAssignments.getMinMaxGroups().size() > 0 )
{
final MinMaxGroup group = setupAssignments.getMinMaxGroups().get( 0 );
for ( final ConverterSetup setup : setupAssignments.getConverterSetups() )
setupAssignments.moveSetupToGroup( setup, group );
}
// =============== Dialogs for brightness and visibility&grouping ==================
final BrightnessDialog brightnessDialog = new BrightnessDialog( viewerFrame, setupAssignments );
final VisibilityAndGroupingDialog activeSourcesDialog = new VisibilityAndGroupingDialog( viewerFrame, viewer.getVisibilityAndGrouping() );
// =============== the bounding box dialog ==================
final int boxSetupId = 9999; // some non-existing setup id
final Interval initialInterval = Intervals.createMinMax( 500, 100, -100, 1500, 800, 300 ); // the initially selected bounding box
final Interval rangeInterval = Intervals.createMinMax( -500, -500, -500, 3000, 3000, 1000 ); // the range (bounding box of possible bounding boxes)
final BoundingBoxDialog boundingBoxDialog = new BoundingBoxDialog( viewerFrame, viewer, setupAssignments, boxSetupId, initialInterval, rangeInterval );
// =============== install standard navigation shortcuts and S/F6/B for brightness/visibility&grouping/boundingbox dialogs ==================
final KeyProperties keyProperties = KeyProperties.readPropertyFile();
NavigationActions.installActionBindings( viewerFrame.getKeybindings(), viewer, keyProperties );
final String BRIGHTNESS_SETTINGS = "brightness settings";
final String VISIBILITY_AND_GROUPING = "visibility and grouping";
final String BOUNDING_BOX_DIALOG = "bounding box";
final ActionMap actionMap = new ActionMap();
final NamedActionAdder amap = new NamedActionAdder( actionMap );
amap.put( new ToggleDialogAction( BRIGHTNESS_SETTINGS, brightnessDialog ) );
amap.put( new ToggleDialogAction( VISIBILITY_AND_GROUPING, activeSourcesDialog ) );
amap.put( new ToggleDialogAction( BOUNDING_BOX_DIALOG, boundingBoxDialog ) );
final InputMap inputMap = new InputMap();
final KeyStrokeAdder imap = new KeyProperties( null ).adder( inputMap );
imap.put( BRIGHTNESS_SETTINGS, "S" );
imap.put( VISIBILITY_AND_GROUPING, "F6" );
imap.put( BOUNDING_BOX_DIALOG, "B" );
viewerFrame.getKeybindings().addActionMap( "bdvbox", actionMap );
viewerFrame.getKeybindings().addInputMap( "bdvbox", inputMap );
// =============== go to fused mode (overlay all sources) such that bounding box will be visible ==================
viewer.setDisplayMode( DisplayMode.FUSED );
// show viewer window
viewerFrame.setVisible( true );
// initialize transform and brightness
InitializeViewerState.initTransform( viewer );
InitializeViewerState.initBrightness( 0.001, 0.999, viewer, setupAssignments );
// ( ( Hdf5ImageLoader ) seq.imgLoader ).initCachedDimensionsFromHdf5( false );
}
public static void main( final String[] args )
{
final String fn = "/Users/Pietzsch/Desktop/bdv example/drosophila 2.xml";
try
{
System.setProperty("apple.laf.useScreenMenuBar", "true");
new BdvBoundingBoxExample( fn );
}
catch ( final Exception e )
{
e.printStackTrace();
}
}
}
package bdv.viewer.box;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.KeyStroke;
import net.imglib2.Interval;
import net.imglib2.display.RealARGBColorConverter;
import net.imglib2.type.numeric.ARGBType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import bdv.tools.brightness.RealARGBColorConverterSetup;
import bdv.tools.brightness.SetupAssignments;
import bdv.viewer.SourceAndConverter;
import bdv.viewer.ViewerPanel;
// dialog to change bounding box
// while dialog is visible, bounding box is added as a source to the viewer
public class BoundingBoxDialog extends JDialog
{
private final ViewerPanel viewer;
private final BoxRealRandomAccessible< UnsignedShortType > boxRealRandomAccessible;
private final BoxSelectionPanel boxSelectionPanel;
protected final SourceAndConverter< UnsignedShortType > boxSourceAndConverter;
protected final RealARGBColorConverterSetup boxConverterSetup;
public BoundingBoxDialog( final Frame owner, final ViewerPanel viewer, final SetupAssignments setupAssignments, final int boxSetupId, final Interval initialInterval, final Interval rangeInterval )
{
super( owner, "bounding box", false );
this.viewer = viewer;
// create a procedural RealRandomAccessible that will render the bounding box
final UnsignedShortType insideValue = new UnsignedShortType( 1000 ); // inside the box pixel value is 1000
final UnsignedShortType outsideValue = new UnsignedShortType( 0 ); // outside is 0
boxRealRandomAccessible = new BoxRealRandomAccessible< UnsignedShortType >( initialInterval, insideValue, outsideValue );
// create a bdv.viewer.Source providing data from the bbox RealRandomAccessible
final RealRandomAccessibleSource< UnsignedShortType > boxSource = new RealRandomAccessibleSource< UnsignedShortType >( boxRealRandomAccessible, "selection" )
{
@Override
public Interval getInterval( final int t, final int level )
{
return boxRealRandomAccessible.getInterval();
}
};
// set up a converter from the source type (UnsignedShortType in this case) to ARGBType
final RealARGBColorConverter< UnsignedShortType > converter = new RealARGBColorConverter.Imp1< UnsignedShortType >( 0, 3000 );
converter.setColor( new ARGBType( ARGBType.rgba( 0, 255, 0, 255 ) ) ); // set bounding box color to green
// create a ConverterSetup (can be used by the brightness dialog to adjust the converter settings)
boxConverterSetup = new RealARGBColorConverterSetup( boxSetupId, converter );
boxConverterSetup.setViewer( viewer );
// create a SourceAndConverter (can be added to the viewer for display
boxSourceAndConverter = new SourceAndConverter< UnsignedShortType >( boxSource, converter );
// create a JPanel with sliders to modify the bounding box interval (boxRealRandomAccessible.getInterval())
boxSelectionPanel = new BoxSelectionPanel( boxRealRandomAccessible.getInterval(), rangeInterval );
boxSelectionPanel.addSelectionUpdateListener( new BoxSelectionPanel.SelectionUpdateListener() // listen for updates on the bbox to trigger repainting
{
@Override
public void selectionUpdated()
{
viewer.requestRepaint();
}
} );
// button prints the bounding box interval
final JButton button = new JButton( "ok" );
button.addActionListener( new AbstractAction()
{
@Override
public void actionPerformed( final ActionEvent e )
{
System.out.println( "bounding box:" + net.imglib2.util.Util.printInterval( boxRealRandomAccessible.getInterval() ) );
}
} );
// when dialog is made visible, add bbox source
// when dialog is hidden, remove bbox source
addComponentListener( new ComponentAdapter()
{
@Override
public void componentShown( final ComponentEvent e )
{
viewer.addSource( boxSourceAndConverter );
setupAssignments.addSetup( boxConverterSetup );
}
@Override
public void componentHidden( final ComponentEvent e )
{
viewer.removeSource( boxSourceAndConverter.getSpimSource() );
setupAssignments.removeSetup( boxConverterSetup );
}
} );
// make ESC key hide dialog
final ActionMap am = getRootPane().getActionMap();
final InputMap im = getRootPane().getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
final Object hideKey = new Object();
final Action hideAction = new AbstractAction()
{
@Override
public void actionPerformed( final ActionEvent e )
{
setVisible( false );
}
};
im.put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), hideKey );
am.put( hideKey, hideAction );
// layout and show dialog
final Container content = getContentPane();
content.add( boxSelectionPanel, BorderLayout.NORTH );
content.add( button, BorderLayout.SOUTH );
pack();
setDefaultCloseOperation( JDialog.HIDE_ON_CLOSE );
}
}
package bdv.viewer.box;
import net.imglib2.Interval;
import net.imglib2.RealInterval;
import net.imglib2.RealPoint;
import net.imglib2.RealRandomAccess;
import net.imglib2.RealRandomAccessible;
import net.imglib2.type.Type;
import net.imglib2.util.Intervals;
import bdv.util.ModifiableInterval;
// a simple imglib2 RealRandomAccessible that has one value inside a box and another value outside
public class BoxRealRandomAccessible< T extends Type< T > > implements RealRandomAccessible< T >
{
private final int n;
private final ModifiableInterval interval;
private final T insideValue;
private final T outsideValue;
public BoxRealRandomAccessible( final Interval interval, final T insideValue, final T outsideValue )
{
n = interval.numDimensions();
this.interval = new ModifiableInterval( interval );
this.insideValue = insideValue.copy();
this.outsideValue = outsideValue.copy();
}
@Override
public int numDimensions()
{
return n;
}
public class Access extends RealPoint implements RealRandomAccess< T >
{
public Access()
{
super( BoxRealRandomAccessible.this.n );
}
protected Access( final Access a )
{
super( a );
}
@Override
public T get()
{
return Intervals.contains( interval, this ) ? insideValue : outsideValue;
}
@Override
public Access copy()
{
return new Access( this );
}
@Override
public Access copyRealRandomAccess()
{
return copy();
}
}
@Override
public RealRandomAccess< T > realRandomAccess()
{
return new Access();
}
@Override
public RealRandomAccess< T > realRandomAccess( final RealInterval interval )
{
return new Access();
}
public ModifiableInterval getInterval()
{
return interval;
}
}
package bdv.viewer.box;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import net.imglib2.FinalInterval;
import net.imglib2.Interval;
import bdv.tools.brightness.SliderPanel;
import bdv.util.BoundedInterval;
import bdv.util.ModifiableInterval;
// a JPanel containing X,Y,Z min/max sliders for adjusting an interval
public class BoxSelectionPanel extends JPanel
{
public static interface SelectionUpdateListener
{
public void selectionUpdated();
}
private static final long serialVersionUID = 1L;
private final BoundedInterval[] ranges;
private final ModifiableInterval selection;
private final ArrayList< SelectionUpdateListener > listeners;
public BoxSelectionPanel( final ModifiableInterval selection, final Interval rangeInterval )
{
final int n = selection.numDimensions();
this.selection = selection;
ranges = new BoundedInterval[ n ];
listeners = new ArrayList< SelectionUpdateListener >();
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
for ( int d = 0; d < n; ++d )
{
final int rangeMin = ( int ) rangeInterval.min( d );
final int rangeMax = ( int ) rangeInterval.max( d );
final int initialMin = Math.max( ( int ) selection.min( d ), rangeMin );
final int initialMax = Math.min( ( int ) selection.max( d ), rangeMax );
final BoundedInterval range = new BoundedInterval( rangeMin, rangeMax, initialMin, initialMax, 1 )
{
@Override
protected void updateInterval( final int min, final int max )
{
updateSelection();
}
};
final JPanel sliders = new JPanel();
sliders.setLayout( new BoxLayout( sliders, BoxLayout.PAGE_AXIS ) );
final SliderPanel minPanel = new SliderPanel( "min", range.getMinBoundedValue(), 1 );
minPanel.setBorder( BorderFactory.createEmptyBorder( 0, 10, 10, 10 ) );
sliders.add( minPanel );
final SliderPanel maxPanel = new SliderPanel( "max", range.getMaxBoundedValue(), 1 );
maxPanel.setBorder( BorderFactory.createEmptyBorder( 0, 10, 10, 10 ) );
sliders.add( maxPanel );
add( sliders );
ranges[ d ] = range;
}
}
public void setBoundsInterval( final Interval interval )
{
final int n = selection.numDimensions();
for ( int d = 0; d < n; ++d )
ranges[ d ].setRange( ( int ) interval.min( d ), ( int ) interval.max( d ) );
}
public void addSelectionUpdateListener( final SelectionUpdateListener l )
{
listeners.add( l );
}
public void updateSelection()
{
final int n = selection.numDimensions();
final long[] min = new long[ n ];
final long[] max = new long[ n ];
for ( int d = 0; d < n; ++d )
{
min[ d ] = ranges[ d ].getMinBoundedValue().getCurrentValue();
max[ d ] = ranges[ d ].getMaxBoundedValue().getCurrentValue();
}
selection.set( new FinalInterval( min, max ) );
for ( final SelectionUpdateListener l : listeners )
l.selectionUpdated();
}
public void updateSliders( final Interval interval )
{
final int n = selection.numDimensions();
if ( interval.numDimensions() != n )
throw new IllegalArgumentException();
final long[] min = new long[ n ];
final long[] max = new long[ n ];
interval.min( min );
interval.max( max );
for ( int d = 0; d < n; ++d )
{
ranges[ d ].getMinBoundedValue().setCurrentValue( ( int ) min[ d ] );
ranges[ d ].getMaxBoundedValue().setCurrentValue( ( int ) max[ d ] );
}
}
}
package bdv.viewer.box;
import net.imglib2.Interval;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealRandomAccessible;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.Type;
import net.imglib2.util.Util;
import net.imglib2.view.Views;
import bdv.viewer.Interpolation;
import bdv.viewer.Source;
/**
* A {@link Source} wrapping some {@link RealRandomAccessible}.
*
* @param <T>
*
* @author Tobias Pietzsch <tobias.pietzsch@gmail.com>
*/
public abstract class RealRandomAccessibleSource< T extends Type< T > > implements Source< T >
{
protected RealRandomAccessible< T > accessible;
protected final T type;
protected final String name;
public RealRandomAccessibleSource( final RealRandomAccessible< T > accessible, final String name )
{
this.accessible = accessible;
this.type = Util.getTypeFromRealRandomAccess( accessible ).createVariable();
this.name = name;
}
@Override
public boolean isPresent( final int t )
{
return true;
}
public abstract Interval getInterval( final int t, final int level );
@Override
public RandomAccessibleInterval< T > getSource( final int t, final int level )
{
return Views.interval( Views.raster( accessible ), getInterval( t, level ) );
}
@Override
public RealRandomAccessible< T > getInterpolatedSource( final int t, final int level, final Interpolation method )
{
return accessible;
}
@Override
public AffineTransform3D getSourceTransform( final int t, final int level )
{
return new AffineTransform3D();
}
@Override
public T getType()
{
return type;
}
@Override
public String getName()
{
return name;
}
@Override
public int getNumMipmapLevels()
{
return 1;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment