From e7d90ec1a2e2f29a280341f02a3c6e31a9418485 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 29 Jan 2018 21:17:34 +0100 Subject: [PATCH 01/16] Improve accuracy of Y to L conversion, fixes #4338 --- rtengine/color.cc | 239 +++++++++++++++++++---------------- rtengine/color.h | 28 +++- rtengine/improcfun.cc | 5 +- rtengine/iplab2rgb.cc | 2 +- rtengine/ipvibrance.cc | 2 +- rtgui/histogrampanel.cc | 151 +--------------------- rtgui/histogrampanel.h | 3 +- rtgui/lockablecolorpicker.cc | 4 +- rtgui/navigator.cc | 4 +- 9 files changed, 163 insertions(+), 275 deletions(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index 68fd467e2..4a71d222b 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -34,6 +34,7 @@ extern const Settings* settings; cmsToneCurve* Color::linearGammaTRC; LUTf Color::cachef; +LUTf Color::cachefy; LUTf Color::gamma2curve; LUTf Color::gammatab; @@ -65,6 +66,9 @@ const double Color::eps_max = 580.40756; //(MAXVALF* 216.0f/24389.0); const double Color::eps = 216.0f / 24389.0; //0.008856 const double Color::kappa = 24389.0 / 27.0; //903.29630; +const float Color::epsf = eps; +const float Color::kappaf = kappa; + const float Color::D50x = 0.9642f; //0.96422; const float Color::D50z = 0.8249f; //0.82521; const double Color::u0 = 4.0 * D50x / (D50x + 15 + 3 * D50z); @@ -137,6 +141,7 @@ void Color::init () constexpr auto maxindex = 65536; cachef(maxindex, LUT_CLIP_BELOW); + cachefy(maxindex, LUT_CLIP_BELOW); gammatab(maxindex, 0); gammatabThumb(maxindex, 0); @@ -180,6 +185,23 @@ void Color::init () } #ifdef _OPENMP #pragma omp section +#endif + { + int i = 0; + int epsmaxint = eps_max; + + for (; i <= epsmaxint; i++) + { + cachefy[i] = 327.68 * (kappa * i / MAXVALF); + } + + for(; i < maxindex; i++) + { + cachefy[i] = 327.68 * (116.0 * std::cbrt((double)i / MAXVALF) - 16.0); + } + } +#ifdef _OPENMP + #pragma omp section #endif { for (int i = 0; i < maxindex; i++) @@ -360,150 +382,122 @@ void Color::cleanup () } } -void Color::rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b, bool workingSpace) -{ - double xyz_rgb[3][3]; - const double ep = 216.0 / 24389.0; - const double ka = 24389.0 / 27.0; +void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profileW, float r, float g, float b, float &LAB_l, float &LAB_a, float &LAB_b, bool workingSpace) +{ // do not use this function in a loop. It really eats processing time caused by Glib::ustring comparisons - double var_R = r / 65535.0; - double var_G = g / 65535.0; - double var_B = b / 65535.0; - - Glib::ustring profileCalc; - profileCalc = "sRGB"; //default - - if (workingSpace) { - profileCalc = profileW; //display working - } - - else {// if you want display = output space - if (profile == "RT_sRGB" || profile == "RT_sRGB_gBT709" || profile == "RT_sRGB_g10") { - profileCalc = "sRGB"; - } - - if (profile == "ProPhoto" || profile == "RT_Large_gBT709" || profile == "RT_Large_g10" || profile == "RT_Large_gsRGB") { - profileCalc = "ProPhoto"; - } - - if (profile == "AdobeRGB1998" || profile == "RT_Medium_gsRGB") { - profileCalc = "Adobe RGB"; - } - - if (profile == "WideGamutRGB") { - profileCalc = "WideGamut"; - } - } + Glib::ustring profileCalc = "sRGB"; //default if (workingSpace) {//display working + profileCalc = profileW; //display working if (profileW == "sRGB") { //apply sRGB inverse gamma - if ( var_R > 0.04045 ) { - var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + if (r > 0.04045f) { + r = pow_F(((r + 0.055f) / 1.055f), rtengine::Color::sRGBGammaCurve); } else { - var_R = var_R / 12.92; + r /= 12.92f; } - if ( var_G > 0.04045 ) { - var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + if (g > 0.04045f) { + g = pow_F(((g + 0.055f) / 1.055f), rtengine::Color::sRGBGammaCurve); } else { - var_G = var_G / 12.92; + g /= 12.92f; } - if ( var_B > 0.04045 ) { - var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + if (b > 0.04045f) { + b = pow_F(((b + 0.055f) / 1.055f), rtengine::Color::sRGBGammaCurve); } else { - var_B = var_B / 12.92; + b /= 12.92f; } } else if (profileW == "ProPhoto") { // apply inverse gamma 1.8 - var_R = pow ( var_R, 1.8); - var_G = pow ( var_G, 1.8); - var_B = pow ( var_B, 1.8); + r = pow_F(r, 1.8f); + g = pow_F(g, 1.8f); + b = pow_F(b, 1.8f); } else { // apply inverse gamma 2.2 - var_R = pow ( var_R, 2.2); - var_G = pow ( var_G, 2.2); - var_B = pow ( var_B, 2.2); + r = pow_F(r, 2.2f); + g = pow_F(g, 2.2f); + b = pow_F(b, 2.2f); + } + } else { //display output profile + if (profile == "RT_sRGB" || profile == "RT_sRGB_gBT709" || profile == "RT_sRGB_g10") { // use default "sRGB" + } else if (profile == "ProPhoto" || profile == "RT_Large_gBT709" || profile == "RT_Large_g10" || profile == "RT_Large_gsRGB") { + profileCalc = "ProPhoto"; + } else if (profile == "AdobeRGB1998" || profile == "RT_Medium_gsRGB") { + profileCalc = "Adobe RGB"; + } else if (profile == "WideGamutRGB") { + profileCalc = "WideGamut"; } - } else { //display outout profile if (profile == "RT_sRGB" || profile == "RT_Large_gsRGB" || profile == "RT_Medium_gsRGB") { //apply sRGB inverse gamma - if ( var_R > 0.04045 ) { - var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + if (r > 0.04045f) { + r = pow_F(((r + 0.055f) / 1.055f), rtengine::Color::sRGBGammaCurve); } else { - var_R = var_R / 12.92; + r /= 12.92f; } - if ( var_G > 0.04045 ) { - var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + if (g > 0.04045f) { + g = pow_F(((g + 0.055f) / 1.055f), rtengine::Color::sRGBGammaCurve); } else { - var_G = var_G / 12.92; + g /= 12.92f; } - if ( var_B > 0.04045 ) { - var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + if (b > 0.04045f) { + b = pow_F(((b + 0.055f) / 1.055f), rtengine::Color::sRGBGammaCurve); } else { - var_B = var_B / 12.92; + b /= 12.92f; } - } - - else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { // - if ( var_R > 0.0795 ) { - var_R = pow ( ( ( var_R + 0.0954 ) / 1.0954 ), 2.2); + } else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { // + if (r > 0.0795f) { + r = pow_F(((r + 0.0954f) / 1.0954f), 2.2f); } else { - var_R = var_R / 4.5; + r /= 4.5f; } - if ( var_G > 0.0795 ) { - var_G = pow ( ( ( var_G + 0.0954 ) / 1.0954 ), 2.2); + if (g > 0.0795f) { + g = pow_F(((g + 0.0954f) / 1.0954f), 2.2f); } else { - var_G = var_G / 4.5; + g /= 4.5f; } - if ( var_B > 0.0795 ) { - var_B = pow ( ( ( var_B + 0.0954 ) / 1.0954 ), 2.2); + if (b > 0.0795f) { + b = pow_F(((b + 0.0954f) / 1.0954f), 2.2f); } else { - var_B = var_B / 4.5; + b /= 4.5f; } } else if (profile == "ProPhoto") { // apply inverse gamma 1.8 - var_R = pow ( var_R, 1.8); - var_G = pow ( var_G, 1.8); - var_B = pow ( var_B, 1.8); - } else if (profile == "RT_sRGB_g10" || profile == "RT_Large_g10") { // apply inverse gamma 1.8 + r = pow_F(r, 1.8f); + g = pow_F(g, 1.8f); + b = pow_F(b, 1.8f); + } else if (profile == "RT_sRGB_g10" || profile == "RT_Large_g10") { // gamma 1.0, do nothing - var_R = pow ( var_R, 1.); - var_G = pow ( var_G, 1.); - var_B = pow ( var_B, 1.); - } + } else {// apply inverse gamma 2.2 - else {// apply inverse gamma 2.2 - var_R = pow ( var_R, 2.2); - var_G = pow ( var_G, 2.2); - var_B = pow ( var_B, 2.2); + r = pow_F(r, 2.2f); + g = pow_F(g, 2.2f); + b = pow_F(b, 2.2f); } } - // TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileW); + TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix(profileCalc); - TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileCalc); + float xyz_rgb[3][3]; for (int m = 0; m < 3; m++) for (int n = 0; n < 3; n++) { xyz_rgb[m][n] = wprof[m][n]; } - double varxx, varyy, varzz; - double var_X = ( xyz_rgb[0][0] * var_R + xyz_rgb[0][1] * var_G + xyz_rgb[0][2] * var_B ) / Color::D50x; - double var_Y = ( xyz_rgb[1][0] * var_R + xyz_rgb[1][1] * var_G + xyz_rgb[1][2] * var_B ) ; - double var_Z = ( xyz_rgb[2][0] * var_R + xyz_rgb[2][1] * var_G + xyz_rgb[2][2] * var_B ) / Color::D50z; + float var_X = (xyz_rgb[0][0] * r + xyz_rgb[0][1] * g + xyz_rgb[0][2] * b) / Color::D50x; + float var_Y = (xyz_rgb[1][0] * r + xyz_rgb[1][1] * g + xyz_rgb[1][2] * b); + float var_Z = (xyz_rgb[2][0] * r + xyz_rgb[2][1] * g + xyz_rgb[2][2] * b) / Color::D50z; - varxx = var_X > ep ? cbrt(var_X) : ( ka * var_X + 16.0) / 116.0 ; - varyy = var_Y > ep ? cbrt(var_Y) : ( ka * var_Y + 16.0) / 116.0 ; - varzz = var_Z > ep ? cbrt(var_Z) : ( ka * var_Z + 16.0) / 116.0 ; - LAB_l = ( 116 * varyy ) - 16; - LAB_a = 500 * ( varxx - varyy ); - LAB_b = 200 * ( varyy - varzz ); + float varxx = var_X > epsf ? xcbrtf(var_X) : (kappaf * var_X + 16.f) / 116.f ; + float varyy = var_Y > epsf ? xcbrtf(var_Y) : (kappaf * var_Y + 16.f) / 116.f ; + float varzz = var_Z > epsf ? xcbrtf(var_Z) : (kappaf * var_Z + 16.f) / 116.f ; + LAB_l = var_Y > epsf ? (xcbrtf(var_Y) * 116.f) - 16.f : kappaf * var_Y; + LAB_a = 500.f * ( varxx - varyy ); + LAB_b = 200.f * ( varyy - varzz ); } @@ -746,6 +740,37 @@ void Color::rgb2hsv(float r, float g, float b, float &h, float &s, float &v) } } +void Color::rgb2hsv01(float r, float g, float b, float &h, float &s, float &v) +{ + + const float var_Min = min(r, g, b); + const float var_Max = max(r, g, b); + const float del_Max = var_Max - var_Min; + + h = 0.f; + v = var_Max; + + if (del_Max < 0.00001f) { + s = 0.f; + } else { + s = del_Max / var_Max; + + if (r == var_Max) { + h = (g - b) / del_Max; + } else if (g == var_Max) { + h = 2.f + (b - r) / del_Max; + } else if (b == var_Max) { + h = 4.f + (r - g) / del_Max; + } + + h /= 6.f; + + if (h < 0.f) { + h += 1.f; + } + } +} + void Color::hsv2rgb (float h, float s, float v, float &r, float &g, float &b) { @@ -1683,7 +1708,7 @@ void Color::Lab2XYZ(float L, float a, float b, float &x, float &y, float &z) float LL = L / 327.68f; float aa = a / 327.68f; float bb = b / 327.68f; - float fy = (0.00862069f * LL) + 0.137932f; // (L+16)/116 + float fy = (c1By116 * LL) + c16By116; // (L+16)/116 float fx = (0.002f * aa) + fy; float fz = fy - (0.005f * bb); x = 65535.0f * f2xyz(fx) * D50x; @@ -1694,7 +1719,7 @@ void Color::Lab2XYZ(float L, float a, float b, float &x, float &y, float &z) void Color::L2XYZ(float L, float &x, float &y, float &z) // for black & white { float LL = L / 327.68f; - float fy = (0.00862069f * LL) + 0.137932f; // (L+16)/116 + float fy = (c1By116 * LL) + c16By116; // (L+16)/116 float fxz = 65535.f * f2xyz(fy); x = fxz * D50x; z = fxz * D50z; @@ -1709,7 +1734,7 @@ void Color::Lab2XYZ(vfloat L, vfloat a, vfloat b, vfloat &x, vfloat &y, vfloat & L /= c327d68; a /= c327d68; b /= c327d68; - vfloat fy = F2V(0.00862069f) * L + F2V(0.137932f); + vfloat fy = F2V(c1By116) * L + F2V(c16By116); vfloat fx = F2V(0.002f) * a + fy; vfloat fz = fy - (F2V(0.005f) * b); vfloat c65535 = F2V(65535.f); @@ -1727,8 +1752,6 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, #ifdef __SSE2__ vfloat maxvalfv = F2V(MAXVALF); - vfloat c116v = F2V(116.f); - vfloat c5242d88v = F2V(5242.88f); vfloat c500v = F2V(500.f); vfloat c200v = F2V(200.f); #endif @@ -1753,7 +1776,7 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, float fy = (y <= 65535.f ? cachef[y] : (327.68f * xcbrtf(y / MAXVALF))); float fz = (z <= 65535.f ? cachef[z] : (327.68f * xcbrtf(z / MAXVALF))); - L[i + k] = (116.f * fy - 5242.88f); //5242.88=16.0*327.68; + L[i + k] = (y <= 65535.0f ? cachefy[y] : 327.68f * (116.f * xcbrtf(y / MAXVALF) - 16.f)); a[i + k] = (500.f * (fx - fy) ); b[i + k] = (200.f * (fy - fz) ); } @@ -1762,7 +1785,7 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, const vfloat fy = cachef[yv]; const vfloat fz = cachef[zv]; - STVFU(L[i], c116v * fy - c5242d88v); //5242.88=16.0*327.68; + STVFU(L[i], cachefy[yv]); STVFU(a[i], c500v * (fx - fy)); STVFU(b[i], c200v * (fy - fz)); } @@ -1781,7 +1804,7 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, fy = (y <= 65535.0f ? cachef[y] : (327.68f * xcbrtf(y / MAXVALF))); fz = (z <= 65535.0f ? cachef[z] : (327.68f * xcbrtf(z / MAXVALF))); - L[i] = 116.0f * fy - 5242.88f; //5242.88=16.0*327.68; + L[i] = (y <= 65535.0f ? cachefy[y] : 327.68f * (116.f * xcbrtf(y / MAXVALF) - 16.f)); a[i] = 500.0f * (fx - fy); b[i] = 200.0f * (fy - fz); } @@ -1799,14 +1822,14 @@ void Color::XYZ2Lab(float X, float Y, float Z, float &L, float &a, float &b) fy = (y <= 65535.0f ? cachef[y] : (327.68f * xcbrtf(y / MAXVALF))); fz = (z <= 65535.0f ? cachef[z] : (327.68f * xcbrtf(z / MAXVALF))); - L = (116.0f * fy - 5242.88f); //5242.88=16.0*327.68; + L = (y <= 65535.0f ? cachefy[y] : 327.68f * (116.f * xcbrtf(y / MAXVALF) - 16.f)); a = (500.0f * (fx - fy) ); b = (200.0f * (fy - fz) ); } void Color::Lab2Yuv(float L, float a, float b, float &Y, float &u, float &v) { - float fy = (0.00862069 * L / 327.68) + 0.137932; // (L+16)/116 + float fy = (c1By116 * L / 327.68) + c16By116; // (L+16)/116 float fx = (0.002 * a / 327.68) + fy; float fz = fy - (0.005 * b / 327.68); float LL = L / 327.68; @@ -1835,7 +1858,7 @@ void Color::Yuv2Lab(float Yin, float u, float v, float &L, float &a, float &b, c float fy = (Y <= 65535.0 ? cachef[Y] : (327.68 * std::cbrt(Y / MAXVALF))); float fz = (Z <= 65535.0 ? cachef[Z] : (327.68 * std::cbrt(Z / MAXVALF))); - L = (116.0 * fy - 5242.88); //5242.88=16.0*327.68; + L = (Y <= 65535.0f ? cachefy[Y] : 327.68f * (116.f * xcbrtf(Y / MAXVALF) - 16.f)); a = (500.0 * (fx - fy) ); b = (200.0 * (fy - fz) ); } @@ -2304,7 +2327,7 @@ void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, flo float bprov1 = Chprov1 * sincosval.x; //conversion Lab RGB to limit Lab values - this conversion is useful before Munsell correction - float fy = (0.00862069f * Lprov1 ) + 0.137932f; + float fy = (c1By116 * Lprov1 ) + c16By116; float fx = (0.002f * aprov1) + fy; float fz = fy - (0.005f * bprov1); @@ -2424,7 +2447,7 @@ void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chpr float bprov1 = Chprov1 * sincosval.x; //conversion Lab RGB to limit Lab values - this conversion is useful before Munsell correction - float fy = (0.00862069f * Lprov1 ) + 0.137932f; + float fy = (c1By116 * Lprov1 ) + c16By116; float fx = (0.002f * aprov1) + fy; float fz = fy - (0.005f * bprov1); @@ -2533,7 +2556,7 @@ void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float bprov1 = Chprov1 * sincosval.x; //conversion Lab RGB to limit Lab values - this conversion is useful before Munsell correction - float fy = (0.00862069f * Lprov1 ) + 0.137932f; + float fy = (c1By116 * Lprov1 ) + c16By116; float fx = (0.002f * aprov1) + fy; float fz = fy - (0.005f * bprov1); diff --git a/rtengine/color.h b/rtengine/color.h index d56b30e52..06b9dceb8 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -123,12 +123,15 @@ public: const static double sRGBGamma; // standard average gamma const static double sRGBGammaCurve; // 2.4 in the curve const static double eps, eps_max, kappa, epskap; + const static float epsf, kappaf; const static float D50x, D50z; const static double u0, v0; - + constexpr static float c1By116 = 0.00862068965517241379310344827586f; // 1/116 + constexpr static float c16By116 = 0.13793103448275862068965517241379f; // 16/116 static cmsToneCurve* linearGammaTRC; static LUTf cachef; + static LUTf cachefy; static LUTf gamma2curve; // look-up tables for the standard srgb gamma and its inverse (filled by init()) @@ -184,16 +187,16 @@ public: * @brief Convert red/green/blue to hue/saturation/luminance * @param profile output profile name * @param profileW working profile name - * @param r red channel [0 ; 65535] - * @param g green channel [0 ; 65535] - * @param b blue channel [0 ; 65535] + * @param r red channel [0 ; 1] + * @param g green channel [0 ; 1] + * @param b blue channel [0 ; 1] * @param L Lab L channel [0 ; 1] (return value) - * @param a Lab a channel [0 ; 1] (return value) + * @param a Lab a channel [0 ; 1] (return value) * @param b Lab b channel [0; 1] (return value) * @param workingSpace true: compute the Lab value using the Working color space ; false: use the Output color space */ - static void rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b, bool workingSpace); - + // do not use this function in a loop. It really eats processing time caused by Glib::ustring comparisons + static void rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profileW, float r, float g, float b, float &LAB_l, float &LAB_a, float &LAB_b, bool workingSpace); /** * @brief Convert red/green/blue to hue/saturation/luminance @@ -327,6 +330,17 @@ public: */ static void rgb2hsv (float r, float g, float b, float &h, float &s, float &v); + /** + * @brief Convert red green blue to hue saturation value + * @param r red channel [0 ; 1] + * @param g green channel [0 ; 1] + * @param b blue channel [0 ; 1] + * @param h hue channel [0 ; 1] (return value) + * @param s saturation channel [0 ; 1] (return value) + * @param v value channel [0 ; 1] (return value) + */ + static void rgb2hsv01 (float r, float g, float b, float &h, float &s, float &v); + static inline float rgb2s(float r, float g, float b) // fast version if only saturation is needed { float var_Min = min(r, g, b); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index a76d4cd84..1a545bdf5 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -3877,8 +3877,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer // Luminosity after // only Luminance in Lab float newy = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b; - float newfy = newy < MAXVALF ? Color::cachef[newy] : 327.68f * std::cbrt (newy / MAXVALF); - float L_2 = 116.0f * newfy - 5242.88f; + float L_2 = newy <= MAXVALF ? Color::cachefy[newy] : 327.68f * (116.f * xcbrtf(newy / MAXVALF) - 16.f); //gamut control if (settings->rgbcurveslumamode_gamut) { @@ -5775,7 +5774,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW float aprov1 = Chprov2 * sincosval.y; float bprov1 = Chprov2 * sincosval.x; - float fy = (0.00862069f * Lprov1 ) + 0.137932f; + float fy = (Color::c1By116 * Lprov1 ) + Color::c1By116; float fx = (0.002f * aprov1) + fy; float fz = fy - (0.005f * bprov1); diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index ba2fe6ffb..853b8ae53 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -318,7 +318,7 @@ Imagefloat* ImProcFunctions::lab2rgbOut (LabImage* lab, int cx, int cy, int cw, for (int j = cx; j < cx + cw; j++) { - float fy = (0.0086206897f * rL[j]) / 327.68f + 0.1379310345f; // (L+16)/116 + float fy = (Color::c1By116 * rL[j]) / 327.68f + Color::c16By116; // (L+16)/116 float fx = (0.002f * ra[j]) / 327.68f + fy; float fz = fy - (0.005f * rb[j]) / 327.68f; float LL = rL[j] / 327.68f; diff --git a/rtengine/ipvibrance.cc b/rtengine/ipvibrance.cc index f17d5d9b1..d7fb0da97 100644 --- a/rtengine/ipvibrance.cc +++ b/rtengine/ipvibrance.cc @@ -700,7 +700,7 @@ void ImProcFunctions::vibrance (LabImage* lab) aprovn = Chprov * sincosval.y; bprovn = Chprov * sincosval.x; - float fyy = (0.00862069f * Lprov ) + 0.137932f; + float fyy = (Color::c1By116 * Lprov ) + Color::c16By116; float fxx = (0.002f * aprovn) + fyy; float fzz = fyy - (0.005f * bprovn); float xx_ = 65535.f * Color::f2xyz (fxx) * Color::D50x; diff --git a/rtgui/histogrampanel.cc b/rtgui/histogrampanel.cc index 51e288951..c713b51a8 100644 --- a/rtgui/histogrampanel.cc +++ b/rtgui/histogrampanel.cc @@ -476,7 +476,7 @@ void HistogramRGBArea::updateFreeze (bool f) return; } -void HistogramRGBArea::updateBackBuffer (int r, int g, int b, Glib::ustring profile, Glib::ustring profileW) +void HistogramRGBArea::updateBackBuffer (int r, int g, int b, const Glib::ustring &profile, const Glib::ustring &profileW) { if (!get_realized () || frozen || !showMode) { return; @@ -531,7 +531,7 @@ void HistogramRGBArea::updateBackBuffer (int r, int g, int b, Glib::ustring prof if(needLuma || needChroma) { float Lab_L, Lab_a, Lab_b; - rgb2lab( profile, profileW, r, g, b, Lab_L, Lab_a, Lab_b); + rtengine::Color::rgb2lab01(profile, profileW, r / 255.f, g / 255.f, b / 255.f, Lab_L, Lab_a, Lab_b, options.rtSettings.HistogramWorking); if (needLuma) { // Luma @@ -557,153 +557,6 @@ void HistogramRGBArea::updateBackBuffer (int r, int g, int b, Glib::ustring prof setDirty(false); } -void HistogramRGBArea::rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b) -{ - double xyz_rgb[3][3]; - const double ep = 216.0 / 24389.0; - const double ka = 24389.0 / 27.0; - - double var_R = r / 255.0; - double var_G = g / 255.0; - double var_B = b / 255.0; - - Glib::ustring profileCalc; - profileCalc = "sRGB"; //default - - if(options.rtSettings.HistogramWorking) { - profileCalc = profileW; //display working - } - - else {// if you want display = output space - if (profile == "RT_sRGB" || profile == "RT_sRGB_gBT709" || profile == "RT_sRGB_g10") { - profileCalc = "sRGB"; - } - - if (profile == "ProPhoto" || profile == "RT_Large_gBT709" || profile == "RT_Large_g10" || profile == "RT_Large_gsRGB") { - profileCalc = "ProPhoto"; - } - - if (profile == "AdobeRGB1998" || profile == "RT_Medium_gsRGB") { - profileCalc = "Adobe RGB"; - } - - if (profile == "WideGamutRGB") { - profileCalc = "WideGamut"; - } - } - - if(options.rtSettings.HistogramWorking) {//display working - if (profileW == "sRGB") { //apply sRGB inverse gamma - - if ( var_R > 0.04045 ) { - var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_R = var_R / 12.92; - } - - if ( var_G > 0.04045 ) { - var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_G = var_G / 12.92; - } - - if ( var_B > 0.04045 ) { - var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_B = var_B / 12.92; - } - } else if (profileW == "ProPhoto") { // apply inverse gamma 1.8 - var_R = pow ( var_R, 1.8); - var_G = pow ( var_G, 1.8); - var_B = pow ( var_B, 1.8); - } else { // apply inverse gamma 2.2 - var_R = pow ( var_R, 2.2); - var_G = pow ( var_G, 2.2); - var_B = pow ( var_B, 2.2); - } - } else { //display outout profile - - if (profile == "RT_sRGB" || profile == "RT_Large_gsRGB" || profile == "RT_Medium_gsRGB") { //apply sRGB inverse gamma - if ( var_R > 0.04045 ) { - var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_R = var_R / 12.92; - } - - if ( var_G > 0.04045 ) { - var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_G = var_G / 12.92; - } - - if ( var_B > 0.04045 ) { - var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_B = var_B / 12.92; - } - } - - else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { // - if ( var_R > 0.0795 ) { - var_R = pow ( ( ( var_R + 0.0954 ) / 1.0954 ), 2.2); - } else { - var_R = var_R / 4.5; - } - - if ( var_G > 0.0795 ) { - var_G = pow ( ( ( var_G + 0.0954 ) / 1.0954 ), 2.2); - } else { - var_G = var_G / 4.5; - } - - if ( var_B > 0.0795 ) { - var_B = pow ( ( ( var_B + 0.0954 ) / 1.0954 ), 2.2); - } else { - var_B = var_B / 4.5; - } - - } else if (profile == "ProPhoto") { // apply inverse gamma 1.8 - - var_R = pow ( var_R, 1.8); - var_G = pow ( var_G, 1.8); - var_B = pow ( var_B, 1.8); - } else if (profile == "RT_sRGB_g10" || profile == "RT_Large_g10") { // apply inverse gamma 1.8 - - var_R = pow ( var_R, 1.); - var_G = pow ( var_G, 1.); - var_B = pow ( var_B, 1.); - } - - else {// apply inverse gamma 2.2 - var_R = pow ( var_R, 2.2); - var_G = pow ( var_G, 2.2); - var_B = pow ( var_B, 2.2); - } - } - - // TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileW); - - TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileCalc); - - for (int m = 0; m < 3; m++) - for (int n = 0; n < 3; n++) { - xyz_rgb[m][n] = wprof[m][n]; - } - - double varxx, varyy, varzz; - double var_X = ( xyz_rgb[0][0] * var_R + xyz_rgb[0][1] * var_G + xyz_rgb[0][2] * var_B ) / Color::D50x; - double var_Y = ( xyz_rgb[1][0] * var_R + xyz_rgb[1][1] * var_G + xyz_rgb[1][2] * var_B ) ; - double var_Z = ( xyz_rgb[2][0] * var_R + xyz_rgb[2][1] * var_G + xyz_rgb[2][2] * var_B ) / Color::D50z; - - varxx = var_X > ep ? cbrt(var_X) : ( ka * var_X + 16.0) / 116.0 ; - varyy = var_Y > ep ? cbrt(var_Y) : ( ka * var_Y + 16.0) / 116.0 ; - varzz = var_Z > ep ? cbrt(var_Z) : ( ka * var_Z + 16.0) / 116.0 ; - LAB_l = ( 116 * varyy ) - 16; - LAB_a = 500 * ( varxx - varyy ); - LAB_b = 200 * ( varyy - varzz ); - -} - void HistogramRGBArea::update (int valh, int rh, int gh, int bh) { diff --git a/rtgui/histogrampanel.h b/rtgui/histogrampanel.h index 25fa15c23..0ce62956c 100644 --- a/rtgui/histogrampanel.h +++ b/rtgui/histogrampanel.h @@ -76,7 +76,7 @@ public: HistogramRGBArea(); ~HistogramRGBArea(); - void updateBackBuffer (int r, int g, int b, Glib::ustring profile = "", Glib::ustring profileW = ""); + void updateBackBuffer (int r, int g, int b, const Glib::ustring &profile = "", const Glib::ustring &profileW = ""); void updateFreeze (bool f); bool getFreeze (); bool getShow (); @@ -92,7 +92,6 @@ public: bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr); bool on_button_press_event (GdkEventButton* event); private: - void rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b); Gtk::SizeRequestMode get_request_mode_vfunc () const; void get_preferred_height_vfunc (int& minimum_height, int& natural_height) const; void get_preferred_width_vfunc (int &minimum_width, int &natural_width) const; diff --git a/rtgui/lockablecolorpicker.cc b/rtgui/lockablecolorpicker.cc index d10dfb438..859057e2c 100644 --- a/rtgui/lockablecolorpicker.cc +++ b/rtgui/lockablecolorpicker.cc @@ -277,8 +277,8 @@ void LockableColorPicker::setRGB (const float R, const float G, const float B, c gpreview = previewG; bpreview = previewB; - rtengine::Color::rgb2hsv(r*65535.f, g*65535.f, b*65535.f, hue, sat, val); - rtengine::Color::rgb2lab (*outputProfile, *workingProfile, r * 65535.f, g * 65535.f, b * 65535.f, L, a, bb, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? + rtengine::Color::rgb2hsv01(r, g, b, hue, sat, val); + rtengine::Color::rgb2lab01(*outputProfile, *workingProfile, r, g, b, L, a, bb, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? if (validity != Validity::OUTSIDE) { setDirty(true); diff --git a/rtgui/navigator.cc b/rtgui/navigator.cc index 7222436b6..7f159861d 100644 --- a/rtgui/navigator.cc +++ b/rtgui/navigator.cc @@ -290,13 +290,13 @@ void Navigator::pointerMoved (bool validPos, Glib::ustring profile, Glib::ustrin G->set_text (s2); B->set_text (s3); - Color::rgb2hsv (r * 0xffff / 0xff, g * 0xffff / 0xff, b * 0xffff / 0xff, h, s, v); + Color::rgb2hsv01(r / 255.f, g / 255.f, b / 255.f, h, s, v); getHSVText (h, s, v, s1, s2, s3); H->set_text (s1); S->set_text (s2); V->set_text (s3); - Color::rgb2lab (profile, profileW, r * 0xffff / 0xff, g * 0xffff / 0xff, b * 0xffff / 0xff, LAB_l, LAB_a, LAB_b, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? + Color::rgb2lab01(profile, profileW, r / 255.f, g / 255.f, b / 255.f, LAB_l, LAB_a, LAB_b, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? getLABText (LAB_l, LAB_a, LAB_b, s1, s2, s3); LAB_L->set_text (s1); LAB_A->set_text (s2); From 8af91ce74c971185e2b7e84060158625dd38dba0 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 30 Jan 2018 10:06:20 +0100 Subject: [PATCH 02/16] faster histogram matching by using the processing pipeline in the thumbnail --- rtengine/histmatching.cc | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index 56fe21d80..1870d3180 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -208,26 +208,35 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) PreviewProps pp(cx, cy, fw, fh, skip); ColorTemp currWB = getWB(); - std::unique_ptr image(new Imagefloat(int(fw / skip), int(fh / skip))); { - RawImageSource rsrc; - rsrc.load(getFileName()); - rsrc.preprocess(neutral.raw, neutral.lensProf, neutral.coarse, false); - rsrc.demosaic(neutral.raw); - rsrc.getImage(currWB, TR_NONE, image.get(), pp, neutral.toneCurve, neutral.raw); + RawMetaDataLocation rml; + eSensorType sensor_type; + double scale; + int w = fw / skip, h = fh / skip; + std::unique_ptr thumb(Thumbnail::loadFromRaw(getFileName(), rml, sensor_type, w, h, 1, false, false)); + target.reset(thumb->processImage(neutral, sensor_type, fh / skip, TI_Nearest, getMetaData(), scale, false)); } + + // std::unique_ptr image(new Imagefloat(int(fw / skip), int(fh / skip))); + // { + // RawImageSource rsrc; + // rsrc.load(getFileName()); + // rsrc.preprocess(neutral.raw, neutral.lensProf, neutral.coarse, false); + // rsrc.demosaic(neutral.raw); + // rsrc.getImage(currWB, TR_NONE, image.get(), pp, neutral.toneCurve, neutral.raw); + // } // this could probably be made faster -- ideally we would need to just // perform the transformation from camera space to the output space // (taking gamma into account), but I couldn't find anything // ready-made, so for now this will do. Remember the famous quote: // "premature optimization is the root of all evil" :-) - convertColorSpace(image.get(), neutral.icm, currWB); - ImProcFunctions ipf(&neutral); - LabImage tmplab(image->getWidth(), image->getHeight()); - ipf.rgb2lab(*image, tmplab, neutral.icm.working); - image.reset(ipf.lab2rgbOut(&tmplab, 0, 0, tmplab.W, tmplab.H, neutral.icm)); - target.reset(image->to8()); + // convertColorSpace(image.get(), neutral.icm, currWB); + // ImProcFunctions ipf(&neutral); + // LabImage tmplab(image->getWidth(), image->getHeight()); + // ipf.rgb2lab(*image, tmplab, neutral.icm.working); + // image.reset(ipf.lab2rgbOut(&tmplab, 0, 0, tmplab.W, tmplab.H, neutral.icm)); + // target.reset(image->to8()); if (settings->verbose) { std::cout << "histogram matching: generated neutral rendering" << std::endl; From 71a3af0c86a50e78cffce57be653a7d6d3c972c8 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 30 Jan 2018 10:06:57 +0100 Subject: [PATCH 03/16] improve generated curve in histogram matching on underexposed pictures --- rtengine/histmatching.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index 1870d3180..6728e70ee 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -120,6 +120,15 @@ void mappingToCurve(const std::vector &mapping, std::vector &curve) curve.pop_back(); curve.pop_back(); } + // remove the plateau at the end (if any) + size_t j = curve.size() / 2; + for (; j > 1; --j) { + if (std::abs(curve[2*(j-1)-1] - curve.back()) > 0.01) { + break; + } + } + curve.resize(2*j); + curve.push_back(1.0); curve.push_back(1.0); From 5effd15bb3663363c1498247d8436d746975d74e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 30 Jan 2018 10:24:19 +0100 Subject: [PATCH 04/16] histmatching: fix cropping of the target (broken by recent refactoring) --- rtengine/histmatching.cc | 67 +++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index 6728e70ee..9128aa75e 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -194,58 +194,47 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) std::unique_ptr target; { - int tw = source->getWidth(), th = source->getHeight(); - float thumb_ratio = float(std::max(tw, th)) / float(std::min(tw, th)); - float target_ratio = float(std::max(fw, fh)) / float(std::min(fw, fh)); + RawMetaDataLocation rml; + eSensorType sensor_type; + double scale; + int w = fw / skip, h = fh / skip; + std::unique_ptr thumb(Thumbnail::loadFromRaw(getFileName(), rml, sensor_type, w, h, 1, false, false)); + target.reset(thumb->processImage(neutral, sensor_type, fh / skip, TI_Nearest, getMetaData(), scale, false)); + + int sw = source->getWidth(), sh = source->getHeight(); + int tw = target->getWidth(), th = target->getHeight(); + float thumb_ratio = float(std::max(sw, sh)) / float(std::min(sw, sh)); + float target_ratio = float(std::max(tw, th)) / float(std::min(tw, th)); int cx = 0, cy = 0; if (std::abs(thumb_ratio - target_ratio) > 0.01) { if (thumb_ratio > target_ratio) { // crop the height - int ch = fh - (fw * float(th) / float(tw)); + int ch = th - (tw * float(sh) / float(sw)); cy += ch / 2; - fh -= ch; + th -= ch; } else { // crop the width - int cw = fw - (fh * float(tw) / float(th)); + int cw = tw - (th * float(sw) / float(sh)); cx += cw / 2; - fw -= cw; + tw -= cw; } if (settings->verbose) { - std::cout << "histogram matching: cropping target to get an aspect ratio of " << std::fixed << std::setprecision(2) << thumb_ratio << ":1, new full size is " << fw << "x" << fh << std::endl; + std::cout << "histogram matching: cropping target to get an aspect ratio of " << std::fixed << std::setprecision(2) << thumb_ratio << ":1, new size is " << tw << "x" << th << std::endl; } - } - PreviewProps pp(cx, cy, fw, fh, skip); - ColorTemp currWB = getWB(); - { - RawMetaDataLocation rml; - eSensorType sensor_type; - double scale; - int w = fw / skip, h = fh / skip; - std::unique_ptr thumb(Thumbnail::loadFromRaw(getFileName(), rml, sensor_type, w, h, 1, false, false)); - target.reset(thumb->processImage(neutral, sensor_type, fh / skip, TI_Nearest, getMetaData(), scale, false)); + Image8 *tmp = new Image8(tw, th); +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int y = 0; y < th; ++y) { + for (int x = 0; x < tw; ++x) { + tmp->r(y, x) = target->r(y+cy, x+cx); + tmp->g(y, x) = target->g(y+cy, x+cx); + tmp->b(y, x) = target->b(y+cy, x+cx); + } + } + target.reset(tmp); } - - // std::unique_ptr image(new Imagefloat(int(fw / skip), int(fh / skip))); - // { - // RawImageSource rsrc; - // rsrc.load(getFileName()); - // rsrc.preprocess(neutral.raw, neutral.lensProf, neutral.coarse, false); - // rsrc.demosaic(neutral.raw); - // rsrc.getImage(currWB, TR_NONE, image.get(), pp, neutral.toneCurve, neutral.raw); - // } - - // this could probably be made faster -- ideally we would need to just - // perform the transformation from camera space to the output space - // (taking gamma into account), but I couldn't find anything - // ready-made, so for now this will do. Remember the famous quote: - // "premature optimization is the root of all evil" :-) - // convertColorSpace(image.get(), neutral.icm, currWB); - // ImProcFunctions ipf(&neutral); - // LabImage tmplab(image->getWidth(), image->getHeight()); - // ipf.rgb2lab(*image, tmplab, neutral.icm.working); - // image.reset(ipf.lab2rgbOut(&tmplab, 0, 0, tmplab.W, tmplab.H, neutral.icm)); - // target.reset(image->to8()); if (settings->verbose) { std::cout << "histogram matching: generated neutral rendering" << std::endl; From ffbd97960f3f2eb4903cb45771dfad4dc19e7dd4 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 30 Jan 2018 10:47:18 +0100 Subject: [PATCH 05/16] histmatching: make sure we use an sRGB profile for generating the target rendering --- rtengine/histmatching.cc | 1 + rtengine/improcfun.h | 2 +- rtengine/iplab2rgb.cc | 4 ++-- rtengine/rtthumbnail.cc | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index 9128aa75e..87eb672c8 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -171,6 +171,7 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) ProcParams neutral; neutral.raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST); neutral.raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST); + neutral.icm.output = "sRGB"; std::unique_ptr source; { diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 83ffc12d6..8204516fd 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -348,7 +348,7 @@ public: void localContrast(LabImage *lab); void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); - Image8* lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm); + Image8* lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool consider_histogram_settings=true); Imagefloat* lab2rgbOut (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga = nullptr); // CieImage *ciec; diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index ba2fe6ffb..27d9982fb 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -122,7 +122,7 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) // // If output profile used, divide by 327.68 then apply the "profile" profile (eventually with a standard gamma) // otherwise divide by 327.68, convert to xyz and apply the RGB transform, before converting with gamma2curve -Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm) +Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool consider_histogram_settings) { //gamutmap(lab); @@ -147,7 +147,7 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, bool standard_gamma; - if(settings->HistogramWorking) { + if(settings->HistogramWorking && consider_histogram_settings) { profile = icm.working; standard_gamma = true; } else { diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 1a8d18897..2dcb6b9e0 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -137,6 +137,7 @@ extern Options options; namespace rtengine { + using namespace procparams; Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq, bool inspectorMode) @@ -1303,7 +1304,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT readyImg = new Image8 (fw, fh); ipf.lab2monitorRgb (labView, readyImg); } else { - readyImg = ipf.lab2rgb(labView, 0, 0, fw, fh, params.icm); + readyImg = ipf.lab2rgb(labView, 0, 0, fw, fh, params.icm, false); } delete labView; delete baseImg; From c04150be35bbbc4591944593d438ed39ea4bca0c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 30 Jan 2018 15:27:56 +0100 Subject: [PATCH 06/16] histmatching: slightly improved match for the shadows part of the tone curve --- rtengine/histmatching.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index 87eb672c8..60cbda287 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -86,12 +86,19 @@ void mappingToCurve(const std::vector &mapping, std::vector &curve) curve.clear(); const int npoints = 8; - int idx = 1; + int idx = 15; for (; idx < int(mapping.size()); ++idx) { if (mapping[idx] >= idx) { break; } } + if (idx == int(mapping.size())) { + for (idx = 1; idx < int(mapping.size()); ++idx) { + if (mapping[idx] >= idx) { + break; + } + } + } int step = max(int(mapping.size())/npoints, 1); auto coord = [](int v) -> double { return double(v)/255.0; }; From 50ba33900b8d9053b460889b2457f400f029f248 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 30 Jan 2018 18:59:59 +0100 Subject: [PATCH 07/16] some more constexpr --- rtengine/color.cc | 18 -------------- rtengine/color.h | 50 +++++++++++++++++++++++--------------- rtengine/demosaic_algos.cc | 15 ++++++------ 3 files changed, 38 insertions(+), 45 deletions(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index 4a71d222b..d6ba26b55 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -56,24 +56,6 @@ LUTf Color::igammatab_115_2; LUTf Color::gammatab_145_3; LUTf Color::igammatab_145_3; -// Wikipedia sRGB: Unlike most other RGB color spaces, the sRGB gamma cannot be expressed as a single numerical value. -// The overall gamma is approximately 2.2, consisting of a linear (gamma 1.0) section near black, and a non-linear section elsewhere involving a 2.4 exponent -// and a gamma (slope of log output versus log input) changing from 1.0 through about 2.3. -const double Color::sRGBGamma = 2.2; -const double Color::sRGBGammaCurve = 2.4; - -const double Color::eps_max = 580.40756; //(MAXVALF* 216.0f/24389.0); -const double Color::eps = 216.0f / 24389.0; //0.008856 -const double Color::kappa = 24389.0 / 27.0; //903.29630; - -const float Color::epsf = eps; -const float Color::kappaf = kappa; - -const float Color::D50x = 0.9642f; //0.96422; -const float Color::D50z = 0.8249f; //0.82521; -const double Color::u0 = 4.0 * D50x / (D50x + 15 + 3 * D50z); -const double Color::v0 = 9.0 / (D50x + 15 + 3 * D50z); -const double Color::epskap = 8.0; /* * Munsell Lch correction * Copyright (c) 2011 Jacques Desmis diff --git a/rtengine/color.h b/rtengine/color.h index 06b9dceb8..4b865f1d9 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -120,14 +120,32 @@ public: ID_DOWN /// Interpolate color by decreasing the hue value, crossing the lower limit } eInterpolationDirection; - const static double sRGBGamma; // standard average gamma - const static double sRGBGammaCurve; // 2.4 in the curve - const static double eps, eps_max, kappa, epskap; - const static float epsf, kappaf; - const static float D50x, D50z; - const static double u0, v0; - constexpr static float c1By116 = 0.00862068965517241379310344827586f; // 1/116 - constexpr static float c16By116 = 0.13793103448275862068965517241379f; // 16/116 + // Wikipedia sRGB: Unlike most other RGB color spaces, the sRGB gamma cannot be expressed as a single numerical value. + // The overall gamma is approximately 2.2, consisting of a linear (gamma 1.0) section near black, and a non-linear section elsewhere involving a 2.4 exponent + // and a gamma (slope of log output versus log input) changing from 1.0 through about 2.3. + constexpr static double sRGBGamma = 2.2; + constexpr static double sRGBGammaCurve = 2.4; + + constexpr static double eps = 216.0 / 24389.0; //0.008856 + constexpr static double eps_max = MAXVALF * eps; //580.40756; + constexpr static double kappa = 24389.0 / 27.0; //903.29630; + constexpr static double kappaInv = 27.0 / 24389.0; + constexpr static double epsilonExpInv3 = 6.0 / 29.0; + + constexpr static float epsf = eps; + constexpr static float kappaf = kappa; + constexpr static float kappaInvf = kappaInv; + constexpr static float epsilonExpInv3f = epsilonExpInv3; + + constexpr static float D50x = 0.9642f; //0.96422; + constexpr static float D50z = 0.8249f; //0.82521; + constexpr static double u0 = 4.0 * D50x / (D50x + 15 + 3 * D50z); + constexpr static double v0 = 9.0 / (D50x + 15 + 3 * D50z); + constexpr static double epskap = 8.0; + + constexpr static float c1By116 = 1.0 / 116.0; + constexpr static float c16By116 = 16.0 / 116.0; + static cmsToneCurve* linearGammaTRC; static LUTf cachef; @@ -689,27 +707,21 @@ public: */ static inline double f2xyz(double f) { - const double epsilonExpInv3 = 6.0 / 29.0; - const double kappaInv = 27.0 / 24389.0; // inverse of kappa - return (f > epsilonExpInv3) ? f * f * f : (116. * f - 16.) * kappaInv; } static inline float f2xyz(float f) { - const float epsilonExpInv3 = 0.20689655f; // 6.0f/29.0f; - const float kappaInv = 0.0011070565f; // 27.0f/24389.0f; // inverse of kappa - - return (f > epsilonExpInv3) ? f * f * f : (116.f * f - 16.f) * kappaInv; + return (f > epsilonExpInv3f) ? f * f * f : (116.f * f - 16.f) * kappaInvf; } #ifdef __SSE2__ static inline vfloat f2xyz(vfloat f) { - const vfloat epsilonExpInv3 = F2V(0.20689655f); // 6.0f/29.0f; - const vfloat kappaInv = F2V(0.0011070565f); // 27.0f/24389.0f; // inverse of kappa + const vfloat epsilonExpInv3v = F2V(epsilonExpInv3f); + const vfloat kappaInvv = F2V(kappaInvf); vfloat res1 = f * f * f; - vfloat res2 = (F2V(116.f) * f - F2V(16.f)) * kappaInv; - return vself(vmaskf_gt(f, epsilonExpInv3), res1, res2); + vfloat res2 = (F2V(116.f) * f - F2V(16.f)) * kappaInvv; + return vself(vmaskf_gt(f, epsilonExpInv3v), res1, res2); } #endif diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc index 90a202a1c..1bbea204c 100644 --- a/rtengine/demosaic_algos.cc +++ b/rtengine/demosaic_algos.cc @@ -3861,7 +3861,7 @@ void RawImageSource::cielab (const float (*rgb)[3], float* l, float* a, float *b if(!cbrtinit) { for (int i = 0; i < 0x14000; i++) { double r = i / 65535.0; - cbrt[i] = r > 0.008856f ? std::cbrt(r) : 7.787f * r + 16.f / 116.f; + cbrt[i] = r > Color::eps ? std::cbrt(r) : (Color::kappa * r + 16.0) / 116.0; } cbrtinit = true; @@ -3871,7 +3871,6 @@ void RawImageSource::cielab (const float (*rgb)[3], float* l, float* a, float *b } #ifdef __SSE2__ - vfloat zd5v = F2V(0.5f); vfloat c116v = F2V(116.f); vfloat c16v = F2V(16.f); vfloat c500v = F2V(500.f); @@ -3892,12 +3891,12 @@ void RawImageSource::cielab (const float (*rgb)[3], float* l, float* a, float *b for(; j < labWidth - 3; j += 4) { vfloat redv, greenv, bluev; vconvertrgbrgbrgbrgb2rrrrggggbbbb(rgb[i * width + j], redv, greenv, bluev); - vfloat xyz0v = zd5v + redv * xyz_camv[0][0] + greenv * xyz_camv[0][1] + bluev * xyz_camv[0][2]; - vfloat xyz1v = zd5v + redv * xyz_camv[1][0] + greenv * xyz_camv[1][1] + bluev * xyz_camv[1][2]; - vfloat xyz2v = zd5v + redv * xyz_camv[2][0] + greenv * xyz_camv[2][1] + bluev * xyz_camv[2][2]; - xyz0v = cbrt[_mm_cvttps_epi32(xyz0v)]; - xyz1v = cbrt[_mm_cvttps_epi32(xyz1v)]; - xyz2v = cbrt[_mm_cvttps_epi32(xyz2v)]; + vfloat xyz0v = redv * xyz_camv[0][0] + greenv * xyz_camv[0][1] + bluev * xyz_camv[0][2]; + vfloat xyz1v = redv * xyz_camv[1][0] + greenv * xyz_camv[1][1] + bluev * xyz_camv[1][2]; + vfloat xyz2v = redv * xyz_camv[2][0] + greenv * xyz_camv[2][1] + bluev * xyz_camv[2][2]; + xyz0v = cbrt[_mm_cvtps_epi32(xyz0v)]; + xyz1v = cbrt[_mm_cvtps_epi32(xyz1v)]; + xyz2v = cbrt[_mm_cvtps_epi32(xyz2v)]; STVFU(l[i * labWidth + j], c116v * xyz1v - c16v); STVFU(a[i * labWidth + j], c500v * (xyz0v - xyz1v)); From 3133adb1b5ca7bae682ae44dec90635b73198e36 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 30 Jan 2018 21:19:20 +0100 Subject: [PATCH 08/16] =?UTF-8?q?Cleanup=20after=20Fl=C3=B6ssies=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rtengine/color.cc | 62 +++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index d6ba26b55..b62f9a170 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -369,8 +369,8 @@ void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profil Glib::ustring profileCalc = "sRGB"; //default - if (workingSpace) {//display working - profileCalc = profileW; //display working + if (workingSpace) {//display working profile + profileCalc = profileW; if (profileW == "sRGB") { //apply sRGB inverse gamma if (r > 0.04045f) { @@ -400,7 +400,8 @@ void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profil b = pow_F(b, 2.2f); } } else { //display output profile - if (profile == "RT_sRGB" || profile == "RT_sRGB_gBT709" || profile == "RT_sRGB_g10") { // use default "sRGB" + if (profile == "RT_sRGB" || profile == "RT_sRGB_gBT709" || profile == "RT_sRGB_g10") { + // use default "sRGB" } else if (profile == "ProPhoto" || profile == "RT_Large_gBT709" || profile == "RT_Large_g10" || profile == "RT_Large_gsRGB") { profileCalc = "ProPhoto"; } else if (profile == "AdobeRGB1998" || profile == "RT_Medium_gsRGB") { @@ -427,7 +428,7 @@ void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profil } else { b /= 12.92f; } - } else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { // + } else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { if (r > 0.0795f) { r = pow_F(((r + 0.0954f) / 1.0954f), 2.2f); } else { @@ -451,7 +452,8 @@ void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profil r = pow_F(r, 1.8f); g = pow_F(g, 1.8f); b = pow_F(b, 1.8f); - } else if (profile == "RT_sRGB_g10" || profile == "RT_Large_g10") { // gamma 1.0, do nothing + } else if (profile == "RT_sRGB_g10" || profile == "RT_Large_g10") { + // gamma 1.0, do nothing } else {// apply inverse gamma 2.2 @@ -461,25 +463,24 @@ void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profil } } - TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix(profileCalc); + const TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix(profileCalc); - float xyz_rgb[3][3]; + const float xyz_rgb[3][3] = { {static_cast(wprof[0][0]), static_cast(wprof[0][1]), static_cast(wprof[0][2])}, + {static_cast(wprof[1][0]), static_cast(wprof[1][1]), static_cast(wprof[1][2])}, + {static_cast(wprof[2][0]), static_cast(wprof[2][1]), static_cast(wprof[2][2])} + }; - for (int m = 0; m < 3; m++) - for (int n = 0; n < 3; n++) { - xyz_rgb[m][n] = wprof[m][n]; - } + const float var_X = (xyz_rgb[0][0] * r + xyz_rgb[0][1] * g + xyz_rgb[0][2] * b) / Color::D50x; + const float var_Y = (xyz_rgb[1][0] * r + xyz_rgb[1][1] * g + xyz_rgb[1][2] * b); + const float var_Z = (xyz_rgb[2][0] * r + xyz_rgb[2][1] * g + xyz_rgb[2][2] * b) / Color::D50z; - float var_X = (xyz_rgb[0][0] * r + xyz_rgb[0][1] * g + xyz_rgb[0][2] * b) / Color::D50x; - float var_Y = (xyz_rgb[1][0] * r + xyz_rgb[1][1] * g + xyz_rgb[1][2] * b); - float var_Z = (xyz_rgb[2][0] * r + xyz_rgb[2][1] * g + xyz_rgb[2][2] * b) / Color::D50z; + const float varxx = var_X > epsf ? xcbrtf(var_X) : (kappaf * var_X + 16.f) / 116.f ; + const float varyy = var_Y > epsf ? xcbrtf(var_Y) : (kappaf * var_Y + 16.f) / 116.f ; + const float varzz = var_Z > epsf ? xcbrtf(var_Z) : (kappaf * var_Z + 16.f) / 116.f ; - float varxx = var_X > epsf ? xcbrtf(var_X) : (kappaf * var_X + 16.f) / 116.f ; - float varyy = var_Y > epsf ? xcbrtf(var_Y) : (kappaf * var_Y + 16.f) / 116.f ; - float varzz = var_Z > epsf ? xcbrtf(var_Z) : (kappaf * var_Z + 16.f) / 116.f ; LAB_l = var_Y > epsf ? (xcbrtf(var_Y) * 116.f) - 16.f : kappaf * var_Y; - LAB_a = 500.f * ( varxx - varyy ); - LAB_b = 200.f * ( varyy - varzz ); + LAB_a = 500.f * (varxx - varyy); + LAB_b = 200.f * (varyy - varzz); } @@ -725,24 +726,23 @@ void Color::rgb2hsv(float r, float g, float b, float &h, float &s, float &v) void Color::rgb2hsv01(float r, float g, float b, float &h, float &s, float &v) { - const float var_Min = min(r, g, b); - const float var_Max = max(r, g, b); - const float del_Max = var_Max - var_Min; + const float minVal = min(r, g, b); + v = max(r, g, b); + const float delta = v - minVal; h = 0.f; - v = var_Max; - if (del_Max < 0.00001f) { + if (delta < 0.00001f) { s = 0.f; } else { - s = del_Max / var_Max; + s = delta / (v == 0.f ? 1.f : v); - if (r == var_Max) { - h = (g - b) / del_Max; - } else if (g == var_Max) { - h = 2.f + (b - r) / del_Max; - } else if (b == var_Max) { - h = 4.f + (r - g) / del_Max; + if (r == v) { + h = (g - b) / delta; + } else if (g == v) { + h = 2.f + (b - r) / delta; + } else if (b == v) { + h = 4.f + (r - g) / delta; } h /= 6.f; From fdde31839d954f1b89cad8aba7467da839727825 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 31 Jan 2018 08:29:19 +0100 Subject: [PATCH 09/16] incorporated Floessie's feedback --- rtengine/histmatching.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index 60cbda287..33ef70c58 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -28,7 +28,6 @@ #include "improcfun.h" #include "StopWatch.h" #include -#include namespace rtengine { @@ -99,7 +98,7 @@ void mappingToCurve(const std::vector &mapping, std::vector &curve) } } } - int step = max(int(mapping.size())/npoints, 1); + int step = std::max(int(mapping.size())/npoints, 1); auto coord = [](int v) -> double { return double(v)/255.0; }; auto doit = @@ -207,6 +206,13 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) double scale; int w = fw / skip, h = fh / skip; std::unique_ptr thumb(Thumbnail::loadFromRaw(getFileName(), rml, sensor_type, w, h, 1, false, false)); + if (!thumb) { + if (settings->verbose) { + std::cout << "histogram matching: raw decoding failed, generating a neutral curve" << std::endl; + } + histMatchingCache = outCurve; + return; + } target.reset(thumb->processImage(neutral, sensor_type, fh / skip, TI_Nearest, getMetaData(), scale, false)); int sw = source->getWidth(), sh = source->getHeight(); @@ -227,7 +233,7 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) tw -= cw; } if (settings->verbose) { - std::cout << "histogram matching: cropping target to get an aspect ratio of " << std::fixed << std::setprecision(2) << thumb_ratio << ":1, new size is " << tw << "x" << th << std::endl; + std::cout << "histogram matching: cropping target to get an aspect ratio of " << round(thumb_ratio * 100)/100.0 << ":1, new size is " << tw << "x" << th << std::endl; } Image8 *tmp = new Image8(tw, th); From 79324ed5d017bea74eeb85436c61284d87bd4dd6 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 31 Jan 2018 10:34:50 +0100 Subject: [PATCH 10/16] more fine-tuning of the histogram matching make sure we do not place curve points in regions of the histogram that are outside the dynamic range of the input images -- that would be wild guessing, better to just leave this to the spline interpolation --- rtengine/histmatching.cc | 69 +++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index 33ef70c58..b17eff5f5 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -36,20 +36,36 @@ extern const Settings *settings; namespace { -std::vector getCdf(const IImage8 &img) +struct CdfInfo { + std::vector cdf; + int min_val; + int max_val; + + CdfInfo(): cdf(256), min_val(-1), max_val(-1) {} +}; + + +CdfInfo getCdf(const IImage8 &img) { - std::vector ret(256); + CdfInfo ret; + for (int y = 0; y < img.getHeight(); ++y) { for (int x = 0; x < img.getWidth(); ++x) { int lum = LIM(0, int(Color::rgbLuminance(float(img.r(y, x)), float(img.g(y, x)), float(img.b(y, x)))), 255); - ++ret[lum]; + ++ret.cdf[lum]; } } int sum = 0; - for (size_t i = 0; i < ret.size(); ++i) { - sum += ret[i]; - ret[i] = sum; + for (size_t i = 0; i < ret.cdf.size(); ++i) { + if (ret.cdf[i] > 0) { + if (ret.min_val < 0) { + ret.min_val = i; + } + ret.max_val = i; + } + sum += ret.cdf[i]; + ret.cdf[i] = sum; } return ret; @@ -105,12 +121,15 @@ void mappingToCurve(const std::vector &mapping, std::vector &curve) [&](int start, int stop, int step, bool addstart) -> void { int prev = start; - if (addstart) { + if (addstart && mapping[start] >= 0) { curve.push_back(coord(start)); curve.push_back(coord(mapping[start])); } for (int i = start; i < stop; ++i) { int v = mapping[i]; + if (v < 0) { + continue; + } bool change = i > 0 && v != mapping[i-1]; int diff = i - prev; if ((change && std::abs(diff - step) <= 1) || diff > step * 2) { @@ -120,20 +139,22 @@ void mappingToCurve(const std::vector &mapping, std::vector &curve) } } }; - doit(0, idx, idx > step ? step : idx / 2, true); - doit(idx, int(mapping.size()), step, idx - step > step / 2); + + curve.push_back(0.0); + curve.push_back(0.0); + + int start = 0; + while (start < idx && (mapping[start] < 0 || start < idx / 2)) { + ++start; + } + + doit(start, idx, idx > step ? step : idx / 2, true); + doit(idx, int(mapping.size()), step, idx - step > step / 2 && std::abs(curve[curve.size()-2] - coord(idx)) > 0.01); + if (curve.size() > 2 && (1 - curve[curve.size()-2] <= step / (256.0 * 3))) { curve.pop_back(); curve.pop_back(); } - // remove the plateau at the end (if any) - size_t j = curve.size() / 2; - for (; j > 1; --j) { - if (std::abs(curve[2*(j-1)-1] - curve.back()) > 0.01) { - break; - } - } - curve.resize(2*j); curve.push_back(1.0); curve.push_back(1.0); @@ -259,14 +280,18 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) target->resizeImgTo(source->getWidth(), source->getHeight(), TI_Nearest, tmp); target.reset(tmp); } - std::vector scdf = getCdf(*source); - std::vector tcdf = getCdf(*target); + CdfInfo scdf = getCdf(*source); + CdfInfo tcdf = getCdf(*target); std::vector mapping; int j = 0; - for (size_t i = 0; i < tcdf.size(); ++i) { - j = findMatch(tcdf[i], scdf, j); - mapping.push_back(j); + for (int i = 0; i < int(tcdf.cdf.size()); ++i) { + j = findMatch(tcdf.cdf[i], scdf.cdf, j); + if (i >= tcdf.min_val && i <= tcdf.max_val && j >= scdf.min_val && j <= scdf.max_val) { + mapping.push_back(j); + } else { + mapping.push_back(-1); + } } mappingToCurve(mapping, outCurve); From 34bd19f4bfb25ec52df785e55feabca9dd817124 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Wed, 31 Jan 2018 15:17:23 +0100 Subject: [PATCH 11/16] Updated mime-types --- rtdata/icons/mime-types | 11 +++++++---- rtdata/icons/rawtherapee.desktop.in | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rtdata/icons/mime-types b/rtdata/icons/mime-types index c232119ee..505b00140 100644 --- a/rtdata/icons/mime-types +++ b/rtdata/icons/mime-types @@ -9,10 +9,14 @@ image/x-canon-cr2; image/x-canon-crf; image/x-canon-crw; image/x-fuji-raf; +image/x-hasselblad-3fr; +image/x-hasselblad-fff; image/x-jpg; image/x-kodak-dcr; image/x-kodak-k25; image/x-kodak-kdc; +image/x-leaf-mos; +image/x-leica-rwl; image/x-mamiya-mef; image/x-minolta-mrw; image/x-nikon-nef; @@ -22,15 +26,14 @@ image/x-panasonic-raw; image/x-panasonic-rw2; image/x-pentax-pef; image/x-pentax-raw; +image/x-phaseone-iiq; image/x-raw; image/x-rwz; image/x-samsung-srw; +image/x-sigma-x3f; +image/x-sony-arq; image/x-sony-arw; image/x-sony-sr2; image/x-sony-srf; -image/x-hasselblad-3fr; -image/x-hasselblad-fff; -image/x-leaf-mos; -image/x-phaseone-iiq; image/x-tif; inode/directory; diff --git a/rtdata/icons/rawtherapee.desktop.in b/rtdata/icons/rawtherapee.desktop.in index a573f19d3..c3d26c980 100644 --- a/rtdata/icons/rawtherapee.desktop.in +++ b/rtdata/icons/rawtherapee.desktop.in @@ -13,6 +13,6 @@ Comment[pl]=Zaawansowany program do wywoływania zdjęć typu raw Icon=rawtherapee Exec=rawtherapee %f Terminal=false -MimeType=image/jpeg;image/png;image/tiff;image/x-adobe-dng;image/x-canon-cr2;image/x-canon-crf;image/x-canon-crw;image/x-fuji-raf;image/x-jpg;image/x-kodak-dcr;image/x-kodak-k25;image/x-kodak-kdc;image/x-mamiya-mef;image/x-minolta-mrw;image/x-nikon-nef;image/x-nikon-nrw;image/x-olympus-orf;image/x-panasonic-raw;image/x-panasonic-rw2;image/x-pentax-pef;image/x-pentax-raw;image/x-raw;image/x-rwz;image/x-samsung-srw;image/x-sony-arw;image/x-sony-sr2;image/x-sony-srf;image/x-hasselblad-3fr;image/x-hasselblad-fff;image/x-leaf-mos;image/x-phaseone-iiq;image/x-tif; +MimeType=image/jpeg;image/png;image/tiff;image/x-adobe-dng;image/x-canon-cr2;image/x-canon-crf;image/x-canon-crw;image/x-fuji-raf;image/x-hasselblad-3fr;image/x-hasselblad-fff;image/x-jpg;image/x-kodak-dcr;image/x-kodak-k25;image/x-kodak-kdc;image/x-leaf-mos;image/x-leica-rwl;image/x-mamiya-mef;image/x-minolta-mrw;image/x-nikon-nef;image/x-nikon-nrw;image/x-olympus-orf;image/x-panasonic-raw;image/x-panasonic-rw2;image/x-pentax-pef;image/x-pentax-raw;image/x-phaseone-iiq;image/x-raw;image/x-rwz;image/x-samsung-srw;image/x-sigma-x3f;image/x-sony-arq;image/x-sony-arw;image/x-sony-sr2;image/x-sony-srf;image/x-tif; Categories=Photography;Graphics;2DGraphics;RasterGraphics;GTK; Keywords=raw;photography;develop;pp3;graphics; From b09aeaa6ae9552dada335b4d049a382f70c730ad Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 31 Jan 2018 17:06:39 +0100 Subject: [PATCH 12/16] avoid one useless copy if no cropping is required --- rtengine/histmatching.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index b17eff5f5..e496f6f82 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -257,18 +257,20 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) std::cout << "histogram matching: cropping target to get an aspect ratio of " << round(thumb_ratio * 100)/100.0 << ":1, new size is " << tw << "x" << th << std::endl; } - Image8 *tmp = new Image8(tw, th); + if (cx || cy) { + Image8 *tmp = new Image8(tw, th); #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for #endif - for (int y = 0; y < th; ++y) { - for (int x = 0; x < tw; ++x) { - tmp->r(y, x) = target->r(y+cy, x+cx); - tmp->g(y, x) = target->g(y+cy, x+cx); - tmp->b(y, x) = target->b(y+cy, x+cx); + for (int y = 0; y < th; ++y) { + for (int x = 0; x < tw; ++x) { + tmp->r(y, x) = target->r(y+cy, x+cx); + tmp->g(y, x) = target->g(y+cy, x+cx); + tmp->b(y, x) = target->b(y+cy, x+cx); + } } + target.reset(tmp); } - target.reset(tmp); } if (settings->verbose) { From 74e31dff45293ca5e8ce3fe22c1d870eca290b65 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 31 Jan 2018 18:09:28 +0100 Subject: [PATCH 13/16] histmatching: dynamically adjust the skip factor according to the side of the embedded thumbnail with large the embedded thumbnails we should skip less to improve the quality of the matching --- rtengine/histmatching.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index e496f6f82..e326a3f7d 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -189,7 +189,7 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) int fw, fh; getFullSize(fw, fh, TR_NONE); - int skip = 10; + int skip = 3; if (settings->verbose) { std::cout << "histogram matching: full raw image size is " << fw << "x" << fh << std::endl; @@ -213,6 +213,7 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) histMatchingCache = outCurve; return; } + skip = LIM(5, skip * fh / h, 10); // adjust the skip factor -- the larger the thumbnail, the less we should skip to get a good match source.reset(thumb->quickProcessImage(neutral, fh / skip, TI_Nearest)); if (settings->verbose) { From 8721b08346116683a07c779f7eb09bf448c4f1f1 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 31 Jan 2018 20:59:32 +0100 Subject: [PATCH 14/16] Show correct rgb values in histogram, navigator and colour picker when using 'Rec2020' --- rtengine/color.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index b62f9a170..3be412b6d 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -394,6 +394,24 @@ void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profil r = pow_F(r, 1.8f); g = pow_F(g, 1.8f); b = pow_F(b, 1.8f); + } else if (profile == "Rec2020") { + if (r > 0.0795f) { + r = pow_F(((r + 0.0954f) / 1.0954f), 2.2f); + } else { + r /= 4.5f; + } + + if (g > 0.0795f) { + g = pow_F(((g + 0.0954f) / 1.0954f), 2.2f); + } else { + g /= 4.5f; + } + + if (b > 0.0795f) { + b = pow_F(((b + 0.0954f) / 1.0954f), 2.2f); + } else { + b /= 4.5f; + } } else { // apply inverse gamma 2.2 r = pow_F(r, 2.2f); g = pow_F(g, 2.2f); @@ -428,7 +446,7 @@ void Color::rgb2lab01 (const Glib::ustring &profile, const Glib::ustring &profil } else { b /= 12.92f; } - } else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { + } else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709" || profile == "Rec2020") { if (r > 0.0795f) { r = pow_F(((r + 0.0954f) / 1.0954f), 2.2f); } else { From 7dd598d9b7acc3b52a867351aa36e3d9d077a956 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 31 Jan 2018 21:24:56 +0100 Subject: [PATCH 15/16] bug fixes by heckflosse --- rtengine/histmatching.cc | 2 +- rtengine/rtthumbnail.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rtengine/histmatching.cc b/rtengine/histmatching.cc index e326a3f7d..aaeada81a 100644 --- a/rtengine/histmatching.cc +++ b/rtengine/histmatching.cc @@ -213,7 +213,7 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector &outCurve) histMatchingCache = outCurve; return; } - skip = LIM(5, skip * fh / h, 10); // adjust the skip factor -- the larger the thumbnail, the less we should skip to get a good match + skip = LIM(skip * fh / h, 6, 10); // adjust the skip factor -- the larger the thumbnail, the less we should skip to get a good match source.reset(thumb->quickProcessImage(neutral, fh / skip, TI_Nearest)); if (settings->verbose) { diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 2dcb6b9e0..5318c5eaa 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -466,8 +466,8 @@ Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocati skip--; } - if (skip < 1) { - skip = 1; + if (skip < 2) { + skip = 2; } int hskip = skip, vskip = skip; From adb13928391c2af2bc9f1e7a58cabd93a3112684 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 31 Jan 2018 21:34:10 +0100 Subject: [PATCH 16/16] renamed formal parameters of LIM from (a, b, c) to (val, low, high) --- rtengine/rt_math.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 787e2e63f..ca93619ee 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -74,9 +74,9 @@ constexpr const T& max(const T& a, const T& b, const ARGS&... args) } template -constexpr const T& LIM(const T& a, const T& b, const T& c) +constexpr const T& LIM(const T& val, const T& low, const T& high) { - return max(b, min(a, c)); + return max(low, min(val, high)); } template