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