Skip to content

Commit

Permalink
Merge pull request #1129 from mobie/fix-linking
Browse files Browse the repository at this point in the history
Fix linking
  • Loading branch information
tischi authored Apr 29, 2024
2 parents fe4b0e2 + ce7a9cb commit d4f4682
Show file tree
Hide file tree
Showing 10 changed files with 401 additions and 217 deletions.
9 changes: 6 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<n5-zarr.version>1.3.1</n5-zarr.version>
<n5-hdf5.version>2.2.0</n5-hdf5.version>
<n5-ij.version>4.1.1</n5-ij.version>
<n5-universe.version>1.4.1</n5-universe.version>
<n5-universe.version>1.4.3-SNAPSHOT</n5-universe.version>
<n5-aws-s3.version>4.1.1</n5-aws-s3.version>
<bigdataviewer-core.version>10.4.13</bigdataviewer-core.version>
<bigdataviewer-vistools.version>1.0.0-beta-34</bigdataviewer-vistools.version>
Expand Down Expand Up @@ -248,13 +248,16 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.4.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.4.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
41 changes: 31 additions & 10 deletions src/main/java/org/embl/mobie/lib/create/DatasetSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -61,7 +62,7 @@ public class DatasetSerializer
ProjectCreator projectCreator;

/**
* Make a datasetJsonCreator - includes all functions for creating and modifying metadata
* Make a DatasetSerializer - includes all functions for creating and modifying metadata
* stored in dataset Json files
* @param projectCreator projectCreator
*/
Expand All @@ -83,6 +84,7 @@ public void addDataset( String datasetName, boolean is2D ) {
/**
* Add named image to dataset Json (including a view with the image name and sensible defaults)
* @param imageName image name
* @param imageFile image file
* @param datasetName dataset name
* @param uiSelectionGroup name of ui selection group to add image view to i.e. the name of the MoBIE dropdown
* menu it will appear in
Expand All @@ -93,6 +95,7 @@ public void addDataset( String datasetName, boolean is2D ) {
* @param sourceTransform affine transform of image view
*/
public void addImage(String imageName,
File imageFile,
String datasetName,
String uiSelectionGroup,
double[] contrastLimits,
Expand All @@ -102,7 +105,7 @@ public void addImage(String imageName,
{
Dataset dataset = fetchDataset( datasetName );

addNewImageSource( dataset, imageName );
addNewImageSource( dataset, datasetName, imageName, imageFile );
if ( uiSelectionGroup != null ) {
// add a view with the same name as the image, and sensible defaults
addNewImageView( dataset, imageName, uiSelectionGroup, contrastLimits, colour, exclusive, sourceTransform );
Expand All @@ -119,17 +122,23 @@ public void addImage(String imageName,
/**
* Add named segmentation to dataset Json (including a view with the segmentation name and sensible defaults)
* @param imageName segmentation name
* @param imageFile segmentation file
* @param datasetName dataset name
* @param uiSelectionGroup name of ui selection group to add segmentation view to i.e. the name of the MoBIE
* dropdown menu it will appear in
* @param exclusive whether the segmentation view is exclusive or not i.e. when viewed, does it first
* remove all current images from the viewer?
* @param sourceTransform affine transform of segmentation view
*/
public void addSegmentation(String imageName, String datasetName, String uiSelectionGroup, boolean exclusive, AffineTransform3D sourceTransform ) {
public void addSegmentation(String imageName,
File imageFile,
String datasetName,
String uiSelectionGroup,
boolean exclusive,
AffineTransform3D sourceTransform ) {
Dataset dataset = fetchDataset( datasetName );

addNewSegmentationSource( dataset, imageName );
addNewSegmentationSource( dataset, datasetName, imageName, imageFile );
if ( uiSelectionGroup != null ) {
// add a view with the same name as the image, and sensible defaults
addNewSegmentationView( dataset, imageName, uiSelectionGroup, exclusive, sourceTransform );
Expand Down Expand Up @@ -161,16 +170,16 @@ private Dataset fetchDataset( String datasetName )
return dataset;
}

private void addNewImageSource( Dataset dataset, String imageName )
private void addNewImageSource( Dataset dataset, String datasetName, String imageName, File imageFile )
{
Map< ImageDataFormat, StorageLocation > imageDataLocations;
ImageDataSource imageSource = new ImageDataSource();
imageDataLocations = createImageDataLocations( imageName );
imageDataLocations = createImageDataLocations( datasetName, imageFile );
imageSource.imageData = imageDataLocations;
dataset.sources().put( imageName, imageSource );
}

private void addNewSegmentationSource( Dataset dataset, String imageName )
private void addNewSegmentationSource( Dataset dataset, String datasetName, String imageName, File imageFile )
{
Map< ImageDataFormat, StorageLocation > imageDataLocations;

Expand All @@ -180,17 +189,29 @@ private void addNewSegmentationSource( Dataset dataset, String imageName )
tableStorageLocation.relativePath = "tables/" + imageName;
annotatedLabelMaskSource.tableData.put( TableDataFormat.TSV, tableStorageLocation );

imageDataLocations = createImageDataLocations( imageName );
imageDataLocations = createImageDataLocations( datasetName, imageFile );
annotatedLabelMaskSource.imageData = imageDataLocations;

dataset.sources().put( imageName, annotatedLabelMaskSource );
}

private Map< ImageDataFormat, StorageLocation > createImageDataLocations( String imageName )
private Map< ImageDataFormat, StorageLocation > createImageDataLocations( String datasetName, File imageFile )
{
Map< ImageDataFormat, StorageLocation > imageDataLocations = new HashMap<>();
StorageLocation imageStorageLocation = new StorageLocation();
imageStorageLocation.relativePath = "images" + File.separator + imageName + ".ome.zarr";

// if image file is inside project dir, then use a relative path. Otherwise, use an absolute path.
File projectDir = projectCreator.getProjectLocation();
File datasetDir = new File(projectDir, datasetName);
if (ProjectCreatorHelper.pathIsInsideDir(imageFile, projectDir)) {
String relativePath = datasetDir.toPath().relativize(imageFile.toPath()).toString();
// force use of forward slashes in relative paths (even on windows). Windows can handle / or \ in relative
// paths, so this means they'll work for all OS.
imageStorageLocation.relativePath = relativePath.replaceAll("\\\\", "/");
} else {
imageStorageLocation.absolutePath = imageFile.getAbsolutePath();
}

imageDataLocations.put( ImageDataFormat.OmeZarr, imageStorageLocation );
return imageDataLocations;
}
Expand Down
117 changes: 88 additions & 29 deletions src/main/java/org/embl/mobie/lib/create/ImagesCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import ij.IJ;
import ij.ImagePlus;
import ij.process.LUT;
import net.imagej.patcher.LegacyInjector;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.converter.RealTypeConverters;
import net.imglib2.realtransform.AffineTransform3D;
Expand Down Expand Up @@ -67,7 +68,7 @@
*/
public class ImagesCreator {

static { net.imagej.patcher.LegacyInjector.preinit(); }
static { LegacyInjector.preinit(); }

ProjectCreator projectCreator;

Expand Down Expand Up @@ -106,17 +107,37 @@ public boolean imageExists( String datasetName, String imageName ) {
return new File (filePath).exists();
}

/**
* Add ImagePlus image to MoBIE project.
*
* @param imp image
* @param imageName image name
* @param datasetName dataset name
* @param imageType image type i.e. image or segmentation
* @param sourceTransform Affine transform of image
* @param uiSelectionGroup name of ui selection group to add image view to i.e. the name of the MoBIE dropdown
* menu it will appear in
* @param exclusive whether the image view is exclusive or not i.e. when viewed, does it first remove all current
* images from the viewer?
* @param overwrite whether to overwrite any existing images inside the dataset with the same name
*/
public void addImage( ImagePlus imp,
String imageName,
String datasetName,
ProjectCreator.ImageType imageType,
AffineTransform3D sourceTransform,
String uiSelectionGroup,
boolean exclusive )
boolean exclusive,
boolean overwrite )
{
String filePath = getDefaultLocalImagePath( datasetName, imageName );
File imageFile = new File(filePath);

if (imageFile.exists() && !overwrite) {
throw new UnsupportedOperationException("An image called " + imageName + "already exists in the dataset " +
datasetName + ", and overwrite is set to false" );
}

if ( ! isImageValid( imp.getNChannels(), imp.getCalibration().getUnit(), projectCreator.getVoxelUnit(), false ) )
{
return;
Expand All @@ -132,25 +153,19 @@ public void addImage( ImagePlus imp,
projectCreator.setVoxelUnit( imp.getCalibration().getUnit() );
}

// Done by N5?!
// File imageDir = new File(imageFile.getParent());
// if ( ! imageDir.exists() )
// {
// imageDir.mkdirs();
// }


OMEZarrWriter.write( imp, filePath, getImageType( imageType ), true );
OMEZarrWriter.write( imp, filePath, getImageType( imageType ), overwrite );

// check image written successfully, before writing JSONs
if ( imageFile.exists() ) {
if (imageType == ProjectCreator.ImageType.Image ) {
double[] contrastLimits = new double[]{imp.getDisplayRangeMin(), imp.getDisplayRangeMax()};
LUT lut = imp.getLuts()[ 0 ];
String colour = ColorHelper.getString( lut );
updateTableAndJsonsForNewImage( imageName, datasetName, uiSelectionGroup, contrastLimits, colour, exclusive, sourceTransform );
updateTableAndJsonsForNewImage( imageName, imageFile, datasetName, uiSelectionGroup, contrastLimits,
colour, exclusive, sourceTransform );
} else {
updateTableAndJsonsForNewSegmentation(imageName, datasetName, uiSelectionGroup, exclusive, sourceTransform );
updateTableAndJsonsForNewSegmentation(imageName, imageFile, datasetName, uiSelectionGroup,
exclusive, sourceTransform );
}
}

Expand Down Expand Up @@ -239,13 +254,30 @@ private ArrayList<Object[]> computeObjectFeatures( Source< ? > labelsSource, int
return rows;
}

/**
* Add existing ome-zarr image to MoBIE project.
*
* @param uri ome-zarr uri
* @param imageName image name
* @param datasetName dataset name
* @param imageType image type i.e. image or segmentation
* @param addMethod link or copy the image - link (leave image as-is, and link to this location. Linking to images
* outside of the project folder is only supported for local projects),
* copy (copy image into project)
* @param uiSelectionGroup name of ui selection group to add image view to i.e. the name of the MoBIE dropdown
* menu it will appear in
* @param exclusive whether the image view is exclusive or not i.e. when viewed, does it first remove all current
* images from the viewer?
* @param overwrite whether to overwrite any existing images inside the dataset with the same name
*/
public void addOMEZarrImage( String uri,
String imageName,
String datasetName,
ProjectCreator.ImageType imageType,
ProjectCreator.AddMethod addMethod,
String uiSelectionGroup,
boolean exclusive )
boolean exclusive,
boolean overwrite )
{
File imagesDirectory = new File( getDefaultLocalImageDirPath( datasetName ) );

Expand All @@ -260,40 +292,53 @@ public void addOMEZarrImage( String uri,
throw new UnsupportedOperationException("Can't add a 3D image to a 2D dataset" );
}

if ( projectCreator.getVoxelUnit() == null ) {
projectCreator.setVoxelUnit( imageData.getSourcePair( 0 ).getB().getVoxelDimensions().unit() );
// If an image of the same name is inside the project, delete it when overwrite=True.
File oldImageFile = new File(imagesDirectory, imageName + ".ome.zarr" );
if (oldImageFile.exists()) {
if (overwrite) {
IJ.log("Overwriting image " + imageName + " in dataset " + datasetName );
deleteImageFiles( datasetName, imageName );
} else {
throw new UnsupportedOperationException("An image called " + imageName + "already exists in the dataset " +
datasetName + ", and overwrite is set to false" );
}
}

File newImageFile = new File(imagesDirectory, imageName + ".ome.zarr" );
if ( newImageFile.exists() ) {
IJ.log("Overwriting image " + imageName + " in dataset " + datasetName );
deleteImageFiles( datasetName, imageName );
if ( projectCreator.getVoxelUnit() == null ) {
projectCreator.setVoxelUnit( imageData.getSourcePair( 0 ).getB().getVoxelDimensions().unit() );
}

File newImageFile;
switch (addMethod) {
case Link:
// Do nothing, the absolute path to the linked image will be added to the dataset.json
// Do nothing, the relative or absolute path to the linked image will be added to the dataset.json
newImageFile = new File(uri);
break;
case Copy:
copyImage( uri, imagesDirectory, imageName);
newImageFile = copyImage( uri, imagesDirectory, imageName);
break;
default:
throw new IllegalStateException("Unexpected value: " + addMethod);
}

if ( imageType == ProjectCreator.ImageType.Image ) {
updateTableAndJsonsForNewImage( imageName, datasetName, uiSelectionGroup, new double[]{0.0, 255.0}, "white", exclusive, new AffineTransform3D() );
updateTableAndJsonsForNewImage( imageName, newImageFile, datasetName, uiSelectionGroup,
new double[]{0.0, 255.0}, "white", exclusive, new AffineTransform3D() );
} else {
updateTableAndJsonsForNewSegmentation( imageName, datasetName, uiSelectionGroup, exclusive, new AffineTransform3D() );
updateTableAndJsonsForNewSegmentation( imageName, newImageFile,
datasetName, uiSelectionGroup, exclusive, new AffineTransform3D() );
}

IJ.log( imageName + " added to project" );
}

private void copyImage( String uri, File imagesDir, String imageName )
private File copyImage( String uri, File imagesDir, String imageName )
{
try
{
File destination = new File( imagesDir, imageName + ".ome.zarr" );
FileUtils.copyDirectory( new File( uri ), destination);
return destination;
}
catch ( IOException e )
{
Expand Down Expand Up @@ -353,14 +398,28 @@ private void addDefaultTableForImage ( String imageName, String datasetName ) {
}


private void updateTableAndJsonsForNewImage ( String imageName, String datasetName, String uiSelectionGroup, double[] contrastLimits, String colour, boolean exclusive, AffineTransform3D sourceTransform ) {
private void updateTableAndJsonsForNewImage ( String imageName,
File imageFile,
String datasetName,
String uiSelectionGroup,
double[] contrastLimits,
String colour,
boolean exclusive,
AffineTransform3D sourceTransform ) {
DatasetSerializer datasetSerializer = projectCreator.getDatasetJsonCreator();
datasetSerializer.addImage( imageName, datasetName, uiSelectionGroup, contrastLimits, colour, exclusive, sourceTransform );
datasetSerializer.addImage( imageName, imageFile, datasetName, uiSelectionGroup, contrastLimits, colour,
exclusive, sourceTransform );
}

private void updateTableAndJsonsForNewSegmentation( String imageName, String datasetName, String uiSelectionGroup, boolean exclusive, AffineTransform3D sourceTransform ) {
private void updateTableAndJsonsForNewSegmentation( String imageName,
File imageFile,
String datasetName,
String uiSelectionGroup,
boolean exclusive,
AffineTransform3D sourceTransform ) {
addDefaultTableForImage( imageName, datasetName );
DatasetSerializer datasetSerializer = projectCreator.getDatasetJsonCreator();
datasetSerializer.addSegmentation( imageName, datasetName, uiSelectionGroup, exclusive, sourceTransform );
datasetSerializer.addSegmentation( imageName, imageFile, datasetName, uiSelectionGroup,
exclusive, sourceTransform );
}
}
3 changes: 1 addition & 2 deletions src/main/java/org/embl/mobie/lib/create/ProjectCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ public enum ImageType {

public enum AddMethod {
Link,
Copy,
move
Copy
}

/**
Expand Down
Loading

0 comments on commit d4f4682

Please sign in to comment.