2 * folded-ctf is an implementation of the folded hierarchy of
3 * classifiers for object detection, developed by Francois Fleuret
6 * Copyright (c) 2008 Idiap Research Institute, http://www.idiap.ch/
7 * Written by Francois Fleuret <francois.fleuret@idiap.ch>
9 * This file is part of folded-ctf.
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.
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.
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/>.
26 #include "pose_cell_hierarchy.h"
29 PoseCellHierarchy::PoseCellHierarchy() {
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;
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);
48 RelativeBellyPoseCell targets[nb_total_targets];
51 for(int i = 0; i < train_pool->nb_images(); i++) {
52 image = train_pool->grab_image(i);
55 add_root_cells(image, &cell_set);
57 for(int t = 0; t < image->nb_targets(); t++) {
58 Pose pose = *image->get_target_pose(t);
61 cell_set.get_containing_cell(&pose)->get_centroid(&coarse);
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);
67 pose.horizontal_flip(image->width());
69 cell_set.get_containing_cell(&pose)->get_centroid(&coarse);
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);
76 train_pool->release_image(i);
79 scalar_t fattening = 1.1;
81 Interval belly_rxc, belly_ryc;
83 belly_rxc.set(&targets[0]._belly_xc);
84 belly_ryc.set(&targets[0]._belly_yc);
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);
91 belly_rxc.min *= fattening;
92 belly_rxc.max *= fattening;
93 belly_ryc.min *= fattening;
94 belly_ryc.max *= fattening;
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));
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));
102 int used[nb_belly_rxc * nb_belly_rxc];
104 for(int k = 0; k < nb_belly_rxc * nb_belly_ryc; k++) {
108 // An ugly way to compute the convexe enveloppe
110 for(scalar_t alpha = 0; alpha < M_PI * 2; alpha += (2 * M_PI) / 100) {
111 scalar_t vx = cos(alpha), vy = sin(alpha);
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());
120 for(int j = 0; j < nb_belly_ryc; j++) {
121 for(int i = 0; i < nb_belly_rxc; i++) {
123 vx * (scalar_t(i + 0) * belly_resolution + belly_rxc_min) +
124 vy * (scalar_t(j + 0) * belly_resolution + belly_ryc_min) < rho
126 vx * (scalar_t(i + 1) * belly_resolution + belly_rxc_min) +
127 vy * (scalar_t(j + 0) * belly_resolution + belly_ryc_min) < rho
129 vx * (scalar_t(i + 0) * belly_resolution + belly_rxc_min) +
130 vy * (scalar_t(j + 1) * belly_resolution + belly_ryc_min) < rho
132 vx * (scalar_t(i + 1) * belly_resolution + belly_rxc_min) +
133 vy * (scalar_t(j + 1) * belly_resolution + belly_ryc_min) < rho
135 used[i + j * nb_belly_rxc] = 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]) {
150 _belly_cells = new RelativeBellyPoseCell[_nb_belly_cells];
153 for(int j = 0; j < nb_belly_ryc; j++) {
154 for(int i = 0; i < nb_belly_rxc; i++) {
156 if(used[i + nb_belly_rxc * j]) {
158 RelativeBellyPoseCell mother;
160 scalar_t x = scalar_t(i) * belly_resolution + belly_rxc_min;
161 scalar_t y = scalar_t(j) * belly_resolution + belly_ryc_min;
163 mother._belly_xc.set(x, x + belly_resolution);
164 mother._belly_yc.set(y, y + belly_resolution);
166 _belly_cells[k++] = mother;
172 PoseCellHierarchy::~PoseCellHierarchy() {
173 delete[] _belly_cells;
176 int PoseCellHierarchy::nb_levels() {
180 void PoseCellHierarchy::get_containing_cell(Image *image, int level,
181 Pose *pose, PoseCell *result_cell) {
182 PoseCellSet cell_set;
184 for(int l = 0; l < level + 1; l++) {
185 cell_set.erase_content();
187 add_root_cells(image, &cell_set);
189 add_subcells(l, result_cell, &cell_set);
192 *result_cell = *(cell_set.get_containing_cell(pose));
196 void PoseCellHierarchy::add_root_cells(Image *image, PoseCellSet *cell_set) {
198 const int nb_scales = int((log(_max_head_radius) - log(_min_head_radius)) / log(2) *
199 global.nb_scales_per_power_of_two);
201 scalar_t alpha = log(_min_head_radius);
202 scalar_t beta = log(2) / scalar_t(global.nb_scales_per_power_of_two);
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;
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);
226 void PoseCellHierarchy::add_subcells(int level, PoseCell *root,
227 PoseCellSet *cell_set) {
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);
250 cerr << "Inconsistent level in PoseCellHierarchy::add_subcells" << endl;
258 int PoseCellHierarchy::nb_incompatible_poses(LabelledImagePool *pool) {
259 PoseCell target_cell;
260 PoseCellSet cell_set;
261 LabelledImage *image;
265 for(int i = 0; i < pool->nb_images(); i++) {
266 image = pool->grab_image(i);
268 for(int t = 0; t < image->nb_targets(); t++) {
269 cell_set.erase_content();
271 int error_level = -1;
273 for(int l = 0; error_level < 0 && l < _nb_levels; l++) {
274 cell_set.erase_content();
277 add_root_cells(image, &cell_set);
279 add_subcells(l, &target_cell, &cell_set);
282 int nb_compliant = 0;
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));
291 if(nb_compliant != 1) {
296 if(error_level >= 0) {
301 pool->release_image(i);
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]);
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]);