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 #ifndef PF_IMPULSE_NR_H
31 #define PF_IMPULSE_NR_H
32 
33 #include <string.h>
34 
35 #include "../base/splinecurve.hh"
36 #include "../base/processor.hh"
37 
38 namespace PF
39 {
40 
41 class ImpulseNRPar: public OpParBase
42 {
43   Property<float> threshold;
44 
45   ProcessorBase* convert2lab;
46   ProcessorBase* convert2input;
47   ProcessorBase* gauss_blur;
48   ProcessorBase* impulse_nr_algo;
49 
50   PF::ICCProfile* in_profile;
51 
52 public:
53   ImpulseNRPar();
54 
has_intensity()55   bool has_intensity() { return false; }
needs_caching()56   bool needs_caching() { return false; }
57 
get_threshold()58   float get_threshold() { return threshold.get(); }
set_threshold(float t)59   void set_threshold( float t ) { threshold.set(t); }
60 
61   VipsImage* build(std::vector<VipsImage*>& in, int first,
62       VipsImage* imap, VipsImage* omap,
63       unsigned int& level);
64 };
65 
66 
67 
68 class ImpulseNR_RTAlgo_Par: public OpParBase
69 {
70   Property<float> threshold;
71 
72 public:
ImpulseNR_RTAlgo_Par()73   ImpulseNR_RTAlgo_Par(): OpParBase(), threshold("threshold",this,0.5) {set_type("impulse_nr_RT");}
74 
has_intensity()75   bool has_intensity() { return false; }
needs_caching()76   bool needs_caching() { return false; }
77 
get_padding()78   int get_padding() { return 2; }
79 
80   /* Function to derive the output area from the input area
81    */
transform(const VipsRect * rin,VipsRect * rout,int)82   virtual void transform(const VipsRect* rin, VipsRect* rout, int /*id*/)
83   {
84     int pad = get_padding();
85     rout->left = rin->left+pad;
86     rout->top = rin->top+pad;
87     rout->width = rin->width-pad*2;
88     rout->height = rin->height-pad*2;
89   }
90 
91   /* Function to derive the area to be read from input images,
92      based on the requested output area
93   */
transform_inv(const VipsRect * rout,VipsRect * rin,int)94   virtual void transform_inv(const VipsRect* rout, VipsRect* rin, int /*id*/)
95   {
96     int pad = get_padding();
97     rin->left = rout->left-pad;
98     rin->top = rout->top-pad;
99     rin->width = rout->width+pad*2;
100     rin->height = rout->height+pad*2;
101   }
102 
103 
get_threshold()104   float get_threshold() { return threshold.get(); }
set_threshold(float t)105   void set_threshold( float t ) { threshold.update(t); }
106 };
107 
108 
109 
110 template < OP_TEMPLATE_DEF >
111 class ImpulseNRProc
112 {
113 public:
render(VipsRegion ** in,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * out,OpParBase * par)114   void render(VipsRegion** in, int n, int in_first,
115       VipsRegion* imap, VipsRegion* omap,
116       VipsRegion* out, OpParBase* par)
117   {
118     std::cout<<"ImpulseNRProc::render() called."<<std::endl;
119   }
120 };
121 
122 
123 template < OP_TEMPLATE_DEF >
124 class ImpulseNR_RTAlgo_Proc
125 {
126 public:
render(VipsRegion ** in,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * out,OpParBase * par)127   void render(VipsRegion** in, int n, int in_first,
128       VipsRegion* imap, VipsRegion* omap,
129       VipsRegion* out, OpParBase* par)
130   {
131     std::cout<<"ImpulseNR_RTAlgo_Proc::render() called."<<std::endl;
132   }
133 };
134 
135 
136 
137 /* Impulse noise reduction tool, adapted from Rawtherapee's impulse_denoise.h:
138  * https://code.google.com/p/rawtherapee/source/browse/rtengine/impulse_denoise.h
139 */
140 
141 template<class T>
ImpulseNR_RTAlgo(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)142 void ImpulseNR_RTAlgo(VipsRegion** ireg, int n, int in_first,
143     VipsRegion* imap, VipsRegion* omap,
144     VipsRegion* oreg, OpParBase* par)
145 {
146   //std::cout<<"ImpulseNR_RTAlgo_Proc::render(RGB) called: n="<<n<<std::endl;
147   if( n != 3 ) return;
148   if( ireg[0] == NULL ) {std::cout<<"ImpulseNR_RTAlgo_Proc::render(RGB): ireg[0]==NULL"<<std::endl;return;}
149   if( ireg[1] == NULL ) {std::cout<<"ImpulseNR_RTAlgo_Proc::render(RGB): ireg[1]==NULL"<<std::endl;return;}
150   if( ireg[2] == NULL ) {std::cout<<"ImpulseNR_RTAlgo_Proc::render(RGB): ireg[2]==NULL"<<std::endl;return;}
151 
152   //The cleaning algorithm starts here
153 
154   ImpulseNR_RTAlgo_Par* opar = dynamic_cast<ImpulseNR_RTAlgo_Par*>(par);
155   if( !opar ) {
156     std::cout<<"ImpulseNR_RTAlgo_Proc::render(RGB): opar==NULL"<<std::endl;
157     return;
158   }
159 
160   int padding = opar->get_padding();
161   VipsRect *r = &oreg->valid;
162   //VipsRect *ir = &ireg[0]->valid;
163   VipsRect ir = {r->left-padding, r->top-padding, r->width+2*padding, r->height+2*padding};
164   int iwidth = ir.width;
165   int iheight = ir.height;
166   VipsRect roi = {ireg[0]->valid.left+padding, ireg[0]->valid.top+padding, ireg[0]->valid.width-2*padding, ireg[0]->valid.height-2*padding};
167   vips_rect_intersectrect( &roi, r, &roi );
168   int line_size = roi.width * oreg->im->Bands;
169   int width = roi.width;
170   int height = roi.height;
171   int dx = r->left - ir.left;
172   int dy = r->top - ir.top;
173 
174   // buffer for the highpass image
175   float ** impish = new float *[iheight];
176   for (int i = 0; i < iheight; i++) {
177     impish[i] = new float [iwidth];
178     memset (impish[i], 0, iwidth*sizeof(float));
179   }
180   //std::cout<<"ImpulseNR_RTAlgo_Proc::render(RGB):"<<std::endl
181   //    <<"ir: "<<ir.width<<","<<ir.height<<"+"<<ir.left<<"+"<<ir.top<<std::endl
182   //    <<"r:  "<<r->width<<","<<r->height<<"+"<<r->left<<"+"<<r->top<<std::endl
183   //    <<"dx: "<<dx<<"  dy: "<<dy<<std::endl;
184 
185   T* pin0;
186   T* pin1;
187   T* pin2;
188   T* pin20;
189   T* pin21;
190   T* pin22;
191   T* pout;
192   int x, y, oy, x1, y1, pos, ipos, pos1;
193   //float threshold = opar->get_threshold()*FormatInfo<T>::RANGE;
194 
195   const float eps = 1.0;
196   float impthr = MAX(1.0, 5.5 - opar->get_threshold());
197   float impthrDiv24 = impthr / 24.0f;
198   float hpfabs, hfnbrave;
199 
200   for( y = padding; y < iheight-padding; y++ ) {
201     pin1 = (T*)VIPS_REGION_ADDR( ireg[1], ir.left, ir.top + y );
202     pin2 = (T*)VIPS_REGION_ADDR( ireg[2], ir.left, ir.top + y );
203 
204     for( x = 2; x < iwidth-2; x++ ) {
205       //intensity = 0;
206       hpfabs = fabs( (float)pin1[x] - (float)pin2[x] );
207 
208       //block average of high pass data
209       for( y1 = y-2, hfnbrave = 0; y1 <= y+2; y1++ ) {
210         pin21 = (T*)VIPS_REGION_ADDR( ireg[1], ir.left+x-2, ir.top + y1 );
211         pin22 = (T*)VIPS_REGION_ADDR( ireg[2], ir.left+x-2, ir.top + y1 );
212         for (x1 = 0; x1 < 5; x1++) {
213           if( y1 == y && x1 == 2) continue;
214           hfnbrave += fabs( (float)pin21[x1] - (float)pin22[x1] );
215         }
216       }
217 
218       impish[y][x] = (hpfabs > ((hfnbrave - hpfabs) * impthrDiv24));
219       //std::cout<<y<<","<<x<<"  hpfabs="<<hpfabs
220       //    <<"  ((hfnbrave - hpfabs) * impthrDiv24)="<<((hfnbrave - hpfabs) * impthrDiv24)
221       //    <<"  impish="<<impish[y][x]<<std::endl;
222     }
223   }
224 
225   //now impulsive values have been identified
226   float wtdsum[3], dirwt, norm, delta;
227   for( y = 2, oy = 2 - dy; y < iheight-2; y++, oy++ ) {
228     pin0 = (T*)VIPS_REGION_ADDR( ireg[0], ir.left, ir.top + y );
229     pin1 = (T*)VIPS_REGION_ADDR( ireg[1], ir.left, ir.top + y );
230     pin2 = (T*)VIPS_REGION_ADDR( ireg[2], ir.left, ir.top + y );
231     pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + oy );
232 
233     for( x = 2, ipos = 2*ireg[0]->im->Bands, pos = (2-dx)*oreg->im->Bands;
234         x < iwidth-2; x++, ipos += ireg[0]->im->Bands, pos += oreg->im->Bands ) {
235 
236       //std::cout<<"Filling "<<r->left+(pos/oreg->im->Bands)<<","<<r->top+oy<<std::endl;
237 
238       if( !impish[y][x] ) {
239         pout[pos] = pin0[ipos];
240         pout[pos+1] = pin0[ipos+1];
241         pout[pos+2] = pin0[ipos+2];
242         continue;
243       }
244 
245       norm = 0.0;
246       wtdsum[0] = wtdsum[1] = wtdsum[2] = 0.0;
247 
248       for( y1 = y-2; y1 <= y+2; y1++ ) {
249         pin20 = (T*)VIPS_REGION_ADDR( ireg[0], ir.left+x-2, ir.top + y1 );
250         pin21 = (T*)VIPS_REGION_ADDR( ireg[1], ir.left+x-2, ir.top + y1 );
251         pin22 = (T*)VIPS_REGION_ADDR( ireg[2], ir.left+x-2, ir.top + y1 );
252         for (x1 = 0, pos1 = 0; x1 < 5; x1++, pos1 += ireg[0]->im->Bands) {
253           if( y1 == y && x1 == 2)
254             continue;
255           if (impish[y1][x+x1-2])
256             continue;
257 
258           delta = (float)pin21[x1] - (float)pin1[x];
259           dirwt = 1.0f/( delta*delta + eps ); //use more sophisticated rangefn???
260           //std::cout<<"pin21["<<x1<<"]="<<pin21[x1]<<"  pin1["<<x<<"]="<<pin1[x]<<std::endl;
261           //std::cout<<"  SQR((float)pin21[x1] - (float)pin1[x])="<<SQR((float)pin21[x1] - (float)pin1[x])
262           //    <<"  dirwt="<<dirwt<<std::endl;
263           wtdsum[0] += dirwt * pin20[pos1];
264           wtdsum[1] += dirwt * pin20[pos1+1];
265           wtdsum[2] += dirwt * pin20[pos1+2];
266           norm += dirwt;
267         }
268       }
269 
270       //std::cout<<"norm="<<norm<<std::endl;
271 
272       if (norm) {
273         pout[pos] = wtdsum[0] / norm; //low pass filter
274         pout[pos+1] = wtdsum[1] / norm; //low pass filter
275         pout[pos+2] = wtdsum[2] / norm; //low pass filter
276       }
277     }
278   }
279 
280   for (int i = 0; i < iheight; i++) {
281     delete[] impish[i];
282   }
283   delete[] impish;
284 }
285 
286 
287 template < OP_TEMPLATE_DEF_CS_SPEC >
288 class ImpulseNR_RTAlgo_Proc< OP_TEMPLATE_IMP_CS_SPEC(PF_COLORSPACE_RGB) >
289 {
290 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)291   void render(VipsRegion** ireg, int n, int in_first,
292       VipsRegion* imap, VipsRegion* omap,
293       VipsRegion* oreg, OpParBase* par)
294   {
295     ImpulseNR_RTAlgo<T>(ireg, n, in_first, imap, omap, oreg, par);
296   }
297 };
298 
299 
300 
301 /* Impulse noise reduction tool, adapted from Rawtherapee's impulse_denoise.h:
302  * https://code.google.com/p/rawtherapee/source/browse/rtengine/impulse_denoise.h
303 */
304 template < OP_TEMPLATE_DEF_CS_SPEC >
305 class ImpulseNR_RTAlgo_Proc< OP_TEMPLATE_IMP_CS_SPEC(PF_COLORSPACE_LAB) >
306 {
307 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)308   void render(VipsRegion** ireg, int n, int in_first,
309       VipsRegion* imap, VipsRegion* omap,
310       VipsRegion* oreg, OpParBase* par)
311   {
312     ImpulseNR_RTAlgo<T>(ireg, n, in_first, imap, omap, oreg, par);
313   }
314 };
315 
316 
317 
318 ProcessorBase* new_impulse_nr_algo();
319 
320 ProcessorBase* new_impulse_nr();
321 
322 }
323 
324 #endif
325 
326 
327