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;
61 for(int j = 0; j < height; j++)
62 {
63 // rows
64 // predict, get detail
65 int i = st;
66 for(; i < width - st; i += step) /*for(ch=0; ch<3; ch++)*/
67 gbuf(buf, i, j)
68 = _to_uint8((int)gbuf(buf, i, j) - ((int)gbuf(buf, i - st, j) + (int)gbuf(buf, i + st, j)) / 2);
69 if(i < width) /*for(ch=0; ch<3; ch++)*/
70 gbuf(buf, i, j) = _to_uint8(gbuf(buf, i, j) - gbuf(buf, i - st, j));
71 // update coarse
72 /*for(ch=0; ch<3; ch++)*/ gbuf(buf, 0, j) += _from_uint8(gbuf(buf, st, j)) / 2;
73 for(i = step; i < width - st; i += step) /*for(ch=0; ch<3; ch++)*/
74 gbuf(buf, i, j) += (_from_uint8(gbuf(buf, i - st, j)) + _from_uint8(gbuf(buf, i + st, j))) / 4;
75 if(i < width) /*for(ch=0; ch<3; ch++)*/
76 gbuf(buf, i, j) += _from_uint8(gbuf(buf, i - st, j)) / 2;
77 }
79 for(int i = 0; i < width; i++)
80 {
81 // cols
82 int j = st;
83 // predict, get detail
84 for(; j < height - st; j += step) /*for(ch=0; ch<3; ch++)*/
85 gbuf(buf, i, j)
86 = _to_uint8((int)gbuf(buf, i, j) - ((int)gbuf(buf, i, j - st) + (int)gbuf(buf, i, j + st)) / 2);
87 if(j < height) /*for(int ch=0; ch<3; ch++)*/
88 gbuf(buf, i, j) = _to_uint8((int)gbuf(buf, i, j) - (int)gbuf(buf, i, j - st));
89 // update
90 /*for(ch=0; ch<3; ch++)*/ gbuf(buf, i, 0) += _from_uint8(gbuf(buf, i, st)) / 2;
91 for(j = step; j < height - st; j += step) /*for(ch=0; ch<3; ch++)*/
92 gbuf(buf, i, j) += (_from_uint8(gbuf(buf, i, j - st)) + _from_uint8(gbuf(buf, i, j + st))) / 4;
93 if(j < height) /*for(int ch=0; ch<3; ch++)*/
94 gbuf(buf, i, j) += _from_uint8(gbuf(buf, i, j - st)) / 2;
95 }
96}
97
98static void _dt_focus_update(dt_focus_cluster_t *f, int frows, int fcols, int i, int j, int wd, int ht,
99 int diff)
100{
101 const int32_t thrs = FOCUS_THRS;
102 if(diff > thrs)
103 {
104 int fx = i / (float)wd * fcols;
105 int fy = j / (float)ht * frows;
106 int fi = fcols * fy + fx;
107#ifdef _OPENMP
108#pragma omp atomic
109#endif
110 f[fi].x += i;
111#ifdef _OPENMP
112#pragma omp atomic
113#endif
114 f[fi].y += j;
115#ifdef _OPENMP
116#pragma omp atomic
117#endif
118 f[fi].x2 += (float)i * i;
119#ifdef _OPENMP
120#pragma omp atomic
121#endif
122 f[fi].y2 += (float)j * j;
123#ifdef _OPENMP
124#pragma omp atomic
125#endif
126 f[fi].n++;
127#ifdef _OPENMP
128#pragma omp atomic
129#endif
130 f[fi].thrs += diff;
131 }
132}
133
134
135// read 8-bit buffer and create focus clusters from it
136static void dt_focus_create_clusters(dt_focus_cluster_t *focus, int frows, int fcols, uint8_t *buffer,
137 int buffer_width, int buffer_height)
138{
139 // mark in-focus pixels:
140 const int wd = buffer_width;
141 const int ht = buffer_height;
142 const int fs = frows * fcols;
143 // two-stage cdf 2/2 wavelet transform, use HH1 and HH2 to detect very sharp and sharp spots:
144 // pretend we already did the first step (coarse will stay in place, maybe even where the pre-demosaic
145 // sample was at)
146 _dt_focus_cdf22_wtf(buffer, 2, wd, ht);
147 // go through HH1 and detect sharp clusters:
148 memset(focus, 0, sizeof(dt_focus_cluster_t) * fcols * frows);
149#ifdef _OPENMP
150#pragma omp parallel for default(shared)
151#endif
152 for(int j = 0; j < ht - 1; j += 4)
153 for(int i = 0; i < wd - 1; i += 4)
154 {
155 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
156 abs(_from_uint8(buffer[4 * ((j + 2) * wd + i) + CHANNEL])));
157 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
158 abs(_from_uint8(buffer[4 * (j * wd + i + 2) + CHANNEL])));
159 }
160
161#if 1 // second pass, HH2
162 int num_clusters = 0;
163 for(int k = 0; k < fs; k++)
164 if(focus[k].n * 4 > wd * ht / (float)fs * 0.01f) num_clusters++;
165 if(num_clusters < 1)
166 {
167 memset(focus, 0, sizeof(dt_focus_cluster_t) * fs);
168 _dt_focus_cdf22_wtf(buffer, 3, wd, ht);
169#ifdef _OPENMP
170#pragma omp parallel for default(shared)
171#endif
172 for(int j = 0; j < ht - 1; j += 8)
173 {
174 for(int i = 0; i < wd - 1; i += 8)
175 {
176 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
177 1.5 * abs(_from_uint8(buffer[4 * ((j + 4) * wd + i) + CHANNEL])));
178 _dt_focus_update(focus, frows, fcols, i, j, wd, ht,
179 1.5 * abs(_from_uint8(buffer[4 * (j * wd + i + 4) + CHANNEL])));
180 }
181 }
182 num_clusters = 0;
183 for(int k = 0; k < fs; k++)
184 {
185 if(focus[k].n * 6.0f > wd * ht / (float)fs * 0.01f)
186 {
187 focus[k].n *= -1;
188 num_clusters++;
189 }
190 }
191 }
192#endif
193#undef CHANNEL
194
195#if 0 // simple high pass filter, doesn't work on slightly unsharp/high iso images
196 memset(focus, 0, sizeof(dt_focus_cluster_t)*fs);
197#ifdef _OPENMP
198#pragma omp parallel for default(shared)
199#endif
200 for(int j=1;j<ht-1;j++)
201 {
202 int index = 4*j*wd+4;
203 for(int i=1;i<wd-1;i++)
204 {
205 int32_t diff = 4*buffer[index+1]
206 - buffer[index-4+1]
207 - buffer[index+4+1]
208 - buffer[index-4*wd+1]
209 - buffer[index+4*wd+1];
210 _dt_focus_update(focus, frows, fcols, i, j, wd, ht, abs(diff));
211 index += 4;
212 }
213 }
214#endif
215 // normalize data in clusters:
216 for(int k = 0; k < fs; k++)
217 {
218 focus[k].thrs /= fabsf((float)focus[k].n);
219 focus[k].x /= fabsf((float)focus[k].n);
220 focus[k].x2 /= fabsf((float)focus[k].n);
221 focus[k].y /= fabsf((float)focus[k].n);
222 focus[k].y2 /= fabsf((float)focus[k].n);
223 }
224}
225
226static void dt_focus_draw_clusters(cairo_t *cr, int width, int height, int32_t imgid, int buffer_width,
227 int buffer_height, dt_focus_cluster_t *focus, int frows, int fcols,
228 float full_zoom, float full_x, float full_y)
229{
230 const int fs = frows * fcols;
231 cairo_save(cr);
232 cairo_translate(cr, width / 2.0, height / 2.0f);
233
234 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
235 dt_image_t image = *img;
237
238 // FIXME: get those from rawprepare IOP somehow !!!
239 int wd = buffer_width + image.crop_x;
240 int ht = buffer_height + image.crop_y;
241
242 // array with cluster positions
243 float *pos = malloc(fs * 6 * sizeof(float));
244 float *offx = pos + fs * 2, *offy = pos + fs * 4;
245
246 for(int k = 0; k < fs; k++)
247 {
248 const float stddevx = sqrtf(focus[k].x2 - focus[k].x * focus[k].x);
249 const float stddevy = sqrtf(focus[k].y2 - focus[k].y * focus[k].y);
250
251 // FIXME: get those from rawprepare IOP somehow !!!
252 const float x = focus[k].x + image.crop_x;
253 const float y = focus[k].y + image.crop_y;
254
255 pos[2 * k + 0] = x;
256 pos[2 * k + 1] = y;
257 offx[2 * k + 0] = x + stddevx;
258 offx[2 * k + 1] = y;
259 offy[2 * k + 0] = x;
260 offy[2 * k + 1] = y + stddevy;
261 }
262
263 // could use dt_image_altered() here, but it ignores flip module
264 {
265 dt_develop_t dev;
266 dt_dev_init(&dev, 0);
267 dt_dev_load_image(&dev, imgid);
269 const int res = dt_dev_pixelpipe_init_dummy(&pipe, &dev);
270 if(res)
271 {
272 // set mem pointer to 0, won't be used.
277 &pipe.processed_height);
280 wd = pipe.processed_width;
281 ht = pipe.processed_height;
282 }
283 dt_dev_cleanup(&dev);
284 }
285
286 const int32_t tb = darktable.develop->roi.border_size;
287 const float scale = fminf((width - 2 * tb) / (float)wd, (height - 2 * tb) / (float)ht) * full_zoom;
288 cairo_scale(cr, scale, scale);
289 float fx = 0.0f;
290 float fy = 0.0f;
291 if(full_zoom > 1.0f)
292 {
293 // we want to be sure the image stay in the window
294 fx = fminf((wd * scale - width) / 2, fabsf(full_x));
295 if(full_x < 0) fx = -fx;
296 if(wd * scale <= width) fx = 0;
297 fy = fminf((ht * scale - height) / 2, fabsf(full_y));
298 if(full_y < 0) fy = -fy;
299 if(ht * scale <= height) fy = 0;
300 }
301
302 cairo_translate(cr, -wd / 2.0f + fx / scale * darktable.gui->ppd, -ht / 2.0f + fy / scale * darktable.gui->ppd);
303
304 cairo_rectangle(cr, 0, 0, wd, ht);
305 cairo_clip(cr);
306
307 double dashes[] = { DT_PIXEL_APPLY_DPI(5.), DT_PIXEL_APPLY_DPI(5.) };
308 const int ndash = sizeof(dashes) / sizeof(dashes[0]);
309 double offset = 0.0f;
310 cairo_set_dash(cr, dashes, ndash, offset);
311
312 // draw clustered focus regions
313 for(int k = 0; k < fs; k++)
314 {
315 const float intens = (focus[k].thrs - FOCUS_THRS) / FOCUS_THRS;
316 const float col = fminf(1.0f, intens);
317 int draw = 0;
318 if(focus[k].n * 4.0f > buffer_width * buffer_height / (float)fs * 0.01f)
319 draw = 1;
320 else if(-focus[k].n * 6.0f > buffer_width * buffer_height / (float)fs * 0.01f)
321 draw = 2;
322 if(draw)
323 {
324 for(int i = 0; i < 2; i++)
325 {
326 if(i)
327 {
328 if(draw == 2)
329 cairo_set_source_rgb(cr, .1f, .1f, col);
330 else
331 cairo_set_source_rgb(cr, col, .1f, .1f);
332 cairo_set_dash(cr, dashes, ndash, dashes[0]);
333 }
334 else
335 {
336 cairo_set_source_rgb(cr, .1f, .1f, .1f);
337 cairo_set_dash(cr, dashes, ndash, 0);
338 }
339 cairo_move_to(cr, offx[2 * k + 0], offx[2 * k + 1]);
340 cairo_curve_to(cr, -pos[2 * k + 0] + offx[2 * k + 0] + offy[2 * k + 0],
341 -pos[2 * k + 1] + offx[2 * k + 1] + offy[2 * k + 1],
342 -pos[2 * k + 0] + offx[2 * k + 0] + offy[2 * k + 0],
343 -pos[2 * k + 1] + offx[2 * k + 1] + offy[2 * k + 1], offy[2 * k + 0], offy[2 * k + 1]);
344 cairo_curve_to(cr, pos[2 * k + 0] - offx[2 * k + 0] + offy[2 * k + 0],
345 pos[2 * k + 1] - offx[2 * k + 1] + offy[2 * k + 1],
346 pos[2 * k + 0] - offx[2 * k + 0] + offy[2 * k + 0],
347 pos[2 * k + 1] - offx[2 * k + 1] + offy[2 * k + 1],
348 2 * pos[2 * k + 0] - offx[2 * k + 0], 2 * pos[2 * k + 1] - offx[2 * k + 1]);
349 cairo_curve_to(cr, 3 * pos[2 * k + 0] - offx[2 * k + 0] - offy[2 * k + 0],
350 3 * pos[2 * k + 1] - offx[2 * k + 1] - offy[2 * k + 1],
351 3 * pos[2 * k + 0] - offx[2 * k + 0] - offy[2 * k + 0],
352 3 * pos[2 * k + 1] - offx[2 * k + 1] - offy[2 * k + 1],
353 2 * pos[2 * k + 0] - offy[2 * k + 0], 2 * pos[2 * k + 1] - offy[2 * k + 1]);
354 cairo_curve_to(cr, pos[2 * k + 0] + offx[2 * k + 0] - offy[2 * k + 0],
355 pos[2 * k + 1] + offx[2 * k + 1] - offy[2 * k + 1],
356 pos[2 * k + 0] + offx[2 * k + 0] - offy[2 * k + 0],
357 pos[2 * k + 1] + offx[2 * k + 1] - offy[2 * k + 1], offx[2 * k + 0], offx[2 * k + 1]);
358
359 cairo_save(cr);
360 cairo_scale(cr, 1. / scale, 1. / scale);
361 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2));
362 cairo_stroke(cr);
363 cairo_restore(cr);
364 }
365 }
366 }
367 cairo_restore(cr);
368 dt_free(pos);
369}
370#undef CHANNEL
371#undef gbuf
372#undef FOCUS_THRS
373
374// clang-format off
375// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
376// vim: shiftwidth=2 expandtab tabstop=2 cindent
377// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
378// 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:440
const float fx
Definition colorspaces_inline_conversions.h:100
const dt_aligned_pixel_t f
Definition colorspaces_inline_conversions.h:102
const float n
Definition colorspaces_inline_conversions.h:678
darktable_t darktable
Definition darktable.c:173
#define UNKNOWN_IMAGE
Definition darktable.h:182
#define dt_free(ptr)
Definition darktable.h:456
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
void dt_dev_pixelpipe_get_roi_out(dt_dev_pixelpipe_t *pipe, const int width_in, const int height_in, int *width, int *height)
Definition dev_pixelpipe.c:355
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:187
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1419
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:764
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:128
@ 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:226
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:136
#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:98
#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
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
Definition image_cache.c:508
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
Definition image_cache.c:398
static const float x
Definition iop_profile.h:235
float *const restrict const size_t k
Definition luminance_mask.h:78
float *const restrict const size_t const size_t ch
Definition luminance_mask.h:78
@ DT_MIPMAP_NONE
Definition mipmap_cache.h:53
void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, int32_t imgid, int width, int height, float iscale, dt_mipmap_size_t size)
Definition pixelpipe_hb.c:466
int dt_dev_pixelpipe_init_dummy(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
Definition pixelpipe_hb.c:392
void dt_dev_pixelpipe_create_nodes(dt_dev_pixelpipe_t *pipe)
Definition pixelpipe_hb.c:591
void dt_dev_pixelpipe_cleanup(dt_dev_pixelpipe_t *pipe)
Definition pixelpipe_hb.c:486
#define dt_dev_pixelpipe_synch_all(pipe)
Definition pixelpipe_hb.h:482
struct dt_gui_gtk_t * gui
Definition darktable.h:774
struct dt_image_cache_t * image_cache
Definition darktable.h:776
struct dt_develop_t * develop
Definition darktable.h:769
Definition pixelpipe_hb.h:218
int iwidth
Definition pixelpipe_hb.h:227
int processed_width
Definition pixelpipe_hb.h:235
int processed_height
Definition pixelpipe_hb.h:235
int iheight
Definition pixelpipe_hb.h:227
Definition develop.h:159
int32_t border_size
Definition develop.h:189
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 image.h:247
int32_t crop_y
Definition image.h:282
int32_t crop_x
Definition image.h:282