From f8368f43c158f2ed76a696bf5a3c99226e0fefab Mon Sep 17 00:00:00 2001 From: Jean-Yves Tinevez Date: Fri, 8 Sep 2023 11:54:18 +0200 Subject: [PATCH] Add commonly useful lazy features. - Number of incoming and outgoing links on a spot. Useful to fish for cells that have a division or a merge, and debug unwanted linking events. - Oriented time-difference for links. Useful to detect buggy links introduced by improper semi-auto tracking. Debugging without this feature is really a pain. --- .../mamut/feature/LinkDeltaFrameFeature.java | 142 ++++++++++++++++++ .../mamut/feature/SpotNLinksFeature.java | 96 ++++++++++-- .../java/org/mastodon/mamut/model/Model.java | 8 +- 3 files changed, 233 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/mastodon/mamut/feature/LinkDeltaFrameFeature.java diff --git a/src/main/java/org/mastodon/mamut/feature/LinkDeltaFrameFeature.java b/src/main/java/org/mastodon/mamut/feature/LinkDeltaFrameFeature.java new file mode 100644 index 000000000..4d7dc8bfd --- /dev/null +++ b/src/main/java/org/mastodon/mamut/feature/LinkDeltaFrameFeature.java @@ -0,0 +1,142 @@ +/*- + * #%L + * Mastodon + * %% + * Copyright (C) 2014 - 2023 Tobias Pietzsch, Jean-Yves Tinevez + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package org.mastodon.mamut.feature; + +import static org.mastodon.feature.FeatureProjectionKey.key; + +import java.util.Collections; +import java.util.Set; + +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.FeatureSpec; +import org.mastodon.feature.Multiplicity; +import org.mastodon.mamut.model.Link; +import org.mastodon.mamut.model.ModelGraph; +import org.mastodon.mamut.model.Spot; +import org.scijava.plugin.Plugin; + +public class LinkDeltaFrameFeature implements Feature< Link > +{ + + private static final String KEY = "Link delta T"; + + private static final String HELP_STRING = "The time difference in number of time-points between the target and source of links."; + + private static final FeatureProjectionSpec PROJECTION_SPEC = new FeatureProjectionSpec( KEY, Dimension.NONE ); + + public static final Spec SPEC = new Spec(); + + final ModelGraph graph; + + @Plugin( type = FeatureSpec.class ) + public static class Spec extends FeatureSpec< LinkDeltaFrameFeature, Link > + { + public Spec() + { + super( + KEY, + HELP_STRING, + LinkDeltaFrameFeature.class, + Link.class, + Multiplicity.SINGLE, + PROJECTION_SPEC ); + } + } + + public LinkDeltaFrameFeature( final ModelGraph graph ) + { + this.graph = graph; + } + + @Override + public FeatureProjection< Link > project( final FeatureProjectionKey key ) + { + return key( PROJECTION_SPEC ).equals( key ) ? new MyProjection( graph ) : null; + } + + @Override + public Set< FeatureProjection< Link > > projections() + { + return Collections.singleton( new MyProjection( graph ) ); + } + + @Override + public Spec getSpec() + { + return SPEC; + } + + @Override + public void invalidate( final Link link ) + {} + + private static final class MyProjection implements FeatureProjection< Link > + { + + private final Spot ref1; + + private final Spot ref2; + + public MyProjection( final ModelGraph graph ) + { + this.ref1 = graph.vertexRef(); + this.ref2 = graph.vertexRef(); + } + + @Override + public FeatureProjectionKey getKey() + { + return key( PROJECTION_SPEC ); + } + + @Override + public boolean isSet( final Link link ) + { + return true; + } + + @Override + public synchronized double value( final Link link ) + { + final Spot source = link.getSource( ref1 ); + final Spot target = link.getTarget( ref2 ); + return target.getTimepoint() - source.getTimepoint(); + } + + @Override + public String units() + { + return Dimension.NONE_UNITS; + } + } +} diff --git a/src/main/java/org/mastodon/mamut/feature/SpotNLinksFeature.java b/src/main/java/org/mastodon/mamut/feature/SpotNLinksFeature.java index 1f7488e76..454f4be3e 100644 --- a/src/main/java/org/mastodon/mamut/feature/SpotNLinksFeature.java +++ b/src/main/java/org/mastodon/mamut/feature/SpotNLinksFeature.java @@ -28,7 +28,9 @@ */ package org.mastodon.mamut.feature; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import org.mastodon.feature.Dimension; @@ -47,13 +49,21 @@ public class SpotNLinksFeature implements Feature< Spot > public static final String KEY = "Spot N links"; - private static final String HELP_STRING = "Computes the number of links that touch a spot."; + private static final String HELP_STRING = "Number of outgoing and incoming links of a spot."; - public static final FeatureProjectionSpec PROJECTION_SPEC = new FeatureProjectionSpec( KEY ); + public static final FeatureProjectionSpec N_LINKS_PROJECTION_SPEC = new FeatureProjectionSpec( KEY ); + + public static final FeatureProjectionSpec N_OUTGOING_LINKS_PROJECTION_SPEC = new FeatureProjectionSpec( "N outgoing links" ); + + public static final FeatureProjectionSpec N_INCOMING_LINKS_PROJECTION_SPEC = new FeatureProjectionSpec( "N incoming links" ); public static final Spec SPEC = new Spec(); - private final IntFeatureProjection< Spot > projection; + private final IntFeatureProjection< Spot > nLinksProjection; + + private final IntFeatureProjection< Spot > nIncomingLinksProjection; + + private final IntFeatureProjection< Spot > nOutgoingLinksProjection; @Plugin( type = FeatureSpec.class ) public static class Spec extends FeatureSpec< SpotNLinksFeature, Spot > @@ -66,25 +76,37 @@ public Spec() SpotNLinksFeature.class, Spot.class, Multiplicity.SINGLE, - PROJECTION_SPEC ); + N_LINKS_PROJECTION_SPEC, + N_OUTGOING_LINKS_PROJECTION_SPEC, + N_INCOMING_LINKS_PROJECTION_SPEC ); } } public SpotNLinksFeature() { - this.projection = new MyProjection(); + this.nLinksProjection = new NLinkProjection(); + this.nOutgoingLinksProjection = new NOutgoingLinkProjection(); + this.nIncomingLinksProjection = new NIncomingLinkProjection(); } @Override public FeatureProjection< Spot > project( final FeatureProjectionKey key ) { - return projection.getKey().equals( key ) ? projection : null; + if ( nLinksProjection.getKey().equals( key ) ) + return nLinksProjection; + else if ( nOutgoingLinksProjection.getKey().equals( key ) ) + return nOutgoingLinksProjection; + else if ( nIncomingLinksProjection.getKey().equals( key ) ) + return nIncomingLinksProjection; + else + return null; } @Override public Set< FeatureProjection< Spot > > projections() { - return Collections.singleton( projection ); + return Collections.unmodifiableSet( new HashSet<>( Arrays.asList( + nLinksProjection, nOutgoingLinksProjection, nIncomingLinksProjection ) ) ); } @Override @@ -97,13 +119,13 @@ public Spec getSpec() public void invalidate( final Spot spot ) {} - private static final class MyProjection implements IntFeatureProjection< Spot > + private static final class NLinkProjection implements IntFeatureProjection< Spot > { @Override public FeatureProjectionKey getKey() { - return FeatureProjectionKey.key( PROJECTION_SPEC ); + return FeatureProjectionKey.key( N_LINKS_PROJECTION_SPEC ); } @Override @@ -125,4 +147,60 @@ public String units() } } + + private static final class NOutgoingLinkProjection implements IntFeatureProjection< Spot > + { + + @Override + public FeatureProjectionKey getKey() + { + return FeatureProjectionKey.key( N_OUTGOING_LINKS_PROJECTION_SPEC ); + } + + @Override + public boolean isSet( final Spot obj ) + { + return true; + } + + @Override + public double value( final Spot obj ) + { + return obj.outgoingEdges().size(); + } + + @Override + public String units() + { + return Dimension.NONE_UNITS; + } + } + + private static final class NIncomingLinkProjection implements IntFeatureProjection< Spot > + { + + @Override + public FeatureProjectionKey getKey() + { + return FeatureProjectionKey.key( N_INCOMING_LINKS_PROJECTION_SPEC ); + } + + @Override + public boolean isSet( final Spot obj ) + { + return true; + } + + @Override + public double value( final Spot obj ) + { + return obj.incomingEdges().size(); + } + + @Override + public String units() + { + return Dimension.NONE_UNITS; + } + } } diff --git a/src/main/java/org/mastodon/mamut/model/Model.java b/src/main/java/org/mastodon/mamut/model/Model.java index 82459cee8..3cfd675f3 100644 --- a/src/main/java/org/mastodon/mamut/model/Model.java +++ b/src/main/java/org/mastodon/mamut/model/Model.java @@ -47,6 +47,7 @@ import org.mastodon.graph.io.RawGraphIO.FileIdToGraphMap; import org.mastodon.graph.io.RawGraphIO.GraphToFileIdMap; import org.mastodon.labels.LabelSets; +import org.mastodon.mamut.feature.LinkDeltaFrameFeature; import org.mastodon.mamut.feature.LinkDisplacementFeature; import org.mastodon.mamut.feature.LinkTargetIdFeature; import org.mastodon.mamut.feature.LinkVelocityFeature; @@ -195,10 +196,9 @@ public void declareDefaultFeatures() featureModel.declareFeature( new SpotFrameFeature() ); featureModel.declareFeature( new SpotNLinksFeature() ); featureModel.declareFeature( new LinkTargetIdFeature( modelGraph ) ); - featureModel.declareFeature( - new LinkDisplacementFeature( modelGraph, Dimension.LENGTH.getUnits( spaceUnits, timeUnits ) ) ); - featureModel.declareFeature( - new LinkVelocityFeature( modelGraph, Dimension.VELOCITY.getUnits( spaceUnits, timeUnits ) ) ); + featureModel.declareFeature( new LinkDisplacementFeature( modelGraph, Dimension.LENGTH.getUnits( spaceUnits, timeUnits ) ) ); + featureModel.declareFeature( new LinkVelocityFeature( modelGraph, Dimension.VELOCITY.getUnits( spaceUnits, timeUnits ) ) ); + featureModel.declareFeature( new LinkDeltaFrameFeature( modelGraph ) ); featureModel.declareFeature( new BranchNDivisionsFeature() ); }