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