Task 094: Tonemap an HDR Image

Your task is to convert an HDR image to a displayable LDR format (8 bits per channel). You can either use a global technique, or a more advanced local one.



The template application 094tonemapping from the repository grcis serves as the basis of this project. This is a complete template application in which you only have to implement the method ToneMapping.ToneMap(). This method is responsible for calculating the output image. One can enter parameters for the tone mapping algorithm via a provided text form field.
The result is automatically displayed on the screen, and can be saved to disk in PNG format.

The template application contains several useful functions:

  • The left mouse button can be used to examine pixel values - the pixel value in the input file, and the HDR and LDR values will be displayed.
  • The parameter 'sub=K' scales the input HDR image to smaller values, with K as an integer reduction factor.
  • In the lower right corner of the window, there is a 'track-bar' to show image exposure. There the range of the HDR input is displayed (in a logarithmic scale on the basis of 2).
  • Pressing the button 'Tone-map' invokes your own tone mapping algorithm. For reasons of convenience, the algorithm is run in a separate thread, and it is possible to terminate it via the 'Stop' button.
  • Pressing the button 'Save image' does what one expects, and saves the current LDR result as PNG file.

Sample images to try your algorithm on can be found at the HDR image archive that is also linked from Pepca's HDR page. An overview of tone mapping algorithms can be found on the tone mapping algorithm page of Martin Cadik.

Technical details

ToneMapping.ToneMap() is the function in which you must implement your conversion algorithm that converts an HDR input parameter to an LDR result. The Bitmap result parameter can be a previously used instance of the Bitmap class if they are the same size and depth: this is more efficient than creating a completely new instance every time (see the pilot implementation).
The usual string param is used to transfer any additional information that is needed. The pilot implementation uses the comfortable value extraction functionality in Util.ParseKeyValueList() and the variation Util.TryParse(). The framework application works with two parameters gamma and sub, but you can add others as you wish and need.
In order to be able to terminate the computations it is necessary to frequently (e.g. once per scanline) check the global variable Form1.cont - if that is set to false, it means the computation should be abandoned.

Initialisation of parameters: in order to have all relevant code nicely grouped in a single location, you can initialise your application parameters in the function InitParams(). It is called during application start-up.

In the last parameter field out string name please write your full name (as well as in the comments on the first line of the source file). This will make it easier to perform automatic evaluations of the results.

Fast image buffer access

The project 094tonemapping uses a fast access technique to the contents of a raster image. The following code works with the contents of a Bitmap object (this is only for reading, writing would be analogous):

BitmapData dataOut = result.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb );
  byte* optr;
  int dO = Image.GetPixelFormatSize( PixelFormat.Format24bppRgb ) / 8;    // BGR

  for ( int y = 0; y < height; y++ )
    optr = (byte*)dataOut.Scan0 + y * dataOut.Stride;

    for ( int x = 0; x < width; x++, optr += dO )
      optr[ 0 ] = blue;
      optr[ 1 ] = green;
      optr[ 2 ] = red;
result.UnlockBits( dataOut );

The second example concerns an instance of FloatImage, in which all data of the n-Channel raster image stored in a long field float[] Data. Fast reading of the input HDR image pixel by pixel can be done like this:

fixed ( float* id = input.Data )
  float* iptr;

  for ( int y = 0; y < height; y++ )
    iptr = id + input.Scan0 + y * input.Stride;

    for ( int x = 0; x < width; x++, iptr += input.Channels )
      float R = iptr[ 0 ];
      float G = iptr[ 1 ];
      float B = iptr[ 2 ];
Properties of the class FloatImage that are useful for fast data access:
  • float[] Data - the actual pixel array. In a memory managed environment, it is necessary to use a fixed ( float* ptr = img.Data ) structure.
  • int Channels - number of channes in the image - how many float values each pixel has
  • int Scan0 - first pixel index (upper left is [0,0])
  • int Stride - the length of horizontal pixel lines (the number of float values) - by which index we have to jump forward in the image array to reach the next scanline.

What to hand in

As solution of the problem, you have to send us the file ToneMapping.cs (and only that).


Hand in the assignment until: 6. 12. 2016


Basic: 6 points, plus a potential bonus of 8 points.


Visual Studio project: 094tonemapping.

Source file

Modify and hand in the source file: ToneMapping.cs
As a comment in the first line, please include your name!

Copyright (C) 2016-2018 J. Pelikán & A. Wilkie, last change: 2019-05-09 17:52:59 +0200 (Thu, 09 May 2019)