From 368cc7c0749ac523597d19985598f662d7520d35 Mon Sep 17 00:00:00 2001 From: Vojtech Moravec <vojtech.moravec.st@vsb.cz> Date: Fri, 25 Sep 2020 13:06:13 +0200 Subject: [PATCH] Enable usage of multiple decompressors. This contains functionality to map mipmap level to codebook size. Also -compress-from options is added to set the minimal mipmap level to compress. Eg. -comress-from 1, means that level 0 is not compressed and levels > 1 are. --- .../ViewerCompressionOptions.java | 22 ++++ src/main/java/bdv/BigDataViewer.java | 118 +++++++++--------- .../bdv/img/remote/RemoteImageLoader.java | 22 ++-- .../RemoteVolatileShortArrayLoader.java | 53 ++++++-- .../bdv/spimdata/XmlIoSpimDataMinimal.java | 85 ++++++------- 5 files changed, 173 insertions(+), 127 deletions(-) create mode 100644 src/main/java/azgracompress/ViewerCompressionOptions.java diff --git a/src/main/java/azgracompress/ViewerCompressionOptions.java b/src/main/java/azgracompress/ViewerCompressionOptions.java new file mode 100644 index 00000000..c74cd3c1 --- /dev/null +++ b/src/main/java/azgracompress/ViewerCompressionOptions.java @@ -0,0 +1,22 @@ +package azgracompress; + +public class ViewerCompressionOptions { + private boolean enabled = false; + private int compressFromMipmapLevel = 0; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(final boolean enable) { + this.enabled = enable; + } + + public int getCompressFromMipmapLevel() { + return compressFromMipmapLevel; + } + + public void setCompressFromMipmapLevel(final int compressFrom) { + this.compressFromMipmapLevel = compressFrom; + } +} diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java index 88583bc6..e1f6a331 100644 --- a/src/main/java/bdv/BigDataViewer.java +++ b/src/main/java/bdv/BigDataViewer.java @@ -29,73 +29,55 @@ */ package bdv; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.ActionMap; -import javax.swing.JFileChooser; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.filechooser.FileFilter; - -import azgracompress.cache.ICacheFile; -import net.imglib2.Volatile; -import net.imglib2.converter.Converter; -import net.imglib2.display.ColorConverter; -import net.imglib2.display.RealARGBColorConverter; -import net.imglib2.display.ScaledARGBConverter; -import net.imglib2.type.numeric.ARGBType; -import net.imglib2.type.numeric.NumericType; -import net.imglib2.type.numeric.RealType; -import net.imglib2.type.volatiles.VolatileARGBType; - -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; -import org.jdom2.input.SAXBuilder; -import org.jdom2.output.Format; -import org.jdom2.output.XMLOutputter; -import org.scijava.ui.behaviour.io.InputTriggerConfig; -import org.scijava.ui.behaviour.io.yaml.YamlConfigIO; - +import azgracompress.ViewerCompressionOptions; +import azgracompress.utilities.ColorConsole; import bdv.cache.CacheControl; import bdv.export.ProgressWriter; import bdv.export.ProgressWriterConsole; import bdv.spimdata.SpimDataMinimal; import bdv.spimdata.WrapBasicImgLoader; import bdv.spimdata.XmlIoSpimDataMinimal; -import bdv.tools.HelpDialog; -import bdv.tools.InitializeViewerState; -import bdv.tools.RecordMaxProjectionDialog; -import bdv.tools.RecordMovieDialog; -import bdv.tools.VisibilityAndGroupingDialog; +import bdv.tools.*; import bdv.tools.bookmarks.Bookmarks; import bdv.tools.bookmarks.BookmarksEditor; -import bdv.tools.brightness.BrightnessDialog; -import bdv.tools.brightness.ConverterSetup; -import bdv.tools.brightness.MinMaxGroup; -import bdv.tools.brightness.RealARGBColorConverterSetup; -import bdv.tools.brightness.SetupAssignments; +import bdv.tools.brightness.*; import bdv.tools.crop.CropDialog; import bdv.tools.transformation.ManualTransformation; import bdv.tools.transformation.ManualTransformationEditor; import bdv.tools.transformation.TransformedSource; -import bdv.viewer.NavigationActions; -import bdv.viewer.SourceAndConverter; -import bdv.viewer.ViewerFrame; -import bdv.viewer.ViewerOptions; -import bdv.viewer.ViewerPanel; +import bdv.viewer.*; import mpicbg.spim.data.SpimDataException; import mpicbg.spim.data.generic.AbstractSpimData; import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription; import mpicbg.spim.data.generic.sequence.BasicViewSetup; import mpicbg.spim.data.sequence.Angle; import mpicbg.spim.data.sequence.Channel; +import net.imglib2.Volatile; +import net.imglib2.converter.Converter; +import net.imglib2.display.ColorConverter; +import net.imglib2.display.RealARGBColorConverter; +import net.imglib2.display.ScaledARGBConverter; +import net.imglib2.type.numeric.ARGBType; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.volatiles.VolatileARGBType; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.scijava.ui.behaviour.io.InputTriggerConfig; +import org.scijava.ui.behaviour.io.yaml.YamlConfigIO; + +import javax.swing.*; +import javax.swing.filechooser.FileFilter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; public class BigDataViewer { protected final ViewerFrame viewerFrame; @@ -465,10 +447,10 @@ public class BigDataViewer { final String windowTitle, final ProgressWriter progressWriter, final ViewerOptions options, - final boolean allowCompression) throws SpimDataException { + final ViewerCompressionOptions ops) throws SpimDataException { final XmlIoSpimDataMinimal xmlIoSpimDataMinimal = new XmlIoSpimDataMinimal(); - xmlIoSpimDataMinimal.setAllowCompression(allowCompression); + xmlIoSpimDataMinimal.setViewerCompressionOptions(ops); final SpimDataMinimal spimData = xmlIoSpimDataMinimal.load(xmlFilename); final BigDataViewer bdv = open(spimData, windowTitle, progressWriter, options); if (!bdv.tryLoadSettings(xmlFilename)) @@ -635,19 +617,37 @@ public class BigDataViewer { viewer.requestRepaint(); } - public static void main(final String[] args) { - - // Default - String fn = "http://127.0.0.1:8080/drosophila32"; - + public static void main(final String[] args) { if (args.length < 1) { System.err.println("Provide path."); return; } + final String fn = args[0]; + + final ViewerCompressionOptions ops = new ViewerCompressionOptions(); + if (args.length > 1) { + for (int i = 1; i < args.length; i++) { + + switch (args[i]) { + case "-qcmp": + ops.setEnabled(true); + break; + case "-compress-from": + if (args.length <= i + 1) { + System.err.println("Missing -compress-from integer parameter."); + return; + } + ops.setCompressFromMipmapLevel(Integer.parseInt(args[++i])); + break; + } + + } + } - fn = args[0]; - final boolean allowCompression = (args.length > 1) && (args[1].equals("-qcmp")); + ColorConsole.printf(ColorConsole.Color.Green, "Compression:\t" + (ops.isEnabled() ? "ON" : "OFF")); + if (ops.isEnabled()) + ColorConsole.printf(ColorConsole.Color.Green, "CompressFrom:\t" + ops.getCompressFromMipmapLevel()); try { System.setProperty("apple.laf.useScreenMenuBar", "true"); @@ -656,7 +656,7 @@ public class BigDataViewer { "Server test", new ProgressWriterConsole(), ViewerOptions.options(), - allowCompression); + ops); } catch (final Exception e) { e.printStackTrace(); diff --git a/src/main/java/bdv/img/remote/RemoteImageLoader.java b/src/main/java/bdv/img/remote/RemoteImageLoader.java index e2fb6499..949c7cf3 100644 --- a/src/main/java/bdv/img/remote/RemoteImageLoader.java +++ b/src/main/java/bdv/img/remote/RemoteImageLoader.java @@ -29,6 +29,7 @@ */ package bdv.img.remote; +import azgracompress.ViewerCompressionOptions; import azgracompress.cache.ICacheFile; import azgracompress.cache.QuantizationCacheManager; import azgracompress.compression.ImageDecompressor; @@ -77,7 +78,7 @@ public class RemoteImageLoader implements ViewerImgLoader { /** * Flag whether we allow the server to send us compressed data. */ - private boolean allowCompression; + private ViewerCompressionOptions viewerCompressionOptions; /** @@ -124,19 +125,15 @@ public class RemoteImageLoader implements ViewerImgLoader { for (final int setupId : metadata.perSetupMipmapInfo.keySet()) setupImgLoaders.put(setupId, new SetupImgLoader(setupId)); - if (allowCompression) { + if (viewerCompressionOptions.isEnabled()) { setupCompression(); } } } } - public boolean shouldAllowCompression() { - return allowCompression; - } - - public void setAllowCompression(final boolean compressionEnabled) { - this.allowCompression = compressionEnabled; + public void setViewerCompressionOptions(final ViewerCompressionOptions ops) { + this.viewerCompressionOptions = ops; } private void setupCompression() throws IOException { @@ -166,10 +163,11 @@ public class RemoteImageLoader implements ViewerImgLoader { ColorConsole.fprintf(ColorConsole.Target.stdout, ColorConsole.Color.Yellow, "Received %d cache files.", cacheFiles.size()); - final ICacheFile cachedCodebook = cacheFiles.get(cacheFiles.size() - 1); - // TODO(Moravec): Provide all decompressors. - shortLoader.setDataDecompressor(new ImageDecompressor(cachedCodebook)); - System.out.println("\u001b[33mRemoteImageLoader::setupCompression() - instantiated image decompressor in shortLoader.\u001b[0m"); + final ImageDecompressor[] decompressors = new ImageDecompressor[cacheFiles.size()]; + for (int i = 0; i < cacheFiles.size(); i++) { + decompressors[i] = new ImageDecompressor(cacheFiles.get(i)); + } + shortLoader.setDataDecompressors(decompressors, metadata.maxNumLevels, viewerCompressionOptions.getCompressFromMipmapLevel()); } diff --git a/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java index 0e85c9ce..7a85c86b 100644 --- a/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java +++ b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java @@ -29,7 +29,9 @@ */ package bdv.img.remote; +import azgracompress.compression.CompressorDecompressorBase; import azgracompress.compression.ImageDecompressor; +import azgracompress.utilities.ColorConsole; import bdv.img.cache.CacheArrayLoader; import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray; @@ -38,12 +40,17 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; public class RemoteVolatileShortArrayLoader implements CacheArrayLoader<VolatileShortArray> { private final RemoteImageLoader imgLoader; private boolean requestCompressedData = false; - private ImageDecompressor decompressor; + private HashMap<Integer, ImageDecompressor> decompressors; + private ImageDecompressor lowestResDecompressor; + private int compressFromMipmapLevel = 0; public RemoteVolatileShortArrayLoader(final RemoteImageLoader imgLoader) { @@ -70,10 +77,9 @@ public class RemoteVolatileShortArrayLoader implements CacheArrayLoader<Volatile final int setup, final int level, final int[] dimensions, - final long[] min) throws InterruptedException { + final long[] min) { - - if (requestCompressedData) { + if (requestCompressedData && level >= compressFromMipmapLevel) { return loadArrayFromCompressedDataStream(timepoint, setup, level, dimensions, min); } @@ -104,13 +110,13 @@ public class RemoteVolatileShortArrayLoader implements CacheArrayLoader<Volatile public VolatileShortArray loadArrayFromCompressedDataStream(final int timepoint, final int setup, - final int level, + final int mipmapLevel, final int[] dimensions, final long[] min) { short[] data = null; try { - final URL url = new URL(constructRequestUrl("cell_qcmp", timepoint, setup, level, dimensions, min)); + final URL url = new URL(constructRequestUrl("cell_qcmp", timepoint, setup, mipmapLevel, dimensions, min)); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); @@ -119,7 +125,7 @@ public class RemoteVolatileShortArrayLoader implements CacheArrayLoader<Volatile final int contentLength = connection.getContentLength(); final InputStream urlStream = connection.getInputStream(); - data = decompressor.decompressStream(urlStream, contentLength); + data = getDecompressorForMipmapLevel(mipmapLevel).decompressStream(urlStream, contentLength); urlStream.close(); } catch (final Exception e) { @@ -134,8 +140,35 @@ public class RemoteVolatileShortArrayLoader implements CacheArrayLoader<Volatile return 2; } - public void setDataDecompressor(final ImageDecompressor imageDecompressor) { - this.decompressor = imageDecompressor; - requestCompressedData = true; + private ImageDecompressor getDecompressorForMipmapLevel(final int mipmapLevel) { + assert (decompressors != null && !decompressors.isEmpty()); + if (decompressors.containsKey(mipmapLevel)) { + return decompressors.get(mipmapLevel); + } + return lowestResDecompressor; + } + + public void setDataDecompressors(final ImageDecompressor[] imageDecompressors, + final int levelCount, + final int compressFromMipmapLevel) { + Arrays.sort(imageDecompressors, Comparator.comparingInt(CompressorDecompressorBase::getBitsPerCodebookIndex)); + + + final int numberOfDecompressionLevels = Math.min((levelCount - compressFromMipmapLevel), imageDecompressors.length); + decompressors = new HashMap<>(numberOfDecompressionLevels); + + for (int mipmapLevel = 0; mipmapLevel < numberOfDecompressionLevels; mipmapLevel++) { + final ImageDecompressor decompressor = imageDecompressors[(imageDecompressors.length - 1) - mipmapLevel]; + final int cbSize = (int) Math.pow(2, decompressor.getBitsPerCodebookIndex()); + final int actualKey = mipmapLevel + compressFromMipmapLevel; + decompressors.put(actualKey, decompressor); + + ColorConsole.fprintf(ColorConsole.Target.stdout, ColorConsole.Color.Yellow, + "Created decompressor for mipmap level %d with codebook of size %d.", + actualKey, cbSize); + lowestResDecompressor = decompressor; + } + this.compressFromMipmapLevel = compressFromMipmapLevel; + requestCompressedData = !decompressors.isEmpty(); } } diff --git a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java index 8737c521..2988d6d2 100644 --- a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java +++ b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java @@ -29,11 +29,9 @@ */ package bdv.spimdata; -import static mpicbg.spim.data.XmlKeys.SPIMDATA_TAG; - -import java.io.File; - +import azgracompress.ViewerCompressionOptions; import bdv.img.remote.RemoteImageLoader; +import bdv.spimdata.legacy.XmlIoSpimDataMinimalLegacy; import mpicbg.spim.data.SpimDataException; import mpicbg.spim.data.SpimDataIOException; import mpicbg.spim.data.generic.XmlIoAbstractSpimData; @@ -43,58 +41,53 @@ import mpicbg.spim.data.generic.sequence.XmlIoBasicViewSetups; import mpicbg.spim.data.registration.XmlIoViewRegistrations; import mpicbg.spim.data.sequence.XmlIoMissingViews; import mpicbg.spim.data.sequence.XmlIoTimePoints; - import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.input.SAXBuilder; -import bdv.spimdata.legacy.XmlIoSpimDataMinimalLegacy; +import java.io.File; + +import static mpicbg.spim.data.XmlKeys.SPIMDATA_TAG; -public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData< SequenceDescriptionMinimal, SpimDataMinimal > -{ - private boolean allowCompression = false; +public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData<SequenceDescriptionMinimal, SpimDataMinimal> { + private ViewerCompressionOptions compressionOptions; - public XmlIoSpimDataMinimal() - { - super( SpimDataMinimal.class, - new XmlIoAbstractSequenceDescription<>( - SequenceDescriptionMinimal.class, - new XmlIoTimePoints(), - new XmlIoBasicViewSetups<>( BasicViewSetup.class ), - new XmlIoMissingViews() ), - new XmlIoViewRegistrations() ); - } + public XmlIoSpimDataMinimal() { + super(SpimDataMinimal.class, + new XmlIoAbstractSequenceDescription<>( + SequenceDescriptionMinimal.class, + new XmlIoTimePoints(), + new XmlIoBasicViewSetups<>(BasicViewSetup.class), + new XmlIoMissingViews()), + new XmlIoViewRegistrations()); + } - public void setAllowCompression(final boolean allowCompression) { - this.allowCompression = allowCompression; + public void setViewerCompressionOptions(final ViewerCompressionOptions ops) { + this.compressionOptions = ops; } - @Override - public SpimDataMinimal load( final String xmlFilename ) throws SpimDataException - { - final SAXBuilder sax = new SAXBuilder(); - Document doc; - try - { - doc = sax.build( xmlFilename ); - } - catch ( final Exception e ) - { - throw new SpimDataIOException( e ); - } - final Element root = doc.getRootElement(); + @Override + public SpimDataMinimal load(final String xmlFilename) throws SpimDataException { + final SAXBuilder sax = new SAXBuilder(); + final Document doc; + try { + doc = sax.build(xmlFilename); + } catch (final Exception e) { + throw new SpimDataIOException(e); + } + final Element root = doc.getRootElement(); - if ( root.getName().equals( "SequenceDescription" ) ) - return XmlIoSpimDataMinimalLegacy.fromXml( root, new File( xmlFilename ) ); + if (root.getName().equals("SequenceDescription")) + return XmlIoSpimDataMinimalLegacy.fromXml(root, new File(xmlFilename)); - if ( root.getName() != SPIMDATA_TAG ) - throw new RuntimeException( "expected <" + SPIMDATA_TAG + "> root element. wrong file?" ); + if (root.getName() != SPIMDATA_TAG) + throw new RuntimeException("expected <" + SPIMDATA_TAG + "> root element. wrong file?"); - SpimDataMinimal spimDataMinimal = fromXml(root, new File(xmlFilename)); - if (spimDataMinimal.getSequenceDescription().getImgLoader() instanceof RemoteImageLoader) { - final RemoteImageLoader remoteImageLoader = (RemoteImageLoader) spimDataMinimal.getSequenceDescription().getImgLoader(); - remoteImageLoader.setAllowCompression(allowCompression); - } - return spimDataMinimal; - } + final SpimDataMinimal spimDataMinimal = fromXml(root, new File(xmlFilename)); + if (spimDataMinimal.getSequenceDescription().getImgLoader() instanceof RemoteImageLoader) { + final RemoteImageLoader remoteImageLoader = (RemoteImageLoader) spimDataMinimal.getSequenceDescription().getImgLoader(); + remoteImageLoader.setViewerCompressionOptions(compressionOptions); + } + return spimDataMinimal; + } } -- GitLab