htpp

htrdr-image post-processing
git clone git://git.meso-star.fr/htpp.git
Log | Files | Refs | README | LICENSE

commit 4ad8cdc25b5449b000982fd4ca757b37dca634c1
parent bb81f0f60505bcf7902532537545ae56a6d72dbb
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri,  5 Jun 2020 11:49:32 +0200

Merge branch 'release_0.4'

Diffstat:
MREADME.md | 9+++++++++
Mcmake/CMakeLists.txt | 2+-
Mdoc/htpp.1.txt | 59+++++++++++++++++++++++++++++++++++------------------------
Msrc/htpp.c | 114++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 149 insertions(+), 35 deletions(-)

diff --git a/README.md b/README.md @@ -29,6 +29,15 @@ informations on CMake. ## Release notes +### Version 0.4 + +- Add the `gnuplot` parameter to the `-m` option. Once set, the result image is + written as a gnuplot script rather than a PPM image. This script generates a + PNG image with an embedded color ramp. +- Fix the tone map operator: the exposure term is now also applied to the white + radiance to ensure that it effectively corresponds to the white color in the + output image. + ### Version 0.3 - Add the `-i` option that regroups all the options controlling the diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -37,7 +37,7 @@ include_directories(${RSys_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 3) +set(VERSION_MINOR 4) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) diff --git a/doc/htpp.1.txt b/doc/htpp.1.txt @@ -30,9 +30,9 @@ htpp [_option_] ... [_input_] DESCRIPTION ----------- -*htpp* post-processes a *htrdr-image*(5) and converts it to a regular PPM -image [1]. If _input_ is not defined, the *htrdr-image*(5) is read from -standard input. Two post-processing procedures are provided: the +*htpp* post-processes a *htrdr-image*(5) and converts it to a PPM image [1] or +to a *gnuplot*(1) script. If _input_ is not defined, the *htrdr-image*(5) is +read from standard input. Two post-processing procedures are provided: the post-processing of the image colors (option *-i*), and the mapping of a given pixel component onto a color ramp (option "*-m*). By default, *htpp* post-processes the image color. @@ -42,7 +42,7 @@ the third and the fifth components of each *htrdr-image*(5) pixel encode a color with respect to the CIE 1931 XYZ color space. *htpp* first tone maps these colors with the following filmic tone mapping operator [2]: - out-color = f(in-color * exposure) / f(white-scale) + out-color = f(in-color * exposure) / f(white-value * exposure) with: @@ -57,11 +57,11 @@ with: The _exposure_ term is an user-defined parameter provided as the _exposure_ image option. If not defined, a default _exposure_ of 1 is used. The -_white-scale_ factor is either defined by the user through the _white_ image -option or automatically computed as the luminance that is such that 99.5% of -pixel radiances are less than _white-scale_. Once tone mapped, the pixels are +_white-value_ is either defined by the user through the _white_ image option +or automatically computed as the radiance that is such that 99.5% of pixel +radiances are less than _white-value. Once tone mapped, the pixels are transformed from the CIE 1931 XYZ color space into the sRGB linear color space -before being gamma corrected. Finally, the resulting pixel components are +before being gamma corrected. Finally, the resulting pixel components are clamped to [0, 1] and encoded on 8-bits. The mapping of a pixel component onto a color ramp is controlled by the *-m* @@ -90,9 +90,10 @@ OPTIONS **exposure**=__real__;; Pixel exposure. By default its value is 1. - **white**=__white-scale__;; - Factor used to normalize input colors. If not defined, the white scale is - automatically computed from the luminance of the _input_ image. + **white**=__radiance__;; + Radiance in the _input_ image that will represent the white color in + _output_. If not defined, the white value is automatically computed from + the radiance of the _input_ image. *-m* <__sub-option__>[:<__sub-option__> ...]:: Map a pixel component to a regular color. Available sub options are: @@ -114,9 +115,15 @@ OPTIONS selected pixel component over the whole image. This is the default comportment. + **gnuplot**;; + The _output_ image is formatted as a gnuplot script rather than a PPM + image. Once executed, the script generates a PNG image with an embedded + color ramp. Note that this script can be edited in order to adjust the + generated image to any requirements. + *-o* _output_:: - File where the PPM image is written. If not defined, write _output_ to - standard output. + File where the result is written. If not defined, write _output_ to standard + output. *-t* _threads-count_:: Hint on the number of threads to use. By default use as many threads as CPU @@ -124,13 +131,9 @@ OPTIONS *-v*:: Make *htpp* verbose. When used in post-processing of the pixel color (*-i* - option), this option displays the __white-scale__ factor used to normalize - the colors. In pixel component mapping mode (*-m* option), this option - displays the color ramp and its associated values. - -*-w _white-scale_*:: - Factor used to normalize input colors. If not defined, _white-scale_ is - automatically computed from the luminance of the _input_ image. + option), this option displays the radiance corresponding to the __white__ + color in the output image. In pixel component mapping mode (*-m* option), + this option displays the color ramp and its associated values. *--version*:: Display version information and exit. @@ -145,15 +148,22 @@ already exists: $ htpp -f -o img.ppm img.htrdr Convert *img.htrdr* and visualise the resulting image by redirecting the -standard output to the *feh*(1) image viewer. Use an _exposure_ of *0.5* and -explicitly define the normalization factor to *0.0025*: +standard output to the *feh*(1) image viewer. Use an _exposure_ of *0.2* and +explicitly define the white color to *31.2* W/sr/m^2: - $ htpp -i exposure=0.5:white=0.0025 img.htrdr | feh - + $ htpp -i exposure=0.2:white=31.2 img.htrdr | feh - Use the *-m* option to map the values of the second pixel component clamped in [0, 2] to the color ramp _magma_. - $ htpp -m pixcpnt=1:palette=magma:range=0 img.htrdr | feh - + $ htpp -v -m pixcpnt=1:palette=magma:range=0,2 img.htrdr | feh - + +Use the *-m* option to map the values of the sixth pixel component and write +it as a gnuplot script. Run *gnuplot*(1) to generate a PNG of the result and +visualise the resulting image with *feh*(1). + + $ htpp -m pixcpnt=6:gnuplot -o img.gp img.htrdr + $ gnuplot img.gp | feh - NOTES ----- @@ -174,4 +184,5 @@ it. There is NO WARRANTY, to the extent permitted by law. SEE ALSO -------- *feh*(1), +*gnuplot*(1) *htrdr-image*(5) diff --git a/src/htpp.c b/src/htpp.c @@ -68,6 +68,7 @@ struct args { const struct scmap_palette* palette; unsigned pixcpnt; /* In [0, PIXCPNTS_COUNT__[ */ double range[2]; + int gnuplot; } map; int verbose; @@ -86,6 +87,7 @@ struct args { &scmap_palette_inferno, /* Map palette */ \ 0, /* Map channel */ \ {DBL_MAX,-DBL_MAX}, /* Range */ \ + 0 /* Gnuplot */ \ }, \ 0, /* Verbosity level */ \ 0, /* Force overwrite? */ \ @@ -116,8 +118,8 @@ print_help(const char* cmd) printf( "Usage: %s [options] [image]\n" "Post process a htrdr-image(5) and convert the result in a regular PPM\n" -"image. If no image name is defined, read the image data from\n" -"standard input.\n", +"image or in a gnuplot script. If no image name is defined, read the \n" +"image data from standard input.\n", cmd); printf("\n"); printf( @@ -279,7 +281,8 @@ parse_map_option(struct args* args, const char* str) goto error; } args->map = ARGS_DEFAULT.map; - + } else if(!strcmp(key, "gnuplot")) { + args->map.gnuplot = 1; } else { if(!val) { fprintf(stderr, "Missing value to the map option `%s'.\n", key); @@ -491,7 +494,7 @@ filmic_tone_mapping const double F = 0.30; const double W = Ymax; #define TONE_MAP(X) ((((X)*(A*(X)+C*B)+D*E)/((X)*(A*(X)+B)+D*F))-E/F) - const double white_scale = TONE_MAP(W); + const double white_scale = TONE_MAP(W*exposure); ASSERT(pixel); pixel[PIXCPNT_X] = TONE_MAP(pixel[PIXCPNT_X]*exposure) / white_scale; @@ -683,6 +686,86 @@ error: goto exit; } +static res_T +img_write_gnuplot + (const struct img* img, + const struct args* args, + FILE* stream, + const char* stream_name) +{ + double cbox_width = 0.8; + double cbox_height = 0.08; + double cbox_tmargin = 0.02; + size_t icol; + size_t x, y; + res_T res = RES_OK; + ASSERT(img && args && stream && stream_name); + + #define CHKWR(FPrintf) { \ + const int i = FPrintf; \ + if(i < 0) { \ + fprintf(stderr, "%s: could not write the gnuplot map.\n", stream_name); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + CHKWR(fprintf(stream, "unset xtics\n")); + CHKWR(fprintf(stream, "unset ytics\n")); + CHKWR(fprintf(stream, "unset key\n")); + CHKWR(fprintf(stream, "unset colorbox\n")); + CHKWR(fprintf(stream, "unset origin\n")); + CHKWR(fprintf(stream, "unset border\n")); + CHKWR(fprintf(stream, "unset title\n")); + CHKWR(fprintf(stream, "unset margin\n")); + CHKWR(fprintf(stream, "set margins 0,0,0,0\n")); + CHKWR(fprintf(stream, "set xrange[0:%lu]\n", (unsigned long)img->width-1)); + CHKWR(fprintf(stream, "set yrange[%lu:0]\n", (unsigned long)img->height-1)); + if(args->map.range[0] < args->map.range[1]) { + CHKWR(fprintf(stream, "set cbrange[%g:%g]\n", + args->map.range[0], args->map.range[1])); + } + CHKWR(fprintf(stream, "set terminal png size %lu,%lu*(1+%g+%g)\n", + img->width, img->height, cbox_height, cbox_tmargin)); + CHKWR(fprintf(stream, "set origin 0, %g\n", (cbox_height+cbox_tmargin)*0.5)); + CHKWR(fprintf(stream, "set size ratio %g\n", + (double)img->height/(double)img->width)); + CHKWR(fprintf(stream, "set colorbox horiz user origin %g,%g size %g,%g\n", + (1.0-cbox_width)*0.5, cbox_height*0.5, cbox_width, cbox_height*0.5)); + + CHKWR(fprintf(stream, "set palette defined (\\\n")); + FOR_EACH(icol, 0, args->map.palette->ncolors) { + double col[3]; + args->map.palette->get_color(icol, col, args->map.palette->context); + CHKWR(fprintf(stream, " %lu %g %g %g", + (unsigned long)icol, col[0], col[1], col[2])); + if(icol < args->map.palette->ncolors-1) { + CHKWR(fprintf(stream, ",\\\n")); + } else { + CHKWR(fprintf(stream, ")\n")); + } + } + + CHKWR(fprintf(stream, "$map2 << EOD\n")); + FOR_EACH(y, 0, img->height) { + FOR_EACH(x, 0, img->width) { + double* row = (double*)(img->pixels + img->pitch*y); + double* pixel = row + x*PIXCPNTS_COUNT__; + CHKWR(fprintf(stream, "%lu %lu %g\n", y, x, pixel[args->map.pixcpnt])); + } + if(y != img->height-1) { + CHKWR(fprintf(stream, "\n")); + } + } + CHKWR(fprintf(stream, "EOD\n")); + CHKWR(fprintf(stream, "plot '$map2' using 2:1:3 with image pixels\n")); + #undef CHKWR + +exit: + return res; +error: + goto exit; +} + static double compute_XYZ_normalization_factor(const struct img* img) { @@ -915,14 +998,25 @@ main(int argc, char** argv) img_is_loaded = 1; switch(args.pp_type) { - case PP_IMAGE: res = pp_image(&img, &args); break; - case PP_MAP: res = pp_map(&img, &args); break; + case PP_IMAGE: + res = pp_image(&img, &args); + if(res != RES_OK) goto error; + res = img_write_ppm(&img, stream_out, stream_out_name); + if(res != RES_OK) goto error; + break; + case PP_MAP: + if(args.map.gnuplot) { + img_write_gnuplot(&img, &args, stream_out, stream_out_name); + if(res != RES_OK) goto error; + } else { + res = pp_map(&img, &args); + if(res != RES_OK) goto error; + res = img_write_ppm(&img, stream_out, stream_out_name); + if(res != RES_OK) goto error; + } + break; default: FATAL("Unreachable code.\n"); break; } - if(res != RES_OK) goto error; - - res = img_write_ppm(&img, stream_out, stream_out_name); - if(res != RES_OK) goto error; exit: if(stream_out && stream_out != stdout) fclose(stream_out);