diff --git a/pom.xml b/pom.xml index 569772970..bb7ef45df 100644 --- a/pom.xml +++ b/pom.xml @@ -167,19 +167,29 @@ imagej-common - net.imagej - imagej-mesh - 0.8.2-SNAPSHOT + net.imglib2 + imglib2-mesh + 1.0.0-SNAPSHOT net.imagej - imagej-mesh-io - 0.1.3-SNAPSHOT + imagej-ops - net.imagej - imagej-ops + sc.fiji + bigvolumeviewer + 0.3.1 + + org.jogamp.jogl + jogl-all-main + 2.3.2 + + + org.jogamp.gluegen + gluegen-rt-main + 2.3.2 + @@ -222,6 +232,10 @@ + + com.google.guava + guava + com.github.vlsi.mxgraph jgraphx diff --git a/src/main/java/fiji/plugin/trackmate/Spot.java b/src/main/java/fiji/plugin/trackmate/Spot.java index 39a07e0e2..c67b3dd8b 100644 --- a/src/main/java/fiji/plugin/trackmate/Spot.java +++ b/src/main/java/fiji/plugin/trackmate/Spot.java @@ -422,34 +422,34 @@ default int numDimensions() @Override public default void move( final float distance, final int d ) { - putFeature( POSITION_FEATURES[d], getFeature( POSITION_FEATURES[d] + distance ) ); + putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] ) + distance ); } @Override public default void move( final double distance, final int d ) { - putFeature( POSITION_FEATURES[d], getFeature( POSITION_FEATURES[d] + distance ) ); + putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] ) + distance ); } @Override public default void move( final RealLocalizable distance ) { for ( int d = 0; d < 3; d++ ) - putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] + distance ) ); + putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] ) + distance.getDoublePosition( d ) ); } @Override public default void move( final float[] distance ) { for ( int d = 0; d < 3; d++ ) - putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] + distance[ d ] ) ); + putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] ) + distance[ d ] ); } @Override public default void move( final double[] distance ) { for ( int d = 0; d < 3; d++ ) - putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] + distance[ d ] ) ); + putFeature( POSITION_FEATURES[ d ], getFeature( POSITION_FEATURES[ d ] ) + distance[ d ] ); } @Override diff --git a/src/main/java/fiji/plugin/trackmate/SpotMesh.java b/src/main/java/fiji/plugin/trackmate/SpotMesh.java index 9692b30e4..a469de7c3 100644 --- a/src/main/java/fiji/plugin/trackmate/SpotMesh.java +++ b/src/main/java/fiji/plugin/trackmate/SpotMesh.java @@ -6,19 +6,18 @@ import java.util.stream.Collectors; import fiji.plugin.trackmate.util.mesh.SpotMeshIterable; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.Meshes; -import net.imagej.mesh.Triangles; -import net.imagej.mesh.Vertices; -import net.imagej.mesh.alg.zslicer.RamerDouglasPeucker; -import net.imagej.mesh.alg.zslicer.Slice; -import net.imagej.mesh.alg.zslicer.ZSlicer; -import net.imagej.mesh.nio.BufferMesh; import net.imglib2.IterableInterval; import net.imglib2.RandomAccessible; import net.imglib2.RealInterval; import net.imglib2.RealLocalizable; import net.imglib2.RealPoint; +import net.imglib2.mesh.Meshes; +import net.imglib2.mesh.alg.zslicer.RamerDouglasPeucker; +import net.imglib2.mesh.alg.zslicer.Slice; +import net.imglib2.mesh.alg.zslicer.ZSlicer; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.Vertices; +import net.imglib2.mesh.obj.nio.BufferMesh; import net.imglib2.type.numeric.RealType; import net.imglib2.util.Intervals; @@ -50,15 +49,20 @@ public SpotMesh( * * @param quality * @param name - * @param mesh + * @param m */ public SpotMesh( - final Mesh mesh, + final Mesh m, final double quality, final String name ) { // Dummy coordinates and radius. super( 0., 0., 0., 0., quality, name ); + + // Compute triangles and vertices normals. + final BufferMesh mesh = new BufferMesh( ( int ) m.vertices().size(), ( int ) m.triangles().size() ); + Meshes.calculateNormals( m, mesh ); + this.mesh = mesh; final RealPoint center = Meshes.center( mesh ); @@ -202,7 +206,7 @@ public static double volume( final Mesh mesh ) { final Vertices vertices = mesh.vertices(); - final Triangles triangles = mesh.triangles(); + final net.imglib2.mesh.obj.Triangles triangles = mesh.triangles(); final long nTriangles = triangles.size(); double sum = 0.; for ( long t = 0; t < nTriangles; t++ ) @@ -306,7 +310,7 @@ public String toString() str.append( String.format( "\n%5d: %7.2f %7.2f %7.2f", i, vertices.x( i ), vertices.y( i ), vertices.z( i ) ) ); - final Triangles triangles = mesh.triangles(); + final net.imglib2.mesh.obj.Triangles triangles = mesh.triangles(); final long nTriangles = triangles.size(); str.append( "\nF (" + nTriangles + "):" ); for ( long i = 0; i < nTriangles; i++ ) diff --git a/src/main/java/fiji/plugin/trackmate/SpotRoi.java b/src/main/java/fiji/plugin/trackmate/SpotRoi.java index ac7b0b70e..ce1a04aec 100644 --- a/src/main/java/fiji/plugin/trackmate/SpotRoi.java +++ b/src/main/java/fiji/plugin/trackmate/SpotRoi.java @@ -75,7 +75,7 @@ public SpotRoi( final double[] x, final double[] y ) { - super( ID ); + super( ID ); this.x = x; this.y = y; } diff --git a/src/main/java/fiji/plugin/trackmate/action/MeshSeriesExporter.java b/src/main/java/fiji/plugin/trackmate/action/MeshSeriesExporter.java index abe3a263c..054b723b2 100644 --- a/src/main/java/fiji/plugin/trackmate/action/MeshSeriesExporter.java +++ b/src/main/java/fiji/plugin/trackmate/action/MeshSeriesExporter.java @@ -43,11 +43,11 @@ import fiji.plugin.trackmate.TrackMate; import fiji.plugin.trackmate.gui.displaysettings.DisplaySettings; import fiji.plugin.trackmate.io.IOUtils; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.Meshes; -import net.imagej.mesh.io.ply.PLYMeshIO; -import net.imagej.mesh.nio.BufferMesh; -import net.imagej.mesh.obj.transform.TranslateMesh; +import net.imglib2.mesh.Meshes; +import net.imglib2.mesh.io.ply.PLYMeshIO; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.nio.BufferMesh; +import net.imglib2.mesh.obj.transform.TranslateMesh; public class MeshSeriesExporter extends AbstractTMAction { @@ -106,8 +106,6 @@ public static void exportMeshesToFileSeries( final SpotCollection spots, final F final File folder = new File( folderName ); folder.mkdirs(); - final PLYMeshIO io = new PLYMeshIO(); - final NavigableSet< Integer > frames = spots.keySet(); for ( final Integer frame : frames ) { @@ -128,7 +126,7 @@ public static void exportMeshesToFileSeries( final SpotCollection spots, final F Meshes.calculateNormals( merged, mesh ); try { - io.save( mesh, targetFile.getAbsolutePath() ); + PLYMeshIO.save( mesh, targetFile.getAbsolutePath() ); } catch ( final IOException e ) { diff --git a/src/main/java/fiji/plugin/trackmate/detection/MaskUtils.java b/src/main/java/fiji/plugin/trackmate/detection/MaskUtils.java index d7d2bcbc9..4557e484e 100644 --- a/src/main/java/fiji/plugin/trackmate/detection/MaskUtils.java +++ b/src/main/java/fiji/plugin/trackmate/detection/MaskUtils.java @@ -516,7 +516,6 @@ else if ( input.numDimensions() == 3 ) threshold, simplify, qualityImage ); - } else { diff --git a/src/main/java/fiji/plugin/trackmate/detection/Process2DZ.java b/src/main/java/fiji/plugin/trackmate/detection/Process2DZ.java index b29c832db..62a313bd7 100644 --- a/src/main/java/fiji/plugin/trackmate/detection/Process2DZ.java +++ b/src/main/java/fiji/plugin/trackmate/detection/Process2DZ.java @@ -1,21 +1,31 @@ package fiji.plugin.trackmate.detection; +import java.util.ArrayList; import java.util.List; +import java.util.Set; import fiji.plugin.trackmate.Logger; import fiji.plugin.trackmate.Settings; import fiji.plugin.trackmate.Spot; +import fiji.plugin.trackmate.SpotMesh; import fiji.plugin.trackmate.TrackMate; +import fiji.plugin.trackmate.TrackModel; import fiji.plugin.trackmate.action.LabelImgExporter; import fiji.plugin.trackmate.util.TMUtils; import ij.ImagePlus; import net.imagej.ImgPlus; -import net.imagej.axis.Axes; +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; import net.imglib2.algorithm.MultiThreadedBenchmarkAlgorithm; -import net.imglib2.img.display.imagej.CalibrationUtils; import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.mesh.alg.TaubinSmoothing; +import net.imglib2.mesh.obj.nio.BufferMesh; +import net.imglib2.mesh.obj.transform.TranslateMesh; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; /** * A {@link SpotDetector} for 3D images that work by running a spot segmentation @@ -36,9 +46,13 @@ public class Process2DZ< T extends RealType< T > & NativeType< T > > implements SpotDetector< T > { - private static final String BASE_ERROR_MESSAGE = "[Process3Das2DZ] "; + private static final String BASE_ERROR_MESSAGE = "[Process2DZ] "; - private final ImgPlus< T > img; + private final RandomAccessible< T > img; + + private final Interval interval; + + private final double[] calibration; private final Settings settings; @@ -46,9 +60,34 @@ public class Process2DZ< T extends RealType< T > & NativeType< T > > private List< Spot > spots; - public Process2DZ( final ImgPlus< T > img, final Settings settings, final boolean simplifyMeshes ) + /** + * Creates a new {@link Process2DZ} detector. + * + * @param img + * the input data. Must be 3D (plus possible channels) and the 3 + * dimensions must be X, Y and Z. + * @param interval + * the interval in the input data to process. Must have the same + * number of dimensions that the input data. + * @param calibration + * the pixel size array. + * @param settings + * a TrackMate settings object, configured to operate on the + * (cropped) input data as if it was a 2D+T image. + * @param simplifyMeshes + * whether or not to smooth and simplify meshes resulting from + * merging the 2D contours. + */ + public Process2DZ( + final RandomAccessible< T > img, + final Interval interval, + final double[] calibration, + final Settings settings, + final boolean simplifyMeshes ) { this.img = img; + this.interval = interval; + this.calibration = calibration; this.settings = settings; this.simplify = simplifyMeshes; } @@ -56,14 +95,9 @@ public Process2DZ( final ImgPlus< T > img, final Settings settings, final boolea @Override public boolean checkInput() { - if ( img.dimensionIndex( Axes.Z ) < 0 || img.dimension( img.dimensionIndex( Axes.Z ) ) < 2 ) - { - errorMessage = BASE_ERROR_MESSAGE + "Source image is not 3D."; - return false; - } - if ( img.dimensionIndex( Axes.TIME ) > 0 && img.dimension( img.dimensionIndex( Axes.TIME ) ) > 1 ) + if ( img.numDimensions() != 3 ) { - errorMessage = BASE_ERROR_MESSAGE + "Source image has more than one time-point."; + errorMessage = BASE_ERROR_MESSAGE + "Source image is not 3D.\n"; return false; } return true; @@ -73,13 +107,21 @@ public boolean checkInput() public boolean process() { spots = null; - // Make the final single T 3D image, a 2D + T image final by making Z -> T - final ImagePlus imp = ImageJFunctions.wrap( img, null ); - final int nChannels = ( int ) ( img.dimensionIndex( Axes.CHANNEL ) < 0 ? 1 : img.dimension( img.dimensionIndex( Axes.CHANNEL ) ) ); - final int nSlices = 1; // We force 2D. - final int nFrames = ( int ) img.dimension( img.dimensionIndex( Axes.Z ) ); - imp.setDimensions( nChannels, nSlices, nFrames ); - CalibrationUtils.copyCalibrationToImagePlus( img, imp ); + + /* + * Segment and track as a 2D+T image with the specified detector and + * settings. + */ + + // Make the final single T 3D image, a 2D + T image final by making Z->T + final IntervalView< T > cropped = Views.interval( img, interval ); + final ImagePlus imp = ImageJFunctions.wrap( cropped, null ); + final int nFrames = ( int ) interval.dimension( 2 ); + final int nChannels = ( interval.numDimensions() > 3 ) ? ( int ) interval.dimension( 3 ) : 1; + imp.setDimensions( nChannels, 1, nFrames ); + imp.getCalibration().pixelWidth = calibration[ 0 ]; + imp.getCalibration().pixelHeight = calibration[ 1 ]; + imp.getCalibration().pixelDepth = calibration[ 2 ]; // Execute segmentation and tracking. final Settings settingsFrame = settings.copyOn( imp ); @@ -95,19 +137,87 @@ public boolean process() // Get 2D+T masks final ImagePlus lblImp = LabelImgExporter.createLabelImagePlus( trackmate, false, true, false ); + /* + * Exposes tracked labels as a 3D image and segment them again with + * label image detector. + */ + // Back to a 3D single time-point image. lblImp.setDimensions( lblImp.getNChannels(), lblImp.getNFrames(), lblImp.getNSlices() ); - + // Convert labels to 3D meshes. final ImgPlus< T > lblImg = TMUtils.rawWraps( lblImp ); - final LabelImageDetector< T > detector = new LabelImageDetector<>( lblImg, lblImg, TMUtils.getSpatialCalibration( lblImp ), simplify ); + final LabelImageDetector< T > detector = new LabelImageDetector<>( lblImg, lblImg, calibration, simplify ); if ( !detector.checkInput() || !detector.process() ) { errorMessage = BASE_ERROR_MESSAGE + detector.getErrorMessage(); return false; } - - this.spots = detector.getResult(); + + final List< Spot > results = detector.getResult(); + spots = new ArrayList<>( results.size() ); + + // To read the label value (=trackID) later. + final RandomAccess< T > ra = lblImg.randomAccess(); + final TrackModel tm = trackmate.getModel().getTrackModel(); + + for ( final Spot spot : results ) + { + + /* + * Smooth spot? + */ + + final Spot newSpot; + if ( !simplify || !spot.getClass().isAssignableFrom( SpotMesh.class ) ) + { + newSpot = spot; + } + else + { + final SpotMesh sm = ( SpotMesh ) spot; + final BufferMesh out = TaubinSmoothing.smooth( TranslateMesh.translate( sm.getMesh(), sm ) ); + newSpot = SpotMeshUtils.meshToSpotMesh( out, simplify, new double[] { 1., 1., 1. }, null, new double[] { 0., 0., 0. } ); + if ( newSpot == null ) + continue; + } + + /* + * Try to get quality from the tracks resulting from the 2D+T image. + */ + + // Position RA where the spot is. + for ( int d = 0; d < 3; d++ ) + ra.setPosition( Math.round( spot.getDoublePosition( d ) / calibration[ d ] ), d ); + + // Read track ID from label value. + final int trackID = ( int ) ra.get().getRealDouble() - 1; + + // Average quality from the corresponding track. + final Set< Spot > trackSpots = tm.trackSpots( trackID ); + final double avgQuality; + if ( trackSpots != null ) + { + avgQuality = trackSpots.stream() + .mapToDouble( s -> s.getFeature( Spot.QUALITY ).doubleValue() ) + .average() + .getAsDouble(); + } + else + { + // default if something goes wrong. + avgQuality = spot.getFeature( Spot.QUALITY ); + } + + // Pass quality to new spot. + newSpot.putFeature( Spot.QUALITY, Double.valueOf( avgQuality ) ); + + // Shift them by interval min. + for ( int d = 0; d < 3; d++ ) + newSpot.move( interval.min( d ) * calibration[ d ], d ); + + spots.add( newSpot ); + } return true; } diff --git a/src/main/java/fiji/plugin/trackmate/detection/SpotMeshUtils.java b/src/main/java/fiji/plugin/trackmate/detection/SpotMeshUtils.java index 0ec65e3b4..7e9d4825e 100644 --- a/src/main/java/fiji/plugin/trackmate/detection/SpotMeshUtils.java +++ b/src/main/java/fiji/plugin/trackmate/detection/SpotMeshUtils.java @@ -7,16 +7,16 @@ import fiji.plugin.trackmate.Spot; import fiji.plugin.trackmate.SpotMesh; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.MeshConnectedComponents; -import net.imagej.mesh.Meshes; -import net.imagej.mesh.Vertices; -import net.imagej.mesh.nio.BufferMesh; import net.imglib2.Interval; import net.imglib2.IterableInterval; import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; import net.imglib2.RealInterval; +import net.imglib2.mesh.Meshes; +import net.imglib2.mesh.alg.MeshConnectedComponents; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.Vertices; +import net.imglib2.mesh.obj.nio.BufferMesh; import net.imglib2.roi.labeling.ImgLabeling; import net.imglib2.roi.labeling.LabelRegion; import net.imglib2.roi.labeling.LabelRegions; @@ -119,6 +119,10 @@ public static < T extends RealType< T >, S extends RealType< S > > List< Spot > final RealInterval bbi = boundingBoxes.get( i ); final Mesh meshi = meshes.get( i ); + /* + * FIXME revise this. Improper and incorrect. + */ + // Can we put it inside another? for ( int j = i + 1; j < meshes.size(); j++ ) { @@ -143,7 +147,7 @@ public static < T extends RealType< T >, S extends RealType< S > > List< Spot > final double[] origin = interval.minAsDoubleArray(); for ( final Mesh mesh : out ) { - final Spot spot = meshToSpotMesh( + final SpotMesh spot = meshToSpotMesh( mesh, simplify, calibration, @@ -279,7 +283,7 @@ private static < S extends RealType< S > > Spot regionToSpotMesh( * image). * @return */ - private static < S extends RealType< S > > Spot meshToSpotMesh( + public static < S extends RealType< S > > SpotMesh meshToSpotMesh( final Mesh mesh, final boolean simplify, final double[] calibration, diff --git a/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DFitEllipsoidAnalyzer.java b/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DFitEllipsoidAnalyzer.java index ba363a07a..e30e36007 100644 --- a/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DFitEllipsoidAnalyzer.java +++ b/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DFitEllipsoidAnalyzer.java @@ -22,9 +22,9 @@ import fiji.plugin.trackmate.Spot; import fiji.plugin.trackmate.SpotMesh; -import fiji.plugin.trackmate.util.mesh.EllipsoidFitter; -import fiji.plugin.trackmate.util.mesh.EllipsoidFitter.EllipsoidFit; import net.imglib2.RealLocalizable; +import net.imglib2.mesh.alg.EllipsoidFitter; +import net.imglib2.mesh.alg.EllipsoidFitter.EllipsoidFit; import net.imglib2.type.numeric.RealType; public class Spot3DFitEllipsoidAnalyzer< T extends RealType< T > > extends AbstractSpotFeatureAnalyzer< T > diff --git a/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DShapeAnalyzer.java b/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DShapeAnalyzer.java index 64aa88096..363dfd9e2 100644 --- a/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DShapeAnalyzer.java +++ b/src/main/java/fiji/plugin/trackmate/features/spot/Spot3DShapeAnalyzer.java @@ -23,10 +23,9 @@ import fiji.plugin.trackmate.Spot; import fiji.plugin.trackmate.SpotMesh; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.Meshes; -import net.imagej.ops.geom.geom3d.DefaultConvexHull3D; -import net.imagej.ops.geom.geom3d.DefaultSurfaceArea; +import net.imglib2.mesh.MeshShapeDescriptors; +import net.imglib2.mesh.alg.hull.ConvexHull; +import net.imglib2.mesh.obj.naive.NaiveDoubleMesh; import net.imglib2.type.numeric.RealType; public class Spot3DShapeAnalyzer< T extends RealType< T > > extends AbstractSpotFeatureAnalyzer< T > @@ -34,15 +33,9 @@ public class Spot3DShapeAnalyzer< T extends RealType< T > > extends AbstractSpot private final boolean is3D; - private final DefaultConvexHull3D convexHull; - - private final DefaultSurfaceArea surfaceArea; - public Spot3DShapeAnalyzer( final boolean is3D ) { this.is3D = is3D; - this.convexHull = new DefaultConvexHull3D(); - this.surfaceArea = new DefaultSurfaceArea(); } @Override @@ -58,13 +51,13 @@ public void process( final Spot spot ) if ( spot instanceof SpotMesh ) { final SpotMesh sm = ( SpotMesh ) spot; - final Mesh ch = convexHull.calculate( sm.getMesh() ); + final NaiveDoubleMesh ch = ConvexHull.calculate( sm.getMesh() ); volume = sm.volume(); - final double volumeCH = Meshes.volume( ch ); + final double volumeCH = MeshShapeDescriptors.volume( ch ); solidity = volume / volumeCH; - sa = surfaceArea.calculate( sm.getMesh() ).get(); - final double saCH = surfaceArea.calculate( ch ).get(); + sa = MeshShapeDescriptors.surfaceArea( sm.getMesh() ); + final double saCH = MeshShapeDescriptors.surfaceArea( ch ); convexity = sa / saCH; final double sphereArea = Math.pow( Math.PI, 1. / 3. ) diff --git a/src/main/java/fiji/plugin/trackmate/gui/Icons.java b/src/main/java/fiji/plugin/trackmate/gui/Icons.java index 6389fb525..a063c1b1d 100644 --- a/src/main/java/fiji/plugin/trackmate/gui/Icons.java +++ b/src/main/java/fiji/plugin/trackmate/gui/Icons.java @@ -8,12 +8,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -205,4 +205,5 @@ public class Icons public static final ImageIcon QUESTION_ICON = new ImageIcon( Icons.class.getResource( "images/help.png" ) ); + public static final ImageIcon BVV_ICON = new ImageIcon( Icons.class.getResource( "images/TrackMateBVV-logo-16x16.png" ) ); } diff --git a/src/main/java/fiji/plugin/trackmate/gui/components/ConfigureViewsPanel.java b/src/main/java/fiji/plugin/trackmate/gui/components/ConfigureViewsPanel.java index 5dd9e340a..0c47da4cf 100644 --- a/src/main/java/fiji/plugin/trackmate/gui/components/ConfigureViewsPanel.java +++ b/src/main/java/fiji/plugin/trackmate/gui/components/ConfigureViewsPanel.java @@ -8,12 +8,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -77,6 +77,7 @@ public ConfigureViewsPanel( final DisplaySettings ds, final FeatureDisplaySelector featureSelector, final String spaceUnits, + final Action launchBVVAction, final Action launchTrackSchemeAction, final Action showTrackTablesAction, final Action showSpotTableAction ) @@ -350,6 +351,11 @@ public ConfigureViewsPanel( final JPanel panelButtons = new JPanel(); + // BVV button. + final JButton btnShowBVV = new JButton( launchBVVAction ); + panelButtons.add( btnShowBVV ); + btnShowBVV.setFont( FONT ); + // TrackScheme button. final JButton btnShowTrackScheme = new JButton( launchTrackSchemeAction ); panelButtons.add( btnShowTrackScheme ); diff --git a/src/main/java/fiji/plugin/trackmate/gui/wizard/TrackMateWizardSequence.java b/src/main/java/fiji/plugin/trackmate/gui/wizard/TrackMateWizardSequence.java index 51447db41..4a73bd925 100644 --- a/src/main/java/fiji/plugin/trackmate/gui/wizard/TrackMateWizardSequence.java +++ b/src/main/java/fiji/plugin/trackmate/gui/wizard/TrackMateWizardSequence.java @@ -8,12 +8,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -21,10 +21,12 @@ */ package fiji.plugin.trackmate.gui.wizard; +import static fiji.plugin.trackmate.gui.Icons.BVV_ICON; import static fiji.plugin.trackmate.gui.Icons.SPOT_TABLE_ICON; import static fiji.plugin.trackmate.gui.Icons.TRACK_SCHEME_ICON_16x16; import static fiji.plugin.trackmate.gui.Icons.TRACK_TABLES_ICON; +import java.awt.Component; import java.awt.event.ActionEvent; import java.util.Arrays; import java.util.HashMap; @@ -32,7 +34,11 @@ import java.util.Map; import javax.swing.AbstractAction; +import javax.swing.JLabel; +import javax.swing.JRootPane; +import javax.swing.SwingUtilities; +import bvv.vistools.BvvHandle; import fiji.plugin.trackmate.Logger; import fiji.plugin.trackmate.Model; import fiji.plugin.trackmate.SelectionModel; @@ -42,10 +48,12 @@ import fiji.plugin.trackmate.action.AbstractTMAction; import fiji.plugin.trackmate.action.ExportAllSpotsStatsAction; import fiji.plugin.trackmate.action.ExportStatsTablesAction; +import fiji.plugin.trackmate.detection.DetectionUtils; import fiji.plugin.trackmate.detection.ManualDetectorFactory; import fiji.plugin.trackmate.detection.SpotDetectorFactoryBase; import fiji.plugin.trackmate.features.FeatureFilter; import fiji.plugin.trackmate.features.ModelFeatureUpdater; +import fiji.plugin.trackmate.gui.GuiUtils; import fiji.plugin.trackmate.gui.components.ConfigurationPanel; import fiji.plugin.trackmate.gui.components.FeatureDisplaySelector; import fiji.plugin.trackmate.gui.components.LogPanel; @@ -70,9 +78,12 @@ import fiji.plugin.trackmate.providers.TrackerProvider; import fiji.plugin.trackmate.tracking.SpotTrackerFactory; import fiji.plugin.trackmate.tracking.manual.ManualTrackerFactory; +import fiji.plugin.trackmate.util.EverythingDisablerAndReenabler; import fiji.plugin.trackmate.util.Threads; +import fiji.plugin.trackmate.visualization.bvv.TrackMateBVV; import fiji.plugin.trackmate.visualization.trackscheme.SpotImageUpdater; import fiji.plugin.trackmate.visualization.trackscheme.TrackScheme; +import ij.ImagePlus; public class TrackMateWizardSequence implements WizardSequence { @@ -145,7 +156,7 @@ public TrackMateWizardSequence( final TrackMate trackmate, final SelectionModel chooseTrackerDescriptor = new ChooseTrackerDescriptor( new TrackerProvider(), trackmate, displaySettings ); executeTrackingDescriptor = new ExecuteTrackingDescriptor( trackmate, logPanel, displaySettings ); trackFilterDescriptor = new TrackFilterDescriptor( trackmate, trackFilters, featureSelector ); - configureViewsDescriptor = new ConfigureViewsDescriptor( displaySettings, featureSelector, new LaunchTrackSchemeAction(), new ShowTrackTablesAction(), new ShowSpotTableAction(), model.getSpaceUnits() ); + configureViewsDescriptor = new ConfigureViewsDescriptor( displaySettings, featureSelector, new LaunchBVVAction(), new LaunchTrackSchemeAction(), new ShowTrackTablesAction(), new ShowSpotTableAction(), model.getSpaceUnits() ); grapherDescriptor = new GrapherDescriptor( trackmate, selectionModel, displaySettings ); actionChooserDescriptor = new ActionChooserDescriptor( new ActionProvider(), trackmate, selectionModel, displaySettings ); saveDescriptor = new SaveDescriptor( trackmate, displaySettings, this ); @@ -422,7 +433,7 @@ private SpotTrackerDescriptor getTrackerConfigDescriptor() previous.put( configDescriptor, chooseTrackerDescriptor ); previous.put( executeTrackingDescriptor, configDescriptor ); previous.put( trackFilterDescriptor, configDescriptor ); - + return configDescriptor; } @@ -435,6 +446,58 @@ private SpotTrackerDescriptor getTrackerConfigDescriptor() private static final String TRACKSCHEME_BUTTON_TOOLTIP = "Launch a new instance of TrackScheme."; + private static final String BVV_BUTTON_TOOLTIP = "Launch a new 3D viewer."; + + private class LaunchBVVAction extends AbstractAction + { + private static final long serialVersionUID = 1L; + + private LaunchBVVAction() + { + super( "3D view", BVV_ICON ); + putValue( SHORT_DESCRIPTION, BVV_BUTTON_TOOLTIP ); + final ImagePlus imp = trackmate.getSettings().imp; + final boolean enabled = ( imp != null ) && !DetectionUtils.is2D( imp ); + setEnabled( enabled ); + } + + @Override + public void actionPerformed( final ActionEvent e ) + { + new Thread( "Launching BVV thread" ) + { + @Override + public void run() + { + final Component c = ( Component ) e.getSource(); + final JRootPane parent = SwingUtilities.getRootPane( c ); + final EverythingDisablerAndReenabler enabler = new EverythingDisablerAndReenabler( parent, new Class[] { JLabel.class } ); + enabler.disable(); + try + { + final Model model = trackmate.getModel(); + final ImagePlus imp = trackmate.getSettings().imp; + if ( imp != null ) + { + final TrackMateBVV< ? > tbvv = new TrackMateBVV<>( model, selectionModel, imp, displaySettings ); + tbvv.render(); + final BvvHandle bvvHandle = tbvv.getBvvHandle(); + GuiUtils.positionWindow( SwingUtilities.getWindowAncestor( bvvHandle.getViewerPanel() ), c ); + } + } + catch ( final Exception e ) + { + e.printStackTrace(); + } + finally + { + enabler.reenable(); + } + } + }.start(); + } + } + private class LaunchTrackSchemeAction extends AbstractAction { private static final long serialVersionUID = 1L; diff --git a/src/main/java/fiji/plugin/trackmate/gui/wizard/descriptors/ConfigureViewsDescriptor.java b/src/main/java/fiji/plugin/trackmate/gui/wizard/descriptors/ConfigureViewsDescriptor.java index 2e7e08c14..677d12943 100644 --- a/src/main/java/fiji/plugin/trackmate/gui/wizard/descriptors/ConfigureViewsDescriptor.java +++ b/src/main/java/fiji/plugin/trackmate/gui/wizard/descriptors/ConfigureViewsDescriptor.java @@ -8,12 +8,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -36,6 +36,7 @@ public class ConfigureViewsDescriptor extends WizardPanelDescriptor public ConfigureViewsDescriptor( final DisplaySettings ds, final FeatureDisplaySelector featureSelector, + final Action launchBVVAction, final Action launchTrackSchemeAction, final Action showTrackTablesAction, final Action showSpotTableAction, @@ -43,9 +44,10 @@ public ConfigureViewsDescriptor( { super( KEY ); this.targetPanel = new ConfigureViewsPanel( - ds, - featureSelector, + ds, + featureSelector, spaceUnits, + launchBVVAction, launchTrackSchemeAction, showTrackTablesAction, showSpotTableAction ); diff --git a/src/main/java/fiji/plugin/trackmate/io/TmXmlReader.java b/src/main/java/fiji/plugin/trackmate/io/TmXmlReader.java index 1f43ba583..af8ecf771 100644 --- a/src/main/java/fiji/plugin/trackmate/io/TmXmlReader.java +++ b/src/main/java/fiji/plugin/trackmate/io/TmXmlReader.java @@ -92,7 +92,6 @@ import static fiji.plugin.trackmate.io.TmXmlKeys.TRACK_ID_ELEMENT_KEY; import static fiji.plugin.trackmate.io.TmXmlKeys.TRACK_NAME_ATTRIBUTE_NAME; import static fiji.plugin.trackmate.io.TmXmlWriter.MESH_FILE_EXTENSION; -import static fiji.plugin.trackmate.io.TmXmlWriter.PLY_MESH_IO; import static fiji.plugin.trackmate.tracking.TrackerKeys.XML_ATTRIBUTE_TRACKER_NAME; import java.io.File; @@ -156,9 +155,10 @@ import fiji.plugin.trackmate.visualization.trackscheme.TrackScheme; import ij.IJ; import ij.ImagePlus; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.Meshes; -import net.imagej.mesh.nio.BufferMesh; +import net.imglib2.mesh.Meshes; +import net.imglib2.mesh.io.ply.PLYMeshIO; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.nio.BufferMesh; public class TmXmlReader { @@ -960,7 +960,7 @@ private SpotCollection getSpots( final Element modelElement ) // Deserialize mesh. try { - final Mesh m = PLY_MESH_IO.open( zipFile.getInputStream( entry ) ); + final Mesh m = PLYMeshIO.open( zipFile.getInputStream( entry ) ); final BufferMesh mesh = new BufferMesh( ( int ) m.vertices().size(), ( int ) m.triangles().size() ); Meshes.calculateNormals( m, mesh ); @@ -976,7 +976,7 @@ private SpotCollection getSpots( final Element modelElement ) spots.remove( spot ); spots.add( spotMesh ); } - catch ( final IOException e ) + catch ( final Exception e ) { ok = false; logger.error( "Problem reading mesh for spot " + id + ":\n" diff --git a/src/main/java/fiji/plugin/trackmate/io/TmXmlWriter.java b/src/main/java/fiji/plugin/trackmate/io/TmXmlWriter.java index 2a653cd16..36286c97d 100644 --- a/src/main/java/fiji/plugin/trackmate/io/TmXmlWriter.java +++ b/src/main/java/fiji/plugin/trackmate/io/TmXmlWriter.java @@ -133,15 +133,13 @@ import fiji.plugin.trackmate.gui.displaysettings.DisplaySettingsIO; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.procedure.TIntIntProcedure; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.io.ply.PLYMeshIO; -import net.imagej.mesh.obj.transform.TranslateMesh; +import net.imglib2.mesh.io.ply.PLYMeshIO; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.transform.TranslateMesh; public class TmXmlWriter { - static final PLYMeshIO PLY_MESH_IO = new PLYMeshIO(); - static final String MESH_FILE_EXTENSION = ".meshes"; /** Zip compression level (0-9) */ @@ -791,7 +789,7 @@ protected void writeSpotMeshes( final Iterable< Spot > spots ) final SpotMesh sm = ( SpotMesh ) spot; final Mesh mesh = sm.getMesh(); final Mesh translated = TranslateMesh.translate( mesh, spot ); - final byte[] bs = PLY_MESH_IO.writeBinary( translated ); + final byte[] bs = PLYMeshIO.writeBinary( translated ); final String entryName = spot.ID() + ".ply"; zos.putNextEntry( new ZipEntry( entryName ) ); diff --git a/src/main/java/fiji/plugin/trackmate/tracking/overlap/OverlapTracker.java b/src/main/java/fiji/plugin/trackmate/tracking/overlap/OverlapTracker.java index 5b0983ce4..a49085ddc 100644 --- a/src/main/java/fiji/plugin/trackmate/tracking/overlap/OverlapTracker.java +++ b/src/main/java/fiji/plugin/trackmate/tracking/overlap/OverlapTracker.java @@ -203,7 +203,11 @@ public boolean process() final Map< Spot, Polygon2D > targetGeometries = createGeometry( spots.iterable( targetFrame, true ), method, enlargeFactor ); if ( sourceGeometries.isEmpty() || targetGeometries.isEmpty() ) + { + sourceGeometries = targetGeometries; + logger.setProgress( ( double ) progress++ / spots.keySet().size() ); continue; + } final ExecutorService executors = Threads.newFixedThreadPool( numThreads ); final List< Future< IoULink > > futures = new ArrayList<>(); diff --git a/src/main/java/fiji/plugin/trackmate/util/TMUtils.java b/src/main/java/fiji/plugin/trackmate/util/TMUtils.java index 015bc7d43..0a9547004 100644 --- a/src/main/java/fiji/plugin/trackmate/util/TMUtils.java +++ b/src/main/java/fiji/plugin/trackmate/util/TMUtils.java @@ -56,7 +56,6 @@ import net.imglib2.img.ImagePlusAdapter; import net.imglib2.img.display.imagej.ImgPlusViews; import net.imglib2.type.Type; -import net.imglib2.util.Cast; import net.imglib2.util.Util; /** @@ -137,9 +136,10 @@ else if ( obj instanceof Logger ) * Wraps an IJ {@link ImagePlus} in an imglib2 {@link ImgPlus}, abinding to * a returned type. */ + @SuppressWarnings( "unchecked" ) public static final < T > ImgPlus< T > rawWraps( final ImagePlus imp ) { - return Cast.unchecked( ImagePlusAdapter.wrapImgPlus( imp ) ); + return ( ImgPlus< T > ) ImagePlusAdapter.wrapImgPlus( imp ); } /** diff --git a/src/main/java/fiji/plugin/trackmate/util/mesh/EllipsoidFitter.java b/src/main/java/fiji/plugin/trackmate/util/mesh/EllipsoidFitter.java deleted file mode 100644 index 6f0deb46d..000000000 --- a/src/main/java/fiji/plugin/trackmate/util/mesh/EllipsoidFitter.java +++ /dev/null @@ -1,310 +0,0 @@ -package fiji.plugin.trackmate.util.mesh; - -import org.apache.commons.math3.linear.Array2DRowRealMatrix; -import org.apache.commons.math3.linear.ArrayRealVector; -import org.apache.commons.math3.linear.DecompositionSolver; -import org.apache.commons.math3.linear.EigenDecomposition; -import org.apache.commons.math3.linear.MatrixUtils; -import org.apache.commons.math3.linear.RealMatrix; -import org.apache.commons.math3.linear.RealVector; -import org.apache.commons.math3.linear.SingularValueDecomposition; - -import net.imagej.mesh.Mesh; -import net.imagej.ops.geom.geom3d.DefaultConvexHull3D; -import net.imglib2.RealLocalizable; -import net.imglib2.RealPoint; -import net.imglib2.util.Util; - -/** - * Fit an ellipsoid to the convex-Hull of a 3D mesh. - *

- * Adapted from Yury Petrov's Ellipsoid - * Fit MATLAB function and KalebKE ellipsoidfit. - * - * @author Jean-Yves Tinevez - */ -public class EllipsoidFitter -{ - - /** - * The results of fitting an ellpsoid to a mesh or a collection of points. - */ - public static final class EllipsoidFit - { - /** The ellipsoid center. */ - public final RealLocalizable center; - - /** The eigenvector of the smallest axis of the ellipsoid. */ - public final RealLocalizable ev1; - - /** The eigenvector of the middle axis of the ellipsoid. */ - public final RealLocalizable ev2; - - /** The eigenvector of the largest axis of the ellipsoid. */ - public final RealLocalizable ev3; - - /** The radius of the smallest axis of the ellipsoid. */ - public final double r1; - - /** The radius of the middle axis of the ellipsoid. */ - public final double r2; - - /** The radius of the largest axis of the ellipsoid. */ - public final double r3; - - private EllipsoidFit( final RealLocalizable center, - final RealLocalizable ev1, - final RealLocalizable ev2, - final RealLocalizable ev3, - final double r1, - final double r2, - final double r3 ) - { - this.center = center; - this.ev1 = ev1; - this.ev2 = ev2; - this.ev3 = ev3; - this.r1 = r1; - this.r2 = r2; - this.r3 = r3; - } - - @Override - public String toString() - { - final StringBuilder str = new StringBuilder( super.toString() ); - str.append( "\n - center: " + Util.printCoordinates( center ) ); - str.append( String.format( "\n - axis 1: radius = %.2f, vector = %s", r1, ev1 ) ); - str.append( String.format( "\n - axis 2: radius = %.2f, vector = %s", r2, ev2 ) ); - str.append( String.format( "\n - axis 3: radius = %.2f, vector = %s", r3, ev3 ) ); - return str.toString(); - } - } - - private static final DefaultConvexHull3D cHull = new DefaultConvexHull3D(); - - /** - * Fit an ellipsoid to the convex-Hull of a 3D mesh. - * - * @param mesh - * the mesh to fit. - * @return the fit results. - */ - public static final EllipsoidFit fit( final Mesh mesh ) - { - final Mesh ch = cHull.calculate( mesh ); - return fitOnConvexHull( ch ); - } - - /** - * Fit an ellipsoid to a 3D mesh, assuming it is the convex-Hull. - * - * @param mesh - * the convex-Hull of the mesh to fit. - * @return the fit results. - */ - public static final EllipsoidFit fitOnConvexHull( final Mesh mesh ) - { - return fit( mesh.vertices(), ( int ) mesh.vertices().size() ); - } - - /** - * Fit an ellipsoid to a collection of 3D points. - * - * @param points - * an iterable over the points to fit. - * @param nPoints - * the number of points to include in the fit. The fit will - * consider at most the first nPoints of the iterable, or all the - * points in the iterable, whatever comes first. - * @return the fit results. - */ - public static EllipsoidFit fit( final Iterable< ? extends RealLocalizable > points, final int nPoints ) - { - final RealVector V = solve( points, nPoints ); - - // To algebraix form. - final RealMatrix A = toAlgebraicForm( V ); - - // Find the center of the ellipsoid. - final RealVector C = findCenter( A ); - - // Translate the algebraic form of the ellipsoid to the center. - final RealMatrix R = translateToCenter( C, A ); - - // Ellipsoid eigenvectors and eigenvalues. - final EllipsoidFit fit = getFit( R, C ); - return fit; - } - - private static EllipsoidFit getFit( final RealMatrix R, final RealVector C ) - { - final RealMatrix subr = R.getSubMatrix( 0, 2, 0, 2 ); - - // subr[i][j] = subr[i][j] / -r[3][3]). - final double divr = -R.getEntry( 3, 3 ); - for ( int i = 0; i < subr.getRowDimension(); i++ ) - for ( int j = 0; j < subr.getRowDimension(); j++ ) - subr.setEntry( i, j, subr.getEntry( i, j ) / divr ); - - // Get the eigenvalues and eigenvectors. - final EigenDecomposition ed = new EigenDecomposition( subr ); - final double[] eigenvalues = ed.getRealEigenvalues(); - final RealVector e1 = ed.getEigenvector( 0 ); - final RealVector e2 = ed.getEigenvector( 1 ); - final RealVector e3 = ed.getEigenvector( 2 ); - - // Semi-axis length (radius). - final RealVector SAL = new ArrayRealVector( eigenvalues.length ); - for ( int i = 0; i < eigenvalues.length; i++ ) - SAL.setEntry( i, Math.sqrt( 1. / eigenvalues[ i ] ) ); - - // Put everything in a fit object. - final RealPoint center = new RealPoint( C.getEntry( 0 ), C.getEntry( 1 ), C.getEntry( 2 ) ); - final RealPoint ev1 = new RealPoint( e1.getEntry( 0 ), e1.getEntry( 1 ), e1.getEntry( 2 ) ); - final RealPoint ev2 = new RealPoint( e2.getEntry( 0 ), e2.getEntry( 1 ), e2.getEntry( 2 ) ); - final RealPoint ev3 = new RealPoint( e3.getEntry( 0 ), e3.getEntry( 1 ), e3.getEntry( 2 ) ); - return new EllipsoidFit( center, ev1, ev2, ev3, SAL.getEntry( 0 ), SAL.getEntry( 1 ), SAL.getEntry( 2 ) ); - } - - /** - * Translate the algebraic form of the ellipsoid to the center. - * - * @param C - * the center of the ellipsoid. - * @param A - * the ellipsoid matrix. - * @return the center translated form of the algebraic ellipsoid. - */ - private static final RealMatrix translateToCenter( final RealVector C, final RealMatrix A ) - { - final RealMatrix T = MatrixUtils.createRealIdentityMatrix( 4 ); - final RealMatrix centerMatrix = new Array2DRowRealMatrix( 1, 3 ); - centerMatrix.setRowVector( 0, C ); - T.setSubMatrix( centerMatrix.getData(), 3, 0 ); - final RealMatrix R = T.multiply( A ).multiply( T.transpose() ); - return R; - } - - /** - * Find the center of the ellipsoid. - * - * @param a - * the algebraic from of the polynomial. - * @return a vector containing the center of the ellipsoid. - */ - private static final RealVector findCenter( final RealMatrix A ) - { - final RealMatrix subA = A.getSubMatrix( 0, 2, 0, 2 ); - - for ( int q = 0; q < subA.getRowDimension(); q++ ) - for ( int s = 0; s < subA.getColumnDimension(); s++ ) - subA.multiplyEntry( q, s, -1.0 ); - - final RealVector subV = A.getRowVector( 3 ).getSubVector( 0, 3 ); - - final DecompositionSolver solver = new SingularValueDecomposition( subA ).getSolver(); - final RealMatrix subAi = solver.getInverse(); - return subAi.operate( subV ); - } - - /** - * Solve for Ax^2 + By^2 + Cz^2 + 2Dxy + 2Exz + 2Fyz + 2Gx + 2Hy + - * 2Iz = 1. - * - * @param points - * an iterable over 3D points. - * @param nPoints - * the number of points in the iterable. - * @return - */ - private static final RealVector solve( final Iterable< ? extends RealLocalizable > points, final int nPoints ) - { - final RealMatrix M0 = new Array2DRowRealMatrix( nPoints, 9 ); - int i = 0; - for ( final RealLocalizable point : points ) - { - final double x = point.getDoublePosition( 0 ); - final double y = point.getDoublePosition( 1 ); - final double z = point.getDoublePosition( 2 ); - - final double xx = x * x; - final double yy = y * y; - final double zz = z * z; - - final double xy = 2. * x * y; - final double xz = 2. * x * z; - final double yz = 2. * y * z; - - M0.setEntry( i, 0, xx ); - M0.setEntry( i, 1, yy ); - M0.setEntry( i, 2, zz ); - M0.setEntry( i, 3, xy ); - M0.setEntry( i, 4, xz ); - M0.setEntry( i, 5, yz ); - M0.setEntry( i, 6, 2. * x ); - M0.setEntry( i, 7, 2. * y ); - M0.setEntry( i, 8, 2. * z ); - - i++; - if ( i >= nPoints ) - break; - } - final RealMatrix M; - if ( i == nPoints ) - M = M0; - else - M = M0.getSubMatrix( 0, i, 0, 9 ); - - final RealMatrix M2 = M.transpose().multiply( M ); - - final RealVector O = new ArrayRealVector( nPoints ); - O.mapAddToSelf( 1 ); - - final RealVector MO = M.transpose().operate( O ); - - final DecompositionSolver solver = new SingularValueDecomposition( M2 ).getSolver(); - final RealMatrix I = solver.getInverse(); - - final RealVector V = I.operate( MO ); - return V; - } - - /** - * Reshape the fit result vector in the shape of an algebraic matrix. - * - *

-	 * A = 		[ Ax2 	2Dxy 	2Exz 	2Gx ] 
-	 * 		[ 2Dxy 	By2 	2Fyz 	2Hy ] 
-	 * 		[ 2Exz 	2Fyz 	Cz2 	2Iz ] 
-	 * 		[ 2Gx 	2Hy 	2Iz 	-1 ] ]
-	 * 
-	 * 
-	 * @param V the fit result.
-	 * @return a new 4x4 real matrix.
-	 */
-	private static final RealMatrix toAlgebraicForm( final RealVector V )
-	{
-		final RealMatrix A = new Array2DRowRealMatrix( 4, 4 );
-
-		A.setEntry( 0, 0, V.getEntry( 0 ) );
-		A.setEntry( 0, 1, V.getEntry( 3 ) );
-		A.setEntry( 0, 2, V.getEntry( 4 ) );
-		A.setEntry( 0, 3, V.getEntry( 6 ) );
-		A.setEntry( 1, 0, V.getEntry( 3 ) );
-		A.setEntry( 1, 1, V.getEntry( 1 ) );
-		A.setEntry( 1, 2, V.getEntry( 5 ) );
-		A.setEntry( 1, 3, V.getEntry( 7 ) );
-		A.setEntry( 2, 0, V.getEntry( 4 ) );
-		A.setEntry( 2, 1, V.getEntry( 5 ) );
-		A.setEntry( 2, 2, V.getEntry( 2 ) );
-		A.setEntry( 2, 3, V.getEntry( 8 ) );
-		A.setEntry( 3, 0, V.getEntry( 6 ) );
-		A.setEntry( 3, 1, V.getEntry( 7 ) );
-		A.setEntry( 3, 2, V.getEntry( 8 ) );
-		A.setEntry( 3, 3, -1 );
-		return A;
-	}
-}
diff --git a/src/main/java/fiji/plugin/trackmate/util/mesh/SpotMeshCursor.java b/src/main/java/fiji/plugin/trackmate/util/mesh/SpotMeshCursor.java
index 1dd49e9d5..3f384f969 100644
--- a/src/main/java/fiji/plugin/trackmate/util/mesh/SpotMeshCursor.java
+++ b/src/main/java/fiji/plugin/trackmate/util/mesh/SpotMeshCursor.java
@@ -2,10 +2,9 @@
 
 import fiji.plugin.trackmate.SpotMesh;
 import gnu.trove.list.array.TDoubleArrayList;
-import net.imagej.mesh.alg.zslicer.Slice;
 import net.imglib2.Cursor;
 import net.imglib2.RandomAccess;
-import net.imglib2.Sampler;
+import net.imglib2.mesh.alg.zslicer.Slice;
 
 /**
  * A {@link Cursor} that iterates over the pixels inside a mesh.
@@ -197,13 +196,13 @@ public long getLongPosition( final int d )
 	public Cursor< T > copyCursor()
 	{
 		return new SpotMeshCursor<>(
-				ra.copyRandomAccess(),
+				ra.copy(),
 				sm.copy(),
 				cal.clone() );
 	}
 
 	@Override
-	public Sampler< T > copy()
+	public Cursor< T > copy()
 	{
 		return copyCursor();
 	}
diff --git a/src/main/java/fiji/plugin/trackmate/visualization/bvv/BVVUtils.java b/src/main/java/fiji/plugin/trackmate/visualization/bvv/BVVUtils.java
new file mode 100644
index 000000000..d3ff277bd
--- /dev/null
+++ b/src/main/java/fiji/plugin/trackmate/visualization/bvv/BVVUtils.java
@@ -0,0 +1,122 @@
+package fiji.plugin.trackmate.visualization.bvv;
+
+import bvv.vistools.Bvv;
+import bvv.vistools.BvvFunctions;
+import bvv.vistools.BvvHandle;
+import bvv.vistools.BvvSource;
+import fiji.plugin.trackmate.Spot;
+import fiji.plugin.trackmate.SpotMesh;
+import fiji.plugin.trackmate.util.TMUtils;
+import ij.CompositeImage;
+import ij.ImagePlus;
+import ij.process.ImageProcessor;
+import ij.process.LUT;
+import net.imagej.ImgPlus;
+import net.imagej.axis.Axes;
+import net.imglib2.img.display.imagej.ImgPlusViews;
+import net.imglib2.mesh.Meshes;
+import net.imglib2.mesh.obj.Mesh;
+import net.imglib2.mesh.obj.nio.BufferMesh;
+import net.imglib2.mesh.obj.transform.TranslateMesh;
+import net.imglib2.type.Type;
+import net.imglib2.type.numeric.ARGBType;
+
+public class BVVUtils
+{
+
+	public static final StupidMesh createMesh( final Spot spot )
+	{
+		if ( spot instanceof SpotMesh )
+		{
+			final SpotMesh sm = ( SpotMesh ) spot;
+			final Mesh mesh = TranslateMesh.translate( sm.getMesh(), spot );
+			final BufferMesh bm = new BufferMesh( ( int ) mesh.vertices().size(), ( int ) mesh.triangles().size() );
+			Meshes.copy( mesh, bm );
+			return new StupidMesh( bm );
+		}
+		return new StupidMesh( Icosahedron.sphere( spot ) );
+	}
+
+	public static final < T extends Type< T > > BvvHandle createViewer( final ImagePlus imp )
+	{
+		final double[] cal = TMUtils.getSpatialCalibration( imp );
+
+		// Convert and split by channels.
+		final ImgPlus< T > img = TMUtils.rawWraps( imp );
+		final int cAxis = img.dimensionIndex( Axes.CHANNEL );
+		final BvvHandle bvvHandle;
+		if ( cAxis < 0 )
+		{
+			final BvvSource source = BvvFunctions.show( img, imp.getShortTitle(),
+					Bvv.options()
+							.maxAllowedStepInVoxels( 0 )
+							.renderWidth( 1024 )
+							.renderHeight( 1024 )
+							.preferredSize( 512, 512 )
+							.frameTitle( "3D view " + imp.getShortTitle() )
+							.sourceTransform( cal ) );
+			source.setDisplayRange( imp.getDisplayRangeMin(), imp.getDisplayRangeMax() );
+			if ( imp.getLuts().length > 0 )
+			{
+				final LUT lut = imp.getLuts()[ 0 ];
+				final int rgb = lut.getColorModel().getRGB( ( int ) imp.getDisplayRangeMax() );
+				source.setColor( new ARGBType( rgb ) );
+			}
+			bvvHandle = source.getBvvHandle();
+		}
+		else
+		{
+			BvvHandle h = null;
+			final long nChannels = img.dimension( cAxis );
+			final String st = imp.getShortTitle();
+			for ( int c = 0; c < nChannels; c++ )
+			{
+				final ImgPlus< T > channel = ImgPlusViews.hyperSlice( img, cAxis, c );
+				final BvvSource source;
+				if ( h == null )
+				{
+					source = BvvFunctions.show( channel, st + "_c" + ( c + 1 ),
+							Bvv.options()
+									.maxAllowedStepInVoxels( 0 )
+									.renderWidth( 1024 )
+									.renderHeight( 1024 )
+									.preferredSize( 512, 512 )
+									.frameTitle( "3D view " + imp.getShortTitle() )
+									.sourceTransform( cal ) );
+					h = source.getBvvHandle();
+				}
+				else
+				{
+					source = BvvFunctions.show( channel, st + "_c" + ( c + 1 ),
+							Bvv.options()
+									.maxAllowedStepInVoxels( 0 )
+									.renderWidth( 1024 )
+									.renderHeight( 1024 )
+									.preferredSize( 512, 512 )
+									.sourceTransform( cal )
+									.addTo( h ) );
+
+				}
+				final int i = imp.getStackIndex( c + 1, 1, 1 );
+				if ( imp instanceof CompositeImage )
+				{
+					final CompositeImage cp = ( CompositeImage ) imp;
+					source.setDisplayRange( cp.getChannelLut( c + 1 ).min, cp.getChannelLut( c + 1 ).max );
+				}
+				else
+				{
+					final ImageProcessor ip = imp.getStack().getProcessor( i );
+					source.setDisplayRange( ip.getMin(), ip.getMax() );
+				}
+				if ( imp.getLuts().length > 0 )
+				{
+					final LUT lut = imp.getLuts()[ c ];
+					final int rgb = lut.getColorModel().getRGB( ( int ) imp.getDisplayRangeMax() );
+					source.setColor( new ARGBType( rgb ) );
+				}
+			}
+			bvvHandle = h;
+		}
+		return bvvHandle;
+	}
+}
diff --git a/src/main/java/fiji/plugin/trackmate/visualization/bvv/Icosahedron.java b/src/main/java/fiji/plugin/trackmate/visualization/bvv/Icosahedron.java
new file mode 100644
index 000000000..d77d407d1
--- /dev/null
+++ b/src/main/java/fiji/plugin/trackmate/visualization/bvv/Icosahedron.java
@@ -0,0 +1,154 @@
+package fiji.plugin.trackmate.visualization.bvv;
+
+import fiji.plugin.trackmate.Spot;
+import net.imglib2.RealLocalizable;
+import net.imglib2.mesh.Meshes;
+import net.imglib2.mesh.obj.Mesh;
+import net.imglib2.mesh.obj.Triangle;
+import net.imglib2.mesh.obj.naive.NaiveDoubleMesh;
+import net.imglib2.mesh.obj.nio.BufferMesh;
+
+/**
+ * Icosahedron spheres.
+ * 

+ * Based on https://github.com/caosdoar/spheres + * + * @author Jean-Yves Tinevez + */ +public class Icosahedron +{ + + public static final Mesh core() + { + final NaiveDoubleMesh mesh = new NaiveDoubleMesh(); + + // Vertices + final double t = ( 1. + Math.sqrt( 5. ) ) / 2.; + final double[][] vs = new double[][] { + { -1.0, t, 0.0 }, + { 1.0, t, 0.0 }, + { -1.0, -t, 0.0 }, + { 1.0, -t, 0.0 }, + { 0.0, -1.0, t }, + { 0.0, 1.0, t }, + { 0.0, -1.0, -t }, + { 0.0, 1.0, -t }, + { t, 0.0, -1.0 }, + { t, 0.0, 1.0 }, + { -t, 0.0, -1.0 }, + { -t, 0.0, 1.0 } + }; + final double[] tmp = new double[ 3 ]; + for ( final double[] v : vs ) + { + normalize( v, tmp ); + mesh.vertices().add( tmp[ 0 ], tmp[ 1 ], tmp[ 2 ] ); + } + + // Faces + mesh.triangles().add( 0, 11, 5 ); + mesh.triangles().add( 0, 5, 1 ); + mesh.triangles().add( 0, 1, 7 ); + mesh.triangles().add( 0, 7, 10 ); + mesh.triangles().add( 0, 10, 11 ); + mesh.triangles().add( 1, 5, 9 ); + mesh.triangles().add( 5, 11, 4 ); + mesh.triangles().add( 11, 10, 2 ); + mesh.triangles().add( 10, 7, 6 ); + mesh.triangles().add( 7, 1, 8 ); + mesh.triangles().add( 3, 9, 4 ); + mesh.triangles().add( 3, 4, 2 ); + mesh.triangles().add( 3, 2, 6 ); + mesh.triangles().add( 3, 6, 8 ); + mesh.triangles().add( 3, 8, 9 ); + mesh.triangles().add( 4, 9, 5 ); + mesh.triangles().add( 2, 4, 11 ); + mesh.triangles().add( 6, 2, 10 ); + mesh.triangles().add( 8, 6, 7 ); + mesh.triangles().add( 9, 8, 1 ); + return mesh; + } + + public static final BufferMesh refine( final Mesh core ) + { + final int nVerticesOut = ( int ) ( 6 * core.triangles().size() ); + final int nTrianglesOut = ( int ) ( 4 * core.triangles().size() ); + final BufferMesh out = new BufferMesh( nVerticesOut, nTrianglesOut ); + + final double[] tmpIn = new double[ 3 ]; + final double[] tmpOut = new double[ 3 ]; + for ( final Triangle t : core.triangles() ) + { + final long v0 = out.vertices().add( t.v0x(), t.v0y(), t.v0z() ); + final long v1 = out.vertices().add( t.v1x(), t.v1y(), t.v1z() ); + final long v2 = out.vertices().add( t.v2x(), t.v2y(), t.v2z() ); + + tmpIn[ 0 ] = 0.5 * ( t.v0xf() + t.v1xf() ); + tmpIn[ 1 ] = 0.5 * ( t.v0yf() + t.v1yf() ); + tmpIn[ 2 ] = 0.5 * ( t.v0zf() + t.v1zf() ); + normalize( tmpIn, tmpOut ); + final long v3 = out.vertices().add( tmpOut[ 0 ], tmpOut[ 1 ], tmpOut[ 2 ] ); + + tmpIn[ 0 ] = 0.5 * ( t.v2xf() + t.v1xf() ); + tmpIn[ 1 ] = 0.5 * ( t.v2yf() + t.v1yf() ); + tmpIn[ 2 ] = 0.5 * ( t.v2zf() + t.v1zf() ); + normalize( tmpIn, tmpOut ); + final long v4 = out.vertices().add( tmpOut[ 0 ], tmpOut[ 1 ], tmpOut[ 2 ] ); + + tmpIn[ 0 ] = 0.5 * ( t.v0xf() + t.v2xf() ); + tmpIn[ 1 ] = 0.5 * ( t.v0yf() + t.v2yf() ); + tmpIn[ 2 ] = 0.5 * ( t.v0zf() + t.v2zf() ); + normalize( tmpIn, tmpOut ); + final long v5 = out.vertices().add( tmpOut[ 0 ], tmpOut[ 1 ], tmpOut[ 2 ] ); + + out.triangles().add( v0, v3, v5 ); + out.triangles().add( v3, v1, v4 ); + out.triangles().add( v5, v4, v2 ); + out.triangles().add( v3, v4, v5 ); + } + + return out; + } + + public static BufferMesh sphere( final Spot spot ) + { + return sphere( spot, spot.getFeature( Spot.RADIUS ) ); + } + + public static BufferMesh sphere( final RealLocalizable center, final double radius ) + { + return sphere( center, radius, 3 ); + } + + public static BufferMesh sphere( final RealLocalizable center, final double radius, final int nSubdivisions ) + { + Mesh mesh = core(); + for ( int i = 0; i < nSubdivisions; i++ ) + mesh = refine( mesh ); + + scale( mesh, center, radius ); + final BufferMesh out = new BufferMesh( ( int ) mesh.vertices().size(), ( int ) mesh.triangles().size() ); + Meshes.calculateNormals( mesh, out ); + return out; + } + + private static void scale( final Mesh mesh, final RealLocalizable center, final double radius ) + { + final long nV = mesh.vertices().size(); + for ( int i = 0; i < nV; i++ ) + { + final double x = mesh.vertices().x( i ) * radius + center.getDoublePosition( 0 ); + final double y = mesh.vertices().y( i ) * radius + center.getDoublePosition( 1 ); + final double z = mesh.vertices().z( i ) * radius + center.getDoublePosition( 2 ); + mesh.vertices().set( i, x, y, z ); + } + } + + private static void normalize( final double[] v, final double[] tmp ) + { + final double l = Math.sqrt( v[ 0 ] * v[ 0 ] + v[ 1 ] * v[ 1 ] + v[ 2 ] * v[ 2 ] ); + tmp[ 0 ] = v[ 0 ] / l; + tmp[ 1 ] = v[ 1 ] / l; + tmp[ 2 ] = v[ 2 ] / l; + } +} diff --git a/src/main/java/fiji/plugin/trackmate/visualization/bvv/StupidMesh.java b/src/main/java/fiji/plugin/trackmate/visualization/bvv/StupidMesh.java new file mode 100644 index 000000000..46da5742e --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/visualization/bvv/StupidMesh.java @@ -0,0 +1,155 @@ +/*- + * #%L + * Volume rendering of bdv datasets + * %% + * Copyright (C) 2018 - 2021 Tobias Pietzsch + * %% + * 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 fiji.plugin.trackmate.visualization.bvv; + +import static com.jogamp.opengl.GL.GL_FLOAT; +import static com.jogamp.opengl.GL.GL_TRIANGLES; +import static com.jogamp.opengl.GL.GL_UNSIGNED_INT; + +import java.awt.Color; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; + +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL3; + +import bvv.core.backend.jogl.JoglGpuContext; +import bvv.core.shadergen.DefaultShader; +import bvv.core.shadergen.Shader; +import bvv.core.shadergen.generate.Segment; +import bvv.core.shadergen.generate.SegmentTemplate; +import fiji.plugin.trackmate.gui.displaysettings.DisplaySettings; +import net.imglib2.mesh.obj.nio.BufferMesh; + +public class StupidMesh +{ + private final Shader prog; + + private final BufferMesh mesh; + + private boolean initialized; + + private int vao; + + private final float[] carr = new float[ 4 ]; + + private final float[] scarr = new float[ 4 ]; + + public StupidMesh( final BufferMesh mesh ) + { + this.mesh = mesh; + final Segment meshVp = new SegmentTemplate( StupidMesh.class, "mesh.vp" ).instantiate(); + final Segment meshFp = new SegmentTemplate( StupidMesh.class, "mesh.fp" ).instantiate(); + prog = new DefaultShader( meshVp.getCode(), meshFp.getCode() ); + DisplaySettings.defaultStyle().getSpotUniformColor().getColorComponents( carr ); + DisplaySettings.defaultStyle().getHighlightColor().getColorComponents( scarr ); + } + + private void init( final GL3 gl ) + { + initialized = true; + + final int[] tmp = new int[ 3 ]; + gl.glGenBuffers( 3, tmp, 0 ); + final int meshPosVbo = tmp[ 0 ]; + final int meshNormalVbo = tmp[ 1 ]; + final int meshEbo = tmp[ 2 ]; + + final FloatBuffer vertices = mesh.vertices().verts(); + vertices.rewind(); + gl.glBindBuffer( GL.GL_ARRAY_BUFFER, meshPosVbo ); + gl.glBufferData( GL.GL_ARRAY_BUFFER, vertices.limit() * Float.BYTES, vertices, GL.GL_STATIC_DRAW ); + gl.glBindBuffer( GL.GL_ARRAY_BUFFER, 0 ); + + final FloatBuffer normals = mesh.vertices().normals(); + normals.rewind(); + gl.glBindBuffer( GL.GL_ARRAY_BUFFER, meshNormalVbo ); + gl.glBufferData( GL.GL_ARRAY_BUFFER, normals.limit() * Float.BYTES, normals, GL.GL_STATIC_DRAW ); + gl.glBindBuffer( GL.GL_ARRAY_BUFFER, 0 ); + + final IntBuffer indices = mesh.triangles().indices(); + indices.rewind(); + gl.glBindBuffer( GL.GL_ELEMENT_ARRAY_BUFFER, meshEbo ); + gl.glBufferData( GL.GL_ELEMENT_ARRAY_BUFFER, indices.limit() * Integer.BYTES, indices, GL.GL_STATIC_DRAW ); + gl.glBindBuffer( GL.GL_ELEMENT_ARRAY_BUFFER, 0 ); + + gl.glGenVertexArrays( 1, tmp, 0 ); + vao = tmp[ 0 ]; + gl.glBindVertexArray( vao ); + gl.glBindBuffer( GL.GL_ARRAY_BUFFER, meshPosVbo ); + gl.glVertexAttribPointer( 0, 3, GL_FLOAT, false, 3 * Float.BYTES, 0 ); + gl.glEnableVertexAttribArray( 0 ); + gl.glBindBuffer( GL.GL_ARRAY_BUFFER, meshNormalVbo ); + gl.glVertexAttribPointer( 1, 3, GL_FLOAT, false, 3 * Float.BYTES, 0 ); + gl.glEnableVertexAttribArray( 1 ); + gl.glBindBuffer( GL.GL_ELEMENT_ARRAY_BUFFER, meshEbo ); + gl.glBindVertexArray( 0 ); + } + + public void setColor( final Color color, final float alpha ) + { + color.getComponents( carr ); + carr[ 3 ] = alpha; + } + + public void setSelectionColor( final Color selectionColor, final float alpha ) + { + selectionColor.getComponents( scarr ); + scarr[ 3 ] = alpha; + } + + public void draw( final GL3 gl, final Matrix4fc pvm, final Matrix4fc vm, final boolean isSelected ) + { + if ( !initialized ) + init( gl ); + + final JoglGpuContext context = JoglGpuContext.get( gl ); + final Matrix4f itvm = vm.invert( new Matrix4f() ).transpose(); + + prog.getUniformMatrix4f( "pvm" ).set( pvm ); + prog.getUniformMatrix4f( "vm" ).set( vm ); + prog.getUniformMatrix3f( "itvm" ).set( itvm.get3x3( new Matrix3f() ) ); + prog.getUniform4f( "ObjectColor" ).set( carr[ 0 ], carr[ 1 ], carr[ 2 ], carr[ 3 ] ); + prog.getUniform1f( "IsSelected" ).set( isSelected ? 1f : 0f ); + prog.getUniform4f( "SelectionColor" ).set( scarr[ 0 ], scarr[ 1 ], scarr[ 2 ], scarr[ 3 ] ); + prog.setUniforms( context ); + prog.use( context ); + + gl.glBindVertexArray( vao ); + gl.glEnable( GL.GL_CULL_FACE ); + gl.glCullFace( GL.GL_BACK ); + gl.glFrontFace( GL.GL_CCW ); + gl.glDrawElements( GL_TRIANGLES, ( int ) mesh.triangles().size() * 3, GL_UNSIGNED_INT, 0 ); + gl.glBindVertexArray( 0 ); + } +} diff --git a/src/main/java/fiji/plugin/trackmate/visualization/bvv/TrackMateBVV.java b/src/main/java/fiji/plugin/trackmate/visualization/bvv/TrackMateBVV.java new file mode 100644 index 000000000..a7c024b5b --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/visualization/bvv/TrackMateBVV.java @@ -0,0 +1,189 @@ +package fiji.plugin.trackmate.visualization.bvv; + +import java.awt.Color; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.joml.Matrix4f; + +import bdv.viewer.animate.TranslationAnimator; +import bvv.core.VolumeViewerPanel; +import bvv.core.util.MatrixMath; +import bvv.vistools.BvvHandle; +import fiji.plugin.trackmate.Model; +import fiji.plugin.trackmate.ModelChangeEvent; +import fiji.plugin.trackmate.SelectionModel; +import fiji.plugin.trackmate.Spot; +import fiji.plugin.trackmate.features.FeatureUtils; +import fiji.plugin.trackmate.gui.displaysettings.DisplaySettings; +import fiji.plugin.trackmate.visualization.AbstractTrackMateModelView; +import fiji.plugin.trackmate.visualization.FeatureColorGenerator; +import ij.ImagePlus; +import net.imglib2.RealLocalizable; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.type.Type; + +public class TrackMateBVV< T extends Type< T > > extends AbstractTrackMateModelView +{ + + private static final String KEY = "BIGVOLUMEVIEWER"; + + private final ImagePlus imp; + + private BvvHandle handle; + + private final Map< Spot, StupidMesh > meshMap; + + public TrackMateBVV( final Model model, final SelectionModel selectionModel, final ImagePlus imp, final DisplaySettings displaySettings ) + { + super( model, selectionModel, displaySettings ); + this.imp = imp; + this.meshMap = new HashMap<>(); + final Iterable< Spot > it = model.getSpots().iterable( true ); + it.forEach( s -> meshMap.computeIfAbsent( s, BVVUtils::createMesh ) ); + updateColor(); + displaySettings.listeners().add( this::updateColor ); + selectionModel.addSelectionChangeListener( e -> refresh() ); + } + + /** + * Returns the {@link BvvHandle} that contains this view. Returns + * null if this view has not been rendered yet. + * + * @return the BVV handle, or null. + */ + public BvvHandle getBvvHandle() + { + return handle; + } + + @Override + public void render() + { + this.handle = BVVUtils.createViewer( imp ); + final VolumeViewerPanel viewer = handle.getViewerPanel(); + viewer.setRenderScene( ( gl, data ) -> { + if ( displaySettings.isSpotVisible() ) + { + final Matrix4f pvm = new Matrix4f( data.getPv() ); + final Matrix4f view = MatrixMath.affine( data.getRenderTransformWorldToScreen(), new Matrix4f() ); + final Matrix4f vm = MatrixMath.screen( data.getDCam(), data.getScreenWidth(), data.getScreenHeight(), new Matrix4f() ).mul( view ); + + final int t = data.getTimepoint(); + final Iterable< Spot > it = model.getSpots().iterable( t, true ); + it.forEach( s -> meshMap.computeIfAbsent( s, BVVUtils::createMesh ).draw( gl, pvm, vm, selectionModel.getSpotSelection().contains( s ) ) ); + } + } ); + } + + @Override + public void refresh() + { + if ( handle != null ) + handle.getViewerPanel().requestRepaint(); + } + + @Override + public void clear() + { + // TODO Auto-generated method stub + + } + + @Override + public void centerViewOn( final Spot spot ) + { + if ( handle == null ) + return; + + final VolumeViewerPanel panel = handle.getViewerPanel(); + panel.setTimepoint( spot.getFeature( Spot.FRAME ).intValue() ); + + final AffineTransform3D c = panel.state().getViewerTransform(); + final double[] translation = getTranslation( c, spot, panel.getWidth(), panel.getHeight() ); + if ( translation != null ) + { + final TranslationAnimator animator = new TranslationAnimator( c, translation, 300 ); + animator.setTime( System.currentTimeMillis() ); + panel.setTransformAnimator( animator ); + } + } + + /** + * Returns a translation vector that will put the specified position at the + * center of the panel when used with a TranslationAnimator. + * + * @param t + * the viewer panel current view transform. + * @param target + * the position to focus on. + * @param width + * the width of the panel. + * @param height + * the height of the panel. + * @return a new double[] array with 3 elements containing the + * translation to use. + */ + private static final double[] getTranslation( final AffineTransform3D t, final RealLocalizable target, final int width, final int height ) + { + final double[] pos = new double[ 3 ]; + final double[] vPos = new double[ 3 ]; + target.localize( pos ); + t.apply( pos, vPos ); + + final double dx = width / 2 - vPos[ 0 ] + t.get( 0, 3 ); + final double dy = height / 2 - vPos[ 1 ] + t.get( 1, 3 ); + final double dz = -vPos[ 2 ] + t.get( 2, 3 ); + + return new double[] { dx, dy, dz }; + } + + @Override + public String getKey() + { + return KEY; + } + + @Override + public void modelChanged( final ModelChangeEvent event ) + { + switch ( event.getEventID() ) + { + case ModelChangeEvent.SPOTS_FILTERED: + case ModelChangeEvent.SPOTS_COMPUTED: + case ModelChangeEvent.TRACKS_VISIBILITY_CHANGED: + case ModelChangeEvent.TRACKS_COMPUTED: + refresh(); + break; + case ModelChangeEvent.MODEL_MODIFIED: + { + for ( final Spot spot : event.getSpots() ) + { + final StupidMesh mesh = BVVUtils.createMesh( spot ); + meshMap.put( spot, mesh ); + } + updateColor(); + refresh(); + break; + } + } + } + + private void updateColor() + { + final FeatureColorGenerator< Spot > spotColorGenerator = FeatureUtils.createSpotColorGenerator( model, displaySettings ); + for ( final Entry< Spot, StupidMesh > entry : meshMap.entrySet() ) + { + final StupidMesh sm = entry.getValue(); + if ( sm == null ) + continue; + + final Color color = spotColorGenerator.color( entry.getKey() ); + final float alpha = ( float ) displaySettings.getSpotTransparencyAlpha(); + sm.setColor( color, alpha ); + sm.setSelectionColor( displaySettings.getHighlightColor(), alpha ); + } + refresh(); + } +} diff --git a/src/main/java/fiji/plugin/trackmate/visualization/bvv/mesh.fp b/src/main/java/fiji/plugin/trackmate/visualization/bvv/mesh.fp new file mode 100644 index 000000000..fb732ddd4 --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/visualization/bvv/mesh.fp @@ -0,0 +1,48 @@ +out vec4 fragColor; + +in vec3 Normal; +in vec3 FragPos; + +uniform vec4 ObjectColor; +uniform float IsSelected; +uniform vec4 SelectionColor; + +const vec3 lightColor1 = 0.5 * vec3(0.9, 0.9, 1); +const vec3 lightDir1 = normalize(vec3(0, -0.2, -1)); + +const vec3 lightColor2 = 0.5 * vec3(0.1, 0.1, 1); +const vec3 lightDir2 = normalize(vec3(1, 1, 0.5)); + +const vec3 ambient = vec3(0.7, 0.7, 0.7); + +const float specularStrength = 5; + +vec3 phong(vec3 norm, vec3 viewDir, vec3 lightDir, vec3 lightColor, float shininess, float specularStrength) +{ + float diff = max(dot(norm, lightDir), 0.0); + vec3 diffuse = diff * lightColor; + + vec3 reflectDir = reflect(-lightDir, norm); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess); + vec3 specular = specularStrength * spec * lightColor; + + return diffuse + specular; +} + +void main() +{ +// fragColor = vec4(ObjectColor, 1); + vec3 norm = normalize(Normal); + vec3 viewDir = normalize(-FragPos); + + vec3 l1 = phong( norm, viewDir, lightDir1, lightColor1, 32, 0.1 ); + vec3 l2 = phong( norm, viewDir, lightDir2, lightColor2, 32, 0.5 ); + + if (IsSelected > 0.5) { + fragColor = vec4( (ambient + l1 + l2), SelectionColor[3]) * SelectionColor; + } else { + float it = dot(norm, viewDir); + fragColor = vec4( it * (ambient + l1 + l2), 1) * ObjectColor + (1-it) * vec4(1,1,1,ObjectColor[3]); + } + +} diff --git a/src/main/java/fiji/plugin/trackmate/visualization/bvv/mesh.vp b/src/main/java/fiji/plugin/trackmate/visualization/bvv/mesh.vp new file mode 100644 index 000000000..e86810409 --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/visualization/bvv/mesh.vp @@ -0,0 +1,16 @@ +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; + +out vec3 FragPos; +out vec3 Normal; + +uniform mat4 pvm; +uniform mat4 vm; +uniform mat3 itvm; + +void main() +{ + gl_Position = pvm * vec4( aPos, 1.0 ); + FragPos = vec3(vm * vec4(aPos, 1.0)); + Normal = itvm * aNormal; +} diff --git a/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/ModelEditActions.java b/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/ModelEditActions.java index dfc83d4ed..fded6eef5 100644 --- a/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/ModelEditActions.java +++ b/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/ModelEditActions.java @@ -17,7 +17,7 @@ import fiji.plugin.trackmate.Model; import fiji.plugin.trackmate.SelectionModel; import fiji.plugin.trackmate.Spot; -import fiji.plugin.trackmate.SpotShape; +import fiji.plugin.trackmate.SpotBase; import fiji.plugin.trackmate.detection.semiauto.SemiAutoTracker; import fiji.plugin.trackmate.util.ModelTools; import fiji.plugin.trackmate.util.TMUtils; @@ -76,7 +76,7 @@ private Spot makeSpot( Point mouseLocation ) SwingUtilities.convertPointFromScreen( mouseLocation, canvas ); } final double[] calibration = TMUtils.getSpatialCalibration( imp ); - return new Spot( + return new SpotBase( ( -0.5 + canvas.offScreenXD( mouseLocation.x ) ) * calibration[ 0 ], ( -0.5 + canvas.offScreenYD( mouseLocation.y ) ) * calibration[ 1 ], ( imp.getSlice() - 1 ) * calibration[ 2 ], @@ -265,24 +265,14 @@ public void changeSpotRadius( final boolean increase, final boolean fast ) ? radius + factor * dx * COARSE_STEP : radius + factor * dx * FINE_STEP; - if ( newRadius <= dx ) return; // Store new value of radius for next spot creation. previousRadius = newRadius; - final SpotShape shape = target.getShape(); - if ( null == shape ) - { - target.putFeature( Spot.RADIUS, newRadius ); - } - else - { - final double alpha = newRadius / radius; - shape.scale( alpha ); - target.putFeature( Spot.RADIUS, shape.radius() ); - } + // Actually scale the spot. + target.scale( radius / newRadius ); model.beginUpdate(); try diff --git a/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/PaintSpotMesh.java b/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/PaintSpotMesh.java index af1cc0e7d..158260da4 100644 --- a/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/PaintSpotMesh.java +++ b/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/PaintSpotMesh.java @@ -11,9 +11,9 @@ import fiji.plugin.trackmate.SpotMesh; import fiji.plugin.trackmate.gui.displaysettings.DisplaySettings; import ij.ImagePlus; -import net.imagej.mesh.alg.zslicer.Contour; -import net.imagej.mesh.alg.zslicer.Slice; import net.imglib2.RealLocalizable; +import net.imglib2.mesh.alg.zslicer.Contour; +import net.imglib2.mesh.alg.zslicer.Slice; /** * Utility class to paint the {@link SpotMesh} component of spots. diff --git a/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/SpotEditTool.java b/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/SpotEditTool.java index 2b92ea64f..baedb0394 100644 --- a/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/SpotEditTool.java +++ b/src/main/java/fiji/plugin/trackmate/visualization/hyperstack/SpotEditTool.java @@ -403,7 +403,6 @@ public void keyPressed( final KeyEvent e ) break; } } - } @Override diff --git a/src/main/resources/fiji/plugin/trackmate/gui/images/TrackMateBVV-logo-16x16.png b/src/main/resources/fiji/plugin/trackmate/gui/images/TrackMateBVV-logo-16x16.png new file mode 100644 index 000000000..2e5b7d5fa Binary files /dev/null and b/src/main/resources/fiji/plugin/trackmate/gui/images/TrackMateBVV-logo-16x16.png differ diff --git a/src/test/java/fiji/plugin/trackmate/mesh/DebugZSlicer.java b/src/test/java/fiji/plugin/trackmate/mesh/DebugZSlicer.java index 94456b577..9d28c8e35 100644 --- a/src/test/java/fiji/plugin/trackmate/mesh/DebugZSlicer.java +++ b/src/test/java/fiji/plugin/trackmate/mesh/DebugZSlicer.java @@ -13,9 +13,9 @@ import ij.CompositeImage; import ij.ImageJ; import ij.ImagePlus; -import net.imagej.mesh.alg.zslicer.Contour; -import net.imagej.mesh.alg.zslicer.Slice; -import net.imagej.mesh.alg.zslicer.ZSlicer; +import net.imglib2.mesh.alg.zslicer.Contour; +import net.imglib2.mesh.alg.zslicer.Slice; +import net.imglib2.mesh.alg.zslicer.ZSlicer; public class DebugZSlicer { diff --git a/src/test/java/fiji/plugin/trackmate/mesh/DefaultMesh.java b/src/test/java/fiji/plugin/trackmate/mesh/DefaultMesh.java index 8ce097ad2..37e6f9961 100644 --- a/src/test/java/fiji/plugin/trackmate/mesh/DefaultMesh.java +++ b/src/test/java/fiji/plugin/trackmate/mesh/DefaultMesh.java @@ -16,8 +16,8 @@ import ij.measure.Calibration; import net.imagej.ImgPlus; import net.imagej.axis.Axes; -import net.imagej.mesh.Mesh; import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.mesh.obj.Mesh; import net.imglib2.type.logic.BitType; public class DefaultMesh diff --git a/src/test/java/fiji/plugin/trackmate/mesh/Demo3DMesh.java b/src/test/java/fiji/plugin/trackmate/mesh/Demo3DMesh.java index 84b9912f9..766d92e4b 100644 --- a/src/test/java/fiji/plugin/trackmate/mesh/Demo3DMesh.java +++ b/src/test/java/fiji/plugin/trackmate/mesh/Demo3DMesh.java @@ -14,21 +14,20 @@ import ij.gui.PolygonRoi; import net.imagej.ImgPlus; import net.imagej.axis.Axes; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.Meshes; -import net.imagej.mesh.Vertices; -import net.imagej.mesh.alg.zslicer.Contour; -import net.imagej.mesh.alg.zslicer.Slice; -import net.imagej.mesh.alg.zslicer.ZSlicer; -import net.imagej.mesh.io.ply.PLYMeshIO; -import net.imagej.mesh.io.stl.STLMeshIO; -import net.imagej.mesh.naive.NaiveDoubleMesh; -import net.imagej.mesh.naive.NaiveDoubleMesh.Triangles; import net.imglib2.RandomAccessibleInterval; import net.imglib2.converter.RealTypeConverters; import net.imglib2.img.ImgView; import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.img.display.imagej.ImgPlusViews; +import net.imglib2.mesh.Meshes; +import net.imglib2.mesh.alg.zslicer.Contour; +import net.imglib2.mesh.alg.zslicer.Slice; +import net.imglib2.mesh.alg.zslicer.ZSlicer; +import net.imglib2.mesh.io.ply.PLYMeshIO; +import net.imglib2.mesh.io.stl.STLMeshIO; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.Vertices; +import net.imglib2.mesh.obj.naive.NaiveDoubleMesh; import net.imglib2.roi.labeling.ImgLabeling; import net.imglib2.roi.labeling.LabelRegion; import net.imglib2.roi.labeling.LabelRegions; @@ -112,8 +111,8 @@ public static void main( final String[] args ) static Mesh debugMesh( final long[] min, final long[] max ) { final NaiveDoubleMesh mesh = new NaiveDoubleMesh(); - final net.imagej.mesh.naive.NaiveDoubleMesh.Vertices vertices = mesh.vertices(); - final Triangles triangles = mesh.triangles(); + final net.imglib2.mesh.obj.naive.NaiveDoubleMesh.Vertices vertices = mesh.vertices(); + final net.imglib2.mesh.obj.naive.NaiveDoubleMesh.Triangles triangles = mesh.triangles(); // Coords as X Y Z @@ -198,11 +197,10 @@ private static void testIO( final Mesh mesh, final int j ) // Serialize to disk. try { - new STLMeshIO().save( mesh, String.format( "samples/mesh/io/STL_%02d.stl", j ) ); + STLMeshIO.save( mesh, String.format( "samples/mesh/io/STL_%02d.stl", j ) ); - final PLYMeshIO plyio = new PLYMeshIO(); - plyio.save( mesh, String.format( "samples/mesh/io/PLY_%02d.ply", j ) ); - final byte[] bs = plyio.writeAscii( mesh ); + PLYMeshIO.save( mesh, String.format( "samples/mesh/io/PLY_%02d.ply", j ) ); + final byte[] bs = PLYMeshIO.writeAscii( mesh ); final String str = new String( bs ); try (final FileWriter writer = new FileWriter( String.format( "samples/mesh/io/PLYTEXT_%02d.txt", j ) )) diff --git a/src/test/java/fiji/plugin/trackmate/mesh/ExportMeshForDemo.java b/src/test/java/fiji/plugin/trackmate/mesh/ExportMeshForDemo.java index 44b560382..890b4a553 100644 --- a/src/test/java/fiji/plugin/trackmate/mesh/ExportMeshForDemo.java +++ b/src/test/java/fiji/plugin/trackmate/mesh/ExportMeshForDemo.java @@ -12,7 +12,7 @@ import fiji.plugin.trackmate.detection.ThresholdDetectorFactory; import ij.IJ; import ij.ImagePlus; -import net.imagej.mesh.io.stl.STLMeshIO; +import net.imglib2.mesh.io.stl.STLMeshIO; public class ExportMeshForDemo { @@ -42,7 +42,6 @@ public static void main( final String[] args ) if ( !file.isDirectory() ) file.delete(); - final STLMeshIO io = new STLMeshIO(); for ( final Spot spot : spots.iterable( true ) ) { final int t = spot.getFeature( Spot.FRAME ).intValue(); @@ -51,7 +50,7 @@ public static void main( final String[] args ) if ( spot instanceof SpotMesh ) { final SpotMesh mesh = ( SpotMesh ) spot; - io.save( mesh.getMesh(), savePath ); + STLMeshIO.save( mesh.getMesh(), savePath ); } } System.out.println( "Export done." ); diff --git a/src/test/java/fiji/plugin/trackmate/mesh/MeshPlayground.java b/src/test/java/fiji/plugin/trackmate/mesh/MeshPlayground.java new file mode 100644 index 000000000..0c2cb85e7 --- /dev/null +++ b/src/test/java/fiji/plugin/trackmate/mesh/MeshPlayground.java @@ -0,0 +1,114 @@ +package fiji.plugin.trackmate.mesh; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.joml.Matrix4f; +import org.scijava.ui.behaviour.io.InputTriggerConfig; +import org.scijava.ui.behaviour.util.Actions; + +import bvv.core.VolumeViewerPanel; +import bvv.core.util.MatrixMath; +import bvv.vistools.Bvv; +import bvv.vistools.BvvFunctions; +import bvv.vistools.BvvSource; +import fiji.plugin.trackmate.util.TMUtils; +import fiji.plugin.trackmate.visualization.bvv.StupidMesh; +import ij.IJ; +import ij.ImagePlus; +import net.imagej.ImgPlus; +import net.imagej.axis.Axes; +import net.imglib2.img.display.imagej.ImgPlusViews; +import net.imglib2.mesh.Meshes; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.naive.NaiveDoubleMesh; +import net.imglib2.mesh.obj.nio.BufferMesh; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.ARGBType; + +public class MeshPlayground +{ + public static < T extends Type< T > > void main( final String[] args ) + { + final String filePath = "samples/mesh/CElegansMask3D.tif"; + final ImagePlus imp = IJ.openImage( filePath ); + + final ImgPlus< T > img = TMUtils.rawWraps( imp ); + final ImgPlus< T > c1 = ImgPlusViews.hyperSlice( img, img.dimensionIndex( Axes.CHANNEL ), 1 ); + final ImgPlus< T > t1 = ImgPlusViews.hyperSlice( c1, c1.dimensionIndex( Axes.TIME ), 0 ); + final double[] cal = TMUtils.getSpatialCalibration( t1 ); + + final BvvSource source = BvvFunctions.show( c1, "t1", + Bvv.options() + .maxAllowedStepInVoxels( 0 ) + .renderWidth( 1024 ) + .renderHeight( 1024 ) + .preferredSize( 512, 512 ) + .sourceTransform( cal ) ); + + source.setDisplayRangeBounds( 0, 1024 ); + source.setColor( new ARGBType( 0xaaffaa ) ); + + + final List< StupidMesh > meshes = new ArrayList<>(); + for ( int j = 1; j <= 3; ++j ) + { + final String fn = String.format( "samples/mesh/CElegansMask3D_%02d.stl", j ); + meshes.add( new StupidMesh( load( fn ) ) ); + } + + final VolumeViewerPanel viewer = source.getBvvHandle().getViewerPanel(); + + final AtomicBoolean showMeshes = new AtomicBoolean( true ); + viewer.setRenderScene( ( gl, data ) -> { + if ( showMeshes.get() ) + { + final Matrix4f pvm = new Matrix4f( data.getPv() ); + final Matrix4f view = MatrixMath.affine( data.getRenderTransformWorldToScreen(), new Matrix4f() ); + final Matrix4f vm = MatrixMath.screen( data.getDCam(), data.getScreenWidth(), data.getScreenHeight(), new Matrix4f() ).mul( view ); + meshes.forEach( mesh -> mesh.draw( gl, pvm, vm, false ) ); + } + } ); + + final Actions actions = new Actions( new InputTriggerConfig() ); + actions.install( source.getBvvHandle().getKeybindings(), "my-new-actions" ); + actions.runnableAction( () -> { + showMeshes.set( !showMeshes.get() ); + viewer.requestRepaint(); + }, "toggle meshes", "G" ); + + viewer.requestRepaint(); + } + + + private static BufferMesh load( final String fn ) + { + BufferMesh mesh = null; + try + { + final NaiveDoubleMesh nmesh = new NaiveDoubleMesh(); + net.imglib2.mesh.io.stl.STLMeshIO.read( nmesh, new File( fn ) ); + mesh = calculateNormals( + nmesh +// Meshes.removeDuplicateVertices( nmesh, 5 ) + ); + } + catch ( final IOException e ) + { + e.printStackTrace(); + } + return mesh; + } + + private static BufferMesh calculateNormals( final Mesh mesh ) + { + final int nvertices = ( int ) mesh.vertices().size(); + final int ntriangles = ( int ) mesh.triangles().size(); + final BufferMesh bufferMesh = new BufferMesh( nvertices, ntriangles, true ); + Meshes.calculateNormals( mesh, bufferMesh ); + return bufferMesh; + } +} diff --git a/src/test/java/fiji/plugin/trackmate/mesh/TestEllipsoidFit.java b/src/test/java/fiji/plugin/trackmate/mesh/TestEllipsoidFit.java index c5fabdd62..f5172a51c 100644 --- a/src/test/java/fiji/plugin/trackmate/mesh/TestEllipsoidFit.java +++ b/src/test/java/fiji/plugin/trackmate/mesh/TestEllipsoidFit.java @@ -5,10 +5,10 @@ import org.junit.Test; -import fiji.plugin.trackmate.util.mesh.EllipsoidFitter; -import fiji.plugin.trackmate.util.mesh.EllipsoidFitter.EllipsoidFit; -import net.imagej.mesh.Mesh; -import net.imagej.mesh.naive.NaiveDoubleMesh; +import net.imglib2.mesh.alg.EllipsoidFitter; +import net.imglib2.mesh.alg.EllipsoidFitter.EllipsoidFit; +import net.imglib2.mesh.obj.Mesh; +import net.imglib2.mesh.obj.naive.NaiveDoubleMesh; public class TestEllipsoidFit {