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/new_operation.hh"
35 #include "convert_colorspace.hh"
36 #include "icc_transform.hh"
37 #include "gaussblur.hh"
38 #include "defringe.hh"
39 
40 
DefringePar()41 PF::DefringePar::DefringePar():
42 OpParBase(),
43 op_mode("op_mode",this,PF::MODE_LOCAL_AVERAGE,"MODE_LOCAL_AVERAGE","local average (slow)"),
44 radius("radius",this,4.0),
45 threshold("threshold",this,20.0),
46 in_profile( NULL )
47 {
48   //op_mode("op_mode",this,PF::MODE_GLOBAL_AVERAGE,"MODE_GLOBAL_AVERAGE","global average (fast)");
49   op_mode.add_enum_value(PF::MODE_STATIC,"MODE_STATIC","static threshold (fast)");
50 
51   gauss = new_gaussblur();
52   convert2lab = PF::new_convert_colorspace();
53   PF::ConvertColorspacePar* csconvpar = dynamic_cast<PF::ConvertColorspacePar*>(convert2lab->get_par());
54   if(csconvpar) {
55     csconvpar->set_out_profile_mode( PF::PROF_MODE_DEFAULT );
56     csconvpar->set_out_profile_type( PF::PROF_TYPE_LAB );
57   }
58   convert2input = new_icc_transform();
59   defringe_algo = new_defringe_algo();
60 
61   set_type("defringe" );
62 
63   set_default_name( _("defringe") );
64 }
65 
66 
needs_caching()67 bool PF::DefringePar::needs_caching()
68 {
69   return false;
70 }
71 
72 
73 
74 
compute_padding(VipsImage * full_res,unsigned int id,unsigned int level)75 void PF::DefringePar::compute_padding( VipsImage* full_res, unsigned int id, unsigned int level )
76 {
77   double radius2 = radius.get();
78   for( unsigned int l = 1; l <= level; l++ )
79     radius2 /= 2;
80 
81   DefringeAlgoPar* defringepar = dynamic_cast<DefringeAlgoPar*>( defringe_algo->get_par() );
82   defringepar->set_sigma( radius2 );
83   defringepar->set_threshold( threshold.get() );
84   defringepar->set_op_mode( (defringe_method_t)(op_mode.get_enum_value().first) );
85   defringepar->fb_init();
86 
87   int padding = defringepar->get_padding();
88 
89   set_padding( padding, id );
90 }
91 
92 
build(std::vector<VipsImage * > & in,int first,VipsImage * imap,VipsImage * omap,unsigned int & level)93 VipsImage* PF::DefringePar::build(std::vector<VipsImage*>& in, int first,
94     VipsImage* imap, VipsImage* omap,
95     unsigned int& level)
96 {
97   if( (in.size()<1) || (in[0]==NULL) )
98     return NULL;
99 
100   VipsImage* srcimg = in[0];
101 
102   double radius2 = radius.get();
103   for( unsigned int l = 1; l <= level; l++ )
104     radius2 /= 2;
105 
106   std::vector<VipsImage*> in2;
107 
108   in_profile = PF::get_icc_profile( in[0] );
109 
110   DefringeAlgoPar* defringepar = dynamic_cast<DefringeAlgoPar*>( defringe_algo->get_par() );
111   //defringepar->set_sigma( radius2 );
112   //defringepar->set_threshold( threshold.get() );
113   //defringepar->set_op_mode( (defringe_method_t)(op_mode.get_enum_value().first) );
114   //defringepar->fb_init();
115 
116   int padding = defringepar->get_padding();
117 
118 //  std::cout<<"padding: "<<padding<<std::endl;
119 //  std::cout<<"srcimg->Xsize: "<<srcimg->Xsize<<std::endl;
120 
121   bool do_conversion = true;
122   if( get_colorspace() == PF::PF_COLORSPACE_LAB ) do_conversion = false;
123 
124   // Extend the image by two pixels to account for the pixel averaging window
125   // of the impulse noise reduction algorithm
126   VipsImage* extended;
127   VipsExtend extend = VIPS_EXTEND_COPY;
128   if( vips_embed(srcimg, &extended, padding, padding,
129       srcimg->Xsize+padding*2, srcimg->Ysize+padding*2,
130       "extend", extend, NULL) ) {
131     std::cout<<"DefringePar::build(): vips_embed() failed."<<std::endl;
132     PF_REF( in[0], "DefringePar::build(): vips_embed() failed." );
133     return NULL;
134   }
135 //  std::cout<<"extended->Xsize: "<<extended->Xsize<<std::endl;
136 
137   VipsImage* labimg;
138   if( do_conversion ) {
139     convert2lab->get_par()->set_image_hints( extended );
140     convert2lab->get_par()->set_format( get_format() );
141     in2.clear(); in2.push_back( extended );
142     labimg = convert2lab->get_par()->build( in2, 0, NULL, NULL, level );
143     if( !labimg ) {
144       std::cout<<"DefringePar::build(): null Lab image"<<std::endl;
145       PF_REF( in[0], "DefringePar::build(): null Lab image" );
146       return in[0];
147     }
148     PF_UNREF( extended, "DefringePar::build(): extended unref after convert2lab" );
149   } else {
150     labimg = extended;
151   }
152 
153 
154   VipsImage* blurred = NULL;
155 
156   GaussBlurPar* gausspar = dynamic_cast<GaussBlurPar*>( gauss->get_par() );
157   if( gausspar ) {
158     gausspar->set_radius( radius.get() ); // TODO: should this be radius2?
159     gausspar->set_image_hints( labimg );
160     gausspar->set_format( get_format() );
161     in2.clear(); in2.push_back( labimg );
162     blurred = gausspar->build( in2, 0, NULL, NULL, level );
163     PF_UNREF( labimg, "ImageReaderPar::build(): extended unref after convert2lab" );
164   }
165 
166   if( !blurred ) {
167     std::cout<<"DefringePar::build(): null Lab image"<<std::endl;
168     PF_REF( in[0], "DefringePar::build(): null Lab image" );
169     return in[0];
170   }
171 
172   defringepar->set_image_hints( labimg );
173   defringepar->set_format( get_format() );
174   in2.clear();
175   in2.push_back( labimg );
176   in2.push_back(blurred);
177   VipsImage* defr = defringepar->build( in2, 0, NULL, NULL, level );
178   PF_UNREF( blurred, "DefringePar::build(): extended unref after convert2lab" );
179 
180 //  std::cout<<"defr->Xsize: "<<defr->Xsize<<std::endl;
181 
182   // Final cropping to remove the padding pixels
183   VipsImage* cropped;
184   if( vips_crop(defr, &cropped, padding, padding,
185       srcimg->Xsize, srcimg->Ysize, NULL) ) {
186     std::cout<<"DefringePar::build(): vips_crop() failed."<<std::endl;
187     PF_UNREF( defr, "DefringePar::build(): defr unref" );
188     PF_REF( in[0], "DefringePar::build(): vips_crop() failed" );
189     return in[0];
190   }
191   PF_UNREF( defr, "DefringePar::build(): defr unref" );
192 
193 //  std::cout<<"cropped->Xsize: "<<cropped->Xsize<<std::endl;
194 
195   VipsImage* out;
196   if( do_conversion ) {
197     PF::ICCTransformPar* icc_par = dynamic_cast<PF::ICCTransformPar*>( convert2input->get_par() );
198     if( icc_par ) {
199       icc_par->set_out_profile( in_profile );
200     }
201     convert2input->get_par()->set_image_hints( cropped );
202     convert2input->get_par()->set_format( get_format() );
203     in2.clear(); in2.push_back( cropped );
204     std::cout<<"DefringePar::build(): calling convert2input->get_par()->build()"<<std::endl;
205     out = convert2input->get_par()->build(in2, 0, NULL, NULL, level );
206     PF_UNREF( cropped, "DefringePar::update() cropped unref" );
207   } else {
208     out = cropped;
209   }
210 
211 //  std::cout<<"out->Xsize: "<<out->Xsize<<std::endl;
212 
213   set_image_hints( out );
214 
215   return out;
216 }
217