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