- kernelSource "// version 2\n//\n// References :\n// - \[2] Ohno, Yoshi (2014). Practical Use and Calculation of CCT and Duv. LEUKOS, 10(1), 47-55. doi:10.1080/15502724.2014.839020\n// - \[3] https://en.wikipedia.org/wiki/Planckian_locus#Approximation\n// - \[4] SMPTE Recommended Practice - Derivation of Basic Television Color Equations https://ieeexplore.ieee.org/document/7291155\n\n#define ohno_deltaT float(0.01)\n\n\nfloat powsafe(float color, float power)\{\n // pow() but safe for NaNs/negatives\n return pow(fabs(color), power) * sign(color);\n\}\n\n\nfloat2 convert_CCT_to_uv_Krystek1985(float CCT)\{\n // Convert the given CCT to CIE 1960 u,v colorspace values using Krystek\\'s method.\n //\n // Krystek\\'s method is an approximation and not intended for accuracy.\n //\n // :param CCT: in kelvin, ~\[1000-15000] range\n // --\[3]\n float CCT_2 = pow(CCT,2.0f);\n float u = 0.860117757f + 1.54118254f * pow(10.0f,-4.0f) * CCT + 1.28641212f * pow(10.0f,-7.0f) * CCT_2;\n u = u / (1.0f + 8.42420235f * pow(10.0f,-4.0f) * CCT + 7.08145163f * pow(10.0f,-7.0f) * CCT_2);\n float v = 0.317398726f + 4.22806245f * pow(10.0f,-5.0f) * CCT + 4.20481691f * pow(10.0f,-8.0f) * CCT_2;\n v = v / (1.0f - 2.89741816f * pow(10.0f,-5.0f) * CCT + 1.61456053f * pow(10.0f,-7.0f) * CCT_2);\n return float2(u, v);\n\}\n\n\nfloat2 convert_CCT_Duv_to_xy(float CCT, float Duv)\{\n // :param CCT: correlated color temperature in kelvin, ~\[1000-15000] range\n // :param Duv: also called \"tint\" \[-0.05-+0.05] range\n // -- \[2]\n float2 uv0 = convert_CCT_to_uv_Krystek1985(CCT);\n float2 uv1 = convert_CCT_to_uv_Krystek1985(CCT + ohno_deltaT);\n\n float du = uv0.x - uv1.x;\n float dv = uv0.y - uv1.y;\n\n float hypothenus = sqrt(powsafe(du,2.0f) + powsafe(dv,2.0f));\n float sinTheta = dv / hypothenus;\n float cosTheta = du / hypothenus;\n\n float u = uv0.x - Duv * sinTheta;\n float v = uv0.y + Duv * cosTheta;\n\n float u_p = u;\n float v_p = 1.5f * v;\n\n float x = 9.0f * u_p / (6.0f * u_p - 16.0f * v_p + 12.0f);\n float y = 2.0f * v_p / (3.0f * u_p - 8.0f * v_p + 6.0f);\n return float2(x, y);\n\}\n\n\nkernel WhiteBalance : ImageComputationKernel<ePixelWise>\n\{\n Image<eRead, eAccessPoint, eEdgeClamped> src;\n Image<eWrite> dst;\n\n param:\n bool u_show_coeffs;\n float u_temperature;\n float u_tint;\n float u_intensity;\n\n void define()\{\n // default values try to match illuminant E\n defineParam(u_temperature, \"u_temperature\", 5600.0f);\n defineParam(u_tint, \"u_tint\", -15.5f);\n defineParam(u_intensity, \"u_intensity\", 1.0f);\n \}\n\n float lerp(float a1, float a2, float amount)\{\n // linear interpolation between 2 values\n return (1.0f - amount) * a1 + amount * a2;\n \}\n\n void process() \{\n\n // 3000 is an arbitrary scale for the tint parameter to have a more UI friendly range.\n // (actually same as Adobe)\n float2 new_white_xy = convert_CCT_Duv_to_xy(u_temperature, u_tint/3000.0f);\n\n // --\[4] normalise primary matrix algorithm but only with whitepoint\n float Wz = 1.0f - new_white_xy.x - new_white_xy.y;\n float3 W = float3(new_white_xy.x / new_white_xy.y, 1.0f, Wz / new_white_xy.y);\n\n float4 rgba = src();\n float3 new_rgb(rgba.x, rgba.y, rgba.z);\n\n if (u_show_coeffs)\{\n new_rgb.x = W.x;\n new_rgb.y = W.y;\n new_rgb.z = W.z;\n \} else \{\n new_rgb.x = lerp(rgba.x, new_rgb.x * W.x, u_intensity);\n new_rgb.y = lerp(rgba.y, new_rgb.y * W.y, u_intensity);\n new_rgb.z = lerp(rgba.z, new_rgb.z * W.z, u_intensity);\n \}\n\n dst() = float4(\n new_rgb.x,\n new_rgb.y,\n new_rgb.z,\n rgba.w\n );\n \}\n\};"
0 commit comments