diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java index 18455e27d6e43148ec32634c5f593f3134e53885..867effcf27ba32ba93ec118ed9feb56c673d1573 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java @@ -21,6 +21,7 @@ import static ch.systemsx.cisd.common.utilities.DataTypeUtil.JPEG_FILE; import static ch.systemsx.cisd.common.utilities.DataTypeUtil.PNG_FILE; import static ch.systemsx.cisd.common.utilities.DataTypeUtil.TIFF_FILE; +import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; @@ -83,6 +84,8 @@ public class ImageUtil private static interface ImageLoader { public BufferedImage load(IRandomAccessFile raf, ImageID imageID); + + public Dimension readDimension(IRandomAccessFile handle, ImageID imageID); } private static final class TiffImageLoader implements ImageLoader @@ -115,6 +118,33 @@ public class ImageUtil } } } + + public Dimension readDimension(IRandomAccessFile handle, ImageID imageID) + { + handle.mark(MAX_READ_AHEAD); + try + { + return loadDimensionWithBioFormats(handle, imageID); + } catch (RuntimeException ex1) + { + try + { + return loadDimensionJavaAdvancedImagingTiff(handle, imageID); + } catch (RuntimeException ex2) + { + if (imageID.equals(ImageID.NULL)) + { + handle.reset(); + // There are some TIFF files which cannot be opened by JAI, try ImageJ + // instead... + return loadDimensionWithImageJ(handle); + } else + { + throw ex2; + } + } + } + } } private static BufferedImage loadWithImageJ(IRandomAccessFile handle) @@ -122,12 +152,24 @@ public class ImageUtil return loadWithLibrary(handle, ImageID.NULL, ImageReaderConstants.IMAGEJ_LIBRARY, "tiff"); } + private static Dimension loadDimensionWithImageJ(IRandomAccessFile handle) + { + return loadDimensionWithLibrary(handle, ImageID.NULL, ImageReaderConstants.IMAGEJ_LIBRARY, + "tiff"); + } + private static BufferedImage loadWithBioFormats(IRandomAccessFile handle, ImageID imageID) { return loadWithLibrary(handle, imageID, ImageReaderConstants.BIOFORMATS_LIBRARY, "TiffDelegateReader"); } + private static Dimension loadDimensionWithBioFormats(IRandomAccessFile handle, ImageID imageID) + { + return loadDimensionWithLibrary(handle, imageID, ImageReaderConstants.BIOFORMATS_LIBRARY, + "TiffDelegateReader"); + } + /** * For experts only! Loads some kinds of TIFF images handled by JAI library. */ @@ -137,6 +179,15 @@ public class ImageUtil return loadWithLibrary(handle, imageID, ImageReaderConstants.JAI_LIBRARY, "tiff"); } + /** + * For experts only! Loads some kinds of TIFF images handled by JAI library. + */ + public static Dimension loadDimensionJavaAdvancedImagingTiff(IRandomAccessFile handle, + ImageID imageID) throws EnvironmentFailureException + { + return loadDimensionWithLibrary(handle, imageID, ImageReaderConstants.JAI_LIBRARY, "tiff"); + } + private static BufferedImage loadWithLibrary(IRandomAccessFile handle, ImageID imageIDOrNull, String libraryName, String readerName) { @@ -156,6 +207,25 @@ public class ImageUtil } } + private static Dimension loadDimensionWithLibrary(IRandomAccessFile handle, + ImageID imageIDOrNull, String libraryName, String readerName) + { + operationLog.debug("Load tiff image using " + libraryName); + IImageReader imageReader = ImageReaderFactory.tryGetReader(libraryName, readerName); + if (imageReader == null) + { + throw new IllegalStateException(String.format( + "There is no reader '%s' in image library '%s'.", readerName, libraryName)); + } + try + { + return imageReader.readDimensions(handle, imageIDOrNull); + } catch (Exception ex) + { + throw EnvironmentFailureException.fromTemplate("Cannot decode image.", ex); + } + } + private static final class JavaImageLoader implements ImageLoader { private final String fileType; @@ -184,6 +254,24 @@ public class ImageUtil } } + public Dimension readDimension(IRandomAccessFile handle, ImageID imageID) + { + if (imageID.equals(ImageID.NULL)) + { + IImageReader imageReader = + ImageReaderFactory.tryGetReader(ImageReaderConstants.IMAGEIO_LIBRARY, + fileType); + if (imageReader == null) + { + throw EnvironmentFailureException.fromTemplate( + "Cannot find ImageIO reader for file type '%s'", fileType); + } + return imageReader.readDimensions(handle, imageID); + } else + { + throw new UnsupportedOperationException(); + } + } } private static final Map<String, ImageLoader> imageLoaders = new HashMap<String, ImageLoader>(); @@ -247,6 +335,43 @@ public class ImageUtil return loadImageGuessingLibrary(contentNode, imageID); } + /** + * Loads the image specified by <var>imageIdOrNull</var> from the given </var>inputStream</var>. + * Supported images formats are GIF, JPG, PNG, and TIFF. The input stream will be closed after + * loading. + * <p> + * Note that the original color depth will be kept, so e.g. 12 or 16 bit grayscale images will + * not be converted to RGB. + * + * @throws IllegalArgumentException if the input stream doesn't start with a magic number + * identifying supported image format. + */ + public static Dimension loadUnchangedImageDimension(IHierarchicalContentNode contentNode, + String imageIdOrNull, String imageLibraryNameOrNull, String imageLibraryReaderNameOrNull) + { + assert (imageLibraryReaderNameOrNull == null || imageLibraryNameOrNull != null) : "if image reader " + + "is specified then library name should be specified as well"; + ImageID imageID = parseImageID(imageIdOrNull); + if (imageLibraryNameOrNull != null && imageLibraryReaderNameOrNull != null) + { + IImageReader reader = + ImageReaderFactory.tryGetReader(imageLibraryNameOrNull, + imageLibraryReaderNameOrNull); + if (reader != null) + { + IRandomAccessFile handle = contentNode.getFileContent(); + try + { + return reader.readDimensions(handle, imageID); + } finally + { + closeQuietly(handle); + } + } + } + return loadImageDimensionGuessingLibrary(contentNode, imageID); + } + /** * Converts the given <var>image</var> to a PNG image. Uses fast parameters for the filter and * deflate level (no filter and no deflation). @@ -412,6 +537,14 @@ public class ImageUtil return loadImageGuessingLibrary(handle, fileType, imageID); } + private static Dimension loadImageDimensionGuessingLibrary( + IHierarchicalContentNode contentNode, ImageID imageID) + { + IRandomAccessFile handle = contentNode.getFileContent(); + String fileType = DataTypeUtil.tryToFigureOutFileTypeOf(handle); + return loadImageDimensionGuessingLibrary(handle, fileType, imageID); + } + /** * Loads the image specified by <var>imageID</var> from the image from the given * </var>handle</var>. Supported images formats are GIF, JPG, PNG, and TIFF. The input stream @@ -443,6 +576,29 @@ public class ImageUtil } } + private static Dimension loadImageDimensionGuessingLibrary(IRandomAccessFile handle, + String fileType, ImageID imageID) + { + try + { + if (fileType == null) + { + throw new IllegalArgumentException( + "File type of an image input stream couldn't be determined."); + } + ImageLoader imageLoader = imageLoaders.get(fileType); + if (imageLoader == null) + { + throw new IllegalArgumentException("Unable to load image of file type '" + fileType + + "'."); + } + return imageLoader.readDimension(handle, imageID); + } finally + { + closeQuietly(handle); + } + } + /** * Only for tests */ diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbsoluteImageReference.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbsoluteImageReference.java index c653eea86c63c9189b96c71d4fae1b619fbb26fc..95d90d19c9804a433f8ba3717bd8b8a1a55740bc 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbsoluteImageReference.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbsoluteImageReference.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.openbis.dss.etl; +import java.awt.Dimension; import java.awt.image.BufferedImage; import ch.systemsx.cisd.common.io.hierarchical_content.api.IHierarchicalContentNode; @@ -119,6 +120,20 @@ public class AbsoluteImageReference extends AbstractImageReference imageLibraryReaderNameOrNull, null); } + static Dimension loadUnchangedImageDimension(IHierarchicalContentNode contentNode, + String imageIdOrNull, ImageLibraryInfo imageLibraryOrNull) + { + String imageLibraryNameOrNull = null; + String imageLibraryReaderNameOrNull = null; + if (imageLibraryOrNull != null) + { + imageLibraryNameOrNull = imageLibraryOrNull.getName(); + imageLibraryReaderNameOrNull = imageLibraryOrNull.getReaderName(); + } + return ImageUtil.loadUnchangedImageDimension(contentNode, imageIdOrNull, + imageLibraryNameOrNull, imageLibraryReaderNameOrNull); + } + public RequestedImageSize getRequestedSize() { return imageSize; diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java index b30e716206c37dfbea79c32f4657a714b0ce344d..f3145f15c3af77f014aa659e739b142817c038a7 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.openbis.dss.etl; +import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -43,8 +44,8 @@ import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.common.process.ProcessExecutionHelper; import ch.systemsx.cisd.common.process.ProcessIOStrategy; import ch.systemsx.cisd.common.process.ProcessResult; -import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo; +import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailFilePaths; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageStorageConfiguraton; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ThumbnailsStorageFormat; @@ -68,10 +69,10 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient */ public static ThumbnailFilePaths tryGenerateThumbnails(List<RelativeImageFile> images, File imagesParentDirectory, String thumbnailFilePath, - ImageStorageConfiguraton imageStorageConfiguraton, String thumbnailPhysicalDatasetPermId) + ImageStorageConfiguraton imageStorageConfiguraton, + String thumbnailPhysicalDatasetPermId, + ThumbnailsStorageFormat thumbnailsStorageFormatOrNull) { - ThumbnailsStorageFormat thumbnailsStorageFormatOrNull = - imageStorageConfiguraton.getThumbnailsStorageFormat(); if (thumbnailsStorageFormatOrNull != null) { ThumbnailFilePaths thumbnailPaths = @@ -211,6 +212,19 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient String size = thumbnailsStorageFormat.getMaxWidth() + "x" + thumbnailsStorageFormat.getMaxHeight(); + + if (thumbnailsStorageFormat.getZoomLevel() != null) + { + Dimension originalSize = loadUnchangedImageDimension(imageFile, null); + long x = + (int) Math.round(thumbnailsStorageFormat.getZoomLevel() + * originalSize.getWidth()); + long y = + (int) Math.round(thumbnailsStorageFormat.getZoomLevel() + * originalSize.getHeight()); + size = x + "x" + y; + } + String imageFilePath = imageFile.getPath(); List<String> params = new ArrayList<String>(); params.addAll(Arrays.asList(convertUtilityOrNull.getPath(), imageFilePath, "-scale", size)); @@ -239,9 +253,17 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient ByteArrayOutputStream bufferOutputStream) throws IOException { BufferedImage image = loadUnchangedImage(imageFile, imageIdOrNull); + + long x = thumbnailsStorageFormat.getMaxWidth(); + long y = thumbnailsStorageFormat.getMaxHeight(); + if (thumbnailsStorageFormat.getZoomLevel() != null) + { + x = Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getWidth()); + y = Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getHeight()); + } + BufferedImage thumbnail = - ImageUtil.rescale(image, thumbnailsStorageFormat.getMaxWidth(), - thumbnailsStorageFormat.getMaxHeight(), false, + ImageUtil.rescale(image, (int) x, (int) y, false, thumbnailsStorageFormat.isHighQuality()); ImageUtil.writeImageToPng(thumbnail, bufferOutputStream); return bufferOutputStream.toByteArray(); @@ -253,6 +275,12 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient imageIdOrNull, imageLibraryOrNull); } + private Dimension loadUnchangedImageDimension(File imageFile, String imageIdOrNull) + { + return AbsoluteImageReference.loadUnchangedImageDimension(new FileBasedContentNode( + imageFile), imageIdOrNull, imageLibraryOrNull); + } + private Status createStatus(String thumbnailPath, IOException ex) { logger.warn("Retriable error when creating thumbnail '" + thumbnailPath + "'", ex); diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetStructure.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetStructure.java index 2c48a25dda81c409043b1f4dfbf7e320910199ba..21b727cda33b1832b3f1bcd48f938338cec59b36 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetStructure.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetStructure.java @@ -79,7 +79,7 @@ public class ImageDataSetStructure public boolean areThumbnailsGenerated() { - return getImageStorageConfiguraton().getThumbnailsStorageFormat() != null; + return getImageStorageConfiguraton().getThumbnailsStorageFormat().size() > 0; } // ------ setters diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ImageStorageConfiguraton.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ImageStorageConfiguraton.java index 9625dc3fc1969b74daca684862b6811b826e3aba..2b964329b07a3f2b4746689d463b1ed45e5426d7 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ImageStorageConfiguraton.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ImageStorageConfiguraton.java @@ -16,6 +16,9 @@ package ch.systemsx.cisd.openbis.dss.etl.dto.api.v1; +import java.util.ArrayList; +import java.util.List; + import ch.systemsx.cisd.base.image.IImageTransformerFactory; import ch.systemsx.cisd.common.utilities.AbstractHashable; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo; @@ -40,7 +43,8 @@ public class ImageStorageConfiguraton extends AbstractHashable // --- State ---------- /** No thumbnails are generated by default. */ - private ThumbnailsStorageFormat thumbnailsStorageFormatOrNull = null; + private List<ThumbnailsStorageFormat> thumbnailsStorageFormatsList = + new ArrayList<ThumbnailsStorageFormat>(); private OriginalDataStorageFormat originalDataStorageFormat = OriginalDataStorageFormat.UNCHANGED; @@ -62,15 +66,28 @@ public class ImageStorageConfiguraton extends AbstractHashable // --- Getters & Setters ---------- /** @return null if no thumbnails should be generated */ - public ThumbnailsStorageFormat getThumbnailsStorageFormat() + public List<ThumbnailsStorageFormat> getThumbnailsStorageFormat() { - return thumbnailsStorageFormatOrNull; + return thumbnailsStorageFormatsList; } - /** Set to null if no thumbnails should be generated */ + /** Set to null if no thumbnails should be generated. Overrides previous thumbnails settings. */ public void setThumbnailsStorageFormat(ThumbnailsStorageFormat thumbnailsStorageFormatOrNull) { - this.thumbnailsStorageFormatOrNull = thumbnailsStorageFormatOrNull; + thumbnailsStorageFormatsList.clear(); + addThumbnailsStorageFormat(thumbnailsStorageFormatOrNull); + + } + + /** + * Adds new thumbnails setting to the list + */ + public void addThumbnailsStorageFormat(ThumbnailsStorageFormat thumbnailsStorageFormatOrNull) + { + if (thumbnailsStorageFormatOrNull != null) + { + thumbnailsStorageFormatsList.add(thumbnailsStorageFormatOrNull); + } } /** @@ -79,7 +96,8 @@ public class ImageStorageConfiguraton extends AbstractHashable */ public void switchOnThumbnailsGeneration() { - this.thumbnailsStorageFormatOrNull = new ThumbnailsStorageFormat(); + thumbnailsStorageFormatsList.clear(); + thumbnailsStorageFormatsList.add(new ThumbnailsStorageFormat()); } public OriginalDataStorageFormat getOriginalDataStorageFormat() @@ -143,9 +161,12 @@ public class ImageStorageConfiguraton extends AbstractHashable appendNameAndObject(buffer, "original data storage format", originalDataStorageFormat.toString()); } - if (thumbnailsStorageFormatOrNull != null) + if (thumbnailsStorageFormatsList.size() > 0) { - appendNameAndObject(buffer, "thumbnails", thumbnailsStorageFormatOrNull.toString()); + for (ThumbnailsStorageFormat thumbnailsStorageFormat : thumbnailsStorageFormatsList) + { + appendNameAndObject(buffer, "thumbnails", thumbnailsStorageFormat.toString()); + } } if (storeChannelsOnExperimentLevelOrNull != null) { diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/SimpleImageDataConfig.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/SimpleImageDataConfig.java index e58045e06e8e6ef33498f55cde1279f8a90fc847..5d9b274d0aa2d350fd06c0f0ef541476df2cf245 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/SimpleImageDataConfig.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/SimpleImageDataConfig.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.openbis.dss.etl.dto.api.v1; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -23,6 +24,9 @@ import java.util.List; import ch.systemsx.cisd.base.image.IImageTransformerFactory; import ch.systemsx.cisd.common.shared.basic.utils.StringUtils; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.thumbnails.DefaultThumbnailsConfiguration; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.thumbnails.IThumbnailsConfiguration; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.thumbnails.ZoomLevelBasedThumbnailsConfiguration; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.transformations.ConvertToolImageTransformerFactory; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.transformations.ImageTransformation; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.transformations.ImageTransformationBuffer; @@ -200,7 +204,8 @@ abstract public class SimpleImageDataConfig private String[] recognizedImageExtensions = new String[] { "tiff", "tif", "png", "gif", "jpg", "jpeg" }; - private boolean generateThumbnails = false; + private List<IThumbnailsConfiguration> thumbnailsPyramid = + new ArrayList<IThumbnailsConfiguration>(); private int maxThumbnailWidthAndHeight = 256; @@ -244,17 +249,10 @@ abstract public class SimpleImageDataConfig imageStorageConfiguraton .setStoreChannelsOnExperimentLevel(isStoreChannelsOnExperimentLevel()); imageStorageConfiguraton.setOriginalDataStorageFormat(getOriginalDataStorageFormat()); - if (isGenerateThumbnails()) + for (IThumbnailsConfiguration thumbnailsConfiguration : thumbnailsPyramid) { - ThumbnailsStorageFormat thumbnailsStorageFormat = new ThumbnailsStorageFormat(); - thumbnailsStorageFormat - .setAllowedMachineLoadDuringGeneration(getAllowedMachineLoadDuringThumbnailsGeneration()); - thumbnailsStorageFormat.setMaxWidth(getMaxThumbnailWidthAndHeight()); - thumbnailsStorageFormat.setMaxHeight(getMaxThumbnailWidthAndHeight()); - thumbnailsStorageFormat.setGenerateWithImageMagic(generateThumbnailsWithImageMagic); - thumbnailsStorageFormat.setImageMagicParams(thumbnailsGenerationImageMagicParams); - thumbnailsStorageFormat.setHighQuality(generateThumbnailsIn8BitHighQuality); - imageStorageConfiguraton.setThumbnailsStorageFormat(thumbnailsStorageFormat); + imageStorageConfiguraton.addThumbnailsStorageFormat(thumbnailsConfiguration + .getThumbnailsStorageFormat(this)); } if (false == StringUtils.isBlank(convertTransformationCliArgumentsOrNull)) { @@ -284,7 +282,7 @@ abstract public class SimpleImageDataConfig public boolean isGenerateThumbnails() { - return generateThumbnails; + return thumbnailsPyramid.size() > 0; } public int getMaxThumbnailWidthAndHeight() @@ -358,7 +356,22 @@ abstract public class SimpleImageDataConfig /** should thumbnails be generated? False by default. */ public void setGenerateThumbnails(boolean generateThumbnails) { - this.generateThumbnails = generateThumbnails; + thumbnailsPyramid.clear(); + thumbnailsPyramid.add(new DefaultThumbnailsConfiguration()); + } + + public void setGenerateThumbnailsPyramid(double[] zoomLevels) + { + if (zoomLevels == null) + { + thumbnailsPyramid.clear(); + } else + { + for (double zoomLevel : zoomLevels) + { + thumbnailsPyramid.add(new ZoomLevelBasedThumbnailsConfiguration(zoomLevel)); + } + } } /** the maximal width and height of the generated thumbnails */ @@ -391,6 +404,11 @@ abstract public class SimpleImageDataConfig this.generateThumbnailsWithImageMagic = generateWithImageMagic; } + public boolean getGenerateThumbnailsWithImageMagic() + { + return generateThumbnailsWithImageMagic; + } + /** * Sets additional parameters which should be passed to ImageMagic 'convert' utility when it is * used to generate thumbnails. @@ -403,6 +421,11 @@ abstract public class SimpleImageDataConfig this.thumbnailsGenerationImageMagicParams = Arrays.asList(imageMagicParams); } + public List<String> getThumbnailsGenerationImageMagicParams() + { + return thumbnailsGenerationImageMagicParams; + } + /** * If true and thumbnails generation is switched on, thumbnails will be generated with high * quality. @@ -416,6 +439,11 @@ abstract public class SimpleImageDataConfig this.generateThumbnailsIn8BitHighQuality = highQualityThumbnails; } + public boolean getGenerateThumbnailsIn8BitHighQuality() + { + return generateThumbnailsIn8BitHighQuality; + } + /** * See {@link #setGenerateHighQuality8BitThumbnails}. * diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ThumbnailsStorageFormat.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ThumbnailsStorageFormat.java index 2b11cceba9f51a07ba1eb971f9dff17428247f46..736b1f09fa668866fbfb2b2b2b3529621a0ca862 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ThumbnailsStorageFormat.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/ThumbnailsStorageFormat.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.List; import ch.systemsx.cisd.common.utilities.AbstractHashable; +import ch.systemsx.cisd.openbis.dss.Constants; /** * Configuration parameters which describe how thumbnails should be generated. @@ -40,6 +41,8 @@ public class ThumbnailsStorageFormat extends AbstractHashable private int maxHeight = DEFAULT_THUMBNAIL_MAX_SIZE; + private Double zoomLevel = null; + private boolean storeCompressed = DEFAULT_COMPRESS_THUMBNAILS; private double allowedMachineLoadDuringGeneration = 1; @@ -50,6 +53,8 @@ public class ThumbnailsStorageFormat extends AbstractHashable private List<String> imageMagicParams = Collections.emptyList(); + private String thumbnailsFileName; + /** * Creates empty object which instructs that the thumbnails should be generated with default * settings. Use setters to change default behaviour (you will probably not have to). @@ -153,4 +158,24 @@ public class ThumbnailsStorageFormat extends AbstractHashable this.imageMagicParams = imageMagicParams; } + public Double getZoomLevel() + { + return zoomLevel; + } + + public void setZoomLevel(Double zoomLevel) + { + this.zoomLevel = zoomLevel; + } + + public String getThumbnailsFileName() + { + return thumbnailsFileName == null ? Constants.HDF5_CONTAINER_THUMBNAILS_FILE_NAME + : thumbnailsFileName; + } + + public void setThumbnailsFileName(String thumbnailsFileName) + { + this.thumbnailsFileName = thumbnailsFileName; + } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/AbstractThumbnailsConfiguration.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/AbstractThumbnailsConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..2f1987fa810375d5e49a857be21087f4d7915020 --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/AbstractThumbnailsConfiguration.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.thumbnails; + +/** + * @author Pawel Glyzewski + */ +public abstract class AbstractThumbnailsConfiguration implements IThumbnailsConfiguration +{ + +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/DefaultThumbnailsConfiguration.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/DefaultThumbnailsConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..4088edbaa9978632c3514f8752d493a9f18cd1e8 --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/DefaultThumbnailsConfiguration.java @@ -0,0 +1,41 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.thumbnails; + +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.SimpleImageDataConfig; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ThumbnailsStorageFormat; + +/** + * @author Pawel Glyzewski + */ +public class DefaultThumbnailsConfiguration extends AbstractThumbnailsConfiguration +{ + public ThumbnailsStorageFormat getThumbnailsStorageFormat(SimpleImageDataConfig config) + { + ThumbnailsStorageFormat thumbnailsStorageFormat = new ThumbnailsStorageFormat(); + thumbnailsStorageFormat.setAllowedMachineLoadDuringGeneration(config + .getAllowedMachineLoadDuringThumbnailsGeneration()); + thumbnailsStorageFormat.setMaxWidth(config.getMaxThumbnailWidthAndHeight()); + thumbnailsStorageFormat.setMaxHeight(config.getMaxThumbnailWidthAndHeight()); + thumbnailsStorageFormat.setGenerateWithImageMagic(config + .getGenerateThumbnailsWithImageMagic()); + thumbnailsStorageFormat.setImageMagicParams(config + .getThumbnailsGenerationImageMagicParams()); + thumbnailsStorageFormat.setHighQuality(config.getGenerateThumbnailsIn8BitHighQuality()); + return thumbnailsStorageFormat; + } +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/IThumbnailsConfiguration.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/IThumbnailsConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..411cb80eb4f2138f52e1f4dec6d6c6bd468f914a --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/IThumbnailsConfiguration.java @@ -0,0 +1,28 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.thumbnails; + +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.SimpleImageDataConfig; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ThumbnailsStorageFormat; + +/** + * @author Pawel Glyzewski + */ +public interface IThumbnailsConfiguration +{ + public ThumbnailsStorageFormat getThumbnailsStorageFormat(SimpleImageDataConfig config); +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/ZoomLevelBasedThumbnailsConfiguration.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/ZoomLevelBasedThumbnailsConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..64657150acf9accb81515fbbc87835fca7bb62c6 --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/v1/thumbnails/ZoomLevelBasedThumbnailsConfiguration.java @@ -0,0 +1,49 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.thumbnails; + +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.SimpleImageDataConfig; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ThumbnailsStorageFormat; + +/** + * @author Pawel Glyzewski + */ +public class ZoomLevelBasedThumbnailsConfiguration extends AbstractThumbnailsConfiguration +{ + private double zoomLevel; + + public ZoomLevelBasedThumbnailsConfiguration(double zoomLevel) + { + this.zoomLevel = zoomLevel; + } + + public ThumbnailsStorageFormat getThumbnailsStorageFormat(SimpleImageDataConfig config) + { + ThumbnailsStorageFormat thumbnailsStorageFormat = new ThumbnailsStorageFormat(); + thumbnailsStorageFormat.setAllowedMachineLoadDuringGeneration(config + .getAllowedMachineLoadDuringThumbnailsGeneration()); + thumbnailsStorageFormat.setZoomLevel(zoomLevel); + thumbnailsStorageFormat.setThumbnailsFileName(String.format("thumbnails_%.0f%%.h5", + zoomLevel * 100.0)); + thumbnailsStorageFormat.setGenerateWithImageMagic(config + .getGenerateThumbnailsWithImageMagic()); + thumbnailsStorageFormat.setImageMagicParams(config + .getThumbnailsGenerationImageMagicParams()); + thumbnailsStorageFormat.setHighQuality(config.getGenerateThumbnailsIn8BitHighQuality()); + return thumbnailsStorageFormat; + } +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java index db29ded27713fd02364cd66084fbfaba935d3bd3..1054c30c7d3a6477ba585444499b89a7763a2f88 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java @@ -44,6 +44,7 @@ import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IImagingDataSetRegistrationTr import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IImagingDatasetFactory; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageFileInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.SimpleImageDataConfig; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ThumbnailsStorageFormat; import ch.systemsx.cisd.openbis.dss.etl.featurevector.CsvFeatureVectorParser; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; @@ -419,17 +420,25 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data List<String> containedDataSetCodes = new ArrayList<String>(); // create thumbnails dataset if needed - IDataSet thumbnailDatasetOrNull = null; + List<IDataSet> thumbnailDatasets = new ArrayList<IDataSet>(); boolean generateThumbnails = imageDataSetStructure.areThumbnailsGenerated(); if (generateThumbnails) { - thumbnailDatasetOrNull = createThumbnailDataset(); + List<ThumbnailsStorageFormat> thumbnailsStorageFormatList = + imageDataSetStructure.getImageStorageConfiguraton() + .getThumbnailsStorageFormat(); - ThumbnailFilePaths thumbnailPaths = - generateThumbnails(imageDataSetStructure, incomingDirectory, - thumbnailDatasetOrNull); - imageDataSetInformation.setThumbnailFilePaths(thumbnailPaths); - containedDataSetCodes.add(thumbnailDatasetOrNull.getDataSetCode()); + for (ThumbnailsStorageFormat thumbnailsStorageFormat : thumbnailsStorageFormatList) + { + IDataSet thumbnailDataset = createThumbnailDataset(); + thumbnailDatasets.add(thumbnailDataset); + + ThumbnailFilePaths thumbnailPaths = + generateThumbnails(imageDataSetStructure, incomingDirectory, + thumbnailDataset, thumbnailsStorageFormat); + imageDataSetInformation.setThumbnailFilePaths(thumbnailPaths); + containedDataSetCodes.add(thumbnailDataset.getDataSetCode()); + } } // create main dataset (with original images) @SuppressWarnings("unchecked") @@ -438,18 +447,17 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data .createNewDataSet(imageRegistrationDetails); containedDataSetCodes.add(mainDataset.getDataSetCode()); - if (thumbnailDatasetOrNull != null) + for (IDataSet thumbnailDataset : thumbnailDatasets) { - setSameDatasetOwner(mainDataset, thumbnailDatasetOrNull); + setSameDatasetOwner(mainDataset, thumbnailDataset); } - ImageContainerDataSet containerDataset = createImageContainerDataset(mainDataset, imageDataSetInformation, containedDataSetCodes); containerDataset.setOriginalDataset(mainDataset); - if (thumbnailDatasetOrNull != null) + for (IDataSet thumbnailDataset : thumbnailDatasets) { - containerDataset.setThumbnailDatasets(Arrays.asList(thumbnailDatasetOrNull)); + containerDataset.setThumbnailDatasets(Arrays.asList(thumbnailDataset)); } imageDataSetInformation.setContainerDatasetPermId(containerDataset.getDataSetCode()); @@ -462,16 +470,27 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data } private ThumbnailFilePaths generateThumbnails(ImageDataSetStructure imageDataSetStructure, - File incomingDirectory, IDataSet thumbnailDataset) + File incomingDirectory, IDataSet thumbnailDataset, + ThumbnailsStorageFormat thumbnailsStorageFormatOrNull) { - String thumbnailFile = - createNewFile(thumbnailDataset, Constants.HDF5_CONTAINER_THUMBNAILS_FILE_NAME); + String thumbnailFile; + if (thumbnailsStorageFormatOrNull == null) + { + thumbnailFile = + createNewFile(thumbnailDataset, + Constants.HDF5_CONTAINER_THUMBNAILS_FILE_NAME); + } else + { + thumbnailFile = + createNewFile(thumbnailDataset, + thumbnailsStorageFormatOrNull.getThumbnailsFileName()); + } List<RelativeImageFile> images = asRelativeImageFile(imageDataSetStructure.getImages()); ThumbnailFilePaths thumbnailPaths = Hdf5ThumbnailGenerator.tryGenerateThumbnails(images, incomingDirectory, thumbnailFile, imageDataSetStructure.getImageStorageConfiguraton(), - thumbnailDataset.getDataSetCode()); + thumbnailDataset.getDataSetCode(), thumbnailsStorageFormatOrNull); return thumbnailPaths; }