DGtal  0.9.3beta
Handle large images in DGtal

Table of Contents

Authors
Martial Tola, David Coeurjolly
Date
2013/03/12

Part of the Image package

Overview

Large images like tiled images, HDF5 images, ... can be handled in DGtal.

For this, we provide two important mechanisms: an image factory mechanism and an image cache mechanism with read and write policies.

The image factory is necessary to keep control on the created images. As its responsibility is to create images according to a given domain. The factory can update (flush) the image on disk and also can free the image from memory. Image factory models can associated with specific image formats such as HDF5 which can be activated using a WITH_HDF5 flag during DGtal build (please refer to Building DGtal). This mechanism is a DGtal concept called CImageFactory which is detailed below.

As we want to take also into account large images effectively, we also need a cache mechanism to access to subsets, so called tiles, of such images.

The cache has the responsibility to store tiles and giving us read and write access to them. The reading, writing and updating mechanisms of the cache are controlled with two policies: the read and the write policies. These policies are DGtal concepts called respectively CImageCacheReadPolicy and CImageCacheWritePolicy which are detailed just below.

The cache process is rather simple. It consists of three important methods that are detailed here:

Concepts

CImageFactory

Any model of the concept CImageFactory must have this nested type:

Moreover, it must have the following methods:

Cache policies concepts

Cache policies concepts are divided in two concepts: read and write policies.

CImageCacheReadPolicy

Any model of the concept CImageCacheReadPolicy must have this nested type:

Moreover, it must have the following methods:

CImageCacheWritePolicy

Any model of the concept CImageCacheWritePolicy must have this nested type:

Moreover, it must have the following methods:

Models

Image factory models

Cache policies models

Cache policies models are split into read and write policies.

The TiledImage class

The TiledImage is a simple class that implements a tiled image from a "bigger/original" one from an ImageFactory.

The tiled image is created from an existing image and with four parameters:

Note
It is important to take into account that read and write policies are passed as aliases in the TiledImage constructor, so for example, if two TiledImage instances are successively created with the same read policy instance, the state of the cache for a given time is therefore the same for the two TiledImage instances.

Concerning the cache mechanism explained at the top of this document, the accessor operator() (i.e. the getter) and the setter setValue are therefore really simple to write for the TiledImage class:

In order to illustrate the next TiledImage usage sample, we are going a) to use these includes:

#include "DGtal/io/colormaps/HueShadeColorMap.h"
#include "DGtal/images/ImageContainerBySTLVector.h"
#include "DGtal/images/ImageFactoryFromImage.h"
#include "DGtal/images/TiledImage.h"

b) then define these type and variable:

typedef HueShadeColorMap<int> HueShade; // a simple HueShadeColorMap varying on 'int' values

c) then define a simple 16x16 (1,1) to (16,16) image (of 'int' type):

typedef ImageContainerBySTLVector<Z2i::Domain, int> VImage;
VImage image(Z2i::Domain(Z2i::Point(1,1), Z2i::Point(16,16)));

filled with 1 to 256 values like that:

int i = 1;
for (VImage::Iterator it = image.begin(); it != image.end(); ++it)
*it = i++;

which looks like that with a simple HueShadeColorMap varying from 1 to 256 and with (1,1) the first bottom-left point:

tiledImageFromImage-image.png
(1) simple 16x16 source image: (1,1) to (16,16) drawn with a simple HueShadeColorMap varying from 1 to 256.

Here is now the construction of a simple 4x4 tiled image with therefore 16 subdomains of the initial image domain. The tiled image is created from an existing image and with four parameters. The first parameter is an alias on the image factory (see ImageFactoryFromImage). The second parameter is an alias on a read policy. The third parameter is an alias on a write policy. The fourth parameter is to set how many tiles we want for each dimension:

// here we create an image factory
typedef ImageFactoryFromImage<VImage> MyImageFactoryFromImage;
typedef MyImageFactoryFromImage::OutputImage OutputImage;
MyImageFactoryFromImage imageFactoryFromImage(image);
// here we create read and write policies
typedef ImageCacheReadPolicyFIFO<OutputImage, MyImageFactoryFromImage> MyImageCacheReadPolicyFIFO;
typedef ImageCacheWritePolicyWT<OutputImage, MyImageFactoryFromImage> MyImageCacheWritePolicyWT;
MyImageCacheReadPolicyFIFO imageCacheReadPolicyFIFO(imageFactoryFromImage, 2);
MyImageCacheWritePolicyWT imageCacheWritePolicyWT(imageFactoryFromImage);
// here we create the TiledImage
typedef TiledImage<VImage, MyImageFactoryFromImage, MyImageCacheReadPolicyFIFO, MyImageCacheWritePolicyWT> MyTiledImage;
BOOST_CONCEPT_ASSERT(( concepts::CImage< MyTiledImage > ));
MyTiledImage tiledImage(imageFactoryFromImage, imageCacheReadPolicyFIFO, imageCacheWritePolicyWT, 4);

In this sample, we are going to work only with the 8 bottom subdomains of the original image.

At the beginning the cache is empty.

First we want to read the point 4,2 (which is in domain 1),

trace.info() << "Read value for Point 4,2: " << tiledImage(Z2i::Point(4,2)) << endl;

so after that the cache is (domain1,empty). Here is the domain1:

TiledImageFromImage-01-read.png
(3) read: not in cache, so, update. Cache: (domain1,empty). Read: domain1.

Then we want to read the point 10,6 (which is in domain 7),

trace.info() << "Read value for Point 10,6: " << tiledImage(Z2i::Point(10,6)) << endl;

so after that the cache is (domain1,domain7). Here is the domain7:

TiledImageFromImage-03-read.png
(4) read: not in cache, so, update. Cache: (domain1,domain7). Read: domain7.

Then we want to set the value of the point 11,7 to 1 (which is in domain 7),

aValue = 1; tiledImage.setValue(Z2i::Point(11,7), aValue);
trace.info() << "Write value for Point 11,7: " << aValue << endl;

so after that the cache is always (domain1,domain7). Here is the new domain7 after writing:

TiledImageFromImage-05-write.png
(5) write: in cache. Cache: (domain1,domain7). Write: domain7.

Then we want to read the point 2,3 (which is in domain 1),

trace.info() << "Read value for Point 2,3: " << tiledImage(Z2i::Point(2,3)) << endl;

so after that the cache is always (domain1,domain7). Here is the domain1:

TiledImageFromImage-07-read.png
(6) read: in cache. Cache: (domain1,domain7). Read: domain1.

Then we want to read the point 16,1 (which is in domain 4),

trace.info() << "Read value for Point 16,1: " << tiledImage(Z2i::Point(16,1)) << endl;

so after that the cache is (domain7,domain4). Here is the domain4:

TiledImageFromImage-09-read.png
(7) read: not in cache, so, update. Cache: (domain7,domain4). Read: domain4.

Then we want to set the value of the point 16,1 to 128 (which is in domain 4),

aValue = 128; tiledImage.setValue(Z2i::Point(16,1), aValue);
trace.info() << "Write value for Point 16,1: " << aValue << endl;

so after that the cache is always (domain7,domain4). Here is the new domain4 after writing:

TiledImageFromImage-11-write.png
(8) write: in cache. Cache: (domain7,domain4). Write: domain4

And finally, here is the modified original image after the two writings.

tiledImageFromImage-image2.png
(9) result image.