Skip to content

Commit

Permalink
Implement AnnotationOverlay
Browse files Browse the repository at this point in the history
  • Loading branch information
tischi committed Oct 29, 2024
1 parent 84a6207 commit acc3655
Show file tree
Hide file tree
Showing 16 changed files with 217 additions and 129 deletions.
13 changes: 0 additions & 13 deletions src/main/java/org/embl/mobie/lib/bdv/OverlayStringItem.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*-
* #%L
* Fiji viewer for MoBIE projects
* %%
* Copyright (C) 2018 - 2024 EMBL
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.embl.mobie.lib.bdv.overlay;

import bdv.util.*;
import net.imglib2.FinalRealInterval;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.roi.RealMaskRealInterval;
import net.imglib2.util.Intervals;
import org.embl.mobie.lib.annotation.AnnotatedRegion;
import org.embl.mobie.lib.bdv.view.SliceViewer;
import sc.fiji.bdvpg.bdv.BdvHandleHelper;

import java.awt.*;
import java.util.ArrayList;

public class AnnotatedRegionsOverlay< AR extends AnnotatedRegion >
extends BdvOverlay implements AnnotationOverlay
{
private final SliceViewer sliceViewer;
private final ArrayList< AR > annotatedRegions;
private final String annotationColumn;
private BdvOverlaySource< AnnotatedRegionsOverlay > overlaySource;
public static final int MAX_FONT_SIZE = 20;
private static final Font font = new Font( "Monospaced", Font.PLAIN, MAX_FONT_SIZE );

public AnnotatedRegionsOverlay(
SliceViewer sliceViewer,
ArrayList< AR > annotatedRegions,
String annotationColumn )
{
this.sliceViewer = sliceViewer;
this.annotatedRegions = annotatedRegions;
this.annotationColumn = annotationColumn;

this.overlaySource = BdvFunctions.showOverlay(
this,
"annotationOverlay",
BdvOptions.options().addTo( sliceViewer.getBdvHandle() ) );

// The below seems needed probably due a bug:
// https://imagesc.zulipchat.com/#narrow/stream/327326-BigDataViewer/topic/BdvOverlay.20and.20Timepoints
// https://github.com/mobie/mobie-viewer-fiji/issues/976
sliceViewer.updateTimepointSlider();
}

@Override
public void close()
{
overlaySource.removeFromBdv();
sliceViewer.getBdvHandle().getViewerPanel().requestRepaint();
}

@Override
protected void draw( Graphics2D g )
{
BdvHandle bdvHandle = sliceViewer.getBdvHandle();
final AffineTransform3D viewerTransform = bdvHandle.getViewerPanel().state().getViewerTransform();

FinalRealInterval viewerInterval = BdvHandleHelper.getViewerGlobalBoundingInterval( bdvHandle );

ArrayList< AR > visibleRegions = new ArrayList<>();
for ( AR annotatedRegion : annotatedRegions )
{
if ( Intervals.isEmpty( Intervals.intersect( viewerInterval, annotatedRegion.getMask() ) ) )
{
continue; // The region is not currently visible on the canvas
}
else
{
visibleRegions.add( annotatedRegion );
}
}

for ( AR annotation : visibleRegions )
{
final RealMaskRealInterval mask = annotation.getMask();
FinalRealInterval bounds = viewerTransform.estimateBounds( mask );

OverlayTextItem item = OverlayHelper.itemFromBounds(
g,
bounds,
annotation.getValue( annotationColumn ).toString(),
font
);

OverlayHelper.drawTextWithBackground( g, item );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,37 +26,37 @@
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.embl.mobie.lib.bdv;
package org.embl.mobie.lib.bdv.overlay;

import bdv.util.*;
import bdv.viewer.ViewerState;
import net.imglib2.FinalRealInterval;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.roi.RealMaskRealInterval;
import net.imglib2.util.Intervals;
import org.embl.mobie.lib.annotation.AnnotatedRegion;
import org.embl.mobie.lib.annotation.Annotation;
import org.embl.mobie.lib.bdv.ActiveListener;
import org.embl.mobie.lib.bdv.view.SliceViewer;
import org.embl.mobie.lib.select.Listeners;
import sc.fiji.bdvpg.bdv.BdvHandleHelper;

import java.awt.*;
import java.util.ArrayList;

public class AnnotationOverlay< A extends Annotation > extends BdvOverlay
public class AnnotatedSegmentsOrSpotsOverlay< A extends Annotation >
extends BdvOverlay implements AnnotationOverlay
{
private final SliceViewer sliceViewer;
private final ArrayList< A > annotations;
private final String annotationColumn;
private BdvOverlaySource< AnnotationOverlay > overlaySource;
private BdvOverlaySource< AnnotatedSegmentsOrSpotsOverlay > overlaySource;
public static final int MAX_FONT_SIZE = 20;
private static final Font font = new Font( "Monospaced", Font.PLAIN, MAX_FONT_SIZE );

protected final Listeners.SynchronizedList< ActiveListener > listeners
= new Listeners.SynchronizedList< ActiveListener >( );
private AffineTransform3D viewerTransform;

public AnnotationOverlay( SliceViewer sliceViewer, ArrayList< A > annotations, String annotationColumn )
public AnnotatedSegmentsOrSpotsOverlay(
SliceViewer sliceViewer,
ArrayList< A > annotations,
String annotationColumn )
{
this.sliceViewer = sliceViewer;
this.annotations = annotations;
Expand All @@ -73,6 +73,7 @@ public AnnotationOverlay( SliceViewer sliceViewer, ArrayList< A > annotations, S
sliceViewer.updateTimepointSlider();
}

@Override
public void close()
{
overlaySource.removeFromBdv();
Expand All @@ -82,65 +83,48 @@ public void close()
@Override
protected void draw( Graphics2D g )
{
final ViewerState viewerState = sliceViewer.getBdvHandle().getViewerPanel().state().snapshot();
viewerTransform = viewerState.getViewerTransform();
FinalRealInterval viewerInterval = BdvHandleHelper.getViewerGlobalBoundingInterval( sliceViewer.getBdvHandle() );
double[] min = viewerInterval.minAsDoubleArray();
double[] max = viewerInterval.maxAsDoubleArray();

// add some extent along the z-axis (which is otherwise 0)
double zMargin = ( max[ 0 ] - min[ 0 ] ) / 100; // FIXME: how much??
min[ 2 ] -= zMargin;
max[ 2 ] += zMargin;
FinalRealInterval expandedViewerGlobalInterval = new FinalRealInterval( min, max );

ArrayList< A > visibleAnnotations = new ArrayList<>();
for ( A annotation : annotations )
{
if ( Intervals.contains( expandedViewerGlobalInterval, annotation ) )
{
visibleAnnotations.add( annotation );
}
}
// TODO: If there are 10k annotations that slows down the BDV rendering

for ( A annotation : visibleAnnotations )
{
if ( annotation instanceof AnnotatedRegion )
{
// use the bounds
final RealMaskRealInterval mask = ( ( AnnotatedRegion ) annotation ).getMask();
FinalRealInterval bounds = viewerTransform.estimateBounds( mask );

OverlayStringItem item = OverlayHelper.itemFromBounds(
g,
bounds,
annotation.getValue( annotationColumn ).toString(),
font
);

OverlayHelper.drawTextWithBackground( g, item );
}
else
{
// only use the location
double[] canvasPosition = new double[ 3 ];
viewerTransform.apply( annotation.positionAsDoubleArray(), canvasPosition );

OverlayStringItem item = OverlayHelper.itemFromLocation(
g,
annotation.getValue( annotationColumn ).toString(),
canvasPosition,
visibleAnnotations.size(),
font );

OverlayHelper.drawTextWithBackground( g, item );
}
}
}
BdvHandle bdvHandle = sliceViewer.getBdvHandle();
final AffineTransform3D viewerTransform = bdvHandle.getViewerPanel().state().getViewerTransform();
//double scale = Affine3DHelpers.extractScale( viewerTransform, 2 );
int width = bdvHandle.getViewerPanel().getWidth();
int height = bdvHandle.getViewerPanel().getHeight();

if ( annotations == null || annotations.size() == 0 )
return;

public void addListener( ActiveListener activeListener )
{
listeners.add( activeListener );
OverlayTextItem item = new OverlayTextItem();
double[] globalPosition = new double[ 3 ];
double[] viewPosition = new double[ 3 ];

for ( A annotation : annotations )
{
annotation.localize( globalPosition );
viewerTransform.apply( globalPosition, viewPosition );

if ( viewPosition[ 0 ] < 0
|| viewPosition[ 1 ] < 0
|| viewPosition[ 0 ] > width
|| viewPosition[ 1 ] > height )
continue;

// final double depth = Math.abs( viewPosition[ 2 ] ) / scale;
//System.out.println( text + ": " + depth + "; " + Math.abs( viewPosition[ 2 ] ));
//float computedFontSize = ( float ) ( 3.0 * AnnotationOverlay.MAX_FONT_SIZE / Math.sqrt( numAnnotations ) );

float fontSize = (float) ( MAX_FONT_SIZE - Math.abs( viewPosition[ 2 ] ) );
if ( fontSize < 2 )
continue;

g.setFont( font.deriveFont( Math.min( MAX_FONT_SIZE, fontSize ) ) );
item.text = annotation.getValue( annotationColumn ).toString();
item.width = g.getFontMetrics().stringWidth( item.text );
item.height = g.getFontMetrics().getHeight();
item.x = ( int ) ( viewPosition[ 0 ] - item.width / 2 );
item.y = ( int ) ( viewPosition[ 1 ] + 1.5 * item.height ); // paint a bit below (good for points)

OverlayHelper.drawTextWithBackground( g, item );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.embl.mobie.lib.bdv.overlay;

public interface AnnotationOverlay
{
void close();
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.embl.mobie.lib.bdv;
package org.embl.mobie.lib.bdv.overlay;

import bdv.util.Affine3DHelpers;
import bdv.util.BdvOverlay;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.embl.mobie.lib.bdv;
package org.embl.mobie.lib.bdv.overlay;

import bdv.util.BdvFunctions;
import bdv.util.BdvHandle;
Expand All @@ -41,6 +41,7 @@
import net.imglib2.roi.RealMaskRealInterval;
import net.imglib2.util.Intervals;
import org.embl.mobie.DataStore;
import org.embl.mobie.lib.bdv.ActiveListener;
import org.embl.mobie.lib.bdv.view.SliceViewer;
import org.embl.mobie.lib.image.Image;
import org.embl.mobie.lib.image.RegionAnnotationImage;
Expand Down Expand Up @@ -176,9 +177,9 @@ protected void draw( Graphics2D g )
for ( Map.Entry< String, FinalRealInterval > entry : imageNameToBounds.entrySet() )
{
final FinalRealInterval bounds = entry.getValue();
final String name = entry.getKey();
final String imageName = entry.getKey();

OverlayStringItem item = OverlayHelper.itemFromBounds( g, bounds, name, font );
OverlayTextItem item = OverlayHelper.itemFromBounds( g, bounds, imageName, font );

OverlayHelper.drawTextWithBackground( g, item );
}
Expand Down
Loading

0 comments on commit acc3655

Please sign in to comment.