Skip to content

Yocto/Image: Image utilities

Yocto/Image is a collection of image utilities useful when writing rendering algorithms. These include a simple image data structure, color conversion utilities and tone mapping, and image resizing. Yocto/Image is implemented in yocto_image.h and yocto_image.cpp, and depends on stb_image_resize.h.

Image representation

Images are represented as a simple struct called image_data, that stores the image width and height, a boolean flat that indicates whether the colors are in linear ot sRGB color space, and an array of pixels of vec4f type.

While images can be constructed by direct manipulation of their values, th preferred way to construct images is by using the make_image(widgh, height, linear) function, where colors are default initialized to black.

Image pixels are stored in row-major order and be accessed with index j * width + i. They can be conveniently accessed using get_pixel(image,i,j) and set_pixel(image,i,j,color) function.

auto image = make_image(512,512,false); // creates a 512x512 sRGB image
for(auto& pixel : image.pixels)         // iterate over pixels
  print_info(pixel);
auto color = get_pixel(image,128,128);  // pixel access
for(auto j=0; j<image.height; j++)      // iterate over image rows
  for(auto i=0; i<image.width; i++)     // iterate over row pixels
    print_info(get_pixel(image,i,j));

Images are sampled with eval_image(img,uv,...), where uv are in the [0,1]x[0,1] range. By default, image values are returned in the color space of the image, but can be forced to to be linear. By default, images are sampled with bilinear interpolation and tiling, with nearest-neighbor interpolation and edge-clamp behavior available as overrides.

auto hdr = make_image(w,h,true);        // creates a linear image
auto ch0 = eval_image(hdr, {0.5,0.5});  // samples the image center
auto ch1 = eval_image(hdr, {0.5,0.5}, false, false);  // no interpolation
auto ch2 = eval_image(hdr, {0.5,0.5}, false, false, false);  // clamp to edge
auto ldr = make_image(w,h,false);       // creates a sRGB image
auto ch0 = eval_image(ldr, {0.5,0.5});  // samples in sRGB, returns as sRGB
auto ch1 = eval_image(ldr, {0.5,0.5}, true);  // treats sRGB values as linear

Image serialization

Image loading and saving is defined in Yocto/ImageIO.

Image utilities

Images can be converted between linear RGB and sRGB color space using convert_image(image,linear).

auto linear = make_image(w,h,true);   // initialize a linear image
auto srgb = convert_image(w,h,false); // convert to sRGB

HDR images can be tone mapped using tonemap_image(hdr,exposure,filmic) that applies an exposure correction followed by an optional filmic tone curve. Use tonemap_image_mt(...) to quickly tone map large images using multiple threads.

auto hdr = make_image(w,h,true);         // initialize am HDR image
auto ldr = tonemap_image(hdr, 2);        // tone mapping with exposure 2
auto flm = tonemap_image(hdr, 2, true);  // filmic tone mapping

Images can be color graded by applying a set of minimal color grading tools using colorgrade_image(image,params), in manner similar to Hable 2017. Color grading corrections can be applied on images that are either linear HDR or sRGB encoded. The results is always an LDR image encoded in sRGB. Use colorgrade_image_mt(...) to quickly tone map large images using multiple threads.

Several color corrections are bundled together with their parameters packed in a convenience structure colorgrade_params. Color grading operations are applied in a fixed sequence and consist of the following operations: exposure compensation, color tint, contrast in the log domain, filmic curve, conversion to sRGB, S-shaped contrast, saturation, and shadow/midtone/highlight correction. Color tinting can be used to apply white balance by using compute_white_balance(img) to determine the correct color.

auto hdr = make_image(w,h,true);         // initialize an HDR image
auto params = colorgrade_params{};       // default color grading params
params.exposure = 1;                     // set desired params
params.logcontrast = 0.75;               // set desired params
params.tint = compute_white_balance(hdr);// apply white balance
auto ldr = colorgrade_image(hdr, params);// color grading

Images are can resized with resize_image(image,w,h). Just like all other functions, images are not resized in placed, but a new image is created. Resizing works for both linear and 8bit images.

auto img = make_image(...);              // initialize an image
auto res = resize_image(img, 512, 512);  // resizing to fixed size
auto asp = resize_image(img, 512, 0);    // aspect-preserving

Image differences can be computed using image_difference(a,b). This function performs a simple per-pixel difference and returns the image so that one can use any metric to determine whether images where different within some threshold. Optionally, a difference image is returned that highlights the diff.

auto a = make_image(...), b = make_image(...); // init images
auto diff = image_difference(a, b);            // image difference
auto display = image_difference(a,nb, true);   // diff display

Procedural images

Yocto/Image defines several procedural images used for both testing and to quickly create textures for procedural scenes. Testing patterns take as input the desired image size, the pattern scale, and optionally two colors to interpolate between. Use make_grid(...) for a grid image, make_checker(...) for a checker, make_bumps(...) for a bumpy test, make_ramp(...) for a linear ramp, make_gammaramp(...) for a ramp with different gamma settings, make_uvramp(...) and make_uvgrid(...) for test images for texture coordinates as either ramps or grids, make_colormapramp(...) for a ramp to test different color maps, and make_blackbodyramp(...) for a ramp with different blackbody temperatures.

Perlin noise patterns can be generated with make_noisemap(...), make_fbmmap(...), make_fbmmap(...) and make_fbmmap(...). The latter three functions take as input the set of params that control fractal variations. See Yocto/Noise for a description.

auto w = 1024, h = 1024;                           // image size
auto scale = float{1};                             // pattern scale
auto c0 = vec4f{0,0,0,1}, c1 = vec4f{0,0,0,1};     // colors
auto i1 = make_grid(w, h, scale, c0, c1);          // grid image
auto i2 = make_checker(w, h, scale, c0, c1);       // checker image
auto i3 = make_bumps(w, h, scale, c0, c1);         // bumps image
auto i4 = make_ramp(w, h, scale, c0, c1);          // ramp image
auto i5 = make_gammaramp(w, h, scale, c0, c1);     // gamma ramp image
auto i6 = make_uvramp(w, h, scale);                // uv ramp image
auto i7 = make_uvgrid(w, h, scale);                // uv grid image
auto i8 = make_colormapramp(w, h, scale);          // color map image

auto t0 = 1000, t1 = 12000;                        // blackbody temperatures
auto b2 = make_blackbodyramp(w, h, scale, t0, t1); // blackbody ramp image

auto noise = vec4f{2, 0.5, 8, 1};                    // noise params
auto n1 = make_noisemap(w, h, scale, noise, c0, c1); // noise image
auto n2 = make_fbmmap(w, h, scale, noise, c0, c1);   // fbm image
auto n3 = make_turbulencemap(w, h, scale, noise, c0, c1);// turbulence image
auto n4 = make_ridgemap(w, h, scale, noise, c0, c1); // ridge image

Procedurals skies are generated with make_sunsky(img, w, h, elevation, turbidity, sun). The function returns a procedural HDR sky. The sun position is controlled by its elevation that is an angle in [0,pi/2\]. The sky turbidity is controlled by the turbidity parameter that is defined in the range [1.7,10]. The sun flag determines whether the sun disk is present in the image. The function support optional parameters to control sun size and intensity and ground albedo, mostly used for artistic effects.

auto sky = make_sunsky(1024, 512, pi/2, 3);        // clear sky
auto sun = make_sunsky(1024, 512, pi/2, 3, true);  // clear sky with sun
auto tur = make_sunsky(1024, 512, pi/2, 10);       // sky with turbidity

Use bump_to_normal(bumps) to convert a bump map to a normal map, with both images stored in a linear color spaces.

auto bumps = make_bumps(512, 512);    // procedural bump map
auto normal = bump_to_normal(bumps);  // convert bump map to normal map

Finally, borders can be added to images using add_border(img,width,color).

auto bordered = add_border(img, 1, {0, 0, 0, 1}); // add a thin black border

Low-level operations

Yocto/Image supports versions of the most of the above functions that work directly on pixel arrays, rather than the image structure. This low-level interface may be helpful when building applications that have their own image data structure.

In this interface we support arrays of vec4f pixels together with arrays of vec4b pixels for 8bit images, the latter with a much smaller number of functions. In this case, the color space is handled in the application logic. Conversion between color types is handled with float_to_byte(pixels), byte_tyo_float(pixels), rgb_to_srgb(pixels) and srgb_to_rgb(pixels).

Since most of these functions have signatures similar to the ones above, we do not document here explicitly.