Skip to content

Commit

Permalink
Merge pull request #44 from saalfeldlab/mixedFCOrderMultiscales
Browse files Browse the repository at this point in the history
 Better loading of arbitrarily axis-permuted data
  • Loading branch information
bogovicj authored May 15, 2024
2 parents bd347c6 + 43a70e0 commit 1fb275e
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 36 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/platform-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: test

on:
push:
branches:
- master
tags:
- "*-[0-9]+.*"
pull_request:
branches:
- master
workflow_dispatch:

jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install blosc (Windows)
if: matrix.os == 'windows-latest'
run: |
pip install blosc --no-input --target src/test/resources
mv src/test/resources/bin/* src/test/resources
- name: Install blosc (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
pip install blosc --no-input --target src/test/resources
mv src/test/resources/lib64/* src/test/resources
- name: Set up Java
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'zulu'
cache: 'maven'
- name: Maven Test
run: mvn -B clean test --file pom.xml
30 changes: 23 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,18 @@

<alphanumeric-comparator.version>1.4.1</alphanumeric-comparator.version>

<bigdataviewer-core.version>10.4.13</bigdataviewer-core.version>
<bigdataviewer-vistools.version>1.0.0-beta-34</bigdataviewer-vistools.version>

<n5.version>3.2.0</n5.version>
<n5-aws-s3.version>4.1.2</n5-aws-s3.version>
<n5-blosc.version>1.1.1</n5-blosc.version>
<n5-google-cloud.version>4.1.0</n5-google-cloud.version>
<n5-hdf5.version>2.2.0</n5-hdf5.version>
<n5-ij.version>4.1.3</n5-ij.version>
<n5-universe.version>1.4.3</n5-universe.version>
<n5-ij.version>4.2.0</n5-ij.version>
<n5-universe.version>1.5.0</n5-universe.version>
<n5-zarr.version>1.3.2</n5-zarr.version>
<n5-zstandard.version>1.0.2</n5-zstandard.version>
<n5-zarr.version>1.3.1</n5-zarr.version>
<bigdataviewer-core.version>10.4.13</bigdataviewer-core.version>
<bigdataviewer-vistools.version>1.0.0-beta-34</bigdataviewer-vistools.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -195,12 +199,24 @@
<dependency>
<groupId>org.janelia.saalfeldlab</groupId>
<artifactId>n5-google-cloud</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.janelia.saalfeldlab</groupId>
<artifactId>n5-aws-s3</artifactId>
<version>4.1.2</version>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.janelia.saalfeldlab</groupId>
<artifactId>n5-universe</artifactId>
<version>${n5-universe.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down
92 changes: 63 additions & 29 deletions src/main/java/org/janelia/saalfeldlab/n5/bdv/N5Viewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
import net.imglib2.converter.Converter;
import net.imglib2.converter.Converters;
import net.imglib2.img.basictypeaccess.AccessFlags;
import net.imglib2.realtransform.AffineGet;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.NativeType;
import net.imglib2.type.label.LabelMultisetType;
Expand Down Expand Up @@ -190,14 +191,6 @@ public <T extends NumericType<T> & NativeType<T>, V extends Volatile<T> & Numeri
Prefs.showScaleBar(true);

this.sharedQueue = new SharedQueue(Math.max(1, Runtime.getRuntime().availableProcessors() / 2));

// TODO: These setups are not used anymore, because BdvFunctions creates
// its own.
// They either need to be deleted from here or integrated somehow.
final List<ConverterSetup> converterSetups = new ArrayList<>();

final List<SourceAndConverter<T>> sourcesAndConverters = new ArrayList<>();

final List<N5Metadata> selected = new ArrayList<>();
for (final N5Metadata meta : dataSelection.metadata) {
if (meta instanceof N5ViewerMultichannelMetadata) {
Expand Down Expand Up @@ -271,7 +264,7 @@ public static BdvHandle show(final String uri) {

try {
return show(new N5URI(uri));
} catch (URISyntaxException e) {
} catch (final URISyntaxException e) {
e.printStackTrace();
}
return null;
Expand Down Expand Up @@ -321,12 +314,12 @@ public static <T extends NumericType<T> & NativeType<T>> BdvHandle show(final St
final HashMap<N5Reader,List<String>> selectionsByContainer = new HashMap<>();

final N5ViewerReaderFun n5fun = new N5ViewerReaderFun();
for( String uri : uris )
for( final String uri : uris )
{
N5URI n5uri;
try {
n5uri = new N5URI(uri);
} catch (URISyntaxException e) {
} catch (final URISyntaxException e) {
System.err.println("Could not parse url: " + uri);
continue;
}
Expand All @@ -347,7 +340,7 @@ public static <T extends NumericType<T> & NativeType<T>> BdvHandle show(final St
}

// if this is called, can assume metadata have not been parsed yet. so parse now - once for each container.
for( N5Reader n5 : selectionsByContainer.keySet())
for( final N5Reader n5 : selectionsByContainer.keySet())
{
final N5TreeNode containerRoot = N5DatasetDiscoverer.discover(n5,
Arrays.asList(N5ViewerCreator.n5vParsers),
Expand All @@ -365,7 +358,7 @@ public static <T extends NumericType<T> & NativeType<T>> BdvHandle show(final St
try {
numTimepoints = Math.max(numTimepoints,
buildN5Sources(n5, selection, sharedQueue, converterSetups, sourcesAndConverters, options));
} catch (IOException e) {
} catch (final IOException e) {
System.err.println("Could not load from: " + n5.getURI().toString());
}
}
Expand All @@ -391,7 +384,7 @@ public static <T extends NumericType<T> & NativeType<T>> BdvHandle show(N5Reader
sourcesAndConverters,
options);

} catch (IOException e1) {
} catch (final IOException e1) {
e1.printStackTrace();
return null;
}
Expand Down Expand Up @@ -427,7 +420,7 @@ public static <T extends NumericType<T> & NativeType<T>> BdvHandle show(final Li
}
}

BdvHandle bdv = bdvHandle;
final BdvHandle bdv = bdvHandle;
if (bdv != null) {
final ViewerPanel viewerPanel = bdv.getViewerPanel();
if (viewerPanel != null) {
Expand Down Expand Up @@ -499,7 +492,7 @@ public static <T extends NumericType<T> & NativeType<T>, V extends Volatile<T> &
sharedQueue, converterSetups, sourcesAndConverters, options);
}

public static <T extends NumericType<T> & NativeType<T>, V extends Volatile<T> & NumericType<V>> int buildN5Sources(
public static <T extends NumericType<T> & NativeType<T>, V extends Volatile<T> & NumericType<V>, M extends AxisMetadata & N5Metadata> int buildN5Sources(
final N5Reader n5,
final List<N5Metadata> selectedMetadata,
final SharedQueue sharedQueue,
Expand All @@ -521,7 +514,8 @@ public static <T extends NumericType<T> & NativeType<T>, V extends Volatile<T> &
final N5Metadata metadata = selectedMetadata.get(i);
final String srcName = metadata.getName();

// TODO: simplify this if/elseif block: much of these if cases can be combined

// TODO: simplify this if/elseif block: much of these ifwall cases can be combined
if (metadata instanceof N5SingleScaleMetadata) {
final N5SingleScaleMetadata singleScaleDataset = (N5SingleScaleMetadata)metadata;
final String[] tmpDatasets = new String[]{singleScaleDataset.getPath()};
Expand Down Expand Up @@ -589,7 +583,7 @@ public static <T extends NumericType<T> & NativeType<T>, V extends Volatile<T> &
final RandomAccessibleInterval< ? > imagejImg;
if (metadata instanceof AxisMetadata)
{
imagejImg = AxisUtils.permuteForImagePlus( img, (AxisMetadata)metadata );
imagejImg = AxisUtils.permuteForImagePlus(img, (M)metadata);
unit = unitFromAxes(((AxisMetadata)metadata).getAxes());
}
else if( metadata instanceof N5SingleScaleMetadata )
Expand All @@ -608,14 +602,14 @@ else if( isCosemMultiscale(metadata))
{
final N5CosemMultiScaleMetadata cosemMulti = ((N5CosemMultiScaleMetadata)metadata);
final N5CosemMetadata cosemMeta = cosemMulti.getChildrenMetadata()[0];
imagejImg = AxisUtils.permuteForImagePlus(img, cosemMeta);
imagejImg = permuteForImagePlus(img, transforms[s], cosemMeta);
unit = cosemMeta.unit();
}
else
{
final NgffSingleScaleAxesMetadata ngffMeta = isNgffMultiscale(metadata);
if( ngffMeta != null ) {
imagejImg = AxisUtils.permuteForImagePlus(img, ngffMeta);
imagejImg = permuteForImagePlus(img, transforms[s], ngffMeta);
unit = ngffMeta.unit();
}
else
Expand Down Expand Up @@ -650,8 +644,7 @@ else if( isCosemMultiscale(metadata))
@SuppressWarnings("unchecked")
final T type = (T)Util.getTypeFromInterval(images[0]);

// TODO this could / should be generalized
// resolutions
// this could / should be generalized
final double rx = transforms[0].get(0, 0);
final double ry = transforms[0].get(1, 1);
final double rz = transforms[0].get(2, 2);
Expand Down Expand Up @@ -684,6 +677,44 @@ else if( isCosemMultiscale(metadata))
return numTimepoints;
}

/**
* Returns an image with dimensions in a canonical order XYCZY. Also
* permutes the given pixel to physical transform in-place.
*
* @param <T>
* the type
* @param img
* the image
* @param transform
* the pixel to physical transfom
* @param meta
* axis metadata
* @return a possibly permuted image
*/
protected static <T, M extends N5Metadata, A extends AxisMetadata & N5Metadata> RandomAccessibleInterval<T> permuteForImagePlus(
final RandomAccessibleInterval<T> img,
AffineTransform3D transform,
final A meta) {

final int[] p = AxisUtils.findImagePlusPermutation(meta);
AxisUtils.fillPermutation(p);

RandomAccessibleInterval<T> imgTmp = img;
while (imgTmp.numDimensions() < 5)
imgTmp = Views.addDimension(imgTmp, 0, 0);

if (AxisUtils.isIdentityPermutation(p))
return imgTmp;

// update spatial transformation
// exchange rows and columns of permutation matrix appropriately
final int[] spatialPermutation = new int[]{p[0], p[1], p[3]};
final AffineGet permTform = AxisUtils.axisPermutationTransform(spatialPermutation);
transform.concatenate(permTform.inverse()).preConcatenate(permTform);

return AxisUtils.permute(imgTmp, AxisUtils.invertPermutation(p));
}

/*
* If the image is of type {@link LabelMultisetType} to {@link UnsignedLongType}.
*/
Expand All @@ -704,16 +735,19 @@ protected static <T extends NumericType<T> & NativeType<T>> RandomAccessibleInte
// return (RandomAccessibleInterval<T>)convertLabelMultisetVolatile(
// (CachedCellImg<LabelMultisetType, ?>)img);
}
return (RandomAccessibleInterval<T>)img;

if (OmeNgffMultiScaleMetadata.fOrder(n5.getDatasetAttributes(dataset)))
return AxisUtils.reverseDimensions(img);
else
return (RandomAccessibleInterval<T>)img;
}

private static RandomAccessibleInterval<VolatileUnsignedLongType> convertLabelMultisetVolatile( final CachedCellImg<LabelMultisetType,?> lmsImg ) {

// TODO this isn't working (VolatileViews throws a NPE), but have not yet investigated why
System.out.println( "convert volatile");
// see ViewCosem in n5-utils for something similar

RandomAccessibleInterval<Volatile<LabelMultisetType>> vimg = VolatileViews.wrapAsVolatile( lmsImg );
final RandomAccessibleInterval<Volatile<LabelMultisetType>> vimg = VolatileViews.wrapAsVolatile( lmsImg );
return Converters.convert2(vimg,
(a, b) -> {
b.set(a.get().argMax());
Expand Down Expand Up @@ -793,10 +827,10 @@ private static boolean isCosemMultiscale( final N5Metadata metadata )
return false;
}

private static NgffSingleScaleAxesMetadata isNgffMultiscale( final N5Metadata metadata )
{
if(metadata instanceof OmeNgffMetadata )
{
private static NgffSingleScaleAxesMetadata isNgffMultiscale(final N5Metadata metadata) {

if (metadata instanceof OmeNgffMetadata) {

final OmeNgffMetadata ngff = (OmeNgffMetadata)metadata;
final OmeNgffMultiScaleMetadata[] ms = ngff.multiscales;

Expand Down
Loading

0 comments on commit 1fb275e

Please sign in to comment.