Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Celeste.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2008 by Tim Nugent *
3  * timnugent@gmail.com *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17  ***************************************************************************/
18 
19 #include <iostream>
20 #include <vigra/stdimage.hxx>
21 #include <vigra/resizeimage.hxx>
22 #include <vigra/inspectimage.hxx>
23 #include <vigra/copyimage.hxx>
24 #include <vigra/transformimage.hxx>
25 #include <vigra/initimage.hxx>
26 #include "vigra/colorconversions.hxx"
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <stdlib.h>
30 #include <string>
31 #include <vector>
32 #include "Gabor.h"
33 #include "Utilities.h"
34 #include "CelesteGlobals.h"
35 #include "Celeste.h"
36 #include "svm.h"
37 #include <stdio.h>
38 
39 namespace celeste
40 {
41 
42 typedef vigra::BRGBImage::PixelType RGB;
43 
44 // load SVM model
45 bool loadSVMmodel(struct svm_model*& model, std::string& model_file)
46 {
47  if((model = svm_load_model(model_file.c_str())) == 0)
48  {
49  std::cout << "Couldn't load model file '" << model_file << "'" << std::endl << std::endl;
50  return false;
51  }
52  else
53  {
54  std::cout << "Loaded model file:\t" << model_file << std::endl;
55  return true;
56  }
57 };
58 
59 // destroy SVM model
60 void destroySVMmodel(struct svm_model*& model)
61 {
63 };
64 
65 // prepare image for use with celeste (downscale, converting in Luv)
66 void prepareCelesteImage(vigra::UInt16RGBImage& rgb,vigra::UInt16RGBImage& luv, int& resize_dimension, double& sizefactor,bool verbose)
67 {
68  // Max dimension
69  sizefactor = 1;
70  int nw=rgb.width();
71  int nh=rgb.height();
72 
73  if(nw >= nh)
74  {
75  if (resize_dimension >= nw )
76  {
77  resize_dimension = nw;
78  }
79  }
80  else
81  {
82  if (resize_dimension >= nh)
83  {
84  resize_dimension = nh;
85  }
86  }
87  //std::cout << "Re-size dimenstion:\t" << resize_dimension << std::endl;
88  if(verbose)
89  std::cout << "Image dimensions:\t" << rgb.width() << " x " << rgb.height() << std::endl;
90 
91  // Re-size to max dimension
92  vigra::UInt16RGBImage temp;
93 
94  if (rgb.width() > resize_dimension || rgb.height() > resize_dimension)
95  {
96  if (rgb.width() >= rgb.height())
97  {
98  sizefactor = (double)resize_dimension/rgb.width();
99  // calculate new image size
100  nw = resize_dimension;
101  nh = static_cast<int>(0.5 + (sizefactor*rgb.height()));
102  }
103  else
104  {
105  sizefactor = (double)resize_dimension/rgb.height();
106  // calculate new image size
107  nw = static_cast<int>(0.5 + (sizefactor*rgb.width()));
108  nh = resize_dimension;
109  }
110 
111  if(verbose)
112  {
113  std::cout << "Scaling by:\t\t" << sizefactor << std::endl;
114  std::cout << "New dimensions:\t\t" << nw << " x " << nh << std::endl;
115  };
116 
117  // create a RGB image of appropriate size
118  temp.resize(nw,nh);
119 
120  // resize the image, using a bi-cubic spline algorithm
121  vigra::resizeImageNoInterpolation(srcImageRange(rgb),destImageRange(temp));
122  //exportImage(srcImageRange(out), ImageExportInfo("test.tif").setPixelType("UINT16"));
123  }
124  else
125  {
126  temp.resize(nw,nh);
128  };
129 
130  // Convert to Luv colour space
131  luv.resize(temp.width(),temp.height());
132  vigra::transformImage(srcImageRange(temp), destImage(luv), vigra::RGB2LuvFunctor<double>() );
133  //exportImage(srcImageRange(luv), ImageExportInfo("test_luv.tif").setPixelType("UINT16"));
134  temp.resize(0,0);
135 };
136 
137 // converts the given Luv image into arrays for Gabors filtering
138 void prepareGaborImage(vigra::UInt16RGBImage& luv, float**& pixels)
139 {
140  pixels = CreateMatrix( (float)0, luv.height(), luv.width() );
141  // Prepare framebuf for Gabor API, we need only L channel
142  for (int i = 0; i < luv.height(); i++ )
143  {
144  for (int j = 0; j < luv.width(); j++ )
145  {
146  pixels[i][j] = luv(j,i)[0];
147  //std::cout << i << " " << j << " = " << k << " - " << frameBuf[k] << std::endl;
148  }
149  }
150 };
151 
152 //classify the points with SVM
153 std::vector<double> classifySVM(struct svm_model* model, int gNumLocs,int**& gLocations,int width,int height,int vector_length, float*& response,int gRadius,vigra::UInt16RGBImage& luv,bool needsMoreIndex=false)
154 {
155  std::vector<double> svm_response;
156  // Integers and containers for libsvm
157  int max_nr_attr = 56;
158  int nr_class=svm_get_nr_class(model);
159  struct svm_node *gabor_responses = (struct svm_node *) malloc(max_nr_attr*sizeof(struct svm_node));
160  double *prob_estimates = (double *) malloc(nr_class*sizeof(double));
161 
162  for (int j = 0; j < gNumLocs; j++)
163  {
164  unsigned int feature = 1;
165 
166  if(needsMoreIndex)
167  {
168  if(j >= max_nr_attr - 1)
169  {
170  max_nr_attr *= 2;
171  struct svm_node* newPointer = (struct svm_node *) realloc(gabor_responses,max_nr_attr*sizeof(struct svm_node));
172  if(newPointer == NULL)
173  {
174  svm_response.clear();
175  free(gabor_responses);
176  free(prob_estimates);
177  return svm_response;
178  }
179  gabor_responses=newPointer;
180  }
181  };
182 
183  for ( int v = (j * vector_length); v < ((j + 1) * vector_length); v++)
184  {
185  gabor_responses[feature-1].index = feature;
186  gabor_responses[feature-1].value = response[v];
187  //std::cout << feature << ":" << response[v] << " ";
188  feature++;
189  }
190 
191  // Work out average colour and variance
192  vigra::FindAverageAndVariance<vigra::UInt16RGBImage::PixelType> average;
193  vigra::inspectImage(srcIterRange(
194  luv.upperLeft()+vigra::Diff2D(gLocations[j][0]-gRadius,gLocations[j][1]-gRadius),
195  luv.upperLeft()+vigra::Diff2D(gLocations[j][0]+gRadius,gLocations[j][1]+gRadius)
196  ),average);
197  // Add these colour features to feature vector
198 
199  gabor_responses[feature-1].index = feature;
200  gabor_responses[feature-1].value = average.average()[1];
201  //std::cout << feature << ":" << u_ave << " ";
202  feature++;
203  gabor_responses[feature-1].index = feature;
204  gabor_responses[feature-1].value = sqrt(average.variance()[1]);
205  //std::cout << feature << ":" << std_u << " ";
206  feature++;
207  gabor_responses[feature-1].index = feature;
208  gabor_responses[feature-1].value = average.average()[2];
209  //std::cout << feature << ":" << v_ave << " ";
210  feature++;
211  gabor_responses[feature-1].index = feature;
212  gabor_responses[feature-1].value = sqrt(average.variance()[2]);
213  //std::cout << feature << ":" << std_v << " ";
214  feature++;
215  gabor_responses[feature-1].index = feature;
216  gabor_responses[feature-1].value = luv(gLocations[j][0],gLocations[j][1])[1];
217  //std::cout << feature << ":" << u_values[pixel_number] << " ";
218  feature++;
219  gabor_responses[feature-1].index = feature;
220  gabor_responses[feature-1].value = luv(gLocations[j][0],gLocations[j][1])[2];
221  //std::cout << feature << ":" << v_values[pixel_number] << " " << std::endl;
222  gabor_responses[feature].index = -1;
223 
224  svm_predict_probability(model,gabor_responses,prob_estimates);
225  svm_response.push_back(prob_estimates[0]);
226  }
227  // Free up libsvm stuff
228  free(gabor_responses);
229  free(prob_estimates);
230  return svm_response;
231 };
232 
233 // create a grid of control points for creating masks
234 void createGrid(int& gNumLocs,int**& gLocations,int gRadius,int width, int height)
235 {
236  int spacing=(gRadius*2)+1;
237  for (int i = gRadius; i < height - gRadius; i += spacing )
238  {
239  for (int j = gRadius; j < width - gRadius; j += spacing )
240  {
241  gNumLocs++;
242  }
243  // Add extra FP at the end of each row in case width % gRadius
244  gNumLocs++;
245  }
246 
247  // Add extra FP at the end of each row in case nh % gRadius
248  for (int j = gRadius; j < width - gRadius; j += spacing )
249  {
250  gNumLocs++;
251  }
252 
253  // Create the storage matrix
254  gLocations = CreateMatrix( (int)0, gNumLocs, 2);
255  gNumLocs = 0;
256  for (int i = gRadius; i < height - gRadius; i += spacing )
257  {
258  for (int j = gRadius; j < width - gRadius; j += spacing )
259  {
260  gLocations[gNumLocs][0] = j;
261  gLocations[gNumLocs][1] = i;
262  //std::cout << "fPoint " << gNumLocs << ":\t" << i << " " << j << std::endl;
263  gNumLocs++;
264  }
265 
266  // Add extra FP at the end of each row in case width % spacing
267  if (width % spacing)
268  {
269  gLocations[gNumLocs][0] = width - gRadius - 1;
270  gLocations[gNumLocs][1] = i;
271  //std::cout << "efPoint " << gNumLocs << ":\t" << i << " " << nw - gRadius - 1 << std::endl;
272  gNumLocs++;
273  }
274  }
275 
276  // Add extra FP at the end of each row in case height % spacing
277  if (height % spacing)
278  {
279  for (int j = gRadius; j < width - gRadius; j += spacing )
280  {
281  gLocations[gNumLocs][0] = j;
282  gLocations[gNumLocs][1] = height - gRadius - 1;
283  //std::cout << "efPoint " << gNumLocs << ":\t" << nh - gRadius - 1 << " " << j << std::endl;
284  gNumLocs++;
285  }
286  }
287 };
288 
289 //generates the celeste mask on base of given responses and locations
290 void generateMask(vigra::BImage& mask,int& gNumLocs, int**& gLocations,std::vector<double> svm_responses,int gRadius, double threshold)
291 {
292  for ( int j = 0; j < gNumLocs; j++ )
293  {
294  if (svm_responses[j] >= threshold)
295  {
296  unsigned int sub_x0 = gLocations[j][0] - gRadius;
297  unsigned int sub_y0 = gLocations[j][1] - gRadius;
298  unsigned int sub_x1 = gLocations[j][0] + gRadius + 1;
299  unsigned int sub_y1 = gLocations[j][1] + gRadius + 1;
300  //std::cout << sub_x0 << ","<< sub_y0 << " - " << sub_x1 << "," << sub_y1 << std::endl;
301 
302  // Set region to black
303  vigra::initImage(srcIterRange(mask.upperLeft() + vigra::Diff2D(sub_x0, sub_y0),
304  mask.upperLeft() + vigra::Diff2D(sub_x1, sub_y1)), 0);
305  }
306  else
307  {
308  //std::cout << "Non-cloud\t(score " << prob_estimates[0] << " <= " << threshold << ")" << std::endl;
309  }
310  }
311 };
312 
313 vigra::BImage* getCelesteMask(struct svm_model* model, vigra::UInt16RGBImage& input, int radius, float threshold, int resize_dimension,bool adaptThreshold,bool verbose)
314 {
315  vigra::UInt16RGBImage luv;
316  double sizefactor=1.0;
317  prepareCelesteImage(input,luv,resize_dimension,sizefactor,verbose);
318 
319  // Prepare Gabor API array
320  float** pixels=NULL;
321  prepareGaborImage(luv,pixels);
322 
323  int** gLocations = NULL;
324  int gNumLocs = 0;
325 
326  // Create grid of fiducial points
327  createGrid(gNumLocs,gLocations,radius,luv.width(),luv.height());
328 
329  int len = 0;
330  float* mask_response=NULL;
331  mask_response = ProcessChannel(pixels,luv.width(),luv.height(),gNumLocs,gLocations,radius,mask_response,&len);
332  // Turn the response into SVM vector, and add colour features
333  std::vector<double> svm_responses=classifySVM(model,gNumLocs,gLocations,luv.width(),luv.height(),(int)len/gNumLocs,mask_response,radius,luv);
334  delete [] mask_response;
335 
336  if(adaptThreshold)
337  {
338  double minVal=1;
339  for(unsigned int i=0;i<svm_responses.size();i++)
340  {
341  if(svm_responses[i]<minVal)
342  {
343  minVal=svm_responses[i];
344  };
345  };
346  if(threshold<minVal)
347  {
348  threshold = std::min(minVal + 0.1, 1.0);
349  };
350  };
351  // Create mask of same dimensions
352  vigra::BImage mask_out(luv.width(), luv.height(),255);
353  generateMask(mask_out,gNumLocs,gLocations,svm_responses,radius,threshold);
354  // Re-size mask to match original image
355  vigra::BImage* mask_resize = new vigra::BImage(input.size());
356  vigra::resizeImageNoInterpolation(vigra::srcImageRange(mask_out), vigra::destImageRange(*mask_resize));
357  DisposeMatrix(pixels,luv.height());
358  DisposeMatrix(gLocations,gNumLocs);
359  mask_out.resize(0,0);
360  return mask_resize;
361 };
362 
363 HuginBase::UIntSet getCelesteControlPoints(struct svm_model* model, vigra::UInt16RGBImage& input, HuginBase::CPointVector cps, int radius, float threshold, int resize_dimension,bool verbose)
364 {
365  HuginBase::UIntSet cloudCP;
366  vigra::UInt16RGBImage luv;
367  double sizefactor=1.0;
368  prepareCelesteImage(input,luv,resize_dimension,sizefactor,verbose);
369 
370  // Prepare Gabor API array
371  float** pixels=NULL;
372  prepareGaborImage(luv,pixels);
373 
374  int gNumLocs = cps.size();
375  int** gLocations = CreateMatrix( (int)0, gNumLocs, 2);
376  for(unsigned int j=0;j<cps.size();j++)
377  {
378  HuginBase::ControlPoint cp=cps[j].second;
379  gLocations[j][0] = int(cp.x1 * sizefactor);
380  gLocations[j][1] = int(cp.y1 * sizefactor);
381  // Move CPs to border if the filter radius is out of bounds
382  if (gLocations[j][0] <= radius)
383  {
384  gLocations[j][0] = radius + 1;
385  }
386  if (gLocations[j][1] <= radius)
387  {
388  gLocations[j][1] = radius + 1;
389  }
390  if (gLocations[j][0] >= luv.width() - radius)
391  {
392  gLocations[j][0] = luv.width() - radius - 1;
393  }
394  if (gLocations[j][1] >= luv.height() - radius)
395  {
396  gLocations[j][1] = luv.height() - radius - 1;
397  }
398  };
399 
400  int len = 0;
401  float* response=NULL;
402  response = ProcessChannel(pixels,luv.width(),luv.height(),gNumLocs,gLocations,radius,response,&len);
403  // Turn the response into SVM vector, and add colour features
404  std::vector<double> svm_responses = classifySVM(model, gNumLocs, gLocations, luv.width(), luv.height(), (int)len / gNumLocs, response, radius, luv);
405  delete [] response;
406 
407  for(unsigned int i=0;i<svm_responses.size();i++)
408  {
409  if(svm_responses[i]>=threshold)
410  {
411  cloudCP.insert(cps[i].first);
412  };
413  };
414  DisposeMatrix(pixels,luv.height());
415  DisposeMatrix(gLocations,gNumLocs);
416 
417  return cloudCP;
418 };
419 
420 } // end of namespace
void destroySVMmodel(struct svm_model *&model)
frees the resource of model
Definition: Celeste.cpp:60
double svm_predict_probability(const svm_model *model, const svm_node *x, double *prob_estimates)
Definition: svm.cpp:2641
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
void createGrid(int &gNumLocs, int **&gLocations, int gRadius, int width, int height)
Definition: Celeste.cpp:234
vigra::BImage * getCelesteMask(struct svm_model *model, vigra::UInt16RGBImage &input, int radius, float threshold, int resize_dimension, bool adaptThreshold, bool verbose)
calculates the mask using SVM
Definition: Celeste.cpp:313
void generateMask(vigra::BImage &mask, int &gNumLocs, int **&gLocations, std::vector< double > svm_responses, int gRadius, double threshold)
Definition: Celeste.cpp:290
represents a control point
Definition: ControlPoint.h:38
std::vector< double > classifySVM(struct svm_model *model, int gNumLocs, int **&gLocations, int width, int height, int vector_length, float *&response, int gRadius, vigra::UInt16RGBImage &luv, bool needsMoreIndex=false)
Definition: Celeste.cpp:153
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
void svm_free_and_destroy_model(svm_model **model_ptr_ptr)
Definition: svm.cpp:3073
void prepareCelesteImage(vigra::UInt16RGBImage &rgb, vigra::UInt16RGBImage &luv, int &resize_dimension, double &sizefactor, bool verbose)
Definition: Celeste.cpp:66
svm_model * svm_load_model(const char *model_file_name)
Definition: svm.cpp:2933
bool loadSVMmodel(struct svm_model *&model, std::string &model_file)
loads the SVM model from file
Definition: Celeste.cpp:45
float * ProcessChannel(float **image, int w, int h, int gNumLocs, int **&gLocations, int gRadius, float *response, int *len)
Definition: Gabor.cpp:33
void DisposeMatrix(int **matrix, int row)
Definition: Utilities.cpp:122
static deghosting::EMoR response(0.0f)
double value
Definition: svm.h:47
vigra::BRGBImage::PixelType RGB
Definition: Celeste.cpp:42
int ** CreateMatrix(int val, int row, int col)
Definition: Utilities.cpp:101
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
std::vector< CPoint > CPointVector
Definition: ControlPoint.h:102
int svm_get_nr_class(const svm_model *model)
Definition: svm.cpp:2514
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition: threshold.h:41
HuginBase::UIntSet getCelesteControlPoints(struct svm_model *model, vigra::UInt16RGBImage &input, HuginBase::CPointVector cps, int radius, float threshold, int resize_dimension, bool verbose)
Definition: Celeste.cpp:363
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:312
void prepareGaborImage(vigra::UInt16RGBImage &luv, float **&pixels)
Definition: Celeste.cpp:138
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
Definition: openmp_vigra.h:305
static T min(T x, T y)
Definition: svm.cpp:62