automatic commit
[folded-ctf.git] / pose_cell_hierarchy.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 "pose_cell_hierarchy.h"
27 #include "gaussian.h"
28
29 PoseCellHierarchy::PoseCellHierarchy() {
30   _belly_cells = 0;
31 }
32
33 PoseCellHierarchy::PoseCellHierarchy(LabelledImagePool *train_pool) {
34   _nb_levels = global.nb_levels;
35   _min_head_radius = global.min_head_radius;
36   _max_head_radius = global.max_head_radius;
37   _root_cell_nb_xy_per_radius = global.root_cell_nb_xy_per_radius;
38
39   LabelledImage *image;
40   int nb_total_targets = 0;
41   for(int i = 0; i < train_pool->nb_images(); i++) {
42     image = train_pool->grab_image(i);
43     // We are going to symmetrize
44     nb_total_targets += 2 * image->nb_targets();
45     train_pool->release_image(i);
46   }
47
48   RelativeBellyPoseCell targets[nb_total_targets];
49
50   int u = 0;
51   for(int i = 0; i < train_pool->nb_images(); i++) {
52     image = train_pool->grab_image(i);
53
54     PoseCellSet cell_set;
55     add_root_cells(image, &cell_set);
56
57     for(int t = 0; t < image->nb_targets(); t++) {
58       Pose pose = *image->get_target_pose(t);
59       Pose coarse;
60
61       cell_set.get_containing_cell(&pose)->get_centroid(&coarse);
62
63       targets[u]._belly_xc.set((pose._belly_xc - coarse._head_xc) / coarse._head_radius);
64       targets[u]._belly_yc.set((pose._belly_yc - coarse._head_yc) / coarse._head_radius);
65       u++;
66
67       pose.horizontal_flip(image->width());
68
69       cell_set.get_containing_cell(&pose)->get_centroid(&coarse);
70
71       targets[u]._belly_xc.set((pose._belly_xc - coarse._head_xc) / coarse._head_radius);
72       targets[u]._belly_yc.set((pose._belly_yc - coarse._head_yc) / coarse._head_radius);
73       u++;
74     }
75
76     train_pool->release_image(i);
77   }
78
79   scalar_t fattening = 1.1;
80
81   Interval belly_rxc, belly_ryc;
82
83   belly_rxc.set(&targets[0]._belly_xc);
84   belly_ryc.set(&targets[0]._belly_yc);
85
86   for(int t = 0; t < nb_total_targets; t++) {
87     belly_rxc.swallow(&targets[t]._belly_xc);
88     belly_ryc.swallow(&targets[t]._belly_yc);
89   }
90
91   belly_rxc.min *= fattening;
92   belly_rxc.max *= fattening;
93   belly_ryc.min *= fattening;
94   belly_ryc.max *= fattening;
95
96   scalar_t belly_rxc_min = belly_resolution * floor(belly_rxc.min / belly_resolution);
97   int nb_belly_rxc = int(ceil((belly_rxc.max - belly_rxc_min) / belly_resolution));
98
99   scalar_t belly_ryc_min = belly_resolution * floor(belly_ryc.min / belly_resolution);
100   int nb_belly_ryc = int(ceil((belly_ryc.max - belly_ryc_min) / belly_resolution));
101
102   int used[nb_belly_rxc * nb_belly_rxc];
103
104   for(int k = 0; k < nb_belly_rxc * nb_belly_ryc; k++) {
105     used[k] = 1;
106   }
107
108   // An ugly way to compute the convexe enveloppe
109
110   for(scalar_t alpha = 0; alpha < M_PI * 2; alpha += (2 * M_PI) / 100) {
111     scalar_t vx = cos(alpha), vy = sin(alpha);
112     scalar_t rho = 0;
113
114     for(int t = 0; t < nb_total_targets; t++) {
115       rho = min(rho, vx * targets[t]._belly_xc.middle() + vy * targets[t]._belly_yc.middle());
116     }
117
118     rho *= fattening;
119
120     for(int j = 0; j < nb_belly_ryc; j++) {
121       for(int i = 0; i < nb_belly_rxc; i++) {
122         if(
123            vx * (scalar_t(i + 0) * belly_resolution + belly_rxc_min) +
124            vy * (scalar_t(j + 0) * belly_resolution + belly_ryc_min) < rho
125            &&
126            vx * (scalar_t(i + 1) * belly_resolution + belly_rxc_min) +
127            vy * (scalar_t(j + 0) * belly_resolution + belly_ryc_min) < rho
128            &&
129            vx * (scalar_t(i + 0) * belly_resolution + belly_rxc_min) +
130            vy * (scalar_t(j + 1) * belly_resolution + belly_ryc_min) < rho
131            &&
132            vx * (scalar_t(i + 1) * belly_resolution + belly_rxc_min) +
133            vy * (scalar_t(j + 1) * belly_resolution + belly_ryc_min) < rho
134            ) {
135           used[i + j * nb_belly_rxc] = 0;
136         }
137       }
138     }
139   }
140
141   _nb_belly_cells = 0;
142   for(int j = 0; j < nb_belly_ryc; j++) {
143     for(int i = 0; i < nb_belly_rxc; i++) {
144       if(used[i + nb_belly_rxc * j]) {
145         _nb_belly_cells++;
146       }
147     }
148   }
149
150   _belly_cells = new RelativeBellyPoseCell[_nb_belly_cells];
151
152   int k = 0;
153   for(int j = 0; j < nb_belly_ryc; j++) {
154     for(int i = 0; i < nb_belly_rxc; i++) {
155
156       if(used[i + nb_belly_rxc * j]) {
157
158         RelativeBellyPoseCell mother;
159
160         scalar_t x = scalar_t(i) * belly_resolution + belly_rxc_min;
161         scalar_t y = scalar_t(j) * belly_resolution + belly_ryc_min;
162
163         mother._belly_xc.set(x, x + belly_resolution);
164         mother._belly_yc.set(y, y + belly_resolution);
165
166         _belly_cells[k++] = mother;
167       }
168     }
169   }
170 }
171
172 PoseCellHierarchy::~PoseCellHierarchy() {
173   delete[] _belly_cells;
174 }
175
176 int PoseCellHierarchy::nb_levels() {
177   return _nb_levels;
178 }
179
180 void PoseCellHierarchy::get_containing_cell(Image *image, int level,
181                                             Pose *pose, PoseCell *result_cell) {
182   PoseCellSet cell_set;
183
184   for(int l = 0; l < level + 1; l++) {
185     cell_set.erase_content();
186     if(l == 0) {
187       add_root_cells(image, &cell_set);
188     } else {
189       add_subcells(l, result_cell, &cell_set);
190     }
191
192     *result_cell = *(cell_set.get_containing_cell(pose));
193   }
194 }
195
196 void PoseCellHierarchy::add_root_cells(Image *image, PoseCellSet *cell_set) {
197
198   const int nb_scales = int((log(_max_head_radius) - log(_min_head_radius)) / log(2) *
199                             global.nb_scales_per_power_of_two);
200
201   scalar_t alpha = log(_min_head_radius);
202   scalar_t beta = log(2) / scalar_t(global.nb_scales_per_power_of_two);
203
204   for(int s = 0; s < nb_scales; s++) {
205     scalar_t cell_xy_size = exp(alpha + scalar_t(s) * beta) / global.root_cell_nb_xy_per_radius;
206     PoseCell cell;
207     cell._head_radius.min = exp(alpha + scalar_t(s) * beta);
208     cell._head_radius.max = exp(alpha + scalar_t(s+1) * beta);
209     cell._head_tilt.min = -M_PI;
210     cell._head_tilt.max = M_PI;
211     for(scalar_t y = 0; y < image->height(); y += cell_xy_size)
212       for(scalar_t x = 0; x < image->width(); x += cell_xy_size) {
213         cell._head_xc.min = x;
214         cell._head_xc.max = x + cell_xy_size;
215         cell._head_yc.min = y;
216         cell._head_yc.max = y + cell_xy_size;
217         cell._belly_xc.min = cell._head_xc.min - pseudo_infty;
218         cell._belly_xc.max = cell._head_xc.max + pseudo_infty;
219         cell._belly_yc.min = cell._head_yc.min - pseudo_infty;
220         cell._belly_yc.max = cell._head_yc.max + pseudo_infty;
221         cell_set->add_cell(&cell);
222       }
223   }
224 }
225
226 void PoseCellHierarchy::add_subcells(int level, PoseCell *root,
227                                      PoseCellSet *cell_set) {
228
229   switch(level) {
230
231   case 1:
232     {
233       // Here we split the belly-center coordinate cell part
234       PoseCell cell = *root;
235       scalar_t r = sqrt(cell._head_radius.min * cell._head_radius.max);
236       scalar_t x = (cell._head_xc.min + cell._head_xc.max) / 2.0;
237       scalar_t y = (cell._head_yc.min + cell._head_yc.max) / 2.0;
238       for(int k = 0; k < _nb_belly_cells; k++) {
239         cell._belly_xc.min = (_belly_cells[k]._belly_xc.min * r) + x;
240         cell._belly_xc.max = (_belly_cells[k]._belly_xc.max * r) + x;
241         cell._belly_yc.min = (_belly_cells[k]._belly_yc.min * r) + y;
242         cell._belly_yc.max = (_belly_cells[k]._belly_yc.max * r) + y;
243         cell_set->add_cell(&cell);
244       }
245     }
246     break;
247
248   default:
249     {
250       cerr << "Inconsistent level in PoseCellHierarchy::add_subcells" << endl;
251       abort();
252     }
253     break;
254   }
255 }
256
257
258 int PoseCellHierarchy::nb_incompatible_poses(LabelledImagePool *pool) {
259   PoseCell target_cell;
260   PoseCellSet cell_set;
261   LabelledImage *image;
262
263   int nb_errors = 0;
264
265   for(int i = 0; i < pool->nb_images(); i++) {
266     image = pool->grab_image(i);
267
268     for(int t = 0; t < image->nb_targets(); t++) {
269       cell_set.erase_content();
270
271       int error_level = -1;
272
273       for(int l = 0; error_level < 0 && l < _nb_levels; l++) {
274         cell_set.erase_content();
275
276         if(l == 0) {
277           add_root_cells(image, &cell_set);
278         } else {
279           add_subcells(l, &target_cell, &cell_set);
280         }
281
282         int nb_compliant = 0;
283
284         for(int c = 0; c < cell_set.nb_cells(); c++) {
285           if(cell_set.get_cell(c)->contains(image->get_target_pose(t))) {
286             target_cell = *(cell_set.get_cell(c));
287             nb_compliant++;
288           }
289         }
290
291         if(nb_compliant != 1) {
292           error_level = l;
293         }
294       }
295
296       if(error_level >= 0) {
297         nb_errors++;
298       }
299     }
300
301     pool->release_image(i);
302   }
303
304   return nb_errors;
305 }
306
307 void PoseCellHierarchy::write(ostream *os) {
308   write_var(os, &_min_head_radius);
309   write_var(os, &_max_head_radius);
310   write_var(os, &_root_cell_nb_xy_per_radius);
311   write_var(os, &_nb_belly_cells);
312   for(int k = 0; k < _nb_belly_cells; k++)
313     write_var(os, &_belly_cells[k]);
314 }
315
316 void PoseCellHierarchy::read(istream *is) {
317   delete[] _belly_cells;
318   read_var(is, &_min_head_radius);
319   read_var(is, &_max_head_radius);
320   read_var(is, &_root_cell_nb_xy_per_radius);
321   read_var(is, &_nb_belly_cells);
322   delete[] _belly_cells;
323   _belly_cells = new RelativeBellyPoseCell[_nb_belly_cells];
324   for(int k = 0; k < _nb_belly_cells; k++) {
325     read_var(is, &_belly_cells[k]);
326   }
327 }