Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: build ngff multiscales v0.4 metadata #15

Merged
merged 8 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ public class MetadataUtils {

public static double[] mul(final double[] a, final double[] b) {

final double[] out = new double[ a.length ];
for( int i = 0; i < a.length; i++ )
final double[] out = new double[a.length];
for (int i = 0; i < a.length; i++)
out[i] = a[i] * b[i];

return out;
}

public static double[] mul(final double[] a, final long[] b) {

final double[] out = new double[ a.length ];
for( int i = 0; i < a.length; i++ )
final double[] out = new double[a.length];
for (int i = 0; i < a.length; i++)
out[i] = a[i] * b[i];

return out;
Expand Down Expand Up @@ -91,21 +91,22 @@ public static long[] updateDownsamplingFactors(final long factor, final long[] b
return factors;
}

public static CoordinateTransformation<?>[] buildScaleTranslationTransformList( final double[] scale, final double[] translation ) {
public static CoordinateTransformation<?>[] buildScaleTranslationTransformList(final double[] scale, final double[] translation) {

int nTforms = 0;
if( scale != null )
if (scale != null)
nTforms++;

if( translation != null )
if (translation != null)
nTforms++;

final CoordinateTransformation<?>[] coordinateTransformations = new CoordinateTransformation<?>[nTforms];

int i = 0;
if( scale != null )
if (scale != null)
coordinateTransformations[i++] = new ScaleCoordinateTransformation(scale);

if( translation != null )
if (translation != null)
coordinateTransformations[i++] = new TranslationCoordinateTransformation(translation);

return coordinateTransformations;
Expand All @@ -120,64 +121,64 @@ public static CoordinateTransformation<?>[] buildScaleTranslationTransformList(
* @param datasetMetadata dataset metadata
* @return the single scale metadata
*/
public static N5SingleScaleMetadata setDatasetAttributes( final N5SingleScaleMetadata baseMetadata, final N5DatasetMetadata datasetMetadata )
{
if( baseMetadata.getPath().equals( datasetMetadata.getPath() ))
return new N5SingleScaleMetadata( baseMetadata.getPath(), baseMetadata.spatialTransform3d(),
public static N5SingleScaleMetadata setDatasetAttributes(final N5SingleScaleMetadata baseMetadata, final N5DatasetMetadata datasetMetadata) {

if (baseMetadata.getPath().equals(datasetMetadata.getPath()))
return new N5SingleScaleMetadata(baseMetadata.getPath(), baseMetadata.spatialTransform3d(),
baseMetadata.getDownsamplingFactors(), baseMetadata.getPixelResolution(), baseMetadata.getOffset(),
baseMetadata.unit(), datasetMetadata.getAttributes() );
baseMetadata.unit(), datasetMetadata.getAttributes());
else
return null;
}

public static N5SingleScaleMetadata[] updateChildrenDatasetAttributes( final N5SingleScaleMetadata[] baseMetadata, final N5DatasetMetadata[] datasetMetadata )
{
final HashMap<String,N5SingleScaleMetadata> bases = new HashMap<>();
Arrays.stream( baseMetadata ).forEach( x -> { bases.put( x.getPath(), x ); } );
public static N5SingleScaleMetadata[] updateChildrenDatasetAttributes(final N5SingleScaleMetadata[] baseMetadata,
final N5DatasetMetadata[] datasetMetadata) {

final HashMap<String, N5SingleScaleMetadata> bases = new HashMap<>();
Arrays.stream(baseMetadata).forEach(x -> {
bases.put(x.getPath(), x);
});

return ( N5SingleScaleMetadata[] ) Arrays.stream( datasetMetadata ).map( x -> {
final N5SingleScaleMetadata b = bases.get( x.getPath() );
if( b == null )
return (N5SingleScaleMetadata[])Arrays.stream(datasetMetadata).map(x -> {
final N5SingleScaleMetadata b = bases.get(x.getPath());
if (b == null)
return null;
else
return setDatasetAttributes( b, x );
} ).filter( x -> x != null ).toArray();
return setDatasetAttributes(b, x);
}).filter(x -> x != null).toArray();
}

public static void updateChildrenMetadata( final N5TreeNode parent, final N5Metadata[] childrenMetadata,
final boolean relative )
{
final HashMap<String,N5Metadata> children = new HashMap<>();
Arrays.stream( childrenMetadata ).forEach( x -> {
public static void updateChildrenMetadata(final N5TreeNode parent, final N5Metadata[] childrenMetadata,
final boolean relative) {

final HashMap<String, N5Metadata> children = new HashMap<>();
Arrays.stream(childrenMetadata).forEach(x -> {
final String absolutePath;
if( relative )
{
if (relative) {
absolutePath = normalizeGroupPath(parent.getPath() + "/" + x.getPath());
} else {
absolutePath = x.getPath();
}
children.put( absolutePath, x );
children.put(absolutePath, x);
});
parent.childrenList().forEach( c -> {
parent.childrenList().forEach(c -> {
final N5Metadata m = children.get(MetadataUtils.normalizeGroupPath(c.getPath()));
if( m != null )
c.setMetadata( m );
if (m != null)
c.setMetadata(m);
});
}

public static String canonicalPath( final N5TreeNode parent, final String child )
{
return canonicalPath( parent.getPath(), child );
public static String canonicalPath(final N5TreeNode parent, final String child) {

return canonicalPath(parent.getPath(), child);
}

public static String canonicalPath( final String parent, final String child )
{
try
{
final N5URI url = new N5URI( "?/" + parent + "/" + child );
public static String canonicalPath(final String parent, final String child) {

try {
final N5URI url = new N5URI("?/" + parent + "/" + child);
return url.normalizeGroupPath();
}
catch ( final URISyntaxException e ) { }
} catch (final URISyntaxException e) {}
return null;
}

Expand All @@ -191,21 +192,24 @@ public static String normalizeGroupPath(final String path) {
return path;
}

public static String relativePath( final String parent, final String child )
{
try
{
final N5URI purl = new N5URI( "?" + parent );
final N5URI curl = new N5URI( "?" + child );
final Path ppath = Paths.get( purl.normalizeGroupPath());
final Path cpath = Paths.get( curl.normalizeGroupPath());
return ppath.relativize(cpath).toString();
}
catch ( final URISyntaxException e )
{
e.printStackTrace();
}
return null;
/**
* Returns a relative group path from the child absolute path group path child
* the parent absolute group path.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bogovicj this text does not really seem to make sense.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe: Given an absolute child path and a parent path, it returns the child path relativized with respect to the parent path. If the parent is in fact not a parent of the child, the child path is return as is.

*
* If the child path is not a descendent of parent, child will be returned.
*
* @param parent an absolute path
* @param child an absolute path
* @return relative path from child to parent, if it exists.
*/
public static String relativePath(final String parent, final String child) {

try {
final String purl = new N5URI("?" + parent).normalizeGroupPath();
final String curl = new N5URI("?" + child).normalizeGroupPath();
return new N5URI("?" + curl.replaceFirst("^"+purl, "")).normalizeGroupPath();
} catch (final URISyntaxException e) {}
return child;
}

/**
Expand All @@ -215,13 +219,13 @@ public static String relativePath( final String parent, final String child )
* @param d exponent
* @return result
*/
public static double[] pow( final double[] x, final int d )
{
final double[] y = new double[ x.length ];
Arrays.fill( y, 1 );
for ( int i = 0; i < d; i++ )
for ( int j = 0; j < x.length; j++ )
y[ j ] *= x[ j ];
public static double[] pow(final double[] x, final int d) {

final double[] y = new double[x.length];
Arrays.fill(y, 1);
for (int i = 0; i < d; i++)
for (int j = 0; j < x.length; j++)
y[j] *= x[j];

return y;
}
Expand All @@ -234,6 +238,7 @@ public static double[] pow( final double[] x, final int d )
* @return a string
*/
public static String getStringNullable(final JsonElement element) {

if (element == null || element.isJsonNull())
return null;
else
Expand All @@ -252,10 +257,9 @@ public static AffineGet scaleTranslationTransforms(final double[] scale, final d

if (translation != null) {

if( scale != null ) {
if (scale != null) {
return new ScaleAndTranslation(scale, translation);
}
else {
} else {
// scale null, translation not null
if (translation.length == 2)
return new Translation2D(translation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
package org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04;

import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.DoubleStream;

Expand Down Expand Up @@ -139,11 +137,9 @@ private static OmeNgffDataset[] buildDatasets( final String path, final NgffSing
for( int i = 0; i < children.length; i++ )
{
datasets[i] = new OmeNgffDataset();
// final Path p = Paths.get(path);
// final Path c = Paths.get(children[i].getPath());
// datasets[i].path = p.relativize(c).toString();
datasets[i].path = Paths.get(path).relativize(Paths.get(children[i].getPath())).toString();
datasets[i].path = MetadataUtils.relativePath(path, children[i].getPath());
datasets[i].coordinateTransformations = children[i].getCoordinateTransformations();

}
return datasets;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04;

import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import org.janelia.saalfeldlab.n5.DatasetAttributes;
import org.janelia.saalfeldlab.n5.N5URI;
import org.janelia.saalfeldlab.n5.universe.metadata.MetadataUtils;
import org.janelia.saalfeldlab.n5.universe.metadata.axes.Axis;

Expand Down Expand Up @@ -55,8 +57,7 @@ public void addChild(final int idx, final NgffSingleScaleAxesMetadata child) {

final OmeNgffDataset dset = new OmeNgffDataset();
// paths are relative to this object
dset.path = Paths.get(getPath()).relativize(Paths.get(child.getPath())).toString();

dset.path = MetadataUtils.relativePath(getPath(), child.getPath());
dset.coordinateTransformations = child.getCoordinateTransformations();
if (idx < 0)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04;

import static org.junit.Assert.assertEquals;

import org.janelia.saalfeldlab.n5.N5URI;
import org.janelia.saalfeldlab.n5.universe.metadata.axes.Axis;
import org.janelia.saalfeldlab.n5.universe.metadata.axes.AxisUtils;
import org.junit.Test;

public class BuildMultiscaleTest {

@Test
public void buildNgffMultiscale() {

testHelper("", new String[]{"s0", "s1", "s2", "s3"});

testHelper("a", new String[]{"s0", "s1", "s2", "s3"});
testHelper("a/b", new String[]{"s0", "s1", "s2", "s3"});
testHelper("a/b/c", new String[]{"s0", "s1", "s2", "s3"});

testHelper("a", new String[]{"0/s0", "0/s1", "0/s2", "0/s3"});
}

private static void testHelper(final String path, final String[] childPaths) {

final String downsampleMethod = "sampling";
final Axis[] axes = AxisUtils.buildAxes("x", "y", "z");
final OmeNgffMultiScaleMetadataMutable ms = new OmeNgffMultiScaleMetadataMutable(path);

for (int i = 0; i < childPaths.length; i++) {
final double s = Math.pow(2, i);
ms.addChild(buildScaleLevelMetadata(childPaths[i], new double[]{s, s, s}, axes));
}

final OmeNgffMultiScaleMetadata meta = new OmeNgffMultiScaleMetadata(ms.getAxes().length,
path, path, downsampleMethod, "0.4",
ms.getAxes(),
ms.getDatasets(), null,
ms.coordinateTransformations, ms.metadata, true);

for (int i = 0; i < childPaths.length; i++) {
assertEquals(
String.format("multiscale path incorrect for root: %s, child: %s", path, childPaths[i]),
childPaths[i], meta.getDatasets()[i].path);
}

// test building children from multiscales
// these metadata's path variables must be relative to the root
final NgffSingleScaleAxesMetadata[] children = OmeNgffMultiScaleMetadata.buildMetadata(3, path, null, meta);
for (int i = 0; i < childPaths.length; i++) {
// ensure the paths are equal up to normalization
assertEquals(
String.format("single scale path incorrect for root: %s, child: %s", path, childPaths[i]),
N5URI.normalizeGroupPath(path + "/" + childPaths[i]),
N5URI.normalizeGroupPath(children[i].getPath()));
}
}

private static NgffSingleScaleAxesMetadata buildScaleLevelMetadata(final String path, final double[] res,
final Axis[] axes) {

return new NgffSingleScaleAxesMetadata(path, res, null, axes, null);
}

}
Loading