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