Skip to content

Instantly share code, notes, and snippets.

@bogovicj
Created April 16, 2024 01:53
Show Gist options
  • Save bogovicj/f8e4ffd7ce5b1d377565649a35d597d5 to your computer and use it in GitHub Desktop.
Save bogovicj/f8e4ffd7ce5b1d377565649a35d597d5 to your computer and use it in GitHub Desktop.
#@ File(styles="both") root
#@ String(value="/") datasetPath
/*
* If you used Fiji to export a multi-scale OME-Zarr image using average downsampling
* between approximately March 13 and April 15 2024, the translation metadata may be incorrect.
*
* Running this script will correct the metadata.
*
* John Bogovic
*/
String normDatasetPath = N5URI.normalizeGroupPath(datasetPath);
N5Writer n5 = new N5Factory()
.gsonBuilder(OmeNgffMetadataParser.gsonBuilder())
.openWriter(root.getAbsolutePath());
N5TreeNode treeRoot = N5DatasetDiscoverer.discover(n5,
Collections.singletonList(new N5GenericSingleScaleMetadataParser()),
Collections.singletonList(new OmeNgffMetadataParser()));
Optional<N5TreeNode> node = treeRoot.getDescendant(datasetPath == "/" ? "" : datasetPath);
if (node.isPresent()) {
N5Metadata metadata = node.get().getMetadata();
if (!(metadata instanceof OmeNgffMetadata)) {
System.err.println("Could not find NGFF metadata at: " + datasetPath);
return;
}
final OmeNgffMetadata ngffMeta = (OmeNgffMetadata)metadata;
ScaleCoordinateTransformation baseScale = null;
for( OmeNgffDataset dataset : ngffMeta.multiscales[0].datasets ) {
ValuePair<ScaleCoordinateTransformation, TranslationCoordinateTransformation> tforms = getTransformsMulti(dataset);
if( baseScale == null )
{
baseScale = tforms.getA();
continue;
}
correct(baseScale, tforms);
Optional<N5TreeNode> childNode = treeRoot.getDescendant(normDatasetPath + "/" + dataset.path);
if(childNode.isPresent())
{
N5Metadata childMetadata = childNode.get().getMetadata();
if( childMetadata instanceof NgffSingleScaleAxesMetadata )
{
NgffSingleScaleAxesMetadata ngffChildMetadata = (NgffSingleScaleAxesMetadata)childMetadata;
final ValuePair<ScaleCoordinateTransformation, TranslationCoordinateTransformation> tformsArray = getTransformsArray(ngffChildMetadata);
correct(baseScale, tformsArray);
n5.setAttribute(
N5URI.normalizeGroupPath(normDatasetPath + "/" + dataset.path),
"coordinateTransformations", ngffChildMetadata.getCoordinateTransformations());
}
}
}
// write the new metadata out
n5.setAttribute(normDatasetPath, "multiscales", ngffMeta.multiscales);
}
else {
System.err.println("could not find : " + datasetPath);
}
def correct(ScaleCoordinateTransformation baseScale, ValuePair tforms) {
EPS = 1e-3;
ScaleCoordinateTransformation scale = tforms.getA();
TranslationCoordinateTransformation translation = tforms.getB();
res = baseScale.getScale();
s = scale.getScale();
t = translation.getTranslation();
if( t[0] < 0.1 * EPS * s[0] )
return;
int nd = scale.getScale().length;
if( translation.getTranslation().length != nd )
{
System.err.println("scale and translation different lengths");
return;
}
for( int i = 0; i < nd; i++ )
{
double factor = s[i] / res[i];
t[i] = res[i] * (0.5 * factor - 0.5);
}
}
def ValuePair validateTransforms(String path, CoordinateTransformation[] coordinateTransformations ) {
numTforms = coordinateTransformations.length;
if (numTforms != 2) {
System.err.println("Nothing to do, need 2 transforms. There are " + numTforms + " transforms at " + path);
return null;
}
CoordinateTransformation<?> fst = coordinateTransformations[0];
CoordinateTransformation<?> snd = coordinateTransformations[1];
if (!(fst instanceof ScaleCoordinateTransformation)) {
System.err.println("First transform not a scale transformation");
return null;
}
if (!(snd instanceof TranslationCoordinateTransformation)) {
System.err.println("Second transform not a translation transformation");
return null;
}
ScaleCoordinateTransformation scale = (ScaleCoordinateTransformation)fst;
TranslationCoordinateTransformation translation = (TranslationCoordinateTransformation)snd;
return new ValuePair(scale, translation);
}
def ValuePair getTransformsMulti(OmeNgffDataset dataset) {
return validateTransforms(dataset.path, dataset.coordinateTransformations)
}
def ValuePair getTransformsArray(NgffSingleScaleAxesMetadata meta) {
return validateTransforms(meta.getPath(), meta.getCoordinateTransformations());
}
import java.io.File;
import java.util.Collections;
import java.util.Optional;
import org.janelia.saalfeldlab.n5.N5URI;
import org.janelia.saalfeldlab.n5.N5Writer;
import org.janelia.saalfeldlab.n5.universe.N5DatasetDiscoverer;
import org.janelia.saalfeldlab.n5.universe.N5Factory;
import org.janelia.saalfeldlab.n5.universe.N5TreeNode;
import org.janelia.saalfeldlab.n5.universe.metadata.N5GenericSingleScaleMetadataParser;
import org.janelia.saalfeldlab.n5.universe.metadata.N5Metadata;
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.NgffSingleScaleAxesMetadata;
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.OmeNgffMetadata;
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.OmeNgffMultiScaleMetadata.OmeNgffDataset;
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.coordinateTransformations.CoordinateTransformation;
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.coordinateTransformations.ScaleCoordinateTransformation;
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.coordinateTransformations.TranslationCoordinateTransformation;
import net.imglib2.util.ValuePair;
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.OmeNgffMetadataParser;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment