Fixed example_imagesegmentation by adding InfAlg::setMaxIter(size_t)
[libdai.git] / examples / example_imagesegmentation.cpp
1 /* This file is part of libDAI - http://www.libdai.org/
2 *
3 * libDAI is licensed under the terms of the GNU General Public License version
4 * 2, or (at your option) any later version. libDAI is distributed without any
5 * warranty. See the file COPYING for more details.
6 *
7 * Copyright (C) 2006-2010 Joris Mooij [joris dot mooij at libdai dot org]
8 * Copyright (C) 2006-2007 Radboud University Nijmegen, The Netherlands
9 */
10
11
12 #include <iostream>
13 #include <dai/alldai.h> // Include main libDAI header file
14 #include <CImg.h> // This example needs CImg to be installed
15
16
17 using namespace dai;
18 using namespace std;
19 using namespace cimg_library;
20
21
22 /// Converts an image into a FactorGraph as described in section 1.1.3 of [\ref Moo08]
23 /** The grayscale pixel values of the image are transformed by a nonlinear transformation
24 into the local fields for each binary Ising variable, and the pairwise interactions
25 are uniform.
26 \param img The input image (should be grayscale, with 0 corresponding to background
27 and 255 corresponding to foreground)
28 \param J The pairwise interaction strength (should be positive for smoothing)
29 \param th_min Minimal value of local field strength (corresponding to background)
30 \param th_max Maximal value of local field strength (corresponding to foreground)
31 \param scale Typical difference in pixel values between fore- and background
32 \param pbg Probability that a pixel belongs to the background (in percent)
33 \param evidence The local evidence (represented as a grayscale image)
34 **/
35 template<class T>
36 FactorGraph img2fg( const CImg<T> &img, double J, double th_min, double th_max, double scale, double pbg, CImg<unsigned char> &evidence ) {
37 vector<Var> vars;
38 vector<Factor> factors;
39
40 #ifndef NEW_CIMG
41 size_t dimx = img.width; // Width of the image in pixels
42 size_t dimy = img.height; // Height of the image in pixels
43 #else
44 size_t dimx = img.width(); // Width of the image in pixels
45 size_t dimy = img.height(); // Height of the image in pixels
46 #endif
47 size_t N = dimx * dimy; // One variable for each pixel
48
49 // Create variables
50 cout << " Image width: " << dimx << endl;
51 cout << " Image height: " << dimy << endl;
52 cout << " Pairwise interaction strength: " << J << endl;
53 cout << " Minimal local evidence strength: " << th_min << endl;
54 cout << " Maximal local evidence strength: " << th_max << endl;
55 cout << " Scale of pixel values: " << scale << endl;
56 cout << " Percentage of background: " << pbg << endl;
57 cout << " Creating " << N << " variables..." << endl;
58 // Reserve memory for the variables
59 vars.reserve( N );
60 // Create a binary variable for each pixel
61 for( size_t i = 0; i < N; i++ )
62 vars.push_back( Var( i, 2 ) );
63
64 // Build image histogram
65 CImg<float> hist = img.get_channel( 0 ).get_histogram( 256, 0, 255 );
66 size_t cum_hist = 0;
67
68 // Find the critical level which corresponds with the seperation
69 // between foreground and background, assuming that the percentage
70 // of pixels in the image that belong to the background is pbg
71 size_t level = 0;
72 for( level = 0; level < 256; level++ ) {
73 cum_hist += (size_t)hist(level);
74 if( cum_hist > pbg * dimx * dimy / 100.0 )
75 break;
76 }
77
78 // Create factors
79 cout << " Creating " << (3 * N - dimx - dimy) << " factors..." << endl;
80 // Reserve memory for the factors
81 factors.reserve( 3 * N - dimx - dimy );
82 // th_avg is the local field strength that would correspond with pixel value level
83 // th_width is the width of the local field strength range
84 double th_avg = (th_min + th_max) / 2.0;
85 double th_width = (th_max - th_min) / 2.0;
86 // For each pixel
87 for( size_t i = 0; i < dimx; i++ )
88 for( size_t j = 0; j < dimy; j++ ) {
89 // Add a pairwise interaction with the left neighboring pixel
90 if( i >= 1 )
91 factors.push_back( createFactorIsing( vars[i*dimy+j], vars[(i-1)*dimy+j], J ) );
92 // Add a pairwise interaction with the upper neighboring pixel
93 if( j >= 1 )
94 factors.push_back( createFactorIsing( vars[i*dimy+j], vars[i*dimy+(j-1)], J ) );
95 // Get the pixel value
96 double x = img(i,j);
97 // Apply the nonlinear transformation to get the local field strength
98 double th = th_avg + th_width * tanh( (x - level) / scale );
99 // Add a single-variable interaction with strength th
100 factors.push_back( createFactorIsing( vars[i*dimy+j], th ) );
101
102 // For visualization, we calculate a grayscale level corresponding to the local field strength
103 unsigned char g = (unsigned char)((tanh(th) + 1.0) / 2.0 * 255.0);
104 // and store it in the evidence image
105 if( g > 127 ) {
106 evidence(i,j,0) = 255;
107 evidence(i,j,1) = 2 * (g - 127);
108 evidence(i,j,2) = 2 * (g - 127);
109 } else {
110 evidence(i,j,0) = 0;
111 evidence(i,j,1) = 0;
112 evidence(i,j,2) = 2*g;
113 }
114 }
115
116 // Create the factor graph out of the variables and factors
117 cout << "Creating the factor graph..." << endl;
118 return FactorGraph( factors.begin(), factors.end(), vars.begin(), vars.end(), factors.size(), vars.size() );
119 }
120
121
122 /// Perform an inference algorithm on a factorgraph, and visualize intermediate beliefs
123 /** \param fg The factorgraph (should have only binary variables)
124 * \param algOpts String specifying the inference algorithm and its options in the format "NAME[key1=val1,key2=val2,...,keyn=valn]"
125 * \param maxIter Maximum number of iterations to perform
126 * \param tol Convergence tolerance
127 * \param m Final magnetizations
128 * \param dimx Image width
129 * \param dimy Image height
130 * \param disp Handle for displaying the intermediate beliefs
131 */
132 double doInference( FactorGraph& fg, string algOpts, size_t maxIter, double tol, vector<double> &m, size_t dimx, size_t dimy, CImgDisplay &disp ) {
133 // Construct inference algorithm
134 cout << "Inference algorithm: " << algOpts << endl;
135 cout << "Constructing inference algorithm object..." << endl;
136 InfAlg* ia = newInfAlgFromString( algOpts, fg );
137
138 // Initialize inference algorithm
139 cout << "Initializing inference algorithm..." << endl;
140 ia->init();
141
142 // Initialize vector for storing the magnetizations
143 m = vector<double>( fg.nrVars(), 0.0 );
144
145 // Construct an image that will hold the intermediate single-variable beliefs
146 CImg<unsigned char> image( dimx, dimy, 1, 3 );
147
148 // maxDiff stores the current convergence level
149 double maxDiff = 1.0;
150
151 // Iterate while maximum number of iterations has not been
152 // reached and requested convergence level has not been reached
153 cout << "Starting inference algorithm..." << endl;
154 for( size_t iter = 0; iter < maxIter && maxDiff > tol; iter++ ) {
155 // Set magnetizations to beliefs
156 for( size_t i = 0; i < fg.nrVars(); i++ )
157 m[i] = ia->beliefV(i)[1] - ia->beliefV(i)[0];
158
159 // For each pixel, calculate a color coded magnetization
160 // and store it in the image for visualization
161 for( size_t i = 0; i < dimx; i++ )
162 for( size_t j = 0; j < dimy; j++ ) {
163 unsigned char g = (unsigned char)((m[i*dimy+j] + 1.0) / 2.0 * 255.0);
164 if( g > 127 ) {
165 image(i,j,0) = 255;
166 image(i,j,1) = 2 * (g - 127);
167 image(i,j,2) = 2 * (g - 127);
168 } else {
169 image(i,j,0) = 0;
170 image(i,j,1) = 0;
171 image(i,j,2) = 2*g;
172 }
173 }
174
175 // Display the image with the current beliefs
176 #ifndef NEW_CIMG
177 disp << image;
178 #else
179 disp = image;
180 #endif
181
182 // Perform the requested inference algorithm for only one step
183 ia->setMaxIter( iter + 1 );
184 maxDiff = ia->run();
185
186 // Output progress
187 cout << " Iterations = " << iter << ", maxDiff = " << maxDiff << endl;
188 }
189 cout << "Finished inference algorithm" << endl;
190
191 // Clean up inference algorithm
192 delete ia;
193
194 // Return reached convergence level
195 return maxDiff;
196 }
197
198
199 /// Main program
200 /** This example shows how one can use approximate inference in factor graphs
201 * on a simple vision task: given two images, identify smooth regions where these
202 * two images differ more than some threshold. This can be used to seperate
203 * foreground from background if one image contains the background and the other
204 * one the combination of background and foreground.
205 */
206 int main(int argc,char **argv) {
207 cout << "This program is part of libDAI - http://www.libdai.org/" << endl;
208 cout << "(Use the option -h for getting help with the command line arguments.)" << endl;
209 // Display program usage, when invoked from the command line with option '-h'
210 cimg_usage( "This example shows how libDAI can be used for a simple image segmentation task" );
211 // Get command line arguments
212 const char* file_i1 = cimg_option( "-i1", "example_img_in1.jpg", "Input image 1" );
213 const char* file_i2 = cimg_option( "-i2", "example_img_in2.jpg", "Input image 2" );
214 const char* file_o1 = cimg_option( "-o1", "example_img_out1.jpg", "Output image (local evidence)" );
215 const char* file_o2 = cimg_option( "-o2", "example_img_out2.jpg", "Output image (magnetizations)" );
216 const double J = cimg_option( "-J", 2.4, "Pairwise interaction strength (i.e., smoothing strength)" );
217 const double th_min = cimg_option( "-thmin", -3.0, "Local evidence strength of background" );
218 const double th_max = cimg_option( "-thmax", 3.2, "Local evidence strength of foreground" );
219 const double scale = cimg_option( "-scale", 40.0, "Typical difference in pixel values between fore- and background" );
220 const double pbg = cimg_option( "-pbg", 90.0, "Percentage of background in image" );
221 const char *infname = cimg_option( "-method", "BP[updates=SEQMAX,maxiter=1,tol=1e-9,logdomain=0]", "Inference method in format name[key1=val1,...,keyn=valn]" );
222 const size_t maxiter = cimg_option( "-maxiter", 100, "Maximum number of iterations for inference method" );
223 const double tol = cimg_option( "-tol", 1e-9, "Desired tolerance level for inference method" );
224 const char *file_fg = cimg_option( "-fg", "", "Output factor graph" );
225
226 // Read input images
227 cout << endl;
228 cout << "Reading input image 1 (" << file_i1 << ")..." << endl;
229 CImg<unsigned char> image1 = CImg<>( file_i1 );
230 cout << "Reading input image 2 (" << file_i2 << ")..." << endl;
231 CImg<unsigned char> image2 = CImg<>( file_i2 );
232
233 // Check image sizes
234 #ifndef NEW_CIMG
235 if( (image1.width != image2.width) || (image1.height != image2.height) )
236 cerr << "Error: input images should have same size." << endl;
237 size_t dimx = image1.width;
238 size_t dimy = image1.height;
239 #else
240 if( (image1.width() != image2.width()) || (image1.height() != image2.height()) )
241 cerr << "Error: input images should have same size." << endl;
242 size_t dimx = image1.width();
243 size_t dimy = image1.height();
244 #endif
245
246 // Display input images
247 cout << "Displaying input image 1..." << endl;
248 CImgDisplay disp1( image1, "Input image 1", 0 );
249 cout << "Displaying input image 2..." << endl;
250 CImgDisplay disp2( image2, "Input image 2", 0 );
251
252 // Construct absolute difference image
253 cout << "Constructing difference image..." << endl;
254 CImg<int> image3( image1 );
255 image3 -= image2;
256 image3.abs();
257 // Normalize difference image
258 image3.norm( 1 ); // 1 = L1, 2 = L2, -1 = Linf
259
260 // Normalize the difference by the average value of the background image
261 for( size_t i = 0; i < dimx; i++ ) {
262 for( size_t j = 0; j < dimy; j++ ) {
263 int avg = 0;
264 #ifndef NEW_CIMG
265 for( int c = 0; c < image1.dimv(); c++ )
266 avg += image1( i, j, c );
267 avg /= image1.dimv();
268 #else
269 for( int c = 0; c < image1.spectrum(); c++ )
270 avg += image1( i, j, c );
271 avg /= image1.spectrum();
272 #endif
273 image3( i, j, 0 ) /= (1.0 + avg / 255.0);
274 }
275 }
276 image3.normalize( 0, 255 );
277
278 // Display difference image
279 cout << "Displaying difference image..." << endl;
280 CImgDisplay disp3( image3, "Relative difference of both inputs", 0 );
281
282 // Convert difference image into a factor graph and store
283 // the local evidence in image4 for visualization
284 CImg<unsigned char> image4( dimx, dimy, 1, 3 );
285 cout << "Converting difference image into factor graph..." << endl;
286 FactorGraph fg = img2fg( image3, J, th_min, th_max, scale, pbg, image4 );
287
288 // Display local evidence
289 cout << "Displaying local evidence..." << endl;
290 CImgDisplay disp4( image4, "Local evidence", 0 );
291 cout << "Saving local evidence as JPEG in " << file_o1 << endl;
292 image4.save_jpeg( file_o1, 100 );
293 if( strlen( file_fg ) > 0 ) {
294 cout << "Saving factor graph as " << file_fg << endl;
295 fg.WriteToFile( file_fg );
296 }
297
298 // Solve the inference problem and visualize intermediate steps
299 CImgDisplay disp5( dimx, dimy, "Beliefs during inference", 0 );
300 vector<double> m; // Stores the final magnetizations
301 cout << "Solving the inference problem...please be patient!" << endl;
302 doInference( fg, infname, maxiter, tol, m, dimx, dimy, disp5 );
303
304 // Visualize the final magnetizations
305 for( size_t i = 0; i < dimx; i++ )
306 for( size_t j = 0; j < dimy; j++ ) {
307 unsigned char g = (unsigned char)((m[i*dimy+j] + 1.0) / 2.0 * 255.0);
308 if( g > 127 ) {
309 image4(i,j,0) = image2(i,j,0);
310 image4(i,j,1) = image2(i,j,1);
311 image4(i,j,2) = image2(i,j,2);
312 } else
313 #ifndef NEW_CIMG
314 for( size_t c = 0; c < (size_t)image4.dimv(); c++ )
315 image4(i,j,c) = 255;
316 #else
317 for( size_t c = 0; c < (size_t)image4.spectrum(); c++ )
318 image4(i,j,c) = 255;
319 #endif
320 }
321 cout << "Displaying the final result of the segmentation problem..." << endl;
322 CImgDisplay main_disp( image4, "Foreground/background segmentation result", 0 );
323
324 cout << "Saving the final result of the segmentation problem as JPEG in " << file_o2 << endl;
325 image4.save_jpeg( file_o2, 100 );
326
327 cout << "Close the last image display in order to finish." << endl;
328 #ifndef NEW_CIMG
329 while( !main_disp.is_closed )
330 cimg::wait( 40 );
331 #else
332 while( !main_disp.is_closed() )
333 cimg::wait( 40 );
334 #endif
335
336 return 0;
337 }