Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
focus.h
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2013-2014 johannes hanika.
4 Copyright (C) 2013-2016 Tobias Ellinghaus.
5 Copyright (C) 2014 Pedro Côrte-Real.
6 Copyright (C) 2014-2016 Roman Lebedev.
7 Copyright (C) 2018 Edgardo Hoszowski.
8 Copyright (C) 2019 Aldric Renaudin.
9 Copyright (C) 2019 Andreas Schneider.
10 Copyright (C) 2019, 2023-2026 Aurélien PIERRE.
11 Copyright (C) 2019 luzpaz.
12 Copyright (C) 2020 Hanno Schwalm.
13 Copyright (C) 2020 Pascal Obry.
14 Copyright (C) 2022 Martin Bařinka.
15
16 darktable is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 darktable is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with darktable. If not, see <http://www.gnu.org/licenses/>.
28*/
29
30#pragma once
31
32#include "common/image_cache.h"
33#include "develop/develop.h"
34
35typedef struct dt_focus_cluster_t
36{
37 int64_t n;
38 float x, y, x2, y2;
39 float thrs;
41
42#define gbuf(BUF, A, B) ((BUF)[4 * (width * ((B)) + ((A))) + ch])
43#define FOCUS_THRS 10
44#define CHANNEL 1
45
46static inline uint8_t _to_uint8(int i)
47{
48 return (uint8_t)CLAMP(i + 127, 0, 255);
49}
50static inline int _from_uint8(uint8_t i)
51{
52 return i - 127;
53}
54static inline void _dt_focus_cdf22_wtf(uint8_t *buf, const int l, const int width, const int height)
55{
56 const int ch = CHANNEL;
57
58 const int step = 1 << l;
59 const int st = step / 2;
60
61#ifdef _OPENMP
62#pragma omp parallel for default(none) \
63 dt_omp_firstprivate(height, st, step, width, ch) \
64 shared(buf) \
65 schedule(static)
66#endif
67 for(int j = 0; j < height; j++)
68 {
69 // rows
70 // predict, get detail
71 int i = st;
72 for(; i < width - st; i += step) /*for(ch=0; ch<3; ch++)*/
73 gbuf(buf, i, j)
74 = _to_uint8((int)gbuf(buf, i, j) - ((int)gbuf(buf, i - st, j) + (int)gbuf(buf, i + st, j)) / 2);
75 if(i < width) /*for(ch=0; ch<3; ch++)*/
76 gbuf(buf, i, j) = _to_uint8(gbuf(buf, i, j) - gbuf(buf, i - st, j));
77 // update coarse
78 /*for(ch=0; ch<3; ch++)*/ gbuf(buf, 0, j) += _from_uint8(gbuf(buf, st, j)) / 2;
79 for(i = step; i < width - st; i += step) /*for(ch=0; ch<3; ch++)*/
80 gbuf(buf, i, j) += (_from_uint8(gbuf(buf, i - st, j)) + _from_uint8(gbuf(buf, i + st, j))) / 4;
81 if(i < width) /*for(ch=0; ch<3; ch++)*/
82 gbuf(buf, i, j) += _from_uint8(gbuf(buf, i - st, j)) / 2;
83 }
84#ifdef _OPENMP
85#pragma omp parallel for default(none) \
86 dt_omp_firstprivate(height, st, step, width, ch) \
87 shared(buf) \
88 schedule(static)
89#endif
90 for(int i = 0; i < width; i++)
91 {
92 // cols
93 int j = st;
94 // predict, get detail
95 for(; j < height - st; j += step) /*for(ch=0; ch<3; ch++)*/
96 gbuf(buf, i, j)
97 = _to_uint8((int)gbuf(buf, i, j) - ((int)gbuf(buf, i, j - st) + (int)gbuf(buf, i, j + st)) / 2);
98 if(j < height) /*for(int ch=0; ch<3; ch++)*/
99 gbuf(buf, i, j) = _to_uint8((int)gbuf(buf, i, j) - (int)gbuf(buf, i, j - st));
100 // update
101 /*for(ch=0; ch<3; ch++)*/ gbuf(buf, i, 0) += _from_uint8(gbuf(buf, i, st)) / 2;
102 for(j = step; j < height - st; j += step) /*for(ch=0; ch<3; ch++)*/
103 gbuf(buf, i, j) += (_from_uint8(gbuf(buf, i, j - st)) + _from_uint8(gbuf(buf, i, j + st))) / 4;
104 if(j < height) /*for(int ch=0; ch<3; ch++)*/
105 gbuf(buf, i, j) += _from_uint8(gbuf(buf, i, j - st)) / 2;
106 }
107}
108
109static void _dt_focus_update(dt_focus_cluster_t *f, int frows, int fcols, int i, int j, int wd, int ht,
110 int diff)
111{
112 const int32_t thrs = FOCUS_THRS;
113 if(diff > thrs)
114 {
115 int fx = i / (float)wd * fcols;
116 int fy = j / (float)ht * frows;
117 int fi = fcols * fy + fx;
118#ifdef _OPENMP
119#pragma omp atomic
120#endif
121 f[fi].x += i;
122#ifdef _OPENMP
123#pragma omp atomic
124#endif
125 f[fi].y += j;
126#ifdef _OPENMP
127#pragma omp atomic
128#endif
129 f[fi].x2 += (float)i * i;
130#ifdef _OPENMP
131#pragma omp atomic
132#endif
133 f[fi].y2 += (float)j * j;
134#ifdef _OPENMP
135#pragma omp atomic
136#endif
137 f[fi].n++;
138#ifdef _OPENMP
139#pragma omp atomic
140#endif
141 f[fi].thrs += diff;
142 }
143}
144
145
146// read 8-bit buffer and create focus clusters from it
147static void dt_focus_create_clusters(dt_focus_cluster_t *focus, int frows, int fcols, uint8_t *buffer,
148 int buffer_width, int buffer_height)
149{
150 // mark in-focus pixels:
151 const int wd = buffer_width;
152 const int ht = buffer_height;
153 const int fs = frows * fcols;
154 // two-stage cdf 2/2 wavelet transform, use HH1 and HH2 to detect very sharp and sharp spots:
155 // pretend we already did the first step (coarse will stay in place, maybe even where the pre-demosaic
156 // sample was at)
157 _dt_focus_cdf22_wtf(buffer, 2, wd, ht);
158 // go through HH1 and detect sharp clusters:
159 memset(focus, 0, sizeof(dt_focus_cluster_t) * fcols * frows);
160#ifdef _OPENMP
161#pragma omp parallel for schedule(static) default(shared)
162#endif
163 for(int j = 0; j < ht - 1; j += 4)
164 for(int i = 0; i < wd - 1; i += 4)
165 {
166 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
167 abs(_from_uint8(buffer[4 * ((j + 2) * wd + i) + CHANNEL])));
168 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
169 abs(_from_uint8(buffer[4 * (j * wd + i + 2) + CHANNEL])));
170 }
171
172#if 1 // second pass, HH2
173 int num_clusters = 0;
174 for(int k = 0; k < fs; k++)
175 if(focus[k].n * 4 > wd * ht / (float)fs * 0.01f) num_clusters++;
176 if(num_clusters < 1)
177 {
178 memset(focus, 0, sizeof(dt_focus_cluster_t) * fs);
179 _dt_focus_cdf22_wtf(buffer, 3, wd, ht);
180#ifdef _OPENMP
181#pragma omp parallel for schedule(static) default(shared)
182#endif
183 for(int j = 0; j < ht - 1; j += 8)
184 {
185 for(int i = 0; i < wd - 1; i += 8)
186 {
187 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
188 1.5 * abs(_from_uint8(buffer[4 * ((j + 4) * wd + i) + CHANNEL])));
189 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
190 1.5 * abs(_from_uint8(buffer[4 * (j * wd + i + 4) + CHANNEL])));
191 }
192 }
193 num_clusters = 0;
194 for(int k = 0; k < fs; k++)
195 {
196 if(focus[k].n * 6.0f > wd * ht / (float)fs * 0.01f)
197 {
198 focus[k].n *= -1;
199 num_clusters++;
200 }
201 }
202 }
203#endif
204#undef CHANNEL
205
206#if 0 // simple high pass filter, doesn't work on slightly unsharp/high iso images
207 memset(focus, 0, sizeof(dt_focus_cluster_t)*fs);
208#ifdef _OPENMP
209#pragma omp parallel for schedule(static) default(shared)
210#endif
211 for(int j=1;j<ht-1;j++)
212 {
213 int index = 4*j*wd+4;
214 for(int i=1;i<wd-1;i++)
215 {
216 int32_t diff = 4*buffer[index+1]
217 - buffer[index-4+1]
218 - buffer[index+4+1]
219 - buffer[index-4*wd+1]
220 - buffer[index+4*wd+1];
221 _dt_focus_update(focus, frows, fcols, i, j, wd, ht, abs(diff));
222 index += 4;
223 }
224 }
225#endif
226 // normalize data in clusters:
227 for(int k = 0; k < fs; k++)
228 {
229 focus[k].thrs /= fabsf((float)focus[k].n);
230 focus[k].x /= fabsf((float)focus[k].n);
231 focus[k].x2 /= fabsf((float)focus[k].n);
232 focus[k].y /= fabsf((float)focus[k].n);
233 focus[k].y2 /= fabsf((float)focus[k].n);
234 }
235}
236
237static void dt_focus_draw_clusters(cairo_t *cr, int width, int height, int32_t imgid, int buffer_width,
238 int buffer_height, dt_focus_cluster_t *focus, int frows, int fcols,
239 float full_zoom, float full_x, float full_y)
240{
241 const int fs = frows * fcols;
242 cairo_save(cr);
243 cairo_translate(cr, width / 2.0, height / 2.0f);
244
245 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
246 dt_image_t image = *img;
248
249 // FIXME: get those from rawprepare IOP somehow !!!
250 int wd = buffer_width + image.crop_x;
251 int ht = buffer_height + image.crop_y;
252
253 // array with cluster positions
254 float *pos = malloc(fs * 6 * sizeof(float));
255 float *offx = pos + fs * 2, *offy = pos + fs * 4;
256
257 for(int k = 0; k < fs; k++)
258 {
259 const float stddevx = sqrtf(focus[k].x2 - focus[k].x * focus[k].x);
260 const float stddevy = sqrtf(focus[k].y2 - focus[k].y * focus[k].y);
261
262 // FIXME: get those from rawprepare IOP somehow !!!
263 const float x = focus[k].x + image.crop_x;
264 const float y = focus[k].y + image.crop_y;
265
266 pos[2 * k + 0] = x;
267 pos[2 * k + 1] = y;
268 offx[2 * k + 0] = x + stddevx;
269 offx[2 * k + 1] = y;
270 offy[2 * k + 0] = x;
271 offy[2 * k + 1] = y + stddevy;
272 }
273
274 // could use dt_image_altered() here, but it ignores flip module
275 {
276 dt_develop_t dev;
277 dt_dev_init(&dev, 0);
278 dt_dev_load_image(&dev, imgid);
280 const int res = dt_dev_pixelpipe_init_dummy(&pipe);
281 if(res)
282 {
283 // set mem pointer to 0, won't be used.
286 dt_dev_pixelpipe_synch_all(&pipe, &dev);
287 dt_dev_pixelpipe_get_roi_out(&pipe, &dev, pipe.iwidth, pipe.iheight, &pipe.processed_width,
288 &pipe.processed_height);
289 dt_dev_distort_transform_plus(&dev, &pipe, 0.f, DT_DEV_TRANSFORM_DIR_ALL, pos, fs * 3);
291 wd = pipe.processed_width;
292 ht = pipe.processed_height;
293 }
294 dt_dev_cleanup(&dev);
295 }
296
297 const int32_t tb = darktable.develop->roi.border_size;
298 const float scale = fminf((width - 2 * tb) / (float)wd, (height - 2 * tb) / (float)ht) * full_zoom;
299 cairo_scale(cr, scale, scale);
300 float fx = 0.0f;
301 float fy = 0.0f;
302 if(full_zoom > 1.0f)
303 {
304 // we want to be sure the image stay in the window
305 fx = fminf((wd * scale - width) / 2, fabsf(full_x));
306 if(full_x < 0) fx = -fx;
307 if(wd * scale <= width) fx = 0;
308 fy = fminf((ht * scale - height) / 2, fabsf(full_y));
309 if(full_y < 0) fy = -fy;
310 if(ht * scale <= height) fy = 0;
311 }
312
313 cairo_translate(cr, -wd / 2.0f + fx / scale * darktable.gui->ppd, -ht / 2.0f + fy / scale * darktable.gui->ppd);
314
315 cairo_rectangle(cr, 0, 0, wd, ht);
316 cairo_clip(cr);
317
318 double dashes[] = { DT_PIXEL_APPLY_DPI(5.), DT_PIXEL_APPLY_DPI(5.) };
319 const int ndash = sizeof(dashes) / sizeof(dashes[0]);
320 double offset = 0.0f;
321 cairo_set_dash(cr, dashes, ndash, offset);
322
323 // draw clustered focus regions
324 for(int k = 0; k < fs; k++)
325 {
326 const float intens = (focus[k].thrs - FOCUS_THRS) / FOCUS_THRS;
327 const float col = fminf(1.0f, intens);
328 int draw = 0;
329 if(focus[k].n * 4.0f > buffer_width * buffer_height / (float)fs * 0.01f)
330 draw = 1;
331 else if(-focus[k].n * 6.0f > buffer_width * buffer_height / (float)fs * 0.01f)
332 draw = 2;
333 if(draw)
334 {
335 for(int i = 0; i < 2; i++)
336 {
337 if(i)
338 {
339 if(draw == 2)
340 cairo_set_source_rgb(cr, .1f, .1f, col);
341 else
342 cairo_set_source_rgb(cr, col, .1f, .1f);
343 cairo_set_dash(cr, dashes, ndash, dashes[0]);
344 }
345 else
346 {
347 cairo_set_source_rgb(cr, .1f, .1f, .1f);
348 cairo_set_dash(cr, dashes, ndash, 0);
349 }
350 cairo_move_to(cr, offx[2 * k + 0], offx[2 * k + 1]);
351 cairo_curve_to(cr, -pos[2 * k + 0] + offx[2 * k + 0] + offy[2 * k + 0],
352 -pos[2 * k + 1] + offx[2 * k + 1] + offy[2 * k + 1],
353 -pos[2 * k + 0] + offx[2 * k + 0] + offy[2 * k + 0],
354 -pos[2 * k + 1] + offx[2 * k + 1] + offy[2 * k + 1], offy[2 * k + 0], offy[2 * k + 1]);
355 cairo_curve_to(cr, pos[2 * k + 0] - offx[2 * k + 0] + offy[2 * k + 0],
356 pos[2 * k + 1] - offx[2 * k + 1] + offy[2 * k + 1],
357 pos[2 * k + 0] - offx[2 * k + 0] + offy[2 * k + 0],
358 pos[2 * k + 1] - offx[2 * k + 1] + offy[2 * k + 1],
359 2 * pos[2 * k + 0] - offx[2 * k + 0], 2 * pos[2 * k + 1] - offx[2 * k + 1]);
360 cairo_curve_to(cr, 3 * pos[2 * k + 0] - offx[2 * k + 0] - offy[2 * k + 0],
361 3 * pos[2 * k + 1] - offx[2 * k + 1] - offy[2 * k + 1],
362 3 * pos[2 * k + 0] - offx[2 * k + 0] - offy[2 * k + 0],
363 3 * pos[2 * k + 1] - offx[2 * k + 1] - offy[2 * k + 1],
364 2 * pos[2 * k + 0] - offy[2 * k + 0], 2 * pos[2 * k + 1] - offy[2 * k + 1]);
365 cairo_curve_to(cr, pos[2 * k + 0] + offx[2 * k + 0] - offy[2 * k + 0],
366 pos[2 * k + 1] + offx[2 * k + 1] - offy[2 * k + 1],
367 pos[2 * k + 0] + offx[2 * k + 0] - offy[2 * k + 0],
368 pos[2 * k + 1] + offx[2 * k + 1] - offy[2 * k + 1], offx[2 * k + 0], offx[2 * k + 1]);
369
370 cairo_save(cr);
371 cairo_scale(cr, 1. / scale, 1. / scale);
372 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
373 cairo_stroke(cr);
374 cairo_restore(cr);
375 }
376 }
377 }
378 cairo_restore(cr);
379 dt_free(pos);
380}
381#undef CHANNEL
382#undef gbuf
383#undef FOCUS_THRS
384
385// clang-format off
386// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
387// vim: shiftwidth=2 expandtab tabstop=2 cindent
388// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
389// clang-format on
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
const double thrs
Definition chart/main.c:54
const float i
Definition colorspaces_inline_conversions.h:669
const float fx
Definition colorspaces_inline_conversions.h:254
const dt_aligned_pixel_t f
Definition colorspaces_inline_conversions.h:256
const float n
Definition colorspaces_inline_conversions.h:929
darktable_t darktable
Definition darktable.c:178
#define UNKNOWN_IMAGE
Definition darktable.h:181
#define dt_free(ptr)
Definition darktable.h:380
void dt_dev_pixelpipe_get_roi_out(dt_dev_pixelpipe_t *pipe, struct dt_develop_t *dev, const int width_in, const int height_in, int *width, int *height)
Definition dev_pixelpipe.c:226
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:189
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:782
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:129
int dt_dev_distort_transform_plus(dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1417
@ DT_DEV_TRANSFORM_DIR_ALL
Definition develop.h:102
static void dt_focus_draw_clusters(cairo_t *cr, int width, int height, int32_t imgid, int buffer_width, int buffer_height, dt_focus_cluster_t *focus, int frows, int fcols, float full_zoom, float full_x, float full_y)
Definition focus.h:237
static void dt_focus_create_clusters(dt_focus_cluster_t *focus, int frows, int fcols, uint8_t *buffer, int buffer_width, int buffer_height)
Definition focus.h:147
#define gbuf(BUF, A, B)
Definition focus.h:42
static void _dt_focus_cdf22_wtf(uint8_t *buf, const int l, const int width, const int height)
Definition focus.h:54
static void _dt_focus_update(dt_focus_cluster_t *f, int frows, int fcols, int i, int j, int wd, int ht, int diff)
Definition focus.h:109
#define CHANNEL
Definition focus.h:44
#define FOCUS_THRS
Definition focus.h:43
static int _from_uint8(uint8_t i)
Definition focus.h:50
static uint8_t _to_uint8(int i)
Definition focus.h:46
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:75
static gboolean draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
Definition hotpixels.c:398
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
Definition image_cache.c:451
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
Definition image_cache.c:341
static const float x
Definition iop_profile.h:239
@ DT_MIPMAP_NONE
Definition mipmap_cache.h:52
void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, int32_t imgid, int width, int height, dt_mipmap_size_t size)
Definition pixelpipe_hb.c:482
int dt_dev_pixelpipe_init_dummy(dt_dev_pixelpipe_t *pipe)
Definition pixelpipe_hb.c:414
void dt_dev_pixelpipe_create_nodes(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
Definition pixelpipe_hb.c:593
void dt_dev_pixelpipe_cleanup(dt_dev_pixelpipe_t *pipe)
Definition pixelpipe_hb.c:501
#define dt_dev_pixelpipe_synch_all(pipe, dev)
Definition pixelpipe_hb.h:427
struct dt_gui_gtk_t * gui
Definition darktable.h:703
struct dt_image_cache_t * image_cache
Definition darktable.h:705
struct dt_develop_t * develop
Definition darktable.h:698
Definition pixelpipe_hb.h:216
int iwidth
Definition pixelpipe_hb.h:222
int processed_width
Definition pixelpipe_hb.h:225
int processed_height
Definition pixelpipe_hb.h:225
int iheight
Definition pixelpipe_hb.h:222
Definition develop.h:155
int32_t border_size
Definition develop.h:184
struct dt_develop_t::@18 roi
Definition focus.h:36
float y
Definition focus.h:38
float x
Definition focus.h:38
float y2
Definition focus.h:38
float x2
Definition focus.h:38
int64_t n
Definition focus.h:37
float thrs
Definition focus.h:39
double ppd
Definition gtk.h:162
Definition common/image.h:247
int32_t crop_y
Definition common/image.h:282
int32_t crop_x
Definition common/image.h:282