From c46aa1f1ec788f46300ffc559f43ccb4a1634507 Mon Sep 17 00:00:00 2001 From: ebner Date: Tue, 14 Apr 2015 13:38:48 +0200 Subject: [PATCH 1/4] implemented a HDF5 Virtual Stack CTRLHA-109 --- .../ch/psi/imagej/hdf5/DatasetSelection.java | 7 + .../java/ch/psi/imagej/hdf5/HDF5Reader.java | 111 +++++++------ .../ch/psi/imagej/hdf5/SelectionPanel.java | 10 +- .../ch/psi/imagej/hdf5/VirtualStackHDF5.java | 157 ++++++++++++++++++ 4 files changed, 233 insertions(+), 52 deletions(-) create mode 100644 src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java diff --git a/src/main/java/ch/psi/imagej/hdf5/DatasetSelection.java b/src/main/java/ch/psi/imagej/hdf5/DatasetSelection.java index 7baf3cd..f2313d3 100644 --- a/src/main/java/ch/psi/imagej/hdf5/DatasetSelection.java +++ b/src/main/java/ch/psi/imagej/hdf5/DatasetSelection.java @@ -12,6 +12,7 @@ public class DatasetSelection { private Integer slice; // Intervall to read images private Integer modulo; + private boolean virtualStack; public List getDatasets() { return datasets; @@ -37,4 +38,10 @@ public class DatasetSelection { public Integer getModulo() { return modulo; } + public void setVirtualStack(boolean virtualStack) { + this.virtualStack = virtualStack; + } + public boolean isVirtualStack(){ + return this.virtualStack; + } } diff --git a/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java b/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java index a9e435b..63262a7 100644 --- a/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java +++ b/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java @@ -51,6 +51,7 @@ public class HDF5Reader implements PlugIn { // Read HDF5 file H5File file = null; + boolean close = true; try { file = new H5File(filename, H5File.READ); file.setMaxMembers(Integer.MAX_VALUE); @@ -58,6 +59,8 @@ public class HDF5Reader implements PlugIn { List datasets = HDF5Utilities.getDatasets(file); DatasetSelection selectedDatasets = selectDatasets(datasets); + // TODO to be removed - Workaround virtual stack - keep HDF5 file open at the end + close=!selectedDatasets.isVirtualStack(); // TODO Remove @@ -208,64 +211,70 @@ public class HDF5Reader implements PlugIn { ImageStack stack; - if(selectedDatasets.getSlice()!=null){ - - // Select what to readout - long[] selected = var.getSelectedDims(); - selected[0] = 1; - selected[1] = dimensions[1]; - selected[2] = dimensions[2]; - - long[] start = var.getStartDims(); - start[0] = selectedDatasets.getSlice(); - - Object wholeDataset = var.read(); - - stack = new ImageStack((int) dimensions[2], (int) dimensions[1]); - int size = (int) (dimensions[1] * dimensions[2]); - -// int startIdx = selectedDatasets.getSlice() * size; - addSlice(stack, wholeDataset, 0, size); + if(selectedDatasets.isVirtualStack()){ + logger.info("Use virtual stack"); + stack = new VirtualStackHDF5(var); } - else if(selectedDatasets.getModulo()!=null){ - logger.info("Read every "+selectedDatasets.getModulo()+" image"); - // Select what to readout - - stack = new ImageStack((int) dimensions[2], (int) dimensions[1]); - - for(int indexToRead=0;indexToRead list; private JCheckBox checkbox; - private JCheckBox chckbxNewCheckBox; + private JCheckBox checkBoxVirtualStack; private JLabel lblSlice; private JPanel panel; private JTextField textField; @@ -63,8 +63,8 @@ public class SelectionPanel extends JPanel { checkbox = new JCheckBox("Group Datasets (2D datasets only)"); add(checkbox); - chckbxNewCheckBox = new JCheckBox("Virtual Stack"); - add(chckbxNewCheckBox); + checkBoxVirtualStack = new JCheckBox("Virtual Stack"); + add(checkBoxVirtualStack); panel = new JPanel(); FlowLayout flowLayout = (FlowLayout) panel.getLayout(); @@ -102,4 +102,8 @@ public class SelectionPanel extends JPanel { } return null; } + + public boolean useVirtualStack(){ + return checkBoxVirtualStack.isSelected(); + } } diff --git a/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java b/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java new file mode 100644 index 0000000..3716f1d --- /dev/null +++ b/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java @@ -0,0 +1,157 @@ +package ch.psi.imagej.hdf5; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import ncsa.hdf.object.Dataset; +import ij.ImageStack; +import ij.process.ByteProcessor; +import ij.process.ColorProcessor; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; +import ij.process.ShortProcessor; + +public class VirtualStackHDF5 extends ImageStack { + + + private static final Logger logger = Logger.getLogger(VirtualStackHDF5.class.getName()); + + private int bitDepth = 0; + private Dataset dataset; + + public VirtualStackHDF5(Dataset dataset){ + super((int) dataset.getDims()[2], (int) dataset.getDims()[1]); + this.dataset = dataset; + } + + /** Does noting. */ + public void addSlice(String sliceLabel, Object pixels) { + } + + /** Does nothing.. */ + public void addSlice(String sliceLabel, ImageProcessor ip) { + } + + /** Does noting. */ + public void addSlice(String sliceLabel, ImageProcessor ip, int n) { + } + + /** Does noting. */ + public void deleteSlice(int n) { + } + + /** Does noting. */ + public void deleteLastSlice() { + } + + public Object getPixels(int slice) { + try { + long[] dimensions = dataset.getDims(); + + // Select what to readout + long[] selected = dataset.getSelectedDims(); + selected[0] = 1; + selected[1] = dimensions[1]; + selected[2] = dimensions[2]; + + long[] start = dataset.getStartDims(); + start[0] = slice-1; // Indexing at image J starts at 1 + + Object wholeDataset = dataset.read(); + + if (wholeDataset instanceof byte[]) { + return (byte[]) wholeDataset; + } else if (wholeDataset instanceof short[]) { + return (short[]) wholeDataset; + } else if (wholeDataset instanceof int[]) { + return HDF5Utilities.convertToFloat((int[]) wholeDataset); + } else if (wholeDataset instanceof long[]) { + return HDF5Utilities.convertToFloat((long[]) wholeDataset); + } else if (wholeDataset instanceof float[]) { + return (float[]) wholeDataset; + } else if (wholeDataset instanceof double[]) { + return HDF5Utilities.convertToFloat((double[]) wholeDataset); + } else { + logger.warning("Datatype not supported"); + } + } catch (OutOfMemoryError | Exception e) { + logger.log(Level.WARNING, "Unable to open slice", e); + } + + return null; + } + + /** + * Assigns a pixel array to the specified slice, were 1<=n<=nslices. + */ + public void setPixels(Object pixels, int n) { + } + + /** + * Returns an ImageProcessor for the specified slice, were 1<=n<=nslices. + * Returns null if the stack is empty. + */ + public ImageProcessor getProcessor(int slice) { + + long[] dimensions = dataset.getDims(); + final Object pixels = getPixels(slice); + + // Todo support more ImageProcessor types + ImageProcessor ip; + + if (pixels instanceof byte[]){ + ip = new ByteProcessor((int) dimensions[2], (int) dimensions[1]); + } + else if (pixels instanceof short[]){ + ip = new ShortProcessor((int) dimensions[2], (int) dimensions[1]); + } + else if (pixels instanceof int[]){ + ip = new ColorProcessor((int) dimensions[2], (int) dimensions[1]); + } + else if (pixels instanceof float[]){ + ip = new FloatProcessor((int) dimensions[2], (int) dimensions[1]); + } + else { + throw new IllegalArgumentException("Unknown stack type"); + } + + ip.setPixels(pixels); + return ip; + } + + /** Returns the number of slices in this stack. */ + public int getSize() { + return (int) this.dataset.getDims()[0]; + } + + /** Returns the label of the Nth image. */ + public String getSliceLabel(int slice) { + return "Slice: "+slice; + } + + /** Returns null. */ + public Object[] getImageArray() { + return null; + } + + /** Does nothing. */ + public void setSliceLabel(String label, int n) { + } + + /** Always return true. */ + public boolean isVirtual() { + return true; + } + + /** Does nothing. */ + public void trim() { + } + + /** + * Returns the bit depth (8, 16, 24 or 32), or 0 if the bit depth is not + * known. + */ + public int getBitDepth() { + return bitDepth; + } +} From 9ab9b8b355588b58d0557781664da80f26680cf0 Mon Sep 17 00:00:00 2001 From: ebner Date: Tue, 14 Apr 2015 13:55:19 +0200 Subject: [PATCH 2/4] tried to implement close of file CTRLHA-109 --- .../java/ch/psi/imagej/hdf5/HDF5Reader.java | 2 +- .../ch/psi/imagej/hdf5/SelectionPanel.java | 1 + .../ch/psi/imagej/hdf5/VirtualStackHDF5.java | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java b/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java index 63262a7..a6b1191 100644 --- a/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java +++ b/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java @@ -213,7 +213,7 @@ public class HDF5Reader implements PlugIn { if(selectedDatasets.isVirtualStack()){ logger.info("Use virtual stack"); - stack = new VirtualStackHDF5(var); + stack = new VirtualStackHDF5(file, var); } else{ if(selectedDatasets.getSlice()!=null){ diff --git a/src/main/java/ch/psi/imagej/hdf5/SelectionPanel.java b/src/main/java/ch/psi/imagej/hdf5/SelectionPanel.java index 5ed4652..6def54e 100644 --- a/src/main/java/ch/psi/imagej/hdf5/SelectionPanel.java +++ b/src/main/java/ch/psi/imagej/hdf5/SelectionPanel.java @@ -64,6 +64,7 @@ public class SelectionPanel extends JPanel { add(checkbox); checkBoxVirtualStack = new JCheckBox("Virtual Stack"); + checkBoxVirtualStack.setSelected(true); add(checkBoxVirtualStack); panel = new JPanel(); diff --git a/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java b/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java index 3716f1d..0cfc5d1 100644 --- a/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java +++ b/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java @@ -4,6 +4,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import ncsa.hdf.object.Dataset; +import ncsa.hdf.object.h5.H5File; import ij.ImageStack; import ij.process.ByteProcessor; import ij.process.ColorProcessor; @@ -18,10 +19,12 @@ public class VirtualStackHDF5 extends ImageStack { private int bitDepth = 0; private Dataset dataset; + private H5File file; - public VirtualStackHDF5(Dataset dataset){ + public VirtualStackHDF5(H5File file, Dataset dataset){ super((int) dataset.getDims()[2], (int) dataset.getDims()[1]); this.dataset = dataset; + this.file = file; } /** Does noting. */ @@ -154,4 +157,18 @@ public class VirtualStackHDF5 extends ImageStack { public int getBitDepth() { return bitDepth; } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + + logger.info("Closing HDF5 file"); + try{ + file.close(); + } + catch(Exception e){ + logger.log(Level.WARNING, "Unable to close HDF5 file", e); + } + + } } From bbefd328a819337070625d9533eed94378cd90a8 Mon Sep 17 00:00:00 2001 From: ebner Date: Tue, 14 Apr 2015 14:15:13 +0200 Subject: [PATCH 3/4] Fixed memory leak that was introduced with the VirtualStack workaround - its still a workaround so CTRLHA-109 --- .../java/ch/psi/imagej/hdf5/HDF5Reader.java | 2 +- .../ch/psi/imagej/hdf5/ImagePlusHDF5.java | 64 +++++++++++++++++++ .../ch/psi/imagej/hdf5/VirtualStackHDF5.java | 8 +-- 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/main/java/ch/psi/imagej/hdf5/ImagePlusHDF5.java diff --git a/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java b/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java index a6b1191..d32a777 100644 --- a/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java +++ b/src/main/java/ch/psi/imagej/hdf5/HDF5Reader.java @@ -278,7 +278,7 @@ public class HDF5Reader implements PlugIn { } } - ImagePlus imp = new ImagePlus(filename + " " + datasetName, stack); + ImagePlus imp = new ImagePlusHDF5(filename + " " + datasetName, stack); imp.resetDisplayRange(); imp.show(); diff --git a/src/main/java/ch/psi/imagej/hdf5/ImagePlusHDF5.java b/src/main/java/ch/psi/imagej/hdf5/ImagePlusHDF5.java new file mode 100644 index 0000000..722509e --- /dev/null +++ b/src/main/java/ch/psi/imagej/hdf5/ImagePlusHDF5.java @@ -0,0 +1,64 @@ +package ch.psi.imagej.hdf5; + +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.logging.Logger; + +import ij.ImagePlus; +import ij.ImageStack; + +public class ImagePlusHDF5 extends ImagePlus { + + + private static final Logger logger = Logger.getLogger(ImagePlusHDF5.class.getName()); + + public ImagePlusHDF5(String title, ImageStack stack) { + super(title, stack); + } + + @Override + public void show() { + super.show(); + getWindow().addWindowListener(new WindowListener() { + + @Override + public void windowOpened(WindowEvent e) { + logger.info(""); + } + + @Override + public void windowIconified(WindowEvent e) { + logger.info(""); + } + + @Override + public void windowDeiconified(WindowEvent e) { + logger.info(""); + } + + @Override + public void windowDeactivated(WindowEvent e) { + logger.info(""); + } + + @Override + public void windowClosing(WindowEvent e) { + logger.info("Closing"); + } + + @Override + public void windowClosed(WindowEvent e) { + logger.info("Closed"); + ImageStack stack = getStack(); + if(stack instanceof VirtualStackHDF5){ + ((VirtualStackHDF5) stack).close(); + } + } + + @Override + public void windowActivated(WindowEvent e) { + logger.info(""); + } + }); + } +} diff --git a/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java b/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java index 0cfc5d1..ed648a6 100644 --- a/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java +++ b/src/main/java/ch/psi/imagej/hdf5/VirtualStackHDF5.java @@ -158,10 +158,10 @@ public class VirtualStackHDF5 extends ImageStack { return bitDepth; } - @Override - protected void finalize() throws Throwable { - super.finalize(); - + /** + * Close HDF5 file + */ + public void close() { logger.info("Closing HDF5 file"); try{ file.close(); From e5331e0c2d5d33dffee6906b5db5bda33f144e5a Mon Sep 17 00:00:00 2001 From: ebner Date: Tue, 14 Apr 2015 14:17:15 +0200 Subject: [PATCH 4/4] Updated version CTRLHA-109 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4852c43..023f899 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 ch.psi imagej.hdf5 - 0.8.0 + 0.9.0