X-Boost  2.3.8
ClassifierDetectorHelper.h
1 /* XBoost: Ada-Boost and Friends on Haar/ICF/HOG Features, Library and ToolBox
2  *
3  * Copyright (c) 2008-2014 Paolo Medici <medici@ce.unipr.it>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 
21 
22 #ifndef _CLASSIFIER_DETECTOR_HELPER_H
23 #define _CLASSIFIER_DETECTOR_HELPER_H
24 
25 #include <Utility/timer.h>
26 #include <Thread/thread.h>
27 #include <Utility/ImageUtils.h>
28 /*
29 #include "IO/pnmio.h"
30 */
31 #define MAX_SCALES 24
32 
33 // TODO
34 // #define DEBUG_TIMING
35 
36 /* Motivazioni:
37  * - HAAR ammette il ricampionamento delle ottave attraverso l'istanziazione di un oggetto ottimizzato.
38  * Il processing pertanto viene svolto per ogni scala.
39  * - ICF non ammette ricampionamento. In questo caso e' vantaggioso fare il ricampionamento delle scale e poi
40  * le ottave applicare un downsample2X per ridurre al minimo l'aliasing.
41  *
42  * in entrambi i casi, per esprimere al meglio le capacita' multithread e il consumo della memoria, si possono mettere in campo diverse strategie
43  * */
44 
48 template<class _Instance, class _DataType>
49 void inner_compute_response(const _Instance * inst, const _DataType * data, double * response, int stride, rect roi)
50 {
51  for(int j=roi.y0; j<roi.y1; j++)
52  for(int i=roi.x0; i<roi.x1; i++)
53  response[i+j*stride]= (*inst)(getData1(data->first,data->second,i,j),getData2(data->first,data->second));
54 }
55 
59 template<class _Instance, class _DataType>
60 void inner_compute_masked_response(const _Instance * inst, const _DataType * data, double * response, int stride, int y0, int y1, const std::pair<int, int> *roi)
61 {
62  for(int j=y0; j<y1; j++)
63  for(int i=roi[j].first; i<roi[j].second; i++)
64  response[i+j*stride]= (*inst)(getData1(data->first,data->second,i,j),getData2(data->first,data->second));
65 }
66 
70 template<class _Instance, class _DataType>
71 void inner_compute_masked_response(const _Instance * inst, const _DataType * data, double * response, int stride, const unsigned char *mask, rect roi)
72 {
73 // std::cout << "ROI: " << roi.x0 << '\t' << roi.y0 << '\t' << roi.x1 << '\t' << roi.y1 << std::endl;
74  for(int j=roi.y0; j<roi.y1; j++)
75  for(int i=roi.x0; i<roi.x1; i++)
76  if(mask[i+j*stride])
77  response[i+j*stride]= (*inst)(getData1(data->first,data->second,i,j),getData2(data->first,data->second));
78  else
79  response[i+j*stride]= -1.0; // TODO
80 }
81 
83 template<class _Preprocessor, class _Instance>
84 class ObjectDetectorWrapperBase: public _Preprocessor
85 {
86 
87 public:
88 
89  typedef typename _Preprocessor::ReturnType data_type;
90 
92  std::vector<_Instance> m_classifs;
94  std::vector<ObjectDetectorParams > m_clsparams;
97 
98 #ifdef DEBUG_TIMING
99  Statistic timer_preprocess, timer_total, timer_response, timer_nms;
101  Statistic timer_preprocess_scale[MAX_SCALES];
102  Statistic timer_response_scale[MAX_SCALES];
103  Statistic timer_nms_scale[MAX_SCALES];
104 #endif
105 
106 public:
107 
108  ObjectDetectorWrapperBase(std::istream & in) : _Preprocessor(in) { }
109 
111  {
112 #ifdef DEBUG_TIMING
113  std::cout << "Timing Debug:\n";
114  std::cout << "Total Total: " << timer_total << std::endl;
115  if(timer_preprocess)
116  std::cout << "Total Preprocess: " << timer_preprocess << std::endl;
117  for(int i =0; i<MAX_SCALES; ++i)
118  {
119  if(timer_preprocess_scale[i])
120  std::cout << "Scale " << i << " preprocess: " << timer_preprocess_scale[i] << std::endl;
121  if(timer_response_scale[i])
122  std::cout << "Scale " << i << " response: " << timer_response_scale[i] << std::endl;
123  if(timer_nms_scale[i])
124  std::cout << "Scale " << i << " nms: " << timer_nms_scale[i] << std::endl;
125  }
126 #endif
127  }
128 
129 
130  void setParams(const ObjectDetectorGlobalParams & params) {
131  m_params = params;
132  }
133  const ObjectDetectorGlobalParams & getParams() const {
134  return m_params;
135  }
136 
138  void setClassifierParams(int index, const ObjectDetectorParams & params) {
139  size backup = m_clsparams.at(index).sz; // store sz to avoid unwanted changes
140  m_clsparams[index] = params;
141  m_clsparams[index].sz = backup;
142  }
145  return m_clsparams.at(index);
146  }
147 
150  bool require_a_mask(int n) const
151  {
152  return !m_clsparams[n].m_srcSearchRanges.empty() || ! m_clsparams[n].m_searchRoi.empty();
153  }
154 
158  void prepare_mask(int cl_index, float s, unsigned char *mask, unsigned int r_width, unsigned int r_height, int cl_width, int cl_height, rect & roi)
159  {
160  std::vector<std::pair<int,int> > searchRanges;
161  std::vector<rect> rois;
162  this->get_ranges(searchRanges, rois, cl_index, s);
163 
164  // proces only inside ROI (TODO: compute precise ROI)
165  roi.x0 = r_width;
166  roi.y0 = r_height;
167  roi.x1 = 0;
168  roi.y1 = 0;
169 
170 // std::cout << "ROIs: " << rois.size() << '\t' << searchRanges.size() << std::endl;
171 
172  if(rois.empty())
173  {
174  // only search ranges
175  int n = std::min<int>(r_height, searchRanges.size()-cl_height); // up to
176  roi.x0 = 0;
177  roi.x1 = r_width;
178  if(n>0)
179  {
180  for(int i = 0; i<n; ++i)
181  {
182 // std::cout << i << '\t' << searchRanges[i+cl_height].first << '\t' << searchRanges[i+cl_height].second << std::endl;
183  // the search range is on the object base
184  if(searchRanges[i+cl_height].first <= cl_width && searchRanges[i+cl_height].second >= cl_width)
185  {
186  if(roi.y0 > i) roi.y0 = i;
187  if(roi.y1 < i) roi.y1 = i;
188 
189  memset(mask + i * r_width, 255, r_width);
190  }
191  else
192  memset(mask + i * r_width, 0, r_width);
193  }
194  }
195  else
196  {
197  roi.y0 = roi.y1 = 0;
198  memset(mask, 0, r_width * r_height);
199  // clean UP
200 // std::cout << "ERROR:" << r_height << '\t' << rois.size() << "\t" << cl_height << std::endl;
201  }
202 
203  }
204  else if(searchRanges.empty())
205  {
206  memset(mask, 0, r_width * r_height);
207 
208  for(unsigned int i = 0; i<rois.size(); ++i)
209  {
210  rect cur = rois[i];
211 
212  // reduce the roi due to the fact that mask indicates the top-left corner of the sliding window
213  cur.x1 -= cl_width;
214  cur.y1 -= cl_height;
215 
216  // clip cur:
217  if(cur.x0 < 0) cur.x0 = 0;
218  if(cur.y0 < 0) cur.y0 = 0;
219  if(cur.x1 > (int) r_width) cur.x1 = r_width;
220  if(cur.y1 > (int) r_height) cur.y1 = r_height;
221 
222  // update roi
223  if(roi.x0 > cur.x0) roi.x0 = cur.x0;
224  if(roi.x1 < cur.x1) roi.x1 = cur.x1;
225  if(roi.y0 > cur.y0) roi.y0 = cur.y0;
226  if(roi.y1 < cur.y1) roi.y1 = cur.y1;
227 
228  if(cur.x0<cur.x1)
229  for(int j = cur.y0; j<cur.y1; ++j)
230  memset(mask + j * r_width + cur.x0, 255, cur.width() );
231  }
232 
233  }
234  else
235  {
236  int n = std::min<int>(r_height, searchRanges.size()-cl_height); // up to
237 
238  // UNION
239  memset(mask, 0, r_width * r_height);
240 
241  for( std::vector<rect>::const_iterator i = rois.begin(); i != rois.end(); ++i)
242  {
243  rect cur = *i;
244 
245  // reduce the roi due to the fact that mask indicates the top-left corner of the sliding window
246  cur.x1 -= cl_width;
247  cur.y1 -= cl_height;
248 
249  // clip cur:
250  if(cur.x0 < 0) cur.x0 = 0;
251  if(cur.y0 < 0) cur.y0 = 0;
252  if(cur.x1 > (int) r_width) cur.x1 = r_width;
253  if(cur.y1 > n) cur.y1 = n; // clip using {n}
254 
255  // update roi
256  if(roi.x0 > cur.x0) roi.x0 = cur.x0;
257  if(roi.x1 < cur.x1) roi.x1 = cur.x1;
258  if(roi.y0 > cur.y0) roi.y0 = cur.y0;
259  if(roi.y1 < cur.y1) roi.y1 = cur.y1;
260 
261  if(cur.x0<cur.x1)
262  for(int j = cur.y0; j<cur.y1; ++j)
263  {
264  if(searchRanges[j+cl_height].first < cl_width && searchRanges[j+cl_height].second > cl_width)
265  {
266  memset(mask + j * r_width + cur.x0, 255, cur.width() );
267  }
268  }
269  }
270 
271  }
272 
273  }
274 
276  void get_ranges(std::vector<std::pair<int,int> > & searchRanges, std::vector<rect> & roi, int n, float s) const
277  {
278  // #1 converts search ranges
279  {
280  const std::vector<std::pair<int,int> > & src = m_clsparams[n].m_srcSearchRanges;
281  unsigned int h = src.size()/s;
282  searchRanges.resize(h);
283 
284  // search range bilinear interpolation:
285  for(unsigned int i=0; i<h; ++i)
286  {
287  float j = i * s;
288  unsigned int ij = (unsigned int) j;
289  float dj = j - (float) ij;
290 
291  searchRanges[i].first = ((ij+1 < src.size()) ? (src[ij].first * (1.0f - dj) + src[ij+1].first * dj) : src[ij].first)/s;
292  searchRanges[i].second = ((ij+1 < src.size()) ? (src[ij].second * (1.0f - dj) + src[ij+1].second * dj) : src[ij].second)/s;
293  }
294  }
295  // #2 converts ROIs
296  {
297  const std::vector<rect> & src = m_clsparams[n].m_searchRoi;
298  roi.resize(src.size());
299 
300  for(unsigned int i=0; i<src.size(); ++i)
301  {
302  roi[i].x0 = (src[i].x0 / s)+0.5f;
303  roi[i].x1 = (src[i].x1 / s)+0.5f;
304  roi[i].y0 = (src[i].y0 / s)+0.5f;
305  roi[i].y1 = (src[i].y1 / s)+0.5f;
306  }
307 
308  }
309 
310  }
311 
324  static void compute_response(const _Instance & inst, const data_type & data, double * response, int stride, const rect & roi, int nThread)
325  {
326  if(nThread>1)
327  {
328  sprint::thread_group thread_pool_;
329 // int nTasks = roi.height();
330  for(int i =0; i<nThread; ++i)
331  {
332  rect ir;
333  ir.x0 = roi.x0;
334  ir.x1 = roi.x1;
335  ir.y0 = roi.y0 + (i * roi.height())/nThread;
336  ir.y1 = roi.y0 + ((i+1) * roi.height())/nThread;
337  thread_pool_.create_thread(sprint::thread_bind(&inner_compute_response<_Instance, data_type>, &inst, &data, response, stride, ir));
338  }
339  thread_pool_.join_all();
340  }
341  else
342  {
343  inner_compute_response(&inst, &data, response, stride, roi);
344  }
345  }
346 
347  static void compute_masked_response(const _Instance & inst, const data_type & data, double * response, int stride, const rect & roi, const unsigned char *mask, int nThread)
348  {
349  nThread = 1;
350  if(nThread>1)
351  {
352  sprint::thread_group thread_pool_;
353 // int nTasks = roi.height();
354  for(int i =0; i<nThread; ++i)
355  {
356  rect ir;
357  ir.x0 = roi.x0;
358  ir.x1 = roi.x1;
359  ir.y0 = roi.y0 + (i * roi.height())/nThread;
360  ir.y1 = roi.y0 + ((i+1) * roi.height())/nThread;
361  thread_pool_.create_thread(sprint::thread_bind(&inner_compute_masked_response<_Instance, data_type>, &inst, &data, response, stride, mask, ir));
362  }
363  thread_pool_.join_all();
364  }
365  else
366  {
367  inner_compute_masked_response(&inst, &data, response, stride, mask, roi);
368  }
369  }
370 
374 // void compute_scale(data_type * out, int width, int height)
375 // {
376 // Image scaled_img;
377 // scaled_img.alloc(width, height, 1);
378 //
379 // m_img.ExportImage(scaled_img);
380 //
381 // _Preprocessor::Process(*out, scaled_img.data, scaled_img.width, scaled_img.height, scaled_img.stride);
382 // }
383 
385 // void compute_scale(data_type * out, const ImageHandle & src, int width, int height)
386 // {
387 // if(src.width == width && src.height == height)
388 // {
389 // _Preprocessor::Process(*out, src.data, src.width, src.height, src.stride);
390 // }
391 // else
392 // {
393 // Image scaled_img;
394 // scaled_img.alloc(width, height, 1);
395 // BilinearResample(scaled_img, src, rect(0,0, width, height) );
396 // _Preprocessor::Process(*out, scaled_img.data, scaled_img.width, scaled_img.height, scaled_img.stride);
397 // }
398 // }
399 
402 // void compute_scale(data_type * out, double scale)
403 // {
404 // compute_scale(out, m_width / scale, m_height / scale);
405 // }
406 
409 // void compute_scale_octaves(data_type * out, float scale, int octaves)
410 // {
411 // Image scaled_img;
412 // for(int i =0; i<octaves; ++i)
413 // {
414 // // FIRST OCTAVE
415 // if(i == 0)
416 // {
417 // int width = m_width / scale;
418 // int height = m_height / scale;
419 //
420 // scaled_img.alloc(width, height, 1);
421 //
422 // m_img.ExportImage(scaled_img);
423 // }
424 // else
425 // {
426 // // OTHERS octaves
427 // ImageHandle downsampled = scaled_img;
428 // downsampled.width /=2;
429 // downsampled.height /=2;
430 // downsampled.stride = downsampled.width;
431 // Downsample2X(downsampled, scaled_img);
432 // scaled_img.width = downsampled.width;
433 // scaled_img.height = downsampled.height;
434 // scaled_img.stride = downsampled.stride;
435 // }
436 //
437 //
438 // // static int count = 0;
439 // // char buffer[256];
440 // // sprintf(buffer, "/tmp/R%u.pgm", count);
441 // // count++;
442 // // pnm_write(scaled_img, buffer);
443 //
444 // _Preprocessor::Process(*out, scaled_img.data, scaled_img.width, scaled_img.height, scaled_img.stride);
445 //
446 // scale *= 2.0;
447 // out++;
448 // }
449 // }
450 
451  void precompute_scales(std::vector< data_type > & preprocess_images, double scale_factor)
452  {
453 #ifdef DEBUG_TIMING
454  timer_preprocess.Start();
455 #endif
456 
457  sprint::thread_group thread_pool_;
458 
459  preprocess_images.resize(m_params.nScales);
460 
461  double s=1;
462  this->timer_parallel_ichn.Start();
463  // ogni thread gestisce una scala
464  for(int scale=0; scale<m_params.nScales; scale++)
465  {
466  thread_pool_.create_thread(sprint::thread_bind(&ObjectDetectorWrapperBase<_Preprocessor, _Instance>::compute_scale,this, &preprocess_images[scale], s));
467  if(!m_params.octave_mode)
468  s+=scale_factor;
469  else
470  s*=scale_factor;
471  }
472  thread_pool_.join_all();
473 #ifdef DEBUG_TIMING
474  timer_preprocess.Stop();
475 #endif
476  }
477 
479  void precompute_scales_octaves(std::vector< data_type > & preprocess_images, double scale_factor, int k_max)
480  {
481 #ifdef DEBUG_TIMING
482  timer_preprocess.Start();
483 #endif
484  sprint::thread_group thread_pool_;
485 
486  preprocess_images.resize(m_params.nScales * k_max);
487 
488  double s=1;
489  this->timer_parallel_ichn.Start();
490  for(int scale=0; scale<m_params.nScales; scale++)
491  {
492  thread_pool_.create_thread(sprint::thread_bind(&ObjectDetectorWrapperBase<_Preprocessor, _Instance>::compute_scale_octaves,this, &preprocess_images[scale * k_max], s, k_max));
493  if(!m_params.octave_mode)
494  s+=scale_factor;
495  else
496  s*=scale_factor;
497  }
498  thread_pool_.join_all();
499 #ifdef DEBUG_TIMING
500  timer_preprocess.Stop();
501 #endif
502  }
503 
504  template<class T>
505  static void dump_response_image(const T *src, unsigned int w, unsigned int h, const char *filename)
506  {
507  Image out(w,h,1);
508  T max = src[0];
509  T min = src[0];
510  std::cout << "Dump " << w << 'x'<< h << ':';
511  for(int i=0; i<w*h; ++i)
512  {
513  if (src[i] > max) max = src[i];
514  if (src[i] < min) min = src[i];
515  }
516  std::cout << "min: " << min << "max: " << max << std::endl;
517 
518  if(-min > max) max = -min;
519 
520  for(int i=0; i<w*h; ++i)
521  out.data[i] = 128 + ((src[i] * T(128)) / max);
522  pnm_write(out, filename);
523  }
524 };
525 
526 // a policy
527 template <class _Instance, class _Preprocessor>
529 
530 #endif
some common method that can be used to inner detector
Definition: ClassifierDetectorHelper.h:84
a structure to hold image data (memory)
Definition: Image.h:74
void prepare_mask(int cl_index, float s, unsigned char *mask, unsigned int r_width, unsigned int r_height, int cl_width, int cl_height, rect &roi)
Definition: ClassifierDetectorHelper.h:158
bool pnm_write(const ImageHandle &in, const char *file)
Over-Classifier params.
Definition: ObjectDetector.h:68
void setClassifierParams(int index, const ObjectDetectorParams &params)
set per-classifier params
Definition: ClassifierDetectorHelper.h:138
Cross Platform High Performance timer.
Definition: ClassifierDetectorHelper.h:528
method to process image
int nScales
Number of Scales per Octave.
Definition: ObjectDetector.h:72
Definition: thread_group.h:82
static void compute_response(const _Instance &inst, const data_type &data, double *response, int stride, const rect &roi, int nThread)
Definition: ClassifierDetectorHelper.h:324
image/size TODO namespace
Definition: Types.h:39
abstracting thread
void join_all()
wait all threads terminate
Definition: thread_group.h:114
const ObjectDetectorParams & getClassifierParams(int index)
get per-classifier params
Definition: ClassifierDetectorHelper.h:144
bool require_a_mask(int n) const
Definition: ClassifierDetectorHelper.h:150
std::vector< _Instance > m_classifs
a vector of classifier instance
Definition: ClassifierDetectorHelper.h:92
Compute timing statistics.
Definition: timer.h:105
bool create_thread(const sprint::thread_function &p)
create an additional thread
Definition: thread_group.h:102
std::vector< ObjectDetectorParams > m_clsparams
per classifier params
Definition: ClassifierDetectorHelper.h:94
Definition: ObjectDetector.h:42
a rectangle structure
Definition: Types.h:55
void precompute_scales_octaves(std::vector< data_type > &preprocess_images, double scale_factor, int k_max)
calcola sia le scale che le ottave
Definition: ClassifierDetectorHelper.h:479
void get_ranges(std::vector< std::pair< int, int > > &searchRanges, std::vector< rect > &roi, int n, float s) const
convert the internal ranges of the classifier of index n
Definition: ClassifierDetectorHelper.h:276
void precompute_scales(std::vector< data_type > &preprocess_images, double scale_factor)
compute scaled preprocessor, without using m_img resampler!
Definition: ClassifierDetectorHelper.h:451
int octave_mode
use octave or linear search mode (deprecated)
Definition: ObjectDetector.h:70