/* tonemapping.c: tone mapping */ #include #include "tonemapping.h" #include "options.h" #include "error.h" #include "defaults.h" #include "cie.h" #include "mymath.h" #include "TONEMAP/dummy.h" #include "TONEMAP/lightness.h" #include "TONEMAP/trwf.h" /* available tone maps */ TONEMAP *AvailableToneMaps[] = { &TM_Lightness, &TM_TumblinRushmeier, &TM_Ward, &TM_RevisedTumblinRushmeier, &TM_Ferwerda, (TONEMAP *)NULL /* sentinel */ }; /* tone mapping context */ TONEMAPPINGCONTEXT tmopts; /* composes explanation for -tonemapping command line option */ static char tonemapping_methods_string[1000]; static void make_tonemapping_methods_string(void) { char *str = tonemapping_methods_string; int n, first = TRUE; sprintf(str, "\ -tonemapping : Set tone mapping method\n%n", &n); str += n; sprintf(str, "\tmethods: %n", &n); str += n; ForAllAvailableToneMaps(method) { if (!first) { sprintf(str, "\t %n", &n); str += n; } first = FALSE; sprintf(str, "%-20.20s %s%s\n%n", method->shortName, method->name, tmopts.ToneMap==method ? " (default)" : "", &n); str += n; } EndForAll; *(str-1) = '\0'; /* discard last newline character */ } static void ToneMappingMethodOption(void *value) { char *name = *(char **)value; ForAllAvailableToneMaps(method) { if (strncasecmp(name, method->shortName, method->abbrev) == 0) { SetToneMap(method); return; } } EndForAll; Error(NULL, "Invalid tone mapping method name '%s'", name); } /* tone mapping options */ static void BrightnessAdjustOption(void *val) { tmopts.pow_bright_adjust = pow(2., tmopts.brightness_adjust); } static float rxy[2], gxy[2], bxy[2], wxy[2]; static void ChromaOption(void *value) { float *chroma = (float *)value; if (chroma == rxy) { tmopts.xr = chroma[0]; tmopts.yr = chroma[1]; } else if (chroma == gxy) { tmopts.xg = chroma[0]; tmopts.yg = chroma[1]; } else if (chroma == bxy) { tmopts.xb = chroma[0]; tmopts.yb = chroma[1]; } else if (chroma == wxy) { tmopts.xw = chroma[0]; tmopts.yw = chroma[1]; } else { Fatal(-1, "ChromaOption", "invalid value pointer"); } ComputeColorConversionTransforms(tmopts.xr, tmopts.yr, tmopts.xg, tmopts.yg, tmopts.xb, tmopts.yb, tmopts.xw, tmopts.yw); } static void _tmAdaptMethodOption(void *value) { char *name = *(char **)value; if (strncasecmp(name, "average", 2) == 0) tmopts.statadapt = TMA_AVERAGE; else if (strncasecmp(name, "median", 2) == 0) tmopts.statadapt = TMA_MEDIAN; /* not yet supported else if (strncasecmp(name, "idrender", 2) == 0) tmopts.statadapt = TMA_IDRENDER; */ else { Error(NULL, "Invalid adaptation estimate method '%s'", name); } } static void GammaOption(void *value) { float gam = *(float *)value; RGBSET(tmopts.gamma, gam, gam, gam); } static CMDLINEOPTDESC tmOptions[] = { {"-tonemapping", 4, Tstring, NULL, ToneMappingMethodOption, tonemapping_methods_string }, {"-brightness-adjust", 4, Tfloat, &tmopts.brightness_adjust, BrightnessAdjustOption, "-brightness-adjust : brightness adjustment factor"}, {"-adapt", 5, Tstring, NULL, _tmAdaptMethodOption, "-adapt \t: adaptation estimation method\n" "\tmethods: \"average\", \"median\""}, {"-lwa", 3, Tfloat, &tmopts.lwa, DEFAULT_ACTION, "-lwa \t\t: real world adaptation luminance"}, {"-ldmax", 5, Tfloat, &tmopts.ldm, DEFAULT_ACTION, "-ldmax \t\t: maximum diaply luminance"}, {"-cmax", 4, Tfloat, &tmopts.cmax, DEFAULT_ACTION, "-cmax \t\t: maximum displayable contrast"}, {"-gamma", 4, Tfloat, NULL, GammaOption, "-gamma \t: gamma correction factor (same for red, green. blue)"}, {"-rgbgamma", 4, TRGB, &tmopts.gamma, DEFAULT_ACTION, "-rgbgamma \t: gamma correction factor (separate for red, green, blue)"}, {"-red", 4, Txy, rxy, ChromaOption, "-red \t: CIE xy chromaticity of monitor red"}, {"-green", 4, Txy, gxy, ChromaOption, "-green \t: CIE xy chromaticity of monitor green"}, {"-blue", 4, Txy, bxy, ChromaOption, "-blue \t: CIE xy chromaticity of monitor blue"}, {"-white", 4, Txy, wxy, ChromaOption, "-white \t: CIE xy chromaticity of monitor white"}, {NULL, 0, TYPELESS, NULL, DEFAULT_ACTION, NULL} }; /* tone mapping defaults */ void ToneMapDefaults(void) { ForAllAvailableToneMaps(map) { map->Defaults(); } EndForAll; tmopts.brightness_adjust = 0.; tmopts.pow_bright_adjust = pow(2., tmopts.brightness_adjust); tmopts.statadapt = TMA_MEDIAN; tmopts.lwa = DEFAULT_TM_LWA; tmopts.ldm = DEFAULT_TM_LDMAX; tmopts.cmax = DEFAULT_TM_CMAX; rxy[0] = tmopts.xr = 0.640; rxy[1] = tmopts.yr = 0.330; gxy[0] = tmopts.xg = 0.290; gxy[1] = tmopts.yg = 0.600; bxy[0] = tmopts.xb = 0.150; bxy[1] = tmopts.yb = 0.060; wxy[0] = tmopts.xw = 0.333333333333; wxy[1] = tmopts.yw = 0.333333333333; ComputeColorConversionTransforms(tmopts.xr, tmopts.yr, tmopts.xg, tmopts.yg, tmopts.xb, tmopts.yb, tmopts.xw, tmopts.yw); RGBSET(tmopts.gamma, DEFAULT_GAMMA, DEFAULT_GAMMA, DEFAULT_GAMMA); RecomputeGammaTables(tmopts.gamma); tmopts.display_test_image = FALSE; tmopts.testimg = 3; tmopts.ToneMap = &TM_Lightness; tmopts.ToneMap->Init(); make_tonemapping_methods_string(); } void ParseToneMapOptions(int *argc, char **argv) { ParseOptions(tmOptions, argc, argv); RecomputeGammaTables(tmopts.gamma); ForAllAvailableToneMaps(map) { if (map->ParseOptions) map->ParseOptions(argc, argv); } EndForAll; } void PrintToneMapOptions(FILE *fp) { fprintf(fp, "\nTone mapping options:\n"); PrintOptions(fp, tmOptions); ForAllAvailableToneMaps(map) { if (map->PrintOptions) map->PrintOptions(fp); } EndForAll; } /* makes map the current tone mapping operator + initialises */ void SetToneMap(TONEMAP *map) { tmopts.ToneMap->Terminate(); tmopts.ToneMap = map ? map : &TM_Dummy; tmopts.ToneMap->Init(); } /* initialises tone mapping, e.g. for a new scene */ void InitToneMapping(void) { InitSceneAdaptation(); SetToneMap(tmopts.ToneMap); } void RecomputeGammaTable(int index, double gamma) { int i; if (gamma <= EPSILON) gamma = 1.; for (i=0; i<=(1< 0.0) { float tmp = 0.3*spf; return pow(1.0+0.008/(spf*spf*spf),-0.2) * 1.42*spf*exp(-tmp) * sqrt(1.0+0.6*exp(tmp)); } return 0.0; } static float _ms_csf(float spf) { if (spf > 0.0) { float tmp = 0.114*spf; return 2.6*(0.0192+tmp)*exp(-pow(tmp,1.1)); } return 0.0; } float ContrastSensitivity(POINT *p1, POINT *p2) { VECTOR a1, a2; float sf; VECTORSUBTRACT(*p1, _cs_eye, a1); VECTORSUBTRACT(*p2, _cs_eye, a2); VECTORNORMALIZE(a1); VECTORNORMALIZE(a2); sf = M_PI/(360.0 * acos(VECTORDOTPRODUCT(a1,a2))); return _ms_csf(sf); } void ContrastSensitivityEye(POINT *p) { _cs_eye = *p; }