Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Feature BranchSinuosity #18

Merged
merged 5 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.mastodon.mamut.feature.branch;

import org.mastodon.feature.FeatureSpec;
import org.mastodon.io.FileIdToObjectMap;
import org.mastodon.io.ObjectToFileIdMap;
import org.mastodon.io.properties.DoublePropertyMapSerializer;
import org.mastodon.mamut.model.ModelGraph;
import org.mastodon.mamut.model.Spot;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.mamut.model.branch.ModelBranchGraph;
import org.mastodon.properties.DoublePropertyMap;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public abstract class BranchSpotDoublePropertyFeatureSerializer< T extends DoublePropertyFeature< BranchSpot > >
implements BranchFeatureSerializer< T, BranchSpot, Spot >
{
@Override
public abstract FeatureSpec< T, BranchSpot > getFeatureSpec();

protected abstract T createFeature( DoublePropertyMap< BranchSpot > map );

protected abstract DoublePropertyMap< BranchSpot > extractPropertyMap( T feature );

@Override
public T deserialize( FileIdToObjectMap< Spot > idmap, ObjectInputStream ois, ModelBranchGraph branchGraph, ModelGraph graph )
throws ClassNotFoundException, IOException
{
// Read the map link -> value
final DoublePropertyMap< Spot > spotPropertyMap = new DoublePropertyMap<>( graph.vertices(), -1 );
final DoublePropertyMapSerializer< Spot > propertyMapSerializer = new DoublePropertyMapSerializer<>( spotPropertyMap );
propertyMapSerializer.readPropertyMap( idmap, ois );

// Map to branch-link -> value
DoublePropertyMap< BranchSpot > branchPropertyMap = BranchFeatureSerializer.mapToBranchSpotMap( spotPropertyMap, branchGraph );
return createFeature( branchPropertyMap );
}

@Override
public void serialize( T feature, ObjectToFileIdMap< Spot > idmap, ObjectOutputStream oos, ModelBranchGraph branchGraph,
ModelGraph graph ) throws IOException
{
DoublePropertyMap< BranchSpot > branchSpotMap = extractPropertyMap( feature );
final DoublePropertyMap< Spot > spotMap =
BranchFeatureSerializer.branchSpotMapToMap( branchSpotMap, branchGraph, graph );
final DoublePropertyMapSerializer< Spot > propertyMapSerializer = new DoublePropertyMapSerializer<>( spotMap );
propertyMapSerializer.writePropertyMap( idmap, oos );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import org.mastodon.mamut.model.branch.ModelBranchGraph;
import org.mastodon.properties.IntPropertyMap;

public abstract class BranchSpotIntPropertyFeatureSerializer< T extends BranchSpotIntPropertyFeature< BranchSpot > >
public abstract class BranchSpotIntPropertyFeatureSerializer< T extends IntPropertyFeature< BranchSpot > >
implements BranchFeatureSerializer< T, BranchSpot, Spot >
{
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.mastodon.mamut.feature.branch;

import org.mastodon.feature.Dimension;
import org.mastodon.feature.Feature;
import org.mastodon.feature.FeatureProjection;
import org.mastodon.feature.FeatureProjectionKey;
import org.mastodon.feature.FeatureProjectionSpec;
import org.mastodon.feature.FeatureProjections;
import org.mastodon.properties.DoublePropertyMap;

import java.util.Collections;
import java.util.Set;

import static org.mastodon.feature.FeatureProjectionKey.key;

public abstract class DoublePropertyFeature< T > implements Feature< T >
{

public final DoublePropertyMap< T > map;

protected final FeatureProjection< T > projection;

public DoublePropertyFeature( DoublePropertyMap< T > map )
{
this.map = map;
this.projection = FeatureProjections.project( key( getFeatureProjectionSpec() ), map, Dimension.NONE_UNITS );
}

protected abstract FeatureProjectionSpec getFeatureProjectionSpec();

public double get( final T branchSpot )
{
return map.getDouble( branchSpot );
}

@Override
public FeatureProjection< T > project( final FeatureProjectionKey key )
{
return projection.getKey().equals( key ) ? projection : null;
}

@Override
public Set< FeatureProjection< T > > projections()
{
return Collections.singleton( projection );
}

@Override
public void invalidate( final T branchSpot )
{
map.remove( branchSpot );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@

import static org.mastodon.feature.FeatureProjectionKey.key;

public abstract class BranchSpotIntPropertyFeature< T > implements Feature< T >
public abstract class IntPropertyFeature< T > implements Feature< T >
{

public final IntPropertyMap< T > map;

protected final IntFeatureProjection< T > projection;

public BranchSpotIntPropertyFeature( IntPropertyMap< T > map )
public IntPropertyFeature( IntPropertyMap< T > map )
{
this.map = map;
this.projection = FeatureProjections.project( key( getFeatureProjectionSpec() ), map, Dimension.NONE_UNITS );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.mastodon.feature.FeatureProjectionSpec;
import org.mastodon.feature.FeatureSpec;
import org.mastodon.feature.Multiplicity;
import org.mastodon.mamut.feature.branch.BranchSpotIntPropertyFeature;
import org.mastodon.mamut.feature.branch.IntPropertyFeature;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.properties.IntPropertyMap;
import org.scijava.plugin.Plugin;
Expand Down Expand Up @@ -32,7 +32,7 @@
* <li>{@code branchSpot4 = 1}</li>
* </ul>
*/
public class BranchNLeavesFeature extends BranchSpotIntPropertyFeature< BranchSpot >
public class BranchNLeavesFeature extends IntPropertyFeature< BranchSpot >
{
public static final String KEY = "Branch N leaves";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.mastodon.mamut.feature.branch.sinuosity;

import org.mastodon.feature.FeatureProjectionSpec;
import org.mastodon.feature.FeatureSpec;
import org.mastodon.feature.Multiplicity;
import org.mastodon.mamut.feature.branch.DoublePropertyFeature;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.properties.DoublePropertyMap;
import org.scijava.plugin.Plugin;

/**
* Computes the sinuosity (cf. <a href="https://en.wikipedia.org/wiki/Sinuosity">Sinuosity</a>) of a Spot during an individual cell life cycle.
* <p>
* <ul>
* <li>A sinuosity of 1 means that the cell moved in a straight line</li>
* <li>A sinuosity of {@link Double#NaN} means that the cell did not move at all.</li>
* <li>A sinuosity > 1 means that the cell moved in a curved line. The higher, this value is, the "curvier" the cell has moved</li>
* </ul>
*/
public class BranchSinuosityFeature extends DoublePropertyFeature< BranchSpot >
{
public static final String KEY = "Branch Sinuosity";

private static final String HELP_STRING =
"Computes the directness of movement a spot during a single cell life cycle.";

public static final FeatureProjectionSpec PROJECTION_SPEC = new FeatureProjectionSpec( KEY );

public static final BranchSinuosityFeature.Spec BRANCH_SINUOSITY_FEATURE_SPEC = new BranchSinuosityFeature.Spec();

@Plugin( type = FeatureSpec.class )
public static class Spec extends FeatureSpec< BranchSinuosityFeature, BranchSpot >
{
public Spec()
{
super(
KEY,
HELP_STRING,
BranchSinuosityFeature.class,
BranchSpot.class,
Multiplicity.SINGLE,
PROJECTION_SPEC );
}
}

public BranchSinuosityFeature( final DoublePropertyMap< BranchSpot > map )
{
super( map );
}

@Override
public FeatureProjectionSpec getFeatureProjectionSpec()
{
return PROJECTION_SPEC;
}

@Override
public BranchSinuosityFeature.Spec getSpec()
{
return BRANCH_SINUOSITY_FEATURE_SPEC;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.mastodon.mamut.feature.branch.sinuosity;

import net.imglib2.util.LinAlgHelpers;
import org.mastodon.mamut.feature.MamutFeatureComputer;
import org.mastodon.mamut.model.Spot;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.mamut.model.branch.ModelBranchGraph;
import org.mastodon.properties.DoublePropertyMap;
import org.scijava.ItemIO;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;

import java.util.Arrays;
import java.util.Iterator;

/**
* Computes {@link BranchSinuosityFeature}
*/
@Plugin( type = MamutFeatureComputer.class )
public class BranchSinuosityFeatureComputer implements MamutFeatureComputer
{

@Parameter
private ModelBranchGraph branchGraph;

@Parameter( type = ItemIO.OUTPUT )
private BranchSinuosityFeature output;

@Override
public void createOutput()
{
if ( null == output )
output = new BranchSinuosityFeature( new DoublePropertyMap<>( branchGraph.vertices().getRefPool(), 0 ) );
}

@Override
public void run()
{
computeBranchSinuosity();
}

private void computeBranchSinuosity()
{
branchGraph.vertices().forEach( branchSpot -> output.map.set( branchSpot, computeSinuosity( branchSpot ) ) );
}

private double computeSinuosity( final BranchSpot branchSpot )
{
final double[] currentSpotCoordinates = new double[ branchSpot.numDimensions() ];

Spot spot;
double accumulatedLifeCycleDistance = 0d;

final Iterator< Spot > spotIterator = branchGraph.vertexBranchIterator( branchSpot );
if ( !spotIterator.hasNext() )
return Double.NaN;
spot = spotIterator.next();
if ( !spotIterator.hasNext() )
return Double.NaN;

spot.localize( currentSpotCoordinates );
double[] previousSpotCoordinates = Arrays.copyOf( currentSpotCoordinates, currentSpotCoordinates.length );
double[] branchStartCoordinates = Arrays.copyOf( currentSpotCoordinates, currentSpotCoordinates.length );

while ( spotIterator.hasNext() )
{
spot = spotIterator.next();
spot.localize( currentSpotCoordinates );
accumulatedLifeCycleDistance += LinAlgHelpers.distance( currentSpotCoordinates, previousSpotCoordinates );
previousSpotCoordinates = Arrays.copyOf( currentSpotCoordinates, currentSpotCoordinates.length );
maarzt marked this conversation as resolved.
Show resolved Hide resolved
}
double directLifeCycleDistance = LinAlgHelpers.distance( currentSpotCoordinates, branchStartCoordinates );
return accumulatedLifeCycleDistance / directLifeCycleDistance;
maarzt marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.mastodon.mamut.feature.branch.sinuosity;

import org.mastodon.feature.FeatureSpec;
import org.mastodon.feature.io.FeatureSerializer;
import org.mastodon.mamut.feature.branch.BranchSpotDoublePropertyFeatureSerializer;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.properties.DoublePropertyMap;
import org.scijava.plugin.Plugin;

/**
* De-/serializes the {@link BranchSinuosityFeature}.
*/
@Plugin( type = FeatureSerializer.class )
public class BranchSinuosityFeatureSerializer extends BranchSpotDoublePropertyFeatureSerializer< BranchSinuosityFeature >
{
@Override
public FeatureSpec< BranchSinuosityFeature, BranchSpot > getFeatureSpec()
{
return BranchSinuosityFeature.BRANCH_SINUOSITY_FEATURE_SPEC;
}

@Override
protected BranchSinuosityFeature createFeature( DoublePropertyMap< BranchSpot > map )
{
return new BranchSinuosityFeature( map );
}

@Override
protected DoublePropertyMap< BranchSpot > extractPropertyMap( BranchSinuosityFeature feature )
{
return feature.map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import org.mastodon.feature.FeatureProjectionSpec;
import org.mastodon.feature.FeatureSpec;
import org.mastodon.feature.Multiplicity;
import org.mastodon.mamut.feature.branch.BranchSpotIntPropertyFeature;
import org.mastodon.mamut.feature.branch.IntPropertyFeature;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.properties.IntPropertyMap;
import org.scijava.plugin.Plugin;

/**
* Represents the total number of successors of a branch spot in the whole track sub-tree of this branch spot.
* Represents the total number of successors of a branch spot in the whole track subtree of this branch spot.
* <p>
* In the following example this number would equal to following branchSpots as
* follows:
Expand All @@ -32,7 +32,7 @@
* <li>{@code branchSpot4 = 0}</li>
* </ul>
*/
public class BranchNSuccessorsFeature extends BranchSpotIntPropertyFeature< BranchSpot >
public class BranchNSuccessorsFeature extends IntPropertyFeature< BranchSpot >
{
public static final String KEY = "Branch N sub branch spots";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.mastodon.mamut.feature.branch.sinuosity;

import org.junit.Test;
import org.mastodon.feature.FeatureProjection;
import org.mastodon.mamut.feature.branch.FeatureComputerTestUtils;
import org.mastodon.mamut.feature.branch.exampleGraph.ExampleGraph1;
import org.mastodon.mamut.feature.branch.exampleGraph.ExampleGraph2;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.scijava.Context;

import static org.junit.Assert.assertEquals;

public class BranchSinuosityFeatureComputerTest
{

@Test
public void testComputeNumberOfSubtreeNodes1()
{
try (Context context = new Context())
{
ExampleGraph1 exampleGraph1 = new ExampleGraph1();
FeatureProjection< BranchSpot > featureProjection =
FeatureComputerTestUtils.getBranchSpotFeatureProjection( context, exampleGraph1,
BranchSinuosityFeature.BRANCH_SINUOSITY_FEATURE_SPEC,
BranchSinuosityFeature.PROJECTION_SPEC );
assertEquals( 1d, featureProjection.value( exampleGraph1.branchSpotA ), 0 );
}
}

@Test
public void testComputeNumberOfSubtreeNodes2()
{
try (Context context = new Context())
{
ExampleGraph2 exampleGraph2 = new ExampleGraph2();
FeatureProjection< BranchSpot > featureProjection =
FeatureComputerTestUtils.getBranchSpotFeatureProjection( context, exampleGraph2,
BranchSinuosityFeature.BRANCH_SINUOSITY_FEATURE_SPEC,
BranchSinuosityFeature.PROJECTION_SPEC );

assertEquals( ( Math.sqrt( 1 + 4 + 9 ) + Math.sqrt( 9 + 36 + 81 ) ) / Math.sqrt( 4 + 16 + 36 ),
featureProjection.value( exampleGraph2.branchSpotA ), 0 );
assertEquals( 1d, featureProjection.value( exampleGraph2.branchSpotC ), 0 );
assertEquals( 1d, featureProjection.value( exampleGraph2.branchSpotB ), 0 );
assertEquals( ( Math.sqrt( 36 + 144 + 324 ) + Math.sqrt( 64 + 256 + 576 ) ) / Math.sqrt( 4 + 16 + 36 ),
featureProjection.value( exampleGraph2.branchSpotD ), 0 );
assertEquals( 1d, featureProjection.value( exampleGraph2.branchSpotE ), 0 );
}
}
}