1 /*
2 */
3
4 /*
5
6 Copyright (C) 2014 Ferrero Andrea
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21
22 */
23
24 /*
25
26 These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/
27
28 */
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33
34 #include "../base/processor_imp.hh"
35 #include "guided_filter.hh"
36 #include "enhanced_usm.hh"
37
38 #define array2D PF::PixelMatrix
39 #define DEBUG_DUMP(arr)
40
41 #include "../rt/rtengine/alignedbuffer.h"
42 #include "../rt/rtengine/rescale.h"
43 #include "../rt/rtengine/boxblur.h"
44
45
46 using namespace rtengine;
47
48 namespace {
49
50
eusm_gf_boxblur(PF::PixelMatrix<T> & _src,PF::PixelMatrix<float> & dst,int radius,int border=0)51 template<class T> void eusm_gf_boxblur(PF::PixelMatrix<T> &_src, PF::PixelMatrix<float>& dst, int radius, int border=0)
52 {
53 const int W = _src.width();
54 const int H = _src.height();
55 const int boxsz = radius*2 + 1;
56 const float scale = boxsz*boxsz;
57
58 PF::PixelMatrix<T>* ppm = &_src;
59 PF::PixelMatrix<T> tbuf;
60 if( _src.get_rows() == dst.get_rows() ) {
61 tbuf = _src;
62 ppm = &tbuf;
63 }
64 PF::PixelMatrix<T>& src = *ppm;
65
66 #ifdef GF_DEBUG2
67 std::cout<<"W="<<W<<" H="<<H<<std::endl;
68 #endif
69
70 // create and initialize column sum vector
71 #ifdef GF_DEBUG2
72 std::cout<<"Column vectors initialization"<<std::endl;
73 #endif
74 T* csum = new T[W];
75 for(int c = border; c < W-border; c++) {
76 csum[c] = 0;
77 for(int r = border; r < (boxsz+border-1); r++) {
78 csum[c] += src[r][c];
79 #ifdef GF_DEBUG2
80 std::cout<<" src["<<r<<"]["<<c<<"]="<<src[r][c]<<" csum["<<c<<"]="<<csum[c]<<std::endl;
81 #endif
82 }
83 }
84
85 // loop on rows, leaving a band of size radius above and below
86 for(int r = radius+border; r < (H-radius-border); r++) {
87
88 #ifdef GF_DEBUG2
89 std::cout<<"Processing row="<<r<<std::endl;
90 #endif
91 // add the leftmost boxsz-1 values of row r+radius
92 for(int c = border, rr=r+radius; c < (boxsz+border-1); c++) {
93 csum[c] += src[rr][c];
94 #ifdef GF_DEBUG2
95 std::cout<<" src["<<rr<<"]["<<c<<"]="<<src[rr][c]<<" csum["<<c<<"]="<<csum[c]<<std::endl;
96 #endif
97 }
98
99 // initialize the box sum
100 T sum = 0;
101 for(int c = border; c < (boxsz+border-1); c++) {
102 sum += csum[c];
103 #ifdef GF_DEBUG2
104 std::cout<<" csum["<<c<<"]="<<csum[c]<<" sum="<<sum<<std::endl;
105 #endif
106 }
107
108 // loop on columns, leaving a band of size radius at left and right
109 for(int c = radius+border; c < (W-radius-border); c++) {
110
111 #ifdef GF_DEBUG2
112 std::cout<<"Processing column="<<c<<std::endl;
113 #endif
114 // update the column sum at c+radius
115 csum[c+radius] += src[r+radius][c+radius];
116 #ifdef GF_DEBUG2
117 std::cout<<" src["<<r+radius<<"]["<<c+radius<<"]="<<src[r+radius][c+radius]<<" csum["<<c+radius<<"]="<<csum[c+radius]<<std::endl;
118 #endif
119 // add the column at c+radius
120 sum += csum[c+radius];
121 #ifdef GF_DEBUG2
122 std::cout<<" csum["<<c+radius<<"]="<<csum[c+radius]<<" sum="<<sum<<std::endl;
123 #endif
124
125 // update the destination value
126 dst[r][c] = sum / scale;
127 #ifdef GF_DEBUG2
128 std::cout<<"dst["<<r<<"]["<<c<<"] = "<<sum<<" / "<<scale<<std::endl;
129 #endif
130
131 // remove the column at c-radius from the sum
132 sum -= csum[c-radius];
133
134 // remove the value at (r-radius, c-radius) from the column a c-radius
135 csum[c-radius] -= src[r-radius][c-radius];
136 }
137
138 // loop on columns finished.
139 // remove the values at r-radius from the csums from W-boxsz+1 to W-1
140 for(int c = (W-border-boxsz+1); c < W-border; c++) {
141 csum[c] -= src[r-radius][c];
142 }
143 }
144
145 delete[] csum;
146 }
147
148 }
149
eusm_gf(const PF::PixelMatrix<float> & src,PF::PixelMatrix<float> & dst_a,PF::PixelMatrix<float> & dst_b,PF::PixelMatrix<float> & dst_mean,int r,float epsilon,bool invert)150 void PF::eusm_gf(const PF::PixelMatrix<float> &src, PF::PixelMatrix<float> &dst_a, PF::PixelMatrix<float> &dst_b,
151 PF::PixelMatrix<float> &dst_mean, int r, float epsilon, bool invert)
152 {
153 bool multithread = false;
154 const int W = src.width();
155 const int H = src.height();
156
157 enum Op { MUL, DIVEPSILON, DIVEPSILONINV, ADD, SUB, ADDMUL, SUBMUL };
158
159 const auto apply =
160 [=](Op op, int border, array2D<float> &res, const array2D<float> &a, const array2D<float> &b, const array2D<float> &c=array2D<float>()) -> void
161 {
162 const int w = res.width()-border;
163 const int h = res.height()-border;
164
165 #ifdef _OPENMP
166 #pragma omp parallel for if (multithread)
167 #endif
168 for (int y = border; y < h; ++y) {
169 for (int x = border; x < w; ++x) {
170 float r;
171 float aa = a[y][x];
172 float bb = b[y][x];
173 switch (op) {
174 case MUL:
175 r = aa * bb;
176 //std::cout<<"r = aa * bb: "<<r<<" = "<<aa<<" * "<<bb<<std::endl;
177 break;
178 case DIVEPSILON:
179 r = aa / (bb + epsilon);
180 //std::cout<<"r = aa / (bb + epsilon): "<<r<<" = "<<aa<<" / "<<bb+epsilon<<std::endl;
181 break;
182 case DIVEPSILONINV:
183 r = 1.0f - (aa / (bb + epsilon));
184 //std::cout<<"r = aa / (bb + epsilon): "<<r<<" = "<<aa<<" / "<<bb+epsilon<<std::endl;
185 break;
186 case ADD:
187 r = aa + bb;
188 //std::cout<<"r = aa + bb: "<<r<<" = "<<aa<<" + "<<bb<<std::endl;
189 break;
190 case SUB:
191 r = aa - bb;
192 //std::cout<<"r = aa - bb: "<<r<<" = "<<aa<<" - "<<bb<<std::endl;
193 break;
194 case ADDMUL:
195 r = aa * bb + c[y][x];
196 //std::cout<<"r = aa * bb + c[y][x]: "<<r<<" = "<<aa<<" * "<<bb<<" + "<<c[y][x]<<std::endl;
197 break;
198 case SUBMUL:
199 r = c[y][x] - (aa * bb);
200 //std::cout<<"r = c[y][x] - (aa * bb): "<<r<<" = "<<c[y][x]<<" - "<<aa<<" * "<<bb<<std::endl;
201 break;
202 default:
203 assert(false);
204 r = 0;
205 break;
206 }
207 res[y][x] = r;
208 //if( op==ADDMUL && r<-100 ) getchar();
209 }
210 }
211 };
212
213 // use the terminology of the paper (Algorithm 2)
214 const array2D<float> &I = src;
215
216 AlignedBuffer<float> blur_buf(I.width() * I.height());
217 const auto f_mean1 =
218 [&](array2D<float> &d, array2D<float> &s, int rad, int border=0) -> void
219 {
220 rad = LIM(rad, 0, (min(s.width(), s.height()) - 1) / 2 - 1);
221 float **src = s;
222 float **dst = d;
223 boxblur<float, float>(src, dst, blur_buf.data, rad, rad, s.width(), s.height());
224 };
225
226 const auto f_mean2 =
227 [&](array2D<float> &d, array2D<float> &s, int rad, int border=0) -> void
228 {
229 eusm_gf_boxblur(s, d, rad, border);
230 };
231
232 const auto f_subsample =
233 [=](array2D<float> &d, const array2D<float> &s) -> void
234 {
235 rescaleBilinear(s, d, multithread);
236 };
237
238 const auto f_mean = f_mean2;
239
240 const auto f_upsample = f_subsample;
241
242 //return;
243
244 const int w = W;
245 const int h = H;
246
247 array2D<float> I1; //(w, h);
248 I1 = I;
249
250 float r1 = float(r);
251 int border = 0;
252
253 #ifdef GF_DEBUG
254 printf("epsilon=%f\n", epsilon);
255
256 printf("I1:\n");
257 for (int x = r1; x < w-r1; ++x) {
258 printf("%f ", I1[r1][x]);
259 }
260 printf("\n");
261 #endif
262
263 array2D<float>& meanI = dst_mean;
264 f_mean(meanI, I1, r1, 0);
265 DEBUG_DUMP(meanI);
266 #ifdef GF_DEBUG
267 printf("After f_mean(meanI, I1, r1)\n");
268 for (int x = r1; x < w-r1; ++x) {
269 printf("%f ", meanI[r1][x]);
270 }
271 printf("\n");
272 #endif
273
274 array2D<float> &corrI = I1;
275 apply(MUL, 0, corrI, I1, I1);
276 f_mean(corrI, corrI, r1, 0);
277 DEBUG_DUMP(corrI);
278 //printf("After apply(MUL, corrI, I1, I1): corrI=%f I1=%f\n", corrI[0][0], I1[0][0]); //getchar();
279
280 array2D<float> &varI = corrI;
281 apply(SUBMUL, r1, varI, meanI, meanI, corrI);
282 DEBUG_DUMP(varI);
283 #ifdef GF_DEBUG
284 printf("After apply(SUBMUL, varI, meanI, meanI, corrI)\n");
285 for (int x = r1; x < w-r1; ++x) {
286 printf("%f ", varI[r1][x]);
287 }
288 printf("\n");
289 #endif
290
291 array2D<float> &a = varI;
292 if(invert)
293 apply(DIVEPSILONINV, r1, a, varI, varI);
294 else
295 apply(DIVEPSILON, r1, a, varI, varI);
296 DEBUG_DUMP(a);
297 #ifdef GF_DEBUG
298 printf("After apply(DIVEPSILON, a, covIp, varI):\n");
299 for (int x = r1; x < w-r1; ++x) {
300 printf("%f ", a[r1][x]);
301 }
302 printf("\n");
303 #endif
304
305 array2D<float> &b = meanI;
306 apply(SUBMUL, r1, b, a, meanI, meanI);
307 DEBUG_DUMP(b);
308 #ifdef GF_DEBUG
309 printf("After apply(SUBMUL, b, a, meanI, meanI):\n");
310 for (int x = r1; x < w-r1; ++x) {
311 printf("%f ", b[r1][x]);
312 }
313 printf("\n");
314 #endif
315
316 array2D<float> &meana = dst_a;
317 f_mean(meana, a, r1, r1);
318 #ifdef GF_DEBUG
319 printf("After f_mean(meana, a, r1, r1):\n");
320 for (int x = r1*2; x < w-r1*2; ++x) {
321 printf("%f ", meana[r1*2][x]);
322 }
323 printf("\n");
324 #endif
325 DEBUG_DUMP(meana);
326
327 array2D<float> &meanb = dst_b;
328 f_mean(meanb, b, r1, r1);
329 #ifdef GF_DEBUG
330 printf("After f_mean(meanb, b, r1, r1):\n");
331 for (int x = r1*2; x < w-r1*2; ++x) {
332 printf("%f ", meanb[r1*2][x]);
333 }
334 printf("\n");
335 #endif
336 DEBUG_DUMP(meanb);
337
338 array2D<float> q(w-r*4, h-r*4);
339 //apply(ADDMUL, r*2, q, meana, I, meanb);
340 #ifdef GF_DEBUG
341 printf("After apply(ADDMUL, r*2, q, meana, I, meanb):\n");
342 for (int x = r*2; x < w-r*2; ++x) {
343 printf("%f ", meana[r*2][x]*I[r*2][x] + meanb[r*2][x]);
344 }
345 printf("\n");
346 printf("\n\n");
347 #endif
348 DEBUG_DUMP(q);
349 }
350
351
352
EnhancedUnsharpMaskPar()353 PF::EnhancedUnsharpMaskPar::EnhancedUnsharpMaskPar():
354 PaddedOpPar(),
355 show_mask("show_mask",this,true),
356 linear("linear",this,false),
357 amount("amount",this,1),
358 radius("radius",this,3),
359 threshold_l("threshold_l",this,0.005),
360 threshold_h("threshold_h",this,0.02),
361 nscales("nscales",this,1)
362 {
363 set_type("enhanced_usm" );
364
365 set_default_name( _("enhanced usm") );
366 }
367
368
369
propagate_settings()370 void PF::EnhancedUnsharpMaskPar::propagate_settings()
371 {
372 }
373
374
375
compute_padding(VipsImage * full_res,unsigned int id,unsigned int level)376 void PF::EnhancedUnsharpMaskPar::compute_padding( VipsImage* full_res, unsigned int id, unsigned int level )
377 {
378 double radius2 = radius.get();
379 for( unsigned int s = 1; s < nscales.get(); s++ ) {
380 radius2 *= 2;
381 }
382 for( unsigned int l = 1; l <= level; l++ ) {
383 radius2 /= 2;
384 }
385 int padding = radius2 * 4;
386 set_padding( padding, id );
387 }
388
389
390
build(std::vector<VipsImage * > & in,int first,VipsImage * imap,VipsImage * omap,unsigned int & level)391 VipsImage* PF::EnhancedUnsharpMaskPar::build(std::vector<VipsImage*>& in, int first,
392 VipsImage* imap, VipsImage* omap, unsigned int& level)
393 {
394 if( (in.size()<1) || (in[0]==NULL) )
395 return NULL;
396
397
398 in_profile = PF::get_icc_profile( in[0] );
399
400 threshold_real_l = threshold_l.get();
401 threshold_real_h = threshold_h.get();
402 if( get_linear() ) {
403 threshold_real_l /= 1;
404 threshold_real_h /= 1;
405 }
406
407 double radius2 = radius.get();
408 for( unsigned int l = 1; l <= level; l++ ) {
409 radius2 /= 2;
410 }
411 radius_real = radius2;
412 for( unsigned int s = 1; s < nscales.get(); s++ ) {
413 radius2 *= 2;
414 }
415
416
417 int padding = radius2 * 4;
418 set_padding( padding, 0 );
419
420 VipsImage* out = PF::PaddedOpPar::build( in, first, imap, omap, level );
421 return out;
422 }
423
424
new_enhanced_usm()425 PF::ProcessorBase* PF::new_enhanced_usm()
426 { return new PF::Processor<PF::EnhancedUnsharpMaskPar,PF::EnhancedUnsharpMaskProc>(); }
427