Hugin trunk 0.1
Loading...
Searching...
No Matches
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
39namespace celeste
40{
41
42typedef vigra::BRGBImage::PixelType RGB;
43
44// load SVM model
45bool 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
60void destroySVMmodel(struct svm_model*& model)
61{
63};
64
65// prepare image for use with celeste (downscale, converting in Luv)
66void 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);
127 vigra::copyImage(srcImageRange(rgb),destImage(temp));
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
138void 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
153std::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
234void 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
290void 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
313vigra::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
363HuginBase::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
represents a control point
static deghosting::EMoR response(0.0f)
std::vector< CPoint > CPointVector
std::set< unsigned int > UIntSet
void prepareGaborImage(vigra::UInt16RGBImage &luv, float **&pixels)
Definition Celeste.cpp:138
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
void DisposeMatrix(int **matrix, int row)
svm_model * svm_load_model(const char *model_file_name)
Definition svm.cpp:2933
vigra::BRGBImage::PixelType RGB
Definition Celeste.cpp:42
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 svm_free_and_destroy_model(svm_model **model_ptr_ptr)
Definition svm.cpp:3073
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
int ** CreateMatrix(int val, int row, int col)
float * ProcessChannel(float **image, int w, int h, int gNumLocs, int **&gLocations, int gRadius, float *response, int *len)
Definition Gabor.cpp:33
double svm_predict_probability(const svm_model *model, const svm_node *x, double *prob_estimates)
Definition svm.cpp:2641
void generateMask(vigra::BImage &mask, int &gNumLocs, int **&gLocations, std::vector< double > svm_responses, int gRadius, double threshold)
Definition Celeste.cpp:290
void createGrid(int &gNumLocs, int **&gLocations, int gRadius, int width, int height)
Definition Celeste.cpp:234
int svm_get_nr_class(const svm_model *model)
Definition svm.cpp:2514
void destroySVMmodel(struct svm_model *&model)
frees the resource of model
Definition Celeste.cpp:60
bool loadSVMmodel(struct svm_model *&model, std::string &model_file)
loads the SVM model from file
Definition Celeste.cpp:45
void prepareCelesteImage(vigra::UInt16RGBImage &rgb, vigra::UInt16RGBImage &luv, int &resize_dimension, double &sizefactor, bool verbose)
Definition Celeste.cpp:66
double value
Definition svm.h:47
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