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 /* The Wavelet Decompose algorithm is based on the GIMP's Wavelet Decompose plugin, by Marco Rossini
31  *
32  * http://registry.gimp.org/node/11742
33  *
34  * */
35 
36 
37 #ifndef PF_WAVDEC_H
38 #define PF_WAVDEC_H
39 
40 #include <string.h>
41 #include "../base/processor.hh"
42 
43 namespace PF
44 {
45 
46 
47 class WavDecPar: public OpParBase
48 {
49   Property<int> numScales, currScale, preview_scale, initial_lev;
50   Property<float> blendFactor;
51 
52   ProcessorBase* wavdec_algo;
53 
54 public:
55   WavDecPar();
56 
has_intensity()57   bool has_intensity() { return false; }
58 //  bool needs_caching() { return true; }
59 
get_numScales()60   int get_numScales() { return numScales.get(); }
set_numScales(int a)61   void set_numScales(int a) { numScales.set(a); }
get_currScale()62   int get_currScale() { return currScale.get(); }
set_currScale(int a)63   void set_currScale(int a) { currScale.set(a); }
get_initial_lev()64   int get_initial_lev() { return initial_lev.get(); }
set_initial_lev(int s)65   void set_initial_lev(int s) { initial_lev.set(s); }
get_preview_scale()66   int get_preview_scale() { return preview_scale.get(); }
set_preview_scale(int s)67   void set_preview_scale(int s) { preview_scale.set(s); }
68 
get_blendFactor()69   float get_blendFactor() { return blendFactor.get(); }
set_blendFactor(float a)70   void set_blendFactor(float a) { blendFactor.set(a); }
71 
get_padding(int nScales,int initial_lev)72   int get_padding(int nScales, int initial_lev) { return pow(2, nScales+initial_lev); }
73   void compute_padding( VipsImage* full_res, unsigned int id, unsigned int level );
74 
75   VipsImage* build(std::vector<VipsImage*>& in, int first,
76       VipsImage* imap, VipsImage* omap,
77       unsigned int& level);
78 };
79 
80 class WavDecAlgoPar: public OpParBase
81 {
82   int numScales, currScale;
83   float blendFactor;
84   int initial_lev;
85   int preview_scale;
86   int padding;
87 
88 public:
WavDecAlgoPar()89   WavDecAlgoPar(): OpParBase()
90   {
91       numScales = 0;
92       currScale = 0;
93       blendFactor = .5f;
94       initial_lev = 0;
95       preview_scale = 1;
96       padding = 0;
97   }
98 
get_numScales()99   int get_numScales() { return numScales; }
set_numScales(int a)100   void set_numScales(int a) { numScales=a; }
get_currScale()101   int get_currScale() { return currScale; }
set_currScale(int a)102   void set_currScale(int a) { currScale=a; }
get_padding()103   int get_padding() { return padding; }
set_padding(int p)104   void set_padding(int p) { padding = p; }
get_initial_lev()105   int get_initial_lev() { return initial_lev; }
set_initial_lev(int p)106   void set_initial_lev(int p) { initial_lev = p; }
get_preview_scale()107   int get_preview_scale() { return preview_scale; }
set_preview_scale(int p)108   void set_preview_scale(int p) { preview_scale = p; }
109 
get_blendFactor()110   float get_blendFactor() { return (float)blendFactor; }
set_blendFactor(float a)111   void set_blendFactor(float a) { blendFactor=a; }
112 
get_maxScales(const int width,const int height)113   static int get_maxScales(const int width, const int height)
114   {
115     int maxscale = 0;
116 
117     // smallest edge must be higher than or equal to 2^scales
118     unsigned int size = MIN(width, height);
119     while (size >>= 1) maxscale++;
120 
121     return maxscale;
122   }
123 
124   /* Function to derive the output area from the input area
125    */
transform(const VipsRect * rin,VipsRect * rout,int)126   virtual void transform(const VipsRect* rin, VipsRect* rout, int /*id*/)
127   {
128     int pad = get_padding();
129     rout->left = rin->left+pad;
130     rout->top = rin->top+pad;
131     rout->width = rin->width-pad*2;
132     rout->height = rin->height-pad*2;
133   }
134 
135   /* Function to derive the area to be read from input images,
136      based on the requested output area
137   */
transform_inv(const VipsRect * rout,VipsRect * rin,int)138   virtual void transform_inv(const VipsRect* rout, VipsRect* rin, int /*id*/)
139   {
140     int pad = get_padding();
141     rin->left = rout->left-pad;
142     rin->top = rout->top-pad;
143     rin->width = rout->width+pad*2;
144     rin->height = rout->height+pad*2;
145   }
146 
147 
148 };
149 
150 
151 
152 template < OP_TEMPLATE_DEF >
153 class WavDecProc
154 {
155 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,PF::OpParBase * par)156   void render(VipsRegion** ireg, int n, int in_first,
157       VipsRegion* imap, VipsRegion* omap,
158       VipsRegion* oreg, PF::OpParBase* par)
159   {
160   }
161 };
162 
163 template < OP_TEMPLATE_DEF >
164 class WavDecAlgoProc
165 {
166 public:
render(VipsRegion ** in,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * out,OpParBase * par)167   void render(VipsRegion** in, int n, int in_first,
168       VipsRegion* imap, VipsRegion* omap,
169       VipsRegion* out, OpParBase* par)
170   {
171   }
172 };
173 
174 
175 
176 template < OP_TEMPLATE_DEF_TYPE_SPEC >
177 class WavDecAlgoProc< OP_TEMPLATE_IMP_TYPE_SPEC(float) >
178 {
179     float *wd_layers; // buffer for internal use
180     float *wd_image;
181     int wd_ch;
182     int wd_width;
183     int wd_height;
184     int wd_scales;
185     int wd_return_layer;
186     float wd_blend_factor; // .128f
187     int wd_max_scales;
188     int wd_initial_lev;
189     float wd_preview_scale;
190 
191 #define INDEX_WT_IMAGE(ch, index) (((index)*ch)+c)
192 
193 
194 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)195   void render(VipsRegion** ireg, int n, int in_first,
196       VipsRegion* imap, VipsRegion* omap,
197       VipsRegion* oreg, OpParBase* par)
198   {
199     if( ireg[0] == NULL ) {
200       std::cout<<"WavDecAlgoProc::render ireg[0] == NULL"<<std::endl;
201       return;
202     }
203 
204     WavDecAlgoPar* opar = dynamic_cast<WavDecAlgoPar*>(par);
205     if( !opar ) {
206       std::cout<<"WavDecAlgoProc::render opar == NULL"<<std::endl;
207       return;
208     }
209 
210     VipsRect *r = &oreg->valid;
211     VipsRect *ir = &ireg[0]->valid;
212 
213     wd_width = ir->width;
214     wd_height = ir->height;
215     wd_ch = oreg->im->Bands;
216 
217     const int i_line_size = wd_width * wd_ch;
218 
219     wd_image = (float*)malloc(wd_width * wd_height * wd_ch * sizeof(float));
220     memset(wd_image, 0, wd_width * wd_height * wd_ch * sizeof(float));
221 
222     for( int y = 0; y < wd_height; y++ ) {
223       float *pin = (float*)VIPS_REGION_ADDR( ireg[0], ir->left, ir->top + y );
224       float *pwd = wd_image + y * i_line_size;
225 
226       memcpy(pwd, pin, i_line_size * sizeof(float));
227     }
228 
229     wd_initial_lev = (int)opar->get_initial_lev();
230     wd_scales = (int)opar->get_numScales();
231     wd_return_layer = (int)opar->get_currScale();
232     wd_blend_factor = (float)opar->get_blendFactor();
233     wd_preview_scale = (float)opar->get_preview_scale()+1;
234     wd_preview_scale = 1.f / wd_preview_scale;
235     wd_max_scales = opar->get_maxScales(wd_width, wd_height);
236     if (wd_scales > wd_max_scales)
237     {
238       wd_scales = wd_max_scales;
239       std::cout<<"WavDecAlgoProc::render: max scale is "<<wd_max_scales<<" for this image preview size"<<std::endl;
240     }
241     if (wd_return_layer > (wd_scales+1)) wd_return_layer = wd_scales+1;
242 
243 //    std::cout<<"WavDecAlgoProc::render: max scale is "<<wd_max_scales<<" for this image preview size"<<std::endl;
244 
245     if (wd_scales > 0)
246       dwt_decompose();
247 
248     const int o_line_size = r->width*wd_ch;
249 
250     for( int y = 0; y < r->height; y++ ) {
251       float *pout = (float*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
252       float *pwd = wd_image + (y+abs(ir->height-r->height)/2) * i_line_size + (abs(ir->width-r->width)/2) * wd_ch;
253 
254       memcpy(pout, pwd, o_line_size * sizeof(float));
255     }
256 
257     if (wd_image) free(wd_image);
258 
259   }
260 
261 
262 private:
dwt_decompose()263   void dwt_decompose()
264   {
265     /* this function prepares for decomposing, which is done in the function dwt_wavelet_decompose() */
266     if (wd_scales > wd_max_scales) wd_scales = wd_max_scales;
267     if (wd_return_layer > (wd_scales+1)) wd_return_layer = wd_scales+1;
268 
269     if (wd_return_layer == 0) {
270       wd_layers = (float *)malloc(wd_width * wd_height * wd_ch * sizeof(float));
271       if (wd_layers == NULL)
272       {
273         std::cout<<"not enough memory for wavelet decomposition"<<std::endl;
274         goto cleanup;
275       }
276       memset(wd_layers, 0, wd_width * wd_height * wd_ch * sizeof(float));
277     }
278     else {
279       wd_layers = NULL;
280     }
281 
282     dwt_wavelet_decompose();
283 
284 
285   cleanup:
286       if (wd_layers)
287       {
288         free(wd_layers);
289         wd_layers = NULL;
290       }
291 
292   }
293 
dwt_wavelet_decompose()294   void dwt_wavelet_decompose()
295   {
296     float *temp = NULL;
297     unsigned int lpass, hpass;
298     float *buffer[2] = {0, 0};
299     int bcontinue = 1;
300 
301     //const float lpass_add = sqrtf(.25f);
302     //const float lpass_mult = (1.f / 16.f);
303     const float lpass_add = 0;//8.f/257.f;//sqrtf(.25f);
304     const float lpass_mult = (1.f / 16.f);//( lpass_add * 2.f );//(1.f / 16.f);
305     const float lpass_sub = wd_blend_factor; //.128f;
306 
307     const int size = wd_width * wd_height * wd_ch;
308     const int i_line_size = wd_width * wd_ch;
309 
310     /* image buffers */
311     buffer[0] = wd_image;
312     /* temporary storage */
313     buffer[1] = (float *)malloc(size * sizeof(float));
314     if (buffer[1] == NULL)
315     {
316       std::cout<<"not enough memory for wavelet decomposition"<<std::endl;
317       goto cleanup;
318     }
319     memset(buffer[1], 0, size * sizeof(float));
320 
321     temp = (float *)malloc(MAX(wd_width, wd_height) * wd_ch * sizeof(float));
322     if (temp == NULL)
323     {
324       std::cout<<"not enough memory for wavelet decomposition"<<std::endl;
325       goto cleanup;
326     }
327     memset(temp, 0, MAX(wd_width, wd_height) * wd_ch * sizeof(float));
328 
329     /* iterate over wavelet scales */
330     lpass = 1;
331     hpass = 0;
332     for (unsigned int lev = 0; lev < wd_scales && bcontinue; lev++)
333     {
334       lpass = (1 - (lev & 1));
335 
336       for (int row = 0; row < wd_height; row++)
337       {
338         dwt_hat_transform(temp, buffer[hpass] + (row * i_line_size), 1, wd_width, 1 << (lev+wd_initial_lev));
339         memcpy(&(buffer[lpass][row * i_line_size]), temp, i_line_size * sizeof(float));
340       }
341 
342       for (int col = 0; col < wd_width; col++)
343       {
344         dwt_hat_transform(temp, buffer[lpass] + col*wd_ch, wd_width, wd_height, 1 << (lev+wd_initial_lev));
345         for (int row = 0; row < wd_height; row++)
346         {
347           for (int c = 0; c < wd_ch; c++)
348           buffer[lpass][INDEX_WT_IMAGE(wd_ch, row * wd_width + col)] = temp[INDEX_WT_IMAGE(wd_ch, row)];
349         }
350       }
351 
352       for (int i = 0; i < size; i++)
353       {
354         //buffer[lpass][i] = ( buffer[lpass][i] + lpass_add ) * lpass_mult;
355         buffer[lpass][i] *= lpass_mult;
356         buffer[hpass][i] -= buffer[lpass][i] - lpass_sub;
357       }
358 
359       if (wd_return_layer == 0)
360       {
361         dwt_add_layer(buffer[hpass], lev + 1);
362       }
363       else if (wd_return_layer == ((int)(lev + 1)))
364       {
365         dwt_get_image_layer(buffer[hpass]);
366 
367         bcontinue = 0;
368       }
369 
370       hpass = lpass;
371     }
372 
373     //  Wavelet residual
374     if (wd_return_layer == (wd_scales+1))
375     {
376       dwt_get_image_layer(buffer[lpass]);
377     }
378     else if (wd_return_layer == 0)
379     {
380       dwt_add_layer(buffer[hpass], wd_scales+1);
381 
382       dwt_get_image_layer(wd_layers);
383     }
384 
385   cleanup:
386     if (temp) free(temp);
387     if (buffer[1]) free(buffer[1]);
388 
389   }
390 
dwt_add_layer(float * const img,const int n_scale)391   void dwt_add_layer(float *const img, const int n_scale)
392   {
393     const float lpass_sub = wd_blend_factor; //.128f;
394     const int buff_size = wd_width * wd_height * wd_ch;
395 
396     if (n_scale == wd_scales+1)
397     {
398       for (int i = 0; i < buff_size; i++)
399         wd_layers[i] += img[i];
400     }
401     else
402     {
403       for (int i = 0; i < buff_size; i++)
404         wd_layers[i] += img[i] - lpass_sub;
405     }
406 
407   }
408 
dwt_get_image_layer(float * const layer)409   void dwt_get_image_layer(float *const layer)
410   {
411     if (wd_image != layer) {
412       memcpy(wd_image, layer, wd_width * wd_height * wd_ch * sizeof(float));
413     }
414   }
415 
dwt_hat_transform(float * temp,float * const base,const int st,const int size,int sc)416   void dwt_hat_transform(float *temp, float *const base, const int st, const int size, int sc)
417   {
418     int i, c;
419     const float hat_mult = 2.f;
420     sc = std::min(size, (int)(sc * wd_preview_scale));
421 
422     for (i = 0; i < sc; i++)
423     {
424       for (c = 0; c < wd_ch; c++, temp++)
425       {
426         *temp = hat_mult * base[INDEX_WT_IMAGE(wd_ch, st * i)] + base[INDEX_WT_IMAGE(wd_ch, st * (sc - i))] + base[INDEX_WT_IMAGE(wd_ch, st * (i + sc))];
427       }
428     }
429     for (; i + sc < size; i++)
430     {
431       for (c = 0; c < wd_ch; c++, temp++)
432       {
433         *temp = hat_mult * base[INDEX_WT_IMAGE(wd_ch, st * i)] + base[INDEX_WT_IMAGE(wd_ch, st * (i - sc))] + base[INDEX_WT_IMAGE(wd_ch, st * (i + sc))];
434       }
435     }
436     for (; i < size; i++)
437     {
438       for (c = 0; c < wd_ch; c++, temp++)
439       {
440         *temp = hat_mult * base[INDEX_WT_IMAGE(wd_ch, st * i)] + base[INDEX_WT_IMAGE(wd_ch, st * (i - sc))]
441                                                                + base[INDEX_WT_IMAGE(wd_ch, st * (2 * size - 2 - (i + sc)))];
442       }
443     }
444 
445   }
446 
447 
448 };
449 
450 
451 ProcessorBase* new_wavdec_algo();
452 ProcessorBase* new_wavdec();
453 
454 }
455 
456 #endif
457