Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/calib3d/src/chessboard.cpp
16354 views
1
// This file is part of OpenCV project.
2
// It is subject to the license terms in the LICENSE file found in the top-level directory
3
// of this distribution and at http://opencv.org/license.html.
4
5
#include "precomp.hpp"
6
#include "opencv2/flann.hpp"
7
#include "chessboard.hpp"
8
#include "math.h"
9
10
//#define CV_DETECTORS_CHESSBOARD_DEBUG
11
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
12
#include <opencv2/highgui.hpp>
13
static cv::Mat debug_image;
14
#endif
15
16
using namespace std;
17
namespace cv {
18
namespace details {
19
20
/////////////////////////////////////////////////////////////////////////////
21
/////////////////////////////////////////////////////////////////////////////
22
// magic numbers used for chessboard corner detection
23
/////////////////////////////////////////////////////////////////////////////
24
static const float CORNERS_SEARCH = 0.5F; // percentage of the edge length to the next corner used to find new corners
25
static const float MAX_ANGLE = float(48.0/180.0*CV_PI); // max angle between line segments supposed to be straight
26
static const float MIN_COS_ANGLE = float(cos(35.0/180*CV_PI)); // min cos angle between board edges
27
static const float MIN_RESPONSE_RATIO = 0.1F;
28
static const float ELLIPSE_WIDTH = 0.35F; // width of the search ellipse in percentage of its length
29
static const float RAD2DEG = float(180.0/CV_PI);
30
static const int MAX_SYMMETRY_ERRORS = 5; // maximal number of failures during point symmetry test (filtering out lines)
31
/////////////////////////////////////////////////////////////////////////////
32
/////////////////////////////////////////////////////////////////////////////
33
34
// some helper methods
35
static bool isPointOnLine(cv::Point2f l1,cv::Point2f l2,cv::Point2f pt,float min_angle);
36
static int testPointSymmetry(const cv::Mat& mat,cv::Point2f pt,float dist,float max_error);
37
static float calcSubpixel(const float &x_l,const float &x,const float &x_r);
38
static float calcSubPos(const float &x_l,const float &x,const float &x_r);
39
static void polyfit(const Mat& src_x, const Mat& src_y, Mat& dst, int order);
40
static float calcSignedDistance(const cv::Vec2f &n,const cv::Point2f &a,const cv::Point2f &pt);
41
static void normalizePoints1D(cv::InputArray _points,cv::OutputArray _T,cv::OutputArray _new_points);
42
static cv::Mat findHomography1D(cv::InputArray _src,cv::InputArray _dst);
43
44
void normalizePoints1D(cv::InputArray _points,cv::OutputArray _T,cv::OutputArray _new_points)
45
{
46
cv::Mat points = _points.getMat();
47
if(points.cols > 1 && points.rows == 1)
48
points = points.reshape(1,points.cols);
49
CV_CheckChannelsEQ(points.channels(), 1, "points must have only one channel");
50
51
// calc centroid
52
double centroid= cv::mean(points)[0];
53
54
// shift origin to centroid
55
cv::Mat new_points = points-centroid;
56
57
// calc mean distance
58
double mean_dist = cv::mean(cv::abs(new_points))[0];
59
if(mean_dist<= DBL_EPSILON)
60
CV_Error(Error::StsBadArg, "all given points are identical");
61
double scale = 1.0/mean_dist;
62
63
64
// generate transformation
65
cv::Matx22d Tx(
66
scale, -scale*centroid,
67
0, 1
68
);
69
Mat(Tx, false).copyTo(_T);
70
71
// calc normalized points;
72
_new_points.create(points.rows,1,points.type());
73
new_points = _new_points.getMat();
74
switch(points.type())
75
{
76
case CV_32FC1:
77
for(int i=0;i < points.rows;++i)
78
{
79
cv::Vec2d p(points.at<float>(i), 1.0);
80
p = Tx*p;
81
new_points.at<float>(i) = float(p(0)/p(1));
82
}
83
break;
84
case CV_64FC1:
85
for(int i=0;i < points.rows;++i)
86
{
87
cv::Vec2d p(points.at<double>(i), 1.0);
88
p = Tx*p;
89
new_points.at<double>(i) = p(0)/p(1);
90
}
91
break;
92
default:
93
CV_Error(Error::StsUnsupportedFormat, "unsupported point type");
94
}
95
}
96
97
cv::Mat findHomography1D(cv::InputArray _src,cv::InputArray _dst)
98
{
99
// check inputs
100
cv::Mat src = _src.getMat();
101
cv::Mat dst = _dst.getMat();
102
if(src.cols > 1 && src.rows == 1)
103
src = src.reshape(1,src.cols);
104
if(dst.cols > 1 && dst.rows == 1)
105
dst = dst.reshape(1,dst.cols);
106
CV_CheckEQ(src.rows, dst.rows, "size mismatch");
107
CV_CheckChannelsEQ(src.channels(), 1, "data with only one channel are supported");
108
CV_CheckChannelsEQ(dst.channels(), 1, "data with only one channel are supported");
109
CV_CheckTypeEQ(src.type(), dst.type(), "src and dst must have the same type");
110
CV_Check(src.rows, src.rows >= 3,"at least three point pairs are needed");
111
112
// normalize points
113
cv::Mat src_T,dst_T, src_n,dst_n;
114
normalizePoints1D(src,src_T,src_n);
115
normalizePoints1D(dst,dst_T,dst_n);
116
117
int count = src_n.rows;
118
cv::Mat A = cv::Mat::zeros(count,3,CV_64FC1);
119
cv::Mat b = cv::Mat::zeros(count,1,CV_64FC1);
120
121
// fill A;b and perform singular value decomposition
122
// it is assumed that w is one for both cooridnates
123
// h22 is kept to 1
124
switch(src_n.type())
125
{
126
case CV_32FC1:
127
for(int i=0;i<count;++i)
128
{
129
double s = src_n.at<float>(i);
130
double d = dst_n.at<float>(i);
131
A.at<double>(i,0) = s;
132
A.at<double>(i,1) = 1.0;
133
A.at<double>(i,2) = -s*d;
134
b.at<double>(i) = d;
135
}
136
break;
137
case CV_64FC1:
138
for(int i=0;i<count;++i)
139
{
140
double s = src_n.at<double>(i);
141
double d = dst_n.at<double>(i);
142
A.at<double>(i,0) = s;
143
A.at<double>(i,1) = 1.0;
144
A.at<double>(i,2) = -s*d;
145
b.at<double>(i) = d;
146
}
147
break;
148
default:
149
CV_Error(Error::StsUnsupportedFormat,"unsupported type");
150
}
151
152
cv::Mat u,d,vt;
153
cv::SVD::compute(A,d,u,vt);
154
cv::Mat b_ = u.t()*b;
155
156
cv::Mat y(b_.rows,1,CV_64FC1);
157
for(int i=0;i<b_.rows;++i)
158
y.at<double>(i) = b_.at<double>(i)/d.at<double>(i);
159
160
cv::Mat x = vt.t()*y;
161
cv::Matx22d H_(x.at<double>(0), x.at<double>(1), x.at<double>(2), 1.0);
162
163
// denormalize
164
Mat H = dst_T.inv()*Mat(H_, false)*src_T;
165
166
// enforce frobeniusnorm of one
167
double scale = cv::norm(H);
168
CV_Assert(fabs(scale) > DBL_EPSILON);
169
scale = 1.0 / scale;
170
return H*scale;
171
}
172
void polyfit(const Mat& src_x, const Mat& src_y, Mat& dst, int order)
173
{
174
int npoints = src_x.checkVector(1);
175
int nypoints = src_y.checkVector(1);
176
CV_Assert(npoints == nypoints && npoints >= order+1);
177
Mat_<double> srcX(src_x), srcY(src_y);
178
Mat_<double> A = Mat_<double>::ones(npoints,order + 1);
179
// build A matrix
180
for (int y = 0; y < npoints; ++y)
181
{
182
for (int x = 1; x < A.cols; ++x)
183
A.at<double>(y,x) = srcX.at<double>(y)*A.at<double>(y,x-1);
184
}
185
cv::Mat w;
186
solve(A,srcY,w,DECOMP_SVD);
187
w.convertTo(dst, ((src_x.depth() == CV_64F || src_y.depth() == CV_64F) ? CV_64F : CV_32F));
188
}
189
190
float calcSignedDistance(const cv::Vec2f &n,const cv::Point2f &a,const cv::Point2f &pt)
191
{
192
cv::Vec3f v1(n[0],n[1],0);
193
cv::Vec3f v2(pt.x-a.x,pt.y-a.y,0);
194
return v1.cross(v2)[2];
195
}
196
197
bool isPointOnLine(cv::Point2f l1,cv::Point2f l2,cv::Point2f pt,float min_angle)
198
{
199
cv::Vec2f vec1(l1-pt);
200
cv::Vec2f vec2(pt-l2);
201
if(vec1.dot(vec2) < min_angle*cv::norm(vec1)*cv::norm(vec2))
202
return false;
203
return true;
204
}
205
206
// returns how many tests fails out of 10
207
int testPointSymmetry(const cv::Mat& mat,cv::Point2f pt,float dist,float max_error)
208
{
209
cv::Rect image_rect(int(0.5*dist),int(0.5*dist),int(mat.cols-0.5*dist),int(mat.rows-0.5*dist));
210
cv::Size size(int(0.5*dist),int(0.5*dist));
211
int count = 0;
212
cv::Mat patch1,patch2;
213
cv::Point2f center1,center2;
214
for (int angle_i = 0; angle_i < 10; angle_i++)
215
{
216
double angle = angle_i * (CV_PI * 0.1);
217
cv::Point2f n(float(cos(angle)),float(-sin(angle)));
218
center1 = pt+dist*n;
219
if(!image_rect.contains(center1))
220
return false;
221
center2 = pt-dist*n;
222
if(!image_rect.contains(center2))
223
return false;
224
cv::getRectSubPix(mat,size,center1,patch1);
225
cv::getRectSubPix(mat,size,center2,patch2);
226
if(fabs(cv::mean(patch1)[0]-cv::mean(patch2)[0]) > max_error)
227
++count;
228
}
229
return count;
230
}
231
232
float calcSubpixel(const float &x_l,const float &x,const float &x_r)
233
{
234
// prevent zero values
235
if(x_l <= 0)
236
return 0;
237
if(x <= 0)
238
return 0;
239
if(x_r <= 0)
240
return 0;
241
const float l0 = float(std::log(x_l+1e-6));
242
const float l1 = float(std::log(x+1e-6));
243
const float l2 = float(std::log(x_r+1e-6));
244
float delta = l2-l1-l1+l0;
245
if(!delta) // this happens if all values are identical
246
return 0;
247
delta = (l0-l2)/(delta+delta);
248
return delta;
249
}
250
251
float calcSubPos(const float &x_l,const float &x,const float &x_r)
252
{
253
float val = 2.0F *(x_l-2.0F*x+x_r);
254
if(val == 0.0F)
255
return 0.0F;
256
val = (x_l-x_r)/val;
257
if(val > 1.0F)
258
return 1.0F;
259
if(val < -1.0F)
260
return -1.0F;
261
return val;
262
}
263
264
FastX::FastX(const Parameters &para)
265
{
266
reconfigure(para);
267
}
268
269
void FastX::reconfigure(const Parameters &para)
270
{
271
CV_Check(para.min_scale, para.min_scale >= 0 && para.min_scale <= para.max_scale, "invalid scale");
272
parameters = para;
273
}
274
275
// rotates the image around its center
276
void FastX::rotate(float angle,const cv::Mat &img,cv::Size size,cv::Mat &out)const
277
{
278
if(angle == 0)
279
{
280
out = img;
281
return;
282
}
283
else
284
{
285
cv::Mat_<double> m = cv::getRotationMatrix2D(cv::Point2f(float(img.cols*0.5),float(img.rows*0.5)),float(angle/CV_PI*180),1);
286
m.at<double>(0,2) += 0.5*(size.width-img.cols);
287
m.at<double>(1,2) += 0.5*(size.height-img.rows);
288
cv::warpAffine(img,out,m,size);
289
}
290
}
291
292
void FastX::calcFeatureMap(const Mat &images,Mat& out)const
293
{
294
if(images.empty())
295
CV_Error(Error::StsBadArg,"no rotation images");
296
int type = images.type(), depth = CV_MAT_DEPTH(type);
297
CV_CheckType(type,depth == CV_8U,
298
"Only 8-bit grayscale or color images are supported");
299
if(!images.isContinuous())
300
CV_Error(Error::StsBadArg,"image must be continuous");
301
302
float signal,noise,rating;
303
int count1;
304
unsigned char val1,val2,val3;
305
const unsigned char* wrap_around;
306
const unsigned char* pend;
307
const unsigned char* pimages = images.data;
308
const int channels = images.channels();
309
if(channels < 4)
310
CV_Error(Error::StsBadArg,"images must have at least four channels");
311
312
// for each pixel
313
out = cv::Mat::zeros(images.rows,images.cols,CV_32FC1);
314
const float *pout_end = reinterpret_cast<const float*>(out.dataend);
315
for(float *pout=out.ptr<float>(0,0);pout != pout_end;++pout)
316
{
317
//reset values
318
rating = 0.0; count1 = 0;
319
noise = 255; signal = 0;
320
321
//calc rating
322
pend = pimages+channels;
323
val1 = *(pend-1); // wrap around (last value)
324
wrap_around = pimages++; // store for wrap around (first value)
325
val2 = *wrap_around; // first value
326
for(;pimages != pend;++pimages)
327
{
328
val3 = *pimages;
329
if(val1 <= val2)
330
{
331
if(val3 < val2) // maxima
332
{
333
if(signal < val2)
334
signal = val2;
335
++count1;
336
}
337
}
338
else if(val1 > val2 && val3 >= val2) // minima
339
{
340
if(noise > val2)
341
noise = val2;
342
++count1;
343
}
344
val1 = val2;
345
val2 = val3;
346
}
347
// wrap around
348
if(val1 <= val2) // maxima
349
{
350
if(*wrap_around < val2)
351
{
352
if(signal < val2)
353
signal = val2;
354
++count1;
355
}
356
}
357
else if(val1 > val2 && *wrap_around >= val2) // minima
358
{
359
if(noise > val2)
360
noise = val2;
361
++count1;
362
}
363
364
// store rating
365
if(count1 == parameters.branches)
366
{
367
rating = signal-noise;
368
*pout = rating*rating; //store rating in the feature map
369
}
370
}
371
}
372
373
std::vector<std::vector<float> > FastX::calcAngles(const std::vector<cv::Mat> &rotated_images,std::vector<cv::KeyPoint> &keypoints)const
374
{
375
// validate rotated_images
376
if(rotated_images.empty())
377
CV_Error(Error::StsBadArg,"no rotated images");
378
std::vector<cv::Mat>::const_iterator iter = rotated_images.begin();
379
for(;iter != rotated_images.end();++iter)
380
{
381
if(iter->empty())
382
CV_Error(Error::StsBadArg,"empty rotated images");
383
if(iter->channels() < 4)
384
CV_Error(Error::StsBadArg,"rotated images must have at least four channels");
385
}
386
387
// assuming all elements of the same channel
388
const int channels = rotated_images.front().channels();
389
int channels_1 = channels-1;
390
float resolution = float(CV_PI/channels);
391
392
float angle;
393
float val1,val2,val3,wrap_around;
394
const unsigned char *pimages1,*pimages2,*pimages3,*pimages4;
395
std::vector<std::vector<float> > angles;
396
angles.resize(keypoints.size());
397
float scale = float(parameters.super_resolution)+1.0F;
398
399
// for each keypoint
400
std::vector<cv::KeyPoint>::iterator pt_iter = keypoints.begin();
401
for(int id=0;pt_iter != keypoints.end();++pt_iter,++id)
402
{
403
int scale_id = pt_iter->octave - parameters.min_scale;
404
if(scale_id>= int(rotated_images.size()) ||scale_id < 0)
405
CV_Error(Error::StsBadArg,"no rotated image for requested keypoint octave");
406
const cv::Mat &s_rotated_images = rotated_images[scale_id];
407
408
float x2 = pt_iter->pt.x*scale;
409
float y2 = pt_iter->pt.y*scale;
410
int row = int(y2);
411
int col = int(x2);
412
x2 -= col;
413
y2 -= row;
414
float x1 = 1.0F-x2; float y1 = 1.0F-y2;
415
float a = x1*y1; float b = x2*y1; float c = x1*y2; float d = x2*y2;
416
pimages1 = s_rotated_images.ptr<unsigned char>(row,col);
417
pimages2 = s_rotated_images.ptr<unsigned char>(row,col+1);
418
pimages3 = s_rotated_images.ptr<unsigned char>(row+1,col);
419
pimages4 = s_rotated_images.ptr<unsigned char>(row+1,col+1);
420
std::vector<float> &angles_i = angles[id];
421
422
//calc rating
423
val1 = a**(pimages1+channels_1)+b**(pimages2+channels_1)+
424
c**(pimages3+channels_1)+d**(pimages4+channels_1); // wrap around (last value)
425
wrap_around = a**(pimages1++)+b**(pimages2++)+c**(pimages3++)+d**(pimages4++); // first value
426
val2 = wrap_around; // first value
427
for(int i=0;i<channels-1;++pimages1,++pimages2,++pimages3,++pimages4,++i)
428
{
429
val3 = a**(pimages1)+b**(pimages2)+c**(pimages3)+d**(pimages4);
430
if(val1 <= val2)
431
{
432
if(val3 < val2)
433
{
434
angle = float((calcSubPos(val1,val2,val3)+i)*resolution);
435
if(angle < 0)
436
angle += float(CV_PI);
437
else if(angle > CV_PI)
438
angle -= float(CV_PI);
439
angles_i.push_back(angle);
440
pt_iter->angle = 360.0F-angle*RAD2DEG;
441
}
442
}
443
else if(val1 > val2 && val3 >= val2)
444
{
445
angle = float((calcSubPos(val1,val2,val3)+i)*resolution);
446
if(angle < 0)
447
angle += float(CV_PI);
448
else if(angle > CV_PI)
449
angle -= float(CV_PI);
450
angles_i.push_back(-angle);
451
pt_iter->angle = 360.0F-angle*RAD2DEG;
452
}
453
val1 = val2;
454
val2 = val3;
455
}
456
// wrap around
457
if(val1 <= val2)
458
{
459
if(wrap_around< val2)
460
{
461
angle = float((calcSubPos(val1,val2,wrap_around)+channels-1)*resolution);
462
if(angle < 0)
463
angle += float(CV_PI);
464
else if(angle > CV_PI)
465
angle -= float(CV_PI);
466
angles_i.push_back(angle);
467
pt_iter->angle = 360.0F-angle*RAD2DEG;
468
}
469
}
470
else if(val1 > val2 && wrap_around >= val2)
471
{
472
angle = float((calcSubPos(val1,val2,wrap_around)+channels-1)*resolution);
473
if(angle < 0)
474
angle += float(CV_PI);
475
else if(angle > CV_PI)
476
angle -= float(CV_PI);
477
angles_i.push_back(-angle);
478
pt_iter->angle = 360.0F-angle*RAD2DEG;
479
}
480
}
481
return angles;
482
}
483
484
void FastX::findKeyPoints(const std::vector<cv::Mat> &feature_maps, std::vector<KeyPoint>& keypoints,const Mat& _mask) const
485
{
486
//TODO check that all feature_maps have the same size
487
int num_scales = parameters.max_scale-parameters.min_scale;
488
CV_CheckGE(int(feature_maps.size()), num_scales, "missing feature maps");
489
if (!_mask.empty())
490
{
491
CV_CheckTypeEQ(_mask.type(), CV_8UC1, "wrong mask type");
492
CV_CheckEQ(_mask.size(), feature_maps.front().size(),"wrong mask type or size");
493
}
494
keypoints.clear();
495
496
cv::Mat mask;
497
if(!_mask.empty())
498
mask = _mask;
499
else
500
mask = cv::Mat::ones(feature_maps.front().size(),CV_8UC1);
501
502
int super_res = int(parameters.super_resolution);
503
int super_scale = super_res+1;
504
float super_comp = 0.25F*super_res;
505
506
// for each scale
507
float strength = parameters.strength;
508
std::vector<int> windows;
509
cv::Point pt,pt2;
510
double min,max;
511
cv::Mat src;
512
for(int scale=parameters.max_scale;scale>=parameters.min_scale;--scale)
513
{
514
int window_size = (1 << (scale + super_res)) + 1;
515
float window_size2 = 0.5F*window_size;
516
float window_size4 = 0.25F*window_size;
517
int window_size2i = cvRound(window_size2);
518
519
const cv::Mat &feature_map = feature_maps[scale-parameters.min_scale];
520
int y = ((feature_map.rows)/window_size)-6;
521
int x = ((feature_map.cols)/window_size)-6;
522
for(int row=5;row<y;++row)
523
{
524
for(int col=5;col<x;++col)
525
{
526
Rect rect(col*window_size,row*window_size,window_size,window_size);
527
src = feature_map(rect);
528
cv::minMaxLoc(src,&min,&max,NULL,&pt);
529
if(min == max || max < strength)
530
continue;
531
532
cv::Point pos(pt.x+rect.x,pt.y+rect.y);
533
if(mask.at<unsigned char>(pos.y,pos.x) == 0)
534
continue;
535
536
Rect rect2(int(pos.x-window_size2),int(pos.y-window_size2),window_size,window_size);
537
src = feature_map(rect2);
538
cv::minMaxLoc(src,NULL,NULL,NULL,&pt2);
539
if(pos.x == pt2.x+rect2.x && pos.y == pt2.y+rect2.y)
540
{
541
// the point is the best one on the current scale
542
// check all larger scales if there is a stronger one
543
double max2;
544
int scale2= scale-1;
545
//parameters.min_scale;
546
for(;scale2>=parameters.min_scale;--scale2)
547
{
548
cv::minMaxLoc(feature_maps[scale2-parameters.min_scale](rect),NULL,&max2,NULL,NULL);
549
if(max2 > max)
550
break;
551
}
552
if(scale2<parameters.min_scale && pos.x+1 < feature_map.cols && pos.y+1 < feature_map.rows)
553
{
554
float sub_x = float(calcSubpixel(feature_map.at<float>(pos.y,pos.x-1),
555
feature_map.at<float>(pos.y,pos.x),
556
feature_map.at<float>(pos.y,pos.x+1)));
557
float sub_y = float(calcSubpixel(feature_map.at<float>(pos.y-1,pos.x),
558
feature_map.at<float>(pos.y,pos.x),
559
feature_map.at<float>(pos.y+1,pos.x)));
560
cv::KeyPoint kpt(sub_x+pos.x,sub_y+pos.y,float(window_size),0.F,float(max),scale);
561
int x2 = std::max(0,int(kpt.pt.x-window_size4));
562
int y2 = std::max(0,int(kpt.pt.y-window_size4));
563
int w = std::min(int(mask.cols-x2),window_size2i);
564
int h = std::min(int(mask.rows-y2),window_size2i);
565
mask(cv::Rect(x2,y2,w,h)) = 0.0;
566
if(super_scale != 1)
567
{
568
kpt.pt.x /= super_scale;
569
kpt.pt.y /= super_scale;
570
kpt.pt.x -= super_comp;
571
kpt.pt.y -= super_comp;
572
kpt.size /= super_scale;
573
}
574
keypoints.push_back(kpt);
575
}
576
}
577
}
578
}
579
}
580
}
581
582
void FastX::detectAndCompute(cv::InputArray image,cv::InputArray mask,std::vector<cv::KeyPoint>& keypoints,
583
cv::OutputArray _descriptors,bool useProvidedKeyPoints)
584
{
585
useProvidedKeyPoints = false;
586
detectImpl(image.getMat(),keypoints,mask.getMat());
587
if(!_descriptors.needed())
588
return;
589
590
// generate descriptors based on their position
591
_descriptors.create(int(keypoints.size()),2,CV_32FC1);
592
cv::Mat descriptors = _descriptors.getMat();
593
std::vector<cv::KeyPoint>::const_iterator iter = keypoints.begin();
594
for(int row=0;iter != keypoints.end();++iter,++row)
595
{
596
descriptors.at<float>(row,0) = iter->pt.x;
597
descriptors.at<float>(row,1) = iter->pt.y;
598
}
599
if(!useProvidedKeyPoints) // suppress compiler warning
600
return;
601
return;
602
}
603
604
void FastX::detectImpl(const cv::Mat& _gray_image,
605
std::vector<cv::Mat> &rotated_images,
606
std::vector<cv::Mat> &feature_maps,
607
const cv::Mat &_mask)const
608
{
609
if(!_mask.empty())
610
CV_Error(Error::StsBadSize, "Mask is not supported");
611
CV_CheckTypeEQ(_gray_image.type(), CV_8UC1, "Unsupported image type");
612
613
// up-sample if needed
614
cv::Mat gray_image;
615
int super_res = int(parameters.super_resolution);
616
if(super_res)
617
cv::resize(_gray_image,gray_image,cv::Size(),2,2);
618
else
619
gray_image = _gray_image;
620
621
//for each scale
622
int num_scales = parameters.max_scale-parameters.min_scale+1;
623
rotated_images.resize(num_scales);
624
feature_maps.resize(num_scales);
625
parallel_for_(Range(parameters.min_scale,parameters.max_scale+1),[&](const Range& range){
626
for(int scale=range.start;scale < range.end;++scale)
627
{
628
// calc images
629
// for each angle step
630
int scale_id = scale-parameters.min_scale;
631
cv::Mat rotated,filtered_h,filtered_v;
632
int diag = int(sqrt(gray_image.rows*gray_image.rows+gray_image.cols*gray_image.cols));
633
cv::Size size(diag,diag);
634
int num = int(0.5001*CV_PI/parameters.resolution);
635
std::vector<cv::Mat> images;
636
images.resize(2*num);
637
int scale_size = int(1+pow(2.0,scale+1+super_res));
638
int scale_size2 = int((scale_size/10)*2+1);
639
for(int i=0;i<num;++i)
640
{
641
float angle = parameters.resolution*i;
642
rotate(-angle,gray_image,size,rotated);
643
cv::blur(rotated,filtered_h,cv::Size(scale_size,scale_size2));
644
cv::blur(rotated,filtered_v,cv::Size(scale_size2,scale_size));
645
646
// rotate filtered images back
647
rotate(angle,filtered_h,gray_image.size(),images[i]);
648
rotate(angle,filtered_v,gray_image.size(),images[i+num]);
649
}
650
cv::merge(images,rotated_images[scale_id]);
651
652
// calc feature map
653
calcFeatureMap(rotated_images[scale_id],feature_maps[scale_id]);
654
// filter feature map to improve impulse responses
655
if(parameters.filter)
656
{
657
cv::Mat high,low;
658
cv::blur(feature_maps[scale_id],low,cv::Size(scale_size,scale_size));
659
int scale2 = int((scale_size/6))*2+1;
660
cv::blur(feature_maps[scale_id],high,cv::Size(scale2,scale2));
661
feature_maps[scale_id] = high-0.8*low;
662
}
663
}
664
});
665
}
666
667
void FastX::detectImpl(const cv::Mat& image,std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,const cv::Mat &mask)const
668
{
669
std::vector<cv::Mat> rotated_images;
670
detectImpl(image,rotated_images,feature_maps,mask);
671
findKeyPoints(feature_maps,keypoints,mask);
672
}
673
674
void FastX::detectImpl(InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask)const
675
{
676
std::vector<cv::Mat> feature_maps;
677
detectImpl(image.getMat(),keypoints,feature_maps,mask.getMat());
678
}
679
680
void FastX::detectImpl(const Mat& src, std::vector<KeyPoint>& keypoints, const Mat& mask)const
681
{
682
std::vector<cv::Mat> feature_maps;
683
detectImpl(src,keypoints,feature_maps,mask);
684
}
685
686
687
Ellipse::Ellipse():
688
angle(0),
689
cosf(0),
690
sinf(0)
691
{
692
}
693
694
Ellipse::Ellipse(const cv::Point2f &_center, const cv::Size2f &_axes, float _angle):
695
center(_center),
696
axes(_axes),
697
angle(_angle),
698
cosf(cos(-_angle)),
699
sinf(sin(-_angle))
700
{
701
}
702
703
Ellipse::Ellipse(const Ellipse &other)
704
{
705
center = other.center;
706
axes= other.axes;
707
angle= other.angle;
708
cosf = other.cosf;
709
sinf = other.sinf;
710
}
711
712
const cv::Size2f &Ellipse::getAxes()const
713
{
714
return axes;
715
}
716
717
cv::Point2f Ellipse::getCenter()const
718
{
719
return center;
720
}
721
722
void Ellipse::draw(cv::InputOutputArray img,const cv::Scalar &color)const
723
{
724
cv::ellipse(img,center,axes,360-angle/CV_PI*180,0,360,color);
725
}
726
727
bool Ellipse::contains(const cv::Point2f &pt)const
728
{
729
cv::Point2f ptc = pt-center;
730
float x = cosf*ptc.x+sinf*ptc.y;
731
float y = -sinf*ptc.x+cosf*ptc.y;
732
if(x*x/(axes.width*axes.width)+y*y/(axes.height*axes.height) <= 1.0)
733
return true;
734
return false;
735
}
736
737
738
// returns false if the angle from the line pt1-pt2 to the line pt3-pt4 is negative
739
static bool checkOrientation(const cv::Point2f &pt1,const cv::Point2f &pt2,
740
const cv::Point2f &pt3,const cv::Point2f &pt4)
741
{
742
cv::Point3f p1(pt2.x-pt1.x,pt2.y-pt1.y,0);
743
cv::Point3f p2(pt4.x-pt3.x,pt4.y-pt3.y,0);
744
return p1.cross(p2).z > 0;
745
}
746
747
static bool sortKeyPoint(const cv::KeyPoint &pt1,const cv::KeyPoint &pt2)
748
{
749
// used as comparison function for partial sort
750
// the keypoints with the best score should be first
751
return pt1.response > pt2.response;
752
}
753
754
cv::Mat Chessboard::getObjectPoints(const cv::Size &pattern_size,float cell_size)
755
{
756
cv::Mat result(pattern_size.width*pattern_size.height,1,CV_32FC3);
757
for(int row=0;row < pattern_size.height;++row)
758
{
759
for(int col=0;col< pattern_size.width;++col)
760
{
761
cv::Point3f &pt = *result.ptr<cv::Point3f>(row*pattern_size.width+col);
762
pt.x = cell_size*col;
763
pt.y = cell_size*row;
764
pt.z = 0;
765
}
766
}
767
return result;
768
}
769
770
bool Chessboard::Board::Cell::empty()const
771
{
772
// check if one of its corners has NaN
773
if(top_left->x != top_left->x || top_left->y != top_left->y)
774
return true;
775
if(top_right->x != top_right->x || top_right->y != top_right->y)
776
return true;
777
if(bottom_right->x != bottom_right->x || bottom_right->y != bottom_right->y)
778
return true;
779
if(bottom_left->x != bottom_left->x || bottom_left->y != bottom_left->y)
780
return true;
781
return false;
782
}
783
784
int Chessboard::Board::Cell::getRow()const
785
{
786
int row = 0;
787
Cell const* temp = this;
788
for(;temp->top;temp=temp->top,++row);
789
return row;
790
}
791
792
int Chessboard::Board::Cell::getCol()const
793
{
794
int col = 0;
795
Cell const* temp = this;
796
for(;temp->left;temp=temp->left,++col);
797
return col;
798
}
799
800
Chessboard::Board::Cell::Cell() :
801
top_left(NULL), top_right(NULL), bottom_right(NULL), bottom_left(NULL),
802
left(NULL), top(NULL), right(NULL), bottom(NULL),black(false)
803
{}
804
805
Chessboard::Board::PointIter::PointIter(Cell *_cell,CornerIndex _corner_index):
806
corner_index(_corner_index),
807
cell(_cell)
808
{
809
}
810
811
Chessboard::Board::PointIter::PointIter(const PointIter &other)
812
{
813
this->operator=(other);
814
}
815
816
void Chessboard::Board::PointIter::operator=(const PointIter &other)
817
{
818
corner_index = other.corner_index;
819
cell = other.cell;
820
}
821
822
Chessboard::Board::Cell* Chessboard::Board::PointIter::getCell()
823
{
824
return cell;
825
}
826
827
bool Chessboard::Board::PointIter::valid()const
828
{
829
return cell != NULL;
830
}
831
832
bool Chessboard::Board::PointIter::isNaN()const
833
{
834
const cv::Point2f *pt = operator*();
835
if(pt->x != pt->x || pt->y != pt->y) // NaN check
836
return true;
837
return false;
838
}
839
840
bool Chessboard::Board::PointIter::checkCorner()const
841
{
842
if(!cell->empty())
843
return true;
844
// test all other cells
845
switch(corner_index)
846
{
847
case BOTTOM_LEFT:
848
if(cell->left)
849
{
850
if(!cell->left->empty())
851
return true;
852
if(cell->left->bottom && !cell->left->bottom->empty())
853
return true;
854
}
855
if(cell->bottom)
856
{
857
if(!cell->bottom->empty())
858
return true;
859
if(cell->bottom->left && !cell->bottom->left->empty())
860
return true;
861
}
862
break;
863
case TOP_LEFT:
864
if(cell->left)
865
{
866
if(!cell->left->empty())
867
return true;
868
if(cell->left->top && !cell->left->top->empty())
869
return true;
870
}
871
if(cell->top)
872
{
873
if(!cell->top->empty())
874
return true;
875
if(cell->top->left && !cell->top->left->empty())
876
return true;
877
}
878
break;
879
case TOP_RIGHT:
880
if(cell->right)
881
{
882
if(!cell->right->empty())
883
return true;
884
if(cell->right->top && !cell->right->top->empty())
885
return true;
886
}
887
if(cell->top)
888
{
889
if(!cell->top->empty())
890
return true;
891
if(cell->top->right && !cell->top->right->empty())
892
return true;
893
}
894
break;
895
case BOTTOM_RIGHT:
896
if(cell->right)
897
{
898
if(!cell->right->empty())
899
return true;
900
if(cell->right->bottom && !cell->right->bottom->empty())
901
return true;
902
}
903
if(cell->bottom)
904
{
905
if(!cell->bottom->empty())
906
return true;
907
if(cell->bottom->right && !cell->bottom->right->empty())
908
return true;
909
}
910
break;
911
default:
912
CV_Assert(false);
913
}
914
return false;
915
}
916
917
918
bool Chessboard::Board::PointIter::left(bool check_empty)
919
{
920
switch(corner_index)
921
{
922
case BOTTOM_LEFT:
923
if(cell->left && (!check_empty || !cell->left->empty()))
924
cell = cell->left;
925
else if(check_empty && cell->bottom && cell->bottom->left && !cell->bottom->left->empty())
926
{
927
cell = cell->bottom->left;
928
corner_index = TOP_LEFT;
929
}
930
else
931
return false;
932
break;
933
case TOP_LEFT:
934
if(cell->left && (!check_empty || !cell->left->empty()))
935
cell = cell->left;
936
else if(check_empty && cell->top && cell->top->left && !cell->top->left->empty())
937
{
938
cell = cell->top->left;
939
corner_index = BOTTOM_LEFT;
940
}
941
else
942
return false;
943
break;
944
case TOP_RIGHT:
945
corner_index = TOP_LEFT;
946
break;
947
case BOTTOM_RIGHT:
948
corner_index = BOTTOM_LEFT;
949
break;
950
default:
951
CV_Assert(false);
952
}
953
return true;
954
}
955
956
bool Chessboard::Board::PointIter::top(bool check_empty)
957
958
{
959
switch(corner_index)
960
{
961
case TOP_RIGHT:
962
if(cell->top && (!check_empty || !cell->top->empty()))
963
cell = cell->top;
964
else if(check_empty && cell->right && cell->right->top&& !cell->right->top->empty())
965
{
966
cell = cell->right->top;
967
corner_index = TOP_LEFT;
968
}
969
else
970
return false;
971
break;
972
case TOP_LEFT:
973
if(cell->top && (!check_empty || !cell->top->empty()))
974
cell = cell->top;
975
else if(check_empty && cell->left && cell->left->top&& !cell->left->top->empty())
976
{
977
cell = cell->left->top;
978
corner_index = TOP_RIGHT;
979
}
980
else
981
return false;
982
break;
983
case BOTTOM_LEFT:
984
corner_index = TOP_LEFT;
985
break;
986
case BOTTOM_RIGHT:
987
corner_index = TOP_RIGHT;
988
break;
989
default:
990
CV_Assert(false);
991
}
992
return true;
993
}
994
995
bool Chessboard::Board::PointIter::right(bool check_empty)
996
{
997
switch(corner_index)
998
{
999
case TOP_RIGHT:
1000
if(cell->right && (!check_empty || !cell->right->empty()))
1001
cell = cell->right;
1002
else if(check_empty && cell->top && cell->top->right && !cell->top->right->empty())
1003
{
1004
cell = cell->top->right;
1005
corner_index = BOTTOM_RIGHT;
1006
}
1007
else
1008
return false;
1009
break;
1010
case BOTTOM_RIGHT:
1011
if(cell->right && (!check_empty || !cell->right->empty()))
1012
cell = cell->right;
1013
else if(check_empty && cell->bottom && cell->bottom->right && !cell->bottom->right->empty())
1014
{
1015
cell = cell->bottom->right;
1016
corner_index = TOP_RIGHT;
1017
}
1018
else
1019
return false;
1020
break;
1021
case TOP_LEFT:
1022
corner_index = TOP_RIGHT;
1023
break;
1024
case BOTTOM_LEFT:
1025
corner_index = BOTTOM_RIGHT;
1026
break;
1027
default:
1028
CV_Assert(false);
1029
}
1030
return true;
1031
}
1032
1033
bool Chessboard::Board::PointIter::bottom(bool check_empty)
1034
{
1035
switch(corner_index)
1036
{
1037
case BOTTOM_LEFT:
1038
if(cell->bottom && (!check_empty || !cell->bottom->empty()))
1039
cell = cell->bottom;
1040
else if(check_empty && cell->left && cell->left->bottom && !cell->left->bottom->empty())
1041
{
1042
cell = cell->left->bottom;
1043
corner_index = BOTTOM_RIGHT;
1044
}
1045
else
1046
return false;
1047
break;
1048
case BOTTOM_RIGHT:
1049
if(cell->bottom && (!check_empty || !cell->bottom->empty()))
1050
cell = cell->bottom;
1051
else if(check_empty && cell->right && cell->right->bottom && !cell->right->bottom->empty())
1052
{
1053
cell = cell->right->bottom;
1054
corner_index = BOTTOM_LEFT;
1055
}
1056
else
1057
return false;
1058
break;
1059
case TOP_LEFT:
1060
corner_index = BOTTOM_LEFT;
1061
break;
1062
case TOP_RIGHT:
1063
corner_index = BOTTOM_RIGHT;
1064
break;
1065
default:
1066
CV_Assert(false);
1067
}
1068
return true;
1069
}
1070
1071
1072
const cv::Point2f* Chessboard::Board::PointIter::operator*()const
1073
{
1074
switch(corner_index)
1075
{
1076
case TOP_LEFT:
1077
return cell->top_left;
1078
case TOP_RIGHT:
1079
return cell->top_right;
1080
case BOTTOM_RIGHT:
1081
return cell->bottom_right;
1082
case BOTTOM_LEFT:
1083
return cell->bottom_left;
1084
}
1085
CV_Assert(false);
1086
}
1087
1088
const cv::Point2f* Chessboard::Board::PointIter::operator->()const
1089
{
1090
return operator*();
1091
}
1092
1093
cv::Point2f* Chessboard::Board::PointIter::operator*()
1094
{
1095
const cv::Point2f *pt = const_cast<const PointIter*>(this)->operator*();
1096
return const_cast<cv::Point2f*>(pt);
1097
}
1098
1099
cv::Point2f* Chessboard::Board::PointIter::operator->()
1100
{
1101
return operator*();
1102
}
1103
1104
Chessboard::Board::Board(float _white_angle,float _black_angle):
1105
top_left(NULL),
1106
rows(0),
1107
cols(0),
1108
white_angle(_white_angle),
1109
black_angle(_black_angle)
1110
{
1111
}
1112
1113
1114
Chessboard::Board::Board(const Chessboard::Board &other):
1115
top_left(NULL),
1116
rows(0),
1117
cols(0)
1118
{
1119
*this = other;
1120
}
1121
1122
Chessboard::Board::Board(const cv::Size &size, const std::vector<cv::Point2f> &points,float _white_angle,float _black_angle):
1123
top_left(NULL),
1124
rows(0),
1125
cols(0),
1126
white_angle(_white_angle),
1127
black_angle(_black_angle)
1128
{
1129
if(size.width*size.height != int(points.size()))
1130
CV_Error(Error::StsBadArg,"size mismatch");
1131
if(size.width < 3 || size.height < 3)
1132
CV_Error(Error::StsBadArg,"at least 3 rows and cols are needed to initialize the board");
1133
1134
// init board with 3x3
1135
// TODO write function speeding up the copying
1136
cv::Mat data = cv::Mat(points).reshape(2,size.height);
1137
cv::Mat temp;
1138
data(cv::Rect(0,0,3,3)).copyTo(temp);
1139
std::vector<cv::Point2f> ipoints = temp.reshape(2,1);
1140
if(!init(ipoints))
1141
return;
1142
1143
// add all cols
1144
for(int col=3 ; col< data.cols;++col)
1145
{
1146
data(cv::Rect(col,0,1,3)).copyTo(temp);
1147
ipoints = temp.reshape(2,1);
1148
addColumnRight(ipoints);
1149
}
1150
1151
// add all rows
1152
for(int row=3; row < data.rows;++row)
1153
{
1154
data(cv::Rect(0,row,cols,1)).copyTo(temp);
1155
ipoints = temp.reshape(2,1);
1156
addRowBottom(ipoints);
1157
}
1158
}
1159
1160
Chessboard::Board::~Board()
1161
{
1162
clear();
1163
}
1164
1165
std::vector<cv::Point2f> Chessboard::Board::getCellCenters()const
1166
{
1167
int icols = int(colCount());
1168
int irows = int(rowCount());
1169
if(icols < 3 || irows < 3)
1170
throw std::runtime_error("getCellCenters: Chessboard must be at least consist of 3 rows and cols to calcualte the cell centers");
1171
1172
std::vector<cv::Point2f> points;
1173
cv::Matx33d H(estimateHomography(DUMMY_FIELD_SIZE));
1174
cv::Vec3d pt1,pt2;
1175
pt1[2] = 1;
1176
for(int row = 0;row < irows;++row)
1177
{
1178
pt1[1] = (0.5+row)*DUMMY_FIELD_SIZE;
1179
for(int col= 0;col< icols;++col)
1180
{
1181
pt1[0] = (0.5+col)*DUMMY_FIELD_SIZE;
1182
pt2 = H*pt1;
1183
points.push_back(cv::Point2f(float(pt2[0]/pt2[2]),float(pt2[1]/pt2[2])));
1184
}
1185
}
1186
return points;
1187
}
1188
1189
void Chessboard::Board::draw(cv::InputArray m,cv::OutputArray out,cv::InputArray _H)const
1190
{
1191
cv::Mat H = _H.getMat();
1192
if(H.empty())
1193
H = estimateHomography();
1194
cv::Mat image = m.getMat().clone();
1195
if(image.type() == CV_32FC1)
1196
{
1197
double maxVal,minVal;
1198
cv::minMaxLoc(image, &minVal, &maxVal);
1199
double scale = 255.0/(maxVal-minVal);
1200
image.convertTo(image,CV_8UC1,scale,-scale*minVal);
1201
cv::applyColorMap(image,image,cv::COLORMAP_JET);
1202
}
1203
1204
// draw all points and search areas
1205
std::vector<cv::Point2f> points = getCorners();
1206
std::vector<cv::Point2f>::const_iterator iter1 = points.begin();
1207
int icols = int(colCount());
1208
int irows = int(rowCount());
1209
int count=0;
1210
for(int row=0;row<irows;++row)
1211
{
1212
for(int col=0;col<icols;++col,++iter1)
1213
{
1214
if(iter1->x != iter1->x) // NaN check
1215
{
1216
// draw search ellipse
1217
Ellipse ellipse = estimateSearchArea(H,row,col,0.4F);
1218
ellipse.draw(image,cv::Scalar::all(200));
1219
}
1220
else
1221
{
1222
cv::circle(image,*iter1,4,cv::Scalar(count*20,count*20,count*20,255),-1);
1223
++count;
1224
}
1225
}
1226
}
1227
1228
// draw field colors
1229
for(int row=0;row<irows-1;++row)
1230
{
1231
for(int col=0;col<icols-1;++col)
1232
{
1233
const Cell *cell = getCell(row,col);
1234
cv::Point2f center = *cell->top_left+*cell->top_right+*cell->bottom_left+*cell->bottom_right;
1235
center.x /=4;
1236
center.y /=4;
1237
int size = 4;
1238
if(row==0&&col==0)
1239
size=8;
1240
if(row==0&&col==1)
1241
size=7;
1242
if(cell->black)
1243
cv::circle(image,center,size,cv::Scalar::all(255),-1);
1244
else
1245
cv::circle(image,center,size,cv::Scalar(0,0,10,255),-1);
1246
}
1247
}
1248
1249
out.create(image.rows,image.cols,image.type());
1250
image.copyTo(out.getMat());
1251
}
1252
1253
bool Chessboard::Board::estimatePose(const cv::Size2f &real_size,cv::InputArray _K,cv::OutputArray rvec,cv::OutputArray tvec)const
1254
{
1255
cv::Mat K = _K.getMat();
1256
CV_CheckTypeEQ(K.type(), CV_64FC1, "wrong K type");
1257
CV_CheckEQ(K.size(), Size(3, 3), "wrong K size");
1258
if(isEmpty())
1259
return false;
1260
1261
int icols = int(colCount());
1262
int irows = int(rowCount());
1263
float field_width = real_size.width/(icols+1);
1264
float field_height= real_size.height/(irows+1);
1265
// the center of the board is placed at (0,0,1)
1266
int offset_x = int(-(icols-1)*field_width*0.5F);
1267
int offset_y = int(-(irows-1)*field_width*0.5F);
1268
1269
std::vector<cv::Point2f> image_points;
1270
std::vector<cv::Point3f> object_points;
1271
std::vector<cv::Point2f> corners_temp = getCorners(true);
1272
std::vector<cv::Point2f>::const_iterator iter = corners_temp.begin();
1273
for(int row = 0;row < irows;++row)
1274
{
1275
for(int col= 0;col<icols;++col,++iter)
1276
{
1277
if(iter == corners_temp.end())
1278
CV_Error(Error::StsInternal,"internal error");
1279
if(iter->x != iter->x) // NaN check
1280
continue;
1281
image_points.push_back(*iter);
1282
object_points.push_back(cv::Point3f(field_width*col-offset_x,field_height*row-offset_y,1.0));
1283
}
1284
}
1285
return cv::solvePnP(object_points,image_points,K,cv::Mat(),rvec,tvec);//,cv::SOLVEPNP_P3P);
1286
}
1287
1288
float Chessboard::Board::getBlackAngle()const
1289
{
1290
return black_angle;
1291
}
1292
1293
float Chessboard::Board::getWhiteAngle()const
1294
{
1295
return white_angle;
1296
}
1297
1298
void Chessboard::Board::swap(Chessboard::Board &other)
1299
{
1300
corners.swap(other.corners);
1301
cells.swap(other.cells);
1302
std::swap(rows,other.rows);
1303
std::swap(cols,other.cols);
1304
std::swap(top_left,other.top_left);
1305
std::swap(white_angle,other.white_angle);
1306
std::swap(black_angle,other.black_angle);
1307
}
1308
1309
Chessboard::Board& Chessboard::Board::operator=(const Chessboard::Board &other)
1310
{
1311
if(this == &other)
1312
return *this;
1313
clear();
1314
rows = other.rows;
1315
cols = other.cols;
1316
white_angle = other.white_angle;
1317
black_angle = other.black_angle;
1318
cells.reserve(other.cells.size());
1319
corners.reserve(other.corners.size());
1320
1321
//copy all points and generate mapping
1322
std::map<cv::Point2f*,cv::Point2f*> point_point_mapping;
1323
point_point_mapping[NULL] = NULL;
1324
std::vector<cv::Point2f*>::const_iterator iter = other.corners.begin();
1325
for(;iter != other.corners.end();++iter)
1326
{
1327
cv::Point2f *pt = new cv::Point2f(**iter);
1328
point_point_mapping[*iter] = pt;
1329
corners.push_back(pt);
1330
}
1331
1332
//copy all cells using mapping
1333
std::map<Cell*,Cell*> cell_cell_mapping;
1334
std::vector<Cell*>::const_iterator iter2 = other.cells.begin();
1335
for(;iter2 != other.cells.end();++iter2)
1336
{
1337
Cell *cell = new Cell;
1338
cell->top_left = point_point_mapping[(*iter2)->top_left];
1339
cell->top_right= point_point_mapping[(*iter2)->top_right];
1340
cell->bottom_right= point_point_mapping[(*iter2)->bottom_right];
1341
cell->bottom_left = point_point_mapping[(*iter2)->bottom_left];
1342
cell->black = (*iter2)->black;
1343
cell_cell_mapping[*iter2] = cell;
1344
cells.push_back(cell);
1345
}
1346
1347
//set cell connections using mapping
1348
cell_cell_mapping[NULL] = NULL;
1349
iter2 = other.cells.begin();
1350
std::vector<Cell*>::iterator iter3 = cells.begin();
1351
for(;iter2 != other.cells.end();++iter2,++iter3)
1352
{
1353
(*iter3)->left = cell_cell_mapping[(*iter2)->left];
1354
(*iter3)->top = cell_cell_mapping[(*iter2)->top];
1355
(*iter3)->right = cell_cell_mapping[(*iter2)->right];
1356
(*iter3)->bottom= cell_cell_mapping[(*iter2)->bottom];
1357
}
1358
top_left = cell_cell_mapping[other.top_left];
1359
return *this;
1360
}
1361
1362
void Chessboard::Board::normalizeOrientation(bool bblack)
1363
{
1364
// fix ordering
1365
cv::Point2f y = getCorner(0,1)-getCorner(2,1);
1366
cv::Point2f x = getCorner(1,2)-getCorner(1,0);
1367
cv::Point3f y3d(y.x,y.y,0);
1368
cv::Point3f x3d(x.x,x.y,0);
1369
if(x3d.cross(y3d).z > 0)
1370
flipHorizontal();
1371
1372
//normalize orientation so that first element is black or white
1373
const Cell* cell = getCell(0,0);
1374
if(cell->black != bblack && colCount()%2 != 0)
1375
rotateLeft();
1376
else if(cell->black != bblack && rowCount()%2 != 0)
1377
{
1378
rotateLeft();
1379
rotateLeft();
1380
}
1381
1382
//find closest point to top left image corner
1383
//in case of symmetric checkerboard
1384
if(colCount() == rowCount())
1385
{
1386
PointIter iter_top_right(top_left,TOP_RIGHT);
1387
while(iter_top_right.right());
1388
PointIter iter_bottom_right(iter_top_right);
1389
while(iter_bottom_right.bottom());
1390
PointIter iter_bottom_left(top_left,BOTTOM_LEFT);
1391
while(iter_bottom_left.bottom());
1392
// check if one of the cell is empty and do not normalize if so
1393
if(top_left->empty() || iter_top_right.getCell()->empty() ||
1394
iter_bottom_left.getCell()->empty() || iter_bottom_right.getCell()->empty())
1395
return;
1396
1397
float d1 = pow(top_left->top_left->x,2)+pow(top_left->top_left->y,2);
1398
float d2 = pow((*iter_top_right)->x,2)+pow((*iter_top_right)->y,2);
1399
float d3 = pow((*iter_bottom_left)->x,2)+pow((*iter_bottom_left)->y,2);
1400
float d4 = pow((*iter_bottom_right)->x,2)+pow((*iter_bottom_right)->y,2);
1401
if(d2 <= d1 && d2 <= d3 && d2 <= d4) // top left is top right
1402
rotateLeft();
1403
else if(d3 <= d1 && d3 <= d2 && d3 <= d4) // top left is bottom left
1404
rotateRight();
1405
else if(d4 <= d1 && d4 <= d2 && d4 <= d3) // top left is bottom right
1406
{
1407
rotateLeft();
1408
rotateLeft();
1409
}
1410
}
1411
}
1412
1413
void Chessboard::Board::rotateRight()
1414
{
1415
PointIter p_iter(top_left,BOTTOM_LEFT);
1416
while(p_iter.bottom());
1417
1418
std::vector<Cell*>::iterator iter = cells.begin();
1419
for(;iter != cells.end();++iter)
1420
{
1421
Cell *temp = (*iter)->bottom;
1422
(*iter)->bottom = (*iter)->right;
1423
(*iter)->right= (*iter)->top;
1424
(*iter)->top= (*iter)->left;
1425
(*iter)->left = temp;
1426
1427
cv::Point2f *ptemp = (*iter)->bottom_left;
1428
(*iter)->bottom_left= (*iter)->bottom_right;
1429
(*iter)->bottom_right= (*iter)->top_right;
1430
(*iter)->top_right= (*iter)->top_left;
1431
(*iter)->top_left= ptemp;
1432
}
1433
int temp = rows;
1434
rows = cols;
1435
cols = temp;
1436
top_left = p_iter.getCell();
1437
}
1438
1439
1440
void Chessboard::Board::rotateLeft()
1441
{
1442
PointIter p_iter(top_left,TOP_RIGHT);
1443
while(p_iter.right());
1444
1445
std::vector<Cell*>::iterator iter = cells.begin();
1446
for(;iter != cells.end();++iter)
1447
{
1448
Cell *temp = (*iter)->top;
1449
(*iter)->top = (*iter)->right;
1450
(*iter)->right= (*iter)->bottom;
1451
(*iter)->bottom= (*iter)->left;
1452
(*iter)->left = temp;
1453
1454
cv::Point2f *ptemp = (*iter)->top_left;
1455
(*iter)->top_left = (*iter)->top_right;
1456
(*iter)->top_right= (*iter)->bottom_right;
1457
(*iter)->bottom_right = (*iter)->bottom_left;
1458
(*iter)->bottom_left = ptemp;
1459
}
1460
int temp = rows;
1461
rows = cols;
1462
cols = temp;
1463
top_left = p_iter.getCell();
1464
}
1465
1466
void Chessboard::Board::flipHorizontal()
1467
{
1468
PointIter p_iter(top_left,TOP_RIGHT);
1469
while(p_iter.right());
1470
1471
std::vector<Cell*>::iterator iter = cells.begin();
1472
for(;iter != cells.end();++iter)
1473
{
1474
Cell *temp = (*iter)->right;
1475
(*iter)->right= (*iter)->left;
1476
(*iter)->left = temp;
1477
1478
cv::Point2f *ptemp = (*iter)->top_left;
1479
(*iter)->top_left = (*iter)->top_right;
1480
(*iter)->top_right = ptemp;
1481
1482
ptemp = (*iter)->bottom_left;
1483
(*iter)->bottom_left = (*iter)->bottom_right;
1484
(*iter)->bottom_right = ptemp;
1485
}
1486
top_left = p_iter.getCell();
1487
}
1488
1489
void Chessboard::Board::flipVertical()
1490
{
1491
PointIter p_iter(top_left,BOTTOM_LEFT);
1492
while(p_iter.bottom());
1493
1494
std::vector<Cell*>::iterator iter = cells.begin();
1495
for(;iter != cells.end();++iter)
1496
{
1497
Cell *temp = (*iter)->top;
1498
(*iter)->top= (*iter)->bottom;
1499
(*iter)->bottom = temp;
1500
1501
cv::Point2f *ptemp = (*iter)->top_left;
1502
(*iter)->top_left = (*iter)->bottom_left;
1503
(*iter)->bottom_left = ptemp;
1504
1505
ptemp = (*iter)->top_right;
1506
(*iter)->top_right = (*iter)->bottom_right;
1507
(*iter)->bottom_right = ptemp;
1508
}
1509
top_left = p_iter.getCell();
1510
}
1511
1512
// returns the best found score
1513
// if NaN is returned for a point no point at all was found
1514
// if 0 is returned the point lies outside of the ellipse
1515
float Chessboard::Board::findMaxPoint(cv::flann::Index &index,const cv::Mat &data,const Ellipse &ellipse,float white_angle,float black_angle,cv::Point2f &point)
1516
{
1517
// flann data type enriched with angles (third column)
1518
CV_CheckType(data.type(), CV_32FC1, "type of flann data is not supported");
1519
CV_CheckEQ(data.cols, 4, "4-cols flann data is expected");
1520
1521
std::vector<float> query,dists;
1522
std::vector<int> indices;
1523
query.resize(2);
1524
point = ellipse.getCenter();
1525
query[0] = point.x;
1526
query[1] = point.y;
1527
index.knnSearch(query,indices,dists,4,cv::flann::SearchParams(64));
1528
std::vector<int>::const_iterator iter = indices.begin();
1529
float best_score = -std::numeric_limits<float>::max();
1530
point.x = std::numeric_limits<float>::quiet_NaN();
1531
point.y = std::numeric_limits<float>::quiet_NaN();
1532
for(;iter != indices.end();++iter)
1533
{
1534
const float *val = data.ptr<float>(*iter);
1535
const float &response = *(val+3);
1536
if(response < best_score)
1537
continue;
1538
const float &a0 = *(val+2);
1539
float a1 = std::fabs(a0-white_angle);
1540
float a2 = std::fabs(a0-black_angle);
1541
if(a1 > CV_PI*0.5)
1542
a1 = std::fabs(float(a1-CV_PI));
1543
if(a2> CV_PI*0.5)
1544
a2 = std::fabs(float(a2-CV_PI));
1545
if(a1 < MAX_ANGLE || a2 < MAX_ANGLE )
1546
{
1547
cv::Point2f pt(val[0], val[1]);
1548
if(point.x != point.x) // NaN check
1549
point = pt;
1550
if(best_score < response && ellipse.contains(pt))
1551
{
1552
best_score = response;
1553
point = pt;
1554
}
1555
}
1556
}
1557
if(best_score == -std::numeric_limits<float>::max())
1558
return 0;
1559
else
1560
return best_score;
1561
}
1562
1563
void Chessboard::Board::clear()
1564
{
1565
top_left = NULL; rows = 0; cols = 0;
1566
std::vector<Cell*>::iterator iter = cells.begin();
1567
for(;iter != cells.end();++iter)
1568
delete *iter;
1569
cells.clear();
1570
std::vector<cv::Point2f*>::iterator iter2 = corners.begin();
1571
for(;iter2 != corners.end();++iter2)
1572
delete *iter2;
1573
corners.clear();
1574
}
1575
1576
// p0 p1 p2
1577
// p3 p4 p5
1578
// p6 p7 p8
1579
bool Chessboard::Board::init(const std::vector<cv::Point2f> points)
1580
{
1581
clear();
1582
if(points.size() != 9)
1583
CV_Error(Error::StsBadArg,"exact nine points are expected to initialize the board");
1584
1585
// generate cells
1586
corners.resize(9);
1587
for(int i=0;i < 9;++i)
1588
corners[i] = new cv::Point2f(points[i]);
1589
cells.resize(4);
1590
for(int i=0;i<4;++i)
1591
cells[i] = new Cell();
1592
1593
//cell 0
1594
cells[0]->top_left = corners[0];
1595
cells[0]->top_right = corners[1];
1596
cells[0]->bottom_right = corners[4];
1597
cells[0]->bottom_left = corners[3];
1598
cells[0]->right = cells[1];
1599
cells[0]->bottom = cells[2];
1600
1601
//cell 1
1602
cells[1]->top_left = corners[1];
1603
cells[1]->top_right = corners[2];
1604
cells[1]->bottom_right = corners[5];
1605
cells[1]->bottom_left = corners[4];
1606
cells[1]->left = cells[0];
1607
cells[1]->bottom = cells[3];
1608
1609
//cell 2
1610
cells[2]->top_left = corners[3];
1611
cells[2]->top_right = corners[4];
1612
cells[2]->bottom_right = corners[7];
1613
cells[2]->bottom_left = corners[6];
1614
cells[2]->top = cells[0];
1615
cells[2]->right = cells[3];
1616
1617
//cell 3
1618
cells[3]->top_left = corners[4];
1619
cells[3]->top_right = corners[5];
1620
cells[3]->bottom_right = corners[8];
1621
cells[3]->bottom_left = corners[7];
1622
cells[3]->top = cells[1];
1623
cells[3]->left= cells[2];
1624
1625
top_left = cells.front();
1626
rows = 3;
1627
cols = 3;
1628
1629
// set inital cell colors
1630
Point2f pt1 = *(cells[0]->top_right)-*(cells[0]->bottom_left);
1631
pt1 /= cv::norm(pt1);
1632
cv::Point2f pt2(cos(white_angle),-sin(white_angle));
1633
cv::Point2f pt3(cos(black_angle),-sin(black_angle));
1634
if(fabs(pt1.dot(pt2)) < fabs(pt1.dot(pt3)))
1635
{
1636
cells[0]->black = false;
1637
cells[1]->black = true;
1638
cells[2]->black = true;
1639
cells[3]->black = false;
1640
}
1641
else
1642
{
1643
cells[0]->black = true;
1644
cells[1]->black = false;
1645
cells[2]->black = false;
1646
cells[3]->black = true;
1647
}
1648
return true;
1649
}
1650
1651
//TODO magic number
1652
bool Chessboard::Board::estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2, cv::Point2f &p3)
1653
{
1654
// use cross ration to find new point
1655
if(p0 == p1 || p0 == p2 || p1 == p2)
1656
return false;
1657
cv::Point2f p01 = p1-p0;
1658
cv::Point2f p12 = p2-p1;
1659
float a = float(cv::norm(p01));
1660
float b = float(cv::norm(p12));
1661
float t = (0.75F*a-0.25F*b);
1662
if(t <= 0)
1663
return false;
1664
float c = 0.25F*b*(a+b)/t;
1665
if(c < 0.1F)
1666
return false;
1667
p01 = p01/a;
1668
p12 = p12/b;
1669
// check angle between p01 and p12 < 25°
1670
if(p01.dot(p12) < 0.9)
1671
return false;
1672
// calc mean
1673
// p12 = (p01+p12)*0.5;
1674
// p3 = p2+p12*c;
1675
p3 = p2+p12*c;
1676
1677
// compensate radial distortion by fitting polynom
1678
std::vector<double> x,y;
1679
x.resize(3,0); y.resize(3,0);
1680
x[1] = b;
1681
x[2] = b+a;
1682
y[2] = calcSignedDistance(-p12,p2,p0);
1683
cv::Mat dst;
1684
polyfit(cv::Mat(x),cv::Mat(y),dst,2);
1685
double d = dst.at<double>(0)-dst.at<double>(1)*c+dst.at<double>(2)*c*c;
1686
cv::Vec3f v1(p12.x,p12.y,0);
1687
cv::Vec3f v2(0,0,1);
1688
cv::Vec3f v3 = v1.cross(v2);
1689
cv::Point2f n2(v3[0],v3[1]);
1690
p3 += d*n2;
1691
return true;
1692
}
1693
1694
bool Chessboard::Board::estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2, const cv::Point2f &p3, cv::Point2f &p4)
1695
{
1696
// use 1D homography to find fith point minimizing square error
1697
if(p0 == p1 || p0 == p2 || p0 == p3 || p1 == p2 || p1 == p3 || p2 == p3 )
1698
return false;
1699
static const cv::Mat src = (cv::Mat_<double>(1,4) << 0,10,20,30);
1700
cv::Point2f p01 = p1-p0;
1701
cv::Point2f p02 = p2-p0;
1702
cv::Point2f p03 = p3-p0;
1703
float a = float(cv::norm(p01));
1704
float b = float(cv::norm(p02));
1705
float c = float(cv::norm(p03));
1706
cv::Mat dst = (cv::Mat_<double>(1,4) << 0,a,b,c);
1707
cv::Mat h = findHomography1D(src,dst);
1708
float d = float((h.at<double>(0,0)*40+h.at<double>(0,1))/(h.at<double>(1,0)*40+h.at<double>(1,1)));
1709
cv::Point2f p12 = p2-p1;
1710
cv::Point2f p23 = p3-p2;
1711
p01 = p01/a;
1712
p12 = p12/cv::norm(p12);
1713
p23 = p23/cv::norm(p23);
1714
p4 = p3+(d-c)*p23;
1715
1716
// compensate radial distortion by fitting polynom
1717
std::vector<double> x,y;
1718
x.resize(4,0); y.resize(4,0);
1719
x[1] = c-b;
1720
x[2] = c-a;
1721
x[3] = c;
1722
y[2] = calcSignedDistance(-p23,p3,p1);
1723
y[3] = calcSignedDistance(-p23,p3,p0);
1724
polyfit(cv::Mat(x),cv::Mat(y),dst,2);
1725
d = d-c;
1726
double e = dst.at<double>(0)-dst.at<double>(1)*fabs(d)+dst.at<double>(2)*d*d;
1727
cv::Vec3f v1(p23.x,p23.y,0);
1728
cv::Vec3f v2(0,0,1);
1729
cv::Vec3f v3 = v1.cross(v2);
1730
cv::Point2f n2(v3[0],v3[1]);
1731
p4 += e*n2;
1732
return true;
1733
}
1734
1735
// H is describing the transformation from dummy to reality
1736
Ellipse Chessboard::Board::estimateSearchArea(cv::Mat _H,int row, int col,float p,int field_size)
1737
{
1738
cv::Matx31d point1,point2,center;
1739
center(0) = (1+col)*field_size;
1740
center(1) = (1+row)*field_size;
1741
center(2) = 1.0;
1742
point1(0) = center(0)-p*field_size;
1743
point1(1) = center(1);
1744
point1(2) = center(2);
1745
point2(0) = center(0);
1746
point2(1) = center(1)-p*field_size;
1747
point2(2) = center(2);
1748
1749
cv::Matx33d H(_H);
1750
point1 = H*point1;
1751
point2 = H*point2;
1752
center = H*center;
1753
cv::Point2f pt(float(center(0)/center(2)),float(center(1)/center(2)));
1754
cv::Point2f pt1(float(point1(0)/point1(2)),float(point1(1)/point1(2)));
1755
cv::Point2f pt2(float(point2(0)/point2(2)),float(point2(1)/point2(2)));
1756
1757
cv::Point2f p01(pt1-pt);
1758
cv::Point2f p02(pt2-pt);
1759
float norm1 = float(cv::norm(p01));
1760
float norm2 = float(cv::norm(p02));
1761
float angle = float(acos(p01.dot(p02)/norm1/norm2));
1762
cv::Size2f axes(norm1,norm2);
1763
return Ellipse(pt,axes,angle);
1764
}
1765
1766
bool Chessboard::Board::estimateSearchArea(const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3,float p,Ellipse &ellipse,const cv::Point2f *p0)
1767
{
1768
cv::Point2f p4,n;
1769
if(p0)
1770
{
1771
// use 1D homography
1772
if(!estimatePoint(*p0,p1,p2,p3,p4))
1773
return false;
1774
n = p4-*p0;
1775
}
1776
else
1777
{
1778
// use cross ratio
1779
if(!estimatePoint(p1,p2,p3,p4))
1780
return false;
1781
n = p4-p1;
1782
}
1783
float norm = float(cv::norm(n));
1784
n = n/norm;
1785
float angle = acos(n.x);
1786
if(n.y > 0)
1787
angle = float(2.0F*CV_PI-angle);
1788
n = p4-p3;
1789
norm = float(cv::norm(n));
1790
double delta = std::max(3.0F,p*norm);
1791
ellipse = Ellipse(p4,cv::Size(int(delta),int(std::max(2.0,delta*ELLIPSE_WIDTH))),angle);
1792
return true;
1793
}
1794
1795
bool Chessboard::Board::checkRowColumn(const std::vector<cv::Point2f> &points)
1796
{
1797
if(points.size() < 4)
1798
{
1799
if(points.size() == 3)
1800
return true;
1801
else
1802
return false;
1803
}
1804
std::vector<cv::Point2f>::const_iterator iter = points.begin();
1805
std::vector<cv::Point2f>::const_iterator iter2 = iter+1;
1806
std::vector<cv::Point2f>::const_iterator iter3 = iter2+1;
1807
std::vector<cv::Point2f>::const_iterator iter4 = iter3+1;
1808
Ellipse ellipse;
1809
if(!estimateSearchArea(*iter4,*iter3,*iter2,CORNERS_SEARCH*3,ellipse))
1810
return false;
1811
if(!ellipse.contains(*iter))
1812
return false;
1813
1814
std::vector<cv::Point2f>::const_iterator iter5 = iter4+1;
1815
for(;iter5 != points.end();++iter5)
1816
{
1817
if(!estimateSearchArea(*iter2,*iter3,*iter4,CORNERS_SEARCH,ellipse,&(*iter)))
1818
return false;
1819
if(!ellipse.contains(*iter5))
1820
return false;
1821
iter = iter2;
1822
iter2 = iter3;
1823
iter3 = iter4;
1824
iter4 = iter5;
1825
}
1826
return true;
1827
}
1828
1829
cv::Point2f &Chessboard::Board::getCorner(int _row,int _col)
1830
{
1831
int _rows = int(rowCount());
1832
int _cols = int(colCount());
1833
if(_row >= _rows || _col >= _cols)
1834
CV_Error(Error::StsBadArg,"out of bound");
1835
if(_row == 0)
1836
{
1837
PointIter iter(top_left,TOP_LEFT);
1838
int count = 0;
1839
do
1840
{
1841
if(count == _col)
1842
return *(*iter);
1843
++count;
1844
}while(iter.right());
1845
}
1846
else
1847
{
1848
Cell *row_start = top_left;
1849
int count = 1;
1850
do
1851
{
1852
if(count == _row)
1853
{
1854
PointIter iter(row_start,BOTTOM_LEFT);
1855
int count2 = 0;
1856
do
1857
{
1858
if(count2 == _col)
1859
return *(*iter);
1860
++count2;
1861
}while(iter.right());
1862
}
1863
++count;
1864
row_start = row_start->bottom;
1865
}while(_row);
1866
}
1867
CV_Error(Error::StsInternal,"cannot find corner");
1868
// return *top_left->top_left; // never reached
1869
}
1870
1871
bool Chessboard::Board::isCellBlack(int row,int col)const
1872
{
1873
return getCell(row,col)->black;
1874
}
1875
1876
bool Chessboard::Board::isCellEmpty(int row,int col)
1877
{
1878
return getCell(row,col)->empty();
1879
}
1880
1881
Chessboard::Board::Cell* Chessboard::Board::getCell(int row,int col)
1882
{
1883
const Cell *cell = const_cast<const Board*>(this)->getCell(row,col);
1884
return const_cast<Cell*>(cell);
1885
}
1886
1887
const Chessboard::Board::Cell* Chessboard::Board::getCell(int row,int col)const
1888
{
1889
if(row > rows-1 || row < 0 || col > cols-1 || col < 0)
1890
CV_Error(Error::StsBadArg,"out of bound");
1891
PointIter p_iter(top_left,BOTTOM_RIGHT);
1892
for(int i=0; i< row; p_iter.bottom(),++i);
1893
for(int i=0; i< col; p_iter.right(),++i);
1894
return p_iter.getCell();
1895
}
1896
1897
1898
bool Chessboard::Board::isEmpty()const
1899
{
1900
return cells.empty();
1901
}
1902
1903
size_t Chessboard::Board::colCount()const
1904
{
1905
return cols;
1906
}
1907
1908
size_t Chessboard::Board::rowCount()const
1909
{
1910
return rows;
1911
}
1912
1913
cv::Size Chessboard::Board::getSize()const
1914
{
1915
return cv::Size(int(colCount()),int(rowCount()));
1916
}
1917
1918
void Chessboard::Board::drawEllipses(const std::vector<Ellipse> &ellipses)
1919
{
1920
// currently there is no global image find way to store global image
1921
// without polluting namespace
1922
if(ellipses.empty())
1923
return; //avoid compiler warning
1924
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
1925
cv::Mat img;
1926
draw(debug_image,img);
1927
std::vector<Ellipse>::iterator iter;
1928
for(;iter != ellipses.end();++iter)
1929
iter->draw(img);
1930
cv::imshow("chessboard",img);
1931
cv::waitKey(-1);
1932
#endif
1933
}
1934
1935
1936
void Chessboard::Board::growLeft()
1937
{
1938
if(isEmpty())
1939
CV_Error(Error::StsInternal,"Board is empty");
1940
PointIter iter(top_left,TOP_LEFT);
1941
std::vector<cv::Point2f> points;
1942
cv::Point2f pt;
1943
do
1944
{
1945
PointIter iter2(iter);
1946
cv::Point2f *p0 = *iter2;
1947
iter2.right();
1948
cv::Point2f *p1 = *iter2;
1949
iter2.right();
1950
cv::Point2f *p2 = *iter2;
1951
if(iter2.right())
1952
estimatePoint(**iter2,*p2,*p1,*p0,pt);
1953
else
1954
estimatePoint(*p2,*p1,*p0,pt);
1955
points.push_back(pt);
1956
}
1957
while(iter.bottom());
1958
addColumnLeft(points);
1959
}
1960
1961
bool Chessboard::Board::growLeft(const cv::Mat &map,cv::flann::Index &flann_index)
1962
{
1963
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
1964
std::vector<Ellipse> ellipses;
1965
#endif
1966
if(isEmpty())
1967
CV_Error(Error::StsInternal,"growLeft: Board is empty");
1968
PointIter iter(top_left,TOP_LEFT);
1969
std::vector<cv::Point2f> points;
1970
int count = 0;
1971
Ellipse ellipse;
1972
cv::Point2f pt;
1973
do
1974
{
1975
PointIter iter2(iter);
1976
cv::Point2f *p0 = *iter2;
1977
iter2.right();
1978
cv::Point2f *p1 = *iter2;
1979
iter2.right();
1980
cv::Point2f *p2 = *iter2;
1981
cv::Point2f *p3 = NULL;
1982
if(iter2.right())
1983
p3 = *iter2;
1984
if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
1985
return false;
1986
float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
1987
if(pt == *p0)
1988
{
1989
++count;
1990
points.push_back(ellipse.getCenter());
1991
}
1992
else if(result != 0)
1993
{
1994
points.push_back(pt);
1995
if(result < 0)
1996
++count;
1997
}
1998
else
1999
{
2000
++count;
2001
if(pt.x != pt.x) // NaN check
2002
points.push_back(ellipse.getCenter());
2003
else
2004
points.push_back(pt);
2005
}
2006
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2007
ellipses.push_back(ellipse);
2008
#endif
2009
}
2010
while(iter.bottom());
2011
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2012
drawEllipses(ellipses);
2013
#endif
2014
if(points.size()-count <= 2)
2015
return false;
2016
if(count > points.size()*0.5 || !checkRowColumn(points))
2017
return false;
2018
addColumnLeft(points);
2019
return true;
2020
}
2021
2022
void Chessboard::Board::growTop()
2023
{
2024
if(isEmpty())
2025
CV_Error(Error::StsInternal,"Board is empty");
2026
PointIter iter(top_left,TOP_LEFT);
2027
std::vector<cv::Point2f> points;
2028
cv::Point2f pt;
2029
do
2030
{
2031
PointIter iter2(iter);
2032
cv::Point2f *p0 = *iter2;
2033
iter2.bottom();
2034
cv::Point2f *p1 = *iter2;
2035
iter2.bottom();
2036
cv::Point2f *p2 = *iter2;
2037
if(iter2.bottom())
2038
estimatePoint(**iter2,*p2,*p1,*p0,pt);
2039
else
2040
estimatePoint(*p2,*p1,*p0,pt);
2041
points.push_back(pt);
2042
}
2043
while(iter.right());
2044
addRowTop(points);
2045
}
2046
2047
bool Chessboard::Board::growTop(const cv::Mat &map,cv::flann::Index &flann_index)
2048
{
2049
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2050
std::vector<Ellipse> ellipses;
2051
#endif
2052
if(isEmpty())
2053
CV_Error(Error::StsInternal,"Board is empty");
2054
2055
PointIter iter(top_left,TOP_LEFT);
2056
std::vector<cv::Point2f> points;
2057
int count = 0;
2058
Ellipse ellipse;
2059
cv::Point2f pt;
2060
do
2061
{
2062
PointIter iter2(iter);
2063
cv::Point2f *p0 = *iter2;
2064
iter2.bottom();
2065
cv::Point2f *p1 = *iter2;
2066
iter2.bottom();
2067
cv::Point2f *p2 = *iter2;
2068
cv::Point2f *p3 = NULL;
2069
if(iter2.bottom())
2070
p3 = *iter2;
2071
if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
2072
return false;
2073
float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
2074
if(pt == *p0)
2075
{
2076
++count;
2077
points.push_back(ellipse.getCenter());
2078
}
2079
else if(result != 0)
2080
{
2081
points.push_back(pt);
2082
if(result < 0)
2083
++count;
2084
}
2085
else
2086
{
2087
++count;
2088
if(pt.x != pt.x) // NaN check
2089
points.push_back(ellipse.getCenter());
2090
else
2091
points.push_back(pt);
2092
}
2093
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2094
ellipses.push_back(ellipse);
2095
#endif
2096
}
2097
while(iter.right());
2098
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2099
drawEllipses(ellipses);
2100
#endif
2101
if(count > points.size()*0.5 || !checkRowColumn(points))
2102
return false;
2103
addRowTop(points);
2104
return true;
2105
}
2106
2107
void Chessboard::Board::growRight()
2108
{
2109
if(isEmpty())
2110
CV_Error(Error::StsInternal,"Board is empty");
2111
PointIter iter(top_left,TOP_RIGHT);
2112
while(iter.right());
2113
std::vector<cv::Point2f> points;
2114
cv::Point2f pt;
2115
do
2116
{
2117
PointIter iter2(iter);
2118
cv::Point2f *p0 = *iter2;
2119
iter2.left();
2120
cv::Point2f *p1 = *iter2;
2121
iter2.left();
2122
cv::Point2f *p2 = *iter2;
2123
if(iter2.left())
2124
estimatePoint(**iter2,*p2,*p1,*p0,pt);
2125
else
2126
estimatePoint(*p2,*p1,*p0,pt);
2127
points.push_back(pt);
2128
}
2129
while(iter.bottom());
2130
addColumnRight(points);
2131
}
2132
2133
bool Chessboard::Board::growRight(const cv::Mat &map,cv::flann::Index &flann_index)
2134
{
2135
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2136
std::vector<Ellipse> ellipses;
2137
#endif
2138
if(isEmpty())
2139
CV_Error(Error::StsInternal,"Board is empty");
2140
2141
PointIter iter(top_left,TOP_RIGHT);
2142
while(iter.right());
2143
std::vector<cv::Point2f> points;
2144
cv::Point2f pt;
2145
Ellipse ellipse;
2146
int count = 0;
2147
do
2148
{
2149
PointIter iter2(iter);
2150
cv::Point2f *p0 = *iter2;
2151
iter2.left();
2152
cv::Point2f *p1 = *iter2;
2153
iter2.left();
2154
cv::Point2f *p2 = *iter2;
2155
cv::Point2f *p3 = NULL;
2156
if(iter2.left())
2157
p3 = *iter2;
2158
if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
2159
return false;
2160
float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
2161
if(pt == *p0)
2162
{
2163
++count;
2164
points.push_back(ellipse.getCenter());
2165
}
2166
else if(result != 0)
2167
{
2168
points.push_back(pt);
2169
if(result < 0)
2170
++count;
2171
}
2172
else
2173
{
2174
++count;
2175
if(pt.x != pt.x) // NaN check
2176
points.push_back(ellipse.getCenter());
2177
else
2178
points.push_back(pt);
2179
}
2180
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2181
ellipses.push_back(ellipse);
2182
#endif
2183
}
2184
while(iter.bottom());
2185
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2186
drawEllipses(ellipses);
2187
#endif
2188
if(count > points.size()*0.5 || !checkRowColumn(points))
2189
return false;
2190
addColumnRight(points);
2191
return true;
2192
}
2193
2194
void Chessboard::Board::growBottom()
2195
{
2196
if(isEmpty())
2197
CV_Error(Error::StsInternal,"Board is empty");
2198
2199
PointIter iter(top_left,BOTTOM_LEFT);
2200
while(iter.bottom());
2201
std::vector<cv::Point2f> points;
2202
cv::Point2f pt;
2203
do
2204
{
2205
PointIter iter2(iter);
2206
cv::Point2f *p0 = *iter2;
2207
iter2.top();
2208
cv::Point2f *p1 = *iter2;
2209
iter2.top();
2210
cv::Point2f *p2 = *iter2;
2211
if(iter2.top())
2212
estimatePoint(**iter2,*p2,*p1,*p0,pt);
2213
else
2214
estimatePoint(*p2,*p1,*p0,pt);
2215
points.push_back(pt);
2216
}
2217
while(iter.right());
2218
addRowBottom(points);
2219
}
2220
2221
bool Chessboard::Board::growBottom(const cv::Mat &map,cv::flann::Index &flann_index)
2222
{
2223
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2224
std::vector<Ellipse> ellipses;
2225
#endif
2226
if(isEmpty())
2227
CV_Error(Error::StsInternal,"Board is empty");
2228
2229
PointIter iter(top_left,BOTTOM_LEFT);
2230
while(iter.bottom());
2231
std::vector<cv::Point2f> points;
2232
cv::Point2f pt;
2233
Ellipse ellipse;
2234
int count = 0;
2235
do
2236
{
2237
PointIter iter2(iter);
2238
cv::Point2f *p0 = *iter2;
2239
iter2.top();
2240
cv::Point2f *p1 = *iter2;
2241
iter2.top();
2242
cv::Point2f *p2 = *iter2;
2243
cv::Point2f *p3 = NULL;
2244
if(iter2.top())
2245
p3 = *iter2;
2246
if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
2247
return false;
2248
float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
2249
if(pt == *p0)
2250
{
2251
++count;
2252
points.push_back(ellipse.getCenter());
2253
}
2254
else if(result != 0)
2255
{
2256
points.push_back(pt);
2257
if(result < 0)
2258
++count;
2259
}
2260
else
2261
{
2262
++count;
2263
if(pt.x != pt.x) // NaN check
2264
points.push_back(ellipse.getCenter());
2265
else
2266
points.push_back(pt);
2267
}
2268
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2269
ellipses.push_back(ellipse);
2270
#endif
2271
}
2272
while(iter.right());
2273
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2274
drawEllipses(ellipses);
2275
#endif
2276
if(count > points.size()*0.5 || !checkRowColumn(points))
2277
return false;
2278
addRowBottom(points);
2279
return true;
2280
}
2281
2282
void Chessboard::Board::addColumnLeft(const std::vector<cv::Point2f> &points)
2283
{
2284
if(points.empty() || points.size() != rowCount())
2285
CV_Error(Error::StsBadArg,"wrong number of points");
2286
2287
int offset = int(cells.size());
2288
cells.resize(offset+points.size()-1);
2289
for(int i = offset;i < (int) cells.size();++i)
2290
cells[i] = new Cell();
2291
corners.push_back(new cv::Point2f(points.front()));
2292
2293
Cell *cell = top_left;
2294
std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
2295
for(int pos=offset;iter != points.end();++iter,cell = cell->bottom,++pos)
2296
{
2297
cell->left = cells[pos];
2298
cells[pos]->black = !cell->black;
2299
if(pos != offset)
2300
cells[pos]->top = cells[pos-1];
2301
cells[pos]->right = cell;
2302
if(pos +1 < (int)cells.size())
2303
cells[pos]->bottom= cells[pos+1];
2304
cells[pos]->top_left = corners.back();
2305
corners.push_back(new cv::Point2f(*iter));
2306
cells[pos]->bottom_left = corners.back();
2307
cells[pos]->top_right=cell->top_left;
2308
cells[pos]->bottom_right=cell->bottom_left;
2309
}
2310
top_left = cells[offset];
2311
++cols;
2312
}
2313
2314
void Chessboard::Board::addRowTop(const std::vector<cv::Point2f> &points)
2315
{
2316
if(points.empty() || points.size() != colCount())
2317
CV_Error(Error::StsBadArg,"wrong number of points");
2318
2319
int offset = int(cells.size());
2320
cells.resize(offset+points.size()-1);
2321
for(int i = offset;i < (int) cells.size();++i)
2322
cells[i] = new Cell();
2323
corners.push_back(new cv::Point2f(points.front()));
2324
2325
Cell *cell = top_left;
2326
std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
2327
for(int pos=offset;iter != points.end();++iter,cell = cell->right,++pos)
2328
{
2329
cell->top = cells[pos];
2330
cells[pos]->black = !cell->black;
2331
if(pos != offset)
2332
cells[pos]->left= cells[pos-1];
2333
cells[pos]->bottom= cell;
2334
if(pos +1 <(int) cells.size())
2335
cells[pos]->right= cells[pos+1];
2336
2337
cells[pos]->top_left = corners.back();
2338
corners.push_back(new cv::Point2f(*iter));
2339
cells[pos]->top_right = corners.back();
2340
cells[pos]->bottom_left = cell->top_left;
2341
cells[pos]->bottom_right = cell->top_right;
2342
}
2343
top_left = cells[offset];
2344
++rows;
2345
}
2346
2347
void Chessboard::Board::addColumnRight(const std::vector<cv::Point2f> &points)
2348
{
2349
if(points.empty() || points.size() != rowCount())
2350
CV_Error(Error::StsBadArg,"wrong number of points");
2351
2352
int offset = int(cells.size());
2353
cells.resize(offset+points.size()-1);
2354
for(int i = offset;i < (int) cells.size();++i)
2355
cells[i] = new Cell();
2356
corners.push_back(new cv::Point2f(points.front()));
2357
2358
Cell *cell = top_left;
2359
for(;cell->right;cell = cell->right);
2360
std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
2361
for(int pos=offset;iter != points.end();++iter,cell = cell->bottom,++pos)
2362
{
2363
cell->right = cells[pos];
2364
cells[pos]->black = !cell->black;
2365
if(pos != offset)
2366
cells[pos]->top= cells[pos-1];
2367
cells[pos]->left = cell;
2368
if(pos +1 <(int) cells.size())
2369
cells[pos]->bottom= cells[pos+1];
2370
2371
cells[pos]->top_right = corners.back();
2372
corners.push_back(new cv::Point2f(*iter));
2373
cells[pos]->bottom_right = corners.back();
2374
cells[pos]->top_left =cell->top_right;
2375
cells[pos]->bottom_left =cell->bottom_right;
2376
}
2377
++cols;
2378
}
2379
2380
void Chessboard::Board::addRowBottom(const std::vector<cv::Point2f> &points)
2381
{
2382
if(points.empty() || points.size() != colCount())
2383
CV_Error(Error::StsBadArg,"wrong number of points");
2384
2385
int offset = int(cells.size());
2386
cells.resize(offset+points.size()-1);
2387
for(int i = offset;i < (int) cells.size();++i)
2388
cells[i] = new Cell();
2389
corners.push_back(new cv::Point2f(points.front()));
2390
2391
Cell *cell = top_left;
2392
for(;cell->bottom;cell = cell->bottom);
2393
std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
2394
for(int pos=offset;iter != points.end();++iter,cell = cell->right,++pos)
2395
{
2396
cell->bottom = cells[pos];
2397
cells[pos]->black = !cell->black;
2398
if(pos != offset)
2399
cells[pos]->left = cells[pos-1];
2400
cells[pos]->top = cell;
2401
if(pos +1 < (int)cells.size())
2402
cells[pos]->right= cells[pos+1];
2403
2404
cells[pos]->bottom_left = corners.back();
2405
corners.push_back(new cv::Point2f(*iter));
2406
cells[pos]->bottom_right = corners.back();
2407
cells[pos]->top_left = cell->bottom_left;
2408
cells[pos]->top_right = cell->bottom_right;
2409
}
2410
++rows;
2411
}
2412
2413
bool Chessboard::Board::checkUnique()const
2414
{
2415
std::vector<cv::Point2f> points = getCorners(false);
2416
std::vector<cv::Point2f>::const_iterator iter = points.begin();
2417
for(;iter != points.end();++iter)
2418
{
2419
std::vector<cv::Point2f>::const_iterator iter2 = iter+1;
2420
for(;iter2 != points.end();++iter2)
2421
{
2422
if(*iter == *iter2)
2423
return false;
2424
}
2425
}
2426
return true;
2427
}
2428
2429
int Chessboard::Board::validateCorners(const cv::Mat &data,cv::flann::Index &flann_index,const cv::Mat &h,float min_response)
2430
{
2431
// TODO check input
2432
if(isEmpty() || h.empty())
2433
return 0;
2434
int count = 0; int icol = 0;
2435
// first row
2436
PointIter iter(top_left,TOP_LEFT);
2437
cv::Point2f point;
2438
do
2439
{
2440
if((*iter)->x == (*iter)->x)
2441
++count;
2442
else
2443
{
2444
Ellipse ellipse = estimateSearchArea(h,0,icol,0.4F);
2445
float result = findMaxPoint(flann_index,data,ellipse,white_angle,black_angle,point);
2446
if(fabs(result) >= min_response)
2447
{
2448
++count;
2449
**iter = point;
2450
}
2451
}
2452
++icol;
2453
}while(iter.right());
2454
2455
// all other rows
2456
int irow = 1;
2457
Cell *row = top_left;
2458
do
2459
{
2460
PointIter iter2(row,BOTTOM_LEFT);
2461
icol = 0;
2462
do
2463
{
2464
if((*iter2)->x == (*iter2)->x)
2465
++count;
2466
else
2467
{
2468
Ellipse ellipse = estimateSearchArea(h,irow,icol,0.4F);
2469
if(min_response <= findMaxPoint(flann_index,data,ellipse,white_angle,black_angle,point))
2470
{
2471
++count;
2472
**iter2 = point;
2473
}
2474
}
2475
++icol;
2476
}while(iter2.right());
2477
row = row->bottom;
2478
++irow;
2479
}while(row);
2480
2481
// check that there are no points with the same coordinate
2482
std::vector<cv::Point2f> points = getCorners(false);
2483
std::vector<cv::Point2f>::const_iterator iter1 = points.begin();
2484
for(;iter1 != points.end();++iter1)
2485
{
2486
// we do not have to check for NaN because of getCorners(flase)
2487
std::vector<cv::Point2f>::const_iterator iter2 = iter1+1;
2488
for(;iter2 != points.end();++iter2)
2489
if(*iter1 == *iter2)
2490
return -1; // one corner is there twice -> not valid configuration
2491
}
2492
return count;
2493
}
2494
2495
bool Chessboard::Board::validateContour()const
2496
{
2497
std::vector<cv::Point2f> contour = getContour();
2498
if(contour.size() != 4)
2499
{
2500
return false;
2501
}
2502
cv::Point2f n1 = contour[1]-contour[0];
2503
cv::Point2f n2 = contour[2]-contour[1];
2504
cv::Point2f n3 = contour[3]-contour[2];
2505
cv::Point2f n4 = contour[0]-contour[3];
2506
n1 = n1/cv::norm(n1);
2507
n2 = n2/cv::norm(n2);
2508
n3 = n3/cv::norm(n3);
2509
n4 = n4/cv::norm(n4);
2510
// a > b => cos(a) < cos(b)
2511
if(fabs(n1.dot(n2)) > MIN_COS_ANGLE||
2512
fabs(n2.dot(n3)) > MIN_COS_ANGLE||
2513
fabs(n3.dot(n4)) > MIN_COS_ANGLE||
2514
fabs(n4.dot(n1)) > MIN_COS_ANGLE)
2515
return false;
2516
return true;
2517
}
2518
2519
std::vector<cv::Point2f> Chessboard::Board::getContour()const
2520
{
2521
std::vector<cv::Point2f> points;
2522
if(isEmpty())
2523
return points;
2524
2525
//find start cell part of the contour
2526
Cell* start_cell = NULL;
2527
PointIter iter(top_left,TOP_LEFT);
2528
do
2529
{
2530
PointIter iter2(iter);
2531
do
2532
{
2533
if(!iter2.getCell()->empty())
2534
{
2535
start_cell = iter2.getCell();
2536
iter = iter2;
2537
break;
2538
}
2539
}while(iter2.right());
2540
}while(!start_cell && iter.bottom());
2541
if(start_cell == NULL)
2542
return points;
2543
2544
// trace contour
2545
const cv::Point2f *start_pt = *iter;
2546
int mode = 2; int last = -1;
2547
do
2548
{
2549
PointIter current_iter(iter);
2550
switch(mode)
2551
{
2552
case 1: // top
2553
if(iter.top(true))
2554
{
2555
if(last != 1)
2556
points.push_back(**current_iter);
2557
mode = 4;
2558
last = 1;
2559
break;
2560
}
2561
/* fallthrough */
2562
case 2: // right
2563
if(iter.right(true))
2564
{
2565
if(last != 2)
2566
points.push_back(**current_iter);
2567
mode = 1;
2568
last = 2;
2569
break;
2570
}
2571
/* fallthrough */
2572
case 3: // bottom
2573
if(iter.bottom(true))
2574
{
2575
if(last != 3)
2576
points.push_back(**current_iter);
2577
mode = 2;
2578
last = 3;
2579
break;
2580
}
2581
/* fallthrough */
2582
case 4: // left
2583
if(iter.left(true))
2584
{
2585
if(last != 4)
2586
points.push_back(**current_iter);
2587
mode = 3;
2588
last = 4;
2589
break;
2590
}
2591
mode = 1;
2592
break;
2593
default:
2594
CV_Error(Error::StsInternal,"cannot retrieve contour");
2595
}
2596
}while(*iter != start_pt);
2597
return points;
2598
}
2599
2600
2601
cv::Mat Chessboard::Board::estimateHomography(cv::Rect rect,int field_size)const
2602
{
2603
int _rows = int(rowCount());
2604
int _cols = int(colCount());
2605
if(_rows < 3 || _cols < 3)
2606
return cv::Mat();
2607
if(rect.width <= 0)
2608
rect.width= _cols;
2609
if(rect.height <= 0)
2610
rect.height= _rows;
2611
2612
int col_end = std::min(rect.x+rect.width,_cols);
2613
int row_end = std::min(rect.y+rect.height,_rows);
2614
std::vector<cv::Point2f> points = getCorners(true);
2615
2616
// build src and dst
2617
std::vector<cv::Point2f> src,dst;
2618
for(int row =rect.y;row < row_end;++row)
2619
{
2620
for(int col=rect.x;col <col_end;++col)
2621
{
2622
const cv::Point2f &pt = points[row*_rows+col];
2623
if(pt.x != pt.x) // NaN check
2624
continue;
2625
src.push_back(cv::Point2f(float(field_size)*(col+1),float(field_size)*(row+1)));
2626
dst.push_back(pt);
2627
}
2628
}
2629
if(dst.size() < 4)
2630
return cv::Mat();
2631
return cv::findHomography(src, dst,cv::LMEDS);
2632
}
2633
2634
cv::Mat Chessboard::Board::estimateHomography(int field_size)const
2635
{
2636
int _rows = int(rowCount());
2637
int _cols = int(colCount());
2638
if(_rows < 3 || _cols < 3)
2639
return cv::Mat();
2640
std::vector<cv::Point2f> src,dst;
2641
std::vector<cv::Point2f> points = getCorners(true);
2642
std::vector<cv::Point2f>::const_iterator iter = points.begin();
2643
for(int row =0;row < _rows;++row)
2644
{
2645
for(int col=0;col <_cols;++col,++iter)
2646
{
2647
const cv::Point2f &pt = *iter;
2648
if(pt.x == pt.x)
2649
{
2650
src.push_back(cv::Point2f(float(field_size)*(col+1),float(field_size)*(row+1)));
2651
dst.push_back(pt);
2652
}
2653
}
2654
}
2655
if(dst.size() < 4)
2656
return cv::Mat();
2657
return cv::findHomography(src, dst);
2658
}
2659
2660
bool Chessboard::Board::findNextPoint(cv::flann::Index &index,const cv::Mat &data,
2661
const cv::Point2f &pt1,const cv::Point2f &pt2, const cv::Point2f &pt3,
2662
float white_angle,float black_angle,float min_response,cv::Point2f &point)
2663
{
2664
Ellipse ellipse;
2665
if(!estimateSearchArea(pt1,pt2,pt3,0.4F,ellipse))
2666
return false;
2667
if(min_response > fabs(findMaxPoint(index,data,ellipse,white_angle,black_angle,point)))
2668
return false;
2669
return true;
2670
}
2671
2672
int Chessboard::Board::grow(const cv::Mat &map,cv::flann::Index &flann_index)
2673
{
2674
if(isEmpty())
2675
CV_Error(Error::StsInternal,"Board is empty");
2676
bool bleft = true;
2677
bool btop = true;
2678
bool bright = true;
2679
bool bbottom= true;
2680
int count = 0;
2681
do
2682
{
2683
// grow to the left
2684
if(bleft)
2685
{
2686
bleft = growLeft(map,flann_index);
2687
if(bleft)
2688
++count;
2689
}
2690
if(btop)
2691
{
2692
btop= growTop(map,flann_index);
2693
if(btop)
2694
++count;
2695
}
2696
if(bright)
2697
{
2698
bright= growRight(map,flann_index);
2699
if(bright)
2700
++count;
2701
}
2702
if(bbottom)
2703
{
2704
bbottom= growBottom(map,flann_index);
2705
if(bbottom)
2706
++count;
2707
}
2708
}while(bleft || btop || bright || bbottom );
2709
return count;
2710
}
2711
2712
std::map<int,int> Chessboard::Board::getMapping()const
2713
{
2714
std::map<int,int> map;
2715
std::vector<cv::Point2f> points = getCorners();
2716
std::vector<cv::Point2f>::iterator iter = points.begin();
2717
for(int idx1=0,idx2=0;iter != points.end();++iter,++idx1)
2718
{
2719
if(iter->x != iter->x) // NaN check
2720
continue;
2721
map[idx1] = idx2++;
2722
}
2723
return map;
2724
}
2725
2726
std::vector<cv::Point2f> Chessboard::Board::getCorners(bool ball)const
2727
{
2728
std::vector<cv::Point2f> points;
2729
if(isEmpty())
2730
return points;
2731
2732
// first row
2733
PointIter iter(top_left,TOP_LEFT);
2734
do
2735
{
2736
if(ball || !iter.isNaN())
2737
points.push_back(*(*iter));
2738
}while(iter.right());
2739
2740
// all other rows
2741
Cell *row = top_left;
2742
do
2743
{
2744
PointIter iter2(row,BOTTOM_LEFT);
2745
do
2746
{
2747
if(ball || !iter2.isNaN())
2748
points.push_back(*(*iter2));
2749
}while(iter2.right());
2750
row = row->bottom;
2751
}while(row);
2752
return points;
2753
}
2754
2755
std::vector<cv::KeyPoint> Chessboard::Board::getKeyPoints(bool ball)const
2756
{
2757
std::vector<cv::KeyPoint> keypoints;
2758
std::vector<cv::Point2f> points = getCorners(ball);
2759
std::vector<cv::Point2f>::const_iterator iter = points.begin();
2760
for(;iter != points.end();++iter)
2761
keypoints.push_back(cv::KeyPoint(iter->x,iter->y,1));
2762
return keypoints;
2763
}
2764
2765
Chessboard::Chessboard(const Parameters &para)
2766
{
2767
reconfigure(para);
2768
}
2769
2770
void Chessboard::reconfigure(const Parameters &config)
2771
{
2772
parameters = config;
2773
}
2774
2775
Chessboard::Parameters Chessboard::getPara()const
2776
{
2777
return parameters;
2778
}
2779
2780
Chessboard::~Chessboard()
2781
{
2782
}
2783
2784
void Chessboard::findKeyPoints(const cv::Mat& image, std::vector<KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,
2785
std::vector<std::vector<float> > &angles ,const cv::Mat& mask)const
2786
{
2787
keypoints.clear();
2788
angles.clear();
2789
vector<KeyPoint> keypoints_temp;
2790
FastX::Parameters para;
2791
2792
para.branches = 2; // this is always the case for checssboard corners
2793
para.strength = 10; // minimal threshold
2794
para.resolution = float(CV_PI*0.25); // this gives the best results taking interpolation into account
2795
para.filter = 1;
2796
para.super_resolution = parameters.super_resolution;
2797
para.min_scale = parameters.min_scale;
2798
para.max_scale = parameters.max_scale;
2799
2800
FastX detector(para);
2801
std::vector<cv::Mat> rotated_images;
2802
detector.detectImpl(image,rotated_images,feature_maps,mask);
2803
2804
//calculate seed chessboard corners
2805
detector.findKeyPoints(feature_maps,keypoints_temp,mask);
2806
2807
//sort points and limit number
2808
int max_seeds = std::min((int)keypoints_temp.size(),parameters.max_points);
2809
if(max_seeds < 9)
2810
return;
2811
2812
std::partial_sort(keypoints_temp.begin(),keypoints_temp.begin()+max_seeds-1,
2813
keypoints_temp.end(),sortKeyPoint);
2814
keypoints_temp.resize(max_seeds);
2815
std::vector<std::vector<float> > angles_temp = detector.calcAngles(rotated_images,keypoints_temp);
2816
2817
// filter out keypoints which are not symmetric
2818
std::vector<KeyPoint>::iterator iter1 = keypoints_temp.begin();
2819
std::vector<std::vector<float> >::const_iterator iter2 = angles_temp.begin();
2820
for(;iter1 != keypoints_temp.end();++iter1,++iter2)
2821
{
2822
cv::KeyPoint &pt = *iter1;
2823
const std::vector<float> &angles_i3 = *iter2;
2824
if(angles_i3.size() != 2)// || pt.response < noise)
2825
continue;
2826
int result = testPointSymmetry(image,pt.pt,pt.size*0.7F,std::max(10.0F,sqrt(pt.response)+0.5F*pt.size));
2827
if(result > MAX_SYMMETRY_ERRORS)
2828
continue;
2829
else if(result > 3)
2830
pt.response = - pt.response;
2831
angles.push_back(angles_i3);
2832
keypoints.push_back(pt);
2833
}
2834
}
2835
2836
cv::Mat Chessboard::buildData(const std::vector<KeyPoint>& keypoints)const
2837
{
2838
cv::Mat data(int(keypoints.size()),4,CV_32FC1); // x + y + angle + strength
2839
std::vector<cv::KeyPoint>::const_iterator iter = keypoints.begin();
2840
float *val = reinterpret_cast<float*>(data.data);
2841
for(;iter != keypoints.end();++iter)
2842
{
2843
(*val++) = iter->pt.x;
2844
(*val++) = iter->pt.y;
2845
(*val++) = float(2.0*CV_PI-iter->angle/180.0*CV_PI);
2846
(*val++) = iter->response;
2847
}
2848
return data;
2849
}
2850
2851
std::vector<cv::KeyPoint> Chessboard::getInitialPoints(cv::flann::Index &flann_index,const cv::Mat &data,const cv::KeyPoint &center,float white_angle,float black_angle,float min_response)const
2852
{
2853
CV_CheckTypeEQ(data.type(), CV_32FC1, "Unsupported source type");
2854
if(data.cols != 4)
2855
CV_Error(Error::StsBadArg,"wrong data format");
2856
2857
std::vector<float> query,dists;
2858
std::vector<int> indices;
2859
query.resize(2); query[0] = center.pt.x; query[1] = center.pt.y;
2860
flann_index.knnSearch(query,indices,dists,21,cv::flann::SearchParams(32));
2861
2862
// collect all points having a similar angle and response
2863
std::vector<cv::KeyPoint> points;
2864
std::vector<int>::const_iterator ids_iter = indices.begin()+1; // first point is center
2865
points.push_back(center);
2866
for(;ids_iter != indices.end();++ids_iter)
2867
{
2868
// TODO do more angle tests
2869
// test only one angle against the stored one
2870
const float &response = data.at<float>(*ids_iter,3);
2871
if(fabs(response) < min_response)
2872
continue;
2873
const float &angle = data.at<float>(*ids_iter,2);
2874
float angle_temp = fabs(angle-white_angle);
2875
if(angle_temp > CV_PI*0.5)
2876
angle_temp = float(fabs(angle_temp-CV_PI));
2877
if(angle_temp > MAX_ANGLE)
2878
{
2879
angle_temp = fabs(angle-black_angle);
2880
if(angle_temp > CV_PI*0.5)
2881
angle_temp = float(fabs(angle_temp-CV_PI));
2882
if(angle_temp >MAX_ANGLE)
2883
continue;
2884
}
2885
points.push_back(cv::KeyPoint(data.at<float>(*ids_iter,0),data.at<float>(*ids_iter,1),center.size,angle,response));
2886
}
2887
return points;
2888
}
2889
2890
Chessboard::BState Chessboard::generateBoards(cv::flann::Index &flann_index,const cv::Mat &data,
2891
const cv::KeyPoint &center,float white_angle,float black_angle,float min_response,const cv::Mat& img,
2892
std::vector<Chessboard::Board> &boards)const
2893
{
2894
// collect all points having a similar angle
2895
std::vector<cv::KeyPoint> kpoints= getInitialPoints(flann_index,data,center,white_angle,black_angle,min_response);
2896
if(kpoints.size() < 5)
2897
return MISSING_POINTS;
2898
2899
if(!img.empty())
2900
{
2901
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
2902
cv::Mat out;
2903
cv::drawKeypoints(img,kpoints,out,cv::Scalar(0,0,255,255),4);
2904
std::vector<cv::KeyPoint> temp;
2905
temp.push_back(kpoints.front());
2906
cv::drawKeypoints(out,temp,out,cv::Scalar(0,255,0,255),4);
2907
cv::imshow("chessboard",out);
2908
cv::waitKey(-1);
2909
#endif
2910
}
2911
2912
// use angles to filter out points
2913
std::vector<cv::KeyPoint> points;
2914
cv::Vec2f n1(cos(white_angle),-sin(white_angle));
2915
cv::Vec2f n2(cos(black_angle),-sin(black_angle));
2916
std::vector<cv::KeyPoint>::const_iterator iter1 = kpoints.begin()+1; // first point is center
2917
for(;iter1 != kpoints.end();++iter1)
2918
{
2919
// calc angle
2920
cv::Vec2f vec(iter1->pt-center.pt);
2921
vec = vec/cv::norm(vec);
2922
if(fabs(vec.dot(n1)) < 0.96 && fabs(vec.dot(n2)) < 0.96) //check that angle is bigger than 15°
2923
points.push_back(*iter1);
2924
}
2925
2926
// genreate pairs those connection goes through the center
2927
std::vector<std::pair<cv::KeyPoint,cv::KeyPoint> > pairs;
2928
iter1 = points.begin();
2929
for(;iter1 != points.end();++iter1)
2930
{
2931
std::vector<cv::KeyPoint>::const_iterator iter2 = iter1+1;
2932
for(;iter2 != points.end();++iter2)
2933
{
2934
if(isPointOnLine(iter1->pt,iter2->pt,center.pt,0.97F))
2935
{
2936
if(cv::norm(iter1->pt) < cv::norm(iter2->pt))
2937
pairs.push_back(std::make_pair(*iter1,*iter2));
2938
else
2939
pairs.push_back(std::make_pair(*iter2,*iter1));
2940
}
2941
}
2942
}
2943
2944
// generate all possible combinations consisting of two pairs
2945
if(pairs.size() < 2)
2946
return MISSING_PAIRS;
2947
std::vector<std::pair<cv::KeyPoint,cv::KeyPoint> >::iterator iter_pair1 = pairs.begin();
2948
2949
BState best_state = MISSING_PAIRS;
2950
for(;iter_pair1 != pairs.end();++iter_pair1)
2951
{
2952
cv::Point2f p1 = iter_pair1->second.pt-iter_pair1->first.pt;
2953
p1 = p1/cv::norm(p1);
2954
std::vector<std::pair<cv::KeyPoint,cv::KeyPoint> >::iterator iter_pair2 = iter_pair1+1;
2955
for(;iter_pair2 != pairs.end();++iter_pair2)
2956
{
2957
cv::Point2f p2 = iter_pair2->second.pt-iter_pair2->first.pt;
2958
p2 = p2/cv::norm(p2);
2959
if(p2.dot(p1) > 0.95)
2960
{
2961
if(best_state < WRONG_PAIR_ANGLE)
2962
best_state = WRONG_PAIR_ANGLE;
2963
}
2964
else
2965
{
2966
// check orientations
2967
if(checkOrientation(iter_pair1->first.pt,iter_pair1->second.pt,iter_pair2->first.pt,iter_pair2->second.pt))
2968
std::swap(iter_pair2->first,iter_pair2->second);
2969
2970
// minimal case
2971
std::vector<cv::Point2f> board_points;
2972
board_points.resize(9,cv::Point2f(std::numeric_limits<float>::quiet_NaN(),
2973
std::numeric_limits<float>::quiet_NaN()));
2974
2975
board_points[1] = iter_pair2->first.pt;
2976
board_points[3] = iter_pair1->first.pt;
2977
board_points[4] = center.pt;
2978
board_points[5] = iter_pair1->second.pt;
2979
board_points[7] = iter_pair2->second.pt;
2980
boards.push_back(Board(cv::Size(3,3),board_points,white_angle,black_angle));
2981
Board &board = boards.back();
2982
2983
if(board.isEmpty())
2984
{
2985
if(best_state < WRONG_CONFIGURATION)
2986
best_state = WRONG_CONFIGURATION;
2987
boards.pop_back(); // MAKE SURE board is no longer used !!!!
2988
continue;
2989
}
2990
best_state = FOUND_BOARD;
2991
}
2992
}
2993
}
2994
return best_state;
2995
}
2996
2997
void Chessboard::detectImpl(const Mat& image, vector<KeyPoint>& keypoints,std::vector<Mat> &feature_maps,const Mat& mask)const
2998
{
2999
keypoints.clear();
3000
Board board = detectImpl(image,feature_maps,mask);
3001
keypoints = board.getKeyPoints();
3002
return;
3003
}
3004
3005
Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &feature_maps,const Mat& mask)const
3006
{
3007
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
3008
debug_image = gray;
3009
#endif
3010
CV_CheckTypeEQ(gray.type(),CV_8UC1, "Unsupported image type");
3011
3012
cv::Size chessboard_size2(parameters.chessboard_size.height,parameters.chessboard_size.width);
3013
std::vector<KeyPoint> keypoints_seed;
3014
std::vector<std::vector<float> > angles;
3015
findKeyPoints(gray,keypoints_seed,feature_maps,angles,mask);
3016
if(keypoints_seed.empty())
3017
return Chessboard::Board();
3018
3019
// check how many points are likely a checkerbord corner
3020
float response = fabs(keypoints_seed.front().response*MIN_RESPONSE_RATIO);
3021
std::vector<KeyPoint>::const_iterator seed_iter = keypoints_seed.begin();
3022
int count = 0;
3023
int inum = chessboard_size2.width*chessboard_size2.height;
3024
for(;seed_iter != keypoints_seed.end() && count < inum;++seed_iter,++count)
3025
{
3026
// points are sorted based on response
3027
if(fabs(seed_iter->response) < response)
3028
{
3029
seed_iter = keypoints_seed.end();
3030
return Chessboard::Board();
3031
}
3032
}
3033
// just add dummy points or flann will fail during knnSearch
3034
if(keypoints_seed.size() < 21)
3035
keypoints_seed.resize(21, cv::KeyPoint(-99999.0F,-99999.0F,0.0F,0.0F,0.0F));
3036
3037
//build kd tree
3038
cv::Mat data = buildData(keypoints_seed);
3039
cv::Mat flann_data(data.rows,2,CV_32FC1);
3040
data(cv::Rect(0,0,2,data.rows)).copyTo(flann_data);
3041
cv::flann::Index flann_index(flann_data,cv::flann::KDTreeIndexParams(1),cvflann::FLANN_DIST_EUCLIDEAN);
3042
3043
// for each point
3044
std::vector<std::vector<float> >::const_iterator angles_iter = angles.begin();
3045
std::vector<cv::KeyPoint>::const_iterator points_iter = keypoints_seed.begin();
3046
cv::Rect bounding_box(5,5,gray.cols-10,gray.rows-10);
3047
int max_tests = std::min(parameters.max_tests,int(keypoints_seed.size()));
3048
for(count=0;count < max_tests;++angles_iter,++points_iter,++count)
3049
{
3050
// regard current point as center point
3051
// which must have two angles!!! (this was already checked)
3052
float min_response = points_iter->response*MIN_RESPONSE_RATIO;
3053
if(min_response <= 0)
3054
{
3055
if(max_tests+1 < int(keypoints_seed.size()))
3056
++max_tests;
3057
continue;
3058
}
3059
const std::vector<float> &angles_i = *angles_iter;
3060
float white_angle = fabs(angles_i.front()); // angle is negative if black --> clockwise
3061
float black_angle = fabs(angles_i.back()); // angle is negative if black --> clockwise
3062
if(angles_i.front() < 0) // ensure white angle is first
3063
swap(white_angle,black_angle);
3064
3065
std::vector<Board> boards;
3066
generateBoards(flann_index, data,*points_iter,white_angle,black_angle,min_response,gray,boards);
3067
parallel_for_(Range(0,(int)boards.size()),[&](const Range& range){
3068
for(int i=range.start;i <range.end;++i)
3069
{
3070
auto iter_boards = boards.begin()+i;
3071
cv::Mat h = iter_boards->estimateHomography();
3072
int size = iter_boards->validateCorners(data,flann_index,h,min_response);
3073
if(size != 9 || !iter_boards->validateContour())
3074
{
3075
iter_boards->clear();
3076
continue;
3077
}
3078
//grow based on kd-tree
3079
iter_boards->grow(data,flann_index);
3080
if(!iter_boards->checkUnique())
3081
{
3082
iter_boards->clear();
3083
continue;
3084
}
3085
3086
// check bounding box
3087
std::vector<cv::Point2f> contour = iter_boards->getContour();
3088
std::vector<cv::Point2f>::const_iterator iter = contour.begin();
3089
for(;iter != contour.end();++iter)
3090
{
3091
if(!bounding_box.contains(*iter))
3092
break;
3093
}
3094
if(iter != contour.end())
3095
{
3096
iter_boards->clear();
3097
continue;
3098
}
3099
3100
if(iter_boards->getSize() == parameters.chessboard_size ||
3101
iter_boards->getSize() == chessboard_size2)
3102
{
3103
iter_boards->normalizeOrientation(false);
3104
if(iter_boards->getSize() != parameters.chessboard_size)
3105
{
3106
if(iter_boards->isCellBlack(0,0) == iter_boards->isCellBlack(0,int(iter_boards->colCount())-1))
3107
iter_boards->rotateLeft();
3108
else
3109
iter_boards->rotateRight();
3110
}
3111
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
3112
cv::Mat img;
3113
iter_boards->draw(debug_image,img);
3114
cv::imshow("chessboard",img);
3115
cv::waitKey(-1);
3116
#endif
3117
}
3118
else
3119
{
3120
if(iter_boards->getSize().width*iter_boards->getSize().height < chessboard_size2.width*chessboard_size2.height)
3121
iter_boards->clear();
3122
else if(!parameters.larger)
3123
iter_boards->clear();
3124
}
3125
}
3126
});
3127
// check if a good board was found
3128
for(const auto &board : boards)
3129
{
3130
if(!board.isEmpty())
3131
return board;
3132
}
3133
}
3134
return Chessboard::Board();
3135
}
3136
3137
void Chessboard::detectAndCompute(cv::InputArray image,cv::InputArray mask,std::vector<cv::KeyPoint>& keypoints,
3138
cv::OutputArray descriptors,bool useProvidedKeyPoints)
3139
{
3140
descriptors.clear();
3141
useProvidedKeyPoints=false;
3142
std::vector<cv::Mat> maps;
3143
detectImpl(image.getMat(),keypoints,maps,mask.getMat());
3144
if(!useProvidedKeyPoints) // suppress compiler warning
3145
return;
3146
return;
3147
}
3148
3149
void Chessboard::detectImpl(const Mat& image, vector<KeyPoint>& keypoints,const Mat& mask)const
3150
{
3151
std::vector<cv::Mat> maps;
3152
detectImpl(image,keypoints,maps,mask);
3153
}
3154
3155
void Chessboard::detectImpl(InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask)const
3156
{
3157
detectImpl(image.getMat(),keypoints,mask.getMat());
3158
}
3159
3160
} // end namespace details
3161
3162
3163
// public API
3164
bool findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size,
3165
cv::OutputArray corners_, int flags)
3166
{
3167
CV_INSTRUMENT_REGION();
3168
int type = image_.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
3169
CV_CheckType(type, depth == CV_8U && (cn == 1 || cn == 3),
3170
"Only 8-bit grayscale or color images are supported");
3171
if(pattern_size.width <= 2 || pattern_size.height <= 2)
3172
{
3173
CV_Error(Error::StsOutOfRange, "Both width and height of the pattern should have bigger than 2");
3174
}
3175
if (!corners_.needed())
3176
CV_Error(Error::StsNullPtr, "Null pointer to corners");
3177
3178
Mat img;
3179
if (image_.channels() != 1)
3180
cvtColor(image_, img, COLOR_BGR2GRAY);
3181
else
3182
img = image_.getMat();
3183
3184
details::Chessboard::Parameters para;
3185
para.chessboard_size = pattern_size;
3186
para.min_scale = 2;
3187
para.max_scale = 4;
3188
para.max_tests = 25;
3189
para.max_points = std::max(100,pattern_size.width*pattern_size.height*2);
3190
para.super_resolution = false;
3191
3192
// setup search based on flags
3193
if(flags & CALIB_CB_NORMALIZE_IMAGE)
3194
{
3195
Mat tmp;
3196
cv::equalizeHist(img, tmp);
3197
swap(img, tmp);
3198
flags ^= CALIB_CB_NORMALIZE_IMAGE;
3199
}
3200
if(flags & CALIB_CB_EXHAUSTIVE)
3201
{
3202
para.max_tests = 100;
3203
para.max_points = std::max(500,pattern_size.width*pattern_size.height*2);
3204
flags ^= CALIB_CB_EXHAUSTIVE;
3205
}
3206
if(flags & CALIB_CB_ACCURACY)
3207
{
3208
para.super_resolution = true;
3209
flags ^= CALIB_CB_ACCURACY;
3210
}
3211
if(flags)
3212
CV_Error(Error::StsOutOfRange, cv::format("Invalid remaing flags %d", (int)flags));
3213
3214
std::vector<cv::KeyPoint> corners;
3215
details::Chessboard board(para);
3216
board.detect(img,corners);
3217
if(corners.empty())
3218
{
3219
corners_.release();
3220
return false;
3221
}
3222
std::vector<cv::Point2f> points;
3223
KeyPoint::convert(corners,points);
3224
Mat(points).copyTo(corners_);
3225
return true;
3226
}
3227
3228
} // namespace cv
3229
3230