PICCANTE  0.4
The hottest HDR imaging library!
find_checker_board.hpp
Go to the documentation of this file.
1 /*
2 
3 PICCANTE
4 The hottest HDR imaging library!
5 http://vcg.isti.cnr.it/piccante
6 
7 Copyright (C) 2014
8 Visual Computing Laboratory - ISTI CNR
9 http://vcg.isti.cnr.it
10 First author: Francesco Banterle
11 
12 This Source Code Form is subject to the terms of the Mozilla Public
13 License, v. 2.0. If a copy of the MPL was not distributed with this
14 file, You can obtain one at http://mozilla.org/MPL/2.0/.
15 
16 */
17 
18 #ifndef PIC_COMPUTER_VISION_FIND_CHECKER_BOARD_HPP
19 #define PIC_COMPUTER_VISION_FIND_CHECKER_BOARD_HPP
20 
21 #include "../filtering/filter_luminance.hpp"
22 #include "../filtering/filter_bilateral_2ds.hpp"
23 
24 #include "../computer_vision/iterative_closest_point_2D.hpp"
25 #include "../computer_vision/nelder_mead_opt_ICP_2D.hpp"
26 
27 #include "../features_matching/harris_corner_detector.hpp"
28 #include "../features_matching/orb_descriptor.hpp"
29 
30 #include "../util/rasterizer.hpp"
31 #include "../util/eigen_util.hpp"
32 
33 #ifndef PIC_DISABLE_EIGEN
34 
35 #ifndef PIC_EIGEN_NOT_BUNDLED
36  #include "../externals/Eigen/Dense"
37  #include "../externals/Eigen/SVD"
38  #include "../externals/Eigen/Geometry"
39 #else
40  #include <Eigen/Dense>
41  #include <Eigen/SVD>
42  #include <Eigen/Geometry>
43 #endif
44 
45 #endif
46 
47 namespace pic {
48 
54 #ifndef PIC_DISABLE_EIGEN
55 
56 PIC_INLINE float getMinDistance(std::vector< Eigen::Vector2f > &points)
57 {
58  float ret = FLT_MAX;
59  for(unsigned int i = 0; i < points.size(); i++) {
60 
61  auto p_i = points[i];
62 
63  for(unsigned int j = 0; j < points.size(); j++) {
64  if(j == i) {
65  continue;
66  }
67 
68  auto delta_ij = p_i - points[j];
69  float dist = delta_ij.norm();
70 
71  if(dist < ret) {
72  ret = dist;
73  }
74  }
75  }
76 
77  return ret;
78 }
79 
85 PIC_INLINE float estimateCheckerBoardSize(std::vector< Eigen::Vector2f > &points)
86 {
87  if(points.size() < 2) {
88  return -1.0f;
89  }
90 
91  float ret = 0.0f;
92 
93  int n = int(points.size());
94 
95  std::vector<float> m_d;
96  for(int i = 0; i < n; i++) {
97  auto p_i = points[i];
98 
99  float closest = FLT_MAX;
100 
101  for(int j = 0; j < n; j++) {
102  if(j == i) {
103  continue;
104  }
105 
106  auto delta_ij = p_i - points[j];
107 
108  float dist = delta_ij.norm();
109 
110  if(dist < closest) {
111  closest = dist;
112  }
113  }
114 
115  if(closest < FLT_MAX) {
116  m_d.push_back(closest);
117  }
118  }
119 
120  if(!m_d.empty()) {
121  std::sort(m_d.begin(), m_d.end());
122 
123  ret = m_d[m_d.size() / 2];
124  }
125 
126  return ret;
127 }
128 
134 PIC_INLINE float estimateCheckerBoardSizeCross(std::vector< Eigen::Vector2f > &points)
135 {
136  if(points.size() < 2) {
137  return -1.0f;
138  }
139 
140  float ret = 0.0f;
141 
142  int n = int(points.size());
143 
144  std::vector<float> m_d;
145  for(int i = 0; i < n; i++) {
146  auto p_i = points[i];
147 
148  float c[] = {FLT_MAX, FLT_MAX, FLT_MAX};
149  int ci[] = {-1, -1, -1};
150 
151  for(int j = 0; j < n; j++) {
152  if(j == i) {
153  continue;
154  }
155 
156  auto delta_ij = p_i - points[j];
157 
158  float dist = delta_ij.norm();
159 
160  if(dist < c[0]) {
161  c[0] = dist;
162  ci[0] = j;
163  } else {
164  if(dist < c[1]) {
165  c[1] = dist;
166  ci[1] = j;
167  } else {
168  if(dist < c[2]) {
169  c[2] = dist;
170  ci[2] = j;
171  }
172  }
173  }
174  }
175 
176  Eigen::Vector2f v0 = (points[ci[0]] - p_i) / c[0];
177  Eigen::Vector2f v1 = (points[ci[1]] - p_i) / c[1];
178  Eigen::Vector2f v2 = (points[ci[2]] - p_i) / c[2];
179 
180  float v01 = fabsf(v0.dot(v1));
181  float v02 = fabsf(v0.dot(v2));
182  float v12 = fabsf(v1.dot(v2));
183 
184  if((v01 < 0.1f) || (v02 < 0.1f) || (v12 < 0.1f))
185  {
186  m_d.push_back(c[0]);
187  }
188  }
189 
190  if(!m_d.empty()) {
191  std::sort(m_d.begin(), m_d.end());
192 
193  ret = m_d[m_d.size() / 2];
194  }
195 
196  return ret;
197 }
198 
207 PIC_INLINE Image *getCheckerBoardModel(int checkers_x, int checkers_y, int checkers_size, std::vector< Eigen::Vector2f > &out)
208 {
209  Image *ret = new Image(1, (checkers_x + 1) * checkers_size, (checkers_y + 1) * checkers_size, 1);
210  *ret = 1.0f;
211 
212  for(int i = 1; i <= checkers_y; i++) {
213  Eigen::Vector2f point;
214 
215  int y = i * checkers_size;
216  point[1] = float(y);
217 
218  for(int j = 1; j <= checkers_x; j++) {
219 
220  int x = j * checkers_size;
221  point[0] = float(x);
222 
223  bool bDraw = false;
224  if(j < checkers_x) {
225  if(((j % 2) == 0) && ((i % 2) == 0)) {
226  bDraw = true;
227  }
228  }
229 
230  if(i < checkers_y) {
231  if(((j % 2) == 1) && ((i % 2) == 1)) {
232  bDraw = true;
233  }
234  }
235 
236  if(bDraw) {
237  for(int yy = y; yy < (y + checkers_size); yy++) {
238  for(int xx = x; xx < (x + checkers_size); xx++) {
239  float *pixel_value = (*ret)(xx, yy);
240  pixel_value[0] = 0.0f;
241  }
242  }
243  }
244 
245  out.push_back(point);
246  }
247  }
248 
249  return ret;
250 }
251 
259 PIC_INLINE void findCheckerBoard(Image *img, std::vector< Eigen::Vector2f > &corners_model, int checkerBoardSizeX = 4, int checkerBoardSizeY = 7)
260 {
261  corners_model.clear();
262 
263  //get corners
264 #ifdef PIC_DEBUG
265  printf("Extracting corners...\n");
266 #endif
267 
268  //compute the luminance images
269  HarrisCornerDetector hcd(2.5f, 5);
270  std::vector< Eigen::Vector2f > corners_from_img;
271  hcd.execute(img, &corners_from_img);
272 
273  #ifdef PIC_DEBUG
274  //automatic white balance
275  float *col_mu = img->getMeanVal(NULL, NULL);
276  float *scaling = FilterWhiteBalance::getScalingFactors(col_mu, img->channels);
277  FilterWhiteBalance fwb(scaling, img->channels, true);
278 
279  Image *img_wb = fwb.Process(Single(img), NULL);
280 
281  float red[] = {1.0f, 0.0f, 0.0f};
282  float green[] = {0.0f, 1.0f, 0.0f};
283  float blue[] = {1.0f, 0.0f, 1.0f};
284  float yellow[] = {1.0f, 1.0f, 0.0f};
285 
286  (*img_wb) *= 0.125f;
287  #endif
288 
289  std::vector< Eigen::Vector2f > cfi_out;
290  GeneralCornerDetector::removeClosestCorners(&corners_from_img, &cfi_out, 16.0f, 64);
291 
292  //compute checkerboard size
293  float checker_size = estimateCheckerBoardSize(corners_from_img);
294 
295  #ifdef PIC_DEBUG
296  //drawPoints(img_wb, cfi_out, blue);
297  #endif
298 
299  //
300  // remove very closed points
301  //
302 
303  std::vector< Eigen::Vector2f > cfi_valid2;
304  auto n = cfi_out.size();
305  for(unsigned int i = 0; i < n; i++) {
306  auto p_i = cfi_out[i];
307 
308  bool bFlag = true;
309 
310  for(unsigned int j = 0; j < n; j++) {
311  if(j != i) {
312  auto delta_ij = p_i - cfi_out[j];
313  float dist = delta_ij.norm();
314 
315  if(dist < (checker_size)) {
316  bFlag = false;
317  break;
318  }
319  }
320  }
321 
322  if(bFlag) {
323  cfi_valid2.push_back(p_i);
324  }
325  }
326 
327  //
328  // remove very far away points
329  //
330 
331  std::vector< Eigen::Vector2f > cfi_valid;
332  n = cfi_valid2.size();
333  for(unsigned int i = 0; i < n; i++) {
334  auto p_i = cfi_valid2[i];
335 
336  float dist = 1e32f;
337  for(unsigned int j = 0; j < n; j++) {
338  if(j != i) {
339  auto delta_ij = p_i - cfi_valid2[j];
340  float t_dist = delta_ij.norm();
341 
342  if(t_dist < dist) {
343  dist = t_dist;
344  }
345  }
346  }
347 
348  if(dist < (checker_size * 3)) {
349  cfi_valid.push_back(p_i);
350  }
351  }
352 
353  #ifdef PIC_DEBUG
354  printf("Checker size: %f\n", checker_size);
355  drawPoints(img_wb, cfi_valid, green);
356  #endif
357 
358  checker_size = estimateCheckerBoardSizeCross(cfi_valid);
359 
360 #ifdef PIC_DEBUG
361  printf("Re-fit Checker size: %f\n", checker_size);
362 #endif
363  //pattern image
364 
365  int checkers_size = 32;
366  Image *img_pattern = getCheckerBoardModel(checkerBoardSizeX, checkerBoardSizeY, checkers_size, corners_model);
367 // corners_model.erase(corners_model.begin() + 3);
368 // corners_model.erase(corners_model.begin());
369 
370  ORBDescriptor b_desc(checkers_size, 256);
371 
372  std::vector< unsigned int *> descs_model, descs_cfi_valid;
373  b_desc.getAll(img_pattern, corners_model, descs_model);
374  b_desc.getAll(img, cfi_valid, descs_cfi_valid);
375 
376  //scale the model using the checker size
377  float min_dist = getMinDistance(corners_model);
378  float scaling_factor = checker_size / min_dist;
379 
380  ICP2DTransform t_init;
381  t_init.scale = scaling_factor;
382  t_init.applyC(corners_model);
383 
384  //run 2D ICP
385  iterativeClosestPoints2D(corners_model, cfi_valid, descs_model, descs_cfi_valid, b_desc.getDescriptorSize(), 3000);
386 
387 #ifdef PIC_DEBUG
388  drawPoints(img_wb, corners_model, red);
389 #endif
390 
391  //At this point, the rotation may be wrong so
392  //this brute-force trick does the job.
393  NelderMeadOptICP2D opt(corners_model, cfi_valid);
394 
395  float prev_err = FLT_MAX;
396  float *x = new float[3];
397  int nSample = 72;
398 
399  float *tmp = new float[4];
400  for(float i = 0; i < nSample; i++) {
401  float angle = float(i) * C_PI_2 / float(nSample);
402  float start[] = {0.0f, 0.0f, angle};
403  opt.run(start, 3, 1e-9f, 100, tmp);
404 
405  if(opt.output_error < prev_err) {
406  memcpy(x, tmp, sizeof(float) * 3);
407  prev_err = opt.output_error;
408  }
409  }
410 
411  #ifdef PIC_DEBUG
412  for(int i = 0; i < 4; i++) {
413  printf("%f\n", x[i]);
414  }
415  #endif
416 
417  float start[] = {x[0], x[1], x[2], 1.0f};
418  opt.run(start, 4, 1e-12f, 100, tmp);
419  ICP2DTransform t2(tmp[0], tmp[1], tmp[2], tmp[3]);
420 
421  #ifdef PIC_DEBUG
422  for(int i = 0; i < 4; i++) {
423  printf("%f\n", tmp[i]);
424  }
425  #endif
426 
427  t2.applyC(corners_model);
428 
429  #ifdef PIC_DEBUG
430  drawPoints(img_wb, corners_model, yellow);
431  img_wb->Write("../data/output/img_wb.bmp");
432 
433  if(img_wb != NULL) {
434  delete img_wb;
435  }
436  #endif
437 }
438 
446 PIC_INLINE float estimateLengthOfCheckers(std::vector< Eigen::Vector2f > &corners_model, Eigen::Vector2f &p0, Eigen::Vector2f &p1)
447 {
448  if(corners_model.size() < 8) {
449  return -1.0f;
450  }
451 
452  int selected = 5;
453  auto p_0 = corners_model[selected];
454 
455  int closest = -1;
456  float ret = FLT_MAX;
457  for(auto j = 0; j < corners_model.size(); j++) {
458  if(j != selected) {
459  auto delta_ij = p_0 - corners_model[j];
460  float dist = delta_ij.norm();
461 
462  if(dist < ret) {
463  ret = dist;
464  closest = j;
465  }
466  }
467  }
468 
469  p0 = p_0;
470  p1 = corners_model[closest];
471 
472  return ret;
473 }
474 
483 PIC_INLINE Eigen::Vector2f estimateCoordinatesWhitePointFromCheckerBoard(Image *img, std::vector< Eigen::Vector2f > &corners_model, int checkerBoardSizeX = 4, int checkerBoardSizeY = 6)
484 {
485  Eigen::Vector2f ret(-1.0f, -1.0f);
486 
487  if(img == NULL || corners_model.empty()) {
488  return ret;
489  }
490 
491  float maxVal = 0.0f;
492 
493  for(int i = 0; i < (checkerBoardSizeY -1) ; i++) {
494  for(int j = 0; j < (checkerBoardSizeX - 1); j++) {
495 
496  int ind0 = (i * checkerBoardSizeX) + j;
497  int ind1 = (i + 1) * checkerBoardSizeX + j + 1;
498 
499  auto p0 = corners_model[ind0];
500  auto p1 = corners_model[ind1];
501 
502  auto pMid = (p0 + p1) / 2.0f;
503 
504  int x = int(pMid[0]);
505  int y = int(pMid[1]);
506  float *color = (*img)(x, y);
507 
508  float meanColor = 0.0f;
509  for(auto c = 0; c < img->channels; c++) {
510  meanColor += color[c];
511  }
512 
513  if(meanColor > maxVal) {
514  maxVal = meanColor;
515  ret = pMid;
516  }
517  }
518  }
519 
520  return ret;
521 }
522 
531 PIC_INLINE float *estimateWhitePointFromCheckerBoard(Image *img, std::vector< Eigen::Vector2f > &corners_model, int checkerBoardSizeX = 4, int checkerBoardSizeY = 6)
532 {
533  Eigen::Vector2f point = estimateCoordinatesWhitePointFromCheckerBoard(img, corners_model, checkerBoardSizeX, checkerBoardSizeY);
534 
535  if(point[0] >= 0.0f && point[1] >= 0.0f) {
536 
537  float *ret = new float[img->channels];
538  float *color = (*img)(int(point[0]), int(point[1]));
539  memcpy(ret, color, img->channels * sizeof(float));
540 
541  return ret;
542  } else {
543  return NULL;
544  }
545 }
546 
547 #endif
548 
549 } // end namespace pic
550 
551 #endif // PIC_COMPUTER_VISION_FIND_CHECKER_BOARD_HPP
Scalar output_error
Definition: nelder_mead_opt_base.hpp:296
int channels
Definition: image.hpp:80
static void removeClosestCorners(std::vector< Eigen::Vector2f > *corners, std::vector< Eigen::Vector2f > *out, float threshold, int max_limit)
removeClosestCorners
Definition: general_corner_detector.hpp:195
float * getMeanVal(BBox *box, float *ret)
getMeanVal computes the mean for the current Image.
PIC_INLINE float estimateCheckerBoardSizeCross(std::vector< Eigen::Vector2f > &points)
estimateCheckerBoardSizeCross
Definition: find_checker_board.hpp:134
void execute(Image *img, std::vector< Eigen::Vector2f > *corners)
execute
Definition: harris_corner_detector.hpp:137
PIC_INLINE float estimateCheckerBoardSize(std::vector< Eigen::Vector2f > &points)
estimateCheckerBoardSize
Definition: find_checker_board.hpp:85
virtual Image * Process(ImageVec imgIn, Image *imgOut)
Process.
Definition: filter.hpp:390
float scale
Definition: iterative_closest_point_2D.hpp:102
const float C_PI_2
Definition: math.hpp:52
PIC_INLINE float * estimateWhitePointFromCheckerBoard(Image *img, std::vector< Eigen::Vector2f > &corners_model, int checkerBoardSizeX=4, int checkerBoardSizeY=6)
estimateWhitePointFromCheckerBoard
Definition: find_checker_board.hpp:531
PIC_INLINE Eigen::Vector2f estimateCoordinatesWhitePointFromCheckerBoard(Image *img, std::vector< Eigen::Vector2f > &corners_model, int checkerBoardSizeX=4, int checkerBoardSizeY=6)
estimateCoordinatesWhitePointFromCheckerBoard
Definition: find_checker_board.hpp:483
PIC_INLINE float getMinDistance(std::vector< Eigen::Vector2f > &points)
getMinDistance
Definition: find_checker_board.hpp:56
static float * getScalingFactors(float *white, int nWhite)
getScalingFactors
Definition: filter_white_balance.hpp:108
The HarrisCornerDetector class.
Definition: harris_corner_detector.hpp:54
PIC_INLINE void drawPoints(Image *img, std::vector< Eigen::Vector2f > &points, float *color)
drawPoints
Definition: rasterizer.hpp:162
void applyC(std::vector< Eigen::Vector2f > &points)
Definition: iterative_closest_point_2D.hpp:158
The FilterWhiteBalance class.
Definition: filter_white_balance.hpp:31
Definition: nelder_mead_opt_ICP_2D.hpp:40
virtual Scalar * run(Scalar *x_start, unsigned int n, Scalar epsilon=1e-4f, int max_iterations=1000, Scalar *x=NULL)
Definition: nelder_mead_opt_base.hpp:333
#define PIC_INLINE
Definition: base.hpp:33
PIC_INLINE Image * getCheckerBoardModel(int checkers_x, int checkers_y, int checkers_size, std::vector< Eigen::Vector2f > &out)
getCheckerBoardModel
Definition: find_checker_board.hpp:207
The Image class stores an image as buffer of float.
Definition: image.hpp:60
void getAll(Image *img, std::vector< Eigen::Vector2f > &corners, std::vector< uint * > &descs)
getAll
Definition: brief_descriptor.hpp:226
The ORBDescriptor class.
Definition: orb_descriptor.hpp:34
PIC_INLINE ImageVec Single(Image *img)
Single creates an std::vector which contains img; this is for filters input.
Definition: image_vec.hpp:36
Definition: bilateral_separation.hpp:25
int getDescriptorSize()
getDescriptorSize returns the descriptor size.
Definition: brief_descriptor.hpp:244
PIC_INLINE float estimateLengthOfCheckers(std::vector< Eigen::Vector2f > &corners_model, Eigen::Vector2f &p0, Eigen::Vector2f &p1)
estimateLengthInPixelOfCheckers
Definition: find_checker_board.hpp:446
PIC_INLINE void iterativeClosestPoints2D(std::vector< Eigen::Vector2f > &points_pattern, std::vector< Eigen::Vector2f > &points, std::vector< unsigned int *> &points_pattern_descs, std::vector< unsigned int *> &points_descs, int size_descs, int maxIterations=1000)
iterativeClosestPoints2D
Definition: iterative_closest_point_2D.hpp:339
bool Write(std::string nameFile, LDR_type typeWrite, int writerCounter)
Write saves an Image into a file on the disk.
Definition: iterative_closest_point_2D.hpp:97
PIC_INLINE void findCheckerBoard(Image *img, std::vector< Eigen::Vector2f > &corners_model, int checkerBoardSizeX=4, int checkerBoardSizeY=7)
findCheckerBoard
Definition: find_checker_board.hpp:259