From b94f8f1cafb6e475131321e32f52dc89b421063e Mon Sep 17 00:00:00 2001 From: Christian Tischer Date: Fri, 8 Dec 2023 10:03:34 +0100 Subject: [PATCH] Enable opening of the Microglia data --- pom.xml | 2 +- .../mobie/lib/SourcesFromTableCreator.java | 6 +- .../lib/files/FileSourcesDataSetter.java | 4 + .../mobie/lib/files/ImageFileSources.java | 4 - .../mobie/lib/files/LabelFileSources.java | 10 +- .../org/embl/mobie/lib/source/Metadata.java | 28 +++++- .../embl/mobie/lib/table/TableDataFormat.java | 12 ++- .../columns/MicrogliaSegmentColumnNames.java | 91 +++++++++++++++++++ .../lib/table/columns/SegmentColumnNames.java | 4 + .../embl/mobie/lib/table/saw/TableOpener.java | 24 ++++- .../saw/TableSawAnnotatedSegmentCreator.java | 6 +- .../java/projects/OpenVeronicaMicroglia.java | 22 +++++ 12 files changed, 187 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/embl/mobie/lib/table/columns/MicrogliaSegmentColumnNames.java create mode 100644 src/test/java/projects/OpenVeronicaMicroglia.java diff --git a/pom.xml b/pom.xml index 0196552c1..ac7e71d66 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ org.embl.mobie mobie-viewer-fiji - 4.1.4-SNAPSHOT + 4.1.4 diff --git a/src/main/java/org/embl/mobie/lib/SourcesFromTableCreator.java b/src/main/java/org/embl/mobie/lib/SourcesFromTableCreator.java index 1e96a23b6..3cd2a309f 100644 --- a/src/main/java/org/embl/mobie/lib/SourcesFromTableCreator.java +++ b/src/main/java/org/embl/mobie/lib/SourcesFromTableCreator.java @@ -104,11 +104,7 @@ public SourcesFromTableCreator( String tablePath, List< String > imageColumns, L // int numSources = imageFileSources.get( 0 ).getSources().size(); - if ( numSources == 1 ) - { - // no region table and no grid view needed - } - else if ( table.rowCount() == numSources ) + if ( table.rowCount() == numSources ) { // the input table is an image table and // can thus be used as the region table diff --git a/src/main/java/org/embl/mobie/lib/files/FileSourcesDataSetter.java b/src/main/java/org/embl/mobie/lib/files/FileSourcesDataSetter.java index 13a6f6e67..5c8625128 100644 --- a/src/main/java/org/embl/mobie/lib/files/FileSourcesDataSetter.java +++ b/src/main/java/org/embl/mobie/lib/files/FileSourcesDataSetter.java @@ -111,7 +111,11 @@ public void addDataAndDisplaysAndViews( Dataset dataset ) } } + addGridView( dataset, allSources ); + } + private void addGridView( Dataset dataset, ArrayList< ImageFileSources > allSources ) + { RegionDisplay< AnnotatedRegion > regionDisplay = null; final List< Display< ? > > displays = new ArrayList<>(); final List< Transformation > transformations = new ArrayList<>(); diff --git a/src/main/java/org/embl/mobie/lib/files/ImageFileSources.java b/src/main/java/org/embl/mobie/lib/files/ImageFileSources.java index f5afc3c74..d5154940a 100644 --- a/src/main/java/org/embl/mobie/lib/files/ImageFileSources.java +++ b/src/main/java/org/embl/mobie/lib/files/ImageFileSources.java @@ -36,22 +36,18 @@ import org.embl.mobie.lib.table.ColumnNames; import org.embl.mobie.lib.table.TableDataFormat; import org.embl.mobie.lib.table.columns.SegmentColumnNames; -import org.embl.mobie.lib.table.saw.Aggregators; import org.embl.mobie.lib.transform.GridType; import tech.tablesaw.api.NumberColumn; import tech.tablesaw.api.StringColumn; import tech.tablesaw.api.Table; -import tech.tablesaw.columns.Column; import java.io.File; -import java.lang.reflect.Executable; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static org.embl.mobie.io.util.IOHelper.getPaths; -import static tech.tablesaw.aggregate.AggregateFunctions.mean; public class ImageFileSources { diff --git a/src/main/java/org/embl/mobie/lib/files/LabelFileSources.java b/src/main/java/org/embl/mobie/lib/files/LabelFileSources.java index aa6f6d197..1f76e34ae 100644 --- a/src/main/java/org/embl/mobie/lib/files/LabelFileSources.java +++ b/src/main/java/org/embl/mobie/lib/files/LabelFileSources.java @@ -28,6 +28,7 @@ */ package org.embl.mobie.lib.files; +import ij.IJ; import org.embl.mobie.lib.io.StorageLocation; import org.embl.mobie.lib.table.TableDataFormat; import org.embl.mobie.lib.table.TableSource; @@ -44,6 +45,7 @@ public class LabelFileSources extends ImageFileSources { protected Map< String, TableSource > nameToLabelTable = new LinkedHashMap<>(); + private static boolean logLabelParsingError = true; public LabelFileSources( String name, Table table, String columnName, Integer channelIndex, String root, GridType gridType, boolean useTableForSegments ) { @@ -66,9 +68,11 @@ public LabelFileSources( String name, Table table, String columnName, Integer ch } else { - /* - The table does not contain any segment information that can be parsed. - */ + if ( logLabelParsingError ) + { + IJ.log( "[WARNING] The table does not contain parseable segments (labels) information." ); + logLabelParsingError = false; + } } } } diff --git a/src/main/java/org/embl/mobie/lib/source/Metadata.java b/src/main/java/org/embl/mobie/lib/source/Metadata.java index 455cc1bf5..5bc0c7022 100644 --- a/src/main/java/org/embl/mobie/lib/source/Metadata.java +++ b/src/main/java/org/embl/mobie/lib/source/Metadata.java @@ -28,8 +28,13 @@ */ package org.embl.mobie.lib.source; +import IceInternal.Ex; import ij.IJ; import ij.ImagePlus; +import ij.plugin.ContrastEnhancer; +import ij.process.ImageStatistics; + +import static ij.measure.Measurements.MIN_MAX; public class Metadata { @@ -39,6 +44,7 @@ public class Metadata public Integer numTimePoints = null; public Integer numZSlices = 1; public Integer numChannelsContainer = 1; // in MoBIE each image has jsut one channel, but the container could have multiple + private static boolean logBFError = true; public Metadata() { @@ -46,11 +52,23 @@ public Metadata() public Metadata( ImagePlus imagePlus ) { - color = "White"; // TODO: why not extract the color? - // we could use more direct methods: - // https://imagej.nih.gov/ij/developer/source/ij/plugin/ContrastEnhancer.java.html - IJ.run( imagePlus, "Enhance Contrast", "saturated=0.35" ); - contrastLimits = new double[]{ imagePlus.getDisplayRangeMin(), imagePlus.getDisplayRangeMax() }; + color = "White"; // TODO: why not also extract the color? + + try + { + ImageStatistics statistics = ImageStatistics.getStatistics( imagePlus.getProcessor(), MIN_MAX, null ); + new ContrastEnhancer().stretchHistogram( imagePlus.getProcessor(), 0.35, statistics ); + } + catch ( Exception e ) + { + if ( logBFError ) + { + // https://forum.image.sc/t/b-c-for-a-whole-virtual-stack-cont/57811/12 + IJ.log( "[WARNING] Could not determine auto-contrast for some images due to Bio-Formats issue: https://forum.image.sc/t/b-c-for-a-whole-virtual-stack-cont/57811/12" ); + logBFError = false; + } + } + contrastLimits = new double[]{ imagePlus.getProcessor().getMin(), imagePlus.getProcessor().getMax() }; numTimePoints = imagePlus.getNFrames(); numZSlices = imagePlus.getNSlices(); numChannelsContainer = imagePlus.getNChannels(); diff --git a/src/main/java/org/embl/mobie/lib/table/TableDataFormat.java b/src/main/java/org/embl/mobie/lib/table/TableDataFormat.java index 71abc7c8f..ede99e6e7 100644 --- a/src/main/java/org/embl/mobie/lib/table/TableDataFormat.java +++ b/src/main/java/org/embl/mobie/lib/table/TableDataFormat.java @@ -31,6 +31,7 @@ import com.google.gson.annotations.SerializedName; import ij.IJ; import org.embl.mobie.lib.table.columns.*; +import org.embl.mobie.lib.table.saw.TableOpener; import java.util.Collection; @@ -76,8 +77,9 @@ public String toString() public static TableDataFormat fromPath( String path ) { - if ( path.endsWith( ".csv" ) ) return CSV; - if ( path.endsWith( ".tsv" ) ) return TSV; + Character delimiter = TableOpener.determineDelimiter( path ); + if ( delimiter.equals( ',' ) ) return CSV; + if ( delimiter.equals( '\t' ) ) return TSV; throw new RuntimeException("Could not determine table format of " + path ); } @@ -129,10 +131,14 @@ public static SegmentColumnNames getSegmentColumnNames( Collection< String > col if ( CellProfilerSegmentColumnNames.matches( columnNames ) ) { - IJ.log( "Identified CellProfiler object table." ); return new CellProfilerSegmentColumnNames( columnNames ); } + if ( MicrogliaSegmentColumnNames.matches( columnNames ) ) + { + return new MicrogliaSegmentColumnNames(); + } + return null; } } diff --git a/src/main/java/org/embl/mobie/lib/table/columns/MicrogliaSegmentColumnNames.java b/src/main/java/org/embl/mobie/lib/table/columns/MicrogliaSegmentColumnNames.java new file mode 100644 index 000000000..56c86402e --- /dev/null +++ b/src/main/java/org/embl/mobie/lib/table/columns/MicrogliaSegmentColumnNames.java @@ -0,0 +1,91 @@ +/*- + * #%L + * Fiji viewer for MoBIE projects + * %% + * Copyright (C) 2018 - 2023 EMBL + * %% + * 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.embl.mobie.lib.table.columns; + +import java.util.Collection; + +/** + * https://github.com/embl-cba/microglia-morphometry/tree/main#features + * + */ +public class MicrogliaSegmentColumnNames implements SegmentColumnNames +{ + private static final String NONE = "None"; + private static final String LABEL_ID = "Object_Label"; + private static final String[] ANCHOR = { "Centroid_X_Pixel", "Centroid_Y_Pixel", "Centroid_Z_Pixel" }; + private static final String[] BB_MIN = { NONE, NONE, NONE }; + private static final String[] BB_MAX = { NONE, NONE, NONE }; + private static final String TIMEPOINT = "Centroid_Time_Frames"; + + @Override + public String labelImageColumn() + { + return NONE; + } + + @Override + public String labelIdColumn() + { + return LABEL_ID; + } + + @Override + public String timePointColumn() + { + return TIMEPOINT; + } + + @Override + public String[] anchorColumns() + { + return ANCHOR; + } + + @Override + public String[] bbMinColumns() + { + return BB_MIN; + } + + @Override + public String[] bbMaxColumns() + { + return BB_MAX; + } + + public static boolean matches( Collection< String > columns ) + { + return columns.contains( LABEL_ID ); + } + + @Override + public boolean timePointsAreOneBased() { + return true; + } +} diff --git a/src/main/java/org/embl/mobie/lib/table/columns/SegmentColumnNames.java b/src/main/java/org/embl/mobie/lib/table/columns/SegmentColumnNames.java index e69bec923..32d841808 100644 --- a/src/main/java/org/embl/mobie/lib/table/columns/SegmentColumnNames.java +++ b/src/main/java/org/embl/mobie/lib/table/columns/SegmentColumnNames.java @@ -43,4 +43,8 @@ public interface SegmentColumnNames String[] bbMaxColumns(); + default boolean timePointsAreOneBased() { + return false; + } + } diff --git a/src/main/java/org/embl/mobie/lib/table/saw/TableOpener.java b/src/main/java/org/embl/mobie/lib/table/saw/TableOpener.java index 68b5f1dbf..18c58f6bb 100644 --- a/src/main/java/org/embl/mobie/lib/table/saw/TableOpener.java +++ b/src/main/java/org/embl/mobie/lib/table/saw/TableOpener.java @@ -41,6 +41,9 @@ import tech.tablesaw.api.IntColumn; import tech.tablesaw.api.Table; import tech.tablesaw.io.csv.CsvReadOptions; + +import java.io.BufferedReader; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -176,7 +179,7 @@ public static Table openResultTable( ResultsTable resultsTable ) return table; } - public static Character guessDelimiter( String path ) + public static Character determineDelimiter( String path ) { if ( path.endsWith( ".txt" ) ) return '\t'; @@ -184,7 +187,22 @@ public static Character guessDelimiter( String path ) if ( path.endsWith( ".tsv" ) ) return '\t'; - return ','; + try (BufferedReader reader = new BufferedReader(new FileReader(path))) { + String firstLine = reader.readLine(); + if (firstLine != null) { + if (firstLine.contains("\t")) { + return '\t'; + } else if (firstLine.contains(",")) { + return ','; + } else { + return ','; // We could also throw and error, but maybe there is only one column ? + } + } + } catch (IOException e) { + throw new RuntimeException( e ); + } + + throw new RuntimeException( "Could not determine table delimiter" ); } public static Table openDelimitedTextFile( String path, char separator ) @@ -253,6 +271,6 @@ private static String dealWithTwoHeaderRowsIfNeeded( char separator, String cont public static Table openDelimitedTextFile( String path ) { - return openDelimitedTextFile( path, guessDelimiter( path ) ); + return openDelimitedTextFile( path, determineDelimiter( path ) ); } } diff --git a/src/main/java/org/embl/mobie/lib/table/saw/TableSawAnnotatedSegmentCreator.java b/src/main/java/org/embl/mobie/lib/table/saw/TableSawAnnotatedSegmentCreator.java index 694cd396f..8f2909019 100644 --- a/src/main/java/org/embl/mobie/lib/table/saw/TableSawAnnotatedSegmentCreator.java +++ b/src/main/java/org/embl/mobie/lib/table/saw/TableSawAnnotatedSegmentCreator.java @@ -30,6 +30,7 @@ import net.imglib2.FinalRealInterval; import org.embl.mobie.lib.table.TableDataFormat; +import org.embl.mobie.lib.table.columns.MicrogliaSegmentColumnNames; import org.embl.mobie.lib.table.columns.SegmentColumnNames; import tech.tablesaw.api.Table; @@ -97,8 +98,6 @@ private synchronized void initColumns( Table table ) is3D = anchorColumnIndices.length == 3 && anchorColumnIndices[ 2 ] > -1; hasBoundingBox = bbMinColumnIndices[ 0 ] > -1; - - } @Override @@ -115,6 +114,9 @@ public TableSawAnnotatedSegment create( TableSawAnnotationTableModel< TableSawAn int timePoint = timePointColumnIndex > -1 ? table.intColumn( timePointColumnIndex ).get( rowIndex ) : 0; + if ( timePointColumnIndex > -1 && segmentColumnNames.timePointsAreOneBased() ) + timePoint -= 1; + final FinalRealInterval boundingBox = boundingBox( table, rowIndex ); // TODO do we want to support missing anchor columns? diff --git a/src/test/java/projects/OpenVeronicaMicroglia.java b/src/test/java/projects/OpenVeronicaMicroglia.java new file mode 100644 index 000000000..800903e29 --- /dev/null +++ b/src/test/java/projects/OpenVeronicaMicroglia.java @@ -0,0 +1,22 @@ +package projects; + +import net.imagej.ImageJ; +import org.embl.mobie.MoBIE; +import org.embl.mobie.command.open.OpenTableCommand; + +import java.io.File; + +public class OpenVeronicaMicroglia +{ + public static void main( String[] args ) + { + new ImageJ().ui().showUI(); + final OpenTableCommand command = new OpenTableCommand(); + command.root = new File( "/Users/tischer/Desktop/veronica" ); + command.table = new File( "/Users/tischer/Desktop/veronica/Intensities c3 pup4 lps.csv" ); + command.images = "Path_Intensities=Signal"; + command.labels = "Path_LabelMasks=Segmentations,Path_Skeletons=Skeletons,Path_Annotations=Annotations"; + command.removeSpatialCalibration = false; + command.run(); + } +}