aruco/src/aruco/markerdetector.cpp
Go to the documentation of this file.
1 /*****************************
2 Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
3 
4 Redistribution and use in source and binary forms, with or without modification, are
5 permitted provided that the following conditions are met:
6 
7  1. Redistributions of source code must retain the above copyright notice, this list of
8  conditions and the following disclaimer.
9 
10  2. Redistributions in binary form must reproduce the above copyright notice, this list
11  of conditions and the following disclaimer in the documentation and/or other materials
12  provided with the distribution.
13 CV_
14 THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
15 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
17 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 
24 The views and conclusions contained in the software and documentation are those of the
25 authors and should not be interpreted as representing official policies, either expressed
26 or implied, of Rafael Muñoz Salinas.
27 ********************************/
28 #include <aruco/markerdetector.h>
29 #include <opencv2/opencv.hpp>
30 #include <opencv2/highgui/highgui.hpp>
31 #include <opencv2/imgproc/imgproc.hpp>
32 #include <iostream>
33 #include <fstream>
34 #include <aruco/arucofidmarkers.h>
35 #include <valarray>
36 using namespace std;
37 using namespace cv;
38 
39 namespace aruco
40 {
41  /************************************
42  *
43  *
44  *
45  *
46  ************************************/
47  MarkerDetector::MarkerDetector()
48  {
49  _doErosion=false;
50  _enableCylinderWarp=false;
51  _thresMethod=ADPT_THRES;
52  _thresParam1=_thresParam2=7;
53  _cornerMethod=LINES;
54  _markerWarpSize=56;
55  _speed=0;
56  markerIdDetector_ptrfunc=aruco::FiducidalMarkers::detect;
57  pyrdown_level=0; // no image reduction
58  _minSize=0.04;
59  _maxSize=0.5;
60  }
61  /************************************
62  *
63  *
64  *
65  *
66  ************************************/
67 
68  MarkerDetector::~MarkerDetector()
69  {
70 
71  }
72 
73  /************************************
74  *
75  *
76  *
77  *
78  ************************************/
79  void MarkerDetector::setDesiredSpeed ( int val )
80  {
81  if ( val<0 ) val=0;
82  else if ( val>3 ) val=2;
83 
84  _speed=val;
85  switch ( _speed )
86  {
87 
88  case 0:
89  _markerWarpSize=56;
90  _cornerMethod=SUBPIX;
91  _doErosion=true;
92  break;
93 
94  case 1:
95  case 2:
96  _markerWarpSize=28;
97  _cornerMethod=NONE;
98  break;
99  };
100  }
101 
102  /************************************
103  *
104  *
105  *
106  *
107  ************************************/
108  void MarkerDetector::detect ( const cv::Mat &input,std::vector<Marker> &detectedMarkers, CameraParameters camParams ,float markerSizeMeters ,bool setYPerpendicular) throw ( cv::Exception )
109  {
110  detect ( input, detectedMarkers,camParams.CameraMatrix ,camParams.Distorsion, markerSizeMeters ,setYPerpendicular);
111  }
112 
113 
114  /************************************
115  *
116  * Main detection function. Performs all steps
117  *
118  *
119  ************************************/
120  void MarkerDetector::detect ( const cv::Mat &input,vector<Marker> &detectedMarkers,Mat camMatrix ,Mat distCoeff ,float markerSizeMeters ,bool setYPerpendicular) throw ( cv::Exception )
121  {
122 
123 
124  //it must be a 3 channel image
125  if ( input.type() ==CV_8UC3 ) cv::cvtColor ( input,grey,cv::COLOR_BGR2GRAY );
126  else grey=input;
127 
128 
129  // cv::cvtColor(grey,_ssImC ,CV_GRAY2BGR); //DELETE
130 
131  //clear input data
132  detectedMarkers.clear();
133 
134 
135  cv::Mat imgToBeThresHolded=grey;
136  double ThresParam1=_thresParam1,ThresParam2=_thresParam2;
137  //Must the image be downsampled before continue processing?
138  if ( pyrdown_level!=0 )
139  {
140  reduced=grey;
141  for ( int i=0;i<pyrdown_level;i++ )
142  {
143  cv::Mat tmp;
144  cv::pyrDown ( reduced,tmp );
145  reduced=tmp;
146  }
147  int red_den=pow ( 2.0f,pyrdown_level );
148  imgToBeThresHolded=reduced;
149  ThresParam1/=float ( red_den );
150  ThresParam2/=float ( red_den );
151  }
152 
154  thresHold ( _thresMethod,imgToBeThresHolded,thres,ThresParam1,ThresParam2 );
155  //an erosion might be required to detect chessboard like boards
156 
157  if ( _doErosion )
158  {
159  erode ( thres,thres2,cv::Mat() );
160  thres2=thres;
161  // cv::bitwise_xor ( thres,thres2,thres );
162  }
163  //find all rectangles in the thresholdes image
164  vector<MarkerCandidate > MarkerCanditates;
165  detectRectangles ( thres,MarkerCanditates );
166  //if the image has been downsampled, then calcualte the location of the corners in the original image
167  if ( pyrdown_level!=0 )
168  {
169  float red_den=pow ( 2.0f,pyrdown_level );
170  float offInc= ( ( pyrdown_level/2. )-0.5 );
171  for ( unsigned int i=0;i<MarkerCanditates.size();++i ) {
172  for ( int c=0;c<4;c++ )
173  {
174  MarkerCanditates[i][c].x=MarkerCanditates[i][c].x*red_den+offInc;
175  MarkerCanditates[i][c].y=MarkerCanditates[i][c].y*red_den+offInc;
176  }
177  //do the same with the the contour points
178  for ( size_t c=0;c<MarkerCanditates[i].contour.size();++c )
179  {
180  MarkerCanditates[i].contour[c].x=MarkerCanditates[i].contour[c].x*red_den+offInc;
181  MarkerCanditates[i].contour[c].y=MarkerCanditates[i].contour[c].y*red_den+offInc;
182  }
183  }
184  }
185 
187  _candidates.clear();
188  for ( unsigned int i=0;i<MarkerCanditates.size();++i )
189  {
190  //Find proyective homography
191  Mat canonicalMarker;
192  bool resW=false;
193  if (_enableCylinderWarp)
194  resW=warp_cylinder( grey,canonicalMarker,Size ( _markerWarpSize,_markerWarpSize ),MarkerCanditates[i] );
195  else resW=warp ( grey,canonicalMarker,Size ( _markerWarpSize,_markerWarpSize ),MarkerCanditates[i] );
196  if (resW) {
197  int nRotations;
198  int id= ( *markerIdDetector_ptrfunc ) ( canonicalMarker,nRotations );
199  if ( id!=-1 )
200  {
201  if(_cornerMethod==LINES) refineCandidateLines( MarkerCanditates[i] ); // make LINES refinement before lose contour points
202  detectedMarkers.push_back ( MarkerCanditates[i] );
203  detectedMarkers.back().id=id;
204  //sort the points so that they are always in the same order no matter the camera orientation
205  std::rotate ( detectedMarkers.back().begin(),detectedMarkers.back().begin() +4-nRotations,detectedMarkers.back().end() );
206  }
207  else _candidates.push_back ( MarkerCanditates[i] );
208  }
209 
210  }
211 
212 
214  if ( detectedMarkers.size() >0 && _cornerMethod!=NONE && _cornerMethod!=LINES )
215  {
216  vector<Point2f> Corners;
217  for ( unsigned int i=0;i<detectedMarkers.size();++i )
218  for ( int c=0;c<4;c++ )
219  Corners.push_back ( detectedMarkers[i][c] );
220 
221  if ( _cornerMethod==HARRIS )
222  findBestCornerInRegion_harris ( grey, Corners,7 );
223  else if ( _cornerMethod==SUBPIX )
224  cornerSubPix ( grey, Corners,cv::Size ( 5,5 ), cv::Size ( -1,-1 ) ,cv::TermCriteria ( cv::TermCriteria::EPS | cv::TermCriteria::COUNT,3,0.05 ) );
225 
226  //copy back
227  for ( unsigned int i=0;i<detectedMarkers.size();++i )
228  for ( int c=0;c<4;c++ ) detectedMarkers[i][c]=Corners[i*4+c];
229  }
230  //sort by id
231  std::sort ( detectedMarkers.begin(),detectedMarkers.end() );
232  //there might be still the case that a marker is detected twice because of the double border indicated earlier,
233  //detect and remove these cases
234  vector<bool> toRemove ( detectedMarkers.size(),false );
235  for ( int i=0;i<int ( detectedMarkers.size() )-1;++i )
236  {
237  if ( detectedMarkers[i].id==detectedMarkers[i+1].id && !toRemove[i+1] )
238  {
239  //deletes the one with smaller perimeter
240  if ( perimeter ( detectedMarkers[i] ) >perimeter ( detectedMarkers[i+1] ) ) toRemove[i+1]=true;
241  else toRemove[i]=true;
242  }
243  }
244  //remove the markers marker
245  removeElements ( detectedMarkers, toRemove );
246 
248  if ( camMatrix.rows!=0 && markerSizeMeters>0 )
249  {
250  for ( unsigned int i=0;i<detectedMarkers.size();i++ )
251  detectedMarkers[i].calculateExtrinsics ( markerSizeMeters,camMatrix,distCoeff,setYPerpendicular );
252  }
253  }
254 
255 
256  /************************************
257  *
258  * Crucial step. Detects the rectangular regions of the thresholded image
259  *
260  *
261  ************************************/
262  void MarkerDetector::detectRectangles ( const cv::Mat &thresold,vector<std::vector<cv::Point2f> > &MarkerCanditates )
263  {
264  vector<MarkerCandidate> candidates;
265  detectRectangles(thresold,candidates);
266  //create the output
267  MarkerCanditates.resize(candidates.size());
268  for (size_t i=0;i<MarkerCanditates.size();i++)
269  MarkerCanditates[i]=candidates[i];
270  }
271 
272  void MarkerDetector::detectRectangles(const cv::Mat &thresImg,vector<MarkerCandidate> & OutMarkerCanditates)
273  {
274  vector<MarkerCandidate> MarkerCanditates;
275  //calcualte the min_max contour sizes
276  unsigned int minSize=_minSize*std::max(thresImg.cols,thresImg.rows)*4;
277  unsigned int maxSize=_maxSize*std::max(thresImg.cols,thresImg.rows)*4;
278  std::vector<std::vector<cv::Point> > contours2;
279  std::vector<cv::Vec4i> hierarchy2;
280 
281  thresImg.copyTo ( thres2 );
282  cv::findContours ( thres2 , contours2, hierarchy2, cv::RETR_TREE, cv::CHAIN_APPROX_NONE );
283  vector<Point> approxCurve;
285 
286  for ( unsigned int i=0;i<contours2.size();i++ )
287  {
288 
289 
290  //check it is a possible element by first checking is has enough points
291  if ( minSize< contours2[i].size() &&contours2[i].size()<maxSize )
292  {
293  //approximate to a poligon
294  approxPolyDP ( contours2[i] ,approxCurve , double ( contours2[i].size() ) *0.05 , true );
295  // drawApproxCurve(copy,approxCurve,Scalar(0,0,255));
296  //check that the poligon has 4 points
297  if ( approxCurve.size() ==4 )
298  {
299 
300  // drawContour(input,contours2[i],Scalar(255,0,225));
301  // namedWindow("input");
302  // imshow("input",input);
303  // waitKey(0);
304  //and is convex
305  if ( isContourConvex ( Mat ( approxCurve ) ) )
306  {
307  // drawApproxCurve(input,approxCurve,Scalar(255,0,255));
308  // //ensure that the distace between consecutive points is large enough
309  float minDist=1e10;
310  for ( int j=0;j<4;j++ )
311  {
312  float d= std::sqrt ( ( float ) ( approxCurve[j].x-approxCurve[ ( j+1 ) %4].x ) * ( approxCurve[j].x-approxCurve[ ( j+1 ) %4].x ) +
313  ( approxCurve[j].y-approxCurve[ ( j+1 ) %4].y ) * ( approxCurve[j].y-approxCurve[ ( j+1 ) %4].y ) );
314  // norm(Mat(approxCurve[i]),Mat(approxCurve[(i+1)%4]));
315  if ( d<minDist ) minDist=d;
316  }
317  //check that distance is not very small
318  if ( minDist>10 )
319  {
320  //add the points
321  // cout<<"ADDED"<<endl;
322  MarkerCanditates.push_back ( MarkerCandidate() );
323  MarkerCanditates.back().idx=i;
324  for ( int j=0;j<4;j++ )
325  {
326  MarkerCanditates.back().push_back ( Point2f ( approxCurve[j].x,approxCurve[j].y ) );
327  }
328  }
329  }
330  }
331  }
332  }
333 
334  // namedWindow("input");
335  // imshow("input",input);
336  // waitKey(0);
338  valarray<bool> swapped(false,MarkerCanditates.size());//used later
339  for ( unsigned int i=0;i<MarkerCanditates.size();i++ )
340  {
341 
342  //trace a line between the first and second point.
343  //if the thrid point is at the right side, then the points are anti-clockwise
344  double dx1 = MarkerCanditates[i][1].x - MarkerCanditates[i][0].x;
345  double dy1 = MarkerCanditates[i][1].y - MarkerCanditates[i][0].y;
346  double dx2 = MarkerCanditates[i][2].x - MarkerCanditates[i][0].x;
347  double dy2 = MarkerCanditates[i][2].y - MarkerCanditates[i][0].y;
348  double o = ( dx1*dy2 )- ( dy1*dx2 );
349 
350  if ( o < 0.0 ) //if the third point is in the left side, then sort in anti-clockwise order
351  {
352  swap ( MarkerCanditates[i][1],MarkerCanditates[i][3] );
353  swapped[i]=true;
354  //sort the contour points
355  // reverse(MarkerCanditates[i].contour.begin(),MarkerCanditates[i].contour.end());//????
356 
357  }
358  }
359 
361  //first detect candidates
362 
363  vector<pair<int,int> > TooNearCandidates;
364  for ( unsigned int i=0;i<MarkerCanditates.size();i++ )
365  {
366  // cout<<"Marker i="<<i<<MarkerCanditates[i]<<endl;
367  //calculate the average distance of each corner to the nearest corner of the other marker candidate
368  for ( unsigned int j=i+1;j<MarkerCanditates.size();j++ )
369  {
370  float dist=0;
371  for ( int c=0;c<4;c++ )
372  dist+= sqrt ( ( MarkerCanditates[i][c].x-MarkerCanditates[j][c].x ) * ( MarkerCanditates[i][c].x-MarkerCanditates[j][c].x ) + ( MarkerCanditates[i][c].y-MarkerCanditates[j][c].y ) * ( MarkerCanditates[i][c].y-MarkerCanditates[j][c].y ) );
373  dist/=4;
374  //if distance is too small
375  if ( dist< 10 )
376  {
377  TooNearCandidates.push_back ( pair<int,int> ( i,j ) );
378  }
379  }
380  }
381 
382  //mark for removal the element of the pair with smaller perimeter
383  valarray<bool> toRemove ( false,MarkerCanditates.size() );
384  for ( unsigned int i=0;i<TooNearCandidates.size();i++ )
385  {
386  if ( perimeter ( MarkerCanditates[TooNearCandidates[i].first ] ) >perimeter ( MarkerCanditates[ TooNearCandidates[i].second] ) )
387  toRemove[TooNearCandidates[i].second]=true;
388  else toRemove[TooNearCandidates[i].first]=true;
389  }
390 
391  //remove the invalid ones
392  // removeElements ( MarkerCanditates,toRemove );
393  //finally, assign to the remaining candidates the contour
394  OutMarkerCanditates.reserve(MarkerCanditates.size());
395  for (size_t i=0;i<MarkerCanditates.size();i++) {
396  if (!toRemove[i]) {
397  OutMarkerCanditates.push_back(MarkerCanditates[i]);
398  OutMarkerCanditates.back().contour=contours2[ MarkerCanditates[i].idx];
399  if (swapped[i] && _enableCylinderWarp )//if the corners where swapped, it is required to reverse here the points so that they are in the same order
400  reverse(OutMarkerCanditates.back().contour.begin(),OutMarkerCanditates.back().contour.end());//????
401  }
402  }
403 
404  }
405 
406  /************************************
407  *
408  *
409  *
410  *
411  ************************************/
412  void MarkerDetector::thresHold ( int method,const Mat &grey_m,Mat &out,double param1,double param2 ) throw ( cv::Exception )
413  {
414 #pragma GCC diagnostic push
415 #pragma GCC diagnostic ignored "-Wfloat-equal"
416  if (param1==-1) param1=_thresParam1;
417  if (param2==-1) param2=_thresParam2;
418 #pragma GCC diagnostic pop
419 
420  if ( grey_m.type() !=CV_8UC1 ) throw cv::Exception ( 9001,"grey_m.type()!=CV_8UC1","MarkerDetector::thresHold",__FILE__,__LINE__ );
421  switch ( method )
422  {
423  case FIXED_THRES:
424  cv::threshold ( grey_m, out, param1,255, cv::THRESH_BINARY_INV );
425  break;
426  case ADPT_THRES://currently, this is the best method
427  //ensure that _thresParam1%2==1
428  if ( param1<3 ) param1=3;
429  else if ( ( ( int ) param1 ) %2 !=1 ) param1= ( int ) ( param1+1 );
430 
431  cv::adaptiveThreshold ( grey_m,out,255,ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY_INV,param1,param2 );
432  break;
433  case CANNY:
434  {
435  //this should be the best method, and generally it is.
436  //However, some times there are small holes in the marker contour that makes
437  //the contour detector not to find it properly
438  //if there is a missing pixel
439  cv::Canny ( grey_m, out, 10, 220 );
440  //I've tried a closing but it add many more points that some
441  //times makes this even worse
442  // Mat aux;
443  // cv::morphologyEx(thres,aux,MORPH_CLOSE,Mat());
444  // out=aux;
445  }
446  break;
447  }
448  }
449  /************************************
450  *
451  *
452  *
453  *
454  ************************************/
455  bool MarkerDetector::warp ( Mat &in,Mat &out,Size size, vector<Point2f> points ) throw ( cv::Exception )
456  {
457 
458  if ( points.size() !=4 ) throw cv::Exception ( 9001,"point.size()!=4","MarkerDetector::warp",__FILE__,__LINE__ );
459  //obtain the perspective transform
460  Point2f pointsRes[4],pointsIn[4];
461  for ( int i=0;i<4;i++ ) pointsIn[i]=points[i];
462  pointsRes[0]= ( Point2f ( 0,0 ) );
463  pointsRes[1]= Point2f ( size.width-1,0 );
464  pointsRes[2]= Point2f ( size.width-1,size.height-1 );
465  pointsRes[3]= Point2f ( 0,size.height-1 );
466  Mat M=getPerspectiveTransform ( pointsIn,pointsRes );
467  cv::warpPerspective ( in, out, M, size,cv::INTER_NEAREST );
468  return true;
469  }
470 
471  void findCornerPointsInContour(const vector<cv::Point2f>& points,const vector<cv::Point> &contour,vector<int> &idxs)
472  {
473  assert(points.size()==4);
474  int idxSegments[4]={-1,-1,-1,-1};
475  //the first point coincides with one
476  cv::Point points2i[4];
477  for (int i=0;i<4;i++) {
478  points2i[i].x=points[i].x;
479  points2i[i].y=points[i].y;
480  }
481 
482  for (size_t i=0;i<contour.size();++i) {
483  if (idxSegments[0]==-1)
484  if (contour[i]==points2i[0]) idxSegments[0]=i;
485  if (idxSegments[1]==-1)
486  if (contour[i]==points2i[1]) idxSegments[1]=i;
487  if (idxSegments[2]==-1)
488  if (contour[i]==points2i[2]) idxSegments[2]=i;
489  if (idxSegments[3]==-1)
490  if (contour[i]==points2i[3]) idxSegments[3]=i;
491  }
492  idxs.resize(4);
493  for (int i=0;i<4;i++) idxs[i]=idxSegments[i];
494  }
495 
496  int findDeformedSidesIdx(const vector<cv::Point> &contour,const vector<int> &idxSegments)
497  {
498  float distSum[4]={0,0,0,0};
499 
500  for (int i=0;i<3;i++) {
501  cv::Point p1=contour[ idxSegments[i]];
502  cv::Point p2=contour[ idxSegments[i+1]];
503  float inv_den=1./ sqrt(float(( p2.x-p1.x)*(p2.x-p1.x)+ (p2.y-p1.y)*(p2.y-p1.y)));
504  // d=|v^^·r|=(|(x_2-x_1)(y_1-y_0)-(x_1-x_0)(y_2-y_1)|)/(sqrt((x_2-x_1)^2+(y_2-y_1)^2)).
505  // cerr<<"POSS="<<idxSegments[i]<<" "<<idxSegments[i+1]<<endl;
506  for (int j=idxSegments[i];j<idxSegments[i+1];++j) {
507  float dist=std::fabs( float( (p2.x-p1.x)*(p1.y-contour[j].y)- (p1.x-contour[j].x)*(p2.y-p1.y)) )*inv_den;
508  distSum[i]+=dist;
509  // cerr<< dist<<" ";
510  // cv::rectangle(_ssImC,contour[j],contour[j],colors[i],-1);
511  }
512  distSum[i]/=float(idxSegments[i+1]-idxSegments[i]);
513  // cout<<endl<<endl;
514  }
515 
516 
517  //for the last one
518  cv::Point p1=contour[ idxSegments[0]];
519  cv::Point p2=contour[ idxSegments[3]];
520  float inv_den=1./ std::sqrt(float(( p2.x-p1.x)*(p2.x-p1.x)+ (p2.y-p1.y)*(p2.y-p1.y)));
521  // d=|v^^·r|=(|(x_2-x_1)(y_1-y_0)-(x_1-x_0)(y_2-y_1)|)/(sqrt((x_2-x_1)^2+(y_2-y_1)^2)).
522  for (int j=0;j<idxSegments[0];++j)
523  distSum[3]+=std::fabs( float((p2.x-p1.x)*(p1.y-contour[j].y)- (p1.x-contour[j].x)*(p2.y-p1.y)))*inv_den;
524  for (int j=idxSegments[3];j<static_cast<int>(contour.size());++j)
525  distSum[3]+=std::fabs( float((p2.x-p1.x)*(p1.y-contour[j].y)- (p1.x-contour[j].x)*(p2.y-p1.y)))*inv_den;
526 
527  distSum[3]/=float( idxSegments[0]+ (contour.size()-idxSegments[3]));
528  //now, get the maximum
529  /* for (int i=0;i<4;i++)
530  cout<<"DD="<<distSum[i]<<endl;*/
531  //check the two combinations to see the one with higher error
532  if ( distSum[0]+distSum[2]> distSum[1]+distSum[3])
533  return 0;
534  else return 1;
535  }
536 
537  void setPointIntoImage(cv::Point2f &p,cv::Size s) {
538  if (p.x<0) p.x=0;
539  else if (p.x>=s.width )p.x=s.width-1;
540  if (p.y<0)p.y=0;
541  else if (p.y>=s.height)p.y=s.height-1;
542 
543  }
544 
545  void setPointIntoImage(cv::Point &p,cv::Size s) {
546  if (p.x<0) p.x=0;
547  else if (p.x>=s.width )p.x=s.width-1;
548  if (p.y<0)p.y=0;
549  else if (p.y>=s.height)p.y=s.height-1;
550 
551  }
552  /************************************
553  *
554  *
555  *
556  *
557  ************************************/
558  bool MarkerDetector::warp_cylinder ( Mat &in,Mat &out,Size size, MarkerCandidate& mcand ) throw ( cv::Exception )
559  {
560 
561  if ( mcand.size() !=4 ) throw cv::Exception ( 9001,"point.size()!=4","MarkerDetector::warp",__FILE__,__LINE__ );
562 
563  //check first the real need for cylinder warping
564  // cout<<"im="<<mcand.contour.size()<<endl;
565 
566  // for (size_t i=0;i<mcand.contour.size();i++) {
567  // cv::rectangle(_ssImC ,mcand.contour[i],mcand.contour[i],cv::Scalar(111,111,111),-1 );
568  // }
569  // mcand.draw(imC,cv::Scalar(0,255,0));
570  //find the 4 different segments of the contour
571  vector<int> idxSegments;
572  findCornerPointsInContour(mcand,mcand.contour,idxSegments);
573  //let us rearrange the points so that the first corner is the one whith smaller idx
574  int minIdx=0;
575  for (int i=1;i<4;i++)
576  if (idxSegments[i] <idxSegments[minIdx]) minIdx=i;
577  //now, rotate the points to be in this order
578  std::rotate(idxSegments.begin(),idxSegments.begin()+minIdx,idxSegments.end());
579  std::rotate(mcand.begin(),mcand.begin()+minIdx,mcand.end());
580 
581  // cout<<"idxSegments="<<idxSegments[0]<< " "<<idxSegments[1]<< " "<<idxSegments[2]<<" "<<idxSegments[3]<<endl;
582  //now, determine the sides that are deformated by cylinder perspective
583  int defrmdSide=findDeformedSidesIdx(mcand.contour,idxSegments);
584  // cout<<"Def="<<defrmdSide<<endl;
585 
586  //instead of removing perspective distortion of the rectangular region
587  //given by the rectangle, we enlarge it a bit to include the deformed parts
588  Point2f enlargedRegion[4];
589  for (int i=0;i<4;i++) enlargedRegion[i]=mcand[i];
590  if (defrmdSide==0) {
591  enlargedRegion[0]=mcand[0]+(mcand[3]-mcand[0])*1.2;
592  enlargedRegion[1]=mcand[1]+(mcand[2]-mcand[1])*1.2;
593  enlargedRegion[2]=mcand[2]+(mcand[1]-mcand[2])*1.2;
594  enlargedRegion[3]=mcand[3]+(mcand[0]-mcand[3])*1.2;
595  }
596  else {
597  enlargedRegion[0]=mcand[0]+(mcand[1]-mcand[0])*1.2;
598  enlargedRegion[1]=mcand[1]+(mcand[0]-mcand[1])*1.2;
599  enlargedRegion[2]=mcand[2]+(mcand[3]-mcand[2])*1.2;
600  enlargedRegion[3]=mcand[3]+(mcand[2]-mcand[3])*1.2;
601  }
602  for (size_t i=0;i<4;i++)
603  setPointIntoImage(enlargedRegion[i],in.size());
604 
605  /*
606  cv::Scalar colors[4]={cv::Scalar(0,0,255),cv::Scalar(255,0,0),cv::Scalar(0,255,0),cv::Scalar(111,111,0)};
607  for (int i=0;i<4;i++) {
608  cv::rectangle(_ssImC,mcand.contour[idxSegments[i]]-cv::Point(2,2),mcand.contour[idxSegments[i]]+cv::Point(2,2),colors[i],-1 );
609  cv::rectangle(_ssImC,enlargedRegion[i]-cv::Point2f(2,2),enlargedRegion[i]+cv::Point2f(2,2),colors[i],-1 );
610 
611  }*/
612  // cv::imshow("imC",_ssImC);
613 
614 
615  //calculate the max distance from each contour point the line of the corresponding segment it belongs to
616  // calculate
617  // cv::waitKey(0);
618  //check that the region is into image limits
619  //obtain the perspective transform
620  Point2f pointsRes[4];
621 
622  cv::Size enlargedSize=size;
623  enlargedSize.width+=2*enlargedSize.width*0.2;
624  pointsRes[0]= ( Point2f ( 0,0 ) );
625  pointsRes[1]= Point2f ( enlargedSize.width-1,0 );
626  pointsRes[2]= Point2f ( enlargedSize.width-1,enlargedSize.height-1 );
627  pointsRes[3]= Point2f ( 0,enlargedSize.height-1 );
628  //rotate to ensure that deformed sides are in the horizontal axis when warping
629  if (defrmdSide==0) rotate(pointsRes,pointsRes+1,pointsRes+4);
630  cv::Mat imAux,imAux2(enlargedSize,CV_8UC1);
631  Mat M=getPerspectiveTransform ( enlargedRegion,pointsRes );
632  cv::warpPerspective ( in, imAux, M, enlargedSize,cv::INTER_NEAREST);
633 
634  //now, transform all points to the new image
635  vector<cv::Point> pointsCO(mcand.contour.size());
636  assert(M.type()==CV_64F);
637  assert(M.cols==3 && M.rows==3);
638  // cout<<M<<endl;
639  double *mptr=M.ptr<double>(0);
640  imAux2.setTo(cv::Scalar::all(0));
641 
642 
643  for (size_t i=0;i<mcand.contour.size();i++) {
644  float inX=mcand.contour[i].x;
645  float inY=mcand.contour[i].y;
646  float w= inX * mptr[6]+inY * mptr[7]+mptr[8];
647  cv::Point2f pres;
648  pointsCO[i].x=( (inX * mptr[0]+inY* mptr[1]+mptr[2])/w)+0.5;
649  pointsCO[i].y=( (inX * mptr[3]+inY* mptr[4]+mptr[5])/w)+0.5;
650  //make integers
651  setPointIntoImage(pointsCO[i],imAux.size());//ensure points are into image limits
652  // cout<<"p="<<pointsCO[i]<<" "<<imAux.size().width<<" "<<imAux.size().height<<endl;
653  imAux2.at<uchar>(pointsCO[i].y,pointsCO[i].x)=255;
654  if (pointsCO[i].y>0)
655  imAux2.at<uchar>(pointsCO[i].y-1,pointsCO[i].x)=255;
656  if (pointsCO[i].y<imAux2.rows-1 )
657  imAux2.at<uchar>(pointsCO[i].y+1,pointsCO[i].x)=255;
658  }
659 
660  cv::Mat outIm(enlargedSize,CV_8UC1);
661  outIm.setTo(cv::Scalar::all(0));
662  //now, scan in lines to determine the required displacement
663  for (int y=0;y<imAux2.rows;y++) {
664  uchar *_offInfo=imAux2.ptr<uchar>(y);
665  int start=-1,end=-1;
666  //determine the start and end of markerd regions
667  for (int x=0;x<imAux.cols;x++) {
668  if (_offInfo[x]) {
669  if (start==-1) start=x;
670  else end=x;
671  }
672  }
673  // cout<<"S="<<start<<" "<<end<<" "<<end-start<<" "<<(size.width>>1)<<endl;
674  //check that the size is big enough and
675  assert(start!=-1 && end!=-1 && (end-start)> size.width>>1);
676  uchar *In_image=imAux.ptr<uchar>(y);
677  uchar *Out_image=outIm.ptr<uchar>(y);
678  memcpy(Out_image,In_image+start,imAux.cols-start );
679  }
680 
681 
682  // cout<<"SS="<<mcand.contour.size()<<" "<<pointsCO.size()<<endl;
683  //get the central region with the size specified
684  cv::Mat centerReg=outIm(cv::Range::all(),cv::Range(0,size.width));
685  out=centerReg.clone();
686  // cv::perspectiveTransform(mcand.contour,pointsCO,M);
687  //draw them
688  // cv::imshow("out2",out);
689  // cv::imshow("imm",imAux2);
690  // cv::waitKey(0);
691  return true;
692  }
693  /************************************
694  *
695  *
696  *
697  *
698  ************************************/
699  bool MarkerDetector::isInto ( Mat &contour,vector<Point2f> &b )
700  {
701 
702  for ( size_t i=0;i<b.size();i++ )
703  if ( pointPolygonTest ( contour,b[i],false ) >0 ) return true;
704  return false;
705  }
706  /************************************
707  *
708  *
709  *
710  *
711  ************************************/
712  int MarkerDetector:: perimeter ( vector<Point2f> &a )
713  {
714  int sum=0;
715  for ( size_t i=0;i<a.size();i++ )
716  {
717  int i2= ( i+1 ) %a.size();
718  sum+= sqrt ( ( a[i].x-a[i2].x ) * ( a[i].x-a[i2].x ) + ( a[i].y-a[i2].y ) * ( a[i].y-a[i2].y ) ) ;
719  }
720  return sum;
721  }
722 
723 
728  void MarkerDetector::findBestCornerInRegion_harris ( const cv::Mat & grey_m,vector<cv::Point2f> & Corners,int blockSize )
729  {
730  int halfSize=blockSize/2;
731  for ( size_t i=0;i<Corners.size();i++ )
732  {
733  //check that the region is into the image limits
734  cv::Point2f min ( Corners[i].x-halfSize,Corners[i].y-halfSize );
735  cv::Point2f max ( Corners[i].x+halfSize,Corners[i].y+halfSize );
736  if ( min.x>=0 && min.y>=0 && max.x<grey_m.cols && max.y<grey_m.rows )
737  {
738  cv::Mat response;
739  cv::Mat subImage ( grey_m,cv::Rect ( Corners[i].x-halfSize,Corners[i].y-halfSize,blockSize ,blockSize ) );
740  vector<Point2f> corners2;
741  goodFeaturesToTrack ( subImage, corners2, 10, 0.001, halfSize );
742  float minD=9999;
743  int bIdx=-1;
744  cv::Point2f Center ( halfSize,halfSize );
745  for ( size_t j=0;j<corners2.size();j++ )
746  {
747  float dist=cv::norm ( corners2[j]-Center );
748  if ( dist<minD )
749  {
750  minD=dist;
751  bIdx=j;
752  }
753  if ( minD<halfSize ) Corners[i]+= ( corners2[bIdx]-Center );
754  }
755  }
756  }
757  }
758 
759 
764  void MarkerDetector::refineCandidateLines(MarkerDetector::MarkerCandidate& candidate)
765  {
766  // search corners on the contour vector
767  vector<size_t> cornerIndex;
768  cornerIndex.resize(4);
769  for(size_t j=0; j<candidate.contour.size(); ++j) {
770  for(size_t k=0; k<4; ++k) {
771 #pragma GCC diagnostic push
772 #pragma GCC diagnostic ignored "-Wfloat-equal"
773  if(candidate.contour[j].x==candidate[k].x && candidate.contour[j].y==candidate[k].y) {
774  cornerIndex[k] = j;
775  }
776 #pragma GCC diagnostic pop
777  }
778  }
779 
780  // contour pixel in inverse order or not?
781  bool inverse;
782  if( (cornerIndex[1] > cornerIndex[0]) && (cornerIndex[2]>cornerIndex[1] || cornerIndex[2]<cornerIndex[0]) )
783  inverse = false;
784  else if( cornerIndex[2]>cornerIndex[1] && cornerIndex[2]<cornerIndex[0] )
785  inverse = false;
786  else inverse = true;
787 
788 
789  // get pixel vector for each line of the marker
790  int inc = 1;
791  if(inverse) inc = -1;
792 
793  vector<std::vector<cv::Point> > contourLines;
794  contourLines.resize(4);
795  for(unsigned int l=0; l<4; ++l) {
796  for(int j=(int)cornerIndex[l]; j!=(int)cornerIndex[(l+1)%4]; j+=inc) {
797  if(j==(int)candidate.contour.size() && !inverse) j=0;
798  else if(j==0 && inverse) j=candidate.contour.size()-1;
799  contourLines[l].push_back(candidate.contour[j]);
800  if(j==(int)cornerIndex[(l+1)%4]) break; // this has to be added because of the previous ifs
801  }
802 
803  }
804 
805  // interpolate marker lines
806  vector<Point3f> lines;
807  lines.resize(4);
808  for(unsigned int j=0; j<lines.size(); ++j)
809  interpolate2Dline(contourLines[j], lines[j]);
810 
811  // get cross points of lines
812  vector<Point2f> crossPoints;
813  crossPoints.resize(4);
814  for(unsigned int i=0; i<4; ++i)
815  crossPoints[i] = getCrossPoint( lines[(i-1)%4], lines[i] );
816 
817  // reassing points
818  for(unsigned int j=0; j<4; ++j)
819  candidate[j] = crossPoints[j];
820  }
821 
822 
825  void MarkerDetector::interpolate2Dline( const std::vector< Point >& inPoints, Point3f& outLine)
826  {
827 
828  float minX, maxX, minY, maxY;
829  minX = maxX = inPoints[0].x;
830  minY = maxY = inPoints[0].y;
831  for(unsigned int i=1; i<inPoints.size(); ++i) {
832  if(inPoints[i].x < minX) minX = inPoints[i].x;
833  if(inPoints[i].x > maxX) maxX = inPoints[i].x;
834  if(inPoints[i].y < minY) minY = inPoints[i].y;
835  if(inPoints[i].y > maxY) maxY = inPoints[i].y;
836  }
837 
838  // create matrices of equation system
839  Mat A(inPoints.size(),2,CV_32FC1, Scalar(0));
840  Mat B(inPoints.size(),1,CV_32FC1, Scalar(0));
841  Mat X;
842 
843 
844 
845  if( maxX-minX > maxY-minY ) {
846  // Ax + C = y
847  for (size_t i=0; i<inPoints.size(); ++i) {
848 
849  A.at<float>(i, 0) = inPoints[i].x;
850  A.at<float>(i, 1) = 1.;
851  B.at<float>(i, 0) = inPoints[i].y;
852 
853  }
854 
855  // solve system
856  solve(A,B,X, DECOMP_SVD);
857  // return Ax + By + C
858  outLine = Point3f(X.at<float>(0,0), -1., X.at<float>(1,0));
859  }
860  else {
861  // By + C = x
862  for (size_t i=0; i<inPoints.size(); ++i) {
863 
864  A.at<float>(i, 0) = inPoints[i].y;
865  A.at<float>(i, 1) = 1.;
866  B.at<float>(i, 0) = inPoints[i].x;
867 
868  }
869 
870  // solve system
871  solve(A,B,X, DECOMP_SVD);
872  // return Ax + By + C
873  outLine = Point3f(-1., X.at<float>(0,0), X.at<float>(1,0));
874  }
875 
876  }
877 
880  Point2f MarkerDetector::getCrossPoint(const cv::Point3f& line1, const cv::Point3f& line2)
881  {
882 
883  // create matrices of equation system
884  Mat A(2,2,CV_32FC1, Scalar(0));
885  Mat B(2,1,CV_32FC1, Scalar(0));
886  Mat X;
887 
888  A.at<float>(0, 0) = line1.x;
889  A.at<float>(0, 1) = line1.y;
890  B.at<float>(0, 0) = -line1.z;
891 
892  A.at<float>(1, 0) = line2.x;
893  A.at<float>(1, 1) = line2.y;
894  B.at<float>(1, 0) = -line2.z;
895 
896  // solve system
897  solve(A,B,X, DECOMP_SVD);
898  return Point2f(X.at<float>(0,0), X.at<float>(1,0));
899 
900  }
901 
902 
903 
904 
905 
906 
907 
908 
909  /************************************
910  *
911  *
912  *
913  *
914  ************************************/
915  void MarkerDetector::drawAllContours ( Mat input, std::vector<std::vector<cv::Point> > &contours )
916  {
917  drawContours ( input, contours, -1,Scalar ( 255,0,255 ) );
918  }
919 
920  /************************************
921  *
922  *
923  *
924  *
925  ************************************/
926  void MarkerDetector:: drawContour ( Mat &in,vector<Point> &contour,Scalar color )
927  {
928  for ( size_t i=0;i<contour.size();++i )
929  {
930  cv::rectangle ( in,contour[i],contour[i],color );
931  }
932  }
933 
934  void MarkerDetector:: drawApproxCurve ( Mat &in,vector<Point> &contour,Scalar color )
935  {
936  for ( size_t i=0;i<contour.size();++i )
937  {
938  cv::line ( in,contour[i],contour[ ( i+1 ) %contour.size() ],color );
939  }
940  }
941  /************************************
942  *
943  *
944  *
945  *
946  ************************************/
947 
948  void MarkerDetector::draw ( Mat out,const vector<Marker> &markers )
949  {
950  for ( size_t i=0;i<markers.size();++i )
951  {
952  cv::line ( out,markers[i][0],markers[i][1],cv::Scalar ( 255,0,0 ),2,LINE_AA );
953  cv::line ( out,markers[i][1],markers[i][2],cv::Scalar ( 255,0,0 ),2,LINE_AA );
954  cv::line ( out,markers[i][2],markers[i][3],cv::Scalar ( 255,0,0 ),2,LINE_AA );
955  cv::line ( out,markers[i][3],markers[i][0],cv::Scalar ( 255,0,0 ),2,LINE_AA );
956  }
957  }
958  /* Attempt to make it faster than in opencv. I could not :( Maybe trying with SSE3...
959 void MarkerDetector::warpPerspective(const cv::Mat &in,cv::Mat & out, const cv::Mat & M,cv::Size size)
960 {
961  //inverse the matrix
962  out.create(size,in.type());
963  //convert to float to speed up operations
964  const double *m=M.ptr<double>(0);
965  float mf[9];
966  mf[0]=m[0];mf[1]=m[1];mf[2]=m[2];
967  mf[3]=m[3];mf[4]=m[4];mf[5]=m[5];
968  mf[6]=m[6];mf[7]=m[7];mf[8]=m[8];
969 
970  for(int y=0;y<out.rows;y++){
971  uchar *_ptrout=out.ptr<uchar>(y);
972  for(int x=0;x<out.cols;x++){
973  //get the x,y position
974  float den=1./(x*mf[6]+y*mf[7]+mf[8]);
975  float ox= (x*mf[0]+y*mf[1]+mf[2])*den;
976  float oy= (x*mf[3]+y*mf[4]+mf[5])*den;
977  _ptrout[x]=in.at<uchar>(oy,ox);
978  }
979  }
980 }
981 */
982 
983  /************************************
984  *
985  *
986  *
987  *
988  ************************************/
989 
990  void MarkerDetector::glGetProjectionMatrix ( CameraParameters & CamMatrix,cv::Size orgImgSize, cv::Size size,double proj_matrix[16],double gnear,double gfar,bool invert )
991  throw ( cv::Exception )
992  {
993  cerr<<"MarkerDetector::glGetProjectionMatrix . This a deprecated function. Use CameraParameters::glGetProjectionMatrix instead. "<<__FILE__<<" "<<__LINE__<<endl;
994  CamMatrix.glGetProjectionMatrix ( orgImgSize,size,proj_matrix,gnear,gfar,invert );
995  }
996 
997  /************************************
998 *
999 *
1000 *
1001 *
1002 ************************************/
1003 
1004  void MarkerDetector::setMinMaxSize(float min ,float max )throw(cv::Exception)
1005  {
1006  if (min<=0 || min>1) throw cv::Exception(1," min parameter out of range","MarkerDetector::setMinMaxSize",__FILE__,__LINE__);
1007  if (max<=0 || max>1) throw cv::Exception(1," max parameter out of range","MarkerDetector::setMinMaxSize",__FILE__,__LINE__);
1008  if (min>max) throw cv::Exception(1," min>max","MarkerDetector::setMinMaxSize",__FILE__,__LINE__);
1009  _minSize=min;
1010  _maxSize=max;
1011  }
1012 
1013 }
1014 
d
void findCornerPointsInContour(const vector< cv::Point2f > &points, const vector< cv::Point > &contour, vector< int > &idxs)
NONE
f
int findDeformedSidesIdx(const vector< cv::Point > &contour, const vector< int > &idxSegments)
void setPointIntoImage(cv::Point &p, cv::Size s)
TFSIMD_FORCE_INLINE const tfScalar & y() const
TFSIMD_FORCE_INLINE Quaternion inverse(const Quaternion &q)
TFSIMD_FORCE_INLINE const tfScalar & x() const
vector< Marker > markers
TFSIMD_FORCE_INLINE const tfScalar & w() const
int min(int a, int b)
static int detect(const cv::Mat &in, int &nRotations)


lidar_camera_calibration
Author(s):
autogenerated on Sat Feb 6 2021 03:39:37