Skip to content

Commit

Permalink
Avoid duplicated attributes when export to MaMuT a file imported from…
Browse files Browse the repository at this point in the history
… MaMuT.

During an export to MaMuT, the position of the spots are encoded in the
feature 'POSITION_X' etc, so that MaMuT can retrieve the spots at the
right position.

A problem might occur if the data in Mastodon have been imported from
a MaMuT (or TrackMate) file. In that case the exporter will try to
write two attribute with the same name: the POSITION_X from the actual
spot position and the POSITION_X that was imported. This is happening
for all the builtin features we need for the MaMuT export.

To avoid this, we rename the imported features with 'IMPORTED_...'. And
if we have imported features already named 'IMPORTED_...' (if the user
re-reimport a MaMuT exported file from a MaMut imported file), we skip
them.

Fix #364
  • Loading branch information
tinevez committed Feb 12, 2025
1 parent 97719f6 commit 876c975
Showing 1 changed file with 90 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,9 @@ private Element[] trackCollectionToXml()
public void processVertexLate( final Spot vertex, final DepthFirstSearch< Spot, Link > search )
{
/*
* 1 root = 1 track, unless a track has several roots. Add
* the iterated vertex to the list of root to skip if
* needed.
* 1 root = 1 track, unless a track has several
* roots. Add the iterated vertex to the list of
* root to skip if needed.
*/
if ( vertex.incomingEdges().isEmpty() )
toSkip.add( vertex );
Expand Down Expand Up @@ -520,6 +520,15 @@ private Element spotCollectionToXml()
return spotCollectionElement;
}

/**
* Collection of link feature names that we want to export in the mamut
* file, but computed from the link data currently set in Mastodon. We used
* this collection to avoid exporting a feature with identical name in the
* case a feature imported as the same name.
*/
private final static Set< String > IMPORTED_LINK_BUILTIN_FEATURES = new HashSet<>( Arrays.asList(
EDGE_SOURCE_ATTRIBUTE, EDGE_TARGET_ATTRIBUTE ) );

private Element edgeToXml( final Link edge, final int sourceSpotID, final int targetSpotID )
{
final Collection< Attribute > attributes = new ArrayList<>();
Expand All @@ -529,9 +538,38 @@ private Element edgeToXml( final Link edge, final int sourceSpotID, final int ta
attributes.add( new Attribute( EDGE_TARGET_ATTRIBUTE, Integer.toString( targetSpotID ) ) );

// Link features.
linkFeatureProjections.forEach( p -> attributes.add( new Attribute(
p.attributeName,
Double.toString( p.projection.value( edge ) ) ) ) );
for ( final ExportFeatureProjection< Link > p : linkFeatureProjections )
{
final String attName;
final String origName = p.attributeName;
/*
* If the model to export was imported from a TrackMate or a MaMuT
* file, it will already contain features with the same name that
* the builtin feature we added just above. Which will cause an
* error.
*
* To avoid this, rename these imported features.
*/
if ( IMPORTED_LINK_BUILTIN_FEATURES.contains( origName ) )
{
final String importedAttName = "IMPORTED_" + origName;
attName = importedAttName;
}
else if ( origName.startsWith( "IMPORTED_" ) )
{
// We skip features that have been re-imported.
continue;
}
else
{
// All good.
attName = origName;
}

attributes.add( new Attribute(
attName,
Double.toString( p.projection.value( edge ) ) ) );
}

final Element edgeElement = new Element( EDGE_TAG );
edgeElement.setAttributes( attributes );
Expand All @@ -556,9 +594,20 @@ private Element trackToXml( final Spot root )
return trackElement;
}

/**
* Collection of spot feature names that we want to export in the mamut
* file, but computed from the actual position, etc, currently set in
* Mastodon. We used this collection to avoid exporting a feature with
* identical name in the case a feature imported as the same name.
*/
private final static Set< String > IMPORTED_SPOT_BUILTIN_FEATURES = new HashSet<>( Arrays.asList( ID_FEATURE_NAME, LABEL_FEATURE_NAME, POSITION_X_FEATURE_NAME,
POSITION_Y_FEATURE_NAME, POSITION_Z_FEATURE_NAME, FRAME_FEATURE_NAME,
POSITION_T_FEATURE_NAME, QUALITY_FEATURE_NAME, VISIBILITY_FEATURE_NAME,
RADIUS_FEATURE_NAME ) );

private Element spotToXml( final Spot spot )
{
final Collection< Attribute > attributes = new ArrayList<>();
final List< Attribute > attributes = new ArrayList<>();

// Id.
attributes.add( new Attribute( ID_FEATURE_NAME, Integer.toString( spot.getInternalPoolIndex() ) ) );
Expand All @@ -583,10 +632,38 @@ private Element spotToXml( final Spot spot )
final double meanRadius = Arrays.stream( eig.getRealEigenvalues() ).map( Math::sqrt ).average().getAsDouble();
attributes.add( new Attribute( RADIUS_FEATURE_NAME, Double.toString( meanRadius ) ) );

// Spot features.
spotFeatureProjections.forEach( p -> attributes.add( new Attribute(
p.attributeName,
Double.toString( p.projection.value( spot ) ) ) ) );
for (final ExportFeatureProjection< Spot > p : spotFeatureProjections )
{
final String attName;
final String origName = p.attributeName;
/*
* If the model to export was imported from a TrackMate or a MaMuT
* file, it will already contain features with the same name that
* the builtin feature we added just above. Which will cause an
* error.
*
* To avoid this, rename these imported features.
*/
if ( IMPORTED_SPOT_BUILTIN_FEATURES.contains( origName ) )
{
final String importedAttName = "IMPORTED_" + origName;
attName = importedAttName;
}
else if ( origName.startsWith( "IMPORTED_" ) )
{
// We skip features that have been re-imported.
continue;
}
else
{
// All good.
attName = origName;
}

attributes.add( new Attribute(
attName,
Double.toString( p.projection.value( spot ) ) ) );
}

final Element spotElement = new Element( SPOT_ELEMENT_TAG );
spotElement.setAttributes( attributes );
Expand Down Expand Up @@ -652,8 +729,8 @@ private static Document getSAXParsedDocument( final String fileName )
}

/**
* Tries to recover the TrackMate dimension from the unit string and the spatial and
* time units.
* Tries to recover the TrackMate dimension from the unit string and the
* spatial and time units.
*
* @param units
* the unit string.
Expand Down

0 comments on commit 876c975

Please sign in to comment.