32 #include <opencv2/imgproc/imgproc.hpp> 45 Mat FiducidalMarkers::createMarkerImage(
int id,
int size,
bool addWaterMark,
bool locked)
throw(cv::Exception) {
46 Mat marker(size, size, CV_8UC1);
47 marker.setTo(Scalar(0));
48 if (0 <=
id &&
id < 1024) {
50 int swidth = size / 7;
51 int ids[4] = {0x10, 0x17, 0x09, 0x0e};
52 for (
int y = 0; y < 5; y++) {
53 int index = (
id >> 2 * (4 - y)) & 0x0003;
55 for (
int x = 0; x < 5; x++) {
56 Mat roi = marker(Rect((x + 1) * swidth, (y + 1) * swidth, swidth, swidth));
57 if ((val >> (4 - x)) & 0x0001)
58 roi.setTo(Scalar(255));
64 throw cv::Exception(9004,
"id invalid",
"createMarker", __FILE__, __LINE__);
68 sprintf(idcad,
"#%d",
id);
69 float ax = float(size) / 100.;
70 int linew = 1 + (marker.rows / 500);
71 cv::putText(marker, idcad, cv::Point(0, marker.rows - marker.rows / 40), cv::FONT_HERSHEY_COMPLEX, ax * 0.15f, cv::Scalar::all(30), linew, CV_AA);
77 int sqSize = float(size) * 0.25;
79 cv::Mat lock_marker(cv::Size(size + sqSize * 2, size + sqSize * 2), marker.type());
81 lock_marker.setTo(cv::Scalar::all(255));
83 cv::Mat sub = lock_marker(cv::Range(0, sqSize), cv::Range(0, sqSize));
84 sub.setTo(cv::Scalar::all(0));
86 sub = lock_marker(cv::Range(lock_marker.rows - sqSize, lock_marker.rows), cv::Range(0, sqSize));
87 sub.setTo(cv::Scalar::all(0));
89 sub = lock_marker(cv::Range(lock_marker.rows - sqSize, lock_marker.rows), cv::Range(lock_marker.cols - sqSize, lock_marker.cols));
90 sub.setTo(cv::Scalar::all(0));
92 sub = lock_marker(cv::Range(0, sqSize), cv::Range(lock_marker.cols - sqSize, lock_marker.cols));
93 sub.setTo(cv::Scalar::all(0));
95 sub = lock_marker(cv::Range(sqSize, marker.rows + sqSize), cv::Range(sqSize, marker.cols + sqSize));
104 cv::Mat FiducidalMarkers::getMarkerMat(
int id)
throw(cv::Exception) {
105 Mat marker(5, 5, CV_8UC1);
106 marker.setTo(Scalar(0));
107 if (0 <=
id &&
id < 1024) {
109 int ids[4] = {0x10, 0x17, 0x09, 0x0e};
110 for (
int y = 0; y < 5; y++) {
111 int index = (
id >> 2 * (4 - y)) & 0x0003;
112 int val = ids[index];
113 for (
int x = 0; x < 5; x++) {
114 if ((val >> (4 - x)) & 0x0001)
115 marker.at< uchar >(y, x) = 1;
117 marker.at< uchar >(y, x) = 0;
121 throw cv::Exception(9189,
"Invalid marker id",
"aruco::fiducidal::createMarkerMat", __FILE__, __LINE__);
131 cv::Mat FiducidalMarkers::createBoardImage(Size gridSize,
int MarkerSize,
int MarkerDistance,
BoardConfiguration &TInfo,
132 vector< int > *excludedIds)
throw(cv::Exception) {
136 srand(cv::getTickCount());
137 int nMarkers = gridSize.height * gridSize.width;
138 TInfo.resize(nMarkers);
139 vector< int > ids = getListOfValidMarkersIds_random(nMarkers, excludedIds);
140 for (
int i = 0; i < nMarkers; i++)
141 TInfo[i].
id = ids[i];
143 int sizeY = gridSize.height * MarkerSize + (gridSize.height - 1) * MarkerDistance;
144 int sizeX = gridSize.width * MarkerSize + (gridSize.width - 1) * MarkerDistance;
146 int centerX = sizeX / 2;
147 int centerY = sizeY / 2;
150 TInfo.mInfoType = BoardConfiguration::PIX;
151 Mat tableImage(sizeY, sizeX, CV_8UC1);
152 tableImage.setTo(Scalar(255));
154 for (
int y = 0; y < gridSize.height; y++)
155 for (
int x = 0; x < gridSize.width; x++, idp++) {
156 Mat subrect(tableImage, Rect(x * (MarkerDistance + MarkerSize), y * (MarkerDistance + MarkerSize), MarkerSize, MarkerSize));
157 Mat marker = createMarkerImage(TInfo[idp].
id, MarkerSize);
159 TInfo[idp].resize(4);
160 TInfo[idp][0] = cv::Point3f(x * (MarkerDistance + MarkerSize), y * (MarkerDistance + MarkerSize), 0);
161 TInfo[idp][1] = cv::Point3f(x * (MarkerDistance + MarkerSize) + MarkerSize, y * (MarkerDistance + MarkerSize), 0);
162 TInfo[idp][2] = cv::Point3f(x * (MarkerDistance + MarkerSize) + MarkerSize, y * (MarkerDistance + MarkerSize) + MarkerSize, 0);
163 TInfo[idp][3] = cv::Point3f(x * (MarkerDistance + MarkerSize), y * (MarkerDistance + MarkerSize) + MarkerSize, 0);
164 for (
int i = 0; i < 4; i++)
165 TInfo[idp][i] -= cv::Point3f(centerX, centerY, 0);
166 marker.copyTo(subrect);
178 cv::Mat FiducidalMarkers::createBoardImage_ChessBoard(Size gridSize,
int MarkerSize,
BoardConfiguration &TInfo,
bool centerData,
179 vector< int > *excludedIds)
throw(cv::Exception) {
182 srand(cv::getTickCount());
185 int nMarkers = 3 * (gridSize.width * gridSize.height) / 4;
186 vector< int > idsVector = getListOfValidMarkersIds_random(nMarkers, excludedIds);
189 int sizeY = gridSize.height * MarkerSize;
190 int sizeX = gridSize.width * MarkerSize;
192 int centerX = sizeX / 2;
193 int centerY = sizeY / 2;
195 Mat tableImage(sizeY, sizeX, CV_8UC1);
196 tableImage.setTo(Scalar(255));
197 TInfo.mInfoType = BoardConfiguration::PIX;
198 int CurMarkerIdx = 0;
199 for (
int y = 0; y < gridSize.height; y++) {
206 for (
int x = 0; x < gridSize.width; x++) {
209 if (CurMarkerIdx >= idsVector.size())
210 throw cv::Exception(999,
" FiducidalMarkers::createBoardImage_ChessBoard",
"INTERNAL ERROR. REWRITE THIS!!", __FILE__, __LINE__);
211 TInfo.push_back(
MarkerInfo(idsVector[CurMarkerIdx++]));
213 Mat subrect(tableImage, Rect(x * MarkerSize, y * MarkerSize, MarkerSize, MarkerSize));
214 Mat marker = createMarkerImage(TInfo.back().id, MarkerSize);
216 TInfo.back().resize(4);
217 TInfo.back()[0] = cv::Point3f(x * (MarkerSize), y * (MarkerSize), 0);
218 TInfo.back()[1] = cv::Point3f(x * (MarkerSize)+MarkerSize, y * (MarkerSize), 0);
219 TInfo.back()[2] = cv::Point3f(x * (MarkerSize)+MarkerSize, y * (MarkerSize)+MarkerSize, 0);
220 TInfo.back()[3] = cv::Point3f(x * (MarkerSize), y * (MarkerSize)+MarkerSize, 0);
222 for (
int i = 0; i < 4; i++)
223 TInfo.back()[i] -= cv::Point3f(centerX, centerY, 0);
225 marker.copyTo(subrect);
241 cv::Mat FiducidalMarkers::createBoardImage_Frame(Size gridSize,
int MarkerSize,
int MarkerDistance,
BoardConfiguration &TInfo,
bool centerData,
242 vector< int > *excludedIds)
throw(cv::Exception) {
246 srand(cv::getTickCount());
247 int nMarkers = 2 * gridSize.height * 2 * gridSize.width;
248 vector< int > idsVector = getListOfValidMarkersIds_random(nMarkers, excludedIds);
250 int sizeY = gridSize.height * MarkerSize + MarkerDistance * (gridSize.height - 1);
251 int sizeX = gridSize.width * MarkerSize + MarkerDistance * (gridSize.width - 1);
253 int centerX = sizeX / 2;
254 int centerY = sizeY / 2;
256 Mat tableImage(sizeY, sizeX, CV_8UC1);
257 tableImage.setTo(Scalar(255));
258 TInfo.mInfoType = BoardConfiguration::PIX;
259 int CurMarkerIdx = 0;
260 int mSize = MarkerSize + MarkerDistance;
261 for (
int y = 0; y < gridSize.height; y++) {
262 for (
int x = 0; x < gridSize.width; x++) {
263 if (y == 0 || y == gridSize.height - 1 || x == 0 || x == gridSize.width - 1) {
264 TInfo.push_back(
MarkerInfo(idsVector[CurMarkerIdx++]));
265 Mat subrect(tableImage, Rect(x * mSize, y * mSize, MarkerSize, MarkerSize));
266 Mat marker = createMarkerImage(TInfo.back().id, MarkerSize);
267 marker.copyTo(subrect);
269 TInfo.back().resize(4);
270 TInfo.back()[0] = cv::Point3f(x * (mSize), y * (mSize), 0);
271 TInfo.back()[1] = cv::Point3f(x * (mSize)+MarkerSize, y * (mSize), 0);
272 TInfo.back()[2] = cv::Point3f(x * (mSize)+MarkerSize, y * (mSize)+MarkerSize, 0);
273 TInfo.back()[3] = cv::Point3f(x * (mSize), y * (mSize)+MarkerSize, 0);
275 for (
int i = 0; i < 4; i++)
276 TInfo.back()[i] -= cv::Point3f(centerX, centerY, 0);
290 Mat FiducidalMarkers::rotate(
const Mat &in) {
293 for (
int i = 0; i < in.rows; i++) {
294 for (
int j = 0; j < in.cols; j++) {
295 out.at< uchar >(i, j) = in.at< uchar >(in.cols - j - 1, i);
308 int FiducidalMarkers::hammDistMarker(Mat bits) {
309 int ids[4][5] = {{1, 0, 0, 0, 0}, {1, 0, 1, 1, 1}, {0, 1, 0, 0, 1}, {0, 1, 1, 1, 0}};
312 for (
int y = 0; y < 5; y++) {
315 for (
int p = 0; p < 4; p++) {
318 for (
int x = 0; x < 5; x++)
319 sum += bits.at< uchar >(y, x) == ids[p][x] ? 0 : 1;
336 int FiducidalMarkers::analyzeMarkerImage(Mat &grey,
int &nRotations) {
341 int swidth = grey.rows / 7;
342 for (
int y = 0; y < 7; y++) {
344 if (y == 0 || y == 6)
346 for (
int x = 0; x < 7; x += inc) {
347 int Xstart = (x) * (swidth);
348 int Ystart = (y) * (swidth);
349 Mat square = grey(Rect(Xstart, Ystart, swidth, swidth));
350 int nZ = countNonZero(square);
351 if (nZ > (swidth * swidth) / 2) {
359 vector< int > markerInfo(5);
360 Mat _bits = Mat::zeros(5, 5, CV_8UC1);
363 for (
int y = 0; y < 5; y++) {
365 for (
int x = 0; x < 5; x++) {
366 int Xstart = (x + 1) * (swidth);
367 int Ystart = (y + 1) * (swidth);
368 Mat square = grey(Rect(Xstart, Ystart, swidth, swidth));
369 int nZ = countNonZero(square);
370 if (nZ > (swidth * swidth) / 2)
371 _bits.at< uchar >(y, x) = 1;
379 Rotations[0] = _bits;
381 dists[0] = hammDistMarker(Rotations[0]);
382 pair< int, int > minDist(dists[0], 0);
383 for (
int i = 1; i < 4; i++) {
385 Rotations[i] = rotate(Rotations[i - 1]);
387 dists[i] = hammDistMarker(Rotations[i]);
388 if (dists[i] < minDist.first) {
389 minDist.first = dists[i];
396 nRotations = minDist.second;
397 if (minDist.first != 0)
401 cv::Mat bits = Rotations[minDist.second];
402 for (
int y = 0; y < 5; y++) {
404 if (bits.at< uchar >(y, 1))
407 if (bits.at< uchar >(y, 3))
421 bool FiducidalMarkers::correctHammMarker(Mat &bits) {
424 int ids[4][5] = {{0, 0, 0, 0, 0}, {0, 0, 1, 1, 1}, {1, 1, 0, 0, 1}, {1, 1, 1, 1, 0}};
426 for (
int y = 0; y < 5; y++) {
429 for (
int p = 0; p < 4; p++) {
432 for (
int x = 0; x < 5; x++)
433 sum += bits.at< uchar >(y, x) == ids[p][x] ? 0 : 1;
452 int FiducidalMarkers::detect(
const Mat &in,
int &nRotations) {
453 assert(in.rows == in.cols);
455 if (in.type() == CV_8UC1)
458 cv::cvtColor(in, grey, CV_BGR2GRAY);
460 threshold(grey, grey, 125, 255, THRESH_BINARY | THRESH_OTSU);
465 return analyzeMarkerImage(grey, nRotations);
475 vector< int > FiducidalMarkers::getListOfValidMarkersIds_random(
int nMarkers, vector< int > *excluded)
throw(cv::Exception) {
477 if (excluded != NULL)
478 if (nMarkers + excluded->size() > 1024)
479 throw cv::Exception(8888,
"FiducidalMarkers::getListOfValidMarkersIds_random",
"Number of possible markers is exceeded", __FILE__, __LINE__);
481 vector< int > listOfMarkers(1024);
483 for (
int i = 0; i < 1024; i++)
484 listOfMarkers[i] = i;
486 if (excluded != NULL)
487 for (
size_t i = 0; i < excluded->size(); i++)
488 listOfMarkers[excluded->at(i)] = -1;
490 random_shuffle(listOfMarkers.begin(), listOfMarkers.end());
493 vector< int > retList;
494 while (retList.size() < nMarkers) {
495 if (listOfMarkers[i] != -1)
496 retList.push_back(listOfMarkers[i]);
This class defines a board with several markers. A Board contains several markers so that they are mo...