Skip to content

Commit

Permalink
An action to smooth meshes with Tuabin smoothing.
Browse files Browse the repository at this point in the history
  • Loading branch information
tinevez committed Nov 17, 2023
1 parent d853d94 commit 1adf74f
Show file tree
Hide file tree
Showing 4 changed files with 447 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package fiji.plugin.trackmate.action.meshtools;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import fiji.plugin.trackmate.Logger;
import fiji.plugin.trackmate.Spot;
import fiji.plugin.trackmate.SpotMesh;
import net.imglib2.mesh.Mesh;
import net.imglib2.mesh.Meshes;
import net.imglib2.mesh.alg.TaubinSmoothing;
import net.imglib2.mesh.alg.TaubinSmoothing.TaubinWeightType;
import net.imglib2.mesh.impl.nio.BufferMesh;

public class MeshSmoother
{

private final Map< SpotMesh, BufferMesh > undoMap;

private final Logger logger;

public MeshSmoother( final Iterable< Spot > spots, final Logger logger )
{
this.logger = logger;
// Store undo.
this.undoMap = new HashMap<>();
final double[] center = new double[ 3 ];
for ( final Spot spot : spots )
{
if ( SpotMesh.class.isInstance( spot ) )
{
final SpotMesh sm = ( SpotMesh ) spot;
final Mesh mesh = sm.getMesh();
final BufferMesh meshCopy = new BufferMesh( mesh.vertices().size(), mesh.triangles().size() );
Meshes.copy( mesh, meshCopy );
sm.localize( center );
Meshes.translate( meshCopy, center );
undoMap.put( sm, meshCopy );
}
}
}

public void undo()
{
logger.setStatus( "Undoing mesh smoothing" );
final Set< SpotMesh > keys = undoMap.keySet();
final int nSpots = keys.size();
int i = 0;
for ( final SpotMesh sm : keys )
{
final BufferMesh old = undoMap.get( sm );
sm.setMesh( old );
logger.setProgress( ( double ) ( ++i ) / nSpots );
}
logger.setStatus( "" );
}

public void smooth( final int nIters, final double mu, final double lambda, final TaubinWeightType weightType )
{
logger.setStatus( "Taubin smoothing" );
final Set< SpotMesh > keys = undoMap.keySet();
final int nSpots = keys.size();
int i = 0;
final double[] center = new double[ 3 ];
for ( final SpotMesh sm : keys )
{
final Mesh mesh = sm.getMesh();
sm.localize( center );
Meshes.translate( mesh, center );
final BufferMesh smoothedMesh = TaubinSmoothing.smooth( mesh, nIters, lambda, mu, weightType );
sm.setMesh( smoothedMesh );
logger.setProgress( ( double ) ( ++i ) / nSpots );
}
logger.setStatus( "" );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package fiji.plugin.trackmate.action.meshtools;

import java.awt.Frame;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

import org.scijava.plugin.Plugin;

import fiji.plugin.trackmate.ModelChangeEvent;
import fiji.plugin.trackmate.SelectionModel;
import fiji.plugin.trackmate.TrackMate;
import fiji.plugin.trackmate.action.AbstractTMAction;
import fiji.plugin.trackmate.action.TrackMateAction;
import fiji.plugin.trackmate.action.TrackMateActionFactory;
import fiji.plugin.trackmate.gui.GuiUtils;
import fiji.plugin.trackmate.gui.Icons;
import fiji.plugin.trackmate.gui.displaysettings.DisplaySettings;
import fiji.plugin.trackmate.util.EverythingDisablerAndReenabler;

public class MeshSmootherAction extends AbstractTMAction
{

@Override
public void execute( final TrackMate trackmate, final SelectionModel selectionModel, final DisplaySettings displaySettings, final Frame parent )
{
final MeshSmoother smoother = new MeshSmoother( trackmate.getModel().getSpots().iterable( true ), logger );

final MeshSmootherModel model = new MeshSmootherModel();
final MeshSmootherPanel panel = new MeshSmootherPanel( model );

panel.btnRun.addActionListener( e -> {
new Thread( () -> {
final EverythingDisablerAndReenabler enabler = new EverythingDisablerAndReenabler( panel, new Class[] { JLabel.class } );
try
{
enabler.disable();
smoother.smooth( model.getNIters(), model.getMu(), model.getLambda(), model.getWeightType() );
// Trigger refresh.
trackmate.getModel().getModelChangeListener().forEach( l -> l.modelChanged( new ModelChangeEvent( this, ModelChangeEvent.SPOTS_COMPUTED ) ) );
}
finally
{
enabler.reenable();
}
}, "TrackMate mesh smoother" ).start();
} );

panel.btnUndo.addActionListener( e -> {
new Thread( () -> {
final EverythingDisablerAndReenabler enabler = new EverythingDisablerAndReenabler( panel, new Class[] { JLabel.class } );
try
{
enabler.disable();
smoother.undo();
// Trigger refresh.
trackmate.getModel().getModelChangeListener().forEach( l -> l.modelChanged( new ModelChangeEvent( this, ModelChangeEvent.SPOTS_COMPUTED ) ) );
}
finally
{
enabler.reenable();
}
}, "TrackMate mesh smoother" ).start();
} );

final JFrame frame = new JFrame( "Smoothing params" );
frame.getContentPane().add( panel );
frame.setSize( 400, 300 );
GuiUtils.positionWindow( frame, parent );
frame.setVisible( true );
}

@Plugin( type = TrackMateActionFactory.class )
public static class Factory implements TrackMateActionFactory
{

public static final String NAME = "Smooth 3D meshes";

public static final String KEY = "MESH_SMOOTHER";

public static final String INFO_TEXT = "<html>"
+ "Displays a tool to smooth the 3D mesh present in "
+ "the data, using the Taubin smoothing algorithm.</html>";

@Override
public String getInfoText()
{
return INFO_TEXT;
}

@Override
public String getKey()
{
return KEY;
}

@Override
public TrackMateAction create()
{
return new MeshSmootherAction();
}

@Override
public ImageIcon getIcon()
{
return Icons.VECTOR_ICON;
}

@Override
public String getName()
{
return NAME;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package fiji.plugin.trackmate.action.meshtools;

import net.imglib2.mesh.alg.TaubinSmoothing.TaubinWeightType;

public class MeshSmootherModel
{

private int nIters = 10;

private double mu = 0.5;

private double lambda = -0.53;

private TaubinWeightType weightType = TaubinWeightType.NAIVE;

public void setWeightType( final TaubinWeightType weightType )
{
this.weightType = weightType;
}

public void setMu( final double mu )
{
this.mu = Math.min( 1., Math.max( 0, mu ) );
}

public void setLambda( final double lambda )
{
this.lambda = Math.min( 0., Math.max( -1., lambda ) );
}

public void setNIters( final int nIters )
{
this.nIters = Math.max( 0, nIters );
}

public double getMu()
{
return mu;
}

public double getLambda()
{
return lambda;
}

public int getNIters()
{
return nIters;
}

public TaubinWeightType getWeightType()
{
return weightType;
}

/**
* Ad-hoc method setting parameters for little smoothing (close to 0) or a
* lot of smoothing (close to 1).
*
* @param smoothing
* the smoothing parameter.
*/
public void setSmoothing( final double smoothing )
{
setMu( Math.max( 0, Math.min( 0.97, smoothing ) ) );
setLambda( -mu - 0.03 );
}
}
Loading

0 comments on commit 1adf74f

Please sign in to comment.