/*- * #%L * Scenery-backed 3D visualization package for ImageJ. * %% * Copyright (C) 2016 - 2018 SciView developers. * %% * 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 sc.iview.io; import bdv.util.AxisOrder; import graphics.scenery.Group; import graphics.scenery.Node; import graphics.scenery.PointCloud; import graphics.scenery.volumes.Volume; import net.imglib2.RandomAccess; import net.imglib2.RandomAccessibleInterval; import net.imglib2.RealLocalizable; import net.imglib2.type.numeric.integer.UnsignedByteType; import org.janelia.saalfeldlab.n5.N5FSReader; import org.janelia.saalfeldlab.n5.N5Reader; import org.janelia.saalfeldlab.n5.imglib2.N5Utils; import org.scijava.io.AbstractIOPlugin; import org.scijava.io.IOPlugin; import org.scijava.plugin.Plugin; import org.scijava.util.FileUtils; import sc.iview.SciView; import sc.iview.SciViewService; import sc.iview.process.MeshConverter; import tpietzsch.example2.VolumeViewerOptions; import java.io.File; import java.io.IOException; import java.util.List; /** {@link IOPlugin} adapter for N5 as a data source (volume or mesh) * * @author Kyle Harrington * * */ @Plugin(type = IOPlugin.class) public class N5IO extends AbstractIOPlugin<graphics.scenery.Node> { @Override public graphics.scenery.Node open( final String source ) throws IOException { int splitPoint = source.indexOf(".n5/") + 3; String n5Path = source.substring(0, splitPoint); String dataset = source.substring(splitPoint) + "/"; N5Reader n5Reader = new N5FSReader(n5Path); // TODO: select the dataset (show n5 tree) Node node; //if( n5Reader.datasetExists( dataset ) ) { if( new File(source).exists() ) { node = open(n5Reader, dataset); } else { throw new IOException("Dataset " + dataset + " does not exist in n5: " + n5Reader.toString() ); } // TODO: Remember to also read the metadata for the node. this is generic to Node return node; } public graphics.scenery.Node open( final N5Reader n5Reader, final String dataset ) throws IOException { Node node; String nodeType = getNodeType( n5Reader, dataset ); // Fail if SciView is not open. We use the active sciview SciViewService sciViewService = context().service(SciViewService.class); if( sciViewService.numSciView() == 0 ) { throw new IOException("SciView is not open, needed for file opening."); } SciView sv = sciViewService.getActiveSciView(); if( nodeType.startsWith("sciview") ) { node = openSciview( n5Reader, dataset, nodeType ); } else { // TODO check for multiresolution and such here // Note: UnsignedByteType is currently hard coded due to BVV constraints RandomAccessibleInterval<UnsignedByteType> image = N5Utils.open(n5Reader, dataset); long[] dimensions = new long[image.numDimensions()]; image.dimensions( dimensions ); long[] minPt = new long[image.numDimensions()]; // Get type at min point RandomAccess<UnsignedByteType> imageRA = image.randomAccess(); image.min(minPt); imageRA.setPosition(minPt); node = Volume.fromRAI(image, new UnsignedByteType(), AxisOrder.DEFAULT, dataset, sv.getHub(), new VolumeViewerOptions()); } return node; } private Node openSciview(N5Reader n5Reader, String dataset, String nodeType ) throws IOException { Node node; // If it is a mesh, then load if( nodeType.compareToIgnoreCase("sciview-1.0.0 trimesh") == 0 ) { node = MeshConverter.toScenery( N5.openMesh(n5Reader, dataset) ); } else if( nodeType.compareToIgnoreCase("sciview-1.0.0 points") == 0 ) { // TODO this can be better List<RealLocalizable> points = N5.openPoints(n5Reader, dataset); if( points.size() == 0 ) { node = new Group(); } else { int numDim = points.get(0).numDimensions(); float[] array = new float[points.size() * numDim]; for( int k = 0; k < points.size(); k++ ) { RealLocalizable point = points.get(k); for (int d = 0; d < numDim; d++) { array[k * numDim + d] = point.getFloatPosition(d); } } node = PointCloud.Companion.fromArray(array); } } else { throw new IOException("Cannot open dataset: " + dataset + " in " + n5Reader); } node.setName(dataset); return node; } private String getNodeType(N5Reader n5Reader, String dataset) throws IOException { return n5Reader.getAttribute(dataset, "nodeType", String.class); } @Override public Class<graphics.scenery.Node> getDataType() { return graphics.scenery.Node.class; } @Override public boolean supportsOpen(final String source) { return source.contains(".n5/");// TODO check what N5 folks are doing, this assumes n5+dataset in same path and parses accordingly //return FileUtils.getExtension(source).toLowerCase().equals(EXTENSION); } String EXTENSION = "n5"; }