Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
pde.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// Sparse-SPD PDE assembly/solve on the region grid (screened Poisson / diffusion), 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/opencl.h"
29#include <stdint.h>
30
31// Assemble the sparse matrix A = diag(d) + lam*Op over the `hole` pixels of the region grid
32// (Op = the diffusion operator from _sp_row_op; pixels outside the hole are fixed boundary
33// values, eliminated into the right-hand side by the caller, exactly like the conjugate
34// gradient does). Outputs the upper triangle in compressed-sparse-column form, with unknowns
35// permuted by geometric nested dissection. Returns 0 when the system is empty or too big for
36// the direct solver. *pgrid_out (size *nh_out) maps permuted unknown -> grid index; free with
37// dt_free_align.
38//
39// MATHS BRIDGE -- step 7 all-clip core, E_chrominance screened-Poisson (article §"The optimization
40// problem", term 3 / §"Chrominance, by diffusion"): this is the LHS matrix A of the diffusion the
41// all-clip core solves per channel. With order 1 the row operator Op = -Delta (minus the 9-point
42// Laplacian) and diag(d) = lambda*I, so A = lambda*I - Delta discretizes the modified-Helmholtz /
43// screened-Poisson operator (lambda - Delta) whose Euler-Lagrange minimizer of
44// int (||grad r||^2 + lambda ||r||^2) dOmega is (Delta - lambda) r = 0, r|dOmega = r_valid. d is the
45// per-pixel screening/reaction strength (react = solid_color^2 * 4, the "inpaint a flat colour"
46// pull toward the mean valid chroma); lam scales Op. A is SPD, hence the sparse Cholesky.
47int _sp_pde_assemble(const uint8_t *const restrict hole, const float *const restrict diffusion,
48 const float diffusion_const, const int order, const float lambda, const int region_w,
49 const int region_h, int **matrix_col_ptr_out, int **matrix_row_index_out,
50 double **matrix_values_out, int **perm_grid_out, int *n_unknowns_out,
51 const dt_dev_pixelpipe_t *const pipe);
52
53// Assemble + factor the diffusion system over the hole pixels (see _sp_pde_assemble).
54// Returns NULL when the system is too big for the direct solver, not positive definite, or
55// on out-of-memory -- callers keep their previous iterative solver as fallback. The returned
56// factor is reused for all three colour channels (same hole, same operator, different
57// right-hand sides). *perm_out maps permuted unknown -> grid index.
58//
59// MATHS BRIDGE -- Cholesky factor of A = diag(d) + lambda*Op for the step-7 all-clip core chroma
60// solve; order 1 -> A = lambda_solid*I - Delta (screened-Poisson, E_chrominance). One factorization
61// serves the three channel right-hand sides (r_R, r_G, r_B share the same A, differing only in the
62// Dirichlet rim data and the flat-colour target).
63_sp_chol_t *_sp_pde_factor(const uint8_t *const restrict hole, const float *const restrict diffusion,
64 const int order, const float lambda, const int region_w, const int region_h,
65 int **perm_out, int *n_unknowns_out, const dt_dev_pixelpipe_t *pipe);
66
67// Exact-solver counterpart of _region_pde_solve for a prebuilt Cholesky factor: same
68// right-hand-side construction as the conjugate-gradient prologue (the fixed boundary values
69// are pushed through the diffusion operator into the right-hand side), then the two
70// triangular solves. b/t1/t2/sc are scratch buffers.
71//
72// MATHS BRIDGE -- solves (lambda*I - Delta) r = lambda_solid*r_target on the hole with r fixed on
73// the rim (Dirichlet r|dOmega = r_valid): the screened-Poisson chroma fill of the all-clip core
74// (article step 7 / §"Chrominance, by diffusion"). The Dirichlet data enters the RHS by embedding
75// the boundary values, applying the operator, and subtracting -- the standard elimination of fixed
76// unknowns into the right-hand side. r_target = the mean valid chromaticity (flat-colour target).
77void _sp_pde_solve(const _sp_chol_t *const factor, const int *const restrict perm_grid,
78 float *const restrict field, const uint8_t *const restrict hole,
79 const float *const restrict diffusion, const float *const restrict target,
80 const float *const restrict source, const int order, const float lambda, const int region_w,
81 const int region_h, double *const restrict rhs, float *const restrict embedded,
82 float *const restrict operator_out, float *const restrict scratch);
83
84// Matrix-free conjugate-gradient solve (iterative fallback solver: repeats operator-vector
85// products until the residual error is small, never forming the matrix explicitly) of
86// (diag(d) + lam*Op) u = diag(d)*target + source on the `hole` pixels, with u held fixed at
87// its current value on non-hole pixels (the boundary condition). Op is the symmetric
88// positive definite diffusion operator (minus the Laplacian for order 1, the biharmonic for
89// order 2). d/target/source may be NULL. u is updated in place on the hole. r,p,ap,t1,t2 are
90// single-channel scratch of size rw*rh. (A per-pixel `source` lets us solve the Poisson step
91// Delta u = w as (-Delta) u = -w, the second half of the mixed biharmonic formulation.)
92//
93// MATHS BRIDGE -- same linear system as _sp_pde_solve, iterative instead of direct: the fallback
94// when the all-clip core exceeds DT_HL_SPARSE_MAX unknowns. Order 1 = the screened-Poisson chroma
95// fill (lambda*I - Delta) r = lambda_solid*r_target of step 7 (article §"Chrominance, by
96// diffusion"); the CG never forms A, only its action A u = diag(d)*u + lambda*Op(u). Because the
97// float CG stops at a relative tolerance it is inexact where the direct solve is exact -- the
98// direct path is preferred, this is only the large-core fallback.
99void _region_pde_solve(float *const restrict field, const uint8_t *const restrict hole,
100 const float *const restrict diffusion, const float *const restrict target,
101 const float *const restrict source, const int order, const float lambda, const int region_w,
102 const int region_h, float *const restrict residual, float *const restrict search_dir,
103 float *const restrict operator_dir, float *const restrict embedded,
104 float *const restrict scratch, const int maxiter);
105
106#ifdef HAVE_OPENCL
107static inline _sp_chol_cl_kernels_t _hl_sp_chol_kernels(void *gd_void)
108{
109 const dt_iop_highlights_global_data_t *const global_data = (const dt_iop_highlights_global_data_t *)gd_void;
113 return kernels;
114}
115#endif
116
117#if defined(HAVE_OPENCL) && DT_HL_SPARSE_SOLVE
118// single-channel gaussian blur on the device (used to feather the joint-core composite weight)
119cl_int _region_blur1_cl(const int devid, cl_mem in, cl_mem out, const int region_w, const int region_h,
120 const float sigma);
121
122// Matrix-free conjugate gradient (iterative solver) on the device, mirroring
123// _region_pde_solve for the joint core's screened-harmonic chroma (order 1, lam 1, constant
124// reaction strength d and flat target): the fallback when the all-clip core exceeds
125// DT_HL_SPARSE_MAX unknowns and the direct factorization is off the table.
126//
127// MATHS BRIDGE -- Step 7 E_chrominance screened-Poisson (article §"Chrominance, by diffusion"):
128// solves (dscalar*I - Delta) r = dscalar*tscalar on the hole, r|dOmega = r_valid (Dirichlet), where
129// dscalar = lambda_solid = solid_color^2*4 (the flat-colour reaction strength) and tscalar = the
130// mean valid chromaticity r_target. A = d*I - Delta is never formed; each iteration applies its
131// action A p = d*p + Op(p) via the hl_cg_* kernels (Op = -Delta, order 1) in highlights_sparse.cl.
132// Dot products accumulate in double precision on the device (64-bit-float program); only the
133// 2 KB of reduction partials cross the bus per iteration. The CPU conjugate gradient is
134// itself OpenMP-summation-order nondeterministic, so tolerance-level (not bit-exact) parity
135// is the honest target here. Any change here must be mirrored in _region_pde_solve and
136// re-validated with the HL_CORECL_TEST self-test (_joint_core_stage_cl_selftest).
137cl_int _region_pde_cg_cl(const int devid, void *gd_void, cl_mem solution, cl_mem hole, const int region_w,
138 const int region_h, const float dscalar, const float tscalar, const int maxiter);
139#endif
const dt_colormatrix_t dt_aligned_pixel_t out
void _sp_pde_solve(const _sp_chol_t *const factor, const int *const restrict perm_grid, float *const restrict field, const uint8_t *const restrict hole, const float *const restrict diffusion, const float *const restrict target, const float *const restrict source, const int order, const float lambda, const int region_w, const int region_h, double *const restrict rhs, float *const restrict embedded, float *const restrict operator_out, float *const restrict scratch)
Definition pde.c:334
_sp_chol_t * _sp_pde_factor(const uint8_t *const restrict hole, const float *const restrict diffusion, const int order, const float lambda, const int region_w, const int region_h, int **perm_out, int *n_unknowns_out, const dt_dev_pixelpipe_t *pipe)
Definition pde.c:308
static _sp_chol_cl_kernels_t _hl_sp_chol_kernels(void *gd_void)
Definition pde.h:107
int _sp_pde_assemble(const uint8_t *const restrict hole, const float *const restrict diffusion, const float diffusion_const, const int order, const float lambda, const int region_w, const int region_h, int **matrix_col_ptr_out, int **matrix_row_index_out, double **matrix_values_out, int **perm_grid_out, int *n_unknowns_out, const dt_dev_pixelpipe_t *const pipe)
Definition pde.c:173
void _region_pde_solve(float *const restrict field, const uint8_t *const restrict hole, const float *const restrict diffusion, const float *const restrict target, const float *const restrict source, const int order, const float lambda, const int region_w, const int region_h, float *const restrict residual, float *const restrict search_dir, float *const restrict operator_dir, float *const restrict embedded, float *const restrict scratch, const int maxiter)
Definition pde.c:368
const float factor
Definition pdf.h:90
const float sigma