diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ServiceVersionHolder.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ServiceVersionHolder.java index 60c37db247db44c4b8a20a80bd9eb3fd94bf7cbc..d14196e7daaba93d70b711684f3a1e3f5a5a10f4 100644 --- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ServiceVersionHolder.java +++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ServiceVersionHolder.java @@ -23,5 +23,5 @@ package ch.systemsx.cisd.openbis.generic.shared.basic.dto; */ public final class ServiceVersionHolder { - public static final int VERSION = 35; // for S145 + public static final int VERSION = 36; // for S190 } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/IImageGenerationAlgorithm.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/IImageGenerationAlgorithm.java new file mode 100644 index 0000000000000000000000000000000000000000..907e1dda8a62a0cf71cbec9d46afac15441f0b24 --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/IImageGenerationAlgorithm.java @@ -0,0 +1,14 @@ +package ch.systemsx.cisd.openbis.dss.etl.dto.api; + +import java.awt.image.BufferedImage; +import java.util.List; + +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetInformation; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetStructure; + +public interface IImageGenerationAlgorithm +{ + public List<BufferedImage> generateImages(ImageDataSetInformation information, ImageDataSetStructure structure); + public String getDataSetTypeCode(); + public String getImageFileName(int index); +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/SimpleImageDataConfig.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/SimpleImageDataConfig.java index acdabb8f08a64458c6afa841099b99d075fb7849..17fd1b64de04e08f4d65e259fb87ce266f1d5407 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/SimpleImageDataConfig.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/SimpleImageDataConfig.java @@ -47,6 +47,8 @@ abstract public class SimpleImageDataConfig { // --- one of the following two methods has to be overridden ----------------- + private IImageGenerationAlgorithm imageGenerationAlgorithm; + /** * Extracts tile number, channel code and well code for a given relative path to a single image. * This method should overridden to deal with files containing single images. It is ignored if @@ -997,5 +999,12 @@ abstract public class SimpleImageDataConfig { this.colorDepth = colorDepth; } + + public IImageGenerationAlgorithm getImageGenerationAlgorithm() { + return imageGenerationAlgorithm; + } + public void setImageGenerationAlgorithm(IImageGenerationAlgorithm algorithm) { + this.imageGenerationAlgorithm = algorithm; + } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/EmptyImageCreationAlgorithm.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/EmptyImageCreationAlgorithm.java new file mode 100644 index 0000000000000000000000000000000000000000..9f036dea2745158698e3a280142f80451676c55d --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/EmptyImageCreationAlgorithm.java @@ -0,0 +1,33 @@ +package ch.systemsx.cisd.openbis.dss.etl.dto.api.impl; + +import java.awt.image.BufferedImage; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import ch.systemsx.cisd.openbis.dss.etl.dto.api.IImageGenerationAlgorithm; +import ch.systemsx.cisd.openbis.generic.shared.IServer; + +public class EmptyImageCreationAlgorithm implements IImageGenerationAlgorithm, Serializable +{ + + private static final long serialVersionUID = IServer.VERSION; + + @Override + public String getDataSetTypeCode() + { + throw new UnsupportedOperationException(); + } + + @Override + public List<BufferedImage> generateImages(ImageDataSetInformation information, ImageDataSetStructure structure) + { + return Collections.emptyList(); + } + + @Override + public String getImageFileName(int index) + { + throw new UnsupportedOperationException(); + } +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java index 5c893d69d29daea5709c949a1ccae48ddfcfb7ae..88fa3da83bfe12e7fbc42aa8e84f72da4b06af10 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java @@ -24,6 +24,7 @@ import ch.systemsx.cisd.common.shared.basic.string.CommaSeparatedListBuilder; import ch.systemsx.cisd.openbis.dss.etl.dto.api.BasicDataSetInformation; import ch.systemsx.cisd.openbis.dss.etl.dto.api.Channel; import ch.systemsx.cisd.openbis.dss.etl.dto.api.ChannelColorComponent; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.IImageGenerationAlgorithm; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; import ch.systemsx.cisd.openbis.generic.shared.IServer; @@ -60,6 +61,8 @@ public class ImageDataSetInformation extends BasicDataSetInformation private Integer colorDepth; private final List<DataSetInformation> secondaryDataSets = new ArrayList<DataSetInformation>(); + + private IImageGenerationAlgorithm imageGenerationAlgorithm; public File getIncomingDirectory() { @@ -222,4 +225,13 @@ public class ImageDataSetInformation extends BasicDataSetInformation } return buffer.toString(); } + + public void setAlgorithm(IImageGenerationAlgorithm imageGenerationAlgorithm) + { + this.imageGenerationAlgorithm = imageGenerationAlgorithm; + } + + public IImageGenerationAlgorithm getAlgorithm() { + return imageGenerationAlgorithm; + } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/MaximumIntensityProjectionGenerationAlgorithm.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/MaximumIntensityProjectionGenerationAlgorithm.java new file mode 100644 index 0000000000000000000000000000000000000000..4358a40ff6c276cab9e02e26ed5105c6f916eac7 --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/MaximumIntensityProjectionGenerationAlgorithm.java @@ -0,0 +1,173 @@ +package ch.systemsx.cisd.openbis.dss.etl.dto.api.impl; + +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import ch.systemsx.cisd.openbis.common.io.FileBasedContentNode; +import ch.systemsx.cisd.openbis.dss.etl.Utils; +import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.IImageGenerationAlgorithm; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.ImageFileInfo; +import ch.systemsx.cisd.openbis.generic.shared.IServer; + +public class MaximumIntensityProjectionGenerationAlgorithm implements IImageGenerationAlgorithm, Serializable +{ + private static final long serialVersionUID = IServer.VERSION; + + private transient BufferedImage result = null; + + private String dataSetTypeCode; + + private String filename; + + private int width; + + private int height; + + public MaximumIntensityProjectionGenerationAlgorithm(String dataSetTypeCode) { + this(dataSetTypeCode, 0, 0); + } + public MaximumIntensityProjectionGenerationAlgorithm(String dataSetTypeCode, String filename) { + this(dataSetTypeCode, 0, 0, filename); + } + + public MaximumIntensityProjectionGenerationAlgorithm(String dataSetTypeCode, int width, int height) { + this(dataSetTypeCode, width, height, "maximum_intensity_projection"); + } + + public MaximumIntensityProjectionGenerationAlgorithm(String dataSetTypeCode, int width, int height, String filename) { + this.dataSetTypeCode = dataSetTypeCode; + this.width = width; + this.height = height; + this.filename = filename; + } + + @Override + public String getDataSetTypeCode() + { + return dataSetTypeCode; + } + + @Override + public List<BufferedImage> generateImages(ImageDataSetInformation information, ImageDataSetStructure structure) + { + ImageLibraryInfo library = structure.getImageStorageConfiguraton().tryGetImageLibrary(); + List<ImageFileInfo> images = structure.getImages(); + int maxIntensity = 0; + for (ImageFileInfo image: images) { + String imagePath = image.getImageRelativePath(); + if (image.tryGetTimepoint() == null || image.tryGetTimepoint() != 0) { + continue; + } + BufferedImage imageData = Utils.loadUnchangedImage(new FileBasedContentNode(new File( + information.getIncomingDirectory(), imagePath)), image.tryGetUniqueStringIdentifier(), library); + maxIntensity = addImage(imageData); + } + + if (result == null) { + return Collections.emptyList(); + } else { + for (int y=0; y<result.getHeight(); y++) { + for (int x=0; x<result.getWidth(); x++) { + result.setRGB(x, y, adjust(result.getRGB(x, y), maxIntensity)); + } + } + if (width > 0 && height > 0) { + BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + AffineTransform at = new AffineTransform(); + at.scale((double)width / (double)result.getWidth(), (double)height / (double)result.getHeight()); + AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); + result = scaleOp.filter(result, scaled); + } + + return Collections.singletonList(result); + } + + } + + private int adjust(int rgb, int maxIntensity) + { + if (maxIntensity > 255) { + maxIntensity = 255; + } + int r = new Double( ((double)getRed(rgb)) / ((double)maxIntensity) * 255).intValue(); + int g = new Double( ((double)getGreen(rgb)) / ((double)maxIntensity) * 255).intValue(); + int b = new Double( ((double)getBlue(rgb)) / ((double)maxIntensity) * 255).intValue(); + + return (r << 16) + (g << 8) + b; + + } + + + private int addImage(BufferedImage image) + { + System.out.println("Adding image "+image.toString()); + if (result == null) { + result = new BufferedImage(image.getWidth(),image.getHeight(), BufferedImage.TYPE_INT_RGB); + for (int y=0; y<image.getHeight(); y++) { + for (int x=0; x<image.getWidth(); x++) { + image.setRGB(x,y, 0); + } + } + } + int maxIntensity = 0; + for (int y=0; y<image.getHeight(); y++) { + for (int x=0; x<image.getWidth(); x++) { + int rgb1 = result.getRGB(x,y); + int rgb2 = image.getRGB(x,y); + + int intensity1 = intensity(rgb1); + int intensity2 = intensity(rgb2); + + if (intensity1 > maxIntensity) { + maxIntensity = intensity1; + } + + if (intensity2 > maxIntensity) { + maxIntensity = intensity2; + } + + result.setRGB(x,y, intensity1 > intensity2 ? rgb1 : rgb2); + } + } + return maxIntensity; + } + + private int intensity(int rgb) + { + double r = getRed(rgb); + double g = getGreen(rgb); + double b = getBlue(rgb); + return new Double(Math.sqrt(r*r + g*g + b*b)).intValue(); + } + + + private int getBlue(int rgb) + { + return rgb & 0xff; + } + + + private int getGreen(int rgb) + { + return (rgb >> 8) & 0xff; + } + + + private int getRed(int rgb) + { + return (rgb >> 16) & 0xff; + } + + @Override + public String getImageFileName(int index) + { + return filename; + } + +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/ImagingDataSetRegistrationTransaction.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/ImagingDataSetRegistrationTransaction.java index a40189a5dc6afaf0539e2fe7dcfe45fa434b610f..0bf97f8f0c8b66f36f54fa03833a0582f1952e41 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/ImagingDataSetRegistrationTransaction.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/ImagingDataSetRegistrationTransaction.java @@ -19,6 +19,7 @@ package ch.systemsx.cisd.openbis.dss.etl.jython.v2; import static ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants.MICROSCOPY_CONTAINER_TYPE_SUBSTRING; import static ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants.MICROSCOPY_IMAGE_TYPE_SUBSTRING; +import java.awt.image.BufferedImage; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -29,6 +30,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Properties; +import javax.imageio.ImageIO; + import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.etlserver.registrator.DataSetRegistrationDetails; @@ -45,6 +48,7 @@ import ch.systemsx.cisd.openbis.dss.Constants; import ch.systemsx.cisd.openbis.dss.etl.Hdf5ThumbnailGenerator; import ch.systemsx.cisd.openbis.dss.etl.Utils; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.IImageGenerationAlgorithm; import ch.systemsx.cisd.openbis.dss.etl.dto.api.ImageFileInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.api.SimpleImageDataConfig; import ch.systemsx.cisd.openbis.dss.etl.dto.api.ThumbnailsStorageFormat; @@ -341,6 +345,29 @@ public class ImagingDataSetRegistrationTransaction extends DataSetRegistrationTr } containedDataSetCodes.add(mainDataset.getDataSetCode()); + + + IImageGenerationAlgorithm algorithm = imageDataSetInformation.getAlgorithm(); + List<BufferedImage> images = algorithm != null ? algorithm.generateImages(imageDataSetInformation, imageDataSetStructure) : new ArrayList<BufferedImage>(); + if (images.size() > 0) { + IDataSet representative = createNewDataSet(algorithm.getDataSetTypeCode()); + + int i=0; + for (BufferedImage imageData: images) { + String imageFile = createNewFile(representative, algorithm.getImageFileName(i)); + File f = new File(imageFile); + try + { + ImageIO.write(imageData, "png", f); + } catch (IOException e) + { + e.printStackTrace(); + } + i++; + } + containedDataSetCodes.add(representative.getDataSetCode()); + thumbnailDatasets.add(representative); + } for (IDataSet thumbnailDataset : thumbnailDatasets) { @@ -353,7 +380,7 @@ public class ImagingDataSetRegistrationTransaction extends DataSetRegistrationTr containerDataset.setThumbnailDatasets(thumbnailDatasets); String containerDataSetCode = containerDataset.getDataSetCode(); imageDataSetInformation.setContainerDatasetPermId(containerDataSetCode); - + return containerDataset; } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/SimpleImageDataSetRegistrator.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/SimpleImageDataSetRegistrator.java index 11e1bf0adcf9d099bd08b90b0cbbe2d0b25e739c..d2e8369cdd372405b92a5b4eb5ba2bee9780d7a9 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/SimpleImageDataSetRegistrator.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/v2/SimpleImageDataSetRegistrator.java @@ -204,6 +204,7 @@ public class SimpleImageDataSetRegistrator imageDataset.setColorDepth(simpleImageConfig.getColorDepth()); setRegistrationDetails(registrationDetails, imageDataset); + registrationDetails.getDataSetInformation().setAlgorithm(simpleImageConfig.getImageGenerationAlgorithm()); return registrationDetails; }