Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
knee.h
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21// R9 sensor-rolloff (knee) estimation and inversion (CPU + OpenCL).
22// Public API of this highlights harmonic-transposition module (a compiled TU). Include
23// this header to call into the module; internals are static in the .c. See common.h.
24
25#include "common/gaussian.h"
26#include "common/opencl.h"
27#include "iop/highlights/blur.h"
29#include <string.h>
30
31static inline void _knee_blur(const float *const restrict in, float *const restrict out, const int width,
32 const int height, const float sigma)
33{
34 dt_gaussian_t *const gaussian = _hl_gauss_get(width, height, 1, sigma); // cached handle, do not free
35
36 if(!gaussian)
37 {
38 memcpy(out, in, (size_t)width * height * sizeof(float));
39 return;
40 }
41
43}
44
45// Step 2 (article "The algorithm", Sensor rolloff (knee) inversion): build the per-channel inverse
46// k^-1(v) = v + median{ v_hat_i - v_i | v_i in bin(v) }
47// from the image itself. v_i are the measured band pixels; v_hat_i is what a windowed colour-line
48// regression predicts each SHOULD read from its still-trusted neighbouring channels; the per-bin
49// median of the votes v_hat_i - v_i is the accepted lift.
50// Works on the RAW CFA `input`, NOT the bilinear-demosaiced buffer: the demosaic samples each
51// channel through a different spatial filter per Bayer phase, and that alternating error is the same
52// size as the knee signal -- it decorrelates the colour-lines and kills the estimate. A 2x2 quad
53// binning of the CFA instead gives co-located R / mean-G / B per cell with no inter-site
54// interpolation (the "quad-binned copy of the raw mosaic" of the article). `clipval_raw` is the
55// clip level per channel in raw units (= clips / DET). Writes 3 curves (engaged = 0 means
56// identity). Never fails: any shortage of data or memory returns identity.
57void _hl_knee_estimate(const float *const restrict input, const size_t width, const size_t height,
58 const uint32_t filters, const dt_iop_roi_t *const roi_in, const uint8_t (*const xtrans)[6],
59 const dt_aligned_pixel_t clipval_raw, _hl_knee_curve_t curves[3],
60 const dt_dev_pixelpipe_t *pipe);
61
62// Step 2 application on the demosaiced [R,G,B,norm] buffer: for each engaged channel whose value
63// lies in the band, replace v by k^-1(v) = v + L(v). Keeps the norm channel consistent with the
64// corrected raw RGB (the "correction applied to the reconstruction anchors" of the article).
65void _hl_knee_apply_interpolated(float *const restrict interpolated, const size_t npix,
66 const dt_aligned_pixel_t clipvaln, const dt_aligned_pixel_t wb4,
67 const _hl_knee_curve_t curves[3]);
68
69// Apply the engaged curves to a CFA copy (raw units) so the final composition hands the corrected
70// band values to the output; unclipped/clipped values pass through untouched.
71void _hl_knee_apply_cfa(const float *const restrict input, float *const restrict input_corr, const size_t width,
72 const size_t height, const uint32_t filters, const dt_iop_roi_t *const roi_in,
73 const uint8_t (*const xtrans)[6], const dt_aligned_pixel_t clipval_raw,
74 const _hl_knee_curve_t curves[3]);
75
76#ifdef HAVE_OPENCL
77// Step 2 (article "The algorithm", knee inversion) on the GPU: build the per-channel inverse
78// k^-1(v) = v + median{ v_hat_i - v_i | v_i in bin(v) },
79// v_hat from a windowed colour-line regression v_hat = a*u1 + b*u2 + d (joint) or a*u + d (single).
80// GPU knee estimation (sensor saturation-rolloff curve, see the DT_HL_KNEE macro comment):
81// the device runs Phase A (colour-filter-array binning, the 5-sigma windowed moment blurs --
82// packed 4 planes per gaussian pass -- and the colour-line regressions producing v_hat/R^2); the
83// host keeps Phase B (vote medians + significance gate + monotone curve fit) on the downloaded
84// BINNED planes (<= 1.5 Mpx grid, x/pred/r2s/done only). The full-res raw mosaic never crosses the
85// bus. Mirrors _hl_knee_estimate on the CPU: any change here must be mirrored there and
86// re-validated with the HL_KNEECL_TEST self-test (_knee_cl_selftest).
87cl_int _hl_knee_estimate_cl(const int devid, void *gd_void, cl_mem dev_in, const size_t width, const size_t height,
88 const uint32_t filters, const dt_iop_roi_t *const roi_in, cl_mem dev_xtrans,
89 const int is_xtrans, const dt_aligned_pixel_t clipval_raw, _hl_knee_curve_t curves[3],
90 const dt_dev_pixelpipe_t *pipe);
91
92// Step 2 application on the device: replace each engaged-channel band pixel v of the raw mosaic by
93// k^-1(v) = v + L(v) (the hl_knee_apply kernel does the per-pixel curve lookup). The per-channel
94// lift[] knot arrays are flattened and uploaded; engaged flags gate which channels are corrected.
95// Mirrors _hl_knee_apply_cfa on the CPU: any change here must be mirrored there and
96// re-validated with the HL_KNEECL_TEST self-test (_knee_cl_selftest).
97cl_int _hl_knee_apply_cfa_cl(const int devid, void *gd_void, cl_mem dev_in, cl_mem dev_out, const size_t width,
98 const size_t height, const uint32_t filters, const dt_iop_roi_t *const roi_in,
99 cl_mem dev_xtrans, const int is_xtrans, const dt_aligned_pixel_t clipval_raw,
100 const _hl_knee_curve_t curves[3]);
101#endif
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
dt_gaussian_t * _hl_gauss_get(const int width, const int height, const int channels, const float sigma)
Definition blur.c:30
const dt_colormatrix_t dt_aligned_pixel_t out
static float gaussian(float x, float std)
Definition filmic.c:397
__DT_CLONE_TARGETS__ void dt_gaussian_blur(dt_gaussian_t *g, const float *const in, float *const out)
Definition gaussian.c:171
void _hl_knee_apply_cfa(const float *const restrict input, float *const restrict input_corr, const size_t width, const size_t height, const uint32_t filters, const dt_iop_roi_t *const roi_in, const uint8_t(*const xtrans)[6], const dt_aligned_pixel_t clipval_raw, const _hl_knee_curve_t curves[3])
Definition knee.c:553
void _hl_knee_estimate(const float *const restrict input, const size_t width, const size_t height, const uint32_t filters, const dt_iop_roi_t *const roi_in, const uint8_t(*const xtrans)[6], const dt_aligned_pixel_t clipval_raw, _hl_knee_curve_t curves[3], const dt_dev_pixelpipe_t *pipe)
Definition knee.c:105
static void _knee_blur(const float *const restrict in, float *const restrict out, const int width, const int height, const float sigma)
Definition knee.h:31
void _hl_knee_apply_interpolated(float *const restrict interpolated, const size_t npix, const dt_aligned_pixel_t clipvaln, const dt_aligned_pixel_t wb4, const _hl_knee_curve_t curves[3])
Definition knee.c:515
cl_int _hl_knee_estimate_cl(const int devid, void *gd_void, cl_mem dev_in, const size_t width, const size_t height, const uint32_t filters, const dt_iop_roi_t *const roi_in, cl_mem dev_xtrans, const int is_xtrans, const dt_aligned_pixel_t clipval_raw, _hl_knee_curve_t curves[3], const dt_dev_pixelpipe_t *pipe)
Definition knee.c:584
cl_int _hl_knee_apply_cfa_cl(const int devid, void *gd_void, cl_mem dev_in, cl_mem dev_out, const size_t width, const size_t height, const uint32_t filters, const dt_iop_roi_t *const roi_in, cl_mem dev_xtrans, const int is_xtrans, const dt_aligned_pixel_t clipval_raw, const _hl_knee_curve_t curves[3])
Definition knee.c:917
float dt_aligned_pixel_t[4]
const float sigma
Region of interest passed through the pixelpipe.
Definition imageop.h:72