automatic commit
[folded-ctf.git] / pi_referential.cc
1 /*
2  *  folded-ctf is an implementation of the folded hierarchy of
3  *  classifiers for object detection, developed by Francois Fleuret
4  *  and Donald Geman.
5  *
6  *  Copyright (c) 2008 Idiap Research Institute, http://www.idiap.ch/
7  *  Written by Francois Fleuret <francois.fleuret@idiap.ch>
8  *
9  *  This file is part of folded-ctf.
10  *
11  *  folded-ctf is free software: you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published
13  *  by the Free Software Foundation, either version 3 of the License,
14  *  or (at your option) any later version.
15  *
16  *  folded-ctf is distributed in the hope that it will be useful, but
17  *  WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *  General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with folded-ctf.  If not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25
26 #include "pi_referential.h"
27 #include "global.h"
28 #include "rich_image.h"
29
30 void PiReferential::draw_frame(RGBImage *image,
31                                int registration_mode,
32                                int x1, int y1,
33                                int x2, int y2,
34                                int x3, int y3,
35                                int x4, int y4) {
36
37   int r, g, b;
38
39   switch(registration_mode) {
40
41   case PiReferential::RM_HEAD:
42     r = 0; g = 255; b = 0;
43     break;
44
45   case PiReferential::RM_HEAD_NO_POLARITY:
46     r = 128; g = 255; b = 128;
47     break;
48
49   case PiReferential::RM_BELLY:
50     r = 64; g = 0; b = 255;
51     break;
52
53   case PiReferential::RM_BELLY_NO_POLARITY:
54     r = 192; g = 128; b = 255;
55     break;
56
57   case PiReferential::RM_HEAD_BELLY:
58   case PiReferential::RM_HEAD_BELLY_EDGES:
59     r = 255; g = 0; b = 0;
60     break;
61
62   default:
63     cerr << "INCONSISTENCY" << endl;
64     abort();
65   }
66
67   if(global.pictures_for_article) {
68     r = 255; g = 255; b = 255;
69     image->draw_line(6, r, g, b, x1, y1, x2, y2);
70     image->draw_line(6, r, g, b, x2, y2, x3, y3);
71     image->draw_line(6, r, g, b, x3, y3, x4, y4);
72     image->draw_line(6, r, g, b, x4, y4, x1, y1);
73
74     r =   0; g =   0; b =   0;
75     image->draw_line(2, r, g, b, x1, y1, x2, y2);
76     image->draw_line(2, r, g, b, x2, y2, x3, y3);
77     image->draw_line(2, r, g, b, x3, y3, x4, y4);
78     image->draw_line(2, r, g, b, x4, y4, x1, y1);
79   } else {
80     image->draw_line(2, r, g, b, x1, y1, x2, y2);
81     image->draw_line(2, r, g, b, x2, y2, x3, y3);
82     image->draw_line(2, r, g, b, x3, y3, x4, y4);
83     image->draw_line(2, r, g, b, x4, y4, x1, y1);
84   }
85 }
86
87 void PiReferential::draw_window(RGBImage *image,
88                                 int registration_mode, Rectangle *window,
89                                 int filled) {
90   int r, g, b;
91
92   switch(registration_mode) {
93
94   case PiReferential::RM_HEAD:
95     r = 0; g = 255; b = 0;
96     break;
97
98   case PiReferential::RM_HEAD_NO_POLARITY:
99     r = 128; g = 255; b = 128;
100     break;
101
102   case PiReferential::RM_BELLY:
103     r = 64; g = 0; b = 255;
104     break;
105
106   case PiReferential::RM_BELLY_NO_POLARITY:
107     r = 192; g = 128; b = 255;
108     break;
109
110   case PiReferential::RM_HEAD_BELLY:
111   case PiReferential::RM_HEAD_BELLY_EDGES:
112     r = 255; g = 0; b = 0;
113     break;
114
115   default:
116     cerr << "INCONSISTENCY" << endl;
117     abort();
118   }
119
120   int xmin = int(window->xmin);
121   int ymin = int(window->ymin);
122   int xmax = int(window->xmax);
123   int ymax = int(window->ymax);
124
125   if(global.pictures_for_article) {
126     r = 255; g = 255; b = 255;
127     image->draw_line(6, r, g, b, xmin, ymin, xmax, ymin);
128     image->draw_line(6, r, g, b, xmax, ymin, xmax, ymax);
129     image->draw_line(6, r, g, b, xmax, ymax, xmin, ymax);
130     image->draw_line(6, r, g, b, xmin, ymax, xmin, ymin);
131
132     r =   0; g =   0; b =   0;
133     image->draw_line(2, r, g, b, xmin, ymin, xmax, ymin);
134     image->draw_line(2, r, g, b, xmax, ymin, xmax, ymax);
135     image->draw_line(2, r, g, b, xmax, ymax, xmin, ymax);
136     image->draw_line(2, r, g, b, xmin, ymax, xmin, ymin);
137
138   } else {
139     image->draw_line(2, r, g, b, xmin, ymin, xmax, ymin);
140     image->draw_line(2, r, g, b, xmax, ymin, xmax, ymax);
141     image->draw_line(2, r, g, b, xmax, ymax, xmin, ymax);
142     image->draw_line(2, r, g, b, xmin, ymax, xmin, ymin);
143     if(filled) {
144       int delta = 4;
145       for(int d = ymin - ymax; d <= xmax - xmin; d += delta) {
146         int x1 = xmin + d;
147         int y1 = ymin;
148         int x2 = xmin + d + ymax - ymin;
149         int y2 = ymax;
150         if(x1 < xmin) { y1 = y1 + (xmin - x1); x1 = xmin; }
151         if(x2 > xmax) { y2 = y2 - (x2 - xmax); x2 = xmax; }
152         image->draw_line(1, r, g, b, x1, y1, x2, y2);
153       }
154     }
155   }
156
157 }
158
159 PiReferential::PiReferential(PoseCell *cell) {
160   scalar_t head_radius = sqrt(scalar_t(cell->_head_radius.min * cell->_head_radius.max));
161
162   _common_scale = global.scale_to_discrete_log_scale(head_radius / global.min_head_radius);
163
164   scalar_t discrete_scale_ratio = global.discrete_log_scale_to_scale(_common_scale);
165
166   //////////////////////////////////////////////////////////////////////
167   // Locations and scales
168
169   // Head location
170
171   _head_xc = cell->_head_xc.middle() * discrete_scale_ratio;
172   _head_yc = cell->_head_yc.middle() * discrete_scale_ratio;
173   _head_radius = cell->_head_radius.middle() * discrete_scale_ratio;
174   _head_window_scaling = _head_radius * 2.0;
175
176   // Body location
177
178   // **********************************************************************
179   // Useless code, but necessary to keep the exact same results with
180   // g++ 4.1 and -O3 options on reference experiments.
181   _body_xc = cell->_belly_xc.middle() * discrete_scale_ratio;
182   _body_yc = cell->_belly_yc.middle() * discrete_scale_ratio;
183   _body_tilt = 0;
184   if((_head_xc - _body_xc) * cos(_body_tilt) + (_head_yc - _body_yc) * sin(_body_tilt) > 0) {
185     _body_tilt += M_PI;
186   }
187   // **********************************************************************
188
189   // Belly location
190
191   const scalar_t belly_frame_factor = 2.0;
192
193   _belly_xc = cell->_belly_xc.middle() * discrete_scale_ratio;
194   _belly_yc = cell->_belly_yc.middle() * discrete_scale_ratio;
195   _belly_window_scaling = _head_window_scaling * belly_frame_factor;
196
197   // Head-belly location
198
199   _head_belly_xc = (_head_xc + _belly_xc) * 0.5;
200   _head_belly_yc = (_head_yc + _belly_yc) * 0.5;
201
202   //////////////////////////////////////////////////////////////////////
203   // Frames
204
205   if(_body_xc >= _head_xc) {
206     //   if(_belly_xc >= _head_xc) {
207     _horizontal_polarity = 1;
208   } else {
209     _horizontal_polarity = -1;
210   }
211
212   // Head frame
213
214   if(_horizontal_polarity < 0) {
215     _head_ux = _head_radius * 2.0;
216     _head_uy = 0;
217   } else {
218     _head_ux = - _head_radius * 2.0;
219     _head_uy = 0;
220   }
221
222   _head_vx = 0;
223   _head_vy = - _head_radius * 2.0;
224
225   _head_ux_nopolarity = _head_radius * 2.0;
226   _head_uy_nopolarity = 0;
227   _head_vx_nopolarity = 0;
228   _head_vy_nopolarity = - _head_radius * 2.0;
229
230   // Belly frame
231
232   _belly_ux = _head_ux * belly_frame_factor;
233   _belly_uy = _head_uy * belly_frame_factor;
234   _belly_vx = _head_vx * belly_frame_factor;
235   _belly_vy = _head_vy * belly_frame_factor;
236
237   _belly_ux_nopolarity = _head_ux_nopolarity * belly_frame_factor;
238   _belly_uy_nopolarity = _head_uy_nopolarity * belly_frame_factor;
239   _belly_vx_nopolarity = _head_vx_nopolarity * belly_frame_factor;
240   _belly_vy_nopolarity = _head_vy_nopolarity * belly_frame_factor;
241
242   // Head-belly frame
243
244   _head_belly_ux = 2 * (_head_xc - _head_belly_xc);
245   _head_belly_uy = 2 * (_head_yc - _head_belly_yc);
246
247   if(_horizontal_polarity < 0) {
248     _head_belly_vx =   _head_belly_uy;
249     _head_belly_vy = - _head_belly_ux;
250   } else {
251     _head_belly_vx = - _head_belly_uy;
252     _head_belly_vy =   _head_belly_ux;
253   }
254
255   scalar_t l = sqrt(_head_belly_vx * _head_belly_vx + _head_belly_vy * _head_belly_vy);
256
257   _head_belly_vx = (_head_belly_vx / l) * _head_radius * 2;
258   _head_belly_vy = (_head_belly_vy / l) * _head_radius * 2;
259   _head_belly_edge_shift = int(floor(- RichImage::nb_edge_tags * atan2(_head_belly_ux, _head_belly_uy) / (2 * M_PI) + 0.5));
260   _head_belly_edge_shift = (RichImage::nb_edge_tags + _head_belly_edge_shift) % RichImage::nb_edge_tags;
261 }
262
263 int PiReferential::common_scale() {
264   return _common_scale;
265 }
266
267 void PiReferential::register_rectangle(int registration_mode,
268                                        Rectangle *original,
269                                        Rectangle *result) {
270   scalar_t alpha, beta , xc, yc, w, h;
271
272   alpha = (original->xmin + original->xmax) * 0.5;
273   beta  = (original->ymin + original->ymax) * 0.5;
274
275   switch(registration_mode) {
276
277   case RM_HEAD:
278     {
279       xc = _head_xc + alpha * _head_ux + beta * _head_vx;
280       yc = _head_yc + alpha * _head_uy + beta * _head_vy;
281       w = (original->xmax - original->xmin) * _head_window_scaling;
282       h = (original->ymax - original->ymin) * _head_window_scaling;
283     }
284     break;
285
286   case RM_HEAD_NO_POLARITY:
287     {
288       xc = _head_xc + alpha * _head_ux_nopolarity + beta * _head_vx_nopolarity;
289       yc = _head_yc + alpha * _head_uy_nopolarity + beta * _head_vy_nopolarity;
290       w = (original->xmax - original->xmin) * _head_window_scaling;
291       h = (original->ymax - original->ymin) * _head_window_scaling;
292     }
293     break;
294
295   case RM_BELLY:
296     {
297       xc = _belly_xc + alpha * _belly_ux + beta * _belly_vx;
298       yc = _belly_yc + alpha * _belly_uy + beta * _belly_vy;
299       w = (original->xmax - original->xmin) * _belly_window_scaling;
300       h = (original->ymax - original->ymin) * _belly_window_scaling;
301     }
302     break;
303
304   case RM_BELLY_NO_POLARITY:
305     {
306       xc = _belly_xc + alpha * _belly_ux_nopolarity + beta * _belly_vx_nopolarity;
307       yc = _belly_yc + alpha * _belly_uy_nopolarity + beta * _belly_vy_nopolarity;
308       w = (original->xmax - original->xmin) * _belly_window_scaling;
309       h = (original->ymax - original->ymin) * _belly_window_scaling;
310     }
311     break;
312
313   case RM_HEAD_BELLY:
314   case RM_HEAD_BELLY_EDGES:
315     {
316       xc = _head_belly_xc + alpha * _head_belly_ux + beta * _head_belly_vx;
317       yc = _head_belly_yc + alpha * _head_belly_uy + beta * _head_belly_vy;
318       w = (original->xmax - original->xmin) * _head_window_scaling;
319       h = (original->ymax - original->ymin) * _head_window_scaling;
320     }
321     break;
322
323   default:
324     cerr << "Undefined registration mode." << endl;
325     abort();
326   }
327
328   result->xmin = xc - 0.5 * w;
329   result->ymin = yc - 0.5 * h;
330   result->xmax = xc + 0.5 * w;
331   result->ymax = yc + 0.5 * h;
332
333   ASSERT(result->xmin < result->xmax && result->ymin < result->ymax);
334 }
335
336 int PiReferential::register_edge(int registration_mode, int edge_type) {
337
338   if(edge_type >= RichImage::first_edge_tag &&
339      edge_type < RichImage::first_edge_tag + RichImage::nb_edge_tags) {
340
341     int e = edge_type - RichImage::first_edge_tag;
342
343     switch(registration_mode) {
344     case PiReferential::RM_HEAD_NO_POLARITY:
345     case PiReferential::RM_BELLY_NO_POLARITY:
346       break;
347
348     case PiReferential::RM_HEAD:
349     case PiReferential::RM_BELLY:
350     case PiReferential::RM_HEAD_BELLY:
351       if(_horizontal_polarity < 0) {
352         e = (RichImage::nb_edge_tags - e) % RichImage::nb_edge_tags;
353       }
354       break;
355
356     case PiReferential::RM_HEAD_BELLY_EDGES:
357       if(_horizontal_polarity < 0) {
358         e = (RichImage::nb_edge_tags - e) % RichImage::nb_edge_tags;
359       }
360       e += _head_belly_edge_shift;
361       break;
362
363     default:
364       cerr << "INCONSISTENCY" << endl;
365       abort();
366     }
367
368     e = e % RichImage::nb_edge_tags;
369
370     return RichImage::first_edge_tag + e;
371
372   }
373
374   else return edge_type;
375 }
376
377 void PiReferential::draw(RGBImage *image, int level) {
378   int x1, y1, x2, y2, x3, y3, x4, y4;
379
380   if(level >= 1) {
381
382     // Draw the RM_BELLY reference frame
383
384     x1 = int(_belly_xc + _belly_ux + _belly_vx);
385     y1 = int(_belly_yc + _belly_uy + _belly_vy);
386     x2 = int(_belly_xc - _belly_ux + _belly_vx);
387     y2 = int(_belly_yc - _belly_uy + _belly_vy);
388     x3 = int(_belly_xc - _belly_ux - _belly_vx);
389     y3 = int(_belly_yc - _belly_uy - _belly_vy);
390     x4 = int(_belly_xc + _belly_ux - _belly_vx);
391     y4 = int(_belly_yc + _belly_uy - _belly_vy);
392
393     draw_frame(image, RM_BELLY, x1, y1, x2, y2, x3, y3, x4, y4);
394
395     // Draw the RM_HEAD_BELLY reference frame
396
397     x1 = int(_head_belly_xc + _head_belly_ux + _head_belly_vx);
398     y1 = int(_head_belly_yc + _head_belly_uy + _head_belly_vy);
399     x2 = int(_head_belly_xc - _head_belly_ux + _head_belly_vx);
400     y2 = int(_head_belly_yc - _head_belly_uy + _head_belly_vy);
401     x3 = int(_head_belly_xc - _head_belly_ux - _head_belly_vx);
402     y3 = int(_head_belly_yc - _head_belly_uy - _head_belly_vy);
403     x4 = int(_head_belly_xc + _head_belly_ux - _head_belly_vx);
404     y4 = int(_head_belly_yc + _head_belly_uy - _head_belly_vy);
405
406     draw_frame(image, RM_HEAD_BELLY, x1, y1, x2, y2, x3, y3, x4, y4);
407   }
408
409   // Draw the RM_HEAD reference frame
410
411   x1 = int(_head_xc + _head_ux + _head_vx);
412   y1 = int(_head_yc + _head_uy + _head_vy);
413   x2 = int(_head_xc - _head_ux + _head_vx);
414   y2 = int(_head_yc - _head_uy + _head_vy);
415   x3 = int(_head_xc - _head_ux - _head_vx);
416   y3 = int(_head_yc - _head_uy - _head_vy);
417   x4 = int(_head_xc + _head_ux - _head_vx);
418   y4 = int(_head_yc + _head_uy - _head_vy);
419
420   draw_frame(image, RM_HEAD, x1, y1, x2, y2, x3, y3, x4, y4);
421 }
422
423 void PiReferential::print_registration_mode(ostream *out, int registration_mode) {
424   switch(registration_mode) {
425   case RM_HEAD:
426     (*out) << "RM_HEAD";
427     break;
428   case RM_HEAD_NO_POLARITY:
429     (*out) << "RM_HEAD_NO_POLARITY";
430     break;
431   case RM_BELLY:
432     (*out) << "RM_BELLY";
433     break;
434   case RM_BELLY_NO_POLARITY:
435     (*out) << "RM_BELLY_NO_POLARITY";
436     break;
437   case RM_HEAD_BELLY:
438     (*out) << "RM_HEAD_BELLY";
439     break;
440   case RM_HEAD_BELLY_EDGES:
441     (*out) << "RM_HEAD_BELLY_EDGES";
442     break;
443   default:
444     abort();
445   }
446 }