View project on GitHub

ASTex

a C++ library for texture generation

Supported image types

Number of channels: 1/3/4

With the possible channel types:

And spectral images (1 channel) :

All floating-point images can be load and save with on the fly conversion from [0,1]to uint8 classic file format or saved in EXR 32 bits float format (double is not supported).

Tutorials

There are some tutorials that explain briefly concepts and data structures of ASTex:

To run the tuto, first copy Data/*.png in TEMPO_PATH(/tmp)

Itk types

Almost all itk types are defined in any dimension, but do not have nice constructors. We provide convenient generator functions:

Index ind  = gen_index(0,0);
Size  sz   = gen_size(256,256);
Region reg = gen_region(0,0,256,256);

PixelType

The pixel accessors (pixelAbsolute(), pixelRelative()) and the traversal use itk::RGBPixel<CHANNEL_TYPE> and itk::RGBAPixel<CHANNEL_TYPE> types. To avoid the lack of pratical constructor, convenient functions * itkPixel(…) * are provided.

auto pi = ImageRGBu8::itkPixel(65, 55, 45);
auto pi = ImageRGBu8::itkPixel(255); // [255,255,255]

Eigen conversion.

To benefit of all computation capabilities of Eigen vector types we provide conversion fonctions eigenPixel<T>(p). The template parameter T is the scalar type of Eigen vectors (int8_t,…, double). The conversion can be done on the fly when accessing (read/write) to the image with pixelEigenAbsolute method:

using DPE = IMG::DoublePixelEigen;
DPE dp1 = image.pixelEigenAbsolute(0,0);
DPE dp2 = image.pixelEigenAbsolute(1,0);
DPE dp3 = image.pixelEigenAbsolute(2,0);
image.pixelEigenAbsolute(1,1) = (dp1+dp2+dp3)/3;

This allow to easily avoid overflow problem when working with uint8_t pixels. It could also be interesting to normalize floating point value into [0,1] during the conversion. It can be done by using normalized, unnormalized conversion functions and pixelNormEigenAbsolute accessor.

Image traversal

With classical nested loop

for(int j=0; j<img.height(); ++j)
    for(int i=0; i<img.width(); ++i)
	img.pixelAbsolute(i,j) = ...

or if you do not want to think about the order of loops !

for_indices(0,img.width(), 0, img.height(), [&] (int x, int y)
{
   img.pixelAbsolute(i,j) = ...
});

With iterators

Images can be traversed using itk iterators. We just provide simplified syntax of the begin method. Iterators can be const or not, and indexed or not. Indexed mean that you can get back position of pixel from iterator.

...
for (auto it = image.beginConstIterator(); !it.IsAtEnd(); ++it)
{
   average += ImageRGBu8::eigenPixel<double>(it.Value());
   nb ++;
}
average /= nb;
std::cout << "Average: "<<ImageRGBu8::itkPixel(average) << std::endl;
...
Region reg = gen_region(0,0,128,64);
for (auto it = image.beginConstIterator(reg); !it.IsAtEnd(); ++it)
{
   average += ImageRGBu8::eigenPixel<double>(it.Value());
   nb ++;
}
average /= nb;
std::cout << "Average: "<<ImageRGBu8::itkPixel(average) << std::endl;

With for_all_pixel fonction

The for_all_pixel methods allow the traversal of the image, a lambda expression is applied on each pixel. It could have different parameters:

img1.for_all_pixels([] (double& p)
{
    p = 0.0;
});

img2.for_all_pixels( [&] (ImageRGBf::PixelType& p, int x, int y)
{
    p = ImageRGBf::itkPixel(0,0,0);
}

Parallel optimisation

Just by prefixing the for_all_pixel method by parallel_ the traversal is split on as many threads as available on your hardware. Performance acceleration depend on the ratio memory access / computations. Of course data race probleme is left to the attention of the library user.

For per thread storage problem you can add a thread id parameter to your lambda (uint16_t type)

std::vector<double> totals(nb_launched_threads(),0.0);
imgA.parallel_for_region_pixels(rA, [&] (ImageGreyd::PixelType& p, uint16_t th_id)
{
    totals[th_id] += p
});
double total=0.0;
for(double t: totals)
    total+= t;

Mask

A mask is a generic class that expose its () operator (x,y) that return a bool. We provide several kinds of masks:

It is easily usable with for_all_pixel traversal:

// a booelan mask filled randomly with true at 10%
MaskBool mask(img.width(), img.height());
mb.random(0.1);

image.for_all_pixels([&] (ImageRGBu8::PixelType& pix)
{
    pix=ImageRGBu8::itkPixel(0);
},mask);

image.for_all_pixels([&] (ImageRGBu8::PixelType& pix, int x, int y)
{
    pix=ImageRGBu8::itkPixel(200);
},
[&](int i, int j) { return i<j;}
);

Input-Output

Images can simply be loaded/saved with the load/save methods if image type is supported by file format (which is deduced from filename extension).

We also provide convenient functions for loading/saving floating point images (in [0,1]) from/to classic 8 bits file formats: loadu8_in_01(img, filename) and save01_in_u8(img, filename)

IO::EXR::load(img,filename) and IO::EXR::save(img,filename) can be used with floating point images, to load/save in the ILM OpenEXR file format

Fourier

** under construction **