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"
21#endif
22// our includes go first:
23#include "bauhaus/bauhaus.h"
24#include "develop/imageop.h"
25#include "develop/imageop_gui.h"
26#include "develop/tiling.h"
28#include "gui/gtk.h"
29#include "iop/iop_api.h"
30
31#include <gtk/gtk.h>
32#include <stdlib.h>
33
35
37{
38 float C_c; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 1.0 $DESCRIPTION: "input cyan"
39 float C_m; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input magenta"
40 float C_y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input yellow"
41 float C_o; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "cyan offset"
42 float M_c; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input cyan"
43 float M_m; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 1.0 $DESCRIPTION: "input magenta"
44 float M_y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input yellow"
45 float M_o; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "magenta offset"
46 float Y_c; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input cyan"
47 float Y_m; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "input magenta"
48 float Y_y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 1.0 $DESCRIPTION: "input yellow"
49 float Y_o; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "yellow offset"
50 float diffusion; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "sharpening"
51 float regularization; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "edge avoiding"
52 int iterations; // $MIN: 1 $MAX: 32 $DEFAULT: 1 $DESCRIPTION: "iterations"
54
62
69
70
71// this returns a translatable name
72const char * name()
73{
74 // make sure you put all your translatable strings into _() !
75 return _("scan restore");
76}
77
78// some additional flags (self explanatory i think):
83
84// where does it appear in the gui?
86{
87 return IOP_GROUP_FILM;
88}
89
94
95#define SET_PIXEL(array, x, y, z, w) \
96 array[0] = x; \
97 array[1] = y; \
98 array[2] = z; \
99 array[3] = w;
100
102{
105
106 SET_PIXEL(d->CMY[0], p->C_c, p->C_m, p->C_y, 0.f);
107 SET_PIXEL(d->CMY[1], p->M_c, p->M_m, p->M_y, 0.f);
108 SET_PIXEL(d->CMY[2], p->Y_c, p->Y_m, p->Y_y, 0.f);
109 SET_PIXEL(d->offsets, p->M_o, p->C_o, p->Y_o, 0.f);
110
111 d->diffusion = p->diffusion;
112 d->iterations = p->iterations;
113}
114
120
122{
123 dt_free_align(piece->data);
124 piece->data = NULL;
125}
126
127void 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)
128{
129 const dt_iop_roi_t *const roi_in = &piece->roi_in;
130 const dt_iop_roi_t *const roi_out = &piece->roi_out;
131 tiling->factor = 2.0f; // input buffer + output buffer; increase if additional memory allocated
132 tiling->factor_cl = 2.0f; // same, but for OpenCL code path running on GPU
133 tiling->maxbuf = 1.0f; // largest buffer needed regardless of how tiling splits up the processing
134 tiling->maxbuf_cl = 1.0f; // same, but for running on GPU
135 tiling->overhead = 0; // number of bytes of fixed overhead
136 tiling->overlap = 1; // how many pixels do we need to access from the neighboring tile?
137 tiling->xalign = 1;
138 tiling->yalign = 1;
139}
140
142int 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)
143{
144 const dt_iop_roi_t *const roi_in = &piece->roi_in;
145 const dt_iop_roi_t *const roi_out = &piece->roi_out;
147 const float scale = dt_dev_get_module_scale(pipe, roi_in);
148
149 const float *const restrict in = (const float *const restrict)ivoid;
150 float *const restrict out = (float *const restrict)ovoid;
151 float *const restrict cmy = dt_pixelpipe_cache_alloc_align_float_cache(roi_in->width * roi_in->height * 4, 0);
152 if(IS_NULL_PTR(cmy)) return 1;
153
154 const float sharpen = d->diffusion / (scale * scale) / d->iterations;
156 for(int i = 0; i < roi_out->height; i++)
157 for(int j = 0; j < roi_out->width; j++)
158 {
159 const size_t index = ((i * roi_out->width) + j) * 4;
160 for_four_channels(c, aligned(in, cmy)) cmy[index + c] = 1.f - in[index + c];
161 }
162
163 float *restrict temp_in = cmy;
164 float *restrict temp_out = out;
165
166 for(int iter = 0; iter < d->iterations; iter++)
167 {
168 if(iter == 0)
169 {
170 temp_in = cmy;
171 temp_out = out;
172 }
173 else if(iter % 2 != 0)
174 {
175 temp_in = out;
176 temp_out = cmy;
177 }
178 else
179 {
180 temp_in = cmy;
181 temp_out = out;
182 }
184 for(int i = 0; i < roi_out->height; i++)
185 for(int j = 0; j < roi_out->width; j++)
186 {
187 const size_t index = ((i * roi_out->width) + j) * 4;
188 dt_aligned_pixel_t temp1, temp2;
189 for_four_channels(c, aligned(temp1, temp_in)) temp1[c] = temp_in[index + c] - d->offsets[c];
190 dot_product(temp1, d->CMY, temp2);
191 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);
192 }
194 for(int i = 0; i < roi_out->height; i++)
195 for(int j = 0; j < roi_out->width; j++)
196 {
197 const size_t index = ((i * roi_out->width) + j) * 4;
198
199 // see in https://eng.aurelienpierre.com/2021/03/rotation-invariant-laplacian-for-2d-grids/#Second-order-isotropic-finite-differences
200 // for references (Oono & Puri)
201 const float kernel[3][3] = { { 0.25f, 0.5f, 0.25f }, { 0.5f, -3.f, 0.5f }, { 0.25f, 0.5f, 0.25f } };
202
203 dt_aligned_pixel_t laplacian = { 0.f };
204
205 for(int ii = 0; ii < 3; ii++)
206 for(int jj = 0; jj < 3; jj++)
207 {
208 const int row = CLAMP(i + ii - 1, 0, roi_in->height - 1);
209 const int column = CLAMP(j + jj - 1, 0, roi_in->width - 1);
210 const size_t idx = ((row * roi_in->width) + column) * 4;
211 for_each_channel(c) laplacian[c] += temp_in[idx + c] * kernel[ii][jj];
212 }
213
214 for_each_channel(c) temp_out[index + c] = CLAMP(temp_in[index + c] - laplacian[c] * sharpen, 0.f, 1.f);
215 }
216 }
218 for(int i = 0; i < roi_out->height; i++)
219 for(int j = 0; j < roi_out->width; j++)
220 {
221 const size_t index = ((i * roi_out->width) + j) * 4;
222 for_four_channels(c, aligned(temp_out, out)) out[index + c] = 1.f - temp_out[index + c];
223 }
224
226 return 0;
227}
228
229
230#if 0
232{
235
236 // This automatically gets called when any of the color pickers set up with
237 // dt_color_picker_new in gui_init is used. If there is more than one,
238 // check which one is active first.
239 if(picker == g->factor)
240 {
241 p->factor = self->picked_color[1];
242 }
243
245 dt_control_queue_redraw_widget(self->widget);
246}
247#endif
248
250{
252 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
253
254 g->C_c = dt_bauhaus_slider_from_params(self, "C_c");
255 g->C_m = dt_bauhaus_slider_from_params(self, "C_m");
256 g->C_y = dt_bauhaus_slider_from_params(self, "C_y");
257 g->C_o = dt_bauhaus_slider_from_params(self, "C_o");
258
259 g->M_c = dt_bauhaus_slider_from_params(self, "M_c");
260 g->M_m = dt_bauhaus_slider_from_params(self, "M_m");
261 g->M_y = dt_bauhaus_slider_from_params(self, "M_y");
262 g->M_o = dt_bauhaus_slider_from_params(self, "M_o");
263
264 g->Y_c = dt_bauhaus_slider_from_params(self, "Y_c");
265 g->Y_m = dt_bauhaus_slider_from_params(self, "Y_m");
266 g->Y_y = dt_bauhaus_slider_from_params(self, "Y_y");
267 g->Y_o = dt_bauhaus_slider_from_params(self, "Y_o");
268
269 g->diffusion = dt_bauhaus_slider_from_params(self, "diffusion");
270 g->regularization = dt_bauhaus_slider_from_params(self, "regularization");
271 g->iterations = dt_bauhaus_slider_from_params(self, "iterations");
272}
273
274// clang-format off
275// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
276// vim: shiftwidth=2 expandtab tabstop=2 cindent
277// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
278// 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:453
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RGB
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:906
darktable_t darktable
Definition darktable.c:181
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define for_each_channel(_var,...)
Definition darktable.h:662
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:447
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define for_four_channels(_var,...)
Definition darktable.h:664
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#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 darktable.h:281
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:131
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_GROUP_FILM
Definition imageop.h:138
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
void *const ovoid
static float kernel(const float *x, const float *y)
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
Definition matrices.h:33
float dt_aligned_pixel_t[4]
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)
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_iop_module_t *void * data
GtkWidget * widget
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:307
dt_aligned_pixel_t offsets
Region of interest passed through the pixelpipe.
Definition imageop.h:72