marker.cpp
Go to the documentation of this file.
1 
17 #include "marker.h"
18 
20 #define _USE_MATH_DEFINES
21 
22 #include <opencv2/calib3d/calib3d.hpp>
23 #include <opencv2/imgproc/imgproc.hpp>
24 
25 #include <cstdio>
26 #include <math.h>
27 #include "cameraparameters.h"
28 #include "ippe.h"
29 
30 namespace aruco
31 {
36 {
37  id = -1;
38  ssize = -1;
39  Rvec.create(3, 1, CV_32FC1);
40  Tvec.create(3, 1, CV_32FC1);
41  for (int i = 0; i < 3; i++)
42  Tvec.at<float>(i, 0) = Rvec.at<float>(i, 0) = -999999;
43 }
48 {
49  id = _id;
50  ssize = -1;
51  Rvec.create(3, 1, CV_32FC1);
52  Tvec.create(3, 1, CV_32FC1);
53  for (int i = 0; i < 3; i++)
54  Tvec.at<float>(i, 0) = Rvec.at<float>(i, 0) = -999999;
55 }
59 Marker::Marker(const Marker& M) : std::vector<cv::Point2f>(M)
60 {
61  M.copyTo(*this);
62 }
63 
67 Marker::Marker(const std::vector<cv::Point2f>& corners, int _id)
68  : std::vector<cv::Point2f>(corners)
69 {
70  id = _id;
71  ssize = -1;
72  Rvec.create(3, 1, CV_32FC1);
73  Tvec.create(3, 1, CV_32FC1);
74  for (int i = 0; i < 3; i++)
75  Tvec.at<float>(i, 0) = Rvec.at<float>(i, 0) = -999999;
76 }
77 
78 void Marker::copyTo(Marker& m) const
79 {
80  m.id = id;
81  // size of the markers sides in meters
82  m.ssize = ssize;
83  // matrices of rotation and translation respect to the camera
84  Rvec.copyTo(m.Rvec);
85  Tvec.copyTo(m.Tvec);
86  m.resize(size());
87  for (size_t i = 0; i < size(); i++)
88  m.at(i) = at(i);
89  m.dict_info = dict_info;
91 }
95 {
96  m.copyTo(*this);
97  return *this;
98 }
102 void Marker::glGetModelViewMatrix(double modelview_matrix[16])
103 {
104  // check if paremeters are valid
105  bool invalid = false;
106  for (int i = 0; i < 3 && !invalid; i++)
107  {
108  if (Tvec.at<float>(i, 0) != -999999)
109  invalid |= false;
110  if (Rvec.at<float>(i, 0) != -999999)
111  invalid |= false;
112  }
113  if (invalid)
114  throw cv::Exception(9003, "extrinsic parameters are not set",
115  "Marker::getModelViewMatrix", __FILE__, __LINE__);
116  cv::Mat Rot(3, 3, CV_32FC1), Jacob;
117  cv::Rodrigues(Rvec, Rot, Jacob);
118 
119  double para[3][4];
120  for (int i = 0; i < 3; i++)
121  for (int j = 0; j < 3; j++)
122  para[i][j] = Rot.at<float>(i, j);
123  // now, add the translation
124  para[0][3] = Tvec.at<float>(0, 0);
125  para[1][3] = Tvec.at<float>(1, 0);
126  para[2][3] = Tvec.at<float>(2, 0);
127  double scale = 1;
128 
129  modelview_matrix[0 + 0 * 4] = para[0][0];
130  // R1C2
131  modelview_matrix[0 + 1 * 4] = para[0][1];
132  modelview_matrix[0 + 2 * 4] = para[0][2];
133  modelview_matrix[0 + 3 * 4] = para[0][3];
134  // R2
135  modelview_matrix[1 + 0 * 4] = para[1][0];
136  modelview_matrix[1 + 1 * 4] = para[1][1];
137  modelview_matrix[1 + 2 * 4] = para[1][2];
138  modelview_matrix[1 + 3 * 4] = para[1][3];
139  // R3
140  modelview_matrix[2 + 0 * 4] = -para[2][0];
141  modelview_matrix[2 + 1 * 4] = -para[2][1];
142  modelview_matrix[2 + 2 * 4] = -para[2][2];
143  modelview_matrix[2 + 3 * 4] = -para[2][3];
144  modelview_matrix[3 + 0 * 4] = 0.0;
145  modelview_matrix[3 + 1 * 4] = 0.0;
146  modelview_matrix[3 + 2 * 4] = 0.0;
147  modelview_matrix[3 + 3 * 4] = 1.0;
148  if (scale != 0.0)
149  {
150  modelview_matrix[12] *= scale;
151  modelview_matrix[13] *= scale;
152  modelview_matrix[14] *= scale;
153  }
154 }
155 
156 /****
157  *
158  */
159 void Marker::OgreGetPoseParameters(double position[3], double orientation[4])
160 {
161  // check if paremeters are valid
162  bool invalid = false;
163  for (int i = 0; i < 3 && !invalid; i++)
164  {
165  if (Tvec.at<float>(i, 0) != -999999)
166  invalid |= false;
167  if (Rvec.at<float>(i, 0) != -999999)
168  invalid |= false;
169  }
170  if (invalid)
171  throw cv::Exception(9003, "extrinsic parameters are not set",
172  "Marker::getModelViewMatrix", __FILE__, __LINE__);
173 
174  // calculate position vector
175  position[0] = -Tvec.ptr<float>(0)[0];
176  position[1] = -Tvec.ptr<float>(0)[1];
177  position[2] = +Tvec.ptr<float>(0)[2];
178 
179  // now calculare orientation quaternion
180  cv::Mat Rot(3, 3, CV_32FC1);
181  cv::Rodrigues(Rvec, Rot);
182 
183  // calculate axes for quaternion
184  double stAxes[3][3];
185  // x axis
186  stAxes[0][0] = -Rot.at<float>(0, 0);
187  stAxes[0][1] = -Rot.at<float>(1, 0);
188  stAxes[0][2] = +Rot.at<float>(2, 0);
189  // y axis
190  stAxes[1][0] = -Rot.at<float>(0, 1);
191  stAxes[1][1] = -Rot.at<float>(1, 1);
192  stAxes[1][2] = +Rot.at<float>(2, 1);
193  // for z axis, we use cross product
194  stAxes[2][0] = stAxes[0][1] * stAxes[1][2] - stAxes[0][2] * stAxes[1][1];
195  stAxes[2][1] = -stAxes[0][0] * stAxes[1][2] + stAxes[0][2] * stAxes[1][0];
196  stAxes[2][2] = stAxes[0][0] * stAxes[1][1] - stAxes[0][1] * stAxes[1][0];
197 
198  // transposed matrix
199  double axes[3][3];
200  axes[0][0] = stAxes[0][0];
201  axes[1][0] = stAxes[0][1];
202  axes[2][0] = stAxes[0][2];
203 
204  axes[0][1] = stAxes[1][0];
205  axes[1][1] = stAxes[1][1];
206  axes[2][1] = stAxes[1][2];
207 
208  axes[0][2] = stAxes[2][0];
209  axes[1][2] = stAxes[2][1];
210  axes[2][2] = stAxes[2][2];
211 
212  // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
213  // article "Quaternion Calculus and Fast Animation".
214  double fTrace = axes[0][0] + axes[1][1] + axes[2][2];
215  double fRoot;
216 
217  if (fTrace > 0.0)
218  {
219  // |w| > 1/2, may as well choose w > 1/2
220  fRoot = sqrt(fTrace + 1.0); // 2w
221  orientation[0] = 0.5 * fRoot;
222  fRoot = 0.5 / fRoot; // 1/(4w)
223  orientation[1] = (axes[2][1] - axes[1][2]) * fRoot;
224  orientation[2] = (axes[0][2] - axes[2][0]) * fRoot;
225  orientation[3] = (axes[1][0] - axes[0][1]) * fRoot;
226  }
227  else
228  {
229  // |w| <= 1/2
230  static unsigned int s_iNext[3] = { 1, 2, 0 };
231  unsigned int i = 0;
232  if (axes[1][1] > axes[0][0])
233  i = 1;
234  if (axes[2][2] > axes[i][i])
235  i = 2;
236  unsigned int j = s_iNext[i];
237  unsigned int k = s_iNext[j];
238 
239  fRoot = sqrt(axes[i][i] - axes[j][j] - axes[k][k] + 1.0);
240  double* apkQuat[3] = { &orientation[1], &orientation[2], &orientation[3] };
241  *apkQuat[i] = 0.5 * fRoot;
242  fRoot = 0.5 / fRoot;
243  orientation[0] = (axes[k][j] - axes[j][k]) * fRoot;
244  *apkQuat[j] = (axes[j][i] + axes[i][j]) * fRoot;
245  *apkQuat[k] = (axes[k][i] + axes[i][k]) * fRoot;
246  }
247 }
248 
249 void Marker::draw(cv::Mat& in, cv::Scalar color, int lineWidth, bool writeId, bool writeInfo) const
250 {
251  auto _to_string = [](int i)
252  {
253  std::stringstream str;
254  str << i;
255  return str.str();
256  };
257 
258  if (size() != 4)
259  return;
260  float flineWidth = lineWidth;
261  if (lineWidth == -1) // auto
262  flineWidth = std::max(1.f, std::min(5.f, float(in.cols) / 500.f));
263  lineWidth = round(flineWidth);
264  cv::line(in, (*this)[0], (*this)[1], color, lineWidth);
265  cv::line(in, (*this)[1], (*this)[2], color, lineWidth);
266  cv::line(in, (*this)[2], (*this)[3], color, lineWidth);
267  cv::line(in, (*this)[3], (*this)[0], color, lineWidth);
268 
269  auto p2 =
270  cv::Point2f(2.f * static_cast<float>(lineWidth), 2.f * static_cast<float>(lineWidth));
271  cv::rectangle(in, (*this)[0] - p2, (*this)[0] + p2, cv::Scalar(0, 0, 255, 255), -1);
272  cv::rectangle(in, (*this)[1] - p2, (*this)[1] + p2, cv::Scalar(0, 255, 0, 255), lineWidth);
273  cv::rectangle(in, (*this)[2] - p2, (*this)[2] + p2, cv::Scalar(255, 0, 0, 255), lineWidth);
274 
275 
276 
277  if (writeId)
278  {
279  // determine the centroid
280  cv::Point cent(0, 0);
281  for (int i = 0; i < 4; i++)
282  {
283  cent.x += static_cast<int>((*this)[i].x);
284  cent.y += static_cast<int>((*this)[i].y);
285  }
286  cent.x /= 4;
287  cent.y /= 4;
288  std::string str;
289  if (writeInfo)
290  str += dict_info + ":";
291  if (writeId)
292  str += _to_string(id);
293  float fsize = std::min(3.0f, flineWidth * 0.75f);
294  cv::putText(in, str, cent - cv::Point(10 * flineWidth, 0), cv::FONT_HERSHEY_SIMPLEX,
295  fsize, cv::Scalar(255, 255, 255) - color, lineWidth, cv::LINE_AA);
296  }
297 }
298 
301 void Marker::calculateExtrinsics(float markerSize, const CameraParameters& CP, bool setYPerpendicular)
302 {
303  if (!CP.isValid())
304  throw cv::Exception(9004, "!CP.isValid(): invalid camera parameters. It is not possible to calculate extrinsics",
305  "calculateExtrinsics", __FILE__, __LINE__);
307  setYPerpendicular);
308 }
309 
310 void print(cv::Point3f p, std::string cad)
311 {
312  std::cout << cad << " " << p.x << " " << p.y << " " << p.z << std::endl;
313 }
316 void Marker::calculateExtrinsics(float markerSizeMeters, cv::Mat camMatrix, cv::Mat distCoeff,
317  cv::Mat Extrinsics, bool setYPerpendicular, bool correctFisheye)
318 {
319  if (!isValid())
320  throw cv::Exception(9004, "!isValid(): invalid marker. It is not possible to calculate extrinsics",
321  "calculateExtrinsics", __FILE__, __LINE__);
322  if (markerSizeMeters <= 0)
323  throw cv::Exception(9004, "markerSize<=0: invalid markerSize", "calculateExtrinsics",
324  __FILE__, __LINE__);
325  if (camMatrix.rows == 0 || camMatrix.cols == 0)
326  throw cv::Exception(9004, "CameraMatrix is empty", "calculateExtrinsics", __FILE__, __LINE__);
327 
328  std::vector<cv::Point3f> objpoints = get3DPoints(markerSizeMeters);
329 
330  cv::Mat raux, taux;
331  if (correctFisheye)
332  {
333  std::vector<cv::Point2f> undistorted;
334  cv::fisheye::undistortPoints(*this, undistorted, camMatrix, distCoeff);
335  cv::solvePnP(objpoints, undistorted, cv::Mat::eye(camMatrix.size(), camMatrix.type()),
336  cv::Mat::zeros(distCoeff.size(), distCoeff.type()), raux, taux);
337  }
338  else
339  {
340  cv::solvePnP(objpoints, *this, camMatrix, distCoeff, raux, taux);
341  }
342  raux.convertTo(Rvec, CV_32F);
343  taux.convertTo(Tvec, CV_32F);
344  float tx = -1 * (Extrinsics.at<double>(0, 0) / camMatrix.at<float>(0, 0));
345  Tvec.at<float>(0) += tx;
346  float ty = -1 * (Extrinsics.at<double>(0, 1) / camMatrix.at<float>(1, 1));
347  Tvec.at<float>(1) += ty;
348  float tz = -1 * (Extrinsics.at<double>(0, 2) / camMatrix.at<float>(2, 2));
349  Tvec.at<float>(2) += tz;
350 
351  // rotate the X axis so that Y is perpendicular to the marker plane
352  if (setYPerpendicular)
353  rotateXAxis(Rvec);
354  ssize = markerSizeMeters;
355  // cout << (*this) << endl;
356 }
357 
358 
359 std::vector<cv::Point3f> Marker::get3DPoints(float msize)
360 {
361  float halfSize = msize / 2.f;
362  // std::vector<cv::Point3f> res(4);
363  // res[0]=cv::Point3f(-halfSize, halfSize, 0);
364  // res[1]=cv::Point3f(halfSize, halfSize, 0);
365  // res[2]=cv::Point3f(halfSize,-halfSize, 0);
366  // res[3]=cv::Point3f(-halfSize,- halfSize, 0);
367  // return res;
368  return { cv::Point3f(-halfSize, halfSize, 0), cv::Point3f(halfSize, halfSize, 0),
369  cv::Point3f(halfSize, -halfSize, 0), cv::Point3f(-halfSize, -halfSize, 0) };
370 }
371 
372 void Marker::rotateXAxis(cv::Mat& rotation)
373 {
374  cv::Mat R(3, 3, CV_32F);
375  cv::Rodrigues(rotation, R);
376  // create a rotation matrix for x axis
377  cv::Mat RX = cv::Mat::eye(3, 3, CV_32F);
378  const float angleRad = 3.14159265359f / 2.f;
379  RX.at<float>(1, 1) = cos(angleRad);
380  RX.at<float>(1, 2) = -sin(angleRad);
381  RX.at<float>(2, 1) = sin(angleRad);
382  RX.at<float>(2, 2) = cos(angleRad);
383  // now multiply
384  R = R * RX;
385  // finally, the the rodrigues back
386  Rodrigues(R, rotation);
387 }
388 
391 cv::Point2f Marker::getCenter() const
392 {
393  cv::Point2f cent(0, 0);
394  for (size_t i = 0; i < size(); i++)
395  {
396  cent.x += (*this)[i].x;
397  cent.y += (*this)[i].y;
398  }
399  cent.x /= float(size());
400  cent.y /= float(size());
401  return cent;
402 }
403 
406 float Marker::getArea() const
407 {
408  assert(size() == 4);
409  // use the cross products
410  cv::Point2f v01 = (*this)[1] - (*this)[0];
411  cv::Point2f v03 = (*this)[3] - (*this)[0];
412  float area1 = fabs(v01.x * v03.y - v01.y * v03.x);
413  cv::Point2f v21 = (*this)[1] - (*this)[2];
414  cv::Point2f v23 = (*this)[3] - (*this)[2];
415  float area2 = fabs(v21.x * v23.y - v21.y * v23.x);
416  return (area2 + area1) / 2.f;
417 }
420 float Marker::getPerimeter() const
421 {
422  assert(size() == 4);
423  float sum = 0;
424  for (int i = 0; i < 4; i++)
425  sum += static_cast<float>(norm((*this)[i] - (*this)[(i + 1) % 4]));
426  return sum;
427 }
428 
429 
430 // saves to a binary stream
431 void Marker::toStream(std::ostream& str) const
432 {
433  assert(Rvec.type() == CV_32F && Tvec.type() == CV_32F);
434  str.write((char*)&id, sizeof(id));
435  str.write((char*)&ssize, sizeof(ssize));
436  str.write((char*)Rvec.ptr<float>(0), 3 * sizeof(float));
437  str.write((char*)Tvec.ptr<float>(0), 3 * sizeof(float));
438  // write the 2d points
439  uint32_t np = static_cast<uint32_t>(size());
440  str.write((char*)&np, sizeof(np));
441  for (size_t i = 0; i < size(); i++)
442  str.write((char*)&at(i), sizeof(cv::Point2f));
443  // write the additional info
444  uint32_t s = dict_info.size();
445  str.write((char*)&s, sizeof(s));
446  str.write((char*)&dict_info[0], dict_info.size());
447  s = contourPoints.size();
448  str.write((char*)&s, sizeof(s));
449  str.write((char*)&contourPoints[0], contourPoints.size() * sizeof(contourPoints[0]));
450 }
451 // reads from a binary stream
452 void Marker::fromStream(std::istream& str)
453 {
454  Rvec.create(1, 3, CV_32F);
455  Tvec.create(1, 3, CV_32F);
456  str.read((char*)&id, sizeof(id));
457  str.read((char*)&ssize, sizeof(ssize));
458  str.read((char*)Rvec.ptr<float>(0), 3 * sizeof(float));
459  str.read((char*)Tvec.ptr<float>(0), 3 * sizeof(float));
460  uint32_t np;
461  str.read((char*)&np, sizeof(np));
462  resize(np);
463  for (size_t i = 0; i < size(); i++)
464  str.read((char*)&(*this)[i], sizeof(cv::Point2f));
465  // read the additional info
466  uint32_t s;
467  str.read((char*)&s, sizeof(s));
468  dict_info.resize(s);
469  str.read((char*)&dict_info[0], dict_info.size());
470  str.read((char*)&s, sizeof(s));
471  contourPoints.resize(s);
472  str.read((char*)&contourPoints[0], contourPoints.size() * sizeof(contourPoints[0]));
473 }
474 
476 {
477  cv::Mat T = cv::Mat::eye(4, 4, CV_32F);
478  cv::Mat rot = T.rowRange(0, 3).colRange(0, 3);
479  cv::Rodrigues(Rvec, rot);
480  for (int i = 0; i < 3; i++)
481  T.at<float>(i, 3) = Tvec.ptr<float>(0)[i];
482  return T;
483 }
484 
485 float Marker::getRadius() const
486 {
487  auto center = getCenter();
488 
489  float maxDist = 0;
490  for (auto p : *this)
491  maxDist = std::max(maxDist, float(cv::norm(p - center)));
492  return maxDist;
493 }
494 
495 } // namespace aruco
ippe.h
aruco::Marker::operator=
Marker & operator=(const Marker &m)
Definition: marker.cpp:94
aruco::CameraParameters
Parameters of the camera.
Definition: cameraparameters.h:29
aruco::Marker::getCenter
cv::Point2f getCenter() const
Definition: marker.cpp:391
aruco::Marker::Rvec
cv::Mat Rvec
Definition: marker.h:43
aruco::Marker::toStream
void toStream(std::ostream &str) const
Definition: marker.cpp:431
aruco::Marker::isValid
bool isValid() const
Definition: marker.h:68
aruco::Marker::id
int id
Definition: marker.h:39
aruco::CameraParameters::isValid
bool isValid() const
Definition: cameraparameters.h:63
marker.h
f
f
aruco::Marker::rotateXAxis
void rotateXAxis(cv::Mat &rotation)
Definition: marker.cpp:372
aruco::Marker::draw
void draw(cv::Mat &in, cv::Scalar color=cv::Scalar(0, 0, 255), int lineWidth=-1, bool writeId=true, bool writeInfo=false) const
Definition: marker.cpp:249
aruco::CameraParameters::CameraMatrix
cv::Mat CameraMatrix
Definition: cameraparameters.h:33
aruco::Marker::calculateExtrinsics
void calculateExtrinsics(float markerSize, const CameraParameters &CP, bool setYPerpendicular=true)
Definition: marker.cpp:301
aruco::print
void print(cv::Point3f p, std::string cad)
Definition: marker.cpp:310
aruco::Marker::glGetModelViewMatrix
void glGetModelViewMatrix(double modelview_matrix[16])
Definition: marker.cpp:102
aruco::Marker
Definition: marker.h:35
aruco::Marker::copyTo
void copyTo(Marker &m) const
Definition: marker.cpp:78
aruco::CameraParameters::ExtrinsicMatrix
cv::Mat ExtrinsicMatrix
Definition: cameraparameters.h:40
aruco::Marker::getPerimeter
float getPerimeter() const
Definition: marker.cpp:420
aruco::Marker::contourPoints
vector< cv::Point > contourPoints
Definition: marker.h:47
std
aruco
Definition: cameraparameters.h:24
aruco::Marker::getTransformMatrix
cv::Mat getTransformMatrix() const
Definition: marker.cpp:475
aruco::Marker::get3DPoints
vector< cv::Point3f > get3DPoints() const
Definition: marker.h:178
aruco::Marker::fromStream
void fromStream(std::istream &str)
Definition: marker.cpp:452
cameraparameters.h
aruco::Marker::getRadius
float getRadius() const
Definition: marker.cpp:485
aruco::Marker::getArea
float getArea() const
Definition: marker.cpp:406
aruco::CameraParameters::Distorsion
cv::Mat Distorsion
Definition: cameraparameters.h:35
aruco::Marker::OgreGetPoseParameters
void OgreGetPoseParameters(double position[3], double orientation[4])
Definition: marker.cpp:159
aruco::Marker::dict_info
std::string dict_info
Definition: marker.h:45
aruco::Marker::Tvec
cv::Mat Tvec
Definition: marker.h:43
aruco::Marker::ssize
float ssize
Definition: marker.h:41
aruco::solvePnP
std::vector< cv::Mat > solvePnP(const std::vector< cv::Point3f > &objPoints, const std::vector< cv::Point2f > &imgPoints, cv::InputArray cameraMatrix, cv::InputArray distCoeffs)
Definition: ippe.cpp:88
aruco::Marker::Marker
Marker()
Definition: marker.cpp:35


aruco
Author(s): Rafael Muñoz Salinas , Bence Magyar
autogenerated on Sat Sep 23 2023 02:26:45