void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { dt_iop_lowlight_data_t *d = (dt_iop_lowlight_data_t *)(piece->data); const int ch = piece->colors; // empiric coefficient const float c = 0.5f; const float threshold = 0.01f; // scotopic white, blue saturated float Lab_sw[3] = { 100.0f, 0, -d->blueness }; float XYZ_sw[3]; dt_Lab_to_XYZ(Lab_sw, XYZ_sw); #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) shared(d, XYZ_sw) #endif for(size_t k = 0; k < (size_t)roi_out->width * roi_out->height; k++) { float *in = (float *)i + ch * k; float *out = (float *)o + ch * k; float XYZ[3], XYZ_s[3]; float V; float w; dt_Lab_to_XYZ(in, XYZ); // calculate scotopic luminance if(XYZ[0] > threshold) { // normal flow V = XYZ[1] * (1.33f * (1.0f + (XYZ[1] + XYZ[2]) / XYZ[0]) - 1.68f); } else { // low red flow, avoids "snow" on dark noisy areas V = XYZ[1] * (1.33f * (1.0f + (XYZ[1] + XYZ[2]) / threshold) - 1.68f); } // scale using empiric coefficient and fit inside limits V = fminf(1.0f, fmaxf(0.0f, c * V)); // blending coefficient from curve w = lookup(d->lut, in[0] / 100.f); XYZ_s[0] = V * XYZ_sw[0]; XYZ_s[1] = V * XYZ_sw[1]; XYZ_s[2] = V * XYZ_sw[2]; XYZ[0] = w * XYZ[0] + (1.0f - w) * XYZ_s[0]; XYZ[1] = w * XYZ[1] + (1.0f - w) * XYZ_s[1]; XYZ[2] = w * XYZ[2] + (1.0f - w) * XYZ_s[2]; dt_XYZ_to_Lab(XYZ, out); out[3] = in[3]; } }
static inline void LCH_2_RGB(const float *LCH, float *RGB) { float Lab[3], XYZ[3]; LCH_2_Lab(LCH, Lab); dt_Lab_to_XYZ(Lab, XYZ); dt_XYZ_to_sRGB_clipped(XYZ, RGB); }
int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { dt_iop_lowlight_data_t *d = (dt_iop_lowlight_data_t *)piece->data; dt_iop_lowlight_global_data_t *gd = (dt_iop_lowlight_global_data_t *)self->data; cl_mem dev_m = NULL; cl_int err = -999; const int devid = piece->pipe->devid; const int width = roi_out->width; const int height = roi_out->height; // scotopic white, blue saturated float Lab_sw[3] = { 100.0f, 0.0f, -d->blueness }; float XYZ_sw[4]; dt_Lab_to_XYZ(Lab_sw, XYZ_sw); dev_m = dt_opencl_copy_host_to_device(devid, d->lut, 256, 256, sizeof(float)); if(dev_m == NULL) goto error; size_t sizes[2] = { ROUNDUPWD(width), ROUNDUPHT(height) }; dt_opencl_set_kernel_arg(devid, gd->kernel_lowlight, 0, sizeof(cl_mem), &dev_in); dt_opencl_set_kernel_arg(devid, gd->kernel_lowlight, 1, sizeof(cl_mem), &dev_out); dt_opencl_set_kernel_arg(devid, gd->kernel_lowlight, 2, sizeof(int), &width); dt_opencl_set_kernel_arg(devid, gd->kernel_lowlight, 3, sizeof(int), &height); dt_opencl_set_kernel_arg(devid, gd->kernel_lowlight, 4, 4 * sizeof(float), &XYZ_sw); dt_opencl_set_kernel_arg(devid, gd->kernel_lowlight, 5, sizeof(cl_mem), &dev_m); err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_lowlight, sizes); if(err != CL_SUCCESS) goto error; dt_opencl_release_mem_object(dev_m); return TRUE; error: dt_opencl_release_mem_object(dev_m); dt_print(DT_DEBUG_OPENCL, "[opencl_lowlight] couldn't enqueue kernel! %d\n", err); return FALSE; }
void set_color(box_t *box, dt_colorspaces_color_profile_type_t color_space, float c0, float c1, float c2) { box->color_space = color_space; box->color[0] = c0; box->color[1] = c1; box->color[2] = c2; float Lab[3] = { c0, c1, c2 }; float XYZ[3] = { c0 * 0.01, c1 * 0.01, c2 * 0.01 }; switch(color_space) { default: case DT_COLORSPACE_NONE: for(int c = 0; c < 3; c++) box->rgb[c] = 0.0; break; case DT_COLORSPACE_LAB: dt_Lab_to_XYZ(Lab, XYZ); case DT_COLORSPACE_XYZ: dt_XYZ_to_sRGB(XYZ, box->rgb); break; } }
static inline void Lab_2_RGB(const float *Lab, float *RGB) { float XYZ[3]; dt_Lab_to_XYZ(Lab, XYZ); dt_XYZ_to_sRGB_clipped(XYZ, RGB); }
// see http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html for the transformation matrices void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *i, void *o, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out) { dt_iop_colorbalance_data_t *d = (dt_iop_colorbalance_data_t *)piece->data; const int ch = piece->colors; // these are RGB values! const float lift[3] = { 2.0 - (d->lift[CHANNEL_RED] * d->lift[CHANNEL_FACTOR]), 2.0 - (d->lift[CHANNEL_GREEN] * d->lift[CHANNEL_FACTOR]), 2.0 - (d->lift[CHANNEL_BLUE] * d->lift[CHANNEL_FACTOR]) }, gamma[3] = { d->gamma[CHANNEL_RED] * d->gamma[CHANNEL_FACTOR], d->gamma[CHANNEL_GREEN] * d->gamma[CHANNEL_FACTOR], d->gamma[CHANNEL_BLUE] * d->gamma[CHANNEL_FACTOR] }, gamma_inv[3] = { (gamma[0] != 0.0) ? 1.0 / gamma[0] : 1000000.0, (gamma[1] != 0.0) ? 1.0 / gamma[1] : 1000000.0, (gamma[2] != 0.0) ? 1.0 / gamma[2] : 1000000.0 }, gain[3] = { d->gain[CHANNEL_RED] * d->gain[CHANNEL_FACTOR], d->gain[CHANNEL_GREEN] * d->gain[CHANNEL_FACTOR], d->gain[CHANNEL_BLUE] * d->gain[CHANNEL_FACTOR] }; // sRGB -> XYZ matrix, D65 const float srgb_to_xyz[3][3] = { { 0.4360747, 0.3850649, 0.1430804 }, { 0.2225045, 0.7168786, 0.0606169 }, { 0.0139322, 0.0971045, 0.7141733 } // {0.4124564, 0.3575761, 0.1804375}, // {0.2126729, 0.7151522, 0.0721750}, // {0.0193339, 0.1191920, 0.9503041} }; // XYZ -> sRGB matrix, D65 const float xyz_to_srgb[3][3] = { { 3.1338561, -1.6168667, -0.4906146 }, { -0.9787684, 1.9161415, 0.0334540 }, { 0.0719453, -0.2289914, 1.4052427 } // {3.2404542, -1.5371385, -0.4985314}, // {-0.9692660, 1.8760108, 0.0415560}, // {0.0556434, -0.2040259, 1.0572252} }; #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) shared(i, o, roi_in, roi_out) #endif for(int j = 0; j < roi_out->height; j++) { float *in = ((float *)i) + (size_t)ch * roi_in->width * j; float *out = ((float *)o) + (size_t)ch * roi_out->width * j; for(int i = 0; i < roi_out->width; i++) { // transform the pixel to sRGB: // Lab -> XYZ float XYZ[3]; dt_Lab_to_XYZ(in, XYZ); // XYZ -> sRGB float rgb[3] = { 0, 0, 0 }; for(int r = 0; r < 3; r++) for(int c = 0; c < 3; c++) rgb[r] += xyz_to_srgb[r][c] * XYZ[c]; // linear sRGB -> gamma corrected sRGB for(int c = 0; c < 3; c++) rgb[c] = rgb[c] <= 0.0031308 ? 12.92 * rgb[c] : (1.0 + 0.055) * powf(rgb[c], 1.0 / 2.4) - 0.055; // do the calculation in RGB space for(int c = 0; c < 3; c++) { float tmp = (((rgb[c] - 1.0f) * lift[c]) + 1.0f) * gain[c]; if(tmp < 0.0f) tmp = 0.0f; rgb[c] = powf(tmp, gamma_inv[c]); } // transform the result back to Lab // sRGB -> XYZ XYZ[0] = XYZ[1] = XYZ[2] = 0.0; // gamma corrected sRGB -> linear sRGB for(int c = 0; c < 3; c++) rgb[c] = rgb[c] <= 0.04045 ? rgb[c] / 12.92 : powf((rgb[c] + 0.055) / (1 + 0.055), 2.4); for(int r = 0; r < 3; r++) for(int c = 0; c < 3; c++) XYZ[r] += srgb_to_xyz[r][c] * rgb[c]; // XYZ -> Lab dt_XYZ_to_Lab(XYZ, out); out[3] = in[3]; in += ch; out += ch; } } }