Skip to content
Snippets Groups Projects
Commit 359a077f authored by Tobias Pietzsch's avatar Tobias Pietzsch
Browse files

BigDataServer and RemoteImageLoader

parent 6214823c
No related branches found
No related tags found
No related merge requests found
package bdv.img.remote;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import mpicbg.spim.data.View;
import net.imglib2.FinalInterval;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.NativeImg;
import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray;
import net.imglib2.img.cell.CellImg;
import net.imglib2.sampler.special.ConstantRandomAccessible;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.type.volatiles.VolatileUnsignedShortType;
import net.imglib2.util.IntervalIndexer;
import net.imglib2.view.Views;
import org.jdom2.Element;
import bdv.ViewerImgLoader;
import bdv.img.cache.VolatileCell;
import bdv.img.cache.VolatileGlobalCellCache;
import bdv.img.cache.VolatileGlobalCellCache.LoadingStrategy;
import bdv.img.cache.VolatileImgCells;
import bdv.img.cache.VolatileImgCells.CellCache;
import com.google.gson.Gson;
public class RemoteImageLoader implements ViewerImgLoader< UnsignedShortType, VolatileUnsignedShortType >
{
protected String baseUrl;
protected RemoteImageLoaderMetaData metadata;
protected int[][] cellsDimensions;
protected VolatileGlobalCellCache< VolatileShortArray > cache;
private void open() throws IOException
{
final URL url = new URL( baseUrl + "?p=init" );
metadata = new Gson().fromJson(
new InputStreamReader( url.openStream() ),
RemoteImageLoaderMetaData.class );
cache = new VolatileGlobalCellCache< VolatileShortArray >(
new RemoteVolatileShortArrayLoader( this ),
metadata.numTimepoints,
metadata.numSetups,
metadata.maxNumLevels,
metadata.maxLevels,
10 );
cellsDimensions = metadata.createCellsDimensions();
}
@Override
public void init( final Element elem, final File basePath )
{
try
{
baseUrl = elem.getChildText( "baseUrl" );
open();
}
catch ( final Exception e )
{
throw new RuntimeException( e );
}
}
@Override
public Element toXml( final File basePath )
{
throw new UnsupportedOperationException( "not implemented" );
}
@Override
public RandomAccessibleInterval< FloatType > getFloatImage( final View view )
{
throw new UnsupportedOperationException( "not implemented" );
}
@Override
public RandomAccessibleInterval< UnsignedShortType > getImage( final View view )
{
return getImage( view, 0 );
}
@Override
public RandomAccessibleInterval< UnsignedShortType > getImage( final View view, final int level )
{
if ( ! existsImageData( view, level ) )
{
System.err.println( "image data for " + view.getBasename() + " level " + level + " could not be found. Partition file missing?" );
return getMissingDataImage( view, level, new UnsignedShortType() );
}
final CellImg< UnsignedShortType, VolatileShortArray, VolatileCell< VolatileShortArray > > img = prepareCachedImage( view, level, LoadingStrategy.BLOCKING );
final UnsignedShortType linkedType = new UnsignedShortType( img );
img.setLinkedType( linkedType );
return img;
}
@Override
public RandomAccessibleInterval< VolatileUnsignedShortType > getVolatileImage( final View view, final int level )
{
if ( ! existsImageData( view, level ) )
{
System.err.println( "image data for " + view.getBasename() + " level " + level + " could not be found." );
return getMissingDataImage( view, level, new VolatileUnsignedShortType() );
}
final CellImg< VolatileUnsignedShortType, VolatileShortArray, VolatileCell< VolatileShortArray > > img = prepareCachedImage( view, level, LoadingStrategy.BUDGETED );
final VolatileUnsignedShortType linkedType = new VolatileUnsignedShortType( img );
img.setLinkedType( linkedType );
return img;
}
public VolatileGlobalCellCache< VolatileShortArray > getCache()
{
return cache;
}
@Override
public double[][] getMipmapResolutions( final int setup )
{
return metadata.perSetupMipmapResolutions.get( setup );
}
public int[][] getSubdivisions( final int setup )
{
return metadata.perSetupSubdivisions.get( setup );
}
@Override
public int numMipmapLevels( final int setup )
{
return getMipmapResolutions( setup ).length;
}
/**
* Checks whether the given image data is present on the server.
*
* @return true, if the given image data is present.
*/
public boolean existsImageData( final View view, final int level )
{
final int timepoint = view.getTimepointIndex();
final int setup = view.getSetupIndex();
return existsImageData( timepoint, setup, level );
}
/**
* Checks whether the given image data is present on the server.
*
* @return true, if the given image data is present.
*/
public boolean existsImageData( final int timepoint, final int setup, final int level )
{
final int index = getViewInfoCacheIndex( timepoint, setup, level );
return metadata.existence[ index ];
}
/**
* For images that are missing on the server, a constant image is created.
* If the dimension of the missing image is present in
* {@link RemoteImageLoaderMetaData#dimensions} then use that. Otherwise
* create a 1x1x1 image.
*/
protected < T > RandomAccessibleInterval< T > getMissingDataImage( final View view, final int level, final T constant )
{
final int t = view.getTimepointIndex();
final int s = view.getSetupIndex();
final int index = getViewInfoCacheIndex( t, s, level );
long[] d = metadata.dimensions[ index ];
if ( d == null )
d = new long[] { 1, 1, 1 };
return Views.interval( new ConstantRandomAccessible< T >( constant, 3 ), new FinalInterval( d ) );
}
public long[] getImageDimension( final int timepoint, final int setup, final int level )
{
final int index = getViewInfoCacheIndex( timepoint, setup, level );
return metadata.dimensions[ index ];
}
private int getViewInfoCacheIndex( final int timepoint, final int setup, final int level )
{
return level + metadata.maxNumLevels * ( setup + metadata.numSetups * timepoint );
}
int getCellIndex( final int timepoint, final int setup, final int level, final long[] globalPosition )
{
final int index = getViewInfoCacheIndex( timepoint, setup, level );
final int[] cellSize = getSubdivisions( setup )[ level ];
final int[] cellPos = new int[] {
( int ) globalPosition[ 0 ] / cellSize[ 0 ],
( int ) globalPosition[ 1 ] / cellSize[ 1 ],
( int ) globalPosition[ 2 ] / cellSize[ 2 ] };
return IntervalIndexer.positionToIndex( cellPos, cellsDimensions[ index ] );
}
/**
* (Almost) create a {@link CellImg} backed by the cache.
* The created image needs a {@link NativeImg#setLinkedType(net.imglib2.type.Type) linked type} before it can be used.
* The type should be either {@link UnsignedShortType} and {@link VolatileUnsignedShortType}.
*/
protected < T extends NativeType< T > > CellImg< T, VolatileShortArray, VolatileCell< VolatileShortArray > > prepareCachedImage( final View view, final int level, final LoadingStrategy loadingStrategy )
{
if ( cache == null )
throw new RuntimeException( "no connection open" );
final int timepoint = view.getTimepointIndex();
final int setup = view.getSetupIndex();
final long[] dimensions = getImageDimension( timepoint, setup, level );
final int[] cellDimensions = metadata.perSetupSubdivisions.get( setup )[ level ];
final CellCache< VolatileShortArray > c = cache.new VolatileCellCache( timepoint, setup, level, loadingStrategy );
final VolatileImgCells< VolatileShortArray > cells = new VolatileImgCells< VolatileShortArray >( c, 1, dimensions, cellDimensions );
final CellImg< T, VolatileShortArray, VolatileCell< VolatileShortArray > > img = new CellImg< T, VolatileShortArray, VolatileCell< VolatileShortArray > >( null, cells );
return img;
}
private final UnsignedShortType type = new UnsignedShortType();
private final VolatileUnsignedShortType volatileType = new VolatileUnsignedShortType();
@Override
public UnsignedShortType getImageType()
{
return type;
}
@Override
public VolatileUnsignedShortType getVolatileImageType()
{
return volatileType;
}
}
package bdv.img.remote;
import java.util.ArrayList;
import bdv.img.hdf5.Hdf5ImageLoader;
public class RemoteImageLoaderMetaData
{
protected final ArrayList< double[][] > perSetupMipmapResolutions;
protected final ArrayList< int[][] > perSetupSubdivisions;
protected int numTimepoints;
protected int numSetups;
protected int[] maxLevels;
protected int maxNumLevels;
/**
* An array of long[] arrays with {@link #numTimepoints} *
* {@link #numSetups} * {@link #maxNumLevels} entries. Every entry is the
* dimensions of one image (identified by flattened index of level, setup,
* and timepoint).
*/
protected long[][] dimensions;
/**
* An array of Booleans with {@link #numTimepoints} * {@link #numSetups} *
* {@link #maxNumLevels} entries. Every entry is the existence of one image
* (identified by flattened index of level, setup, and timepoint).
*/
protected boolean[] existence;
public RemoteImageLoaderMetaData()
{
perSetupMipmapResolutions = new ArrayList< double[][] >();
perSetupSubdivisions = new ArrayList< int[][] >();
}
public RemoteImageLoaderMetaData( final Hdf5ImageLoader imgLoader, final int numTimepoints, final int numSetups )
{
perSetupMipmapResolutions = new ArrayList< double[][] >();
perSetupSubdivisions = new ArrayList< int[][] >();
this.numTimepoints = numTimepoints;
this.numSetups = numSetups;
maxLevels = new int[ numSetups ];
maxNumLevels = 0;
for ( int setup = 0; setup < numSetups; ++setup )
{
perSetupMipmapResolutions.add( imgLoader.getMipmapResolutions( setup ) );
perSetupSubdivisions.add( imgLoader.getSubdivisions( setup ) );
final int numLevels = imgLoader.numMipmapLevels( setup );
maxLevels[ setup ] = numLevels - 1;
if ( numLevels > maxNumLevels )
maxNumLevels = numLevels;
}
final int numImages = numTimepoints * numSetups * maxNumLevels;
dimensions = new long[ numImages ][];
existence = new boolean[ numImages ];
for ( int t = 0; t < numTimepoints; ++t )
for ( int s = 0; s < numSetups; ++s )
for ( int l = 0; l <= maxLevels[ s ]; ++l )
{
final int i = l + maxNumLevels * ( s + numSetups * t );
existence[ i ] = imgLoader.existsImageData( t, s, l );
dimensions[ i ] = imgLoader.getImageDimension( t, s, l );
}
}
/**
* Create an array of int[] with {@link #numTimepoints} * {@link #numSetups}
* * {@link #maxNumLevels} entries. Every entry is the dimensions in cells
* (instead of pixels) of one image (identified by flattened index of level,
* setup, and timepoint).
*/
protected int[][] createCellsDimensions()
{
final int numImages = numTimepoints * numSetups * maxNumLevels;
final int[][] cellsDimensions = new int[ numImages ][];
for ( int t = 0; t < numTimepoints; ++t )
for ( int s = 0; s < numSetups; ++s )
for ( int l = 0; l <= maxLevels[ s ]; ++l )
{
final int i = l + maxNumLevels * ( s + numSetups * t );
final long[] imageDimensions = dimensions[ i ];
final int[] cellSize = perSetupSubdivisions.get( s )[ l ];
cellsDimensions[ i ] = new int[] {
( int ) imageDimensions[ 0 ] / cellSize[ 0 ],
( int ) imageDimensions[ 1 ] / cellSize[ 1 ],
( int ) imageDimensions[ 2 ] / cellSize[ 2 ] };
}
return cellsDimensions;
}
}
package bdv.img.remote;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray;
import bdv.img.cache.CacheArrayLoader;
public class RemoteVolatileShortArrayLoader implements CacheArrayLoader< VolatileShortArray >
{
private VolatileShortArray theEmptyArray;
private final RemoteImageLoader imgLoader;
public RemoteVolatileShortArrayLoader( final RemoteImageLoader imgLoader )
{
theEmptyArray = new VolatileShortArray( 32 * 32 * 32, false );
this.imgLoader = imgLoader;
}
@Override
public VolatileShortArray loadArray( final int timepoint, final int setup, final int level, final int[] dimensions, final long[] min ) throws InterruptedException
{
final int index = imgLoader.getCellIndex( timepoint, setup, level, min );
final short[] data = new short[ dimensions[ 0 ] * dimensions[ 1 ] * dimensions[ 2 ] ];
try
{
final URL url = new URL( String.format( "%s?p=cell/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
imgLoader.baseUrl,
index,
timepoint,
setup,
level,
dimensions[ 0 ],
dimensions[ 1 ],
dimensions[ 2 ],
min[ 0 ],
min[ 1 ],
min[ 2 ] ) );
final InputStream s = url.openStream();
final byte[] buf = new byte[ data.length * 2 ];
for ( int i = 0, l = s.read( buf, 0, buf.length ); l != -1; i += l, l = s.read( buf, i, buf.length - i ) );
for ( int i = 0, j = 0; i < data.length; ++i, j += 2 )
data[ i ] = ( short ) ( ( ( buf[ j ] & 0xff ) << 8 ) | ( buf[ j + 1 ] & 0xff ) );
s.close();
}
catch ( final MalformedURLException e )
{
e.printStackTrace();
}
catch ( final IOException e )
{
e.printStackTrace();
}
return new VolatileShortArray( data, true );
}
@Override
public VolatileShortArray emptyArray( final int[] dimensions )
{
int numEntities = 1;
for ( int i = 0; i < dimensions.length; ++i )
numEntities *= dimensions[ i ];
if ( theEmptyArray.getCurrentStorageArray().length < numEntities )
theEmptyArray = new VolatileShortArray( numEntities, false );
return theEmptyArray;
}
@Override
public int getBytesPerElement() {
return 2;
}
}
package server; package bdv.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.io.PrintWriter;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import mpicbg.spim.data.SequenceDescription; import mpicbg.spim.data.SequenceDescription;
import net.imglib2.img.basictypeaccess.array.ShortArray; import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.xml.sax.SAXException; import org.jdom2.JDOMException;
import viewer.SequenceViewsLoader; import bdv.SequenceViewsLoader;
import viewer.hdf5.Hdf5ImageLoader; import bdv.img.cache.VolatileCell;
import viewer.hdf5.Hdf5ImageLoader.DimensionsInfo; import bdv.img.cache.VolatileGlobalCellCache;
import viewer.hdf5.img.Hdf5Cell; import bdv.img.cache.VolatileGlobalCellCache.LoadingStrategy;
import viewer.hdf5.img.Hdf5GlobalCellCache; import bdv.img.hdf5.Hdf5ImageLoader;
import bdv.img.remote.RemoteImageLoaderMetaData;
public class CellServer import com.google.gson.Gson;
public class BigDataServer
{ {
public static void main( final String[] args ) throws Exception public static void main( final String[] args ) throws Exception
{ {
final String fn = args.length > 0 ? args[ 0 ] : "/Users/pietzsch/Desktop/Valia/valia.xml"; final String fn = args.length > 0 ? args[ 0 ] : "/Users/pietzsch/Desktop/data/fibsem.xml";
final Server server = new Server( 8080 ); final Server server = new Server( 8080 );
server.setHandler( new CellHandler( fn ) ); server.setHandler( new CellHandler( fn ) );
server.start(); server.start();
...@@ -38,28 +38,20 @@ public class CellServer ...@@ -38,28 +38,20 @@ public class CellServer
static class CellHandler extends AbstractHandler static class CellHandler extends AbstractHandler
{ {
final Hdf5ImageLoader imgLoader; private final VolatileGlobalCellCache< VolatileShortArray > cache;
final Hdf5GlobalCellCache< ShortArray > cache; private final String metadataJson;
final byte[] initObjects; private final RemoteImageLoaderMetaData metadata;
public CellHandler( final String xmlFilename ) throws ParserConfigurationException, SAXException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException public CellHandler( final String xmlFilename ) throws JDOMException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException
{ {
final SequenceViewsLoader loader = new SequenceViewsLoader( xmlFilename ); final SequenceViewsLoader loader = new SequenceViewsLoader( xmlFilename );
final SequenceDescription seq = loader.getSequenceDescription(); final SequenceDescription seq = loader.getSequenceDescription();
imgLoader = ( Hdf5ImageLoader ) seq.imgLoader; final Hdf5ImageLoader imgLoader = ( Hdf5ImageLoader ) seq.imgLoader;
cache = imgLoader.getCache(); cache = imgLoader.getCache();
metadata = new RemoteImageLoaderMetaData( imgLoader, seq.numTimepoints(), seq.numViewSetups() );
final ArrayList< double[][] > perSetupResolutions = new ArrayList< double[][] >(); metadataJson = new Gson().toJson( metadata );
for ( int setup = 0; setup < seq.numViewSetups(); ++setup )
perSetupResolutions.add( imgLoader.getMipmapResolutions( setup ) );
final ByteArrayOutputStream bs = new ByteArrayOutputStream();
final ObjectOutputStream os = new ObjectOutputStream( bs );
os.writeInt( seq.numTimepoints() );
os.writeObject( perSetupResolutions );
os.close();
initObjects = bs.toByteArray();
} }
@Override @Override
...@@ -75,13 +67,20 @@ public class CellServer ...@@ -75,13 +67,20 @@ public class CellServer
final int timepoint = Integer.parseInt( parts[ 2 ] ); final int timepoint = Integer.parseInt( parts[ 2 ] );
final int setup = Integer.parseInt( parts[ 3 ] ); final int setup = Integer.parseInt( parts[ 3 ] );
final int level = Integer.parseInt( parts[ 4 ] ); final int level = Integer.parseInt( parts[ 4 ] );
Hdf5Cell< ShortArray > cell = cache.getGlobalIfCached( timepoint, setup, level, index ); VolatileCell< VolatileShortArray > cell = cache.getGlobalIfCached( timepoint, setup, level, index, LoadingStrategy.BLOCKING );
if ( cell == null ) if ( cell == null )
{ {
final int[] cellDims = new int[] { Integer.parseInt( parts[ 5 ] ), Integer.parseInt( parts[ 6 ] ), Integer.parseInt( parts[ 7 ] ) }; final int[] cellDims = new int[] {
final long[] cellMin = new long[] { Long.parseLong( parts[ 8 ] ), Long.parseLong( parts[ 9 ] ), Long.parseLong( parts[ 10 ] ) }; Integer.parseInt( parts[ 5 ] ),
cell = cache.loadGlobal( cellDims, cellMin, timepoint, setup, level, index ); Integer.parseInt( parts[ 6 ] ),
Integer.parseInt( parts[ 7 ] ) };
final long[] cellMin = new long[] {
Long.parseLong( parts[ 8 ] ),
Long.parseLong( parts[ 9 ] ),
Long.parseLong( parts[ 10 ] ) };
cell = cache.createGlobal( cellDims, cellMin, timepoint, setup, level, index, LoadingStrategy.BLOCKING );
} }
final short[] data = cell.getData().getCurrentStorageArray(); final short[] data = cell.getData().getCurrentStorageArray();
final byte[] buf = new byte[ 2 * data.length ]; final byte[] buf = new byte[ 2 * data.length ];
for ( int i = 0, j = 0; i < data.length; i++ ) for ( int i = 0, j = 0; i < data.length; i++ )
...@@ -98,37 +97,15 @@ public class CellServer ...@@ -98,37 +97,15 @@ public class CellServer
final OutputStream os = response.getOutputStream(); final OutputStream os = response.getOutputStream();
os.write( buf ); os.write( buf );
os.close(); os.close();
// final byte[] buf = new byte[ 4096 ];
// for ( int i = 0, l = Math.min( buf.length / 2, data.length - i ); i < data.length; i += l )
// {
// ByteBuffer.wrap( buf ).asShortBuffer().put( data, i, l );
// os.write( buf, 0, 2 * l );
// }
// os.close();
}
else if ( parts[ 0 ].equals( "dim" ) )
{
final int timepoint = Integer.parseInt( parts[ 1 ] );
final int setup = Integer.parseInt( parts[ 2 ] );
final int level = Integer.parseInt( parts[ 3 ] );
final DimensionsInfo info = imgLoader.getDimensionsInfo( timepoint, setup, level );
response.setContentType( "application/octet-stream" );
response.setStatus( HttpServletResponse.SC_OK );
baseRequest.setHandled( true );
final ObjectOutputStream os = new ObjectOutputStream( response.getOutputStream() );
os.writeObject( info );
os.close();
} }
else if ( parts[ 0 ].equals( "init" ) ) else if ( parts[ 0 ].equals( "init" ) )
{ {
response.setContentType( "application/octet-stream" ); response.setContentType( "application/octet-stream" );
response.setStatus( HttpServletResponse.SC_OK ); response.setStatus( HttpServletResponse.SC_OK );
baseRequest.setHandled( true ); baseRequest.setHandled( true );
final OutputStream os = response.getOutputStream(); final PrintWriter ow = response.getWriter();
os.write( initObjects ); ow.write( metadataJson );
os.close(); ow.close();
} }
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment