dictionary.cpp 18.2 KB
Newer Older
S. Garrido's avatar
S. Garrido committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
/*
By downloading, copying, installing or using the software you agree to this
license. If you do not agree to this license, do not download, install,
copy or use the software.

                          License Agreement
               For Open Source Computer Vision Library
                       (3-clause BSD License)

Copyright (C) 2013, OpenCV Foundation, all rights reserved.
Third party copyrights are property of their respective owners.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

  * Neither the names of the copyright holders nor the names of the contributors
    may be used to endorse or promote products derived from this software
    without specific prior written permission.

This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall copyright holders or contributors be liable for
any direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused
and on any theory of liability, whether in contract, strict liability,
or tort (including negligence or otherwise) arising in any way out of
the use of this software, even if advised of the possibility of such damage.
*/

#include "precomp.hpp"
#include "opencv2/aruco/dictionary.hpp"
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
43
#include "predefined_dictionaries.hpp"
44
#include "predefined_dictionaries_apriltag.hpp"
45
#include "opencv2/core/hal/hal.hpp"
S. Garrido's avatar
S. Garrido committed
46 47 48 49 50 51

namespace cv {
namespace aruco {

using namespace std;

52 53 54 55 56 57 58 59 60 61

/**
  */
Dictionary::Dictionary(const Ptr<Dictionary> &_dictionary) {
    markerSize = _dictionary->markerSize;
    maxCorrectionBits = _dictionary->maxCorrectionBits;
    bytesList = _dictionary->bytesList.clone();
}


S. Garrido's avatar
S. Garrido committed
62 63
/**
  */
64
Dictionary::Dictionary(const Mat &_bytesList, int _markerSize, int _maxcorr) {
S. Garrido's avatar
S. Garrido committed
65 66
    markerSize = _markerSize;
    maxCorrectionBits = _maxcorr;
67
    bytesList = _bytesList;
S. Garrido's avatar
S. Garrido committed
68 69 70
}


71 72
/**
 */
73
Ptr<Dictionary> Dictionary::create(int nMarkers, int markerSize, int randomSeed) {
74
    const Ptr<Dictionary> baseDictionary = makePtr<Dictionary>();
75
    return create(nMarkers, markerSize, baseDictionary, randomSeed);
76 77 78 79 80 81
}


/**
 */
Ptr<Dictionary> Dictionary::create(int nMarkers, int markerSize,
82 83 84
                                   const Ptr<Dictionary> &baseDictionary, int randomSeed) {

    return generateCustomDictionary(nMarkers, markerSize, baseDictionary, randomSeed);
85 86 87 88 89 90 91 92 93
}


/**
 */
Ptr<Dictionary> Dictionary::get(int dict) {
    return getPredefinedDictionary(dict);
}

S. Garrido's avatar
S. Garrido committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

/**
 */
bool Dictionary::identify(const Mat &onlyBits, int &idx, int &rotation,
                          double maxCorrectionRate) const {

    CV_Assert(onlyBits.rows == markerSize && onlyBits.cols == markerSize);

    int maxCorrectionRecalculed = int(double(maxCorrectionBits) * maxCorrectionRate);

    // get as a byte list
    Mat candidateBytes = getByteListFromBits(onlyBits);

    idx = -1; // by default, not found

    // search closest marker in dict
    for(int m = 0; m < bytesList.rows; m++) {
        int currentMinDistance = markerSize * markerSize + 1;
        int currentRotation = -1;
        for(unsigned int r = 0; r < 4; r++) {
114
            int currentHamming = cv::hal::normHamming(
115 116 117
                    bytesList.ptr(m)+r*candidateBytes.cols,
                    candidateBytes.ptr(),
                    candidateBytes.cols);
S. Garrido's avatar
S. Garrido committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

            if(currentHamming < currentMinDistance) {
                currentMinDistance = currentHamming;
                currentRotation = r;
            }
        }

        // if maxCorrection is fullfilled, return this one
        if(currentMinDistance <= maxCorrectionRecalculed) {
            idx = m;
            rotation = currentRotation;
            break;
        }
    }

133
    return idx != -1;
S. Garrido's avatar
S. Garrido committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
}


/**
  */
int Dictionary::getDistanceToId(InputArray bits, int id, bool allRotations) const {

    CV_Assert(id >= 0 && id < bytesList.rows);

    unsigned int nRotations = 4;
    if(!allRotations) nRotations = 1;

    Mat candidateBytes = getByteListFromBits(bits.getMat());
    int currentMinDistance = int(bits.total() * bits.total());
    for(unsigned int r = 0; r < nRotations; r++) {
149
        int currentHamming = cv::hal::normHamming(
150 151 152
                bytesList.ptr(id) + r*candidateBytes.cols,
                candidateBytes.ptr(),
                candidateBytes.cols);
S. Garrido's avatar
S. Garrido committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

        if(currentHamming < currentMinDistance) {
            currentMinDistance = currentHamming;
        }
    }
    return currentMinDistance;
}



/**
 * @brief Draw a canonical marker image
 */
void Dictionary::drawMarker(int id, int sidePixels, OutputArray _img, int borderBits) const {

168
    CV_Assert(sidePixels >= (markerSize + 2*borderBits));
S. Garrido's avatar
S. Garrido committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    CV_Assert(id < bytesList.rows);
    CV_Assert(borderBits > 0);

    _img.create(sidePixels, sidePixels, CV_8UC1);

    // create small marker with 1 pixel per bin
    Mat tinyMarker(markerSize + 2 * borderBits, markerSize + 2 * borderBits, CV_8UC1,
                   Scalar::all(0));
    Mat innerRegion = tinyMarker.rowRange(borderBits, tinyMarker.rows - borderBits)
                          .colRange(borderBits, tinyMarker.cols - borderBits);
    // put inner bits
    Mat bits = 255 * getBitsFromByteList(bytesList.rowRange(id, id + 1), markerSize);
    CV_Assert(innerRegion.total() == bits.total());
    bits.copyTo(innerRegion);

    // resize tiny marker to output size
    cv::resize(tinyMarker, _img.getMat(), _img.getMat().size(), 0, 0, INTER_NEAREST);
}




/**
  * @brief Transform matrix of bits to list of bytes in the 4 rotations
  */
Mat Dictionary::getByteListFromBits(const Mat &bits) {
195 196
    // integer ceil
    int nbytes = (bits.cols * bits.rows + 8 - 1) / 8;
S. Garrido's avatar
S. Garrido committed
197 198 199 200

    Mat candidateByteList(1, nbytes, CV_8UC4, Scalar::all(0));
    unsigned char currentBit = 0;
    int currentByte = 0;
201 202 203 204 205 206 207

    // the 4 rotations
    uchar* rot0 = candidateByteList.ptr();
    uchar* rot1 = candidateByteList.ptr() + 1*nbytes;
    uchar* rot2 = candidateByteList.ptr() + 2*nbytes;
    uchar* rot3 = candidateByteList.ptr() + 3*nbytes;

S. Garrido's avatar
S. Garrido committed
208 209 210
    for(int row = 0; row < bits.rows; row++) {
        for(int col = 0; col < bits.cols; col++) {
            // circular shift
211 212 213 214 215 216 217 218 219
            rot0[currentByte] <<= 1;
            rot1[currentByte] <<= 1;
            rot2[currentByte] <<= 1;
            rot3[currentByte] <<= 1;
            // set bit
            rot0[currentByte] |= bits.at<uchar>(row, col);
            rot1[currentByte] |= bits.at<uchar>(col, bits.cols - 1 - row);
            rot2[currentByte] |= bits.at<uchar>(bits.rows - 1 - row, bits.cols - 1 - col);
            rot3[currentByte] |= bits.at<uchar>(bits.rows - 1 - col, row);
S. Garrido's avatar
S. Garrido committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
            currentBit++;
            if(currentBit == 8) {
                // next byte
                currentBit = 0;
                currentByte++;
            }
        }
    }
    return candidateByteList;
}



/**
  * @brief Transform list of bytes to matrix of bits
  */
Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) {
    CV_Assert(byteList.total() > 0 &&
              byteList.total() >= (unsigned int)markerSize * markerSize / 8 &&
              byteList.total() <= (unsigned int)markerSize * markerSize / 8 + 1);
    Mat bits(markerSize, markerSize, CV_8UC1, Scalar::all(0));

    unsigned char base2List[] = { 128, 64, 32, 16, 8, 4, 2, 1 };
    int currentByteIdx = 0;
    // we only need the bytes in normal rotation
245
    unsigned char currentByte = byteList.ptr()[0];
S. Garrido's avatar
S. Garrido committed
246 247 248 249 250 251 252 253 254 255
    int currentBit = 0;
    for(int row = 0; row < bits.rows; row++) {
        for(int col = 0; col < bits.cols; col++) {
            if(currentByte >= base2List[currentBit]) {
                bits.at< unsigned char >(row, col) = 1;
                currentByte -= base2List[currentBit];
            }
            currentBit++;
            if(currentBit == 8) {
                currentByteIdx++;
256
                currentByte = byteList.ptr()[currentByteIdx];
S. Garrido's avatar
S. Garrido committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
                // if not enough bits for one more byte, we are in the end
                // update bit position accordingly
                if(8 * (currentByteIdx + 1) > (int)bits.total())
                    currentBit = 8 * (currentByteIdx + 1) - (int)bits.total();
                else
                    currentBit = 0; // ok, bits enough for next byte
            }
        }
    }
    return bits;
}




// DictionaryData constructors calls
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
const Dictionary DICT_ARUCO_DATA = Dictionary(Mat(1024, (5*5 + 7)/8, CV_8UC4, (uchar*)DICT_ARUCO_BYTES), 5, 0);

const Dictionary DICT_4X4_50_DATA = Dictionary(Mat(50, (4*4 + 7)/8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1);
const Dictionary DICT_4X4_100_DATA = Dictionary(Mat(100, (4*4 + 7)/8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1);
const Dictionary DICT_4X4_250_DATA = Dictionary(Mat(250, (4*4 + 7)/8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 1);
const Dictionary DICT_4X4_1000_DATA = Dictionary(Mat(1000, (4*4 + 7)/8, CV_8UC4, (uchar*)DICT_4X4_1000_BYTES), 4, 0);

const Dictionary DICT_5X5_50_DATA = Dictionary(Mat(50, (5*5 + 7)/8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 3);
const Dictionary DICT_5X5_100_DATA = Dictionary(Mat(100, (5*5 + 7)/8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 3);
const Dictionary DICT_5X5_250_DATA = Dictionary(Mat(250, (5*5 + 7)/8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 2);
const Dictionary DICT_5X5_1000_DATA = Dictionary(Mat(1000, (5*5 + 7)/8, CV_8UC4, (uchar*)DICT_5X5_1000_BYTES), 5, 2);

const Dictionary DICT_6X6_50_DATA = Dictionary(Mat(50, (6*6 + 7)/8 ,CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 6);
const Dictionary DICT_6X6_100_DATA = Dictionary(Mat(100, (6*6 + 7)/8 ,CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 5);
const Dictionary DICT_6X6_250_DATA = Dictionary(Mat(250, (6*6 + 7)/8 ,CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 5);
const Dictionary DICT_6X6_1000_DATA = Dictionary(Mat(1000, (6*6 + 7)/8 ,CV_8UC4, (uchar*)DICT_6X6_1000_BYTES), 6, 4);

const Dictionary DICT_7X7_50_DATA = Dictionary(Mat(50, (7*7 + 7)/8 ,CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 9);
const Dictionary DICT_7X7_100_DATA = Dictionary(Mat(100, (7*7 + 7)/8 ,CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 8);
const Dictionary DICT_7X7_250_DATA = Dictionary(Mat(250, (7*7 + 7)/8 ,CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 8);
const Dictionary DICT_7X7_1000_DATA = Dictionary(Mat(1000, (7*7 + 7)/8 ,CV_8UC4, (uchar*)DICT_7X7_1000_BYTES), 7, 6);
S. Garrido's avatar
S. Garrido committed
294

295 296 297 298 299
const Dictionary DICT_APRILTAG_16h5_DATA = Dictionary(Mat(30, (4*4 + 7)/8, CV_8UC4, (uchar*)DICT_APRILTAG_16h5_BYTES), 4, 0);
const Dictionary DICT_APRILTAG_25h9_DATA = Dictionary(Mat(35, (5*5 + 7)/8, CV_8UC4, (uchar*)DICT_APRILTAG_25h9_BYTES), 5, 0);
const Dictionary DICT_APRILTAG_36h10_DATA = Dictionary(Mat(2320, (6*6 + 7)/8, CV_8UC4, (uchar*)DICT_APRILTAG_36h10_BYTES), 6, 0);
const Dictionary DICT_APRILTAG_36h11_DATA = Dictionary(Mat(587, (6*6 + 7)/8, CV_8UC4, (uchar*)DICT_APRILTAG_36h11_BYTES), 6, 0);

S. Garrido's avatar
S. Garrido committed
300

301
Ptr<Dictionary> getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME name) {
S. Garrido's avatar
S. Garrido committed
302 303 304
    switch(name) {

    case DICT_ARUCO_ORIGINAL:
305
        return makePtr<Dictionary>(DICT_ARUCO_DATA);
S. Garrido's avatar
S. Garrido committed
306 307

    case DICT_4X4_50:
308
        return makePtr<Dictionary>(DICT_4X4_50_DATA);
S. Garrido's avatar
S. Garrido committed
309
    case DICT_4X4_100:
310
        return makePtr<Dictionary>(DICT_4X4_100_DATA);
S. Garrido's avatar
S. Garrido committed
311
    case DICT_4X4_250:
312
        return makePtr<Dictionary>(DICT_4X4_250_DATA);
S. Garrido's avatar
S. Garrido committed
313
    case DICT_4X4_1000:
314
        return makePtr<Dictionary>(DICT_4X4_1000_DATA);
S. Garrido's avatar
S. Garrido committed
315 316

    case DICT_5X5_50:
317
        return makePtr<Dictionary>(DICT_5X5_50_DATA);
S. Garrido's avatar
S. Garrido committed
318
    case DICT_5X5_100:
319
        return makePtr<Dictionary>(DICT_5X5_100_DATA);
S. Garrido's avatar
S. Garrido committed
320
    case DICT_5X5_250:
321
        return makePtr<Dictionary>(DICT_5X5_250_DATA);
S. Garrido's avatar
S. Garrido committed
322
    case DICT_5X5_1000:
323
        return makePtr<Dictionary>(DICT_5X5_1000_DATA);
S. Garrido's avatar
S. Garrido committed
324 325

    case DICT_6X6_50:
326
        return makePtr<Dictionary>(DICT_6X6_50_DATA);
S. Garrido's avatar
S. Garrido committed
327
    case DICT_6X6_100:
328
        return makePtr<Dictionary>(DICT_6X6_100_DATA);
S. Garrido's avatar
S. Garrido committed
329
    case DICT_6X6_250:
330
        return makePtr<Dictionary>(DICT_6X6_250_DATA);
S. Garrido's avatar
S. Garrido committed
331
    case DICT_6X6_1000:
332
        return makePtr<Dictionary>(DICT_6X6_1000_DATA);
S. Garrido's avatar
S. Garrido committed
333 334

    case DICT_7X7_50:
335
        return makePtr<Dictionary>(DICT_7X7_50_DATA);
S. Garrido's avatar
S. Garrido committed
336
    case DICT_7X7_100:
337
        return makePtr<Dictionary>(DICT_7X7_100_DATA);
S. Garrido's avatar
S. Garrido committed
338
    case DICT_7X7_250:
339
        return makePtr<Dictionary>(DICT_7X7_250_DATA);
S. Garrido's avatar
S. Garrido committed
340
    case DICT_7X7_1000:
341
        return makePtr<Dictionary>(DICT_7X7_1000_DATA);
S. Garrido's avatar
S. Garrido committed
342

343 344 345 346 347 348 349 350 351
    case DICT_APRILTAG_16h5:
        return makePtr<Dictionary>(DICT_APRILTAG_16h5_DATA);
    case DICT_APRILTAG_25h9:
        return makePtr<Dictionary>(DICT_APRILTAG_25h9_DATA);
    case DICT_APRILTAG_36h10:
        return makePtr<Dictionary>(DICT_APRILTAG_36h10_DATA);
    case DICT_APRILTAG_36h11:
        return makePtr<Dictionary>(DICT_APRILTAG_36h11_DATA);

S. Garrido's avatar
S. Garrido committed
352
    }
353 354 355 356 357 358
    return makePtr<Dictionary>(DICT_4X4_50_DATA);
}


Ptr<Dictionary> getPredefinedDictionary(int dict) {
    return getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME(dict));
S. Garrido's avatar
S. Garrido committed
359 360 361 362 363 364
}


/**
 * @brief Generates a random marker Mat of size markerSize x markerSize
 */
365
static Mat _generateRandomMarker(int markerSize, RNG &rng) {
S. Garrido's avatar
S. Garrido committed
366 367 368
    Mat marker(markerSize, markerSize, CV_8UC1, Scalar::all(0));
    for(int i = 0; i < markerSize; i++) {
        for(int j = 0; j < markerSize; j++) {
369
            unsigned char bit = (unsigned char) (rng.uniform(0,2));
S. Garrido's avatar
S. Garrido committed
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
            marker.at< unsigned char >(i, j) = bit;
        }
    }
    return marker;
}

/**
 * @brief Calculate selfDistance of the codification of a marker Mat. Self distance is the Hamming
 * distance of the marker to itself in the other rotations.
 * See S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014.
 * "Automatic generation and detection of highly reliable fiducial markers under occlusion".
 * Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005
 */
static int _getSelfDistance(const Mat &marker) {
    Mat bytes = Dictionary::getByteListFromBits(marker);
    int minHamming = (int)marker.total() + 1;
386
    for(int r = 1; r < 4; r++) {
387
        int currentHamming = cv::hal::normHamming(bytes.ptr(), bytes.ptr() + bytes.cols*r, bytes.cols);
S. Garrido's avatar
S. Garrido committed
388 389 390 391 392 393 394
        if(currentHamming < minHamming) minHamming = currentHamming;
    }
    return minHamming;
}

/**
 */
395
Ptr<Dictionary> generateCustomDictionary(int nMarkers, int markerSize,
396 397
                                         const Ptr<Dictionary> &baseDictionary, int randomSeed) {
    RNG rng((uint64)(randomSeed));
S. Garrido's avatar
S. Garrido committed
398

399 400
    Ptr<Dictionary> out = makePtr<Dictionary>();
    out->markerSize = markerSize;
S. Garrido's avatar
S. Garrido committed
401 402 403 404 405 406 407 408 409

    // theoretical maximum intermarker distance
    // See S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014.
    // "Automatic generation and detection of highly reliable fiducial markers under occlusion".
    // Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005
    int C = (int)std::floor(float(markerSize * markerSize) / 4.f);
    int tau = 2 * (int)std::floor(float(C) * 4.f / 3.f);

    // if baseDictionary is provided, calculate its intermarker distance
410 411 412
    if(baseDictionary->bytesList.rows > 0) {
        CV_Assert(baseDictionary->markerSize == markerSize);
        out->bytesList = baseDictionary->bytesList.clone();
S. Garrido's avatar
S. Garrido committed
413 414

        int minDistance = markerSize * markerSize + 1;
415 416
        for(int i = 0; i < out->bytesList.rows; i++) {
            Mat markerBytes = out->bytesList.rowRange(i, i + 1);
S. Garrido's avatar
S. Garrido committed
417 418
            Mat markerBits = Dictionary::getBitsFromByteList(markerBytes, markerSize);
            minDistance = min(minDistance, _getSelfDistance(markerBits));
419 420
            for(int j = i + 1; j < out->bytesList.rows; j++) {
                minDistance = min(minDistance, out->getDistanceToId(markerBits, j));
S. Garrido's avatar
S. Garrido committed
421 422 423 424 425 426 427 428 429 430 431 432 433
            }
        }
        tau = minDistance;
    }

    // current best option
    int bestTau = 0;
    Mat bestMarker;

    // after these number of unproductive iterations, the best option is accepted
    const int maxUnproductiveIterations = 5000;
    int unproductiveIterations = 0;

434
    while(out->bytesList.rows < nMarkers) {
435
        Mat currentMarker = _generateRandomMarker(markerSize, rng);
S. Garrido's avatar
S. Garrido committed
436 437 438 439 440 441 442

        int selfDistance = _getSelfDistance(currentMarker);
        int minDistance = selfDistance;

        // if self distance is better or equal than current best option, calculate distance
        // to previous accepted markers
        if(selfDistance >= bestTau) {
443 444
            for(int i = 0; i < out->bytesList.rows; i++) {
                int currentDistance = out->getDistanceToId(currentMarker, i);
S. Garrido's avatar
S. Garrido committed
445 446 447 448 449 450 451 452 453 454 455 456
                minDistance = min(currentDistance, minDistance);
                if(minDistance <= bestTau) {
                    break;
                }
            }
        }

        // if distance is high enough, accept the marker
        if(minDistance >= tau) {
            unproductiveIterations = 0;
            bestTau = 0;
            Mat bytes = Dictionary::getByteListFromBits(currentMarker);
457
            out->bytesList.push_back(bytes);
S. Garrido's avatar
S. Garrido committed
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
        } else {
            unproductiveIterations++;

            // if distance is not enough, but is better than the current best option
            if(minDistance > bestTau) {
                bestTau = minDistance;
                bestMarker = currentMarker;
            }

            // if number of unproductive iterarions has been reached, accept the current best option
            if(unproductiveIterations == maxUnproductiveIterations) {
                unproductiveIterations = 0;
                tau = bestTau;
                bestTau = 0;
                Mat bytes = Dictionary::getByteListFromBits(bestMarker);
473
                out->bytesList.push_back(bytes);
S. Garrido's avatar
S. Garrido committed
474 475 476 477 478
            }
        }
    }

    // update the maximum number of correction bits for the generated dictionary
479
    out->maxCorrectionBits = (tau - 1) / 2;
S. Garrido's avatar
S. Garrido committed
480 481 482

    return out;
}
483 484 485 486


/**
 */
487
Ptr<Dictionary> generateCustomDictionary(int nMarkers, int markerSize, int randomSeed) {
488
    Ptr<Dictionary> baseDictionary = makePtr<Dictionary>();
489
    return generateCustomDictionary(nMarkers, markerSize, baseDictionary, randomSeed);
490 491 492
}


S. Garrido's avatar
S. Garrido committed
493 494
}
}