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 
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 "markerdetector.h"
29 #include <opencv2/core/core.hpp>
30 #include <opencv2/features2d/features2d.hpp>
31 #include <opencv2/imgproc/imgproc.hpp>
32 #include <opencv2/calib3d/calib3d.hpp>
33 #include <opencv2/highgui/highgui.hpp>
34 #include <iostream>
35 #include <fstream>
36 #include <valarray>
37 #include "ar_omp.h"
38 #include "markerlabeler.h"
39 using namespace std;
40 using namespace cv;
41 
42 namespace aruco {
43 
44 /************************************
45  *
46  *
47  *
48  *
49  ************************************/
50 MarkerDetector::MarkerDetector() {
51 
52  markerIdDetector = aruco::MarkerLabeler::create(Dictionary::ARUCO);
53  // markerIdDetector = aruco::MarkerLabeler::create("ARUCO");
54  if (markerIdDetector->getBestInputSize()!=-1)setWarpSize(markerIdDetector->getBestInputSize());
55 
56 }
57 
58 
59 /************************************
60  *
61  *
62  *
63  *
64  ************************************/
65 
66 MarkerDetector::~MarkerDetector() {}
67 
68 
69 
70 /************************************
71  *
72  *
73  *
74  *
75  ************************************/
76 
77 std::vector<aruco::Marker> MarkerDetector::detect(const cv::Mat &input ) throw(cv::Exception) {
78  std::vector< Marker > detectedMarkers;
79  detect(input,detectedMarkers);
80  return detectedMarkers;
81 }
82 
83 std::vector<aruco::Marker> MarkerDetector::detect(const cv::Mat &input,const CameraParameters &camParams, float markerSizeMeters , bool setYPerperdicular ) throw(cv::Exception){
84  std::vector< Marker > detectedMarkers;
85  detect(input,detectedMarkers,camParams,markerSizeMeters,setYPerperdicular);
86  return detectedMarkers;
87 
88 }
89 
90 /************************************
91  *
92  *
93  *
94  *
95  ************************************/
96 void MarkerDetector::detect(const cv::Mat &input, std::vector< Marker > &detectedMarkers, CameraParameters camParams, float markerSizeMeters,
97  bool setYPerpendicular) throw(cv::Exception) {
98  if ( camParams.CamSize!=input.size() && camParams.isValid() && markerSizeMeters>0){
99  //must resize camera parameters if we want to compute properly marker poses
100  CameraParameters cp_aux=camParams;
101  cp_aux.resize(input.size());
102  detect(input, detectedMarkers, cp_aux.CameraMatrix, cp_aux.Distorsion, markerSizeMeters, setYPerpendicular);
103  }
104  else{
105  detect(input, detectedMarkers, camParams.CameraMatrix, camParams.Distorsion, markerSizeMeters, setYPerpendicular);
106  }
107 
108 }
109 
110 
111 /************************************
112  *
113  * Main detection function. Performs all steps
114  *
115  *
116  ************************************/
117 void MarkerDetector::detect(const cv::Mat &input, vector< Marker > &detectedMarkers, Mat camMatrix, Mat distCoeff, float markerSizeMeters,
118  bool setYPerpendicular) throw(cv::Exception) {
119 
120  // it must be a 3 channel image
121  if (input.type() == CV_8UC3)
122  cv::cvtColor(input, grey, CV_BGR2GRAY);
123  else
124  grey = input;
125 
126  imagePyramid.clear();
127  imagePyramid.push_back(grey);
128  while(imagePyramid.back().cols>120){
129  cv::Mat pyrd;
130  cv::pyrDown(imagePyramid.back(),pyrd);
131  imagePyramid.push_back(pyrd);
132  };
133 
134  // cv::cvtColor(grey,_ssImC ,CV_GRAY2BGR); //DELETE
135 
136  // clear input data
137  detectedMarkers.clear();
138 
139 
140  cv::Mat imgToBeThresHolded = grey;
141 
143  // work simultaneouly in a range of values of the first threshold
144  int n_param1 = 2 * _params._thresParam1_range + 1;
145  vector< cv::Mat > thres_images;
146 
147 
148  //compute the different values of param1
149 
150  vector<int> p1_values;
151  for(int i=std::max(3.,_params._thresParam1-2*_params._thresParam1_range);i<=_params._thresParam1+2*_params._thresParam1_range;i+=2)p1_values.push_back(i);
152  thres_images.resize(p1_values.size());
153 #pragma omp parallel for
154  for (int i = 0; i < int(p1_values.size()); i++)
155  thresHold(_params._thresMethod, imgToBeThresHolded, thres_images[i], p1_values[i], _params._thresParam2);
156  thres = thres_images[n_param1 / 2];
157  //
158 
159 
160  // find all rectangles in the thresholdes image
161  vector< MarkerCandidate > MarkerCanditates;
162  detectRectangles(thres_images, MarkerCanditates);
163 
164 
165  float desiredarea=_params._markerWarpSize*_params._markerWarpSize;
167  vector< vector< Marker > > markers_omp(omp_get_max_threads());
168  vector< vector< std::vector< cv::Point2f > > > candidates_omp(omp_get_max_threads());
169 // for(int i=0;i<imagePyramid.size();i++){
170 // string name="im"+std::to_string(i)+".jpg";
171 // cv::imwrite(name,imagePyramid[i]);
172 // }
173 #pragma omp parallel for
174  for (int i = 0; i < int(MarkerCanditates.size()); i++) {
175  // Find proyective homography
176  Mat canonicalMarker;
177  bool resW = false;
178  //warping is one of the most time consuming operations, especially when the region is large.
179  //To reduce computing time, let us find in the image pyramid, the best configuration to save time
180  //indicates how much bigger observation is wrt to desired patch
181  int imgPyrIdx=0;
182  for(size_t p=1;p<imagePyramid.size();p++){
183  if (MarkerCanditates[i].getArea() / pow(4,p) >= desiredarea ) imgPyrIdx=p;
184  else break;
185  }
186 
187  vector<cv::Point2f> points2d_pyr=MarkerCanditates[i];
188  for(auto &p:points2d_pyr) p*=1./pow(2,imgPyrIdx);
189  resW = warp(imagePyramid[imgPyrIdx], canonicalMarker, Size(_params._markerWarpSize, _params._markerWarpSize), points2d_pyr);
190  //go to a pyramid that minimizes the ratio
191 
192  if (resW) {
193  int id,nRotations;
194  if (markerIdDetector->detect(canonicalMarker, id,nRotations)) {
195  if (_params._cornerMethod == LINES) // make LINES refinement before lose contour points
196  refineCandidateLines(MarkerCanditates[i], camMatrix, distCoeff);
197  markers_omp[omp_get_thread_num()].push_back(MarkerCanditates[i]);
198  markers_omp[omp_get_thread_num()].back().id = id;
199  // sort the points so that they are always in the same order no matter the camera orientation
200  std::rotate(markers_omp[omp_get_thread_num()].back().begin(), markers_omp[omp_get_thread_num()].back().begin() + 4 - nRotations, markers_omp[omp_get_thread_num()].back().end());
201  } else
202  candidates_omp[omp_get_thread_num()].push_back(MarkerCanditates[i]);
203  }
204  }
205  // unify parallel data
206  joinVectors(markers_omp, detectedMarkers, true);
207  joinVectors(candidates_omp, _candidates, true);
208 
209 
210 
211 
212 
213 
215  if (detectedMarkers.size() > 0 && _params._cornerMethod != NONE && _params._cornerMethod != LINES) {
216 
217  vector< Point2f > Corners;
218  for (unsigned int i = 0; i < detectedMarkers.size(); i++)
219  for (int c = 0; c < 4; c++)
220  Corners.push_back(detectedMarkers[i][c]);
221 
222 
223  if (_params._cornerMethod == SUBPIX) {
224  cornerSubPix(grey, Corners, cvSize(_params._subpix_wsize, _params._subpix_wsize), cvSize(-1, -1), cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 12, 0.005));
225  }
226  // copy back
227  for (unsigned int i = 0; i < detectedMarkers.size(); i++)
228  for (int c = 0; c < 4; c++)
229  detectedMarkers[i][c] = Corners[i * 4 + c];
230  }
231 
232 
233  // sort by id
234  std::sort(detectedMarkers.begin(), detectedMarkers.end());
235  // there might be still the case that a marker is detected twice because of the double border indicated earlier,
236  // detect and remove these cases
237  vector< bool > toRemove(detectedMarkers.size(), false);
238 
239  for (int i = 0; i < int(detectedMarkers.size()) - 1; i++) {
240  for (int j = i+1; j < int(detectedMarkers.size()) && !toRemove[i] ; j++) {
241  if (detectedMarkers[i].id == detectedMarkers[j].id) {
242  // deletes the one with smaller perimeter
243  if (perimeter(detectedMarkers[i]) < perimeter(detectedMarkers[j]))
244  toRemove[i ] = true;
245  else
246  toRemove[j ] = true;
247 
248  }
249  }
250  }
251 
252 
253  // remove markers with corners too near the image limits
254  int borderDistThresX = _params._borderDistThres * float(input.cols);
255  int borderDistThresY = _params._borderDistThres * float(input.rows);
256  for (size_t i = 0; i < detectedMarkers.size(); i++) {
257  // delete if any of the corners is too near image border
258  for (size_t c = 0; c < detectedMarkers[i].size(); c++) {
259  if (detectedMarkers[i][c].x < borderDistThresX || detectedMarkers[i][c].y < borderDistThresY ||
260  detectedMarkers[i][c].x > input.cols - borderDistThresX || detectedMarkers[i][c].y > input.rows - borderDistThresY) {
261  toRemove[i] = true;
262  }
263  }
264  }
265 
266 
267  // remove the markers marker
268  removeElements(detectedMarkers, toRemove);
269 
271  if (camMatrix.rows != 0 && markerSizeMeters > 0) {
272  for (unsigned int i = 0; i < detectedMarkers.size(); i++)
273  detectedMarkers[i].calculateExtrinsics(markerSizeMeters, camMatrix, distCoeff, setYPerpendicular);
274  }
275 
276 // cerr << "Threshold: " << 1000*(t2 - t1) / double(cv::getTickFrequency()) << endl;
277 // cerr << "Rectangles: " << 1000*(t3 - t2) / double(cv::getTickFrequency()) << endl;
278 // cerr << "Identify: " << 1000*(t4 - t3) / double(cv::getTickFrequency()) << endl;
279 // cerr << "Subpixel: " << 1000*(t5 - t4) / double(cv::getTickFrequency()) << endl;
280 // cerr << "Filtering: " << 1000*(t6 - t5) / double(cv::getTickFrequency()) << endl;
281 }
282 struct PointSet_2D:public std::vector<cv::Point2f>
283 {
284  // Must return the number of data points
285  inline size_t kdtree_get_point_count() const { return size(); }
286  // Returns the distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
287  inline float kdtree_distance(const float *p1, const size_t idx_p2,size_t size) const
288  {
289  (void)(size);
290  const float d0=p1[0]-at(idx_p2).x;
291  const float d1=p1[1]-at(idx_p2).y;
292  return d0*d0+d1*d1;
293  }
294 
295  // Returns the dim'th component of the idx'th point in the class:
296  // Since this is inlined and the "dim" argument is typically an immediate value, the
297  // "if/else's" are actually solved at compile time.
298  inline float kdtree_get_pt(const size_t idx, int dim) const
299  {
300  return dim==0? at(idx).x:at(idx).y;
301  }
302 
303  // Optional bounding-box computation: return false to default to a standard bbox computation loop.
304  // Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again.
305  // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds)
306  template <class BBOX>
307  bool kdtree_get_bbox(BBOX &bb) const { return false; }
308 };
309 
310 
311 /************************************
312  *
313  * Crucial step. Detects the rectangular regions of the thresholded image
314  *
315  *
316  ************************************/
317 void MarkerDetector::detectRectangles(const cv::Mat &thres, vector< std::vector< cv::Point2f > > &MarkerCanditates) {
318  vector< MarkerCandidate > candidates;
319  vector< cv::Mat > thres_v;
320  thres_v.push_back(thres);
321  detectRectangles(thres_v, candidates);
322  // create the output
323  MarkerCanditates.resize(candidates.size());
324  for (size_t i = 0; i < MarkerCanditates.size(); i++)
325  MarkerCanditates[i] = candidates[i];
326 }
327 
328 void MarkerDetector::detectRectangles(vector< cv::Mat > &thresImgv, vector< MarkerCandidate > &OutMarkerCanditates) {
329  // omp_set_num_threads ( 1 );
330  vector< vector< MarkerCandidate > > MarkerCanditatesV(omp_get_max_threads());
331  // calcualte the min_max contour sizes
332  //int minSize = _params._minSize * std::max(thresImgv[0].cols, thresImgv[0].rows) * 4;
333  int maxSize = _params._maxSize * std::max(thresImgv[0].cols, thresImgv[0].rows) * 4;
334  int minSize= std::min ( float(_params._minSize_pix) , _params._minSize* std::max(thresImgv[0].cols, thresImgv[0].rows) * 4 );
335 // cv::Mat input;
336 // cv::cvtColor ( thresImgv[0],input,CV_GRAY2BGR );
337 #pragma omp parallel for
338  for (int img_idx = 0; img_idx < int(thresImgv.size()); img_idx++) {
339  std::vector< cv::Vec4i > hierarchy2;
340  std::vector< std::vector< cv::Point > > contours2;
341  cv::Mat thres2;
342  thresImgv[img_idx].copyTo(thres2);
343  cv::findContours(thres2, contours2, hierarchy2, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
344 
345  vector< Point > approxCurve;
347  for (unsigned int i = 0; i < contours2.size(); i++) {
348 
349  // check it is a possible element by first checking is has enough points
350  if (minSize < int(contours2[i].size()) && int(contours2[i].size()) < maxSize) {
351  // approximate to a poligon
352  approxPolyDP(contours2[i], approxCurve, double(contours2[i].size()) * 0.05, true);
353  // drawApproxCurve(copy,approxCurve,Scalar(0,0,255));
354  // check that the poligon has 4 points
355  if (approxCurve.size() == 4) {
356  /*
357  drawContour ( input,contours2[i],Scalar ( 255,0,225 ) );
358  namedWindow ( "input" );
359  imshow ( "input",input );*/
360  // waitKey(0);
361  // and is convex
362  if (isContourConvex(Mat(approxCurve))) {
363  // drawApproxCurve(input,approxCurve,Scalar(255,0,255));
364  // //ensure that the distace between consecutive points is large enough
365  float minDist = 1e10;
366  for (int j = 0; j < 4; j++) {
367  float d = std::sqrt((float)(approxCurve[j].x - approxCurve[(j + 1) % 4].x) * (approxCurve[j].x - approxCurve[(j + 1) % 4].x) +
368  (approxCurve[j].y - approxCurve[(j + 1) % 4].y) * (approxCurve[j].y - approxCurve[(j + 1) % 4].y));
369  // norm(Mat(approxCurve[i]),Mat(approxCurve[(i+1)%4]));
370  if (d < minDist) minDist = d;
371  }
372  // check that distance is not very small
373  if (minDist > 10) {
374  // add the points
375  // cout<<"ADDED"<<endl;
376  MarkerCanditatesV[omp_get_thread_num()].push_back(MarkerCandidate());
377  MarkerCanditatesV[omp_get_thread_num()].back().idx = i;
378  if (_params._cornerMethod==LINES)//save all contour points if you need lines refinement method
379  MarkerCanditatesV[omp_get_thread_num()].back().contour = contours2[i];
380  for (int j = 0; j < 4; j++)
381  MarkerCanditatesV[omp_get_thread_num()].back().push_back(Point2f(approxCurve[j].x, approxCurve[j].y));
382  }
383  }
384  }
385  }
386  }
387  }
388 
389  // join all candidates
390  vector< MarkerCandidate > MarkerCanditates;
391 
392  for (size_t i = 0; i < MarkerCanditatesV.size(); i++)
393  for (size_t j = 0; j < MarkerCanditatesV[i].size(); j++) {
394  MarkerCanditates.push_back(MarkerCanditatesV[i][j]);
395  }
396 
398  valarray< bool > swapped(false, MarkerCanditates.size()); // used later
399  for (unsigned int i = 0; i < MarkerCanditates.size(); i++) {
400 
401  // trace a line between the first and second point.
402  // if the thrid point is at the right side, then the points are anti-clockwise
403  double dx1 = MarkerCanditates[i][1].x - MarkerCanditates[i][0].x;
404  double dy1 = MarkerCanditates[i][1].y - MarkerCanditates[i][0].y;
405  double dx2 = MarkerCanditates[i][2].x - MarkerCanditates[i][0].x;
406  double dy2 = MarkerCanditates[i][2].y - MarkerCanditates[i][0].y;
407  double o = (dx1 * dy2) - (dy1 * dx2);
408 
409  if (o < 0.0) { // if the third point is in the left side, then sort in anti-clockwise order
410  swap(MarkerCanditates[i][1], MarkerCanditates[i][3]);
411  swapped[i] = true;
412  // sort the contour points
413  // reverse(MarkerCanditates[i].contour.begin(),MarkerCanditates[i].contour.end());//????
414  }
415  }
417  // first detect candidates to be removed
418  vector< vector< pair< int, int > > > TooNearCandidates_omp(omp_get_max_threads());
419 #pragma omp parallel for
420  for (unsigned int i = 0; i < MarkerCanditates.size(); i++) {
421  // calculate the average distance of each corner to the nearest corner of the other marker candidate
422  for (unsigned int j = i + 1; j < MarkerCanditates.size(); j++) {
423  valarray< float > vdist(4);
424  for (int c = 0; c < 4; c++)
425  vdist[c] = sqrt((MarkerCanditates[i][c].x - MarkerCanditates[j][c].x) * (MarkerCanditates[i][c].x - MarkerCanditates[j][c].x) +
426  (MarkerCanditates[i][c].y - MarkerCanditates[j][c].y) * (MarkerCanditates[i][c].y - MarkerCanditates[j][c].y));
427  // dist/=4;
428  // if distance is too small
429  if (vdist[0] < 6 && vdist[1] < 6 && vdist[2] < 6 && vdist[3] < 6) {
430  TooNearCandidates_omp[omp_get_thread_num()].push_back(pair< int, int >(i, j));
431  }
432  }
433  }
434 
435 
436  // join
437  vector< pair< int, int > > TooNearCandidates;
438  joinVectors(TooNearCandidates_omp, TooNearCandidates);
439  // mark for removal the element of the pair with smaller perimeter
440  valarray< bool > toRemove(false, MarkerCanditates.size());
441  for (unsigned int i = 0; i < TooNearCandidates.size(); i++) {
442  if (perimeter(MarkerCanditates[TooNearCandidates[i].first]) > perimeter(MarkerCanditates[TooNearCandidates[i].second]))
443  toRemove[TooNearCandidates[i].second] = true;
444  else
445  toRemove[TooNearCandidates[i].first] = true;
446  }
447 
448  // remove the invalid ones
449  // finally, assign to the remaining candidates the contour
450  OutMarkerCanditates.reserve(MarkerCanditates.size());
451  for (size_t i = 0; i < MarkerCanditates.size(); i++) {
452  if (!toRemove[i]) {
453  OutMarkerCanditates.push_back(MarkerCanditates[i]);
454  // OutMarkerCanditates.back().contour=contours2[ MarkerCanditates[i].idx];
455  if (swapped[i] && OutMarkerCanditates.back().contour.size()>1) // if the corners where swapped, it is required to reverse here the points so that they are in the same order
456  reverse(OutMarkerCanditates.back().contour.begin(), OutMarkerCanditates.back().contour.end()); //????
457  }
458  }
459 
460  /*
461  for ( size_t i=0; i<OutMarkerCanditates.size(); i++ )
462  OutMarkerCanditates[i].draw ( input,cv::Scalar ( 124, 255,125 ) );
463 
464 
465  namedWindow ( "input" );
466  imshow ( "input",input );*/
467 }
468 
469 /************************************
470  *
471  *
472  * attempt to beat adaptiveThreshold by precomputing the integral image for all the possibilities
473  * I did not make it yet
474  *
475  ************************************/
476 
477 void MarkerDetector::adpt_threshold_multi( const Mat &grey, std::vector<Mat> &outThresImages,double param1 ,double param1_range , double param2,double param2_range ){
478 
479 // param2_range=2;
480  int start_p1 = std::max(3.,param1-2*param1_range);
481  int end_p1 = param1+2*param1_range;
482  int start_p2 = std::max(3.,param2-2*param2_range);
483  int end_p2 = param2+2*param2_range;
484  vector<std::pair<int,int> > p1_2_values;
485  for(int i=start_p1;i<=end_p1;i+=2)
486  for(int j=start_p2;j<=end_p2;j+=2)
487  p1_2_values.push_back(std::pair<int,int>(i,j));
488  outThresImages.resize(p1_2_values.size());
489 
490  cv::Mat intimg;
491  cv::integral(grey,intimg);
492  //now, run in parallel creating the thresholded images
493 #pragma omp parallel for
494  for(int i=0;i<int(p1_2_values.size());i++){
495  // cout<<p1_2_values[i].first<<" "<<p1_2_values[i].second<<endl;
496  //now, for each image, apply the
497  float inv_area=1./(p1_2_values[i].first*p1_2_values[i].first);
498  int wsize_2=p1_2_values[i].first/2;
499  outThresImages[i].create(grey.size(),grey.type() );
500  //start moving accross the image
501  for(int y=wsize_2;y<grey.rows-wsize_2;y++){
502  int *_y1=intimg.ptr<int>(y-wsize_2);
503  int *_y2=intimg.ptr<int>(y+wsize_2+1);
504  uchar *out= outThresImages[i].ptr<uchar>(y);
505  for(int x=wsize_2;x<grey.cols-wsize_2;x++){
506  int x2=x+wsize_2+1;
507  int x1=x-wsize_2;
508  // int sum=intimg.at<int>(y2,x2)-intimg.at<int>(y2,x1)-intimg.at<int>(y1,x2)+intimg.at<int>(y1,x1);
509  float mean=float( _y2[x2]-_y2[x1]-_y1[x2]+_y1[x1])* inv_area ;
510  if ( mean- grey.at<uchar>(y,x)>p1_2_values[i].second)
511  out[x]=255;
512  else
513  out[x]=0;
514  }
515  }
516  }
517 
518 }
519 
520 /************************************
521  *
522  *
523  *
524  *
525  ************************************/
526 void MarkerDetector::thresHold(int method, const Mat &grey, Mat &out, double param1, double param2) throw(cv::Exception) {
527 
528  if (param1 == -1)
529  param1 = _params._thresParam1;
530  if (param2 == -1)
531  param2 = _params._thresParam2;
532 
533  if (grey.type() != CV_8UC1)
534  throw cv::Exception(9001, "grey.type()!=CV_8UC1", "MarkerDetector::thresHold", __FILE__, __LINE__);
535  switch (method) {
536  case FIXED_THRES:
537  cv::threshold(grey, out, param1, 255, CV_THRESH_BINARY_INV);
538  break;
539  case ADPT_THRES: // currently, this is the best method
540  // ensure that _thresParam1%2==1
541  if (param1 < 3)
542  param1 = 3;
543  else if (((int)param1) % 2 != 1)
544  param1 = (int)(param1 + 1);
545 
546  cv::adaptiveThreshold(grey, out, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, param1, param2);
547  break;
548  case CANNY: {
549  // this should be the best method, and generally it is.
550  // However, some times there are small holes in the marker contour that makes
551  // the contour detector not to find it properly
552  // if there is a missing pixel
553  cv::Canny(grey, out, 10, 220);
554  // I've tried a closing but it add many more points that some
555  // times makes this even worse
556  // Mat aux;
557  // cv::morphologyEx(thres,aux,MORPH_CLOSE,Mat());
558  // out=aux;
559  } break;
560  }
561 }
562 /************************************
563  *
564  *
565  *
566  *
567  ************************************/
568 bool MarkerDetector::warp(Mat &in, Mat &out, Size size, vector< Point2f > points) throw(cv::Exception) {
569 
570  if (points.size() != 4)
571  throw cv::Exception(9001, "point.size()!=4", "MarkerDetector::warp", __FILE__, __LINE__);
572  // obtain the perspective transform
573  Point2f pointsRes[4], pointsIn[4];
574  for (int i = 0; i < 4; i++)
575  pointsIn[i] = points[i];
576  pointsRes[0] = (Point2f(0, 0));
577  pointsRes[1] = Point2f(size.width - 1, 0);
578  pointsRes[2] = Point2f(size.width - 1, size.height - 1);
579  pointsRes[3] = Point2f(0, size.height - 1);
580  Mat M = getPerspectiveTransform(pointsIn, pointsRes);
581  cv::warpPerspective(in, out, M, size, cv::INTER_NEAREST);
582  return true;
583 }
584 
585 void findCornerPointsInContour(const vector< cv::Point2f > &points, const vector< cv::Point > &contour, vector< int > &idxs) {
586  assert(points.size() == 4);
587  int idxSegments[4] = {-1, -1, -1, -1};
588  // the first point coincides with one
589  cv::Point points2i[4];
590  for (int i = 0; i < 4; i++) {
591  points2i[i].x = points[i].x;
592  points2i[i].y = points[i].y;
593  }
594 
595  for (size_t i = 0; i < contour.size(); i++) {
596  if (idxSegments[0] == -1)
597  if (contour[i] == points2i[0])
598  idxSegments[0] = i;
599  if (idxSegments[1] == -1)
600  if (contour[i] == points2i[1])
601  idxSegments[1] = i;
602  if (idxSegments[2] == -1)
603  if (contour[i] == points2i[2])
604  idxSegments[2] = i;
605  if (idxSegments[3] == -1)
606  if (contour[i] == points2i[3])
607  idxSegments[3] = i;
608  }
609  idxs.resize(4);
610  for (int i = 0; i < 4; i++)
611  idxs[i] = idxSegments[i];
612 }
613 
614 int findDeformedSidesIdx(const vector< cv::Point > &contour, const vector< int > &idxSegments) {
615  float distSum[4] = {0, 0, 0, 0};
616  //cv::Scalar colors[4] = {cv::Scalar(0, 0, 255), cv::Scalar(255, 0, 0), cv::Scalar(0, 255, 0), cv::Scalar(111, 111, 0)};
617 
618  for (int i = 0; i < 3; i++) {
619  cv::Point p1 = contour[idxSegments[i]];
620  cv::Point p2 = contour[idxSegments[i + 1]];
621  float inv_den = 1. / sqrt(float((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)));
622  // 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)).
623  // cerr<<"POSS="<<idxSegments[i]<<" "<<idxSegments[i+1]<<endl;
624  for (int j = idxSegments[i]; j < idxSegments[i + 1]; j++) {
625  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;
626  distSum[i] += dist;
627  // cerr<< dist<<" ";
628  // cv::rectangle(_ssImC,contour[j],contour[j],colors[i],-1);
629  }
630  distSum[i] /= float(idxSegments[i + 1] - idxSegments[i]);
631  // cout<<endl<<endl;
632  }
633 
634 
635  // for the last one
636  cv::Point p1 = contour[idxSegments[0]];
637  cv::Point p2 = contour[idxSegments[3]];
638  float inv_den = 1. / std::sqrt(float((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)));
639  // 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)).
640  for (int j = 0; j < idxSegments[0]; j++)
641  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;
642  for (size_t j = size_t(idxSegments[3]); j < contour.size(); j++)
643  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;
644 
645  distSum[3] /= float(idxSegments[0] + (contour.size() - idxSegments[3]));
646  // now, get the maximum
647  /* for (int i=0;i<4;i++)
648  cout<<"DD="<<distSum[i]<<endl;*/
649  // check the two combinations to see the one with higher error
650  if (distSum[0] + distSum[2] > distSum[1] + distSum[3])
651  return 0;
652  else
653  return 1;
654 }
655 
656 void setPointIntoImage(cv::Point2f &p, cv::Size s) {
657  if (p.x < 0)
658  p.x = 0;
659  else if (p.x >= s.width)
660  p.x = s.width - 1;
661  if (p.y < 0)
662  p.y = 0;
663  else if (p.y >= s.height)
664  p.y = s.height - 1;
665 }
666 
667 void setPointIntoImage(cv::Point &p, cv::Size s) {
668  if (p.x < 0)
669  p.x = 0;
670  else if (p.x >= s.width)
671  p.x = s.width - 1;
672  if (p.y < 0)
673  p.y = 0;
674  else if (p.y >= s.height)
675  p.y = s.height - 1;
676 }
677 /************************************
678  *
679  *
680  *
681  *
682  ************************************/
683 bool MarkerDetector::warp_cylinder(Mat &in, Mat &out, Size size, MarkerCandidate &mcand) throw(cv::Exception) {
684 
685  if (mcand.size() != 4)
686  throw cv::Exception(9001, "point.size()!=4", "MarkerDetector::warp", __FILE__, __LINE__);
687 
688  // check first the real need for cylinder warping
689  // cout<<"im="<<mcand.contour.size()<<endl;
690 
691  // for (size_t i=0;i<mcand.contour.size();i++) {
692  // cv::rectangle(_ssImC ,mcand.contour[i],mcand.contour[i],cv::Scalar(111,111,111),-1 );
693  // }
694  // mcand.draw(imC,cv::Scalar(0,255,0));
695  // find the 4 different segments of the contour
696  vector< int > idxSegments;
697  findCornerPointsInContour(mcand, mcand.contour, idxSegments);
698  // let us rearrange the points so that the first corner is the one whith smaller idx
699  int minIdx = 0;
700  for (int i = 1; i < 4; i++)
701  if (idxSegments[i] < idxSegments[minIdx])
702  minIdx = i;
703  // now, rotate the points to be in this order
704  std::rotate(idxSegments.begin(), idxSegments.begin() + minIdx, idxSegments.end());
705  std::rotate(mcand.begin(), mcand.begin() + minIdx, mcand.end());
706 
707  // cout<<"idxSegments="<<idxSegments[0]<< " "<<idxSegments[1]<< " "<<idxSegments[2]<<" "<<idxSegments[3]<<endl;
708  // now, determine the sides that are deformated by cylinder perspective
709  int defrmdSide = findDeformedSidesIdx(mcand.contour, idxSegments);
710  // cout<<"Def="<<defrmdSide<<endl;
711 
712  // instead of removing perspective distortion of the rectangular region
713  // given by the rectangle, we enlarge it a bit to include the deformed parts
714  //cv::Point2f center = mcand.getCenter();
715  Point2f enlargedRegion[4];
716  for (int i = 0; i < 4; i++)
717  enlargedRegion[i] = mcand[i];
718  if (defrmdSide == 0) {
719  enlargedRegion[0] = mcand[0] + (mcand[3] - mcand[0]) * 1.2;
720  enlargedRegion[1] = mcand[1] + (mcand[2] - mcand[1]) * 1.2;
721  enlargedRegion[2] = mcand[2] + (mcand[1] - mcand[2]) * 1.2;
722  enlargedRegion[3] = mcand[3] + (mcand[0] - mcand[3]) * 1.2;
723  } else {
724  enlargedRegion[0] = mcand[0] + (mcand[1] - mcand[0]) * 1.2;
725  enlargedRegion[1] = mcand[1] + (mcand[0] - mcand[1]) * 1.2;
726  enlargedRegion[2] = mcand[2] + (mcand[3] - mcand[2]) * 1.2;
727  enlargedRegion[3] = mcand[3] + (mcand[2] - mcand[3]) * 1.2;
728  }
729  for (size_t i = 0; i < 4; i++)
730  setPointIntoImage(enlargedRegion[i], in.size());
731 
732  /*
733  cv::Scalar colors[4]={cv::Scalar(0,0,255),cv::Scalar(255,0,0),cv::Scalar(0,255,0),cv::Scalar(111,111,0)};
734  for (int i=0;i<4;i++) {
735  cv::rectangle(_ssImC,mcand.contour[idxSegments[i]]-cv::Point(2,2),mcand.contour[idxSegments[i]]+cv::Point(2,2),colors[i],-1 );
736  cv::rectangle(_ssImC,enlargedRegion[i]-cv::Point2f(2,2),enlargedRegion[i]+cv::Point2f(2,2),colors[i],-1 );
737 
738  }*/
739  // cv::imshow("imC",_ssImC);
740 
741 
742  // calculate the max distance from each contour point the line of the corresponding segment it belongs to
743  // calculate
744  // cv::waitKey(0);
745  // check that the region is into image limits
746  // obtain the perspective transform
747  Point2f pointsRes[4], pointsIn[4];
748  for (int i = 0; i < 4; i++)
749  pointsIn[i] = mcand[i];
750 
751  cv::Size enlargedSize = size;
752  enlargedSize.width += 2 * enlargedSize.width * 0.2;
753  pointsRes[0] = (Point2f(0, 0));
754  pointsRes[1] = Point2f(enlargedSize.width - 1, 0);
755  pointsRes[2] = Point2f(enlargedSize.width - 1, enlargedSize.height - 1);
756  pointsRes[3] = Point2f(0, enlargedSize.height - 1);
757  // rotate to ensure that deformed sides are in the horizontal axis when warping
758  if (defrmdSide == 0)
759  rotate(pointsRes, pointsRes + 1, pointsRes + 4);
760  cv::Mat imAux, imAux2(enlargedSize, CV_8UC1);
761  Mat M = cv::getPerspectiveTransform(enlargedRegion, pointsRes);
762  cv::warpPerspective(in, imAux, M, enlargedSize, cv::INTER_NEAREST);
763 
764  // now, transform all points to the new image
765  vector< cv::Point > pointsCO(mcand.contour.size());
766  assert(M.type() == CV_64F);
767  assert(M.cols == 3 && M.rows == 3);
768  // cout<<M<<endl;
769  double *mptr = M.ptr< double >(0);
770  imAux2.setTo(cv::Scalar::all(0));
771 
772 
773  for (size_t i = 0; i < mcand.contour.size(); i++) {
774  float inX = mcand.contour[i].x;
775  float inY = mcand.contour[i].y;
776  float w = inX * mptr[6] + inY * mptr[7] + mptr[8];
777  cv::Point2f pres;
778  pointsCO[i].x = ((inX * mptr[0] + inY * mptr[1] + mptr[2]) / w) + 0.5;
779  pointsCO[i].y = ((inX * mptr[3] + inY * mptr[4] + mptr[5]) / w) + 0.5;
780  // make integers
781  setPointIntoImage(pointsCO[i], imAux.size()); // ensure points are into image limits
782  // cout<<"p="<<pointsCO[i]<<" "<<imAux.size().width<<" "<<imAux.size().height<<endl;
783  imAux2.at< uchar >(pointsCO[i].y, pointsCO[i].x) = 255;
784  if (pointsCO[i].y > 0)
785  imAux2.at< uchar >(pointsCO[i].y - 1, pointsCO[i].x) = 255;
786  if (pointsCO[i].y < imAux2.rows - 1)
787  imAux2.at< uchar >(pointsCO[i].y + 1, pointsCO[i].x) = 255;
788  }
789 
790  cv::Mat outIm(enlargedSize, CV_8UC1);
791  outIm.setTo(cv::Scalar::all(0));
792  // now, scan in lines to determine the required displacement
793  for (int y = 0; y < imAux2.rows; y++) {
794  uchar *_offInfo = imAux2.ptr< uchar >(y);
795  int start = -1, end = -1;
796  // determine the start and end of markerd regions
797  for (int x = 0; x < imAux.cols; x++) {
798  if (_offInfo[x]) {
799  if (start == -1)
800  start = x;
801  else
802  end = x;
803  }
804  }
805  // cout<<"S="<<start<<" "<<end<<" "<<end-start<<" "<<(size.width>>1)<<endl;
806  // check that the size is big enough and
807  assert(start != -1 && end != -1 && (end - start) > size.width >> 1);
808  uchar *In_image = imAux.ptr< uchar >(y);
809  uchar *Out_image = outIm.ptr< uchar >(y);
810  memcpy(Out_image, In_image + start, imAux.cols - start);
811  }
812 
813 
814  // cout<<"SS="<<mcand.contour.size()<<" "<<pointsCO.size()<<endl;
815  // get the central region with the size specified
816  cv::Mat centerReg = outIm(cv::Range::all(), cv::Range(0, size.width));
817  out = centerReg.clone();
818  // cv::perspectiveTransform(mcand.contour,pointsCO,M);
819  // draw them
820  // cv::imshow("out2",out);
821  // cv::imshow("imm",imAux2);
822  // cv::waitKey(0);
823  return true;
824 }
825 /************************************
826  *
827  *
828  *
829  *
830  ************************************/
831 bool MarkerDetector::isInto(Mat &contour, vector< Point2f > &b) {
832 
833  for (unsigned int i = 0; i < b.size(); i++)
834  if (pointPolygonTest(contour, b[i], false) > 0)
835  return true;
836  return false;
837 }
838 /************************************
839  *
840  *
841  *
842  *
843  ************************************/
844 int MarkerDetector::perimeter(vector< Point2f > &a) {
845  int sum = 0;
846  for (unsigned int i = 0; i < a.size(); i++) {
847  int i2 = (i + 1) % a.size();
848  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));
849  }
850  return sum;
851 }
852 
853 
854 
855 
856 
861 void MarkerDetector::refineCandidateLines(MarkerDetector::MarkerCandidate &candidate, const cv::Mat &camMatrix, const cv::Mat &distCoeff) {
862  // search corners on the contour vector
863  vector< int > cornerIndex(4,-1);
864  for (unsigned int j = 0; j < candidate.contour.size(); j++) {
865  for (unsigned int k = 0; k < 4; k++) {
866  if (candidate.contour[j].x == candidate[k].x && candidate.contour[j].y == candidate[k].y) {
867  cornerIndex[k] = j;
868  }
869  }
870  }
871 
872  // contour pixel in inverse order or not?
873  bool inverse;
874  if ((cornerIndex[1] > cornerIndex[0]) && (cornerIndex[2] > cornerIndex[1] || cornerIndex[2] < cornerIndex[0]))
875  inverse = false;
876  else if (cornerIndex[2] > cornerIndex[1] && cornerIndex[2] < cornerIndex[0])
877  inverse = false;
878  else
879  inverse = true;
880 
881 
882  // get pixel vector for each line of the marker
883  int inc = 1;
884  if (inverse)
885  inc = -1;
886 
887  // undistort contour
888  vector< Point2f > contour2f;
889  if(!camMatrix.empty() && !distCoeff.empty()){
890  for (unsigned int i = 0; i < candidate.contour.size(); i++)
891  contour2f.push_back(cv::Point2f(candidate.contour[i].x, candidate.contour[i].y));
892  if (!camMatrix.empty() && !distCoeff.empty())
893  cv::undistortPoints(contour2f, contour2f, camMatrix, distCoeff, cv::Mat(), camMatrix);
894 
895  }
896  else {
897  contour2f.reserve(candidate.contour.size());
898  for(auto p:candidate.contour)
899  contour2f.push_back(cv::Point2f(p.x,p.y));
900  }
901 
902  vector< std::vector< cv::Point2f > > contourLines;
903  contourLines.resize(4);
904  for (unsigned int l = 0; l < 4; l++) {
905  for (int j = (int)cornerIndex[l]; j != (int)cornerIndex[(l + 1) % 4]; j += inc) {
906  if (j == (int)candidate.contour.size() && !inverse)
907  j = 0;
908  else if (j == 0 && inverse)
909  j = candidate.contour.size() - 1;
910  contourLines[l].push_back(contour2f[j]);
911  if (j == (int)cornerIndex[(l + 1) % 4])
912  break; // this has to be added because of the previous ifs
913  }
914  }
915 
916  // interpolate marker lines
917  vector< Point3f > lines;
918  lines.resize(4);
919  for (unsigned int j = 0; j < lines.size(); j++)
920  interpolate2Dline(contourLines[j], lines[j]);
921 
922  // get cross points of lines
923  vector< Point2f > crossPoints;
924  crossPoints.resize(4);
925  for (unsigned int i = 0; i < 4; i++)
926  crossPoints[i] = getCrossPoint(lines[(i - 1) % 4], lines[i]);
927 
928  // distort corners again if undistortion was performed
929  if (!camMatrix.empty() && !distCoeff.empty())
930  distortPoints(crossPoints, crossPoints, camMatrix, distCoeff);
931 
932  // reassing points
933  for (unsigned int j = 0; j < 4; j++)
934  candidate[j] = crossPoints[j];
935 }
936 
937 
940 void MarkerDetector::interpolate2Dline(const std::vector< Point2f > &inPoints, Point3f &outLine) {
941 
942  float minX, maxX, minY, maxY;
943  minX = maxX = inPoints[0].x;
944  minY = maxY = inPoints[0].y;
945  for (unsigned int i = 1; i < inPoints.size(); i++) {
946  if (inPoints[i].x < minX)
947  minX = inPoints[i].x;
948  if (inPoints[i].x > maxX)
949  maxX = inPoints[i].x;
950  if (inPoints[i].y < minY)
951  minY = inPoints[i].y;
952  if (inPoints[i].y > maxY)
953  maxY = inPoints[i].y;
954  }
955 
956  // create matrices of equation system
957  Mat A(inPoints.size(), 2, CV_32FC1, Scalar(0));
958  Mat B(inPoints.size(), 1, CV_32FC1, Scalar(0));
959  Mat X;
960 
961 
962 
963  if (maxX - minX > maxY - minY) {
964  // Ax + C = y
965  for (size_t i = 0; i < inPoints.size(); i++) {
966 
967  A.at< float >(i, 0) = inPoints[i].x;
968  A.at< float >(i, 1) = 1.;
969  B.at< float >(i, 0) = inPoints[i].y;
970  }
971 
972  // solve system
973  solve(A, B, X, DECOMP_SVD);
974  // return Ax + By + C
975  outLine = Point3f(X.at< float >(0, 0), -1., X.at< float >(1, 0));
976  } else {
977  // By + C = x
978  for (size_t i = 0; i < inPoints.size(); i++) {
979 
980  A.at< float >(i, 0) = inPoints[i].y;
981  A.at< float >(i, 1) = 1.;
982  B.at< float >(i, 0) = inPoints[i].x;
983  }
984 
985  // solve system
986  solve(A, B, X, DECOMP_SVD);
987  // return Ax + By + C
988  outLine = Point3f(-1., X.at< float >(0, 0), X.at< float >(1, 0));
989  }
990 }
991 
994 Point2f MarkerDetector::getCrossPoint(const cv::Point3f &line1, const cv::Point3f &line2) {
995 
996  // create matrices of equation system
997  Mat A(2, 2, CV_32FC1, Scalar(0));
998  Mat B(2, 1, CV_32FC1, Scalar(0));
999  Mat X;
1000 
1001  A.at< float >(0, 0) = line1.x;
1002  A.at< float >(0, 1) = line1.y;
1003  B.at< float >(0, 0) = -line1.z;
1004 
1005  A.at< float >(1, 0) = line2.x;
1006  A.at< float >(1, 1) = line2.y;
1007  B.at< float >(1, 0) = -line2.z;
1008 
1009  // solve system
1010  solve(A, B, X, DECOMP_SVD);
1011  return Point2f(X.at< float >(0, 0), X.at< float >(1, 0));
1012 }
1013 
1014 
1017 void MarkerDetector::distortPoints(vector< cv::Point2f > in, vector< cv::Point2f > &out, const Mat &camMatrix, const Mat &distCoeff) {
1018  // trivial extrinsics
1019  cv::Mat Rvec = cv::Mat(3, 1, CV_32FC1, cv::Scalar::all(0));
1020  cv::Mat Tvec = Rvec.clone();
1021  // calculate 3d points and then reproject, so opencv makes the distortion internally
1022  vector< cv::Point3f > cornersPoints3d;
1023  for (unsigned int i = 0; i < in.size(); i++)
1024  cornersPoints3d.push_back(cv::Point3f((in[i].x - camMatrix.at< float >(0, 2)) / camMatrix.at< float >(0, 0), // x
1025  (in[i].y - camMatrix.at< float >(1, 2)) / camMatrix.at< float >(1, 1), // y
1026  1)); // z
1027  cv::projectPoints(cornersPoints3d, Rvec, Tvec, camMatrix, distCoeff, out);
1028 }
1029 
1030 
1031 
1032 /************************************
1033  *
1034  *
1035  *
1036  *
1037  ************************************/
1038 void MarkerDetector::drawAllContours(Mat input, std::vector< std::vector< cv::Point > > &contours) { drawContours(input, contours, -1, Scalar(255, 0, 255)); }
1039 
1040 /************************************
1041  *
1042  *
1043  *
1044  *
1045  ************************************/
1046 void MarkerDetector::drawContour(Mat &in, vector< Point > &contour, Scalar color) {
1047  for (unsigned int i = 0; i < contour.size(); i++) {
1048  cv::rectangle(in, contour[i], contour[i], color);
1049  }
1050 }
1051 
1052 void MarkerDetector::drawApproxCurve(Mat &in, vector< Point > &contour, Scalar color) {
1053  for (unsigned int i = 0; i < contour.size(); i++) {
1054  cv::line(in, contour[i], contour[(i + 1) % contour.size()], color);
1055  }
1056 }
1057 /************************************
1058  *
1059  *
1060  *
1061  *
1062  ************************************/
1063 
1064 void MarkerDetector::draw(Mat out, const vector< Marker > &markers) {
1065  for (unsigned int i = 0; i < markers.size(); i++) {
1066  cv::line(out, markers[i][0], markers[i][1], cvScalar(255, 0, 0), 2, CV_AA);
1067  cv::line(out, markers[i][1], markers[i][2], cvScalar(255, 0, 0), 2, CV_AA);
1068  cv::line(out, markers[i][2], markers[i][3], cvScalar(255, 0, 0), 2, CV_AA);
1069  cv::line(out, markers[i][3], markers[i][0], cvScalar(255, 0, 0), 2, CV_AA);
1070  }
1071 }
1072 /* Attempt to make it faster than in opencv. I could not :( Maybe trying with SSE3...
1073 void MarkerDetector::warpPerspective(const cv::Mat &in,cv::Mat & out, const cv::Mat & M,cv::Size size)
1074 {
1075  //inverse the matrix
1076  out.create(size,in.type());
1077  //convert to float to speed up operations
1078  const double *m=M.ptr<double>(0);
1079  float mf[9];
1080  mf[0]=m[0];mf[1]=m[1];mf[2]=m[2];
1081  mf[3]=m[3];mf[4]=m[4];mf[5]=m[5];
1082  mf[6]=m[6];mf[7]=m[7];mf[8]=m[8];
1083 
1084  for(int y=0;y<out.rows;y++){
1085  uchar *_ptrout=out.ptr<uchar>(y);
1086  for(int x=0;x<out.cols;x++){
1087  //get the x,y position
1088  float den=1./(x*mf[6]+y*mf[7]+mf[8]);
1089  float ox= (x*mf[0]+y*mf[1]+mf[2])*den;
1090  float oy= (x*mf[3]+y*mf[4]+mf[5])*den;
1091  _ptrout[x]=in.at<uchar>(oy,ox);
1092  }
1093  }
1094 }
1095 */
1096 
1097 
1098 
1099 void MarkerDetector::findCornerMaxima(vector< cv::Point2f > &Corners, const cv::Mat &grey, int wsize) {
1100 
1101 // for each element, search in a region around
1102 #pragma omp parallel for
1103 
1104  for (size_t i = 0; i < Corners.size(); i++) {
1105  cv::Point2f minLimit(std::max(0, int(Corners[i].x - wsize)), std::max(0, int(Corners[i].y - wsize)));
1106  cv::Point2f maxLimit(std::min(grey.cols, int(Corners[i].x + wsize)), std::min(grey.rows, int(Corners[i].y + wsize)));
1107 
1108  cv::Mat reg = grey(cv::Range(minLimit.y, maxLimit.y), cv::Range(minLimit.x, maxLimit.x));
1109  cv::Mat harr, harrint;
1110  cv::cornerHarris(reg, harr, 3, 3, 0.04);
1111 
1112  // now, do a sum block operation
1113  cv::integral(harr, harrint);
1114  int bls_a = 4;
1115  for (int y = bls_a; y < harr.rows - bls_a; y++) {
1116  float *h = harr.ptr< float >(y);
1117  for (int x = bls_a; x < harr.cols - bls_a; x++)
1118  h[x] = harrint.at< double >(y + bls_a, x + bls_a) - harrint.at< double >(y + bls_a, x) - harrint.at< double >(y, x + bls_a) +
1119  harrint.at< double >(y, x);
1120  }
1121 
1122 
1123 
1124  cv::Point2f best(-1, -1);
1125  cv::Point2f center(reg.cols / 2, reg.rows / 2);
1126  ;
1127  double maxv = 0;
1128  for (int i = 0; i < harr.rows; i++) {
1129  // L1 dist to center
1130  float *har = harr.ptr< float >(i);
1131  for (int x = 0; x < harr.cols; x++) {
1132  float d = float(fabs(center.x - x) + fabs(center.y - i)) / float(reg.cols / 2 + reg.rows / 2);
1133  float w = 1. - d;
1134  if (w * har[x] > maxv) {
1135  maxv = w * har[x];
1136  best = cv::Point2f(x, i);
1137  }
1138  }
1139  }
1140  Corners[i] = best + minLimit;
1141  }
1142 }
1143 
1144 
1145 void MarkerDetector::setMarkerLabeler(cv::Ptr<MarkerLabeler> detector)throw(cv::Exception){
1146  markerIdDetector=detector;
1147  if (markerIdDetector->getBestInputSize()!=-1)setWarpSize(markerIdDetector->getBestInputSize());
1148 
1149 }
1150 
1151 void MarkerDetector::setDictionary(Dictionary::DICT_TYPES dict_type,float error_correction_rate)throw(cv::Exception){
1152  markerIdDetector= MarkerLabeler::create(dict_type,error_correction_rate);
1153  if (markerIdDetector->getBestInputSize()!=-1)setWarpSize(markerIdDetector->getBestInputSize());
1154 }
1155 void MarkerDetector::setDictionary(string dict_type,float error_correction_rate)throw(cv::Exception){
1156  markerIdDetector= MarkerLabeler::create(Dictionary::getTypeFromString( dict_type),error_correction_rate);
1157  if (markerIdDetector->getBestInputSize()!=-1)setWarpSize(markerIdDetector->getBestInputSize());
1158 }
1159 
1160 };
d
void findCornerPointsInContour(const vector< cv::Point2f > &points, const vector< cv::Point > &contour, vector< int > &idxs)
void resize(cv::Size size)
ROSCPP_DECL void start()
NONE
int findDeformedSidesIdx(const vector< cv::Point > &contour, const vector< int > &idxSegments)
float kdtree_get_pt(const size_t idx, int dim) const
void setPointIntoImage(cv::Point &p, cv::Size s)
const CwiseUnaryOp< internal::scalar_inverse_op< Scalar >, const Derived > inverse() const
void swap(Bag &a, Bag &b)
int omp_get_max_threads()
Definition: ar_omp.cpp:2
int omp_get_thread_num()
Definition: ar_omp.cpp:3
Parameters of the camera.
static cv::Ptr< MarkerLabeler > create(Dictionary::DICT_TYPES dict_type, float error_correction_rate=0)
const Scalar & y
bool kdtree_get_bbox(BBOX &bb) const
float kdtree_distance(const float *p1, const size_t idx_p2, size_t size) const
size_t kdtree_get_point_count() const
const CwiseUnaryOp< internal::scalar_pow_op< Scalar >, const Derived > pow(const Scalar &exponent) const
const CwiseUnaryOp< internal::scalar_sqrt_op< Scalar >, const Derived > sqrt() const


tuw_aruco
Author(s): Lukas Pfeifhofer
autogenerated on Mon Feb 28 2022 23:57:56