htpp

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

commit fbe414e922dbab69858c489f8c6fdb82451b25ed
parent f217010d2574b20cf993cc4732f4b3d31d5707f4
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue,  2 Oct 2018 16:23:32 +0200

Full rewrite of the color correction process

Diffstat:
Msrc/htpp.c | 73+++++++++++++++++++++++++++++++------------------------------------------
1 file changed, 31 insertions(+), 42 deletions(-)

diff --git a/src/htpp.c b/src/htpp.c @@ -128,6 +128,16 @@ error: goto exit; } +static int +cmp_dbl(const void* a, const void* b) +{ + const double* dbl0 = a; + const double* dbl1 = b; + if(*dbl0 < *dbl1) return-1; + if(*dbl0 > *dbl1) return 1; + return 0; +} + static res_T open_output_stream(const char* path, const int force_overwrite, FILE** stream) { @@ -203,7 +213,7 @@ error: /* http://filmicworlds.com/blog/filmic-tonemapping-operators/ */ static double* -filmic_tone_mapping(double pixel[3]) +filmic_tone_mapping(double pixel[3], const double exposure, const double Ymax) { const double A = 0.15; const double B = 0.50; @@ -211,14 +221,14 @@ filmic_tone_mapping(double pixel[3]) const double D = 0.20; const double E = 0.02; const double F = 0.30; - const double W = 11.2; + 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); ASSERT(pixel); - pixel[0] = TONE_MAP(pixel[0]) * white_scale; - pixel[1] = TONE_MAP(pixel[1]) * white_scale; - pixel[2] = TONE_MAP(pixel[2]) * white_scale; + pixel[0] = TONE_MAP(pixel[0]) / white_scale * exposure; + pixel[1] = TONE_MAP(pixel[1]) / white_scale * exposure; + pixel[2] = TONE_MAP(pixel[2]) / white_scale * exposure; #undef TONE_MAP return pixel; } @@ -402,49 +412,33 @@ error: static double compute_img_normalization_factor(const struct img* img) { - size_t histo[16] = {0}; - const size_t histo_len = sizeof(histo) / sizeof(histo[0]); - double Yscale; + double* Y = NULL; double Ymax; - double Ydelta; - double rcp_npixels; size_t i, x, y; ASSERT(img); - /* Use nextarfter on the Luminance range to prevent a normalized luminance - * equal to 1, i.e. to prevent an histogram index equal to histo_len. */ - Yscale = 1.0 / nextafter(img->Yrange[1] - img->Yrange[0], DBL_MAX); + CHK(Y = mem_alloc(sizeof(*Y)*img->width*img->height)); - /* Build the histo */ + /* Copy the pixel luminance in the Y array */ + i = 0; FOR_EACH(y, 0, img->height) { const double* row = (double*)(img->pixels + y*img->pitch); FOR_EACH(x, 0, img->width) { const double* pixel = row + x*3; - const double Y = pixel[1]; - const double Ynormalized = (Y - img->Yrange[0]) * Yscale; - const size_t ientry = (size_t)((double)histo_len * Ynormalized); - ASSERT(ientry < histo_len); - ++histo[ientry]; + Y[i] = pixel[1]; + i++; } } - rcp_npixels = 1.0 / (double)(img->width * img->height); - - /* Find the first histogram entry whose cumulative contains N% of the pixels. - * N is defined empirically to 90% */ - FOR_EACH(i, 0, histo_len) { - if(i > 0) histo[i] += histo[i-1]; /* Compute the cumulative */ - if((double)histo[i] * rcp_npixels > 0.9) break; - } - - /* Define the width of an histogram entry */ - Ydelta = (img->Yrange[1] - img->Yrange[0]) / (double)histo_len; + /* Sort the Pixel luminance in ascending order */ + qsort(Y, img->width*img->height, sizeof(*Y), cmp_dbl); - /* Compute the upper bound luminance value of the found histogram entry */ - Ymax = img->Yrange[0] - + (double)(i+1/*Upper bound of the histogram entry*/)*Ydelta; + /* Arbitrarly use the luminance at 99.5% of the overal pixel at the maxium + * luminance of the image */ + i = (size_t)((double)(img->width*img->height)*0.995); + Ymax = Y[i]; - /* Use the previous value as the normalization factor */ + mem_rm(Y); return Ymax; } @@ -460,7 +454,7 @@ main(int argc, char** argv) const char* stream_in_name = "stdin"; struct img img = IMG_NULL; struct args args = ARGS_DEFAULT; - double Yscale; + double Ymax; const int nprocs = omp_get_num_procs(); int err = 0; int64_t i; @@ -484,7 +478,7 @@ main(int argc, char** argv) res = img_load(&img, args.uncertainties, stream_in, stream_in_name); if(res != RES_OK) goto error; - Yscale = args.exposure / compute_img_normalization_factor(&img); + Ymax = compute_img_normalization_factor(&img); omp_set_num_threads(nprocs); /* Convert input HDR XYZ image in RGB LDR image */ @@ -495,14 +489,9 @@ main(int argc, char** argv) double* row = (double*)(img.pixels + img.pitch*y); double* pixel = row + x*3; - /* Scale the pixel value */ - pixel[0] = pixel[0] * Yscale; - pixel[1] = pixel[1] * Yscale; - pixel[2] = pixel[2] * Yscale; - + filmic_tone_mapping(pixel, args.exposure, Ymax); /* Tone map the RGB pixel */ if(!args.uncertainties) { XYZ_to_RGB(pixel); /* Convert in RGB color space */ - filmic_tone_mapping(pixel); /* Tone map the RGB pixel */ RGB_to_sRGB(pixel); /* Convert in sRGB color space (i.e. gamma correction) */ } }