1 #include <iostream>
2 #include <cmath>
3 #include <fstream>
4 #include <sstream>
5 #include "brec_part_gaussian.h"
6 //:
7 // \file
8 // \author Ozge C Ozcanli (ozge@lems.brown.edu)
9 // \date Oct 16, 2008
10
11 #include "brec_part_gaussian_sptr.h"
12
13 #include "vil/vil_convert.h"
14 #include "vil/vil_save.h"
15 #include <vil/algo/vil_threshold.h>
16 #include "vil/vil_load.h"
17 #include <brip/brip_vil_float_ops.h>
18 #include "vnl/vnl_math.h"
19
20 #include <bxml/bxml_find.h>
21 #ifdef _MSC_VER
22 # include "vcl_msvc_warnings.h"
23 #endif
24 #include "vnl/vnl_gamma.h"
25
26 #include <bsta/algo/bsta_fit_weibull.h>
27 #include <bsta/bsta_histogram.h>
28 #include <bsta/bsta_gauss_sf1.h>
29
30 #include "vul/vul_file.h"
31 #include "vul/vul_psfile.h"
32
33 //: strength_threshold in [0,1] - min strength to declare the part as detected
extract_gaussian_primitives(const vil_image_resource_sptr & img,float lambda0,float lambda1,float theta,bool bright,float cutoff_percentage,float strength_threshold,unsigned type,std::vector<brec_part_instance_sptr> & parts)34 bool extract_gaussian_primitives(const vil_image_resource_sptr& img, float lambda0, float lambda1, float theta, bool bright, float cutoff_percentage, float strength_threshold, unsigned type, std::vector<brec_part_instance_sptr>& parts)
35 {
36 vil_image_view<float> fimg = brip_vil_float_ops::convert_to_float(img);
37 vil_image_view<float> extr = brip_vil_float_ops::extrema(fimg, lambda0, lambda1, theta, bright, false,true);
38 if (extr.nplanes() < 2)
39 return false;
40
41 unsigned ni = fimg.ni();
42 unsigned nj = fimg.nj();
43
44 vil_image_view<float> res(ni, nj), mask(ni, nj);
45 for (unsigned j = 0; j<nj; ++j)
46 for (unsigned i = 0; i<ni; ++i)
47 {
48 res(i,j) = extr(i,j,0);
49 mask(i,j) = extr(i,j,1);
50 }
51
52 float min, max;
53 vil_math_value_range(res, min, max);
54
55 #if 0
56 std::cout << "res min: " << min << " max: " << max << std::endl;
57 vil_image_view<vxl_byte> res_o(ni, nj);
58 vil_convert_stretch_range_limited(res, res_o, min, max);
59 vil_save(res_o, "./temp.png");
60 #endif
61
62 float val = max;
63 #if 0
64 // find the top 10 percentile of the output map and convert it into a prob map (scale to [0,1] range) accordingly
65 vil_math_value_range_percentile(res, 1.0, val);
66 std::cout << "res top 10 percentile value: " << val << std::endl;
67 #endif // 0
68 vil_image_view<float> strength_map(ni, nj);
69 vil_convert_stretch_range_limited(res, strength_map, 0.0f, val, 0.0f, 1.0f);
70 #if 0
71 vil_math_value_range(strength_map, min, max);
72 std::cout << "strength_map min: " << min << " max: " << max << std::endl;
73 vil_convert_stretch_range_limited(strength_map, res_o, min, max);
74 vil_save(res_o, "./strength_map.png");
75 #endif
76
77 // extract all the parts from the responses
78 for (unsigned j = 0; j<nj; ++j)
79 for (unsigned i = 0; i<ni; ++i)
80 {
81 if (strength_map(i,j) > strength_threshold) {
82 brec_part_gaussian_sptr dp = new brec_part_gaussian((float)i, (float)j, strength_map(i,j), lambda0, lambda1, theta, bright, type);
83 dp->cutoff_percentage_ = cutoff_percentage;
84 parts.emplace_back(dp->cast_to_instance());
85 }
86 }
87
88 #if 0
89 vil_image_resource_sptr img_resc = vil_new_image_resource_of_view(img);
90 vil_image_resource_sptr res_resc = vil_new_image_resource_of_view(res);
91 vil_image_resource_sptr msk_resc = vil_new_image_resource_of_view(mask);
92 vil_image_view<vil_rgb<vxl_byte> > rgb =
93 brip_vil_float_ops::combine_color_planes(img_resc, res_resc, msk_resc);
94 vil_save(rgb, "./temp.png");
95 vil_math_value_range(fimg, min, max);
96 std::cout << "img min: " << min << " max: " << max << std::endl;
97 vil_math_value_range(mask, min, max);
98 std::cout << "mask min: " << min << " max: " << max << std::endl;
99
100 vil_image_view<bool> res_bool;
101 vil_threshold_above(res, res_bool, max/2);
102 vil_image_view<float> res_bool_f;
103 vil_convert_cast(res_bool, res_bool_f);
104 vil_convert_stretch_range_limited(res_bool_f, res_o, 0.0f, 1.0f);
105 vil_save(res_o, "./temp_thresholded.png");
106 #endif
107
108 return true;
109 }
110
initialize_mask()111 void brec_part_gaussian::initialize_mask()
112 {
113 vbl_array_2d<float> fa;
114 brip_vil_float_ops::extrema_kernel_mask(lambda0_, lambda1_, theta_, fa, mask_);
115 unsigned nrows = fa.rows(), ncols = fa.cols();
116 rj_ = (nrows-1)/2;
117 ri_ = (ncols-1)/2;
118 }
119
brec_part_gaussian(float x,float y,float strength,float lambda0,float lambda1,float theta,bool bright,unsigned type)120 brec_part_gaussian::brec_part_gaussian(float x, float y, float strength, float lambda0, float lambda1, float theta, bool bright, unsigned type)
121 : brec_part_instance(0, type, brec_part_instance_kind::GAUSSIAN, x, y, strength),
122 lambda0_(lambda0), lambda1_(lambda1), theta_(theta), bright_(bright), cutoff_percentage_(0.01f), lambda_(0.0f), k_(0.0f), fitted_weibull_(false)
123 {
124 initialize_mask();
125 }
126
127 //: the following constructor should only be used during parsing
128 // Mask should be initialized after lambda values are parsed
brec_part_gaussian()129 brec_part_gaussian::brec_part_gaussian() : brec_part_instance(0, 0, brec_part_instance_kind::GAUSSIAN, 0.0f, 0.0f, 0.0f),
130 lambda0_(0), lambda1_(0), theta_(0), bright_(true), cutoff_percentage_(0.0f), lambda_(0.0f), k_(0.0f), fitted_weibull_(false)
131 {
132 }
133
cast_to_gaussian(void)134 brec_part_gaussian* brec_part_gaussian::cast_to_gaussian(void)
135 {
136 return this;
137 }
138
mark_receptive_field(vil_image_view<vxl_byte> & img,unsigned plane)139 bool brec_part_gaussian::mark_receptive_field(vil_image_view<vxl_byte>& img, unsigned plane)
140 {
141 if (img.nplanes() <= plane)
142 return false;
143
144 vbl_array_2d<bool> mask;
145 vbl_array_2d<float> kernel;
146 brip_vil_float_ops::extrema_kernel_mask(lambda0_, lambda1_, theta_, kernel, mask);
147
148 unsigned nrows = mask.rows();
149 unsigned ncols = mask.cols();
150
151 int js = (int)std::floor(y_ - (float)nrows/2.0f + 0.5f);
152 int is = (int)std::floor(x_ - (float)ncols/2.0f + 0.5f);
153 int je = (int)std::floor(y_ + (float)nrows/2.0f + 0.5f);
154 int ie = (int)std::floor(x_ + (float)ncols/2.0f + 0.5f);
155
156 int ni = (int)img.ni();
157 int nj = (int)img.nj();
158 for (int i = is; i < ie; i++)
159 for (int j = js; j < je; j++) {
160 int mask_i = i - is;
161 int mask_j = j - js;
162 if (mask[mask_j][mask_i] && i >= 0 && j >= 0 && i < ni && j < nj) {
163 int nw = (int)img(i, j, plane) + int(strength_*255);
164 if (nw > 255) img(i, j, plane) = 255;
165 else img(i, j, plane) = (vxl_byte)nw;
166 }
167 }
168
169 return true;
170 }
171
mark_receptive_field(vil_image_view<float> & img,float val)172 bool brec_part_gaussian::mark_receptive_field(vil_image_view<float>& img, float val)
173 {
174 vbl_array_2d<bool> mask;
175 vbl_array_2d<float> kernel;
176 brip_vil_float_ops::extrema_kernel_mask(lambda0_, lambda1_, theta_, kernel, mask, cutoff_percentage_);
177
178 unsigned nrows = mask.rows();
179 unsigned ncols = mask.cols();
180
181 int js = (int)std::floor(y_ - (float)nrows/2.0f + 0.5f);
182 int is = (int)std::floor(x_ - (float)ncols/2.0f + 0.5f);
183 int je = (int)std::floor(y_ + (float)nrows/2.0f + 0.5f);
184 int ie = (int)std::floor(x_ + (float)ncols/2.0f + 0.5f);
185
186 int ni = (int)img.ni();
187 int nj = (int)img.nj();
188 for (int i = is; i < ie; i++)
189 for (int j = js; j < je; j++) {
190 int mask_i = i - is;
191 int mask_j = j - js;
192 if (mask[mask_j][mask_i] && i >= 0 && j >= 0 && i < ni && j < nj) {
193 /*if ((img(i, j) + val) > 1.0f)
194 img(i, j) = 1.0f;
195 else
196 img(i, j) += val;*/
197 if (img(i,j) < val)
198 img(i,j) = val;
199 }
200 }
201 return true;
202 }
203
mark_center(vil_image_view<float> & img,float val)204 bool brec_part_gaussian::mark_center(vil_image_view<float>& img, float val)
205 {
206 int ni = (int)img.ni();
207 int nj = (int)img.nj();
208
209 int ic = (int)std::floor(x_ + 0.5f);
210 int jc = (int)std::floor(y_ + 0.5f);
211 if (ic >= 0 && jc >= 0 && ic < ni && jc < nj)
212 if (img(ic, jc) < val)
213 img(ic, jc) = val;
214
215 return true;
216 }
217
mark_center(vil_image_view<vxl_byte> & img,unsigned plane)218 bool brec_part_gaussian::mark_center(vil_image_view<vxl_byte>& img, unsigned plane)
219 {
220 if (img.nplanes() <= plane)
221 return false;
222
223 int ni = (int)img.ni();
224 int nj = (int)img.nj();
225
226 int ic = (int)std::floor(x_ + 0.5f);
227 int jc = (int)std::floor(y_ + 0.5f);
228 if (ic >= 0 && jc >= 0 && ic < ni && jc < nj)
229 img(ic, jc, plane) = (vxl_byte)(strength_*255);
230
231 return true;
232 }
233
234 vnl_vector_fixed<float,2>
direction_vector(void)235 brec_part_gaussian::direction_vector(void) // return a unit vector that gives direction of this instance in the image
236 {
237 vnl_vector_fixed<float,2> v;
238 double theta_rad = theta_*vnl_math::pi_over_180;
239 v(0) = (float)std::cos(theta_rad);
240 v(1) = (float)std::sin(theta_rad);
241 return v;
242 }
243
244
xml_element()245 bxml_data_sptr brec_part_gaussian::xml_element()
246 {
247 bxml_data_sptr data_super = brec_part_instance::xml_element();
248
249 bxml_element* data = new bxml_element("gaussian");
250
251 data->set_attribute("lambda0",lambda0_);
252 data->set_attribute("lambda1",lambda1_);
253 data->set_attribute("theta",theta_);
254 if (bright_)
255 data->set_attribute("bright",1);
256 else
257 data->set_attribute("bright",0);
258
259 data->set_attribute("cutoff_perc", cutoff_percentage_);
260 if (fitted_weibull_)
261 data->set_attribute("fitted_weibull", 1);
262 else
263 data->set_attribute("fitted_weibull", 0);
264 data->set_attribute("lambda",lambda_);
265 data->set_attribute("k",k_);
266 data->set_attribute("lambda_nc",lambda_non_class_);
267 data->set_attribute("k_nc",k_non_class_);
268
269 data->append_text("\n ");
270 data->append_data(data_super);
271 data->append_text("\n ");
272 #if 0
273 ((bxml_element*)data_super.ptr())->append_data(data);
274 ((bxml_element*)data_super.ptr())->append_text("\n ");
275 #endif // 0
276 return data;
277 }
278
xml_parse_element(bxml_data_sptr data)279 bool brec_part_gaussian::xml_parse_element(bxml_data_sptr data)
280 {
281 bxml_element query("gaussian");
282 bxml_data_sptr g_root = bxml_find_by_name(data, query);
283
284 if (!g_root)
285 return false;
286
287 if (g_root->type() == bxml_data::ELEMENT) {
288 ((bxml_element*)g_root.ptr())->get_attribute("lambda_nc", lambda_non_class_);
289 ((bxml_element*)g_root.ptr())->get_attribute("k_nc", k_non_class_);
290
291 bool found = (((bxml_element*)g_root.ptr())->get_attribute("lambda0", lambda0_) &&
292 ((bxml_element*)g_root.ptr())->get_attribute("lambda1", lambda1_) &&
293 ((bxml_element*)g_root.ptr())->get_attribute("theta", theta_) &&
294 ((bxml_element*)g_root.ptr())->get_attribute("cutoff_perc", cutoff_percentage_) &&
295 ((bxml_element*)g_root.ptr())->get_attribute("lambda", lambda_) &&
296 ((bxml_element*)g_root.ptr())->get_attribute("k", k_)
297 );
298
299 int bright_int;
300 found = found && ((bxml_element*)g_root.ptr())->get_attribute("bright", bright_int);
301
302 if (!found)
303 return false;
304
305 bright_ = bright_int != 0;
306
307 int fitted_w_int;
308 ((bxml_element*)g_root.ptr())->get_attribute("fitted_weibull", fitted_w_int);
309 fitted_weibull_ = fitted_w_int != 0;
310
311 initialize_mask();
312
313 return brec_part_instance::xml_parse_element(g_root);
314 }
315 else
316 return false;
317 }
318
string_identifier()319 std::string brec_part_gaussian::string_identifier()
320 {
321 std::stringstream l0, l1, theta; l0 << lambda0_; l1 << lambda1_; theta << theta_;
322 std::string str = "gaussian_"+l0.str()+"_"+l1.str()+"_"+theta.str()+"_";
323 if (bright_)
324 str = str+"bright";
325 else
326 str = str+"dark";
327
328 return str;
329 }
330
331 //: the mean img and the std_dev img are float images with values in [0,1] range
construct_bg_response_model(vil_image_view<float> & mean_img,vil_image_view<float> & std_dev_img,vil_image_view<float> & lambda_img,vil_image_view<float> & k_img)332 bool brec_part_gaussian::construct_bg_response_model(vil_image_view<float>& mean_img,
333 vil_image_view<float>& std_dev_img,
334 vil_image_view<float> &lambda_img, // output lambda img
335 vil_image_view<float> &k_img) // output k img
336 {
337 unsigned ni = mean_img.ni();
338 unsigned nj = mean_img.nj();
339
340 // find the response img for this operator
341 vil_image_view<float> mean_res = brip_vil_float_ops::extrema(mean_img, lambda0_, lambda1_, theta_, bright_, false, true); // was "false"
342 #if 0
343 vil_save(mean_res, "mean_response_img.tiff");
344 #endif // 0
345
346 if (mean_res.ni() != ni || mean_res.nj() != nj)
347 return false;
348
349 if (k_img.ni() != ni || k_img.nj() != nj || lambda_img.ni() != ni || lambda_img.nj() != nj)
350 return false;
351
352 for (unsigned j = 0; j<nj; ++j)
353 for (unsigned i = 0; i<ni; ++i)
354 {
355 double m = mean_res(i,j,1);
356 double s = std_dev_img(i,j);
357 // very weak response
358 if (m<1e-6||s<1e-7) {
359 k_img(i,j)=0.0f;
360 lambda_img(i,j)= 0.0;
361 continue;
362 }
363 // approximate k
364 double k = 1 + 1.21*(m/s-1);
365 if (k<1)
366 k=1;
367 double lambda = m/vnl_gamma((k+1)/k);
368 k_img(i,j)= static_cast<float>(k);
369 lambda_img(i,j)= static_cast<float>(lambda);
370 }
371
372 return true;
373 }
374
construct_bg_response_model_gauss(vil_image_view<float> & mean_img,vil_image_view<float> & std_dev_img,vil_image_view<float> & mu_img,vil_image_view<float> & sigma_img)375 bool brec_part_gaussian::construct_bg_response_model_gauss(vil_image_view<float>& mean_img, vil_image_view<float>& std_dev_img, vil_image_view<float> &mu_img, vil_image_view<float> &sigma_img)
376 {
377 unsigned ni = mean_img.ni();
378 unsigned nj = mean_img.nj();
379
380 // find the response img for this operator
381 vil_image_view<float> mean_res = brip_vil_float_ops::extrema(mean_img, lambda0_, lambda1_, theta_, bright_, false, true, true);
382 // find the sd dev of the operator at every pixel
383 vbl_array_2d<float> kernel; vbl_array_2d<bool> mask;
384 brip_vil_float_ops::extrema_kernel_mask(lambda0_, lambda1_, theta_, kernel, mask);
385 vil_image_view<float> std_dev_res = brip_vil_float_ops::std_dev_operator_method2(std_dev_img, kernel);
386
387 if (mean_res.ni() != ni || mean_res.nj() != nj)
388 return false;
389
390 if (mu_img.ni() != ni || mu_img.nj() != nj || sigma_img.ni() != ni || sigma_img.nj() != nj)
391 return false;
392
393 for (unsigned j = 0; j<nj; ++j)
394 for (unsigned i = 0; i<ni; ++i)
395 {
396 float m = mean_res(i,j,2);
397 if (bright_)
398 m = -m;
399 mu_img(i,j)= m;
400
401 float s = std_dev_res(i,j); // was: std_dev_img(i,j);
402 sigma_img(i,j)= s;
403 }
404
405 return true;
406 }
407
408 //: collect operator responses from the input image
409 // Use responses from class regions to estimate lambda and k for the class response model
410 // Use responses from non-class regions to estimate lambda and k for the non-class response model
411 // Class and non-class regions are specified by the class_prob_img which is a float image with values in [0,1] range
construct_class_response_models(vil_image_view<float> & img,vil_image_view<float> & class_prob_img,vil_image_view<bool> & mask_img,double & lambda,double & k,double & lambda_non_class,double & k_non_class)412 bool brec_part_gaussian::construct_class_response_models(vil_image_view<float>& img,
413 vil_image_view<float>& class_prob_img,
414 vil_image_view<bool>& mask_img,
415 double &lambda, double &k, double &lambda_non_class, double &k_non_class)
416 {
417 unsigned ni = img.ni();
418 unsigned nj = img.nj();
419
420 // find the response img for this operator
421 vil_image_view<float> res = brip_vil_float_ops::extrema(img, lambda0_, lambda1_, theta_, bright_, false, true);
422 if (res.ni() != ni || res.nj() != nj)
423 return false;
424
425 bsta_histogram<float> h(-7.0f, 1.0f, 32);
426 double x_sum = 0.0, xsq_sum = 0.0, p_sum = 0.0;
427 double x_sum_non_class = 0.0, xsq_sum_non_class = 0.0, p_sum_non_class = 0.0;
428 for (unsigned j = 0; j<nj; ++j)
429 for (unsigned i = 0; i<ni; ++i)
430 {
431 if (mask_img(i,j)) {
432 float op_res = res(i,j,0);
433 if (op_res < 1.0e-3f)
434 continue;
435
436 float prob_class = fg_prob_operator(class_prob_img, i,j);
437 float prob_non_class = 1.0f - prob_class;
438
439 x_sum += prob_class*op_res;
440 xsq_sum += prob_class*op_res*op_res;
441 p_sum += prob_class;
442
443 x_sum_non_class += prob_non_class*op_res;
444 xsq_sum_non_class += prob_non_class*op_res*op_res;
445 p_sum_non_class += prob_non_class;
446
447 if (prob_class>0.9)
448 h.upcount(std::log10(op_res), 1.0f);
449 }
450 }
451
452 // calculate class parameters
453 double mean = x_sum/p_sum; // estimate of mean
454 double total_var = xsq_sum/p_sum; // estimate of total variance
455 double var = total_var - mean*mean;
456 double std_dev = std::sqrt(var);
457 std::cout << "Class mean = " << mean << " std_dev = " << std_dev << '\n';
458 bsta_weibull_cost_function wcf(mean, std_dev);
459 bsta_fit_weibull<double> fw(&wcf);
460 k = 1.001;
461 fw.init(k);
462 fw.solve(k);
463
464 std::cout << "Class Weibull k fit with residual " << fw.residual() << '\n';
465 lambda = fw.lambda(k);
466 std::cout << "Class k = " << k << " lambda = " << lambda << '\n';
467
468 // calculate non-class parameters
469 mean = x_sum_non_class/p_sum_non_class; // estimate of mean
470 total_var = xsq_sum_non_class/p_sum_non_class; // estimate of total variance
471 var = total_var - mean*mean;
472 std_dev = std::sqrt(var);
473 std::cout << "Non-class mean = " << mean << " std_dev = " << std_dev << '\n';
474 bsta_weibull_cost_function wcfn(mean, std_dev);
475 bsta_fit_weibull<double> fwn(&wcfn);
476 k_non_class = 1.001;
477 fwn.init(k_non_class);
478 fwn.solve(k_non_class);
479
480 std::cout << "Class Weibull k fit with residual " << fwn.residual() << '\n';
481 lambda_non_class = fwn.lambda(k_non_class);
482 std::cout << "Class k = " << k_non_class << " lambda = " << lambda_non_class << '\n';
483
484 return true;
485 }
486
487 //: for gaussian operators we use weibull distribution as the parametric model
fit_distribution_to_response_hist(bsta_histogram<float> & fg_h)488 bool brec_part_gaussian::fit_distribution_to_response_hist(bsta_histogram<float>& fg_h)
489 {
490 float mean = fg_h.mean(); auto std_dev = (float)std::sqrt(fg_h.variance());
491 bsta_weibull_cost_function wcf(mean, std_dev);
492 bsta_fit_weibull<float> fw(&wcf);
493 k_ = 1.0f;
494 if (fw.init(k_)) {
495 fw.solve(k_);
496 std::cout << "Weibull k fit with residual " << fw.residual() << '\n';
497 lambda_ = fw.lambda(k_);
498 std::cout << "k = " << k_ << " lambda = " << lambda_ << '\n';
499
500 fitted_weibull_ = true;
501 }
502 else { // weibull cannot be fit!
503 fitted_weibull_ = false;
504 }
505 return fitted_weibull_;
506 }
507
508
509 //: find P(alpha in foreground): the probability that this operator alpha in foreground
510 // P(alpha in foreground) = argmax_x_kl P(x_kl in foreground) where x_kl is in mask of operator alpha
fg_prob_operator(vil_image_view<float> & fg_prob_img,unsigned i,unsigned j)511 float brec_part_gaussian::fg_prob_operator(vil_image_view<float>& fg_prob_img, unsigned i, unsigned j)
512 {
513 float max_prob = 0.0f;
514 for (int jj=-rj_; jj<=rj_; ++jj) {
515 for (int ii=-ri_; ii<=ri_; ++ii) {
516 if (!mask_[jj+rj_][ii+ri_])
517 continue;
518 else if (fg_prob_img(i+ii, j+jj)>max_prob)
519 max_prob=fg_prob_img(i+ii, j+jj);
520 }
521 }
522 return max_prob;
523 }
524
525 //: find P(alpha in background): the probability that this operator alpha is in background
526 // P(alpha in background) = 1-argmax_x_kl P(x_kl in foreground) where x_kl is in mask of operator alpha
bg_prob_operator(vil_image_view<float> & fg_prob_img,unsigned i,unsigned j)527 float brec_part_gaussian::bg_prob_operator(vil_image_view<float>& fg_prob_img, unsigned i, unsigned j)
528 {
529 float fg_prob = fg_prob_operator(fg_prob_img, i, j);
530 return 1.0f-fg_prob;
531 }
532
533
534 //: collect operator responses from the input image's foreground regions
535 // The input img and the fg_prob_img (foreground probability image) are float images with values in [0,1] range
536 // Assumes histogram is initialized
update_response_hist(vil_image_view<float> & img,vil_image_view<float> & fg_prob_img,vil_image_view<bool> & mask_img,bsta_histogram<float> & fg_h)537 bool brec_part_gaussian::update_response_hist(vil_image_view<float>& img, vil_image_view<float>& fg_prob_img, vil_image_view<bool>& mask_img,
538 bsta_histogram<float>& fg_h)
539 {
540 unsigned ni = img.ni();
541 unsigned nj = img.nj();
542
543 // find the response img for this operator
544 vil_image_view<float> res = brip_vil_float_ops::extrema(img, lambda0_, lambda1_, theta_, bright_, false, false); // was: "true"
545 if (res.ni() != ni || res.nj() != nj)
546 return false;
547
548 for (unsigned j = 0; j<nj; ++j)
549 for (unsigned i = 0; i<ni; ++i)
550 {
551 if (mask_img(i,j)) {
552 float op_res = res(i,j);
553 if (op_res<=0)
554 continue;
555 float prob_fore = fg_prob_operator(fg_prob_img, i,j);
556 if (prob_fore>0.9)
557 fg_h.upcount(op_res, 1.0f); // was: std::log10(op_res)
558 }
559 }
560
561 return true;
562 }
563
564
565 //: run the operator on the input img rotated by the given angle and save the instances in the input vector
566 // Use the response models saved in the model_dir to set the operator response strength which is equivalent to posterior probability of this pixel's being foreground given the operator response
567 // i.e. p(x in foreground | operator response) = p(operator response | x in foreground) / [p(operator response | x in foreground)* + p(operator response | x in background)]
568 // Return all the instances which have a posterior larger than zero (--> no thresholding, return "all" the responses)
569 // fg_prob_image is the probability of being foreground for each pixel
570 // pb_zero is the constant required for the background response model (probability of zero response)
extract(vil_image_view<float> & img,vil_image_view<float> & fg_prob_image,float rot_angle,const std::string & model_dir,std::vector<brec_part_instance_sptr> & instances,float prior_class)571 bool brec_part_gaussian::extract(vil_image_view<float>& img, vil_image_view<float>& fg_prob_image,
572 float rot_angle, const std::string& model_dir, std::vector<brec_part_instance_sptr>& instances, float prior_class)
573 {
574 unsigned ni = img.ni();
575 unsigned nj = img.nj();
576
577 // find the response img for this operator
578 vil_image_view<float> op_res = brip_vil_float_ops::extrema(img, lambda0_, lambda1_, theta_+rot_angle, bright_, false, false);
579 if (op_res.ni() != ni || op_res.nj() != nj)
580 return false;
581
582 std::string str_id = string_identifier();
583 std::string name;
584 #if 1
585 name = "./op_response_img" + str_id + ".tiff";
586 vil_save(op_res, name.c_str());
587 name = "./prob_image" + str_id + ".tiff";
588 vil_save(fg_prob_image, name.c_str());
589 #endif
590
591 name = model_dir+str_id+"_bg_mu_img.tiff";
592 if (!vul_file::exists(name)) {
593 std::cerr << "In brec_part_gaussian::extract() -- Problem: Cannot find model parameter file: " << name << "\nNote: train the models and save model param directory in the hierarchy\n;\n";
594 return false;
595 }
596 vil_image_view<float> mu_img = vil_load(name.c_str());
597
598 name = model_dir+str_id+"_bg_sigma_img.tiff";
599 if (!vul_file::exists(name)) {
600 std::cerr << "In brec_part_gaussian::extract() -- Problem: Cannot find model parameter file: " << name << "\nNote: train the models and save model param directory in the hierarchy\n;\n";
601 return false;
602 }
603 vil_image_view<float> sigma_img = vil_load(name.c_str());
604
605 double lambda, k, lambda_non_class, k_non_class;
606 if (fitted_weibull_) {
607 std::cout << "using fitted weibull parameters lambda: " << lambda_ << " k: " << k_ << " lambda_nc: " << lambda_non_class_ << " k_nc: " << k_non_class_ << " of the part for the foreground response model!\n";
608 lambda = lambda_; lambda_non_class = lambda_non_class_;
609 k = k_; k_non_class = k_non_class_;
610 }
611 else {
612 name = model_dir+str_id+"_fg_params.txt";
613
614 std::cout << "using weibull parameters from the file: " << name << " for the foreground response model!\n";
615 // load the model parameters, check if they exist
616 if (!vul_file::exists(name)) {
617 std::cerr << "In brec_part_gaussian::extract() -- Problem: Cannot find model parameter file: " << name << "\nNote: train the models and save model param directory in the hierarchy\n;\n";
618 return false;
619 }
620
621 std::ifstream ifs(name.c_str(), std::ios::in);
622 ifs >> k; ifs >> lambda;
623 ifs >> k_non_class; ifs >> lambda_non_class;
624 ifs.close();
625 }
626
627 bsta_weibull<float> pd_class((float)lambda, (float)k);
628 bsta_weibull<float> pd_non_class((float)lambda_non_class, (float)k_non_class);
629 float prior_non_class = 1.0f - prior_class;
630
631 // extract all the parts from the responses
632 for (unsigned j = 0; j<nj; ++j)
633 for (unsigned i = 0; i<ni; ++i)
634 {
635 float mu_bk = mu_img(i,j);
636 float sigma_bk = sigma_img(i,j);
637
638 float res = op_res(i,j);
639 // if the operator response is small we can't tell
640 if (res<1.0e-3f)
641 continue;
642
643 float pos_c_f = 0.0f;
644 float pos_nc_f = 0.0f;
645 float pos_c_b = 0.0f;
646 float pos_nc_b = 0.0f;
647
648 float pf = fg_prob_operator(fg_prob_image, i, j); // was: fg_prob_image(i, j);
649
650 float pdb = 0.0f;
651 bsta_gauss_sf1 pdbg(mu_bk, sigma_bk*sigma_bk);
652 pdb = pdbg.prob_density(res);
653
654 float pd_c = pd_class.prob_density(res); // prob density class
655 float pd_nc = pd_non_class.prob_density(res); // prob density non_class
656
657 pos_c_f = pd_c*pf*prior_class;
658 pos_nc_f = pd_nc*pf*prior_non_class;
659
660 pos_c_b = pdb*(1-pf)*prior_class;
661 pos_nc_b = pdb*(1-pf)*prior_non_class;
662
663 float den = pos_c_f + pos_nc_f + pos_c_b + pos_nc_b;
664 if (den <= 0.0f)
665 continue; // not a valid part, no detections
666
667 pos_c_f /= den; pos_nc_f /= den; pos_c_b /= den; pos_nc_b /= den;
668
669 #ifdef DEBUG
670 std::cout << "i: " << i << " j: " << j << " response: " << res
671 << " pf: " << pf << " pdf: " << pdf << " pdb: " << pdb
672 << " neu: " << neu << " den: " << den << " post: " << posterior << std::endl;
673 #endif // DEBUG
674 brec_part_gaussian_sptr dp = new brec_part_gaussian((float)i, (float)j, res, lambda0_, lambda1_, theta_+rot_angle, bright_, type_);
675 dp->rho_c_f_ = pos_c_f;
676 dp->rho_nc_f_ = pos_nc_f;
677 dp->rho_c_b_ = pos_c_b;
678 dp->rho_nc_b_ = pos_nc_b;
679 dp->cutoff_percentage_ = cutoff_percentage_;
680 instances.emplace_back(dp->cast_to_instance());
681
682 #if 0
683 if ((i == 375 && j == 204) || (i == 348 && j == 221) || (i == 374 && j == 193) ||
684 (i == 229 && j == 366) || (i == 223 && j == 358) || (i == 230 && j == 368) || (i == 227 && j == 364) ||
685 (i == 239 && j == 370) || (i == 240 && j == 373) || (i == 235 && j == 369)
686 ) {
687 std::cout << " i == " << i << " && j == " << j << " \nk_fore: " << k_fore << " lam_fore: " << lambda_fore << " mu_bk: " << mu_bk << " sigma_bk: " << sigma_bk << '\n'
688 << " res: " << res << " pb: " << pb << " pdb: " << pdb << " pdf: " << pdf << " den: " << den << " neu: " << neu << " posterior: " << posterior << std::endl;
689 }
690 #endif
691 }
692
693 return true;
694 }
695
696 //: extract and set rho to class probability density of the response
697 // Assumes weibull parameters have already been fitted (i.e. fitted_weibull_ = true)
698 // This method is to be used during training and it returns an instance if class_prob >= 0.9
extract(vil_image_view<float> & img,vil_image_view<float> & class_prob_image,float rot_angle,std::vector<brec_part_instance_sptr> & instances)699 bool brec_part_gaussian::extract(vil_image_view<float>& img, vil_image_view<float>& class_prob_image, float rot_angle, std::vector<brec_part_instance_sptr>& instances)
700 {
701 unsigned ni = img.ni();
702 unsigned nj = img.nj();
703
704 // find the response img for this operator
705 vil_image_view<float> op_res = brip_vil_float_ops::extrema(img, lambda0_, lambda1_, theta_+rot_angle, bright_, false, false);
706 if (op_res.ni() != ni || op_res.nj() != nj)
707 return false;
708
709 if (!fitted_weibull_) {
710 std::cout << "ERROR: Foreground response model's parameters have not been set! Run parameter fitting routine first!\n";
711 return false;
712 }
713
714 bsta_weibull<float> pd_class(lambda_, k_); bsta_weibull<float> pd_non_class(lambda_non_class_, k_non_class_);
715
716 // extract all the parts from the responses
717 for (unsigned j = 0; j<nj; ++j)
718 for (unsigned i = 0; i<ni; ++i)
719 {
720 float res = op_res(i,j);
721 // if the operator response is small we can't tell
722 if (res<1.0e-3f)
723 continue;
724
725 float pf = fg_prob_operator(class_prob_image, i, j);
726 if (pf < 0.9f)
727 continue;
728
729 float pd_c = pd_class.prob_density(res); // prob density class
730 float pd_nc = pd_non_class.prob_density(res); // prob density class
731
732 if (pd_c > 0.0f) {
733 brec_part_gaussian_sptr dp = new brec_part_gaussian((float)i, (float)j, res, lambda0_, lambda1_, theta_+rot_angle, bright_, type_);
734 dp->rho_c_f_ = pd_c;
735 dp->rho_nc_f_ = pd_nc;
736 dp->rho_c_b_ = 0.0f;
737 dp->rho_nc_b_ = 0.0f;
738
739 dp->cutoff_percentage_ = cutoff_percentage_;
740 instances.emplace_back(dp->cast_to_instance());
741 }
742 }
743
744 return true;
745 }
746
747
748 //: use the background mean and std_dev imgs to construct response model for background and calculate posterior ratio's expected value
749 // Assumes that k_ and lambda_ for the foreground response model has already been set
update_foreground_posterior(vil_image_view<float> & img,vil_image_view<float> &,vil_image_view<bool> &,vil_image_view<float> & mean_img,vil_image_view<float> & std_dev_img)750 bool brec_part_gaussian::update_foreground_posterior(vil_image_view<float>& img,
751 vil_image_view<float>& /*fg_prob_img*/,
752 vil_image_view<bool>& /*mask*/, // FIXME - unused
753 vil_image_view<float>& mean_img,
754 vil_image_view<float>& std_dev_img)
755 {
756 #if 0
757 std::cout << "before update, instance rho_: " << rho_ << '\n'
758 << "before update, instance cnt_: " << cnt_ << '\n';
759 #endif // 0
760
761 unsigned ni = img.ni();
762 unsigned nj = img.nj();
763
764 // find the response img for this operator
765 vil_image_view<float> op_res = brip_vil_float_ops::extrema(img, lambda0_, lambda1_, theta_, bright_, false, false);
766 if (op_res.ni() != ni || op_res.nj() != nj)
767 return false;
768
769 vil_image_view<float> mu_img(mean_img.ni(), mean_img.nj());
770 vil_image_view<float> sigma_img(mean_img.ni(), mean_img.nj());
771 if (!construct_bg_response_model_gauss(mean_img, std_dev_img, mu_img, sigma_img)) {
772 std::cout << "In brec_part_gaussian::update_foreground_posterior() - problems in construction of background response model params!\n";
773 return false;
774 }
775
776 #if 0
777 bsta_weibull<float> pdfg(lambda_, k_);
778 #endif // 0
779
780 // extract all the parts from the responses
781 for (unsigned j = 0; j<nj; ++j)
782 for (unsigned i = 0; i<ni; ++i)
783 {
784 float mu_bk = mu_img(i,j);
785 float sigma_bk = sigma_img(i,j);
786
787 float res = op_res(i,j);
788 // if the operator response is small we can't tell
789 if (res<1.0e-3f)
790 continue;
791
792 #if 0
793 float prob_fore = fg_prob_operator(fg_prob_img, i,j),
794 prob_back = 1.0f-prob_fore;
795 #endif // 0
796
797 bsta_gauss_sf1 pdbg(mu_bk, sigma_bk*sigma_bk);
798 float pdb = pdbg.prob_density(res);
799
800 float pdf = 0.0f; // was: = pdfg.prob_density(res); // prob density foreground
801 if (fitted_weibull_) {
802 bsta_weibull<float> pdfg(lambda_, k_);
803 pdf = pdfg.prob_density(res);
804 }
805 else {
806 return false;
807 }
808
809 double den = pdb; // was: = pdb*prob_back;
810 double neu = pdf; // was: = pdf*prob_fore;
811 double rho = 0.0f;
812 if (den > 0.0f)
813 rho = std::log10(neu/den); // foreground and background posterior ratio
814
815 if (rho > 0.0f) {
816 cnt_ = cnt_ + 1;
817 rho_c_f_ = ((cnt_-1)*rho_c_f_ + rho)/cnt_;
818 #ifdef DEBUG
819 std::cout << "after update, instance rho_: " << rho_ << " cnt_: " << cnt_
820 << " rho: " << rho << " neu: " << neu << " den: " << den << '\n';
821 #endif // DEBUG
822 }
823 }
824 #ifdef DEBUG
825 std::cout << "after update, instance rho_: " << rho_ << '\n'
826 << "after update, instance cnt_: " << cnt_ << '\n';
827 #endif // DEBUG
828 return true;
829 }
830
draw_gauss_to_ps(vul_psfile & ps,const brec_part_gaussian_sptr & pi,float x,float y,float cr,float cg,float cb)831 bool draw_gauss_to_ps(vul_psfile& ps, const brec_part_gaussian_sptr& pi, float x, float y, float cr, float cg, float cb)
832 {
833 ps.set_fg_color(cr,cg,cb);
834 ps.set_line_width(1.f);
835 // convert image coordinate system (origin at the top left corner) to ps coordinate system (origin at the bottom left corner)
836 ps.ellipse(x*4, -y*4, pi->lambda0_*4, pi->lambda1_*4, int(-pi->theta_));
837 return true;
838 }
839