diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java
index a182385cdbf765ba6bedb33206d813c65973b138..02d17e343f119cb5b98b6016b2f71ba27c2b5155 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java
@@ -285,7 +285,8 @@ abstract class AbstractImageDatasetUploader
     private ImgImageZoomLevelDTO convert(long imageContainerDatasetId, ImageZoomLevel imageZoomLevel)
     {
         return new ImgImageZoomLevelDTO(imageZoomLevel.getPhysicalDatasetPermId(),
-                imageZoomLevel.isOriginal(), imageContainerDatasetId);
+                imageZoomLevel.isOriginal(), imageZoomLevel.getRootPath(),
+                imageZoomLevel.getWidth(), imageZoomLevel.getHeight(), imageContainerDatasetId);
     }
 
     private static ImgImageDatasetDTO createImageDatasetDTO(String datasetPermId,
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java
index 9a6c7c0e54bde62f200adccd64c56325bfbd1c94..243858f2ffc9a28b8c74d231f46777c71296625a 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java
@@ -41,7 +41,7 @@ import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.PropertyUtils;
 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.impl.ThumbnailsInfo;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.Channel;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageFileInfo;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageIdentifier;
@@ -457,7 +457,7 @@ abstract public class AbstractImageFileExtractor implements IImageFileExtractor
     }
 
     public final static List<AcquiredSingleImage> createImagesWithNoColorComponent(
-            ImageFileInfo imageInfo, ThumbnailFilePaths thumbnailFilePathsOrNull)
+            ImageFileInfo imageInfo, ThumbnailsInfo thumbnailFilePathsOrNull)
     {
         List<AcquiredSingleImage> images = new ArrayList<AcquiredSingleImage>();
         images.add(createImage(imageInfo, imageInfo.getChannelCode(), null,
@@ -467,7 +467,7 @@ abstract public class AbstractImageFileExtractor implements IImageFileExtractor
 
     public final static AcquiredSingleImage createImage(ImageFileInfo imageInfo,
             String channelCode, ColorComponent colorComponentOrNull,
-            ThumbnailFilePaths thumbnailFilePathsOrNull)
+            ThumbnailsInfo thumbnailFilePathsOrNull)
     {
         RelativeImageReference relativeImageRef =
                 new RelativeImageReference(imageInfo.getImageRelativePath(),
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java
index 76bc4d5bf69712f2b22d7bf94ceec92e31ac28eb..b829a8713af74a64a430e9a4a20ce473876d0f6e 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java
@@ -63,7 +63,7 @@ import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingQueryDAO;
 import ch.systemsx.cisd.openbis.dss.etl.dto.ImageSeriesPoint;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetInformation;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetStructure;
-import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailFilePaths;
+import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.Channel;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ChannelColorComponent;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageFileInfo;
@@ -674,8 +674,9 @@ abstract class AbstractImageStorageProcessor extends AbstractStorageProcessor im
                 new Geometry(imageDataSetStructure.getTileRowsNumber(),
                         imageDataSetStructure.getTileColumnsNumber());
 
-        ThumbnailFilePaths thumbnailFilePaths = dataSetInformation.tryGetThumbnailFilePaths();
-        List<AcquiredSingleImage> images = convertImages(imageDataSetStructure, thumbnailFilePaths);
+        ThumbnailsInfo thumbnailsInfo = dataSetInformation.getThumbnailsInfos();
+
+        List<AcquiredSingleImage> images = convertImages(imageDataSetStructure, thumbnailsInfo);
 
         List<File> invalidFiles = new ArrayList<File>(); // handles in an earlier phase
         ImageStorageConfiguraton imageStorageConfiguraton =
@@ -694,18 +695,15 @@ abstract class AbstractImageStorageProcessor extends AbstractStorageProcessor im
                         imageStorageConfiguraton.getStoreChannelsOnExperimentLevel(),
                         imageStorageConfiguraton.tryGetImageLibrary());
 
-        String thumbnailDatasetPermIdOrNull =
-                (thumbnailFilePaths == null) ? null : thumbnailFilePaths
-                        .getThumbnailPhysicalDatasetPermId();
         ImageDatasetOwnerInformation imageDatasetOwner =
                 ImageDatasetOwnerInformation.create(dataSetInformation.getContainerDatasetPermId(),
-                        dataSetInformation, thumbnailDatasetPermIdOrNull);
+                        dataSetInformation, thumbnailsInfo);
         return new ImageFileExtractionWithConfig(imageDatasetOwner, extractionResult,
                 imageStorageConfiguraton);
     }
 
     private static List<AcquiredSingleImage> convertImages(
-            ImageDataSetStructure imageDataSetStructure, ThumbnailFilePaths thumbnailFilePathsOrNull)
+            ImageDataSetStructure imageDataSetStructure, ThumbnailsInfo thumbnailFilePathsOrNull)
     {
         List<ImageFileInfo> imageInfos = imageDataSetStructure.getImages();
         List<ChannelColorComponent> channelColorComponentsOrNull =
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 f3145f15c3af77f014aa659e739b142817c038a7..a77b2e34b0f864092c2b8d56c1c17cc0b04e54c8 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
@@ -46,7 +46,10 @@ import ch.systemsx.cisd.common.process.ProcessIOStrategy;
 import ch.systemsx.cisd.common.process.ProcessResult;
 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.impl.ImageDataSetStructure;
+import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo;
+import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageFileInfo;
+import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageIdentifier;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageStorageConfiguraton;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ThumbnailsStorageFormat;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil;
@@ -58,41 +61,52 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil;
  */
 public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
 {
+    private static class ThumbnailData
+    {
+        private final byte[] data;
+
+        private final int width;
+
+        private final int height;
+
+        private ThumbnailData(byte[] data, int width, int height)
+        {
+            this.data = data;
+            this.width = width;
+            this.height = height;
+        }
+    }
+
     /**
      * Generates thumbnails of specified images whose paths are relative to the specified image
      * parent directory.
      * 
-     * @param images path to the images relative to the imagesParentDirectory
+     * @param imageDataSetStructure the images dataset structure, including paths relative to the
+     *            imagesParentDirectory
      * @param thumbnailFilePath absolute path to the file where thumbnails will be saved
      * @param imageStorageConfiguraton describes how the thumbnails should be generated
-     * @return null if thumbnails generation was not requested
      */
-    public static ThumbnailFilePaths tryGenerateThumbnails(List<RelativeImageFile> images,
+    public static void tryGenerateThumbnails(ImageDataSetStructure imageDataSetStructure,
             File imagesParentDirectory, String thumbnailFilePath,
             ImageStorageConfiguraton imageStorageConfiguraton,
             String thumbnailPhysicalDatasetPermId,
-            ThumbnailsStorageFormat thumbnailsStorageFormatOrNull)
+            ThumbnailsStorageFormat thumbnailsStorageFormatOrNull, ThumbnailsInfo thumbnailPaths)
     {
         if (thumbnailsStorageFormatOrNull != null)
         {
-            ThumbnailFilePaths thumbnailPaths =
-                    new ThumbnailFilePaths(thumbnailPhysicalDatasetPermId);
-
+            thumbnailPaths.putDataSet(thumbnailPhysicalDatasetPermId,
+                    thumbnailsStorageFormatOrNull.getThumbnailsFileName());
             File thumbnailsFile = new File(thumbnailFilePath);
             final String relativeThumbnailFilePath = thumbnailsFile.getName();
 
             HDF5Container container = new HDF5Container(thumbnailsFile);
             ImageLibraryInfo imageLibrary = imageStorageConfiguraton.tryGetImageLibrary();
             Hdf5ThumbnailGenerator thumbnailsGenerator =
-                    new Hdf5ThumbnailGenerator(images, imagesParentDirectory,
-                            thumbnailsStorageFormatOrNull, imageLibrary, relativeThumbnailFilePath,
-                            thumbnailPaths, operationLog);
+                    new Hdf5ThumbnailGenerator(imageDataSetStructure, imagesParentDirectory,
+                            thumbnailPhysicalDatasetPermId, thumbnailsStorageFormatOrNull,
+                            imageLibrary, relativeThumbnailFilePath, thumbnailPaths, operationLog);
             container.runWriterClient(thumbnailsStorageFormatOrNull.isStoreCompressed(),
                     thumbnailsGenerator);
-            return thumbnailPaths;
-        } else
-        {
-            return null;
         }
     }
 
@@ -106,27 +120,31 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
 
     private static final int MAX_RETRY_OF_FAILED_GENERATION = 3;
 
-    private final List<RelativeImageFile> images;
+    private final ImageDataSetStructure imageDataSetStructure;
 
     private final File imagesParentDirectory;
 
+    private String thumbnailPhysicalDatasetPermId;
+
     private final ThumbnailsStorageFormat thumbnailsStorageFormat;
 
     private final ImageLibraryInfo imageLibraryOrNull;
 
     private final String relativeThumbnailFilePath;
 
-    private final ThumbnailFilePaths thumbnailPathCollector;
+    private final ThumbnailsInfo thumbnailPathCollector;
 
     private final Logger logger;
 
-    private Hdf5ThumbnailGenerator(List<RelativeImageFile> images, File imagesParentDirectory,
+    private Hdf5ThumbnailGenerator(ImageDataSetStructure imageDataSetStructure,
+            File imagesParentDirectory, String thumbnailPhysicalDatasetPermId,
             ThumbnailsStorageFormat thumbnailsStorageFormat, ImageLibraryInfo imageLibraryOrNull,
-            String relativeThumbnailFilePath, ThumbnailFilePaths thumbnailPathCollector,
+            String relativeThumbnailFilePath, ThumbnailsInfo thumbnailPathCollector,
             Logger operationLog)
     {
-        this.images = images;
+        this.imageDataSetStructure = imageDataSetStructure;
         this.imagesParentDirectory = imagesParentDirectory;
+        this.thumbnailPhysicalDatasetPermId = thumbnailPhysicalDatasetPermId;
         this.thumbnailsStorageFormat = thumbnailsStorageFormat;
         this.imageLibraryOrNull = imageLibraryOrNull;
         this.relativeThumbnailFilePath = relativeThumbnailFilePath;
@@ -139,7 +157,7 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
      *            the thumbnail. Using it allows not to allocate memory each time when a thumbnail
      *            is generated.
      */
-    private Status generateThumbnail(IHDF5ContainerWriter writer, RelativeImageFile image,
+    private Status generateThumbnail(IHDF5ContainerWriter writer, ImageFileInfo image,
             ByteArrayOutputStream bufferOutputStream)
     {
         String imagePath = image.getImageRelativePath();
@@ -149,24 +167,23 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
         try
         {
             long start = System.currentTimeMillis();
-            String imageIdOrNull = image.tryGetImageID();
-            byte[] byteArray = generateThumbnail(bufferOutputStream, img, imageIdOrNull);
-            String path =
-                    relativeThumbnailFilePath + AbstractImageStorageProcessor.ARCHIVE_DELIMITER
-                            + thumbnailPath;
-            thumbnailPathCollector.saveThumbnailPath(image, path);
+            String imageIdOrNull = tryExtractImageID(image);
+            ThumbnailData thumbnailData = generateThumbnail(bufferOutputStream, img, imageIdOrNull);
+            thumbnailPathCollector.saveThumbnailPath(thumbnailPhysicalDatasetPermId,
+                    RelativeImageFile.create(image), thumbnailPath, thumbnailData.width,
+                    thumbnailData.height);
 
             if (logger.isDebugEnabled())
             {
                 long now = System.currentTimeMillis();
                 logger.debug(Thread.currentThread().getName() + " thumbnail " + thumbnailPath
-                        + " (" + byteArray.length + " bytes) generated in " + (now - start)
-                        + " msec");
+                        + " (" + thumbnailData.data.length + " bytes) generated in "
+                        + (now - start) + " msec");
             }
             synchronized (writer)
             {
-                writer.writeToHDF5Container(thumbnailPath, new ByteArrayInputStream(byteArray),
-                        byteArray.length);
+                writer.writeToHDF5Container(thumbnailPath, new ByteArrayInputStream(
+                        thumbnailData.data), thumbnailData.data.length);
             }
         } catch (IOException ex)
         {
@@ -175,7 +192,7 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
         return Status.OK;
     }
 
-    private String createThumbnailPath(RelativeImageFile plateImage)
+    private String createThumbnailPath(ImageFileInfo plateImage)
     {
         String imagePath = plateImage.getImageRelativePath();
         String newImagePath = imagePath;
@@ -184,7 +201,8 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
         {
             newImagePath = imagePath.substring(0, lastIndex);
         }
-        String imageIdOrNull = plateImage.tryGetImageID();
+
+        String imageIdOrNull = tryExtractImageID(plateImage);
         if (imageIdOrNull != null)
         {
             newImagePath += "_" + imageIdOrNull;
@@ -193,37 +211,39 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
         return newImagePath;
     }
 
-    private byte[] generateThumbnail(ByteArrayOutputStream bufferOutputStream, File img,
+    private static String tryExtractImageID(ImageFileInfo image)
+    {
+        ImageIdentifier imageIdentifier = image.tryGetImageIdentifier();
+        return imageIdentifier == null ? null : imageIdentifier.getUniqueStringIdentifier();
+    }
+
+    private ThumbnailData generateThumbnail(ByteArrayOutputStream bufferOutputStream, File img,
             String imageIdOrNull) throws IOException
     {
-        byte[] byteArray;
+        ThumbnailData thumbnailData;
         if (thumbnailsStorageFormat.isGenerateWithImageMagic())
         {
-            byteArray = generateThumbnailWithImageMagic(img);
+            thumbnailData = generateThumbnailWithImageMagic(img);
         } else
         {
-            byteArray = generateThumbnailInternally(img, imageIdOrNull, bufferOutputStream);
+            thumbnailData = generateThumbnailInternally(img, imageIdOrNull, bufferOutputStream);
         }
-        return byteArray;
+        return thumbnailData;
     }
 
-    private byte[] generateThumbnailWithImageMagic(File imageFile) throws IOException
+    private ThumbnailData generateThumbnailWithImageMagic(File imageFile) throws IOException
     {
-        String size =
-                thumbnailsStorageFormat.getMaxWidth() + "x"
-                        + thumbnailsStorageFormat.getMaxHeight();
+        int width = thumbnailsStorageFormat.getMaxWidth();
+        int height = 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;
+            double zoomLevel = thumbnailsStorageFormat.getZoomLevel();
+            width = (int) Math.round(zoomLevel * originalSize.getWidth());
+            height = (int) Math.round(zoomLevel * originalSize.getHeight());
         }
+        String size = width + "x" + height;
 
         String imageFilePath = imageFile.getPath();
         List<String> params = new ArrayList<String>();
@@ -245,28 +265,29 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
                     imageFilePath, result.getExitValue(), result.getProcessIOResult().getStatus()));
         } else
         {
-            return result.getBinaryOutput();
+            return new ThumbnailData(result.getBinaryOutput(), width, height);
         }
     }
 
-    private byte[] generateThumbnailInternally(File imageFile, String imageIdOrNull,
+    private ThumbnailData generateThumbnailInternally(File imageFile, String imageIdOrNull,
             ByteArrayOutputStream bufferOutputStream) throws IOException
     {
         BufferedImage image = loadUnchangedImage(imageFile, imageIdOrNull);
 
-        long x = thumbnailsStorageFormat.getMaxWidth();
-        long y = thumbnailsStorageFormat.getMaxHeight();
+        int widht = thumbnailsStorageFormat.getMaxWidth();
+        int height = thumbnailsStorageFormat.getMaxHeight();
         if (thumbnailsStorageFormat.getZoomLevel() != null)
         {
-            x = Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getWidth());
-            y = Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getHeight());
+            widht = (int) Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getWidth());
+            height = (int) Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getHeight());
         }
 
         BufferedImage thumbnail =
-                ImageUtil.rescale(image, (int) x, (int) y, false,
+                ImageUtil.rescale(image, widht, height, false,
                         thumbnailsStorageFormat.isHighQuality());
         ImageUtil.writeImageToPng(thumbnail, bufferOutputStream);
-        return bufferOutputStream.toByteArray();
+        return new ThumbnailData(bufferOutputStream.toByteArray(), thumbnail.getWidth(),
+                thumbnail.getHeight());
     }
 
     private BufferedImage loadUnchangedImage(File imageFile, String imageIdOrNull)
@@ -288,10 +309,9 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
                 thumbnailPath, ex.getMessage()));
     }
 
-    private ITaskExecutor<RelativeImageFile> createThumbnailGenerator(
-            final IHDF5ContainerWriter writer)
+    private ITaskExecutor<ImageFileInfo> createThumbnailGenerator(final IHDF5ContainerWriter writer)
     {
-        return new ITaskExecutor<RelativeImageFile>()
+        return new ITaskExecutor<ImageFileInfo>()
             {
                 private ThreadLocal<ByteArrayOutputStream> outputStreamBuffers =
                         new ThreadLocal<ByteArrayOutputStream>()
@@ -303,7 +323,7 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
                                 }
                             };
 
-                public Status execute(RelativeImageFile image)
+                public Status execute(ImageFileInfo image)
                 {
                     // each thread will get its own buffer to avoid allocating memory for the
                     // internal array each time
@@ -316,8 +336,9 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient
 
     public void runWithSimpleWriter(IHDF5ContainerWriter writer)
     {
-        Collection<FailureRecord<RelativeImageFile>> errors =
-                ParallelizedExecutor.process(images, createThumbnailGenerator(writer),
+        Collection<FailureRecord<ImageFileInfo>> errors =
+                ParallelizedExecutor.process(imageDataSetStructure.getImages(),
+                        createThumbnailGenerator(writer),
                         thumbnailsStorageFormat.getAllowedMachineLoadDuringGeneration(), 100,
                         "Thumbnails generation", MAX_RETRY_OF_FAILED_GENERATION, true);
         if (errors.size() > 0)
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java
index 79d1a850af5d97206d1753bfd7bdc9886075fb51..637641bf03caec39cf50925a98f19003d133af11 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java
@@ -72,9 +72,10 @@ public interface IImagingDatasetLoader extends IImageDatasetLoader
      * @param channelCode channel code for which representative image is requested
      * @param wellLocationOrNull if not null the returned images are restricted to one well.
      *            Otherwise the dataset is assumed to have no container and spots.
+     * @param imageSize
      */
     AbsoluteImageReference tryGetRepresentativeThumbnail(String channelCode,
-            Location wellLocationOrNull);
+            Location wellLocationOrNull, RequestedImageSize imageSize);
 
     /**
      * Returns the stored thumbnail for the given parameters, or <code>null</code>, if no thumbnail
@@ -85,8 +86,9 @@ public interface IImagingDatasetLoader extends IImageDatasetLoader
      * 
      * @param channelCode The code of the channel to get the thumbnail for.
      * @param channelStackReference Specifies well and tile of the thumbnail.
+     * @param imageSize
      */
     AbsoluteImageReference tryGetThumbnail(String channelCode,
-            ImageChannelStackReference channelStackReference);
+            ImageChannelStackReference channelStackReference, RequestedImageSize imageSize);
 
 }
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java
index f8d8f792c7fc7f75a3e543d90b5a4e0b0680b61a..7cc8191234f34f72c5cbc1c1c0d7a540447cbb95 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java
@@ -28,7 +28,7 @@ public class ImagingDatabaseVersionHolder implements IDatabaseVersionHolder
 
     public String getDatabaseVersion()
     {
-        return "018"; // changed in S119
+        return "019"; // changed in S121
     }
 
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java
index c7da4d9422045778dcec4a03ca04742a5e7b4aac..bdcd247549df44d3defb06f790a7ebdc9b01388c 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java
@@ -59,14 +59,15 @@ public class ImagingLoaderStrategyFactory
                         ImageChannelStackReference channelStackReference,
                         RequestedImageSize imageSize)
                 {
-                    return this.imageAccessor.tryGetThumbnail(channelCode, channelStackReference);
+                    return this.imageAccessor.tryGetThumbnail(channelCode, channelStackReference,
+                            imageSize);
                 }
 
                 public AbsoluteImageReference tryGetRepresentativeImage(String channelCode,
                         Location wellLocationOrNull, RequestedImageSize imageSize)
                 {
                     return imageAccessor.tryGetRepresentativeThumbnail(channelCode,
-                            wellLocationOrNull);
+                            wellLocationOrNull, imageSize);
                 }
 
             };
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java
index 5fd8d4e23a79fcdd7a77db17cc3b59d3d31798c5..1056eb91ddf65366f951b6ed05110729032f14c3 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java
@@ -23,13 +23,16 @@ import java.util.Properties;
 
 import ch.systemsx.cisd.bds.hcs.Geometry;
 import ch.systemsx.cisd.common.mail.IMailClient;
+import ch.systemsx.cisd.common.shared.basic.utils.StringUtils;
 import ch.systemsx.cisd.common.utilities.AbstractHashable;
 import ch.systemsx.cisd.common.utilities.PropertyUtils;
 import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingQueryDAO;
 import ch.systemsx.cisd.openbis.dss.etl.dto.ImageDatasetInfo;
 import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo;
 import ch.systemsx.cisd.openbis.dss.etl.dto.ImageZoomLevel;
+import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo;
 import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.Size;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
@@ -146,33 +149,47 @@ public final class PlateStorageProcessor extends AbstractImageStorageProcessor
     public static class ImageDatasetOwnerInformation extends DatasetOwnerInformation
     {
         public static ImageDatasetOwnerInformation create(String containerDatasetPermId,
-                DataSetInformation originalDataset, String thumbnailDatasetPermIdOrNull)
+                DataSetInformation originalDataset, ThumbnailsInfo thumbnailsInfosOrNull)
         {
             return new ImageDatasetOwnerInformation(containerDatasetPermId, originalDataset,
-                    thumbnailDatasetPermIdOrNull);
+                    thumbnailsInfosOrNull);
         }
 
         private final List<ImageZoomLevel> imageZoomLevels;
 
         private ImageDatasetOwnerInformation(String containerDatasetPermId,
-                DataSetInformation originalDataset, String thumbnailDatasetPermIdOrNull)
+                DataSetInformation originalDataset, ThumbnailsInfo thumbnailsInfosOrNull)
         {
             super(containerDatasetPermId, originalDataset);
-            this.imageZoomLevels = createZoomLevels(originalDataset, thumbnailDatasetPermIdOrNull);
+            this.imageZoomLevels = createZoomLevels(originalDataset, thumbnailsInfosOrNull);
         }
 
         private static List<ImageZoomLevel> createZoomLevels(DataSetInformation originalDataset,
-                String thumbnailDatasetPermIdOrNull)
+                ThumbnailsInfo thumbnailsInfosOrNull)
         {
             List<ImageZoomLevel> zoomLevels = new ArrayList<ImageZoomLevel>();
+
             ImageZoomLevel originalZoomLevel =
-                    new ImageZoomLevel(originalDataset.getDataSetCode(), true);
+                    new ImageZoomLevel(originalDataset.getDataSetCode(), true,
+                            StringUtils.EMPTY_STRING, null, null);
             zoomLevels.add(originalZoomLevel);
-            if (thumbnailDatasetPermIdOrNull != null)
+            if (thumbnailsInfosOrNull != null)
             {
-                ImageZoomLevel thumbnailZoomLevel =
-                        new ImageZoomLevel(thumbnailDatasetPermIdOrNull, false);
-                zoomLevels.add(thumbnailZoomLevel);
+                for (String permId : thumbnailsInfosOrNull.getThumbnailPhysicalDatasetsPermIds())
+                {
+                    Integer width = null, height = null;
+                    Size dimension = thumbnailsInfosOrNull.tryGetDimension(permId);
+                    if (dimension != null)
+                    {
+                        width = dimension.getWidth();
+                        height = dimension.getHeight();
+                    }
+                    String rootPath = thumbnailsInfosOrNull.getRootPath(permId);
+
+                    ImageZoomLevel thumbnailZoomLevel =
+                            new ImageZoomLevel(permId, false, rootPath, width, height);
+                    zoomLevels.add(thumbnailZoomLevel);
+                }
             }
             return zoomLevels;
         }
@@ -193,7 +210,7 @@ public final class PlateStorageProcessor extends AbstractImageStorageProcessor
                         extractionResult, operationLog, notificationLog, notifyIfPlateIncomplete);
         return validator.validateImages();
     }
-    
+
     private void checkDataSetInformation(final DatasetOwnerInformation dataSetInformation)
     {
         assert dataSetInformation != null : "Unspecified data set information";
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PrefixedImage.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PrefixedImage.java
new file mode 100644
index 0000000000000000000000000000000000000000..47e56572bf4c96c88c36fa303b6b0d9375a175e8
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PrefixedImage.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+import ch.systemsx.cisd.base.image.IImageTransformerFactory;
+import ch.systemsx.cisd.common.shared.basic.utils.StringUtils;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.ColorComponent;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.ImgImageDTO;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class PrefixedImage
+{
+    private final String pathPrefix;
+
+    private final ImgImageDTO image;
+
+    public PrefixedImage(String pathPrefix, ImgImageDTO image)
+    {
+        this.pathPrefix = pathPrefix;
+        this.image = image;
+    }
+
+    public String getFilePath()
+    {
+        if (StringUtils.isBlank(pathPrefix))
+        {
+            return image.getFilePath();
+        }
+        return pathPrefix + "/" + image.getFilePath();
+    }
+
+    public ColorComponent getColorComponent()
+    {
+        return image.getColorComponent();
+    }
+
+    public String getImageID()
+    {
+        return image.getImageID();
+    }
+
+    public IImageTransformerFactory tryGetImageTransformerFactory()
+    {
+        return image.tryGetImageTransformerFactory();
+    }
+
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java
index 74f282d01400ab3e7564d2fb3a875a805684edf9..6861f592a41279730db71f26fa7f819032452f1c 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java
@@ -90,8 +90,8 @@ public interface IImagingQueryDAO extends TransactionQuery, IImagingReadonlyQuer
             + "?{1.imageLibraryName}, ?{1.imageReaderName}) returning ID")
     public long addImageDataset(ImgImageDatasetDTO dataset);
 
-    @Select("insert into image_zoom_levels (physical_dataset_perm_id, is_original, container_dataset_id)  "
-            + "values(?{1.physicalDatasetPermId}, ?{1.isOriginal}, ?{1.containerDatasetId}) returning ID")
+    @Select("insert into image_zoom_levels (physical_dataset_perm_id, is_original, container_dataset_id, path, width, height)  "
+            + "values(?{1.physicalDatasetPermId}, ?{1.isOriginal}, ?{1.containerDatasetId}, ?{1.rootPath}, ?{1.width}, ?{1.height}) returning ID")
     public long addImageZoomLevel(ImgImageZoomLevelDTO dataset);
 
     @Select("insert into ANALYSIS_DATA_SETS (PERM_ID, CONT_ID)                     "
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java
index 46844c2b06428307642191af3336ed5d9dee0f59..ff13e2ac6e252b7d785046f584365a8f20012662 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java
@@ -27,10 +27,20 @@ public class ImageZoomLevel
 
     private final boolean isOriginal;
 
-    public ImageZoomLevel(String physicalDatasetPermId, boolean isOriginal)
+    private final String rootPath;
+
+    private final Integer width;
+
+    private final Integer height;
+
+    public ImageZoomLevel(String physicalDatasetPermId, boolean isOriginal, String rootPath,
+            Integer width, Integer height)
     {
         this.physicalDatasetPermId = physicalDatasetPermId;
         this.isOriginal = isOriginal;
+        this.rootPath = rootPath;
+        this.width = width;
+        this.height = height;
     }
 
     public String getPhysicalDatasetPermId()
@@ -43,4 +53,18 @@ public class ImageZoomLevel
         return isOriginal;
     }
 
+    public Integer getWidth()
+    {
+        return width;
+    }
+
+    public Integer getHeight()
+    {
+        return height;
+    }
+
+    public String getRootPath()
+    {
+        return rootPath;
+    }
 }
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 26514bce22ade9d2f97433585640063e31605989..3505d444113fa804968d8d89733d4bd8748b4cf3 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
@@ -42,7 +42,7 @@ public class ImageDataSetInformation extends BasicDataSetInformation
 
     private ImageDataSetStructure imageDataSetStructure;
 
-    private ThumbnailFilePaths thumbnailFilePathsOrNull;
+    private ThumbnailsInfo thumbnailsInfos;
 
     private String containerDatasetPermId;
 
@@ -57,9 +57,9 @@ public class ImageDataSetInformation extends BasicDataSetInformation
     }
 
     /** @returns null if no thumbnails will be stored */
-    public ThumbnailFilePaths tryGetThumbnailFilePaths()
+    public ThumbnailsInfo getThumbnailsInfos()
     {
-        return thumbnailFilePathsOrNull;
+        return thumbnailsInfos;
     }
 
     public ImageDataSetStructure getImageDataSetStructure()
@@ -88,9 +88,9 @@ public class ImageDataSetInformation extends BasicDataSetInformation
         this.containerDatasetPermId = containerDatasetPermId;
     }
 
-    public void setThumbnailFilePaths(ThumbnailFilePaths thumbnailFilePathsOrNull)
+    public void setThumbnailsInfo(ThumbnailsInfo thumbnailsInfo)
     {
-        this.thumbnailFilePathsOrNull = thumbnailFilePathsOrNull;
+        this.thumbnailsInfos = thumbnailsInfo;
     }
 
     public void setImageDataSetStructure(ImageDataSetStructure imageDataSetStructure)
@@ -107,9 +107,9 @@ public class ImageDataSetInformation extends BasicDataSetInformation
         appendNameAndObject(buffer, "images structure", imageDataSetStructure);
         appendNameAndObject(buffer, "container dataset", containerDatasetPermId);
         appendNameAndObject(buffer, "original dataset", this.getDataSetCode());
-        if (this.tryGetThumbnailFilePaths() != null)
+        if (getThumbnailsInfos() != null)
         {
-            appendNameAndObject(buffer, "thumbnail", this.tryGetThumbnailFilePaths());
+            appendNameAndObject(buffer, "thumbnail", getThumbnailsInfos());
         } else
         {
             appendNameAndObject(buffer, "thumbnail", "none");
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailFilePaths.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailFilePaths.java
deleted file mode 100644
index 1cb5d037e62a9600bd4b46bdbfc8ff5a80b768dc..0000000000000000000000000000000000000000
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailFilePaths.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package ch.systemsx.cisd.openbis.dss.etl.dto.api.impl;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile;
-import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ToStringUtil;
-
-/**
- * Stores the mapping between image and their thumbnails paths. Thread-safe class.
- * 
- * @author Tomasz Pylak
- */
-public class ThumbnailFilePaths
-{
-    private final Map<RelativeImageFile, String> imageToThumbnailPathMap;
-
-    private final String thumbnailPhysicalDatasetPermId;
-
-    public ThumbnailFilePaths(String thumbnailPhysicalDatasetPermId)
-    {
-        assert thumbnailPhysicalDatasetPermId != null : "thumbnailPhysicalDatasetPermId is null";
-
-        this.thumbnailPhysicalDatasetPermId = thumbnailPhysicalDatasetPermId;
-        this.imageToThumbnailPathMap = new HashMap<RelativeImageFile, String>();
-    }
-
-    /**
-     * Saves the path to the thumbnail for the specified image. The thumbnail path is relative and
-     * starts with the name of the thumbnail file.
-     */
-    public synchronized void saveThumbnailPath(RelativeImageFile image, String thumbnailRelativePath)
-    {
-        imageToThumbnailPathMap.put(image, thumbnailRelativePath);
-    }
-
-    public synchronized String getThumbnailPath(RelativeImageFile image)
-    {
-        return imageToThumbnailPathMap.get(image);
-    }
-
-    public String getThumbnailPhysicalDatasetPermId()
-    {
-        return thumbnailPhysicalDatasetPermId;
-    }
-
-    @Override
-    public String toString()
-    {
-        final StringBuilder buffer = new StringBuilder();
-        ToStringUtil.appendNameAndObject(buffer, "number of thumbnails",
-                imageToThumbnailPathMap.size());
-        ToStringUtil.appendNameAndObject(buffer, "dataset perm id", thumbnailPhysicalDatasetPermId);
-        return buffer.toString();
-    }
-}
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailsInfo.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailsInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..d72cb466a7622e4d99752a24daa577007c77565c
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailsInfo.java
@@ -0,0 +1,117 @@
+package ch.systemsx.cisd.openbis.dss.etl.dto.api.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.Size;
+import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ToStringUtil;
+
+/**
+ * Stores the mapping between image and their thumbnails paths. Thread-safe class.
+ * 
+ * @author Tomasz Pylak
+ */
+public class ThumbnailsInfo
+{
+    public static class PhysicalDatasetInfo
+    {
+        private final String rootPath;
+
+        private int thumbnailsWidth;
+
+        private int thumbnailsHeight;
+
+        public PhysicalDatasetInfo(String rootPath)
+        {
+            this.rootPath = rootPath;
+        }
+    }
+
+    private final Map<RelativeImageFile, String> imageToThumbnailPathMap;
+
+    private final Map<String, PhysicalDatasetInfo> datasetInfos;
+
+    public ThumbnailsInfo()
+    {
+        this.imageToThumbnailPathMap = new HashMap<RelativeImageFile, String>();
+        this.datasetInfos = new HashMap<String, ThumbnailsInfo.PhysicalDatasetInfo>();
+    }
+
+    public void putDataSet(String permId, String rootPath)
+    {
+        datasetInfos.put(permId, new PhysicalDatasetInfo(rootPath));
+    }
+
+    /**
+     * Saves the path to the thumbnail for the specified image. The thumbnail path is relative and
+     * starts with the name of the thumbnail file.
+     */
+    public synchronized void saveThumbnailPath(String permId, RelativeImageFile image,
+            String thumbnailRelativePath, int width, int height)
+    {
+        imageToThumbnailPathMap.put(image, thumbnailRelativePath);
+
+        PhysicalDatasetInfo datasetInfo = datasetInfos.get(permId);
+        if (datasetInfo != null)
+        {
+            datasetInfo.thumbnailsWidth = Math.max(datasetInfo.thumbnailsWidth, width);
+            datasetInfo.thumbnailsHeight = Math.max(datasetInfo.thumbnailsHeight, height);
+        }
+    }
+
+    public synchronized String getThumbnailPath(RelativeImageFile image)
+    {
+        return imageToThumbnailPathMap.get(image);
+    }
+
+    public Set<String> getThumbnailPhysicalDatasetsPermIds()
+    {
+        return datasetInfos.keySet();
+    }
+
+    public Size tryGetDimension(String permId)
+    {
+        PhysicalDatasetInfo datasetInfo = datasetInfos.get(permId);
+        if (datasetInfo != null)
+        {
+            if (datasetInfo.thumbnailsWidth > 0 && datasetInfo.thumbnailsHeight > 0)
+            {
+                return new Size(datasetInfo.thumbnailsWidth, datasetInfo.thumbnailsHeight);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString()
+    {
+        final StringBuilder buffer = new StringBuilder();
+        ToStringUtil.appendNameAndObject(buffer, "number of thumbnails",
+                imageToThumbnailPathMap.size());
+        ToStringUtil.appendNameAndObject(buffer, "dataset perm ids: ", printPermIds());
+        return buffer.toString();
+    }
+
+    private String printPermIds()
+    {
+        StringBuilder sb = new StringBuilder("[");
+        boolean notFirst = false;
+        for (String permId : datasetInfos.keySet())
+        {
+            if (notFirst)
+            {
+                sb.append(";");
+                notFirst = true;
+            }
+            sb.append(" ").append(permId);
+        }
+        return sb.append(" ]").toString();
+    }
+
+    public String getRootPath(String permId)
+    {
+        return datasetInfos.get(permId).rootPath;
+    }
+}
\ No newline at end of file
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 64351d4752db5a855348b1dbc57bf3f05bdab8ff..36b75120b11706c345f9f4644a0b6e582c2fa9ec 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
@@ -30,14 +30,13 @@ import ch.systemsx.cisd.etlserver.registrator.api.v1.impl.DataSetRegistrationTra
 import ch.systemsx.cisd.openbis.dss.Constants;
 import ch.systemsx.cisd.openbis.dss.etl.Hdf5ThumbnailGenerator;
 import ch.systemsx.cisd.openbis.dss.etl.PlateGeometryOracle;
-import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.FeatureDefinition;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.FeatureVectorDataSetInformation;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.FeaturesBuilder;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageContainerDataSet;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetInformation;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetStructure;
-import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailFilePaths;
+import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IFeaturesBuilder;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IImageDataSet;
 import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IImagingDataSetRegistrationTransaction;
@@ -429,17 +428,17 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data
                         imageDataSetStructure.getImageStorageConfiguraton()
                                 .getThumbnailsStorageFormat();
 
+                ThumbnailsInfo thumbnailsInfo = new ThumbnailsInfo();
                 for (ThumbnailsStorageFormat thumbnailsStorageFormat : thumbnailsStorageFormatList)
                 {
                     IDataSet thumbnailDataset = createThumbnailDataset();
                     thumbnailDatasets.add(thumbnailDataset);
 
-                    ThumbnailFilePaths thumbnailPaths =
-                            generateThumbnails(imageDataSetStructure, incomingDirectory,
-                                    thumbnailDataset, thumbnailsStorageFormat);
-                    imageDataSetInformation.setThumbnailFilePaths(thumbnailPaths);
+                    generateThumbnails(imageDataSetStructure, incomingDirectory, thumbnailDataset,
+                            thumbnailsStorageFormat, thumbnailsInfo);
                     containedDataSetCodes.add(thumbnailDataset.getDataSetCode());
                 }
+                imageDataSetInformation.setThumbnailsInfo(thumbnailsInfo);
             }
             // create main dataset (with original images)
             @SuppressWarnings("unchecked")
@@ -470,9 +469,9 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data
             return new File(originalDirName + File.separator + directoryPath);
         }
 
-        private ThumbnailFilePaths generateThumbnails(ImageDataSetStructure imageDataSetStructure,
+        private void generateThumbnails(ImageDataSetStructure imageDataSetStructure,
                 File incomingDirectory, IDataSet thumbnailDataset,
-                ThumbnailsStorageFormat thumbnailsStorageFormatOrNull)
+                ThumbnailsStorageFormat thumbnailsStorageFormatOrNull, ThumbnailsInfo thumbnailPaths)
         {
             String thumbnailFile;
             if (thumbnailsStorageFormatOrNull == null)
@@ -487,22 +486,10 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data
                                 thumbnailsStorageFormatOrNull.getThumbnailsFileName());
             }
 
-            List<RelativeImageFile> images = asRelativeImageFile(imageDataSetStructure.getImages());
-            ThumbnailFilePaths thumbnailPaths =
-                    Hdf5ThumbnailGenerator.tryGenerateThumbnails(images, incomingDirectory,
-                            thumbnailFile, imageDataSetStructure.getImageStorageConfiguraton(),
-                            thumbnailDataset.getDataSetCode(), thumbnailsStorageFormatOrNull);
-            return thumbnailPaths;
-        }
-
-        private static List<RelativeImageFile> asRelativeImageFile(List<ImageFileInfo> images)
-        {
-            List<RelativeImageFile> imageFiles = new ArrayList<RelativeImageFile>();
-            for (ImageFileInfo image : images)
-            {
-                imageFiles.add(RelativeImageFile.create(image));
-            }
-            return imageFiles;
+            Hdf5ThumbnailGenerator.tryGenerateThumbnails(imageDataSetStructure, incomingDirectory,
+                    thumbnailFile, imageDataSetStructure.getImageStorageConfiguraton(),
+                    thumbnailDataset.getDataSetCode(), thumbnailsStorageFormatOrNull,
+                    thumbnailPaths);
         }
 
         private IDataSet createThumbnailDataset()
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 2ed895dd957f9a04b875622604eca2cd86e6f08f..3ce22ddfcdd909f6c5ddd089f13f9340e15dad64 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
@@ -346,7 +346,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc
         for (String channelCode : params.getChannelsCodes())
         {
             AbsoluteImageReference image =
-                    imageAccessor.tryGetRepresentativeThumbnail(channelCode, null);
+                    imageAccessor.tryGetRepresentativeThumbnail(channelCode, null, null);
             if (image != null)
             {
                 return image.getUnchangedImage();
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java
index c54e10e35d4b597f36cf5624bd28e7e3a693036a..52efb64ba2862c435339f1deefbd75430dfcd1af 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java
@@ -235,9 +235,8 @@ public interface IImagingReadonlyQueryDAO extends BaseQuery
         { StringArrayMapper.class }, fetchSize = FETCH_SIZE)
     public List<ImgImageDatasetDTO> listImageDatasetsByPermId(String... datasetPermIds);
 
-    @Select(sql = "select * from IMAGE_ZOOM_LEVELS zoom, IMAGE_DATA_SETS img_ds "
-            + "where zoom.CONTAINER_DATASET_ID = img_ds.id and img_ds.PERM_ID = ?{1}", fetchSize = FETCH_SIZE)
-    public List<ImgImageZoomLevelDTO> listImageZoomLevels(String containerDatasetPermId);
+    @Select(sql = "select * from IMAGE_ZOOM_LEVELS zoom where zoom.CONTAINER_DATASET_ID = ?{1}", fetchSize = FETCH_SIZE)
+    public List<ImgImageZoomLevelDTO> listImageZoomLevels(long datasetId);
 
     @Select(sql = "select * from ANALYSIS_DATA_SETS where PERM_ID = any(?{1})", parameterBindings =
         { StringArrayMapper.class }, fetchSize = FETCH_SIZE)
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java
index cb006c85e95ff04441e84251aa5f7b7ae1b47b15..ad1774d821f5b8df77b5c9bd306930b5e60eec28 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java
@@ -34,18 +34,30 @@ public class ImgImageZoomLevelDTO extends AbstractImgIdentifiable
     @ResultColumn("container_dataset_id")
     private long containerDatasetId;
 
+    @ResultColumn("path")
+    private String rootPath;
+
+    @ResultColumn("width")
+    private Integer width;
+
+    @ResultColumn("height")
+    private Integer height;
+
     @SuppressWarnings("unused")
     private ImgImageZoomLevelDTO()
     {
         // All Data-Object classes must have a default constructor.
     }
 
-    public ImgImageZoomLevelDTO(String physicalDatasetPermId, boolean isOriginal,
-            long containerDatasetId)
+    public ImgImageZoomLevelDTO(String physicalDatasetPermId, boolean isOriginal, String rootPath,
+            Integer width, Integer height, long containerDatasetId)
     {
         this.physicalDatasetPermId = physicalDatasetPermId;
         this.isOriginal = isOriginal;
         this.containerDatasetId = containerDatasetId;
+        this.rootPath = rootPath;
+        this.width = width;
+        this.height = height;
     }
 
     public String getPhysicalDatasetPermId()
@@ -77,4 +89,34 @@ public class ImgImageZoomLevelDTO extends AbstractImgIdentifiable
     {
         this.containerDatasetId = containerDatasetId;
     }
+
+    public String getRootPath()
+    {
+        return rootPath;
+    }
+
+    public void setRootPath(String rootPath)
+    {
+        this.rootPath = rootPath;
+    }
+
+    public Integer getWidth()
+    {
+        return width;
+    }
+
+    public void setWidth(Integer width)
+    {
+        this.width = width;
+    }
+
+    public Integer getHeight()
+    {
+        return height;
+    }
+
+    public void setHeight(Integer height)
+    {
+        this.height = height;
+    }
 }
diff --git a/screening/source/sql/imaging/postgresql/019/schema-019.sql b/screening/source/sql/imaging/postgresql/019/schema-019.sql
new file mode 100644
index 0000000000000000000000000000000000000000..476e44e4a39043e60aae1f3c046e4ae4c8310bc5
--- /dev/null
+++ b/screening/source/sql/imaging/postgresql/019/schema-019.sql
@@ -0,0 +1,410 @@
+
+/* ---------------------------------------------------------------------- */
+/* Domains                                                                */
+/* ---------------------------------------------------------------------- */
+
+CREATE DOMAIN TECH_ID AS BIGINT;
+
+CREATE DOMAIN CODE AS VARCHAR(40);
+
+CREATE DOMAIN NAME AS VARCHAR(80);
+
+CREATE DOMAIN DESCRIPTION AS VARCHAR(200);
+
+CREATE DOMAIN FILE_PATH as VARCHAR(1000);
+
+CREATE DOMAIN COLOR_COMPONENT AS VARCHAR(40) CHECK (VALUE IN ('RED', 'GREEN', 'BLUE'));
+
+CREATE DOMAIN CHANNEL_COLOR AS VARCHAR(20) CHECK (VALUE IN ('BLUE', 'GREEN', 'RED', 'RED_GREEN', 'RED_BLUE', 'GREEN_BLUE'));
+ 
+CREATE DOMAIN BOOLEAN_CHAR AS BOOLEAN DEFAULT FALSE;
+
+/* ---------------------------------------------------------------------- */
+/* Tables                                                                 */
+/* ---------------------------------------------------------------------- */
+
+CREATE TABLE EXPERIMENTS (
+  ID BIGSERIAL NOT NULL,
+  PERM_ID CODE NOT NULL,
+  IMAGE_TRANSFORMER_FACTORY BYTEA,
+
+  PRIMARY KEY (ID),
+  UNIQUE (PERM_ID)
+);
+
+CREATE TABLE CONTAINERS (
+  ID BIGSERIAL NOT NULL,
+  PERM_ID CODE NOT NULL,
+
+  SPOTS_WIDTH INTEGER,
+  SPOTS_HEIGHT INTEGER,
+  
+  EXPE_ID TECH_ID NOT NULL,
+
+  PRIMARY KEY (ID),
+  UNIQUE (PERM_ID),
+  CONSTRAINT FK_SAMPLE_1 FOREIGN KEY (EXPE_ID) REFERENCES EXPERIMENTS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX CONTAINERS_EXPE_IDX ON CONTAINERS(EXPE_ID);
+
+CREATE TABLE SPOTS (
+  ID BIGSERIAL NOT NULL,
+	
+	-- position in the container, one-based
+  X INTEGER, 
+  Y INTEGER, 
+  CONT_ID TECH_ID NOT NULL,
+  
+  PRIMARY KEY (ID),
+  CONSTRAINT FK_SPOT_1 FOREIGN KEY (CONT_ID) REFERENCES CONTAINERS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX SPOTS_CONT_IDX ON SPOTS(CONT_ID);
+-- allows to select one spot of the container quicker
+CREATE INDEX SPOTS_COORDS_IDX ON SPOTS(CONT_ID, X, Y);
+
+CREATE TABLE ANALYSIS_DATA_SETS (
+  ID BIGSERIAL NOT NULL,
+  PERM_ID CODE NOT NULL,
+
+  CONT_ID TECH_ID,
+  
+  PRIMARY KEY (ID),
+  UNIQUE (PERM_ID),
+  CONSTRAINT FK_ANALYSIS_DATA_SET_1 FOREIGN KEY (CONT_ID) REFERENCES CONTAINERS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX ANALYSIS_DATA_SETS_CONT_IDX ON ANALYSIS_DATA_SETS(CONT_ID);
+
+
+CREATE TABLE IMAGE_DATA_SETS (
+  ID BIGSERIAL NOT NULL,
+  PERM_ID CODE NOT NULL,
+
+  ---- image dataset specific fields (should be refactored) 
+	FIELDS_WIDTH INTEGER,
+	FIELDS_HEIGHT INTEGER,	
+  -- transformation for merged channels on the dataset level, overrides experiment level transformation
+  IMAGE_TRANSFORMER_FACTORY BYTEA,
+  -- a redundant information if there are timepoint or depth stack data for any spots in this dataset
+  IS_MULTIDIMENSIONAL BOOLEAN_CHAR NOT NULL,
+
+  -- Which image library should be used to read the image? 
+  -- If not specified, some heuristics are used, but it is slower and does not try with all the available libraries. 
+  IMAGE_LIBRARY_NAME NAME,
+  -- Which reader in the library should be used? Valid only if the library name is specified.
+  -- Should be specified when library name is specified.
+  IMAGE_LIBRARY_READER_NAME NAME,
+  ---- END image dataset specific fields
+  
+  CONT_ID TECH_ID,
+  
+  PRIMARY KEY (ID),
+  UNIQUE (PERM_ID),
+  CONSTRAINT FK_IMAGE_DATA_SET_1 FOREIGN KEY (CONT_ID) REFERENCES CONTAINERS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX IMAGE_DATA_SETS_CONT_IDX ON IMAGE_DATA_SETS(CONT_ID);
+
+CREATE TABLE IMAGE_ZOOM_LEVELS (
+  ID BIGSERIAL NOT NULL,
+
+  IS_ORIGINAL BOOLEAN_CHAR NOT NULL,
+  CONTAINER_DATASET_ID TECH_ID NOT NULL, 
+  
+  -- Perm id of the 'physical' dataset which contains all images with this zoom.
+  -- Physical datasets are not stored in "image_data_sets" table, but we need the reference to them 
+  -- when we delete or archive one zoom level. 
+  PHYSICAL_DATASET_PERM_ID TEXT NOT NULL,
+  
+  PATH FILE_PATH,
+  WIDTH INTEGER,
+  HEIGHT INTEGER,
+  
+  PRIMARY KEY (ID),
+  UNIQUE (PHYSICAL_DATASET_PERM_ID),
+  CONSTRAINT FK_IMAGE_ZOOM_LEVELS_1 FOREIGN KEY (CONTAINER_DATASET_ID) REFERENCES IMAGE_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX IMAGE_ZOOM_LEVELS_PHYS_DS_IDX ON IMAGE_ZOOM_LEVELS (PHYSICAL_DATASET_PERM_ID);
+CREATE INDEX IMAGE_ZOOM_LEVELS_CONT_FK_IDX ON IMAGE_ZOOM_LEVELS (CONTAINER_DATASET_ID);
+
+CREATE TABLE CHANNELS (
+    ID BIGSERIAL  NOT NULL,
+    
+    CODE NAME NOT NULL,
+    LABEL NAME NOT NULL,
+    DESCRIPTION DESCRIPTION,
+    WAVELENGTH INTEGER,
+
+    -- RGB color components specify the color in which channel should be displayed
+    RED_CC INTEGER NOT NULL,
+    GREEN_CC INTEGER NOT NULL,
+    BLUE_CC INTEGER NOT NULL,
+
+    DS_ID TECH_ID,
+    EXP_ID TECH_ID,
+    
+    PRIMARY KEY (ID),
+    CONSTRAINT FK_CHANNELS_1 FOREIGN KEY (DS_ID) REFERENCES IMAGE_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT FK_CHANNELS_2 FOREIGN KEY (EXP_ID) REFERENCES EXPERIMENTS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT CHANNELS_DS_EXP_ARC_CK CHECK ((DS_ID IS NOT NULL AND EXP_ID IS NULL) OR (DS_ID IS NULL AND EXP_ID IS NOT NULL)),
+    
+    CONSTRAINT CHANNELS_UK_1 UNIQUE(CODE, DS_ID),
+    CONSTRAINT CHANNELS_UK_2 UNIQUE(CODE, EXP_ID)
+);
+
+CREATE INDEX CHANNELS_DS_IDX ON CHANNELS(DS_ID);
+
+CREATE TABLE IMAGE_TRANSFORMATIONS (
+    ID BIGSERIAL  NOT NULL,
+    
+    CODE NAME NOT NULL,
+    LABEL NAME NOT NULL,
+    DESCRIPTION character varying(1000),
+    IMAGE_TRANSFORMER_FACTORY BYTEA NOT NULL,
+    
+    -- For now there can be only one transformation for each channel which is editable by Image Viewer,
+    -- but when GUI will support more then this column will become really useful.
+    IS_EDITABLE BOOLEAN_CHAR NOT NULL,
+    
+    -- The default choice to present the image.
+    -- If not present a 'hard-coded' default transformation will become available. 
+    IS_DEFAULT BOOLEAN_CHAR NOT NULL DEFAULT 'F',
+    
+    CHANNEL_ID TECH_ID NOT NULL,
+    
+    PRIMARY KEY (ID),
+    CONSTRAINT FK_IMAGE_TRANSFORMATIONS_CHANNEL FOREIGN KEY (CHANNEL_ID) REFERENCES CHANNELS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
+    
+    CONSTRAINT IMAGE_TRANSFORMATIONS_UK_1 UNIQUE(CODE, CHANNEL_ID)
+);
+
+CREATE INDEX IMAGE_TRANSFORMATIONS_CHANNELS_IDX ON IMAGE_TRANSFORMATIONS(CHANNEL_ID);
+
+CREATE TABLE CHANNEL_STACKS (
+    ID BIGSERIAL  NOT NULL,
+		
+		-- x and y are kind of a two dimensional sequence number, some use case may only use x and leave y alone
+		X INTEGER,
+		Y INTEGER,
+		-- We use the fixed dimension meters here.
+		Z_in_M REAL,
+		-- We use the fixed dimension seconds here.
+		T_in_SEC REAL,
+		SERIES_NUMBER INTEGER,
+		
+		-- For all channel stacks of a well (HCS) or image dataset (microscopy) there should be exactly 
+		-- one record with is_representative = true
+		is_representative BOOLEAN_CHAR NOT NULL DEFAULT 'F',
+    
+    DS_ID TECH_ID	NOT NULL,
+		SPOT_ID TECH_ID,
+
+    PRIMARY KEY (ID),
+    CONSTRAINT FK_CHANNEL_STACKS_1 FOREIGN KEY (SPOT_ID) REFERENCES SPOTS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT FK_CHANNEL_STACKS_2 FOREIGN KEY (DS_ID) REFERENCES IMAGE_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX CHANNEL_STACKS_DS_IDX ON CHANNEL_STACKS(DS_ID);
+CREATE INDEX CHANNEL_STACKS_SPOT_IDX ON CHANNEL_STACKS(SPOT_ID);
+CREATE INDEX CHANNEL_STACKS_DIM_IDX ON CHANNEL_STACKS(X, Y, Z_in_M, T_in_SEC);
+
+CREATE TABLE IMAGES (
+    ID BIGSERIAL  NOT NULL,
+   
+    PATH	FILE_PATH NOT NULL,
+    IMAGE_ID	CODE,
+    COLOR	COLOR_COMPONENT,
+    
+    PRIMARY KEY (ID)
+);
+
+CREATE TABLE ACQUIRED_IMAGES (
+    ID BIGSERIAL  NOT NULL,
+   
+		IMG_ID TECH_ID,
+		THUMBNAIL_ID TECH_ID,
+		IMAGE_TRANSFORMER_FACTORY BYTEA,
+
+    CHANNEL_STACK_ID  TECH_ID NOT NULL,
+    CHANNEL_ID  TECH_ID NOT NULL,
+
+    PRIMARY KEY (ID),
+    CONSTRAINT FK_IMAGES_1 FOREIGN KEY (CHANNEL_STACK_ID) REFERENCES CHANNEL_STACKS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT FK_IMAGES_2 FOREIGN KEY (CHANNEL_ID) REFERENCES CHANNELS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT FK_IMAGES_3 FOREIGN KEY (IMG_ID) REFERENCES IMAGES (ID) ON DELETE SET NULL ON UPDATE CASCADE,
+    CONSTRAINT FK_IMAGES_4 FOREIGN KEY (THUMBNAIL_ID) REFERENCES IMAGES (ID) ON DELETE SET NULL ON UPDATE CASCADE
+);
+
+CREATE INDEX IMAGES_CHANNEL_STACK_IDX ON ACQUIRED_IMAGES(CHANNEL_STACK_ID);
+CREATE INDEX IMAGES_CHANNEL_IDX ON ACQUIRED_IMAGES(CHANNEL_ID);
+CREATE INDEX IMAGES_IMG_IDX ON ACQUIRED_IMAGES(IMG_ID);
+CREATE INDEX IMAGES_THUMBNAIL_IDX ON ACQUIRED_IMAGES(THUMBNAIL_ID);
+
+CREATE TABLE EVENTS (
+  LAST_SEEN_DELETION_EVENT_ID TECH_ID NOT NULL
+);
+
+/* ---------------------------------------------------------------------- */
+/* FEATURE VECTORS                                                        */
+/* ---------------------------------------------------------------------- */ 
+
+CREATE TABLE FEATURE_DEFS (
+    ID BIGSERIAL  NOT NULL,
+    
+    CODE NAME NOT NULL,
+    LABEL NAME NOT NULL,
+    DESCRIPTION DESCRIPTION,
+    
+    DS_ID  TECH_ID NOT NULL,
+    
+    PRIMARY KEY (ID),
+    CONSTRAINT FK_FEATURE_DEFS_1 FOREIGN KEY (DS_ID) REFERENCES ANALYSIS_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT FEATURE_DEFS_UK_1 UNIQUE(CODE, DS_ID)
+);
+
+CREATE INDEX FEATURE_DEFS_DS_IDX ON FEATURE_DEFS(DS_ID);
+
+CREATE TABLE FEATURE_VOCABULARY_TERMS (
+	ID BIGSERIAL  NOT NULL,
+
+  CODE NAME NOT NULL,
+  SEQUENCE_NUMBER INTEGER NOT NULL,
+	FD_ID  TECH_ID NOT NULL,
+
+	PRIMARY KEY (ID),
+	CONSTRAINT FK_FEATURE_VOCABULARY_TERMS_1 FOREIGN KEY (FD_ID) REFERENCES FEATURE_DEFS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX FEATURE_VOCABULARY_TERMS_FD_IDX ON FEATURE_VOCABULARY_TERMS(FD_ID);
+
+CREATE TABLE FEATURE_VALUES (
+    ID BIGSERIAL  NOT NULL,
+		
+		-- we use the fixed dimension meters here
+		Z_in_M REAL,
+		-- we use the fixed dimension seconds here
+		T_in_SEC REAL,
+		-- Serialized 2D matrix with values for each spot.
+		-- Contains floats which can be NaN. 
+		-- It is never a case that the whole matrix contains NaN - in such a case we save nothing.
+		-- If feature definition has some connected vocabulary terms then the matrix 
+		-- stores FEATURE_VOCABULARY_TERMS.SEQUENCE_NUMBER of the terms (should be casted from float to int).
+		-- If the term is null the Float.NaN is stored.
+		VALUES BYTEA NOT NULL,
+		
+		FD_ID  TECH_ID NOT NULL,
+		
+		PRIMARY KEY (ID),
+		CONSTRAINT FK_FEATURE_VALUES_1 FOREIGN KEY (FD_ID) REFERENCES FEATURE_DEFS (ID) ON DELETE CASCADE ON UPDATE CASCADE
+    -- This constaint does not make any sense. Leave it out for now.
+    -- CONSTRAINT FEATURE_VALUES_UK_1 UNIQUE(Z_in_M, T_in_SEC)
+);
+
+CREATE INDEX FEATURE_VALUES_FD_IDX ON FEATURE_VALUES(FD_ID);
+CREATE INDEX FEATURE_VALUES_Z_AND_T_IDX ON FEATURE_VALUES(Z_in_M, T_in_SEC);
+
+
+/* ---------------------------------------------------------------------- */
+/* FUNCTIONS AND TRIGGERS                                                 */
+/* ---------------------------------------------------------------------- */ 
+
+CREATE OR REPLACE FUNCTION DELETE_UNUSED_IMAGES() RETURNS trigger AS $$
+BEGIN
+   delete from images where id = OLD.img_id or id = OLD.thumbnail_id;
+   RETURN NEW;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER UNUSED_IMAGES AFTER DELETE ON ACQUIRED_IMAGES
+    FOR EACH ROW EXECUTE PROCEDURE DELETE_UNUSED_IMAGES();
+
+CREATE OR REPLACE FUNCTION DELETE_UNUSED_NULLED_IMAGES() RETURNS trigger AS $$
+BEGIN
+	if NEW.img_id IS NULL then
+		if OLD.img_id IS NOT NULL then
+		  delete from images where id = OLD.img_id;
+		end if;
+	end if;
+	if NEW.thumbnail_id IS NULL then
+		if OLD.thumbnail_id IS NOT NULL then
+		  delete from images where id = OLD.thumbnail_id;
+		end if;
+	end if;
+	RETURN NEW;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER UNUSED_NULLED_IMAGES AFTER UPDATE ON ACQUIRED_IMAGES
+    FOR EACH ROW EXECUTE PROCEDURE DELETE_UNUSED_NULLED_IMAGES();
+
+CREATE OR REPLACE FUNCTION DELETE_EMPTY_ACQUIRED_IMAGES() RETURNS trigger AS $$
+BEGIN
+	delete from acquired_images where id = OLD.id;
+	RETURN NEW;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER EMPTY_ACQUIRED_IMAGES BEFORE UPDATE ON ACQUIRED_IMAGES
+		FOR EACH ROW
+		WHEN (NEW.img_id IS NULL AND NEW.thumbnail_id IS NULL)
+		EXECUTE PROCEDURE DELETE_EMPTY_ACQUIRED_IMAGES();
+
+
+------------------------------------------------------------------------------------
+--  Purpose:  Create trigger CHANNEL_STACKS_CHECK which checks if both spot_id and dataset.cont_id 
+--            are both null or not null.
+------------------------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION CHANNEL_STACKS_CHECK() RETURNS trigger AS $$
+DECLARE
+   v_cont_id  CODE;
+BEGIN
+
+   select cont_id into v_cont_id from image_data_sets where id = NEW.ds_id;
+
+   -- Check that if there is no spot than there is no dataset container as well
+   if v_cont_id IS NULL then
+      if NEW.spot_id IS NOT NULL then
+         RAISE EXCEPTION 'Insert/Update of CHANNEL_STACKS failed, as the dataset container is not set, but spot is (spot id = %).',NEW.spot_id;
+      end if;
+	 else
+      if NEW.spot_id IS NULL then
+         RAISE EXCEPTION 'Insert/Update of CHANNEL_STACKS failed, as the dataset container is set (id = %), but spot is not set.',v_cont_id;
+      end if; 
+   end if;
+   RETURN NEW;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER CHANNEL_STACKS_CHECK BEFORE INSERT OR UPDATE ON CHANNEL_STACKS
+    FOR EACH ROW EXECUTE PROCEDURE CHANNEL_STACKS_CHECK();
+    
+------------------------------------------------------------------------------------
+--  Purpose:  Create trigger IMAGE_TRANSFORMATIONS_DEFAULT_CHECK which checks 
+--            if at most one channel's transformation is default
+------------------------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION IMAGE_TRANSFORMATIONS_DEFAULT_CHECK() RETURNS trigger AS $$
+DECLARE
+   v_is_default boolean;
+BEGIN
+   if NEW.is_default = 'T' then
+	   select is_default into v_is_default from IMAGE_TRANSFORMATIONS 
+	   	where is_default = 'T' 
+	   			  and channel_id = NEW.channel_id
+	   				and id != NEW.id;
+	   if v_is_default is NOT NULL then
+	      RAISE EXCEPTION 'Insert/Update of image transformation (Code: %) failed, as the new record has is_default set to true and there is already a default record defined.', NEW.code;
+	   end if;
+   end if;
+
+   RETURN NEW;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER IMAGE_TRANSFORMATIONS_DEFAULT_CHECK BEFORE INSERT OR UPDATE ON IMAGE_TRANSFORMATIONS
+    FOR EACH ROW EXECUTE PROCEDURE IMAGE_TRANSFORMATIONS_DEFAULT_CHECK();
+    
\ No newline at end of file
diff --git a/screening/source/sql/imaging/postgresql/migration/migration-018-019.sql b/screening/source/sql/imaging/postgresql/migration/migration-018-019.sql
new file mode 100644
index 0000000000000000000000000000000000000000..4e727895a8047c5c995cd14844414d62ffed24e3
--- /dev/null
+++ b/screening/source/sql/imaging/postgresql/migration/migration-018-019.sql
@@ -0,0 +1,5 @@
+-- Migration from 018 to 019
+
+ALTER TABLE IMAGE_ZOOM_LEVELS ADD COLUMN PATH FILE_PATH;
+ALTER TABLE IMAGE_ZOOM_LEVELS ADD COLUMN  WIDTH INTEGER;
+ALTER TABLE IMAGE_ZOOM_LEVELS ADD COLUMN  HEIGHT INTEGER;
\ No newline at end of file
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java
index 068607e700745c2063f4de82fb677d6898c97618..fc2f669aab8a7758701c1dcf09bcea59136341f1 100644
--- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java
@@ -428,9 +428,9 @@ public class ImagingQueryDAOTest extends AbstractDBTest
         final long datasetId = dao.addImageDataset(dataset);
         String physicalDatasetPermIdPrefix = permId + "666";
         dao.addImageZoomLevel(new ImgImageZoomLevelDTO(physicalDatasetPermIdPrefix + "-123", true,
-                datasetId));
+                "original", 800, 600, datasetId));
         dao.addImageZoomLevel(new ImgImageZoomLevelDTO(physicalDatasetPermIdPrefix + "-666", false,
-                datasetId));
+                "thumbs", 200, 150, datasetId));
 
         final ImgImageDatasetDTO loadedDataset = dao.tryGetImageDatasetByPermId(permId);
         assertNotNull(loadedDataset);
@@ -448,12 +448,23 @@ public class ImagingQueryDAOTest extends AbstractDBTest
         assertEquals(loadedDataset, datasets.get(0));
 
         // test listImageZoomLevels
-        List<ImgImageZoomLevelDTO> zoomLevels = dao.listImageZoomLevels(permId);
+        List<ImgImageZoomLevelDTO> zoomLevels = dao.listImageZoomLevels(datasetId);
         assertEquals(2, zoomLevels.size());
         for (ImgImageZoomLevelDTO zoomLevel : zoomLevels)
         {
             AssertionUtil.assertContains(physicalDatasetPermIdPrefix,
                     zoomLevel.getPhysicalDatasetPermId());
+            if (zoomLevel.getIsOriginal())
+            {
+                assertEquals("original", zoomLevel.getRootPath());
+                assertEquals(800, zoomLevel.getWidth().intValue());
+                assertEquals(600, zoomLevel.getHeight().intValue());
+            } else
+            {
+                assertEquals("thumbs", zoomLevel.getRootPath());
+                assertEquals(200, zoomLevel.getWidth().intValue());
+                assertEquals(150, zoomLevel.getHeight().intValue());
+            }
         }
 
         return datasetId;