From 1d84a56b3a1f101b8fbc3f6576b24401e046ec3d Mon Sep 17 00:00:00 2001
From: jakubs <jakubs>
Date: Wed, 6 Feb 2013 12:09:04 +0000
Subject: [PATCH] SP-487 BIS-297 implement fetching stored thumbnails directly
 from the data store without creating buffered image

SVN: 28302
---
 .../dss/etl/AbsoluteImageReference.java       |   8 ++
 .../server/DssServiceRpcScreening.java        | 102 +++++++++++++++++-
 .../server/DssServiceRpcScreeningJson.java    |  14 +++
 .../server/DssServiceRpcScreeningLogger.java  |  18 ++++
 .../api/v1/IDssServiceRpcScreening.java       |  34 ++++++
 .../TransformedImageRepresentationsTest.java  |  53 ++++++++-
 6 files changed, 227 insertions(+), 2 deletions(-)

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 9285df73487..807a98487a3 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
@@ -86,6 +86,14 @@ public class AbsoluteImageReference extends AbstractImageReference
         return uniqueId;
     }
 
+    /**
+     * @return unchanged image content to allow the fastest possible access to any stored image.
+     */
+    public IHierarchicalContentNode getRawContent()
+    {
+        return contentNode;
+    }
+
     /**
      * @return unchanged image content if the image does not have to be extracted from the original
      *         content. This method is provided to allow the fastest possible access to original
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
index 9aa9509ff89..ddfec2fde6b 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
@@ -128,7 +128,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc
     /**
      * The minor version of this service.
      */
-    public static final int MINOR_VERSION = 11;
+    public static final int MINOR_VERSION = 12;
 
     // this dao will hold one connection to the database
     private IImagingReadonlyQueryDAO dao;
@@ -932,6 +932,106 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc
         return convertToBase64(loadThumbnailImages(sessionToken, dataSetIdentifier, channels));
     }
 
+    @Override
+    public List<String> loadPhysicalThumbnailsBase64(String sessionToken,
+            java.util.List<PlateImageReference> imageReferences, ImageRepresentationFormat format)
+    {
+        return convertToBase64(loadPhysicalThumbnails(sessionToken, imageReferences, format));
+    }
+
+    @Override
+    public InputStream loadPhysicalThumbnails(String sessionToken,
+            List<PlateImageReference> imageReferences, final ImageRepresentationFormat format)
+    {
+        for (PlateImageReference plateImageReference : imageReferences)
+        {
+            if (plateImageReference.getDatasetCode().equals(format.getDataSetCode()) == false)
+            {
+                throw new UserFailureException(
+                        "At least for one plate image reference the image representation format "
+                                + "is unknown: Plate image reference: " + plateImageReference
+                                + ", format: " + format);
+            }
+        }
+        IImageRepresentationFormatSelectionCriterion criterion =
+                new AbstractFormatSelectionCriterion()
+                    {
+                        private static final long serialVersionUID = 1L;
+
+                        @Override
+                        protected boolean accept(ImageRepresentationFormat availableFormat)
+                        {
+                            return format.getId() == availableFormat.getId();
+                        }
+                    };
+        return loadPhysicalThumbnails(sessionToken, imageReferences, criterion);
+    }
+
+    public InputStream loadPhysicalThumbnails(String sessionToken,
+            List<PlateImageReference> imageReferences,
+            IImageRepresentationFormatSelectionCriterion... criteria)
+    {
+        final Map<String, IImagingDatasetLoader> imageLoadersMap =
+                getImageDatasetsMap(sessionToken, imageReferences);
+        Map<String, ImgImageDatasetDTO> imageDataSetMap =
+                createDataSetCodeToImageDataSetMap(imageReferences);
+        ImageRepresentationFormatFinder finder = new ImageRepresentationFormatFinder(criteria);
+        Map<String, ImageRepresentationFormat> dataSetToImageReferenceFormatMap =
+                new HashMap<String, ImageRepresentationFormat>();
+        for (Entry<String, ImgImageDatasetDTO> entry : imageDataSetMap.entrySet())
+        {
+            String dataSetCode = entry.getKey();
+            List<ImageRepresentationFormat> filteredFormats =
+                    finder.find(getImageRepresentationFormats(entry.getValue()));
+            if (filteredFormats.isEmpty())
+            {
+                throw new UserFailureException(
+                        "No image representation format fitting criteria found for data set "
+                                + dataSetCode + ".");
+            }
+            if (filteredFormats.size() > 1)
+            {
+                throw new UserFailureException(
+                        "To many image representation formats fitting criteria for data set "
+                                + dataSetCode + ": " + filteredFormats);
+            }
+            dataSetToImageReferenceFormatMap.put(dataSetCode, filteredFormats.get(0));
+        }
+        List<IHierarchicalContentNode> imageContents = new ArrayList<IHierarchicalContentNode>();
+        for (PlateImageReference imageReference : imageReferences)
+        {
+            String datasetCode = imageReference.getDatasetCode();
+            IImagingDatasetLoader loader = imageLoadersMap.get(datasetCode);
+            ImageRepresentationFormat format = dataSetToImageReferenceFormatMap.get(datasetCode);
+            Size size = new Size(format.getWidth(), format.getHeight());
+
+            String transformation = tryGetTransformation(imageReference, format);
+
+            final ImageChannelStackReference channelStackRef =
+                    getImageChannelStackReference(loader, imageReference);
+
+            AbsoluteImageReference imr =
+                    loader.tryGetThumbnail(imageReference.getChannel(), channelStackRef,
+                            new RequestedImageSize(size, false, false), transformation);
+
+            imageContents.add(imr.getRawContent());
+        }
+        return new ConcatenatedContentInputStream(true, imageContents);
+    }
+
+    protected String tryGetTransformation(PlateImageReference imageReference,
+            ImageRepresentationFormat format)
+    {
+        for (ImageRepresentationTransformation t : format.getTransformations())
+        {
+            if (t.getChannelCode().equals(imageReference.getChannel()))
+            {
+                return t.getTransformationCode();
+            }
+        }
+        return null;
+    }
+
     @Override
     public List<MicroscopyImageReference> listImageReferences(String sessionToken,
             IDatasetIdentifier dataSetIdentifier, String channel)
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningJson.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningJson.java
index a5cbdb30398..b775f028c94 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningJson.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningJson.java
@@ -316,6 +316,20 @@ public class DssServiceRpcScreeningJson implements IDssServiceRpcScreening
                 service.listAvailableImageRepresentationFormats(sessionToken, imageDatasets));
     }
 
+    @Override
+    public List<String> loadPhysicalThumbnailsBase64(String sessionToken,
+            List<PlateImageReference> imageReferences, ImageRepresentationFormat format)
+    {
+        return service.loadPhysicalThumbnailsBase64(sessionToken, imageReferences, format);
+    }
+
+    @Override
+    public InputStream loadPhysicalThumbnails(String sessionToken,
+            List<PlateImageReference> imageReferences, ImageRepresentationFormat format)
+    {
+        return handleNotSupportedMethod();
+    }
+
     private <T> T handleNotSupportedMethod()
     {
         throw new UnsupportedOperationException("This method is not supported in JSON API yet");
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java
index 6a4997d93fe..190a4dda55f 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java
@@ -388,4 +388,22 @@ public class DssServiceRpcScreeningLogger extends AbstractServerLogger implement
         return null;
     }
 
+    @Override
+    public List<String> loadPhysicalThumbnailsBase64(String sessionToken,
+            List<PlateImageReference> imageReferences, ImageRepresentationFormat format)
+    {
+        logAccess(sessionToken, "load_physical_thumbnails_base64",
+                "IMAGE_REFERENCES(%s) FORMAT(%s)", imageReferences, format);
+        return null;
+    }
+
+    @Override
+    public InputStream loadPhysicalThumbnails(String sessionToken,
+            List<PlateImageReference> imageReferences, ImageRepresentationFormat format)
+    {
+        logAccess(sessionToken, "load_physical_thumbnails", "IMAGE_REFERENCES(%s) FORMAT(%s)",
+                imageReferences, format);
+        return null;
+    }
+
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java
index 79d16cd5e11..591ed1857c2 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java
@@ -586,4 +586,38 @@ public interface IDssServiceRpcScreening extends IRpcService
             String sessionToken, @AuthorizationGuard(guardClass = DatasetIdentifierPredicate.class)
             List<? extends IDatasetIdentifier> imageDatasets);
 
+    /**
+     * /** Returns the same images as
+     * {@link IDssServiceRpcScreening#loadPhysicalThumbnails(String, List, ImageRepresentationFormat)}
+     * but the result is a list of base64 encoded strings that contain the image data.
+     * 
+     * @since 1.12
+     */
+    @MinimalMinorVersion(12)
+    @DataSetAccessGuard
+    public List<String> loadPhysicalThumbnailsBase64(String sessionToken,
+            @AuthorizationGuard(guardClass = DatasetIdentifierPredicate.class)
+            List<PlateImageReference> imageReferences, ImageRepresentationFormat format);
+
+    /**
+     * The fast method to provide registered thumbnail images (without calculating them) for the
+     * specified list of image references (specified by data set code, well position, channel and
+     * tile) and specified image representation format. The {@link ImageRepresentationFormat}
+     * argument should be an object returned by
+     * {@link #listAvailableImageRepresentationFormats(String, List)}. This method assumes that all
+     * image references belong to the same data set which has image representations of specified
+     * format.
+     * <p>
+     * This method gets the images directly from the data store in the format in which they are
+     * stored there.
+     * 
+     * @throws UserFailureException if the specified format refers to an image representations
+     *             unknown by at least one plate image reference.
+     * @since 1.12
+     */
+    @MinimalMinorVersion(12)
+    @DataSetAccessGuard
+    public InputStream loadPhysicalThumbnails(String sessionToken,
+            @AuthorizationGuard(guardClass = DatasetIdentifierPredicate.class)
+            List<PlateImageReference> imageReferences, ImageRepresentationFormat format);
 }
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/TransformedImageRepresentationsTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/TransformedImageRepresentationsTest.java
index 3cfadf5ec78..51fff215ab5 100644
--- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/TransformedImageRepresentationsTest.java
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/screening/systemtests/TransformedImageRepresentationsTest.java
@@ -19,7 +19,9 @@ package ch.systemsx.cisd.openbis.screening.systemtests;
 import java.awt.Dimension;
 import java.io.File;
 import java.io.IOException;
+import java.net.URL;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
@@ -30,18 +32,25 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
+import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
+import com.googlecode.jsonrpc4j.ProxyUtil;
+
 import ch.systemsx.cisd.common.filesystem.FileUtilities;
 import ch.systemsx.cisd.common.servlet.SpringRequestContextProvider;
+import ch.systemsx.cisd.openbis.dss.screening.shared.api.v1.IDssServiceRpcScreening;
 import ch.systemsx.cisd.openbis.generic.shared.util.TestInstanceHostUtils;
 import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.IScreeningOpenbisServiceFacade;
 import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.ScreeningOpenbisServiceFacade;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.IScreeningClientService;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.ResourceNames;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.json.ScreeningObjectMapper;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.IScreeningApiServer;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.DatasetImageRepresentationFormats;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ImageDatasetReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ImageRepresentationFormat;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition;
 
 /**
  * @author Chandrasekhar Ramakrishnan
@@ -60,6 +69,8 @@ public class TransformedImageRepresentationsTest extends AbstractScreeningSystem
 
     private IScreeningOpenbisServiceFacade screeningFacade;
 
+    private IDssServiceRpcScreening screeningJsonApi;
+
     @BeforeTest
     public void dropAnExampleDataSet() throws IOException, Exception
     {
@@ -83,6 +94,15 @@ public class TransformedImageRepresentationsTest extends AbstractScreeningSystem
         screeningFacade =
                 ScreeningOpenbisServiceFacade.tryCreateForTest(sessionToken,
                         TestInstanceHostUtils.getOpenBISUrl(), screeningServer);
+
+        JsonRpcHttpClient client =
+                new JsonRpcHttpClient(new ScreeningObjectMapper(), new URL(
+                        TestInstanceHostUtils.getDSSUrl()
+                                + "/rmi-datastore-server-screening-api-v1.json/"),
+                        new HashMap<String, String>());
+        screeningJsonApi =
+                ProxyUtil.createProxy(this.getClass().getClassLoader(),
+                        IDssServiceRpcScreening.class, client);
     }
 
     @AfterMethod
@@ -95,6 +115,21 @@ public class TransformedImageRepresentationsTest extends AbstractScreeningSystem
         }
     }
 
+    /**
+     * data to compare whether the fetched thumbnail images are exactly as required
+     */
+    HashMap<Integer, String> expectedThumbnails()
+    {
+        HashMap<Integer, String> map = new HashMap<Integer, String>();
+        map.put(128,
+                "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");
+        map.put(256,
+                "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");
+        map.put(64,
+                "/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABAAEADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDhmYIuScVnSagN5y21f503Urgop54FctNdPJISScdhXj4fD86uz9GzfOfq0uSJ1Q1WMDA6epNB1u3QcuDx2rkDMx6k0zfnrk/jXV9Sg9zw3xPiF8KOsfxDEDgAH6VH/byP3xXL7h6H86TPoapYOmjCXEmMk9WddBqqs4+brWvFKsq5FefRuyuCDXU6XO7KOa5cThlFXR7uS51OvP2dQj1v5UPvXLuME11evjESccE4rlJDk59TXVg9aZ4XEati2htFFHeuw+dD8aKO9FACiup0L95GDXLDoa6rwzloXJ7GuTGfw2z6HhvXGxj3DxGxEMeOx5/KuVbkCup1754z7VyxpYP+Gg4kbeNkxPWjvRR+Fdh88Hej0o/CigBR9011XhjIgkBHU/0rlR6V1Og/JGPeuTGfwmj6HhvTGxl2JtUt2aNiBmuUeNkYgivQJEEgway59KV5CVXr2rkw2JUFZnv51kssRNVKZx+PajA9TXU/2Er9VxR/wjMbHO8j9a6/rlPqzwP9W8a/hjc5bA9aMeldV/wjEQORIT+ApP7BROgzR9cpdGH+reNXxRsc0iMzAAV1OlQOqLxiiDSlWQfLWxFEsS4FcmJxKkrI97JclnQn7Sof/9k=");
+        return map;
+    }
+
     @Test
     public void testTransformedThumbnails() throws Exception
     {
@@ -109,11 +144,28 @@ public class TransformedImageRepresentationsTest extends AbstractScreeningSystem
         List<ImageRepresentationFormat> formats =
                 representationFormats.get(0).getImageRepresentationFormats();
 
+        List<PlateImageReference> plateRefs =
+                screeningJsonApi.listPlateImageReferences(sessionToken, imageDataSets.get(0),
+                        Arrays.asList(new WellPosition(1, 1)), "Cy5");
+
         HashSet<Dimension> expectedResolutions = new HashSet<Dimension>();
         expectedResolutions.addAll(Arrays.asList(new Dimension(64, 64), new Dimension(128, 128),
                 new Dimension(256, 256), new Dimension(512, 512)));
+
         for (ImageRepresentationFormat format : formats)
         {
+
+            HashMap<Integer, String> expectedThumbnails = expectedThumbnails();
+            if (false == format.isOriginal())
+            {
+                List<String> thumbnails =
+                        screeningJsonApi.loadPhysicalThumbnailsBase64(sessionToken, plateRefs,
+                                format);
+                String expectedThumbnailImage = expectedThumbnails.get(format.getWidth());
+                assertEquals(1, thumbnails.size());
+                assertEquals(expectedThumbnailImage, thumbnails.get(0));
+            }
+
             if (format.getFileType() != null)
             {
                 // jpg thumbnails
@@ -145,7 +197,6 @@ public class TransformedImageRepresentationsTest extends AbstractScreeningSystem
                 assertEquals(3, format.getTransformations().size());
             }
         }
-
     }
 
     private File createTestDataContents() throws IOException
-- 
GitLab