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