Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
restorescans.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2022-2023, 2025-2026 Aurélien PIERRE.
4 Copyright (C) 2024 Alynx Zhou.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifdef HAVE_CONFIG_H
20#include "config.h"
22#endif
23// our includes go first:
24#include "develop/imageop.h"
25#include "develop/imageop_gui.h"
26#include "develop/tiling.h"
27#include "iop/iop_api.h"
28
29#include <gtk/gtk.h>
30#include <stdlib.h>
31
33
35{
36 float C_c; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 1.0 $DESCRIPTION: "input cyan"
37 float C_m; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input magenta"
38 float C_y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input yellow"
39 float C_o; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "cyan offset"
40 float M_c; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input cyan"
41 float M_m; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 1.0 $DESCRIPTION: "input magenta"
42 float M_y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input yellow"
43 float M_o; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "magenta offset"
44 float Y_c; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input cyan"
45 float Y_m; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input magenta"
46 float Y_y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 1.0 $DESCRIPTION: "input yellow"
47 float Y_o; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "yellow offset"
48 float diffusion; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "sharpening"
49 float regularization; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "edge avoiding"
50 int iterations; // $MIN: 1 $MAX: 32 $DEFAULT: 1 $DESCRIPTION: "iterations"
52
60
67
68
69// this returns a translatable name
70const char * name()
71{
72 // make sure you put all your translatable strings into _() !
73 return _("scan restore");
74}
75
76// some additional flags (self explanatory i think):
81
82// where does it appear in the gui?
84{
85 return IOP_GROUP_FILM;
86}
87
92
93#define SET_PIXEL(array, x, y, z, w) \
94 array[0] = x; \
95 array[1] = y; \
96 array[2] = z; \
97 array[3] = w;
98
100{
103
104 SET_PIXEL(d->CMY[0], p->C_c, p->C_m, p->C_y, 0.f);
105 SET_PIXEL(d->CMY[1], p->M_c, p->M_m, p->M_y, 0.f);
106 SET_PIXEL(d->CMY[2], p->Y_c, p->Y_m, p->Y_y, 0.f);
107 SET_PIXEL(d->offsets, p->M_o, p->C_o, p->Y_o, 0.f);
108
109 d->diffusion = p->diffusion;
110 d->iterations = p->iterations;
111}
112
118
120{
121 dt_free_align(piece->data);
122 piece->data = NULL;
123}
124
125void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
126{
127 const dt_iop_roi_t *const roi_in = &piece->roi_in;
128 const dt_iop_roi_t *const roi_out = &piece->roi_out;
129 tiling->factor = 2.0f; // input buffer + output buffer; increase if additional memory allocated
130 tiling->factor_cl = 2.0f; // same, but for OpenCL code path running on GPU
131 tiling->maxbuf = 1.0f; // largest buffer needed regardless of how tiling splits up the processing
132 tiling->maxbuf_cl = 1.0f; // same, but for running on GPU
133 tiling->overhead = 0; // number of bytes of fixed overhead
134 tiling->overlap = 1; // how many pixels do we need to access from the neighboring tile?
135 tiling->xalign = 1;
136 tiling->yalign = 1;
137}
138
140int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
141{
142 const dt_iop_roi_t *const roi_in = &piece->roi_in;
143 const dt_iop_roi_t *const roi_out = &piece->roi_out;
145 const float scale = dt_dev_get_module_scale(pipe, roi_in);
146
147 const float *const restrict in = (const float *const restrict)ivoid;
148 float *const restrict out = (float *const restrict)ovoid;
149 float *const restrict cmy = dt_pixelpipe_cache_alloc_align_float_cache(roi_in->width * roi_in->height * 4, 0);
150 if(IS_NULL_PTR(cmy)) return 1;
151
152 const float sharpen = d->diffusion / (scale * scale) / d->iterations;
154 for(int i = 0; i < roi_out->height; i++)
155 for(int j = 0; j < roi_out->width; j++)
156 {
157 const size_t index = ((i * roi_out->width) + j) * 4;
158 for_four_channels(c, aligned(in, cmy)) cmy[index + c] = 1.f - in[index + c];
159 }
160
161 float *restrict temp_in = cmy;
162 float *restrict temp_out = out;
163
164 for(int iter = 0; iter < d->iterations; iter++)
165 {
166 if(iter == 0)
167 {
168 temp_in = cmy;
169 temp_out = out;
170 }
171 else if(iter % 2 != 0)
172 {
173 temp_in = out;
174 temp_out = cmy;
175 }
176 else
177 {
178 temp_in = cmy;
179 temp_out = out;
180 }
182 for(int i = 0; i < roi_out->height; i++)
183 for(int j = 0; j < roi_out->width; j++)
184 {
185 const size_t index = ((i * roi_out->width) + j) * 4;
186 dt_aligned_pixel_t temp1, temp2;
187 for_four_channels(c, aligned(temp1, temp_in)) temp1[c] = temp_in[index + c] - d->offsets[c];
188 dot_product(temp1, d->CMY, temp2);
189 for_four_channels(c, aligned(temp2, temp_in)) temp_in[index + c] = CLAMP(((d->iterations - 1) * temp_in[index + c] + temp2[c]) / (d->iterations), 0.f, 1.f);
190 }
192 for(int i = 0; i < roi_out->height; i++)
193 for(int j = 0; j < roi_out->width; j++)
194 {
195 const size_t index = ((i * roi_out->width) + j) * 4;
196
197 // see in https://eng.aurelienpierre.com/2021/03/rotation-invariant-laplacian-for-2d-grids/#Second-order-isotropic-finite-differences
198 // for references (Oono & Puri)
199 const float kernel[3][3] = { { 0.25f, 0.5f, 0.25f }, { 0.5f, -3.f, 0.5f }, { 0.25f, 0.5f, 0.25f } };
200
201 dt_aligned_pixel_t laplacian = { 0.f };
202
203 for(int ii = 0; ii < 3; ii++)
204 for(int jj = 0; jj < 3; jj++)
205 {
206 const int row = CLAMP(i + ii - 1, 0, roi_in->height - 1);
207 const int column = CLAMP(j + jj - 1, 0, roi_in->width - 1);
208 const size_t idx = ((row * roi_in->width) + column) * 4;
209 for_each_channel(c) laplacian[c] += temp_in[idx + c] * kernel[ii][jj];
210 }
211
212 for_each_channel(c) temp_out[index + c] = CLAMP(temp_in[index + c] - laplacian[c] * sharpen, 0.f, 1.f);
213 }
214 }
216 for(int i = 0; i < roi_out->height; i++)
217 for(int j = 0; j < roi_out->width; j++)
218 {
219 const size_t index = ((i * roi_out->width) + j) * 4;
220 for_four_channels(c, aligned(temp_out, out)) out[index + c] = 1.f - temp_out[index + c];
221 }
222
224 return 0;
225}
226
227
228#if 0
230{
233
234 // This automatically gets called when any of the color pickers set up with
235 // dt_color_picker_new in gui_init is used. If there is more than one,
236 // check which one is active first.
237 if(picker == g->factor)
238 {
239 p->factor = self->picked_color[1];
240 }
241
242 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
243 dt_control_queue_redraw_widget(self->widget);
244}
245#endif
246
248{
250 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
251
252 g->C_c = dt_bauhaus_slider_from_params(self, "C_c");
253 g->C_m = dt_bauhaus_slider_from_params(self, "C_m");
254 g->C_y = dt_bauhaus_slider_from_params(self, "C_y");
255 g->C_o = dt_bauhaus_slider_from_params(self, "C_o");
256
257 g->M_c = dt_bauhaus_slider_from_params(self, "M_c");
258 g->M_m = dt_bauhaus_slider_from_params(self, "M_m");
259 g->M_y = dt_bauhaus_slider_from_params(self, "M_y");
260 g->M_o = dt_bauhaus_slider_from_params(self, "M_o");
261
262 g->Y_c = dt_bauhaus_slider_from_params(self, "Y_c");
263 g->Y_m = dt_bauhaus_slider_from_params(self, "Y_m");
264 g->Y_y = dt_bauhaus_slider_from_params(self, "Y_y");
265 g->Y_o = dt_bauhaus_slider_from_params(self, "Y_o");
266
267 g->diffusion = dt_bauhaus_slider_from_params(self, "diffusion");
268 g->regularization = dt_bauhaus_slider_from_params(self, "regularization");
269 g->iterations = dt_bauhaus_slider_from_params(self, "iterations");
270}
271
272// clang-format off
273// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
274// vim: shiftwidth=2 expandtab tabstop=2 cindent
275// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
276// clang-format on
#define TRUE
Definition ashift_lsd.c:162
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition basicadj.c:462
@ IOP_CS_RGB
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
Definition control.c:969
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:134
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_GROUP_FILM
Definition imageop.h:157
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
static float kernel(const float *x, const float *y)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
Definition matrices.h:34
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
int default_group()
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
const char * name()
void gui_init(dt_iop_module_t *self)
#define SET_PIXEL(array, x, y, z, w)
void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
int flags()
void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_each_channel(_var,...)
Definition simd.h:87
#define for_four_channels(_var,...)
Definition simd.h:89
struct dt_iop_module_t *void * data
dt_iop_params_t * params
Definition imageop.h:333
dt_aligned_pixel_t offsets
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
#define DT_GUI_BOX_SPACING