diff --git a/README.md b/README.md index f2e51ae..601a51d 100755 --- a/README.md +++ b/README.md @@ -28,6 +28,15 @@ tested with version 1.3.0. which is included in /thirdparty. +#### 5. Dataset Description: +The dataset used is [TUM dataset](https://vision.in.tum.de/data/datasets/mono-dataset). For photometric calibration, it contains `images.zip`, `pcalib.txt`, `times.txt` and `camera.txt` files. +- `camera.txt` contains camera parameters (as per the camera model) normalized to the video frame dimensions. +- `pcalib.txt` is the output of gamma calibration and contains inverse irradiance values [0, 255]. +- `times.txt` contains 3 columns: `video_frame_number`, `video_frame_timestamp` (absolute/relative time), exposure_values (1/f). For `exposure_values` TUM_dataset has used sensors but one can use another workaround; capture a static scene without camera motion through various discrete exposure values and +set the exposure values in the `times.txt` `exposure_values` column (generated the files and tested in custom dataset). + +**NOTE:** Gamma calibration requires `times.txt`, `camera.txt` and `images.zip` and outputs `pcalib.txt` +while Vignette calibration requires `camera.txt`, `images.zip` and `pcalib.txt` and outputs `vignette.png` # Usage: C++ code diff --git a/src/BenchmarkDatasetReader.h b/src/BenchmarkDatasetReader.h index 6dafdc1..67e9ab3 100755 --- a/src/BenchmarkDatasetReader.h +++ b/src/BenchmarkDatasetReader.h @@ -249,7 +249,7 @@ class DatasetReader if(!isZipped) { // CHANGE FOR ZIP FILE - return cv::imread(files[id],CV_LOAD_IMAGE_GRAYSCALE); + return cv::imread(files[id],cv::IMREAD_GRAYSCALE); } else { @@ -271,7 +271,7 @@ class DatasetReader exit(1); } } - return cv::imdecode(cv::Mat(readbytes,1,CV_8U, databuffer), CV_LOAD_IMAGE_GRAYSCALE); + return cv::imdecode(cv::Mat(readbytes,1,CV_8U, databuffer),cv::IMREAD_GRAYSCALE); } } @@ -343,4 +343,3 @@ class DatasetReader float* internalTempBuffer; }; - diff --git a/src/PhotometricUndistorter.cpp b/src/PhotometricUndistorter.cpp index 969b9da..01a44b6 100755 --- a/src/PhotometricUndistorter.cpp +++ b/src/PhotometricUndistorter.cpp @@ -117,7 +117,7 @@ PhotometricUndistorter::PhotometricUndistorter( printf("Reading Vignette Image from %s\n",vignetteImage.c_str()); - cv::Mat vignetteMat = cv::imread(vignetteImage.c_str(), CV_LOAD_IMAGE_UNCHANGED); + cv::Mat vignetteMat = cv::imread(vignetteImage.c_str(), cv::IMREAD_UNCHANGED); vignetteMap = new float[w*h]; vignetteMapInv = new float[w*h]; if(vignetteMat.rows != h || vignetteMat.cols != w) @@ -210,6 +210,3 @@ void PhotometricUndistorter::unMapImage( for(int i=0;i arr[j + 1]) + swap(&arr[j], &arr[j + 1]); +} Eigen::Vector2d rmse(double* G, double* E, std::vector &exposureVec, std::vector &dataVec, int wh) { @@ -345,9 +361,21 @@ int main( int argc, char** argv ) plotE(E,w,h, buf); } + double max_G = 0; + for(int i = 0; i < 256; i++) + { + if(max_G < G[i]) + { + max_G = G[i]; + } + } + std::cout << " Max G = " << max_G << std::endl; + + bubbleSort(G); // rescale such that maximum response is 255 (fairly arbitrary choice). - double rescaleFactor=255.0 / G[255]; + // double rescaleFactor=255.0 / G[255]; + double rescaleFactor=255.0 / max_G; for(int i=0;i #include #include #include +#include +#include // reads interpolated element from a uchar* array @@ -187,7 +190,6 @@ int main( int argc, char** argv ) { for(int i=2; igetUndistorter()->getK_rect(); w_out = reader->getUndistorter()->getOutputDims()[0]; h_out = reader->getUndistorter()->getOutputDims()[1]; aruco::MarkerDetector MDetector; + std::string path = argv[1]; + UndistorterFOV* undistorter = new UndistorterFOV((path+"camera.txt").c_str()); + // Eigen::Matrix3f Krect = undistorter->getK_rect(); + // float Omega = undistorter->getOmega(); + + cv::Mat camMatrix; + cv::Mat_ distCoeff(4, 1); + cv::eigen2cv(undistorter->getK_rect(), camMatrix); + distCoeff<< undistorter->getOmega(), 0, 0, 0; std::vector images; std::vector p2imgX; @@ -236,7 +246,7 @@ int main( int argc, char** argv ) cv::Mat(h_out, w_out, CV_32F, img->image).convertTo(InImage, CV_8U, 1, 0); delete img; - MDetector.detect(InImage,Markers); + MDetector.detect(InImage,Markers, camMatrix, distCoeff); if(Markers.size() != 1) continue; std::vector ptsP; @@ -321,7 +331,7 @@ int main( int argc, char** argv ) int v_dT = plane2imgY[idxT]+0.5; if(u_dS>=0 && v_dS >=0 && u_dS=0 && v_dT >=0 && u_dT=0 && v_dS >=0 && u_dS=0 && v_dT >=0 && u_dT in many files to avoid compilation errors in some old systems + +Version 3.0.1 + - aruco_calibration_fromimage chaged so that it now read files from a dicectory +Version 3.0.0 + - Removed all funcions marked as deprecated in version 2.x.x + - Removed refinement methods, only subpixel is now available via MarkerDetector::Params::_doSubPixCornerRefinement + - MarkerDetector::Params::_maxSize removed + - MarkerDetector::Params::_doErosion added for enclosed markers + - MarkerDetector::Params::_thresMethod removed, always will use adaptive threshold + +Version 2.0.20 (Luis Diaz Collaboration) + - clang + - cmake improved + - many bugs corrected + - improved detection of enclosed markers + - improved aruco_test app with more options to debug possible misdetections +Version 2.0.19 + - corrected bug in utils/aruco_print_marker.cpp +Version 2.0.18 + - added support for custom dictionaries + - added ChiliTags dictionary + - utils/aruco_print_marker added option to print a marker with enclosed squareg to allow subpixel refinement +Version 2.0.17 + - Bug in hamming distances corrected + - Bug in dictionary.cpp with the creation of +Version 2.0.16 + - corrected bug in MSVC compilation due to linking problems + +Version 2.0.15 + - corrected bug in MarkerDetector::detectRectangles. The check isContourConvex was removed at some point and has now been set again +Version 2.0.14 + - corrected bug in dictionary.cpp with ARUCO_MIP_25h7 +Version 2.0.13 + - corrected bug in MSVC +Version 2.0.12 + - bug corrected in CameraParameters::resize + +Version 2.0.11 + - improved marker::draw to cope better with large images + - possible bug in rectangle detection found. +Version 2.0.10 + - bug corrected in aruco_print_dictionary + - bug corrected in markermap ( if ( it!=m_meters.end()) ) + +Version 2.0.9 + - bug corrected in MarkerPoseTracker relative to the refence system. The reference system of the markers computed using the Marker::calculateExtrinsics was different from the ne of MarkerPoseTracker. Now, the one + from MarkerPoseTRacker is the wining, since it is the same as the twin library markermapper. + Be careful since there might be a incompatiblilty issue in the reference system with preios data. +Version 2.0.8 + - bugs corrected in Marker::toStream and Marker::fromStream + - proposed solution to the ambiguity problem in MarkerPoseTracker. See posetracker.h for details +Version 2.0.7 + - removed from levmarq.h and included "ar_omp.h" instead + - M_PI substituted by 3.1415.... + - added MarkerMap::calculateExtrinsics + - Conditional use of eigen3. It is only used if compiled against opencv2. In Opencv3 solvePnp works properly +Version 2.0.6 + - Many warnings removed +Version 2.0.5 + - Corrected bug in MarkerMapPoseTracker + +VERSION 2.0.4 + - C++11 code + - Markers ids are 64 bits (8x8) + - Predefined set of dictionaries. It includes ARUCO, ARUCOHRM, AprilTags, ArToolKit and ARTAG + - Added marker tracker. Avoids the problem of ambuguity? Hopefully + - Chromatic mask removed from libary. + - Removed Board and BoardDetector. This concepts are now expanded to the idea of MarkerMap and managed in classes MarkerInfo, MarkerMap and MarkerSetPoseTracker. + see utils_markerset for more examples. + This is to allow compatibility with the twin libary aruco_markermapper +VERSION 1.3.1 + - Added MarkerDetector::Param class to encapsulate all of them. + - Added calibration tool in utils/aruco_calibrate that uses aruco chessboards + - removed MarkerDetector::setDesiredSpeed and MarkerDetector::getDesiredSpeed + - removed HARRIS corner method from markerdetector and locked corners stuff + - Add SVMMarkers class to detect markers with generic patterns using a trained SVM model. + - Code refactoring to encapsulate the detectors into classes. Added the class MarkerLabeler and its factory creator + - in aruco_create_board, random seed is now manually set if, or 0 if not. This creates by default always the same board +VERSION 1.3 + - Compatibility with OpenCV 3 + - In Marker Detector: + - Paralelization using OpenMP + - Marked as deprecated enableErosion and pyrDown + - Added functionality for markers with "locked corners". We refer to marker whose corners are connected + either to another marker (formaing a chessboard pattern), or to another black square. In this mode, + the use of subcorner refinement methods is expected to be more precise. See enableLockedCornersMethod() + - Added funcionality to search for the first threshold parameter simultaneously in several + values. The process is parallelized in multiple threads. See setMultiThresholdSearch() + - In HRM markers: + - Speed up marker identification process + - Improve performance of dictionary generation process. + - Added LICENSE file + +VERSION 1.2.5 + - New type of markers: highly reliable markers (hrm) including utils to use them + - Added chromaticmask class to create occlusion mask based on chromatic boards + - Added watermark with the id to the markers to allow easy identification of printed markers + - Now, by default, setYPerpendicular=false. So, Z is now pointing out of the board. + - Added a reprjection test in boardetector see BoardDetector::set_repj_err_thres() and BoardDetector::get_repj_err_thres () + - Added support for 5 distortion parameters in CameraParameters class + - Removed experimental code for removing the deformation that occurs when a marker is in a cylinder + - Omp support in linux (in markerdetector) + - Added an static function in BoardDetector::detect to do everything in a single line. + - New BoardConfiguration constructor receving a path with the configuration to be read. + - Revised opencv #includes to include just the necessary modules + - Added aruco_calibration.cpp to perform camera calibration using aruco boards + - Changes in LINES refinement to perform undistortion automatically + - Method setYPerperdicular in BoardDetector changed to setYPerpendicular (spelling error) + - Added getWarpSize() and setWarpSize methods in MarkerDetector to allow changing the canonical image size + - Bug fixed in aruco_create_board + +VERSION 1.2.4 + - Bugs fixed for MSVC2010 + +VERSION 1.2.3 + - Changes in boardconfiguration and boardetector to allow arbitrary located markers. No API changes involved, except for the config files that have changed their format. + - Changes in arucofidmarkers to allow the creation of chessboard like boards (FiducidalMarkers::createBoardImage_ChessBoard). + Added the corresponding change in aruco_create_board to support such feature + - Added experimental code for removing the deformation that occurs when a marker is in a cylinder + - Added the corner refinement method LINES. It is based on intersecting the lines of the marker's sides using the contour points. + We believe this is the best refinement method of the library so far. + - Added functionality in aruco::BoarDetector to perform the whole detection process in it if desired + - Changed aruco_test_board to use the new functionality described above + - Changed old way of obtaining extrinsics FindExtrinsicParam2 for the new one solvePnp in BoardDetector and in Marker + +VERSION 1.1.0 + - Ogre integration ( Board::OgreGetPoseParameters and Marker::OgreGetPoseParameters). + - Changes to make it compile with MSVC 2010 + - Remove getopt.h dependency. Command line are simpler now + - MarkerDetector: Externalization of the marker detector to allow using user-defined markers. The function setMakerDetectorFunction allows to define this function + - Added class FiducidalMarkers to detect the original aruco markers + - MarkerDetector: function glGetProjectionMatrix is moved to the CameraParameters class. Sorry, but it is more logical. + - MarkerDetector: Clear separation between the detection phases into separated functions that can be externally called (thresHold and detectRectangles) for more sohpisticated user needs + - MarkerDetector: new corner refinement method based on harris detector. Also, added the possibility of not refining corners at all. + - Added an option to work on a reduced version of the images (pyrDown). + - Changes the adaptive threshold method. Now we employ the MEAN. As a consequence, the process is faster and do not depen on the window size. + - Initial tests with android + - Bugs fixed + +VERSION 1.0.0 + - New names for the main classes Marker,MarkerDector... etc. Sorry, but I needed to clear up things. It wont be difficult to adapt. + - A new class for the camera parameters. It can be readed from the calibration.cpp application in OpenCv2.2 + - Refactorization of code to make it more maintainable + - Better support for Windows (Cmake) and OpenGL + - Improved documentation. Windows users should read the README file that explains how to build the library + - A new class for drawing markers and boards in opencv images + - A couple of new very simple examples to teach the use of the library(aruco_simple and aruco_simple_board) + +VERSION 0.9.5 + Added support for Boards + Added cmake support + Bugs fixed diff --git a/thirdparty/aruco-3.1.12/LICENSE b/thirdparty/aruco-3.1.12/LICENSE new file mode 100644 index 0000000..ff5c1fc --- /dev/null +++ b/thirdparty/aruco-3.1.12/LICENSE @@ -0,0 +1,8 @@ +ArUco is released under a GPLv3 license (see License-gpl.txt). + +If you use ArUco in an academic work, please cite the most relevant publication associated by visiting: +https://www.uco.es/investiga/grupos/ava/node/26 + + + + diff --git a/thirdparty/aruco-3.1.12/License-gpl.txt b/thirdparty/aruco-3.1.12/License-gpl.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/thirdparty/aruco-3.1.12/License-gpl.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/thirdparty/aruco-3.1.12/README b/thirdparty/aruco-3.1.12/README new file mode 100644 index 0000000..ac8eed2 --- /dev/null +++ b/thirdparty/aruco-3.1.12/README @@ -0,0 +1,3 @@ +fsdafsdsadda +For full info about the library please go to +https://www.uco.es/investiga/grupos/ava/node/26 diff --git a/thirdparty/aruco-3.1.12/cmake/aruco-uninstalled.pc.in b/thirdparty/aruco-3.1.12/cmake/aruco-uninstalled.pc.in new file mode 100644 index 0000000..991af56 --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/aruco-uninstalled.pc.in @@ -0,0 +1,10 @@ + +libdir=@CMAKE_CURRENT_BINARY_DIR@ +includedir=@CMAKE_CURRENT_SOURCE_DIR@/src + +Name: @PROJECT_NAME@ +Description: ArUco: a minimal library for Augmented Reality applications based on OpenCv, uninstalled Version. +Version: @PROJECT_VERSION@ +Requires: opencv >= 2.1.0 +Libs: -L${libdir} -laruco +Cflags: -I${includedir} diff --git a/thirdparty/aruco-3.1.12/cmake/aruco.pc.in b/thirdparty/aruco-3.1.12/cmake/aruco.pc.in new file mode 100644 index 0000000..b0a1edb --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/aruco.pc.in @@ -0,0 +1,16 @@ +# pkg-config file for the library + +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: @PROJECT_NAME@ +Description: ARUCO Library for marker detection +Version: @PROJECT_VERSION@ +Requires: opencv >= 2.1.0 +Conflicts: +Libs: -L${libdir} -l@PROJECT_NAME@ +Cflags: -I${includedir}/@PROJECT_NAME@ -I${includedir} + + diff --git a/thirdparty/aruco-3.1.12/cmake/cmake_uninstall.cmake.in b/thirdparty/aruco-3.1.12/cmake/cmake_uninstall.cmake.in new file mode 100644 index 0000000..81482da --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,28 @@ +# ----------------------------------------------- +# File that provides "make uninstall" target +# We use the file 'install_manifest.txt' +# ----------------------------------------------- +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") +# IF(EXISTS "$ENV{DESTDIR}${file}") +# EXEC_PROGRAM( +# "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" +# OUTPUT_VARIABLE rm_out +# RETURN_VALUE rm_retval +# ) + EXECUTE_PROCESS(COMMAND rm $ENV{DESTDIR}${file}) +# IF(NOT "${rm_retval}" STREQUAL 0) +# MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") +# ENDIF(NOT "${rm_retval}" STREQUAL 0) +# ELSE(EXISTS "$ENV{DESTDIR}${file}") +# MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") +# ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) + + diff --git a/thirdparty/aruco-3.1.12/cmake/compilerOptions.cmake b/thirdparty/aruco-3.1.12/cmake/compilerOptions.cmake new file mode 100644 index 0000000..f4da5e4 --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/compilerOptions.cmake @@ -0,0 +1,32 @@ + + +IF(NOT TARGET_PROCESSOR ) + SET(TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) +ENDIF() + +IF(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang") ) + + if(${TARGET_PROCESSOR} MATCHES armv7l) # In ARM_COrtex8 with neon, enalble vectorized operations + set(GENERAL_FLAGS "${GENERAL_FLAGS} -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=hard ") + SET(ADD_LINK_LIBS -latomic)#for raspian, Opencv has not included it + endif() + if(${TARGET_PROCESSOR} MATCHES armv6l) # In ARM_COrtex8 with neon, enalble vectorized operations + set(GENERAL_FLAGS "${GENERAL_FLAGS} -mabi=aapcs-linux -marm -march=armv6 -mfloat-abi=hard -mfp16-format=none -mfpu=vfp -mlittle-endian -mpic-data-is-text-relative -mrestrict-it -msched-prolog -mstructure-size-boundary=0x20 -mtp=auto -mtls-dialect=gnu -munaligned-access -mvectorize-with-neon-quad") + SET(ADD_LINK_LIBS -latomic)#for raspian, Opencv has not included it + endif() + + + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} -O3 -g0 -DNDEBUG") + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} -O0 -g3 -DDEBUG -D_DEBUG -DPRINT_DEBUG_MESSAGES") + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} -O1 -g3 -D_DEBUG -DDEBUG -DPRINT_DEBUG_MESSAGES") + + +ELSE() # MSVC + ADD_DEFINITIONS(-DNOMINMAX) +ENDIF()#END OF COMPILER SPECIFIC OPTIONS + + + +SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EXTRA_EXE_LINKER_FLAGS} ${ADD_LINK_LIBS}") +SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${EXTRA_EXE_LINKER_FLAGS_RELEASE}") +SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${EXTRA_EXE_LINKER_FLAGS_DEBUG}") diff --git a/thirdparty/aruco-3.1.12/cmake/config.cmake.in b/thirdparty/aruco-3.1.12/cmake/config.cmake.in new file mode 100644 index 0000000..0857e25 --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/config.cmake.in @@ -0,0 +1,36 @@ +# =================================================================================== +# @PROJECT_NAME@ CMake configuration file +# +# ** File generated automatically, do not modify ** +# +# Usage from an external project: +# In your CMakeLists.txt, add these lines: +# +# FIND_PACKAGE(@PROJECT_NAME@ REQUIRED ) +# TARGET_LINK_LIBRARIES(MY_TARGET_NAME ${@PROJECT_NAME@_LIBS}) +# +# This file will define the following variables: +# - @PROJECT_NAME@_LIBS : The list of libraries to links against. +# - @PROJECT_NAME@_LIB_DIR : The directory where lib files are. Calling LINK_DIRECTORIES +# with this path is NOT needed. +# - @PROJECT_NAME@_VERSION : The version of this PROJECT_NAME build. Example: "1.2.0" +# - @PROJECT_NAME@_VERSION_MAJOR : Major version part of VERSION. Example: "1" +# - @PROJECT_NAME@_VERSION_MINOR : Minor version part of VERSION. Example: "2" +# - @PROJECT_NAME@_VERSION_PATCH : Patch version part of VERSION. Example: "0" +# +# =================================================================================== +INCLUDE_DIRECTORIES("@CMAKE_INSTALL_PREFIX@/include") +INCLUDE_DIRECTORIES("@CMAKE_INSTALL_PREFIX@/include/@PROJECT_NAME@") +SET(@PROJECT_NAME@_INCLUDE_DIRS "@CMAKE_INSTALL_PREFIX@/include") + +LINK_DIRECTORIES("@CMAKE_INSTALL_PREFIX@/lib") +SET(@PROJECT_NAME@_LIB_DIR "@CMAKE_INSTALL_PREFIX@/lib") + +SET(@PROJECT_NAME@_LIBS @OpenCV_LIBS@ @PROJECT_NAME@@PROJECT_DLLVERSION@) + +SET(@PROJECT_NAME@_FOUND YES) +SET(@PROJECT_NAME@_FOUND "YES") +SET(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) +SET(@PROJECT_NAME@_VERSION_MAJOR @PROJECT_VERSION_MAJOR@) +SET(@PROJECT_NAME@_VERSION_MINOR @PROJECT_VERSION_MINOR@) +SET(@PROJECT_NAME@_VERSION_PATCH @PROJECT_VERSION_PATCH@) diff --git a/thirdparty/aruco-3.1.12/cmake/cpack.cmake b/thirdparty/aruco-3.1.12/cmake/cpack.cmake new file mode 100644 index 0000000..1f2ffaa --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/cpack.cmake @@ -0,0 +1,17 @@ +set(CPACK_PACKAGE_DESCRIPTION "ArUCO Package") +set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +set(CPACK_PACKAGE_VENDOR "University of Cordoba") +set(CPACK_PACKAGE_CONTACT "rmsalinas@uco.es") +set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +SET(CPACK_PACKAGE_DESCRIPTION "ArUCO library and applications") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ArUCO is a library for fiducial tag detection") + +SET(CPACK_GENERATOR "DEB") +SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "i386") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS " ") +SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") + +include (CPack) +#In Ubuntu 18 requires sudo apt-get install libopencv-highgui3.2 libopencv-calib3d3.2 libopencv-imgproc3.2 libqt5gui5 libqt5opengl5 diff --git a/thirdparty/aruco-3.1.12/cmake/findDependencies.cmake b/thirdparty/aruco-3.1.12/cmake/findDependencies.cmake new file mode 100644 index 0000000..cde7898 --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/findDependencies.cmake @@ -0,0 +1,48 @@ +# ---------------------------------------------------------------------------- +# Find Dependencies +# ---------------------------------------------------------------------------- + +find_package(OpenCV REQUIRED) +include_directories( ${OpenCV_INCLUDE_DIRS} ) + +message(STATUS "OpenCV found, version: ${OpenCV_VERSION} in dir ${OpenCV_INCLUDE_DIRS}") + +if(NOT USE_OWN_EIGEN3) + message(WARNING "If you do not want to install Eigen you can turn on the option USE_OWN_EIGEN3") + find_package( Eigen3 REQUIRED ) +else() + set(EIGEN3_INCLUDE_DIR "3rdparty/eigen3") +endif() +include_directories( ${EIGEN3_INCLUDE_DIR} ) + + + + +if(EXISTS ${GLUT_PATH}) + include_directories(${GLUT_PATH}/include) + set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${GLUT_PATH}/lib) +endif() + +##LOOK FOR OPENGL AND GLUT +#FIND OPENGL LIBRARY. In Linux, there is no need since it is included +if(NOT ANDROID_CREATION) + if(BUILD_GLSAMPLES) + find_package(OpenGL) + find_package(GLUT)#standard package + message(STATUS "GLUT_FOUND=${GLUT_FOUND} OPENGL_gl_LIBRARY=${OPENGL_gl_LIBRARY} GLUT_HEADER=${GLUT_HEADER}") + endif() + + if(NOT GLUT_FOUND) #else, freeglut + find_library(GLUT_glut_LIBRARY NAMES freeglut) + message(STATUS "GLUT_glut_LIBRARY=${GLUT_glut_LIBRARY}") + endif() + + if ( (NOT GLUT_glut_LIBRARY AND NOT GLUT_FOUND) OR NOT OPENGL_gl_LIBRARY) + set(GL_FOUND "NO") + else() + set(GL_FOUND "YES") + set (OPENGL_LIBS ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUT_glut_LIBRARY}) + endif() +endif() + + diff --git a/thirdparty/aruco-3.1.12/cmake/installOptions.cmake b/thirdparty/aruco-3.1.12/cmake/installOptions.cmake new file mode 100644 index 0000000..221396d --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/installOptions.cmake @@ -0,0 +1,10 @@ +# pkg-config +configure_file(${PROJECT_SOURCE_DIR}/cmake/aruco.pc.in aruco.pc @ONLY) +configure_file(${PROJECT_SOURCE_DIR}/cmake/aruco-uninstalled.pc.in aruco-uninstalled.pc @ONLY) +install(FILES "${PROJECT_BINARY_DIR}/aruco-uninstalled.pc" "${PROJECT_BINARY_DIR}/aruco.pc" DESTINATION lib/pkgconfig) + +CONFIGURE_FILE("${PROJECT_SOURCE_DIR}/cmake/config.cmake.in" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake") +INSTALL(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" DESTINATION share/${PROJECT_NAME} ) +if(WIN32) + SET(PROJECT_DLLVERSION "${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}") +ENDIF() diff --git a/thirdparty/aruco-3.1.12/cmake/options.cmake b/thirdparty/aruco-3.1.12/cmake/options.cmake new file mode 100644 index 0000000..b889956 --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/options.cmake @@ -0,0 +1,53 @@ +#------------------------------------------------------ +# Build type +#------------------------------------------------------ + +if(NOT CMAKE_BUILD_TYPE ) + set( CMAKE_BUILD_TYPE "Debug" ) +endif() + +#------------------------------------------------------ +# Lib Names and Dirs +#------------------------------------------------------ + +if(WIN32) + # Postfix of DLLs: + # Postfix of DLLs: + set(PROJECT_DLLVERSION "${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}") + set(RUNTIME_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin CACHE PATH "Directory for dlls and binaries") + set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin CACHE PATH "Directory for binaries") + set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin CACHE PATH "Directory for dlls") + +else() + # Postfix of so's: + set(PROJECT_DLLVERSION) +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_INSTALL_PREFIX}/lib/cmake/ /usr/lib/cmake) +endif() + +option(USE_OWN_EIGEN3 "Set to OFF to use a standard eigen3 version" ON) +option(BUILD_UTILS "Set to OFF to not compile utils " ON) +option(BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON) +option(BUILD_GLSAMPLES "Set to OFF to build static libraries" OFF) +option(USE_TIMERS "Set to OFF to build static libraries" OFF) +option(BUILD_DEBPACKAGE "Set to ON to use cpack" OFF) +option(ARUCO_DEVINSTALL "Set to OFF to disable source installation" ON) + iF(USE_TIMERS) +add_definitions(-DUSE_TIMERS) +ENDIF() +# ---------------------------------------------------------------------------- +# PROJECT CONFIGURATION +# force some variables that could be defined in the command line to be written to cache +# ---------------------------------------------------------------------------- +option(INSTALL_DOC "Set to ON to build/install Documentation" OFF) +if (INSTALL_DOC) + find_package(Doxygen REQUIRED) + message( STATUS "INSTALL_DOC: ${INSTALL_DOC} ") + include("${PROJECT_SOURCE_DIR}/cmake/generateDoc.cmake") + generate_documentation(${PROJECT_SOURCE_DIR}/cmake/dox.in) +endif() + +# ---------------------------------------------------------------------------- +# Uninstall target, for "make uninstall" +# ---------------------------------------------------------------------------- +configure_file("${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) +add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake") diff --git a/thirdparty/aruco-3.1.12/cmake/printInfo.cmake b/thirdparty/aruco-3.1.12/cmake/printInfo.cmake new file mode 100644 index 0000000..cb807da --- /dev/null +++ b/thirdparty/aruco-3.1.12/cmake/printInfo.cmake @@ -0,0 +1,47 @@ +# ---------------------------------------------------------------------------- +# display status message for important variables +# ---------------------------------------------------------------------------- +message( STATUS ) +message( STATUS "-------------------------------------------------------------------------------" ) +message( STATUS "General configuration for ${PROJECT_NAME} ${PROJECT_VERSION}") +message( STATUS "-------------------------------------------------------------------------------" ) +message( STATUS ) +message( STATUS "Compiler:" "${CMAKE_COMPILER}" "${CMAKE_CXX_COMPILER}") +message( STATUS "C++ flags (Release): ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") +message( STATUS "C++ flags (Debug): ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG}") +message( STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") +message( STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") + +message( STATUS ) +message( STATUS "TARGET_PROCESSOR = ${TARGET_PROCESSOR}" ) +message( STATUS "BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}" ) +message( STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}" ) +message( STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}" ) +message( STATUS ) + +message( STATUS "BUILD_SVM = ${BUILD_SVM}" ) +message( STATUS "BUILD_UTILS = ${BUILD_UTILS}" ) +message( STATUS "BUILD_TESTS = ${BUILD_TESTS}" ) +message( STATUS "BUILD_GLSAMPLES = ${BUILD_GLSAMPLES}" ) +message( STATUS "USE_OWN_EIGEN3=${USE_OWN_EIGEN3}") +message( STATUS "OpenCV_DIR=${OpenCV_DIR}") + +message( STATUS ) +message( STATUS "--------------------------- Documentation -----------------------------" ) +message( STATUS ) +message( STATUS "INSTALL_DOC = ${INSTALL_DOC}" ) +message( STATUS "USE_LATEX = ${USE_LATEX}" ) +message( STATUS "USE_DOT = ${USE_DOT}" ) +message( STATUS "USE_CHM = ${USE_CHM}" ) +message( STATUS ) +message( STATUS "FOUND OPENGL=${GL_FOUND} LIBS=${OPENGL_LIBS}") +message( STATUS "OpenCV_LIB_DIR=${OpenCV_LIB_DIR} OpenCV_INCLUDE_DIRS=${OpenCV_INCLUDE_DIRS}") +message( STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}") +message( STATUS "EIGEN3_INCLUDE_DIR=${EIGEN3_INCLUDE_DIR}") +message( STATUS "USE_TIMERS=${USE_TIMERS}") + + +message( STATUS ) +message( STATUS ) +message( STATUS "Change a value with: cmake -D=" ) +message( STATUS ) diff --git a/thirdparty/aruco-3.1.12/src/CMakeLists.txt b/thirdparty/aruco-3.1.12/src/CMakeLists.txt new file mode 100644 index 0000000..dd1272a --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/CMakeLists.txt @@ -0,0 +1,60 @@ +SET (LIBNAME ${EXTRALIBNAME}aruco) +include_directories(.) + + +SET(sources + cameraparameters.cpp debug.cpp dictionary.cpp ippe.cpp markerdetector.cpp markerlabeler.cpp posetracker.cpp + cvdrawingutils.cpp dictionary_based.cpp marker.cpp markerdetector_impl.cpp markermap.cpp fractaldetector.cpp + ) +SET(headers + aruco_cvversioning.h cameraparameters.h dictionary_based.h ippe.h markerdetector_impl.h markermap.h timers.h + aruco_export.h cvdrawingutils.h dictionary.h levmarq.h marker.h picoflann.h + aruco.h debug.h markerdetector.h markerlabeler.h posetracker.h fractaldetector.h + ) + set(fractal_sources + fractallabelers/fractalposetracker.cpp + fractallabelers/fractalmarkerset.cpp + fractallabelers/fractalmarker.cpp + fractallabelers/fractallabeler.cpp + ) +set(fractal_headers + fractallabelers/fractalposetracker.h + fractallabelers/fractalmarkerset.h + fractallabelers/fractalmarker.h + fractallabelers/fractallabeler.h + ) + +set(dcf_sources + dcf/dcfmarkermaptracker.cpp dcf/dcfmarkertracker.cpp dcf/dcf_utils.cpp dcf/trackerimpl.cpp ) +set(dcf_headers + dcf/dcfmarkermaptracker.h dcf/dcfmarkertracker.h dcf/dcf_utils.h dcf/trackerimpl.h) + +add_library(${LIBNAME} ${sources} ${headers} ${fractal_sources} ${fractal_headers} ${dcf_sources} ${dcf_headers}) + +set_target_properties(${LIBNAME} PROPERTIES # create *nix style library versions + symbolic links + DEFINE_SYMBOL ARUCO_DSO_EXPORTS + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_SOVERSION} + CLEAN_DIRECT_OUTPUT 1 # allow creating static and shared libs without conflicts + OUTPUT_NAME "${LIBNAME}${PROJECT_DLLVERSION}" # avoid conflicts between library and binary target names +) + +target_link_libraries(${LIBNAME} PUBLIC opencv_core) +IF(BUILD_SVM) +add_definitions(USE_SVM_LABELER) + target_link_libraries(${LIBNAME} PRIVATE opencv_imgproc opencv_calib3d opencv_features2d opencv_ml) +else() + target_link_libraries(${LIBNAME} PRIVATE opencv_imgproc opencv_calib3d opencv_features2d ) +endif() + +INSTALL(TARGETS ${LIBNAME} + RUNTIME DESTINATION bin COMPONENT main # Install the dll file in bin directory + LIBRARY DESTINATION lib PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE COMPONENT main + ARCHIVE DESTINATION lib COMPONENT main) # Install the dll.a file in lib directory + +IF(ARUCO_DEVINSTALL) + install(FILES ${headers} DESTINATION include/aruco) + install(FILES ${fractal_headers} DESTINATION include/aruco/fractallabelers) + install(FILES ${dcf_headers} DESTINATION include/aruco/dcf) +ENDIF() + diff --git a/thirdparty/aruco-3.1.12/src/aruco.h b/thirdparty/aruco-3.1.12/src/aruco.h new file mode 100644 index 0000000..7c9fcac --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/aruco.h @@ -0,0 +1,39 @@ +/** + +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "fractaldetector.h" +#include "markerdetector.h" +#include "posetracker.h" +#include "cvdrawingutils.h" +#include "dictionary.h" + +#define ARUCO_VERSION_MAJOR 3 +#define ARUCO_VERSION_MINOR 0 + + diff --git a/thirdparty/aruco-3.1.12/src/aruco_cvversioning.h b/thirdparty/aruco-3.1.12/src/aruco_cvversioning.h new file mode 100644 index 0000000..0192e1f --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/aruco_cvversioning.h @@ -0,0 +1,18 @@ +#ifndef ARUCO_CV_VERSIONING +#define ARUCO_CV_VERSIONING +#include +#if CV_MAJOR_VERSION >= 4 +#include + +#define CV_CAP_PROP_FRAME_COUNT cv::CAP_PROP_FRAME_COUNT +#define CV_CAP_PROP_POS_FRAMES cv::CAP_PROP_POS_FRAMES +#define CV_BGR2GRAY cv::COLOR_BGR2GRAY +#define CV_GRAY2BGR cv::COLOR_GRAY2BGR +#define CV_FONT_HERSHEY_COMPLEX cv::FONT_HERSHEY_COMPLEX +#define CV_FILLED cv::FILLED +#define CV_RANSAC cv::RANSAC +#define CV_REDUCE_SUM cv::REDUCE_SUM +#endif + +#endif + diff --git a/thirdparty/aruco-3.1.12/src/aruco_export.h b/thirdparty/aruco-3.1.12/src/aruco_export.h new file mode 100644 index 0000000..b6995b2 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/aruco_export.h @@ -0,0 +1,46 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + + + +#ifndef __OPENARUCO_CORE_TYPES_H__ +#define __OPENARUCO_CORE_TYPES_H__ + +#if !defined _CRT_SECURE_NO_DEPRECATE && _MSC_VER > 1300 +#define _CRT_SECURE_NO_DEPRECATE /* to avoid multiple Visual Studio 2005 warnings */ +#endif + + +#if (defined WIN32 || defined _WIN32 || defined WINCE) && defined ARUCO_DSO_EXPORTS +#define ARUCO_EXPORT __declspec(dllexport) +#else +#define ARUCO_EXPORT +#endif + + +#endif diff --git a/thirdparty/aruco-3.1.12/src/cameraparameters.cpp b/thirdparty/aruco-3.1.12/src/cameraparameters.cpp new file mode 100644 index 0000000..de768c3 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/cameraparameters.cpp @@ -0,0 +1,610 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ +#include "cameraparameters.h" + +#include +#include + +#include +#include + +using namespace std; +namespace aruco +{ + CameraParameters::CameraParameters() + { + CameraMatrix = cv::Mat(); + Distorsion = cv::Mat(); + CamSize = cv::Size(-1, -1); + } + /**Creates the object from the info passed + * @param cameraMatrix 3x3 matrix (fx 0 cx, 0 fy cy, 0 0 1) + * @param distorsionCoeff 4x1 matrix (k1,k2,p1,p2) + * @param size image size + */ + CameraParameters::CameraParameters(cv::Mat cameraMatrix, cv::Mat distorsionCoeff, + cv::Size size) + { + setParams(cameraMatrix, distorsionCoeff, size); + } + /** + */ + CameraParameters::CameraParameters(const CameraParameters& CI) + { + CI.CameraMatrix.copyTo(CameraMatrix); + CI.Distorsion.copyTo(Distorsion); + CamSize = CI.CamSize; + } + + /** + */ + CameraParameters& CameraParameters::operator=(const CameraParameters& CI) + { + CI.CameraMatrix.copyTo(CameraMatrix); + CI.Distorsion.copyTo(Distorsion); + CamSize = CI.CamSize; + return *this; + } + void CameraParameters::clear(){ + CameraMatrix=cv::Mat(); + CamSize=cv::Size(-1,-1); + Distorsion=cv::Mat(); + } + /** + */ + void CameraParameters::setParams(cv::Mat cameraMatrix, cv::Mat distorsionCoeff, cv::Size size) + { + printf("\n Camera Matrix Size: %dx%d \n", cameraMatrix.rows, cameraMatrix.cols); + if (cameraMatrix.rows != 3 || cameraMatrix.cols != 3) + throw cv::Exception(9000, "invalid input cameraMatrix", "CameraParameters::setParams", __FILE__, __LINE__); + cameraMatrix.convertTo(CameraMatrix, CV_32FC1); + if (distorsionCoeff.total() < 4 || distorsionCoeff.total() >= 7) + throw cv::Exception(9000, "invalid input distorsionCoeff", "CameraParameters::setParams", __FILE__, + __LINE__); + cv::Mat auxD; + + distorsionCoeff.convertTo(Distorsion, CV_32FC1); + + // Distorsion.create(1,4,CV_32FC1); + // for (int i=0;i<4;i++) + // Distorsion.ptr(0)[i]=auxD.ptr(0)[i]; + + CamSize = size; + } + + /** + */ + cv::Point3f CameraParameters::getCameraLocation(const cv::Mat &Rvec,const cv::Mat &Tvec) + { + + cv::Mat m33(3, 3, CV_32FC1); + cv::Rodrigues(Rvec, m33); + + cv::Mat m44 = cv::Mat::eye(4, 4, CV_32FC1); + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + m44.at(i, j) = m33.at(i, j); + + // now, add translation information + for (int i = 0; i < 3; i++) + m44.at(i, 3) = Tvec.ptr(0)[i]; + + + // invert the matrix + m44.inv(); + return cv::Point3f(m44.at(0, 3), m44.at(1, 3), m44.at(2, 3)); + } + + /**Saves this to a file + */ + void CameraParameters::saveToFile(string path, bool inXML) + { + if (!isValid()) + throw cv::Exception(9006, "invalid object", "CameraParameters::saveToFile", __FILE__, __LINE__); + if (!inXML) + { + ofstream file(path.c_str()); + if (!file) + throw cv::Exception(9006, "could not open file:" + path, "CameraParameters::saveToFile", __FILE__, + __LINE__); + file << "# Aruco 1.0 CameraParameters" << endl; + file << "fx = " << CameraMatrix.at(0, 0) << endl; + file << "cx = " << CameraMatrix.at(0, 2) << endl; + file << "fy = " << CameraMatrix.at(1, 1) << endl; + file << "cy = " << CameraMatrix.at(1, 2) << endl; + file << "k1 = " << Distorsion.at(0, 0) << endl; + file << "k2 = " << Distorsion.at(1, 0) << endl; + file << "p1 = " << Distorsion.at(2, 0) << endl; + file << "p2 = " << Distorsion.at(3, 0) << endl; + file << "width = " << CamSize.width << endl; + file << "height = " << CamSize.height << endl; + } + else + { + cv::FileStorage fs(path, cv::FileStorage::WRITE); + fs << "image_width" << CamSize.width; + fs << "image_height" << CamSize.height; + fs << "camera_matrix" << CameraMatrix; + fs << "distortion_coefficients" << Distorsion; + } + } + + /**Adjust the parameters to the size of the image indicated + */ + void CameraParameters::resize(cv::Size size) + { + if (!isValid()) + throw cv::Exception(9007, "invalid object", "CameraParameters::resize", __FILE__, __LINE__); + if (size == CamSize) + return; + // now, read the camera size + // resize the camera parameters to fit this image size + float AxFactor = float(size.width) / float(CamSize.width); + float AyFactor = float(size.height) / float(CamSize.height); + CameraMatrix.at(0, 0) *= AxFactor; + CameraMatrix.at(0, 2) *= AxFactor; + CameraMatrix.at(1, 1) *= AyFactor; + CameraMatrix.at(1, 2) *= AyFactor; + CamSize = size; + } + + /**** + * + * + * + * + */ + void CameraParameters::readFromXMLFile(string filePath) + { + cv::FileStorage fs(filePath, cv::FileStorage::READ); + if(!fs.isOpened()) throw std::runtime_error("CameraParameters::readFromXMLFile could not open file:"+filePath); + int w = -1, h = -1; + cv::Mat MCamera, MDist; + + + fs["image_width"] >> w; + fs["image_height"] >> h; + fs["distortion_coefficients"] >> MDist; + fs["camera_matrix"] >> MCamera; + + if (MCamera.cols == 0 || MCamera.rows == 0){ + fs["Camera_Matrix"] >> MCamera; + if (MCamera.cols == 0 || MCamera.rows == 0) + throw cv::Exception(9007, "File :" + filePath + " does not contains valid camera matrix", + "CameraParameters::readFromXML", __FILE__, __LINE__); + } + + if (w == -1 || h == 0){ + fs["image_Width"] >> w; + fs["image_Height"] >> h; + if (w == -1 || h == 0) + throw cv::Exception(9007, "File :" + filePath + " does not contains valid camera dimensions", + "CameraParameters::readFromXML", __FILE__, __LINE__); + } + if (MCamera.type() != CV_32FC1) + MCamera.convertTo(CameraMatrix, CV_32FC1); + else + CameraMatrix = MCamera; + + if (MDist.total() < 4){ + fs["Distortion_Coefficients"] >> MDist; + if (MDist.total() < 4) + throw cv::Exception(9007, "File :" + filePath + " does not contains valid distortion_coefficients", + "CameraParameters::readFromXML", __FILE__, __LINE__); + } + // convert to 32 and get the 4 first elements only + cv::Mat mdist32; + MDist.convertTo(mdist32, CV_32FC1); + // Distorsion.create(1,4,CV_32FC1); + // for (int i=0;i<4;i++) + // Distorsion.ptr(0)[i]=mdist32.ptr(0)[i]; + + Distorsion.create(1, 5, CV_32FC1); + for (int i = 0; i < 5; i++) + Distorsion.ptr(0)[i] = mdist32.ptr(0)[i]; + CamSize.width = w; + CamSize.height = h; + } + /**** + * + */ + void CameraParameters::glGetProjectionMatrix(cv::Size orgImgSize, cv::Size size, double proj_matrix[16], + double gnear, double gfar, bool invert) + { + if (cv::countNonZero(Distorsion) != 0) + std::cerr << "CameraParameters::glGetProjectionMatrix :: The camera has distortion coefficients " + << __FILE__ << " " << __LINE__ << endl; + if (isValid() == false) + throw cv::Exception(9100, "invalid camera parameters", "CameraParameters::glGetProjectionMatrix", __FILE__, + __LINE__); + + // Deterime the rsized info + double Ax = double(size.width) / double(orgImgSize.width); + double Ay = double(size.height) / double(orgImgSize.height); + double _fx = CameraMatrix.at(0, 0) * Ax; + double _cx = CameraMatrix.at(0, 2) * Ax; + double _fy = CameraMatrix.at(1, 1) * Ay; + double _cy = CameraMatrix.at(1, 2) * Ay; + double cparam[3][4] = {{_fx, 0, _cx, 0}, {0, _fy, _cy, 0}, {0, 0, 1, 0}}; + + argConvGLcpara2(cparam, size.width, size.height, gnear, gfar, proj_matrix, invert); + } + + /******************* + * + * + *******************/ + double CameraParameters::norm(double a, double b, double c) + { + return (sqrt(a * a + b * b + c * c)); + } + + /******************* + * + * + *******************/ + + double CameraParameters::dot(double a1, double a2, double a3, double b1, double b2, double b3) + { + return (a1 * b1 + a2 * b2 + a3 * b3); + } + + /******************* + * + * + *******************/ + + void CameraParameters::argConvGLcpara2(double cparam[3][4], int width, int height, double gnear, double gfar, + double m[16], bool invert) + { + double icpara[3][4]; + double trans[3][4]; + double p[3][3], q[4][4]; + int i, j; + + cparam[0][2] *= -1.0; + cparam[1][2] *= -1.0; + cparam[2][2] *= -1.0; + + if (arParamDecompMat(cparam, icpara, trans) < 0) + throw cv::Exception(9002, "parameter error", "MarkerDetector::argConvGLcpara2", __FILE__, __LINE__); + + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + p[i][j] = icpara[i][j] / icpara[2][2]; + } + } + q[0][0] = (2.0 * p[0][0] / width); + q[0][1] = (2.0 * p[0][1] / width); + q[0][2] = ((2.0 * p[0][2] / width) - 1.0); + q[0][3] = 0.0; + + q[1][0] = 0.0; + q[1][1] = (2.0 * p[1][1] / height); + q[1][2] = ((2.0 * p[1][2] / height) - 1.0); + q[1][3] = 0.0; + + q[2][0] = 0.0; + q[2][1] = 0.0; + q[2][2] = (gfar + gnear) / (gfar - gnear); + q[2][3] = -2.0 * gfar * gnear / (gfar - gnear); + + q[3][0] = 0.0; + q[3][1] = 0.0; + q[3][2] = 1.0; + q[3][3] = 0.0; + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 3; j++) + { + m[i + j * 4] = q[i][0] * trans[0][j] + q[i][1] * trans[1][j] + q[i][2] * trans[2][j]; + } + m[i + 3 * 4] = q[i][0] * trans[0][3] + q[i][1] * trans[1][3] + q[i][2] * trans[2][3] + q[i][3]; + } + + if (!invert) + { + m[13] = -m[13]; + m[1] = -m[1]; + m[5] = -m[5]; + m[9] = -m[9]; + } + } + /******************* + * + * + *******************/ + + int CameraParameters::arParamDecompMat(double source[3][4], double cpara[3][4], + double trans[3][4]) + { + int r, c; + double Cpara[3][4]; + double rem1, rem2, rem3; + + if (source[2][3] >= 0) + { + for (r = 0; r < 3; r++) + { + for (c = 0; c < 4; c++) + { + Cpara[r][c] = source[r][c]; + } + } + } + else + { + for (r = 0; r < 3; r++) + { + for (c = 0; c < 4; c++) + { + Cpara[r][c] = -(source[r][c]); + } + } + } + + for (r = 0; r < 3; r++) + { + for (c = 0; c < 4; c++) + { + cpara[r][c] = 0.0; + } + } + cpara[2][2] = norm(Cpara[2][0], Cpara[2][1], Cpara[2][2]); + trans[2][0] = Cpara[2][0] / cpara[2][2]; + trans[2][1] = Cpara[2][1] / cpara[2][2]; + trans[2][2] = Cpara[2][2] / cpara[2][2]; + trans[2][3] = Cpara[2][3] / cpara[2][2]; + + cpara[1][2] = dot(trans[2][0], trans[2][1], trans[2][2], Cpara[1][0], Cpara[1][1], Cpara[1][2]); + rem1 = Cpara[1][0] - cpara[1][2] * trans[2][0]; + rem2 = Cpara[1][1] - cpara[1][2] * trans[2][1]; + rem3 = Cpara[1][2] - cpara[1][2] * trans[2][2]; + cpara[1][1] = norm(rem1, rem2, rem3); + trans[1][0] = rem1 / cpara[1][1]; + trans[1][1] = rem2 / cpara[1][1]; + trans[1][2] = rem3 / cpara[1][1]; + + cpara[0][2] = dot(trans[2][0], trans[2][1], trans[2][2], Cpara[0][0], Cpara[0][1], Cpara[0][2]); + cpara[0][1] = dot(trans[1][0], trans[1][1], trans[1][2], Cpara[0][0], Cpara[0][1], Cpara[0][2]); + rem1 = Cpara[0][0] - cpara[0][1] * trans[1][0] - cpara[0][2] * trans[2][0]; + rem2 = Cpara[0][1] - cpara[0][1] * trans[1][1] - cpara[0][2] * trans[2][1]; + rem3 = Cpara[0][2] - cpara[0][1] * trans[1][2] - cpara[0][2] * trans[2][2]; + cpara[0][0] = norm(rem1, rem2, rem3); + trans[0][0] = rem1 / cpara[0][0]; + trans[0][1] = rem2 / cpara[0][0]; + trans[0][2] = rem3 / cpara[0][0]; + + trans[1][3] = (Cpara[1][3] - cpara[1][2] * trans[2][3]) / cpara[1][1]; + trans[0][3] = (Cpara[0][3] - cpara[0][1] * trans[1][3] - cpara[0][2] * trans[2][3]) / cpara[0][0]; + + for (r = 0; r < 3; r++) + { + for (c = 0; c < 3; c++) + { + cpara[r][c] /= cpara[2][2]; + } + } + + return 0; + } + + /****** + * + */ + void CameraParameters::OgreGetProjectionMatrix(cv::Size orgImgSize, cv::Size size, double proj_matrix[16], + double gnear, double gfar, bool invert) + { + double temp_matrix[16]; + (*this).glGetProjectionMatrix(orgImgSize, size, temp_matrix, gnear, gfar, invert); + proj_matrix[0] = -temp_matrix[0]; + proj_matrix[1] = -temp_matrix[4]; + proj_matrix[2] = -temp_matrix[8]; + proj_matrix[3] = temp_matrix[12]; + + proj_matrix[4] = -temp_matrix[1]; + proj_matrix[5] = -temp_matrix[5]; + proj_matrix[6] = -temp_matrix[9]; + proj_matrix[7] = temp_matrix[13]; + + proj_matrix[8] = -temp_matrix[2]; + proj_matrix[9] = -temp_matrix[6]; + proj_matrix[10] = -temp_matrix[10]; + proj_matrix[11] = temp_matrix[14]; + + proj_matrix[12] = -temp_matrix[3]; + proj_matrix[13] = -temp_matrix[7]; + proj_matrix[14] = -temp_matrix[11]; + proj_matrix[15] = temp_matrix[15]; + } + /****** + * + */ + + cv::Mat CameraParameters::getRTMatrix(const cv::Mat& R_, const cv::Mat& T_, int forceType) + { + cv::Mat M; + cv::Mat R, T; + R_.copyTo(R); + T_.copyTo(T); + if (R.type() == CV_64F) + { + assert(T.type() == CV_64F); + cv::Mat Matrix = cv::Mat::eye(4, 4, CV_64FC1); + + cv::Mat R33 = cv::Mat(Matrix, cv::Rect(0, 0, 3, 3)); + if (R.total() == 3) + { + cv::Rodrigues(R, R33); + } + else if (R.total() == 9) + { + cv::Mat R64; + R.convertTo(R64, CV_64F); + R.copyTo(R33); + } + for (int i = 0; i < 3; i++) + Matrix.at(i, 3) = T.ptr(0)[i]; + M = Matrix; + } + else if (R.depth() == CV_32F) + { + cv::Mat Matrix = cv::Mat::eye(4, 4, CV_32FC1); + cv::Mat R33 = cv::Mat(Matrix, cv::Rect(0, 0, 3, 3)); + if (R.total() == 3) + { + cv::Rodrigues(R, R33); + } + else if (R.total() == 9) + { + cv::Mat R32; + R.convertTo(R32, CV_32F); + R.copyTo(R33); + } + + for (int i = 0; i < 3; i++) + Matrix.at(i, 3) = T.ptr(0)[i]; + M = Matrix; + } + + if (forceType == -1) + return M; + else + { + cv::Mat MTyped; + M.convertTo(MTyped, forceType); + return MTyped; + } + } + std::ostream &operator<<(std::ostream &str,const CameraParameters&cp){ + str<<"%YAML:1.0"<(0); + for(int i=0;i<9;i++){ + str<(); + for(int i=0;i<5;i++){ + str<>(std::istream &str,CameraParameters&cp){ + + auto getValue=[](string line){ + //remove ':' + for(auto &c:line) if (c==':') c=' '; + stringstream ss; + ss<>aux>>val; + return val; + }; + + auto parseDataLine=[](string line,cv::Mat &data){ + //remove + for(auto &c:line) + if (c=='[' || c==']'|| c==',' || c==':') c=' '; + //now, read + stringstream sstr;sstr<>sdata; + for(size_t i=0;i>data.ptr(0)[i]; + }; + + cp.CameraMatrix=cv::Mat::eye(3,3,CV_32F); + cp.Distorsion.create(1,5,CV_32F); + cp.Distorsion.setTo(cv::Scalar::all(0)); + string line; + std::getline(str,line); + if (line.find("%YAML:1.0")==string::npos){ + std::cerr<<"Invalid input stream"< +#include +#include + +namespace aruco +{ + /**\brief Parameters of the camera + */ + + class ARUCO_EXPORT CameraParameters + { + public: + // 3x3 matrix (fx 0 cx, 0 fy cy, 0 0 1) + cv::Mat CameraMatrix; + // distortion matrix + cv::Mat Distorsion; + // size of the image + cv::Size CamSize; + + /**Empty constructor + */ + CameraParameters(); + /**Creates the object from the info passed + * @param cameraMatrix 3x3 matrix (fx 0 cx, 0 fy cy, 0 0 1) + * @param distorsionCoeff 4x1 matrix (k1,k2,p1,p2) + * @param size image size + */ + CameraParameters(cv::Mat cameraMatrix, cv::Mat distorsionCoeff, cv::Size size); + /**Sets the parameters + * @param cameraMatrix 3x3 matrix (fx 0 cx, 0 fy cy, 0 0 1) + * @param distorsionCoeff 4x1 matrix (k1,k2,p1,p2) + * @param size image size + */ + void setParams(cv::Mat cameraMatrix, cv::Mat distorsionCoeff, cv::Size size); + /**Copy constructor + */ + CameraParameters(const CameraParameters& CI); + + /**Indicates whether this object is valid + */ + bool isValid() const + { + return CameraMatrix.rows != 0 && CameraMatrix.cols != 0 && Distorsion.rows != 0 && Distorsion.cols != 0 + && CamSize.width != -1 && CamSize.height != -1; + } + /**Assign operator + */ + CameraParameters& operator=(const CameraParameters& CI); + + /**Saves this to a file + */ + void saveToFile(std::string path, bool inXML = true); + + /**Reads from a YAML file generated with the opencv2.2 calibration utility + */ + void readFromXMLFile(std::string filePath); + + /**Adjust the parameters to the size of the image indicated + */ + void resize(cv::Size size); + + /**Returns the location of the camera in the reference system of the marker. + * + * Rvec and Tvec are the transform from the marker to the camera as calculated in other parts of the library + * NOT TESTED + */ + static cv::Point3f getCameraLocation(const cv::Mat &Rvec,const cv::Mat &Tvec); + + /**Given the intrinsic camera parameters returns the GL_PROJECTION matrix for opengl. + * PLease NOTE that when using OpenGL, it is assumed no camera distorsion! So, if it is not true, you should have + * undistor image + * + * @param orgImgSize size of the original image + * @param size of the image/window where to render (can be different from the real camera image). Please not that + *it must be related to CamMatrix + * @param proj_matrix output projection matrix to give to opengl + * @param gnear,gfar: visible rendering range + * @param invert: indicates if the output projection matrix has to yield a horizontally inverted image because + *image data has not been stored in the order of + *glDrawPixels: bottom-to-top. + */ + void glGetProjectionMatrix(cv::Size orgImgSize, cv::Size size, double proj_matrix[16], double gnear, + double gfar, bool invert = false); + + /** + * setup camera for an Ogre project. + * Use: + * ... + * Ogre::Matrix4 PM(proj_matrix[0], proj_matrix[1], ... , proj_matrix[15]); + * yourCamera->setCustomProjectionMatrix(true, PM); + * yourCamera->setCustomViewMatrix(true, Ogre::Matrix4::IDENTITY); + * ... + * As in OpenGL, it assumes no camera distorsion + */ + void OgreGetProjectionMatrix(cv::Size orgImgSize, cv::Size size, double proj_matrix[16], double gnear, + double gfar, bool invert = false); + + /**Returns the 4x4 homogeneous transform matrix from the R and T vectors computed + */ + static cv::Mat getRTMatrix(const cv::Mat& R_, const cv::Mat& T_, int forceType); + + + /**Makes this invalid + */ + void clear(); + + ARUCO_EXPORT friend std::ostream &operator<<(std::ostream &str,const CameraParameters&cp); + ARUCO_EXPORT friend std::istream &operator>>(std::istream &str,CameraParameters&cp); + private: + // GL routines + + static void argConvGLcpara2(double cparam[3][4], int width, int height, double gnear, double gfar, double m[16], + bool invert); + static int arParamDecompMat(double source[3][4], double cpara[3][4], double trans[3][4]); + static double norm(double a, double b, double c); + static double dot(double a1, double a2, double a3, double b1, double b2, double b3); + }; +} +#endif diff --git a/thirdparty/aruco-3.1.12/src/cvdrawingutils.cpp b/thirdparty/aruco-3.1.12/src/cvdrawingutils.cpp new file mode 100644 index 0000000..9b8b340 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/cvdrawingutils.cpp @@ -0,0 +1,174 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ +#include +#include +#include "cvdrawingutils.h" +#include "cameraparameters.h" +using namespace cv; +namespace aruco +{ +void CvDrawingUtils::draw3dAxis(cv::Mat& Image, const CameraParameters& CP, const cv::Mat& Rvec, + const cv::Mat& Tvec, float axis_size) + { + Mat objectPoints(4, 3, CV_32FC1); + objectPoints.at(0, 0) = 0; + objectPoints.at(0, 1) = 0; + objectPoints.at(0, 2) = 0; + objectPoints.at(1, 0) = axis_size; + objectPoints.at(1, 1) = 0; + objectPoints.at(1, 2) = 0; + objectPoints.at(2, 0) = 0; + objectPoints.at(2, 1) = axis_size; + objectPoints.at(2, 2) = 0; + objectPoints.at(3, 0) = 0; + objectPoints.at(3, 1) = 0; + objectPoints.at(3, 2) = axis_size; + + std::vector imagePoints; + cv::projectPoints(objectPoints, Rvec, Tvec, CP.CameraMatrix, CP.Distorsion, imagePoints); + // draw lines of different colours + cv::line(Image, imagePoints[0], imagePoints[1], Scalar(0, 0, 255, 255), 1); + cv::line(Image, imagePoints[0], imagePoints[2], Scalar(0, 255, 0, 255), 1); + cv::line(Image, imagePoints[0], imagePoints[3], Scalar(255, 0, 0, 255), 1); + putText(Image, "x", imagePoints[1], FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 0, 255, 255), 2); + putText(Image, "y", imagePoints[2], FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 255, 0, 255), 2); + putText(Image, "z", imagePoints[3], FONT_HERSHEY_SIMPLEX, 0.6, Scalar(255, 0, 0, 255), 2); + } + /**** + * + * + * + ****/ + void CvDrawingUtils::draw3dAxis(cv::Mat& Image, Marker& m, const CameraParameters& CP,int lineSize) + { + float size = m.ssize *0.6; + Mat objectPoints(4, 3, CV_32FC1); + objectPoints.at(0, 0) = 0; + objectPoints.at(0, 1) = 0; + objectPoints.at(0, 2) = 0; + objectPoints.at(1, 0) = size; + objectPoints.at(1, 1) = 0; + objectPoints.at(1, 2) = 0; + objectPoints.at(2, 0) = 0; + objectPoints.at(2, 1) = size; + objectPoints.at(2, 2) = 0; + objectPoints.at(3, 0) = 0; + objectPoints.at(3, 1) = 0; + objectPoints.at(3, 2) = size; + + std::vector imagePoints; + cv::projectPoints(objectPoints, m.Rvec, m.Tvec, CP.CameraMatrix, CP.Distorsion, imagePoints); + // draw lines of different colours + cv::line(Image, imagePoints[0], imagePoints[1], Scalar(0, 0, 255, 255), lineSize); + cv::line(Image, imagePoints[0], imagePoints[2], Scalar(0, 255, 0, 255), lineSize); + cv::line(Image, imagePoints[0], imagePoints[3], Scalar(255, 0, 0, 255), lineSize); + putText(Image, "x", imagePoints[1], FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 0, 255, 255), 2); + putText(Image, "y", imagePoints[2], FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 255, 0, 255), 2); + putText(Image, "z", imagePoints[3], FONT_HERSHEY_SIMPLEX, 0.6, Scalar(255, 0, 0, 255), 2); + } + + /**** + * + * + * + ****/ + void CvDrawingUtils::draw3dCube(cv::Mat& Image, Marker& m, const CameraParameters& CP, int lineSize, bool setYperpendicular) + { + Mat objectPoints(8, 3, CV_32FC1); + float halfSize = m.ssize / 2.f; + + if (setYperpendicular) + { + objectPoints.at(0, 0) = -halfSize; + objectPoints.at(0, 1) = 0; + objectPoints.at(0, 2) = -halfSize; + objectPoints.at(1, 0) = halfSize; + objectPoints.at(1, 1) = 0; + objectPoints.at(1, 2) = -halfSize; + objectPoints.at(2, 0) = halfSize; + objectPoints.at(2, 1) = 0; + objectPoints.at(2, 2) = halfSize; + objectPoints.at(3, 0) = -halfSize; + objectPoints.at(3, 1) = 0; + objectPoints.at(3, 2) = halfSize; + + objectPoints.at(4, 0) = -halfSize; + objectPoints.at(4, 1) = m.ssize; + objectPoints.at(4, 2) = -halfSize; + objectPoints.at(5, 0) = halfSize; + objectPoints.at(5, 1) = m.ssize; + objectPoints.at(5, 2) = -halfSize; + objectPoints.at(6, 0) = halfSize; + objectPoints.at(6, 1) = m.ssize; + objectPoints.at(6, 2) = halfSize; + objectPoints.at(7, 0) = -halfSize; + objectPoints.at(7, 1) = m.ssize; + objectPoints.at(7, 2) = halfSize; + } + else + { + objectPoints.at(0, 0) = -halfSize; + objectPoints.at(0, 1) = -halfSize; + objectPoints.at(0, 2) = 0; + objectPoints.at(1, 0) = halfSize; + objectPoints.at(1, 1) = -halfSize; + objectPoints.at(1, 2) = 0; + objectPoints.at(2, 0) = halfSize; + objectPoints.at(2, 1) = halfSize; + objectPoints.at(2, 2) = 0; + objectPoints.at(3, 0) = -halfSize; + objectPoints.at(3, 1) = halfSize; + objectPoints.at(3, 2) = 0; + + objectPoints.at(4, 0) = -halfSize; + objectPoints.at(4, 1) = -halfSize; + objectPoints.at(4, 2) = m.ssize; + objectPoints.at(5, 0) = halfSize; + objectPoints.at(5, 1) = -halfSize; + objectPoints.at(5, 2) = m.ssize; + objectPoints.at(6, 0) = halfSize; + objectPoints.at(6, 1) = halfSize; + objectPoints.at(6, 2) = m.ssize; + objectPoints.at(7, 0) = -halfSize; + objectPoints.at(7, 1) = halfSize; + objectPoints.at(7, 2) = m.ssize; + } + + std::vector imagePoints; + projectPoints(objectPoints, m.Rvec, m.Tvec, CP.CameraMatrix, CP.Distorsion, imagePoints); + // draw lines of different colours + for (int i = 0; i < 4; i++) + cv::line(Image, imagePoints[i], imagePoints[(i + 1) % 4], Scalar(0, 0, 255, 255), lineSize); + + for (int i = 0; i < 4; i++) + cv::line(Image, imagePoints[i + 4], imagePoints[4 + (i + 1) % 4], Scalar(0, 0, 255, 255), lineSize); + + for (int i = 0; i < 4; i++) + cv::line(Image, imagePoints[i], imagePoints[i + 4], Scalar(0, 0, 255, 255), lineSize); + } +} diff --git a/thirdparty/aruco-3.1.12/src/cvdrawingutils.h b/thirdparty/aruco-3.1.12/src/cvdrawingutils.h new file mode 100644 index 0000000..4d08922 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/cvdrawingutils.h @@ -0,0 +1,55 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef _ArUco_DrawUtils_H_ +#define _ArUco_DrawUtils_H_ + +#include "aruco.h" +#include "aruco_export.h" + +namespace aruco +{ + /**\brief A set of functions to draw in opencv images + */ + class ARUCO_EXPORT CvDrawingUtils + { + public: + + static void draw3dAxis(cv::Mat& Image, const CameraParameters& CP, const cv::Mat& Rvec, const cv::Mat& Tvec, + float axis_size); + static void draw3dAxis(cv::Mat& Image, Marker& m, const CameraParameters& CP,int lineSize=1); + + static void draw3dCube(cv::Mat& Image, Marker& m, const CameraParameters& CP,int lineSize=1, bool setYperpendicular = false); + + // static void draw3dAxis(cv::Mat &Image, MarkerMap &m, const CameraParameters &CP); + // static void draw3dCube(cv::Mat &Image, MarkerMap &m, const CameraParameters &CP, bool setYperpendicular = + // false); + }; +} + +#endif diff --git a/thirdparty/aruco-3.1.12/src/dcf/dcf_utils.cpp b/thirdparty/aruco-3.1.12/src/dcf/dcf_utils.cpp new file mode 100644 index 0000000..18bfc5d --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dcf/dcf_utils.cpp @@ -0,0 +1,442 @@ +#include "aruco_cvversioning.h" +#include "dcf_utils.h" +#include "bitset" +#include "iostream" + +void cornerClassifier(const cv::Mat &im, std::vector points,std::vector& corner_class, int wsize) +{ + corner_class.resize(points.size()); + + if(im.type()!=CV_8UC1) + throw std::runtime_error("assignClass Input image must be 8UC1"); + int wsizeFull=wsize*2+1; + + cv::Mat labels = cv::Mat::zeros(wsize*2+1,wsize*2+1,CV_8UC1); + cv::Mat thresIm=cv::Mat(wsize*2+1,wsize*2+1,CV_8UC1); + + for(unsigned int i=0; i<4; i++) + { + float x = points[i].x; + float y = points[i].y; + + x= int(x+0.5f); + y= int(y+0.5f); + + cv::Rect r= cv::Rect(x-wsize,y-wsize,wsize*2+1,wsize*2+1); + //Check boundaries + if(r.x<0 || r.x+r.width>im.cols || r.y<0 || r.y+r.height>im.rows){ + corner_class[i]=-1; + continue; + } + + int endX=r.x+r.width; + int endY=r.y+r.height; + uchar minV=255,maxV=0; + for(int y=r.y; y(y); + for(int x=r.x; xptr[x]) minV=ptr[x]; + if(maxV( r.y+y)+r.x; + uchar *thresPtr= thresIm.ptr(y); + for(int x=0; xthres) { + nZ++; + thresPtr[x]=255; + } + else thresPtr[x]=0; + } + } + //set all to zero labels.setTo(cv::Scalar::all(0)); + for(int y=0; y(y); + for(int x=0; x unions; + for(int y=0; y(y); + uchar *labelsPtr=labels.ptr(y); + for(int x=0; x -1) + { + if(reg == thresPtr[x-1]) + lleft_px =labelsPtr[x-1]; + } + + if(y-1 > -1) + { + if(reg ==thresIm.ptr(y-1) [x] + )//thresIm.at(y-1, x) + ltop_px = labels.at(y-1, x); + } + + if(lleft_px==0 && ltop_px==0) + labelsPtr[x] = newLab++; + + else if(lleft_px!=0 && ltop_px!=0) + { + if(lleft_px < ltop_px) + { + labelsPtr[x] = lleft_px; + unions[ltop_px] = lleft_px; + } + else if(lleft_px > ltop_px) + { + labelsPtr[x] = ltop_px; + unions[lleft_px] = ltop_px; + } + else + {//IGuales + labelsPtr[x] = ltop_px; + } + } + else + { + if(lleft_px!=0) labelsPtr[x] = lleft_px; + else labelsPtr[x] = ltop_px; + } + } + } + + int nc= newLab-1 - unions.size(); + if(nc==2) + { + if(nZ > thresIm.total()-nZ) corner_class[i] = 0; + else corner_class[i] = 1; + } + else if (nc > 2) { + corner_class[i] = 2; + } + } +} + +cv::Rect getRectFrom4Points(std::vectorpoints, cv::Size imSize, bool imFit) +{ + int minX=std::numeric_limits::max(), maxX=std::numeric_limits::min(), + minY=std::numeric_limits::max(), maxY=std::numeric_limits::min(); + + for(auto pt:points) + { + if(pt.xmaxX) maxX=pt.x; + if(pt.y>maxY) maxY=pt.y; + } + + if(!imSize.empty()) + { + //The rect is out image + if(minX > imSize.width || minY > imSize.height || maxX < 0 || maxY <0) + return cv::Rect2d(); + + if(imFit) + { + if(minX<0) minX=0; + if(minY<0) minY=0; + if(maxX>imSize.width) maxX=imSize.width; + if(maxY>imSize.height) maxY=imSize.height; + } + else if(minX<0 || minY<0 || maxX>imSize.width || maxY>imSize.height) + return cv::Rect2d(); + } + + return cv::Rect2d(cv::Point2d(minX,minY),cv::Point2d(maxX,maxY)); +} + +bool getMarkerIds(const cv::Mat& im, int total_bits, std::vector& marker_corners, std::vector& ids) +{ + int threshold=100;//125; + const int nBitsSquared = static_cast(std::sqrt(total_bits))+2; + + std::vector obj_corners; + obj_corners.push_back(cv::Point2f(0, 0)); + obj_corners.push_back(cv::Point2f(nBitsSquared, 0)); + obj_corners.push_back(cv::Point2f(nBitsSquared, nBitsSquared)); + obj_corners.push_back(cv::Point2f(0, nBitsSquared)); + + ///Compute homography using marker corners + cv::Mat H = cv::findHomography(obj_corners, marker_corners, CV_RANSAC); + if(H.empty()) + return false; + + //Use homography to get the centers bit from marker + std::vector src_bit_centers; + for(float y=1.5; y vcenter_px; + cv::perspectiveTransform(src_bit_centers, vcenter_px, H); + + int bits_noborder = static_cast(std::sqrt(total_bits)); + + //take the inner code + cv::Mat _bits(bits_noborder,bits_noborder,CV_8UC1); + for(int y=0;yim.cols || vcenter_px[y*bits_noborder+x].x-0.5f<0 || + vcenter_px[y*bits_noborder+x].y+0.5f>im.rows || vcenter_px[y*bits_noborder+x].y-0.5f<0) + return false; + + + if(getSubpixelValue(im, vcenter_px[y*bits_noborder+x])< threshold) + _bits.at(y,x)=0; + else + _bits.at(y,x)=1; + } + + // now, get the 64bits ids + int nr = 0; + do + { + ids.push_back(touulong(_bits)); + _bits = rotate(_bits); + nr++; + } while (nr < 4); + + return true; +} + +/**Gets the subpixel value of the point passed + * @param im_grey image + * @param p point2d + */ +float getSubpixelValue(const cv::Mat &im_grey,const cv::Point2f &p){ + + assert(im_grey.type()==CV_8UC1); + float intpartX; + float decpartX=std::modf(p.x,&intpartX); + float intpartY; + float decpartY=std::modf(p.y,&intpartY); + + cv::Point tl; + + if (decpartX>0.5) { + if (decpartY>0.5) tl=cv::Point(intpartX,intpartY); + else tl=cv::Point(intpartX,intpartY-1); + } + else{ + if (decpartY>0.5) tl=cv::Point(intpartX-1,intpartY); + else tl=cv::Point(intpartX-1,intpartY-1); + } + return (1.f-decpartY)*(1.-decpartX)*float(im_grey.at(tl.y,tl.x))+ + decpartX*(1-decpartY)*float(im_grey.at(tl.y,tl.x+1))+ + (1-decpartX)*decpartY*float(im_grey.at(tl.y+1,tl.x))+ + decpartX*decpartY*float(im_grey.at(tl.y+1,tl.x+1)); +} + +// convert matrix of (0,1)s in a 64 bit value +uint64_t touulong(const cv::Mat& code) +{ + std::bitset<64> bits; + int bidx = 0; + for (int y = code.rows - 1; y >= 0; y--) + for (int x = code.cols - 1; x >= 0; x--) + bits[bidx++] = code.at(y, x); + return bits.to_ullong(); +} + +//Rotate matrix +cv::Mat rotate(const cv::Mat& in) +{ + cv::Mat out; + in.copyTo(out); + for (int i = 0; i < in.rows; i++) + { + for (int j = 0; j < in.cols; j++) + { + out.at(i, j) = in.at(in.cols - j - 1, i); + } + } + return out; +} + + +cv::Mat alignAndScaleToGroundTruth(std::vector >_other ){ + + std::vector points_other,points_gt; + for(auto gto:gt_other){ + assert(gto.first.type()==CV_64F); + points_gt.push_back(cv::Point3d(gto.first.at(0,3),gto.first.at(1,3),gto.first.at(2,3))); + points_other.push_back(cv::Point3d(gto.second.at(0,3),gto.second.at(1,3),gto.second.at(2,3))); + } + + cv::Mat best_T= rigidBodyTransformation_Horn1987(points_other,points_gt,false); + cv::Mat best_T64; + if( best_T.type()!=CV_64F) best_T.convertTo(best_T64,CV_64F); + else best_T64=best_T; + //cout<(std::bitset<64>(a ^ b).count()); +} + + +cv::Mat rigidBodyTransformation_Horn1987 (const std::vector &org, const std::vector &dst,bool mbFixScale){ + auto ComputeCentroid=[](cv::Mat &P, cv::Mat &Pr, cv::Mat &C) + { + cv::reduce(P,C,1,CV_REDUCE_SUM); + C = C/P.cols; + for(int i=0; i(0,i)=org[i].x; + P1.at(1,i)=org[i].y; + P1.at(2,i)=org[i].z; + P2.at(0,i)=dst[i].x; + P2.at(1,i)=dst[i].y; + P2.at(2,i)=dst[i].z; + } + + + // Step 1: Centroid and relative coordinates + + cv::Mat Pr1(P1.size(),P1.type()); // Relative coordinates to centroid (set 1) + cv::Mat Pr2(P2.size(),P2.type()); // Relative coordinates to centroid (set 2) + cv::Mat O1(3,1,Pr1.type()); // Centroid of P1 + cv::Mat O2(3,1,Pr2.type()); // Centroid of P2 + + ComputeCentroid(P1,Pr1,O1); + ComputeCentroid(P2,Pr2,O2); + + // Step 2: Compute M matrix + + cv::Mat M = Pr2*Pr1.t(); + + // Step 3: Compute N matrix + + double N11, N12, N13, N14, N22, N23, N24, N33, N34, N44; + + cv::Mat N(4,4,P1.type()); + + N11 = M.at(0,0)+M.at(1,1)+M.at(2,2); + N12 = M.at(1,2)-M.at(2,1); + N13 = M.at(2,0)-M.at(0,2); + N14 = M.at(0,1)-M.at(1,0); + N22 = M.at(0,0)-M.at(1,1)-M.at(2,2); + N23 = M.at(0,1)+M.at(1,0); + N24 = M.at(2,0)+M.at(0,2); + N33 = -M.at(0,0)+M.at(1,1)-M.at(2,2); + N34 = M.at(1,2)+M.at(2,1); + N44 = -M.at(0,0)-M.at(1,1)+M.at(2,2); + + N = (cv::Mat_(4,4) << N11, N12, N13, N14, + N12, N22, N23, N24, + N13, N23, N33, N34, + N14, N24, N34, N44); + + + // Step 4: Eigenvector of the highest eigenvalue + + cv::Mat eval, evec; + + cv::eigen(N,eval,evec); //evec[0] is the quaternion of the desired rotation + + cv::Mat vec(1,3,evec.type()); + (evec.row(0).colRange(1,4)).copyTo(vec); //extract imaginary part of the quaternion (sin*axis) + + // Rotation angle. sin is the norm of the imaginary part, cos is the real part + double ang=atan2(norm(vec),evec.at(0,0)); + + if (norm(vec)<1e-7)return cv::Mat::eye(4,4,CV_32F); + + vec = 2*ang*vec/norm(vec); //Angle-axis representation. quaternion angle is the half + + cv::Mat mR12i(3,3,P1.type()); + + cv::Rodrigues(vec,mR12i); // computes the rotation matrix from angle-axis + + // Step 5: Rotate set 2 + + cv::Mat P3 = mR12i*Pr2; + + // Step 6: Scale + float ms12i; + + if(!mbFixScale) + { + double nom = Pr1.dot(P3); + cv::Mat aux_P3(P3.size(),P3.type()); + aux_P3=P3; + cv::pow(P3,2,aux_P3); + double den = 0; + + for(int i=0; i(i,j); + } + } + + ms12i = nom/den; + } + else + ms12i = 1.0f; + + // Step 7: Translation + + cv::Mat mt12i(1,3,P1.type()); + mt12i = O1 - ms12i*mR12i*O2; + + // Step 8: Transformation + + // Step 8.1 T12 + cv::Mat mT12i = cv::Mat::eye(4,4,P1.type()); + + cv::Mat sR = ms12i*mR12i; + + sR.copyTo(mT12i.rowRange(0,3).colRange(0,3)); + mt12i.copyTo(mT12i.rowRange(0,3).col(3)); +// return mT12i; + +// // Step 8.2 T21 + + cv::Mat mT21i = cv::Mat::eye(4,4,P1.type()); + + cv::Mat sRinv = (1.0/ms12i)*mR12i.t(); + + sRinv.copyTo(mT21i.rowRange(0,3).colRange(0,3)); + cv::Mat tinv = -sRinv*mt12i; + tinv.copyTo(mT21i.rowRange(0,3).col(3)); + return mT21i; +} + + diff --git a/thirdparty/aruco-3.1.12/src/dcf/dcf_utils.h b/thirdparty/aruco-3.1.12/src/dcf/dcf_utils.h new file mode 100644 index 0000000..43b8781 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dcf/dcf_utils.h @@ -0,0 +1,29 @@ +#ifndef DFC_UTILS_H +#define DFC_UTILS_H + +#include +#include +#include +#include + +bool getMarkerIds(const cv::Mat& im, int total_bits, std::vector& marker_corners, std::vector& ids); +float getSubpixelValue(const cv::Mat &im_grey,const cv::Point2f &p); +uint64_t touulong(const cv::Mat& code); +cv::Mat rotate(const cv::Mat& in); +int hamm_distance(uint64_t a, uint64_t b); +cv::Rect getRectFrom4Points(std::vectorpoints, cv::Size imSize=cv::Size(), bool imFit=false); +void cornerClassifier(const cv::Mat &im, std::vector points,std::vector& corner_class, int wsize); + + +struct FrameMatchLocation{ + cv::Mat first; + cv::Mat second; + std::string frame; + double error; +}; + +cv::Mat rigidBodyTransformation_Horn1987 (const std::vector &org, const std::vector &dst,bool mbFixScale); +cv::Mat alignAndScaleToGroundTruth(std::vector >_other ); + + +#endif // DFC_UTILS_H diff --git a/thirdparty/aruco-3.1.12/src/dcf/dcfmarkermaptracker.cpp b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkermaptracker.cpp new file mode 100644 index 0000000..4bbdf25 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkermaptracker.cpp @@ -0,0 +1,542 @@ +#include "dcfmarkermaptracker.h" +#include "aruco.h" +#include "levmarq.h" +#include "ippe.h" +#include "dictionary_based.h" +#include +#include "dcf_utils.h" +#include +#include "aruco_cvversioning.h" +namespace aruco { + +cv::Mat impl__aruco_getRTMatrix(const cv::Mat& _rvec, const cv::Mat& _tvec) +{ + assert(_rvec.type()==CV_32F && _rvec.total()==3); + assert(_tvec.type()==CV_32F && _tvec.total()==3); + + cv::Mat Matrix(4,4,CV_32F); + float *rt_44=Matrix.ptr(0); + //makes a fast conversion to the 4x4 array passed + float rx=_rvec.ptr(0)[0]; + float ry=_rvec.ptr(0)[1]; + float rz=_rvec.ptr(0)[2]; + float tx=_tvec.ptr(0)[0]; + float ty=_tvec.ptr(0)[1]; + float tz=_tvec.ptr(0)[2]; + float nsqa=rx*rx + ry*ry + rz*rz; + float a=std::sqrt(nsqa); + float i_a=a?1./a:0; + float rnx=rx*i_a; + float rny=ry*i_a; + float rnz=rz*i_a; + float cos_a=cos(a); + float sin_a=sin(a); + float _1_cos_a=1.-cos_a; + rt_44[0] =cos_a+rnx*rnx*_1_cos_a; + rt_44[1]=rnx*rny*_1_cos_a- rnz*sin_a; + rt_44[2]=rny*sin_a + rnx*rnz*_1_cos_a; + rt_44[3]=tx; + rt_44[4]=rnz*sin_a +rnx*rny*_1_cos_a; + rt_44[5]=cos_a+rny*rny*_1_cos_a; + rt_44[6]= -rnx*sin_a+ rny*rnz*_1_cos_a; + rt_44[7]=ty; + rt_44[8]= -rny*sin_a + rnx*rnz*_1_cos_a; + rt_44[9]= rnx*sin_a + rny*rnz*_1_cos_a; + rt_44[10]=cos_a+rnz*rnz*_1_cos_a; + rt_44[11]=tz; + rt_44[12]=rt_44[13]=rt_44[14]=0; + rt_44[15]=1; + return Matrix; +} + +void impl__aruco_getRTfromMatrix44(const cv::Mat& M, cv::Mat& R, cv::Mat& T) +{ + assert(M.cols == M.rows && M.cols == 4); + assert(M.type() == CV_32F || M.type() == CV_64F); + // extract the rotation part + cv::Mat r33 = cv::Mat(M, cv::Rect(0, 0, 3, 3)); + cv::SVD svd(r33); + cv::Mat Rpure = svd.u * svd.vt; + cv::Rodrigues(Rpure, R); + T.create(1, 3, M.type()); + if (M.type() == CV_32F) + for (int i = 0; i < 3; i++) + T.ptr(0)[i] = M.at(i, 3); + else + for (int i = 0; i < 3; i++) + T.ptr(0)[i] = M.at(i, 3); +} + + inline double hubberMono(double e){ + if (e <= 5.991) { // inlier + return e; + } else // outlier + return 4.895303872*sqrt(e) - 5.991; // rho(e) = 2 * delta * e^(1/2) - delta^2 + } + + inline double getHubberMonoWeight(double SqErr,double Information){ + return sqrt(hubberMono(Information * SqErr)/ SqErr); + } + + template + double __aruco_solve_pnp(const std::vector& p3d, const std::vector& p2d, const std::vector&trust_weight, + const cv::Mat& cam_matrix, const cv::Mat& dist, cv::Mat& r_io, cv::Mat& t_io, bool hubber=true) + { + assert(r_io.type() == CV_32F); + assert(t_io.type() == CV_32F); + assert(t_io.total() == r_io.total()); + assert(t_io.total() == 3); + auto toSol = [](const cv::Mat& r, const cv::Mat& t) { + typename LevMarq::eVector sol(6); + for (int i = 0; i < 3; i++) + { + sol(i) = r.ptr(0)[i]; + sol(i + 3) = t.ptr(0)[i]; + } + return sol; + }; + auto fromSol = [](const typename LevMarq::eVector& sol, cv::Mat& r, cv::Mat& t) { + r.create(1, 3, CV_32F); + t.create(1, 3, CV_32F); + for (int i = 0; i < 3; i++) + { + r.ptr(0)[i] = sol(i); + t.ptr(0)[i] = sol(i + 3); + } + }; + + cv::Mat Jacb; + auto err_f = [&](const typename LevMarq::eVector& sol, typename LevMarq::eVector& err) { + std::vector p2d_rej; + cv::Mat r, t; + fromSol(sol, r, t); + cv::projectPoints(p3d, r, t, cam_matrix, dist, p2d_rej, Jacb); + err.resize(p3d.size() * 2); + int err_idx = 0; + for (size_t i = 0; i < p3d.size(); i++) + { + cv::Point2f errP=p2d_rej[i] -p2d[i]; + + + if(hubber) + { + double SqErr=(errP.x*errP.x+ errP.y*errP.y); + + float robuse_weight= getHubberMonoWeight(SqErr*trust_weight[int(i/4)],1); + err(err_idx++) = robuse_weight* errP.x; + err(err_idx++) = robuse_weight* errP.y; + } + else + { + err(err_idx++) = errP.x; + err(err_idx++) = errP.y; + } + } + }; + auto jac_f = [&](const typename LevMarq::eVector& sol, Eigen::Matrix& J) { + (void)(sol); + J.resize(p3d.size() * 2, 6); + for (size_t i = 0; i < p3d.size() * 2; i++) + { + double* jacb = Jacb.ptr(i); + for (int j = 0; j < 6; j++) + J(i, j) = jacb[j]; + } + }; + + LevMarq solver; + solver.setParams(100, 0.01, 0.01); + // solver.verbose()=true; + typename LevMarq::eVector sol = toSol(r_io, t_io); + auto err = solver.solve(sol, err_f, jac_f); + + fromSol(sol, r_io, t_io); + return err; + } + + + double __hub_solve_pnp(const std::vector& p3d, const std::vector& p2d, const std::vector&trust_weight, + const cv::Mat& cam_matrix, const cv::Mat& dist, cv::Mat& r_io, cv::Mat& t_io, float hubber=true) + { + return __aruco_solve_pnp(p3d, p2d, trust_weight, cam_matrix, dist, r_io, t_io, hubber); + } + + DFCMarkerMapTracker::DFCMarkerMapTracker() + { + + } + + void DFCMarkerMapTracker::loadParamsFromFile(const std::string &path) + { + cv::FileStorage fs(path, cv::FileStorage::READ); + if(!fs.isOpened())throw std::runtime_error("Could not open "+path); + + if ( fs["dcf-detectRate"].type()!=cv::FileNode::NONE) + fs["dcf-detectRate"]>>arucoDetectRate; + if ( fs["dcf-tau_c"].type()!=cv::FileNode::NONE) + fs["dcf-tau_c"]>>tau_c; + if ( fs["dcf-psr"].type()!=cv::FileNode::NONE) + fs["dcf-psr"]>>psr; + if ( fs["dcf-tau_s"].type()!=cv::FileNode::NONE) + fs["dcf-tau_s"]>>tau_s; + + TheDetector.loadParamsFromFile(path); + } + + void DFCMarkerMapTracker::setDictionary(const std::string &dict, float error_corretion_rate) + { + TheDetector.setDictionary(dict, error_corretion_rate); + } + + bool DFCMarkerMapTracker::prediction(std::vector&p3d, std::vector&p2d) + { + bool updated=false; + cv::Mat RTMatrix = impl__aruco_getRTMatrix(_rvec, _tvec); + +//#define _show_prj +#ifdef _show_prj + cv::Mat imColor; + cv::cvtColor(grey, imColor, CV_GRAY2BGR); +#endif + + for(auto m:_map_mm) + { + //It is not necessary to analyze tracked markers + if(mapMultiTracker.find(m.first) != mapMultiTracker.end()) + { +#ifdef _show_prj + + for(auto c:mapMultiTracker[m.first]->getCorners()) + cv::circle(imColor,c,5,cv::Scalar(0,0,255),-1); +#endif + continue; + } + + //For a marker, number of corners that may bse visible + int c=0; + for(auto pt:m.second.points) + { + cv::Mat_ src(4,1,RTMatrix.type()); + src(0,0)=pt.x;src(1,0)=pt.y;src(2,0)=pt.z;src(3,0)=1; + cv::Mat cam_image_point = RTMatrix * src; + cam_image_point = cam_image_point/cv::norm(cam_image_point); + if(cam_image_point.at(2,0)>0.75) + c++; + else break; + } + + if(c==4) + { + std::vector prj_points; + cv::projectPoints(m.second.points, _rvec, _tvec, _cam_params.CameraMatrix, _cam_params.Distorsion, prj_points); + + cv::Rect roi = getRectFrom4Points(prj_points, grey.size()); + if(roi.empty()) + continue; + +#ifdef _show_prj + + for(auto cc:prj_points) + cv::circle(imColor,cc,5,cv::Scalar(255,0,0),-1); +#endif + + //Add border + cv::Point2f tl = cv::Point(roi.tl().x-roi.width*0.5f, roi.tl().y-roi.height*0.5f); + if(tl.x<0) tl.x=0; + if(tl.y<0) tl.y=0; + cv::Point2f br = cv::Point(roi.br().x+roi.width*0.5f, roi.br().y+roi.height*0.5f); + if(br.x>grey.size().width) br.x=grey.size().width; + if(br.y>grey.size().height) br.y=grey.size().height; + + cv::Rect roi_border = cv::Rect(tl,br); + cv::Mat patch = grey(roi_border); + cv::threshold(patch, patch, 125, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); + std::vector> contours; + cv::findContours(patch,contours, cv::noArray(), cv::RETR_LIST, cv::CHAIN_APPROX_NONE); +#ifdef _show_prj + cv::Mat patchColor; + cv::cvtColor(patch, patchColor, CV_GRAY2BGR); +#endif + std::vector approxCurve; + for(unsigned int i = 0; i < contours.size(); i++) + { + cv::approxPolyDP(contours[i], approxCurve, double(contours[i].size()) * 0.05, true); + if (approxCurve.size() == 4 && cv::isContourConvex(approxCurve)) + { + std::vectorcorner_class; + cornerClassifier(patch, approxCurve, corner_class, 2); +// std::cout <<"corner class "<< corner_class[0]<<","<< corner_class[1]<<","< idx4rot; + if(!getMarkerIds(patch, dict.nbits(), approxCurve, idx4rot)) + continue; + else + { + uint64_t code; + for(auto _code_id:dict.getMapCode()) + if(_code_id.second == m.first) + { + code =_code_id.first; + break; + } + + int hamm_dst = dict.nbits(); + unsigned int rotations=0; + for(unsigned int r=0; r<4; r++) + { + int tmp = hamm_distance(idx4rot[r], code); + if(tmp < hamm_dst) + { + hamm_dst = tmp; + rotations = r; + } + } + + int _maxCorrectionAllowed = static_cast( static_cast(dict.tau()) * 0.6); + if(hamm_dst<=_maxCorrectionAllowed) + { + //*** + // for(int jj=0;jj<4; jj++) + // cv::putText(patchColor, std::to_string(jj),approxCurve[jj], 1,1, cv::Scalar(0,0,255),0); + + // std::cout << "New tracker id "< DFCMarkerMapTracker::getDictionaries() + { + return TheDetector.getMarkerLabeler().dynamicCast()->getDictionaries(); + } + + void DFCMarkerMapTracker::track(const cv::Mat& input) + { + __ARUCO_ADDTIMER__; + + if (input.type() == CV_8UC3) + cv::cvtColor(input,grey,CV_BGR2GRAY); + else grey = input; + + __ARUCO_TIMER_EVENT__("ConvertGrey"); + + std::vector detectedMarkers; + if(frame%arucoDetectRate == 0 || hull_factor>::iterator it; + for(it=mapMultiTracker.begin(); it!=mapMultiTracker.end(); ++it) + { + bool update=true; + for(auto marker:detectedMarkers) + if(marker.id == it->first) + { + update = false; + break; + } + + if(update) + if(!it->second->updateImpl(grey)) + mapMultiTracker.erase(it); + } + __ARUCO_TIMER_EVENT__("DCF_Update"); + + + frame ++; + } + + + bool DFCMarkerMapTracker::estimatePose() + { + ScopedTimerEvents Timer("aruco::DFCMarkerMapTracker::estimatePose"); + + + cv::Mat _prevr,_prevt; + if (!getRvec().empty()){ + _prevr=getRvec().clone(); + _prevt=getTvec().clone(); + } + + std::vector p2d; + std::vector p3d; + std::vector trust_weight; + + //Tracked markers + for (auto tm : mapMultiTracker) + { + if (_map_mm.find(tm.first) != _map_mm.end()) + { + float confidence = tm.second->getTrustVal(); + if(confidence > 0) + { + std::vector corners = tm.second->getCorners(); + for(auto c:corners) + p2d.push_back(c); + + if(corners.size() == 1) + { + cv::Point3f cent(0.f, 0.f, 0.f); + for (auto p : _map_mm[tm.first].points) + { + cent.x += p.x; + cent.y += p.y; + cent.z += p.z; + } + cent.x /= 4.f; + cent.y /= 4.f; + cent.z /= 4.f; + + p3d.push_back(cent); + } + else + { + for (auto p : _map_mm[tm.first].points) + p3d.push_back(p); + } + + trust_weight.push_back(confidence); + } + } + } + + if (p2d.size() < 4) + { + // no points in the vector + _rvec = cv::Mat(); + _tvec = cv::Mat(); + hull_factor=-1; + + return false; + } + else + { + if(_rvec.empty()) + { + std::vector vmarker; + for (auto tm : mapMultiTracker) + vmarker.push_back(tm.second->getMarker()); + auto InitialPose=relocalization(vmarker); + + if(InitialPose.empty()) + { + hull_factor=-1; + return false; + } + impl__aruco_getRTfromMatrix44(InitialPose,_rvec,_tvec); + } + + __hub_solve_pnp(p3d,p2d,trust_weight,_cam_params.CameraMatrix, _cam_params.Distorsion, _rvec, _tvec); + + Timer.add("PoseEstimation"); + //Update with new predicted markers!! + if(prediction(p3d, p2d)) + __hub_solve_pnp(p3d,p2d,trust_weight,_cam_params.CameraMatrix, _cam_params.Distorsion, _rvec, _tvec); + Timer.add("Prediction"); + + std::vector hull; + cv::convexHull(p2d, hull); + hull_factor=cv::contourArea(hull)/grey.size().area(); + + return true; + } + } + + void DFCMarkerMapTracker::drawMarkers(cv::Mat& img, float alpha, float trust) + { + int lineSize = img.cols/700; + + for(auto m:mapMultiTracker) + { + //The color of the marker depends on the confidence level (threshold alpha) + cv::Scalar color; + if(m.second->getTrustVal() > alpha) + color = cv::Scalar(0,0, 255); + else + color =cv::Scalar(255,0, 255); + + //Marker border + m.second->getMarker().draw(img, color, lineSize, false, false); + + //Marker info (id, confidence) + std::ostringstream txt; + txt << m.second->getMarker().id; + if(trust) txt <<",w:"<getTrustVal(); + cv::putText(img,txt.str(),m.second->getCenter(), cv::FONT_HERSHEY_SIMPLEX, 1, + color, lineSize); + } + } +} diff --git a/thirdparty/aruco-3.1.12/src/dcf/dcfmarkermaptracker.h b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkermaptracker.h new file mode 100644 index 0000000..51c036a --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkermaptracker.h @@ -0,0 +1,36 @@ +#include "markerdetector.h" +#include "posetracker.h" +#include "trackerimpl.h" + + +namespace aruco { + +class ARUCO_EXPORT DFCMarkerMapTracker : public MarkerMapPoseTracker +{ +public: + DFCMarkerMapTracker(); + void loadParamsFromFile(const std::string &path); + void setDictionary(const std::string &dict, float error_corretion_rate= 0); + std::vector getDictionaries(); + MarkerDetector& getDetector(){return TheDetector;} + void track(const cv::Mat& input); + bool estimatePose(); + void drawMarkers(cv::Mat& img, float alpha=0.8, float trust=true); + bool prediction(std::vector&p3d, std::vector&pd2); + + +private: + MarkerDetector TheDetector; + std::map> mapMultiTracker; + int frame=0; + cv::Mat grey; + double hull_factor; + + //To config + int arucoDetectRate = 10; //Rate to call aruco detect + float tau_c = 0.1; //Threshold hull_factor (tau_c paper) + //Tracker params + double psr=5.7; //threshold peak response + int tau_s=32; //filter size +}; +} diff --git a/thirdparty/aruco-3.1.12/src/dcf/dcfmarkertracker.cpp b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkertracker.cpp new file mode 100644 index 0000000..32541e8 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkertracker.cpp @@ -0,0 +1,148 @@ +#include "dcfmarkertracker.h" +#include "aruco.h" +#include "levmarq.h" //solve pnp of opencv is not trustworthy. Create our own + +#include "../ippe.h" +#include "dictionary_based.h" +#include "aruco_cvversioning.h" + +namespace aruco { + + DFCMarkerTracker::DFCMarkerTracker() + { + } + + + void DFCMarkerTracker::setParams(const aruco::CameraParameters& camPar, float mSize) + { + CamParam = camPar; + MarkerSize = mSize; + } + + std::vector DFCMarkerTracker::getDictionaries() + { + return TheDetector.getMarkerLabeler().dynamicCast()->getDictionaries(); + } + + void DFCMarkerTracker::loadParamsFromFile(const std::string &path) + { + cv::FileStorage fs(path, cv::FileStorage::READ); + if(!fs.isOpened())throw std::runtime_error("Could not open "+path); + + if ( fs["dcf-detectRate"].type()!=cv::FileNode::NONE) + fs["dcf-detectRate"]>>arucoDetectRate; + if ( fs["dcf-psr"].type()!=cv::FileNode::NONE) + fs["dcf-psr"]>>psr; + if ( fs["dcf-tau_s"].type()!=cv::FileNode::NONE) + fs["dcf-tau_s"]>>tau_s; + + TheDetector.loadParamsFromFile(path); + } + + void DFCMarkerTracker::setDictionary(const std::string &dict, float error_corretion_rate) + { + TheDetector.setDictionary(dict, error_corretion_rate); + } + + std::map> DFCMarkerTracker::track(const cv::Mat& input, float thresh_confidence) + { + ScopedTimerEvents Timer("DFCMarkerTracker::track"); + + cv::Mat grey; + if (input.type() == CV_8UC3) + cv::cvtColor(input,grey,CV_BGR2GRAY); + else grey = input; + + Timer.add("ConvertGrey"); + + //Aruco marker detection. Refresh list trackers. + std::vector detectedMarkers; + if(frame%arucoDetectRate == 0 || mapMultiTracker.size()==0) + { + detectedMarkers=TheDetector.detect(grey); + std::cout << "ArUco Detected: "<< detectedMarkers.size() <<" new markers"<< std::endl; + } + + Timer.add("ArucoDetect"); + + //Update markers + std::map>::iterator it; + for(it=mapMultiTracker.begin(); it!=mapMultiTracker.end(); ++it) + { + if(!it->second->updateImpl(grey, thresh_confidence)) + mapMultiTracker.erase(it); + } + + Timer.add("DCF_Update"); + + //Initialize filters for detected markers + std::map> mapNewDetections; + for(auto marker:detectedMarkers) + { + for(auto d:getDictionaries()) + { + // Dictionary to which the marker belongs + if(d.getName() == marker.dict_info) + { + for(auto c_id : d.getMapCode()) + { + //Find marker by its id + if(c_id.second == marker.id) + { + //New tracker! + mapMultiTracker[marker.id] = new TrackerImpl(marker, c_id.first, d.nbits(), grey, psr, tau_s); + break; + } + } + } + } + } + Timer.add("DCF_Prepare"); + frame ++; + + return mapMultiTracker; + } + + void DFCMarkerTracker::estimatePose() + { + if(CamParam.isValid() && MarkerSize>0) + { + ScopedTimerEvents Timer("aruco::DFCMarkerTracker::estimatePose"); + + for(auto m:mapMultiTracker) + mapMultiPose[m.first].estimatePose(m.second->getMarker(), CamParam, MarkerSize); + + Timer.add("pose"); + } + } + + void DFCMarkerTracker::draw(cv::Mat& img, cv::Scalar borderColor,cv::Scalar infoColor, bool trust) + { + for(auto m:mapMultiTracker) + { + Marker marker = m.second->getMarker(); + int lineSize = img.cols/700; + + //Marker info (id, confidence) + std::ostringstream txt; + txt << m.second->getMarker().id; + if(trust) txt <<",w:"<getTrustVal(); + cv::putText(img,txt.str(),m.second->getCenter(), cv::FONT_HERSHEY_SIMPLEX, 2, + infoColor, lineSize); + + //Box and axis + if (CamParam.isValid() && MarkerSize != -1) + { + if (m.second->getMarker().isPoseValid()){ + CvDrawingUtils::draw3dCube(img, marker, CamParam, lineSize); + CvDrawingUtils::draw3dAxis(img, marker, CamParam, lineSize); + } + } + else + { + //Marker border + marker.draw(img, borderColor, lineSize, false, false); + } + } + } +} diff --git a/thirdparty/aruco-3.1.12/src/dcf/dcfmarkertracker.h b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkertracker.h new file mode 100644 index 0000000..0d6683b --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dcf/dcfmarkertracker.h @@ -0,0 +1,38 @@ +#include "markerdetector.h" +#include "posetracker.h" +#include "trackerimpl.h" + + +namespace aruco { + +class ARUCO_EXPORT DFCMarkerTracker +{ +public: + DFCMarkerTracker(); + void setParams(const aruco::CameraParameters& camPar, const float mSize); + void loadParamsFromFile(const std::string &path); + void setDictionary(const std::string &dict, float error_corretion_rate= 0); + + MarkerDetector& getDetector(){return TheDetector;} + std::map> track(const cv::Mat& input, float thresh_confidence=0.f); + std::vector getDictionaries(); + void estimatePose(); + void draw(cv::Mat&img, cv::Scalar borderColor = cv::Scalar(0,0,255), cv::Scalar inforColor = cv::Scalar(255,0,255), bool trust=true); + std::map getMapMultiPose(){return mapMultiPose;}; + +private: + MarkerDetector TheDetector; + std::map> mapMultiTracker; + std::map mapMultiPose; + int frame=0; + aruco::CameraParameters CamParam; + float MarkerSize; + + //To config + int arucoDetectRate = 10; //Rate to call aruco detect + + //Tracker params + double psr=5.7; //threshold peak response + int tau_s=32; //filter size +}; +} diff --git a/thirdparty/aruco-3.1.12/src/dcf/trackerimpl.cpp b/thirdparty/aruco-3.1.12/src/dcf/trackerimpl.cpp new file mode 100644 index 0000000..97f8af9 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dcf/trackerimpl.cpp @@ -0,0 +1,594 @@ +#include "trackerimpl.h" +#include "bitset" +#include "opencv2/calib3d/calib3d.hpp" +#include "dcf_utils.h" + +TrackerImpl::TrackerImpl(const Marker& marker, uint64_t code, uint32_t dic_bits, const cv::Mat& image, double psr, int tau_s) +{ + psrThreshold = psr; + markerWarpSize = tau_s; + cornerPatchSize = cv::Size(tau_s, tau_s); + + TheMarkerCode = code; + nbits = dic_bits; + TheMarker = marker; + prepare(marker, image); +} + + +TrackerImpl::~TrackerImpl() +{ + +} + + +void TrackerImpl::prepare(const Marker& marker,const cv::Mat& image) +{ + int minX=std::numeric_limits::max(), maxX=std::numeric_limits::min(), + minY=std::numeric_limits::max(), maxY=std::numeric_limits::min(); + + for(auto pt:marker) + { + if(pt.xmaxX) maxX=pt.x; + if(pt.y>maxY) maxY=pt.y; + + center.x += pt.x; + center.y += pt.y; + } + + boundingBox = cv::Rect2d(cv::Point2d(minX,minY),cv::Point2d(maxX,maxY)); + center.x /= marker.size(); + center.y /= marker.size(); + + double desiredarea = std::pow(markerWarpSize, 2.f); + double boundingBoxArea = boundingBox.area(); + double pyrfactor2 = pow(pyrfactor, 2); + + int p = 1; + while (boundingBoxArea / pow(pyrfactor2, p) >= desiredarea) + imgPyrIdx = p++; + + int s = int(floor(markerWarpSize * (1+padding))); + patchSize = cv::Size(s, s); + if(contextAware) + s = int(floor(2*markerWarpSize + s)); + roiSize = cv::Size(s, s); + + int realRoiSize = int(floor(s * pow(pyrfactor, imgPyrIdx))); + cv::getRectSubPix(image, cv::Size(realRoiSize, realRoiSize), center, roi); + cv::resize(roi,roi,roiSize); + +// cv::imshow("roi", roi); +// cv::waitKey(); +} + +bool TrackerImpl::initImpl() +{ + if(roi.empty()) + return false; + + ScopedTimerEvents Timer("TrackerImpl::initImpl"); + + createHanningWindow(hanWin, patchSize, CV_32F); + + // goal + cv::Mat g=cv::Mat::zeros(patchSize, CV_32F); + g.at(patchSize.height/2, patchSize.width/2) = 1; + GaussianBlur(g, g, cv::Size(-1,-1), 2.0); + double maxVal; + minMaxLoc(g, 0, &maxVal); + g = g / maxVal; + dft(g, G, cv::DFT_COMPLEX_OUTPUT); + + cv::Mat patch; + //Get the center position + int x = int(floor((roi.cols)/2)); + int y = int(floor((roi.rows)/2)); + cv::Point2f patchCenter(x,y); + getRectSubPix(roi, patchSize, patchCenter, patch); + + // initial A,B and H + A = cv::Mat::zeros(G.size(), G.type()); + B = cv::Mat::zeros(G.size(), G.type()); + C = cv::Mat::zeros(G.size(), G.type()); + cv::Mat WINDOW_WARP, A_i, B_i,C_i; + for(int i=0; i<8; i++) + { + cv::Mat window_warp = randWarp(patch); + preProcess(window_warp, hanWin); + dft(window_warp, WINDOW_WARP, cv::DFT_COMPLEX_OUTPUT); + mulSpectrums(G , WINDOW_WARP, A_i, 0, true); + mulSpectrums(WINDOW_WARP, WINDOW_WARP, B_i, 0, true); + A+=A_i; + B+=B_i; + } + + //now the context + if(contextAware){ + for(int i=0;i<4;i++){ + auto center2=center/pow(pyrfactor, imgPyrIdx); + if( i==0) center2.x+=patchSize.width; + else if(i==1)center2.x-=patchSize.width; + else if( i==2) center2.y+=patchSize.height; + else if(i==3)center2.y-=patchSize.height; + + cv::Mat sub_win; + getRectSubPix(roi, patchSize, center2, sub_win); preProcess(sub_win, hanWin); + dft(sub_win, WINDOW_WARP, cv::DFT_COMPLEX_OUTPUT); + mulSpectrums(WINDOW_WARP, WINDOW_WARP, C_i, 0, true); + C+=C_i; + } + H = divDFTs(A,lambda1+B+lambda2*C); + } + else + H = divDFTs(A,lambda1+B); + + + //Corners!! + createHanningWindow(cornerhanWin, cornerPatchSize, CV_32F); + // goal + cv::Mat gc=cv::Mat::zeros(cornerPatchSize,CV_32F); + gc.at(cornerPatchSize.height/2, cornerPatchSize.width/2) = 1; + GaussianBlur(gc, gc, cv::Size(-1,-1), 2.0); + double maxValc; + minMaxLoc(gc, 0, &maxValc); + gc = gc / maxValc; + dft(gc, Gc, cv::DFT_COMPLEX_OUTPUT); + + // initial corners A,B and H + for(size_t i=0;i<4;i++) + { + Ac.push_back(cv::Mat::zeros(Gc.size(), Gc.type())); + Bc.push_back(cv::Mat::zeros(Gc.size(), Gc.type())); + + cv::Mat sub_win; + cv::Point2d p = patchCenter - (center - TheMarker[i])/pow(pyrfactor, imgPyrIdx); + getRectSubPix(roi, cornerPatchSize, p, sub_win); +// cv::imshow("Roi",roi); +// cv::waitKey(); +// cv::imshow("Patch corner",sub_win); +// cv::waitKey(); + preProcess(sub_win, cornerhanWin); + dft(sub_win, WINDOW_WARP, cv::DFT_COMPLEX_OUTPUT); + mulSpectrums(Gc , WINDOW_WARP, A_i, 0, true); + mulSpectrums(WINDOW_WARP, WINDOW_WARP, B_i, 0, true); + Ac[i]+=A_i; + Bc[i]+=B_i; + Hc.push_back(divDFTs(Ac[i],lambda1+Bc[i])); + } + + return true; +} + +bool TrackerImpl::updateImpl(const cv::Mat& image, float thrs_confidence) +{ + //If H empty try init + if(H.empty()) + if(!initImpl()) + return false; + +// std::cout << "### Update Marker ID : "<< TheMarker.id< psrThreshold) + { + goodCorners++; + TheMarker[i].x += delta_xy.x*pow(pyrfactor, imgPyrIdx); + TheMarker[i].y += delta_xy.y*pow(pyrfactor, imgPyrIdx); + } + } + + if(goodCorners == 4) + { + buildCornersPyramid(image); + cornerUpsample(TheMarker); + + for(size_t i=0;i<4;i++) + { + cv::Point2f cornerOffset = roiCenter + ((TheMarker[i] - center)/pow(pyrfactor, imgPyrIdx)); + getRectSubPix(roi, cornerPatchSize, cornerOffset, img_sub_new); + preProcess(img_sub_new, cornerhanWin); + + // new state for A and B + cv::Mat F, A_new, B_new,C_new; + C_new=cv::Mat::zeros(Gc.size(), Gc.type()); + + dft(img_sub_new, F, cv::DFT_COMPLEX_OUTPUT); + mulSpectrums(Gc, F, A_new, 0, true ); + mulSpectrums(F, F, B_new, 0, true ); + + // update A ,B, and H + Ac[i] = Ac[i]*(1-rate) + A_new*rate; + Bc[i] = Bc[i]*(1-rate) + B_new*rate; + Hc[i] = divDFTs(Ac[i], lambda1+Bc[i]); + } + } + + float minX=std::numeric_limits::max(), maxX=std::numeric_limits::min(), + minY=std::numeric_limits::max(), maxY=std::numeric_limits::min(); + cv::Point2f newCenter(0,0); + cv::Rect2f newBox; + int hamm_dst = nbits; + + + for(size_t i=0;i<4;i++) + { + if(TheMarker[i].xmaxX) maxX=TheMarker[i].x; + if(TheMarker[i].y>maxY) maxY=TheMarker[i].y; + newCenter.x+=TheMarker[i].x; + newCenter.y+=TheMarker[i].y; + } + + newBox = cv::Rect2f(cv::Point2f(minX,minY),cv::Point2f(maxX,maxY)); + if(goodCorners == 4) + { + center.x = newCenter.x/4; + center.y = newCenter.y/4; + + double desiredarea = std::pow(markerWarpSize, 2.f); + double pyrfactor2 = pow(pyrfactor,2); + int p = 1; + while (newBox.area() / pow(pyrfactor2, p) >= desiredarea) + imgPyrIdx = p++; + } + + //Check box edges out image + if(newBox.tl().x < 0.f) newBox.x = 0.f; + if(newBox.tl().y < 0.f) newBox.y = 0.f; + if(newBox.br().x >= image.cols) newBox.width = image.cols - newBox.tl().x; + if(newBox.br().y >= image.rows) newBox.height = image.rows - newBox.tl().y; + ////// + // cv::Mat out; + // cv::cvtColor(image, out, cv::COLOR_GRAY2BGR); + // for(auto pt:TheMarker) + // { + // std::cout << "PT:"<< pt << std::endl; + // cv::circle(out,pt,1,cv::Scalar(0,0,255),-1); + // } + // cv::rectangle(out,newBox,cv::Scalar(0,0,255)); + // std::cout << "BOX:"<< newBox << std::endl; + // cv::imshow("Marker",out); + // cv::waitKey(); + /////// + + + cv::Mat markerThres; + cv::threshold(image(newBox), markerThres, 125, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); + + //Transform marker corners to subImage + std::vector im_corner; + im_corner.push_back(cv::Point2f(TheMarker[0].x-newBox.tl().x, TheMarker[0].y-newBox.tl().y)); + im_corner.push_back(cv::Point2f(TheMarker[1].x-newBox.tl().x, TheMarker[1].y-newBox.tl().y)); + im_corner.push_back(cv::Point2f(TheMarker[2].x-newBox.tl().x, TheMarker[2].y-newBox.tl().y)); + im_corner.push_back(cv::Point2f(TheMarker[3].x-newBox.tl().x, TheMarker[3].y-newBox.tl().y)); + + std::vector rot_ids; + if(!getMarkerIds(markerThres, nbits, im_corner, rot_ids)) + return false; + + +// cv::Mat out; +// cv::cvtColor(markerThres, out, cv::COLOR_GRAY2BGR); +// for(auto pt:vpixels) +// cv::circle(out,pt,1,cv::Scalar(0,0,255),-1); +// cv::imshow("pxis"+std::to_string(TheMarker.id),out); +// cv::waitKey(); + + //Best distance in its 4 rotations + for(auto id:rot_ids) + { + int tmp = hamm_distance(id, TheMarkerCode); + if(tmp < hamm_dst) + hamm_dst = tmp; + } + + //Marker confidence + trust = std::fabs(goodCorners * (hamm_dst/float(nbits) - 1.f)/4); + if(trust < thrs_confidence) + return false; + } + Timer.add("track-corners"); + + + getRectSubPix(roi, patchSize, roiCenter, img_sub_new); + +// cv::imshow("roi", roi); +// cv::waitKey(); +// cv::imshow("pathc", warpmarker); +// cv::waitKey(); + + + if (img_sub_new.channels() != 1) + cvtColor(img_sub_new, img_sub_new, cv::COLOR_BGR2GRAY); + preProcess(img_sub_new, hanWin); + + // new state for A and B + cv::Mat F, A_new, B_new,C_new; + C_new=cv::Mat::zeros(G.size(), G.type()); + + dft(img_sub_new, F, cv::DFT_COMPLEX_OUTPUT); + mulSpectrums(G, F, A_new, 0, true ); + mulSpectrums(F, F, B_new, 0, true ); + + // update A ,B + A = A*(1-rate) + A_new*rate; + B = B*(1-rate) + B_new*rate; + + //Update C if contextAware + if(contextAware){ + cv::Mat C_i,window,WINDOW_WARP; + for(int i=0;i<4;i++){ + auto center2=roiCenter; + if( i==0) center2.x+=patchSize.width; + else if(i==1)center2.x-=patchSize.width; + else if( i==2) center2.y+=patchSize.height; + else if(i==3)center2.y-=patchSize.height; + + getRectSubPix(roi, patchSize, center2, window); + if(window.channels()!=1)cvtColor(window, window, cv::COLOR_BGR2GRAY); + preProcess(window, hanWin); + dft(window, WINDOW_WARP, cv::DFT_COMPLEX_OUTPUT); + mulSpectrums(WINDOW_WARP, WINDOW_WARP, C_i, 0, true); + C+=C_i; + } + + C = C*(1-rate) + C_new*rate; + H = divDFTs(A, lambda1+B+lambda2*C); + } + else + H = divDFTs(A, lambda1+B); + + Timer.add("track-marker"); + + + // Tracked rect + double halfwindowSize = markerWarpSize*pow(pyrfactor, imgPyrIdx)*0.5; + boundingBox = cv::Rect2d(cv::Point2d(center.x-halfwindowSize, center.y-halfwindowSize), + cv::Point2d(center.x+halfwindowSize, center.y+halfwindowSize)); + + return true; +} + +bool TrackerImpl::optimalScale(const cv::Mat& image, cv::Point2f& roiCenter) +{ + if(center.x < 0 || center.x>image.cols ||center.y<0 || center.y>image.rows) + return false; + + //Top and bottom level in the pyramid + int tl = imgPyrIdx-int(multiScaleLevels/2); + if(tl<0) tl = 0; + int bl = imgPyrIdx+int(multiScaleLevels/2); + + cv::Mat region; + cv::Size regionSize = cv::Size(int(floor(roiSize.width*pow(pyrfactor,imgPyrIdx))), int(floor(roiSize.height*pow(pyrfactor,imgPyrIdx)))); + getRectSubPix(image, regionSize, center, region); + + double bestPSR=0; + cv::Point newDelta_xy(0,0); + for(int p=tl; p<=bl; p++) + { + cv::Size nsize(regionSize.width/pow(pyrfactor,p), regionSize.height/pow(pyrfactor,p)); + cv::resize(region, region, nsize); + + cv::Mat image_sub; + getRectSubPix(region, patchSize, cv::Point2d(region.rows*0.5,region.cols*0.5), image_sub); +// cv::imshow("Pathc", image_sub); + preProcess(image_sub, hanWin); + cv::Point delta_xy; + double PSR = correlate(image_sub, delta_xy, H); + +// std::cout < bestPSR){ + bestPSR = PSR; + imgPyrIdx = p; + newDelta_xy = delta_xy; + region.copyTo(roi); + } + } + + //Update ROI center + roiCenter = cv::Point2d(roi.rows*0.5+newDelta_xy.x,roi.cols*0.5+newDelta_xy.y); + + //Delta in original image + newDelta_xy = newDelta_xy*pow(pyrfactor,imgPyrIdx); + + //Apply shift + center.x += newDelta_xy.x; + center.y += newDelta_xy.y; + for(auto &c:TheMarker){ + c.x += newDelta_xy.x; + c.y += newDelta_xy.y; + } + +// std::cout <<"PSR: "<< bestPSR << std::endl; + if(center.x < 0 || center.x>image.cols ||center.y<0 || center.y>image.rows) + return false; + if (bestPSR < psrThreshold) + return false; + + return true; +} + +// Element-wise division of complex numbers in src1 and src2 +cv::Mat TrackerImpl::divDFTs( const cv::Mat &src1, const cv::Mat &src2 ) const +{ + cv::Mat c1[2],c2[2],a1,a2,s1,s2,denom,re,im; + + // split into re and im per src + cv::split(src1, c1); + cv::split(src2, c2); + + // (Re2*Re2 + Im2*Im2) = denom + // denom is same for both channels + cv::multiply(c2[0], c2[0], s1); + cv::multiply(c2[1], c2[1], s2); + cv::add(s1, s2, denom); + + // (Re1*Re2 + Im1*Im1)/(Re2*Re2 + Im2*Im2) = Re + cv::multiply(c1[0], c2[0], a1); + cv::multiply(c1[1], c2[1], a2); + cv::divide(a1+a2, denom, re, 1.0 ); + + // (Im1*Re2 - Re1*Im2)/(Re2*Re2 + Im2*Im2) = Im + cv::multiply(c1[1], c2[0], a1); + cv::multiply(c1[0], c2[1], a2); + cv::divide(a1+a2, denom, im, -1.0); + + // Merge Re and Im back into a complex matrix + cv::Mat dst, chn[] = {re,im}; + cv::merge(chn, 2, dst); + return dst; +} + +void TrackerImpl::preProcess( cv::Mat &window , cv::Mat &hw) const +{ + window.convertTo(window, CV_32F); + log(window + 1., window); + + //normalize + cv::Scalar mean,StdDev; + meanStdDev(window, mean, StdDev); + window = (window-mean[0]) / (StdDev[0]+eps); + + //Gaussain weighting + window = window.mul(hw); +} + +double TrackerImpl::correlate( const cv::Mat &image_sub, cv::Point &delta_xy, const cv::Mat& Hm) const +{ + cv::Mat IMAGE_SUB, RESPONSE, response; + // filter in dft space + dft(image_sub, IMAGE_SUB, cv::DFT_COMPLEX_OUTPUT); + mulSpectrums(IMAGE_SUB, Hm, RESPONSE, 0, true ); + idft(RESPONSE, response, cv::DFT_SCALE|cv::DFT_REAL_OUTPUT); + // update center position + double maxVal; cv::Point maxLoc; + minMaxLoc(response, 0, &maxVal, 0, &maxLoc); + delta_xy.x = maxLoc.x - int(response.size().width/2); + delta_xy.y = maxLoc.y - int(response.size().height/2); + // normalize response + cv::Scalar mean,std; + meanStdDev(response, mean, std); + return (maxVal-mean[0]) / (std[0]+eps); // PSR +} + + +cv::Mat TrackerImpl::randWarp( const cv::Mat& a ) const +{ + cv::RNG rng(8031965); + + // random rotation + double C=0.1; + double ang = rng.uniform(-C,C); + double c=cos(ang), s=sin(ang); + // affine warp matrix + cv::Mat_ W(2,3); + W << c + rng.uniform(-C,C), -s + rng.uniform(-C,C), 0, + s + rng.uniform(-C,C), c + rng.uniform(-C,C), 0; + + // random translation + cv::Mat_ center_warp(2, 1); + center_warp << a.cols/2, a.rows/2; + W.col(2) = center_warp - (W.colRange(0, 2))*center_warp; + + cv::Mat warped; + warpAffine(a, warped, W, a.size(), cv::BORDER_REFLECT); + return warped; +} + +void TrackerImpl::cornerUpsample(std::vector &corners) +{ + if(corners.size()==0) return; + + for(size_t cIdx=0; cIdx<4; cIdx++) + { + cv::Size prevLowResSize= cornerPatchSize; + std::vector tmpcorner={cv::Point2f(cornerPatchSize.width/2, cornerPatchSize.height/2)}; + for(int curpyr=cornersPyrs[cIdx].size()-1;curpyr>=0;curpyr--) + { + float factor= float(cornersPyrs[cIdx][curpyr].cols)/float(prevLowResSize.width); + int halfwsize= 0.5+2.5*factor; + tmpcorner[0] = tmpcorner[0]*factor; + cv::cornerSubPix( cornersPyrs[cIdx][curpyr],tmpcorner,cv::Size(halfwsize,halfwsize),cv::Size(-1,-1),cv::TermCriteria(cv::TermCriteria::MAX_ITER , 4,0.5)); + prevLowResSize=cornersPyrs[cIdx][curpyr].size(); + +// cv::Mat out; +// cv::cvtColor(cornersPyrs[cIdx][curpyr], out, cv::COLOR_GRAY2BGR); +// cv::circle(out,tmpcorner[0],3,cv::Scalar(0,0,255),-1); +// cv::imshow("corner", out); +// cv::waitKey(); + } + + //Update real corner in the original image + corners[cIdx].x += tmpcorner[0].x - cornersPyrs[cIdx][0].cols/2; + corners[cIdx].y += tmpcorner[0].y - cornersPyrs[cIdx][0].rows/2; + +// cv::Mat out; +// cv::cvtColor(cornersPyrs[cIdx][0], out, cv::COLOR_GRAY2BGR); +// cv::circle(out,tmpcorner[0],3,cv::Scalar(0,0,255),-1); +// //cv::resize(out,out,cv::Size(1000,1000)); +// cv::imshow("corner", out); +// cv::waitKey(); + } +} + + +void TrackerImpl::buildCornersPyramid(const cv::Mat& image) +{ + //Size of cornerPatchSize in the original image + cv::Size nsize; + nsize.width = int(ceil(cornerPatchSize.width*pow(pyrfactor,imgPyrIdx))); + nsize.height = int(ceil(cornerPatchSize.height*pow(pyrfactor,imgPyrIdx))); + + //Numer of pyramid images + int npyrimgs = 1; + while (nsize.width/pow(cornersPyrFactor,npyrimgs) > cornerPatchSize.width) + npyrimgs++; + + for(size_t cIdx=0; cIdx<4; cIdx++) + { + cornersPyrs[cIdx].resize(npyrimgs); + getRectSubPix(image, nsize, TheMarker[cIdx], cornersPyrs[cIdx][0]); + + for(int i=1;i +#include +#include + +#include "marker.h" +#include "map" +#include "timers.h" +#include "dictionary_based.h" + +using namespace aruco; + +class TrackerImpl +{ +public: + TrackerImpl(const Marker& marker, uint64_t code, uint32_t nbits, const cv::Mat& image, double psr=5.8, int tau_s=32); + + bool initImpl(); + bool updateImpl(const cv::Mat& imgPyr, float thres_confidence=0); + cv::Rect2d getBoundingBox(){return boundingBox;} + cv::Point2d getCenter(){return center;} + std::vector getCorners(){ + if(track_corners) + return TheMarker; + else + { + return {center}; + } + } + + Marker& getMarker(){return TheMarker;} + float getTrustVal() {return trust;} + + ~TrackerImpl(); + + +private: + void prepare(const Marker& marker, const cv::Mat& imgPyr); + cv::Mat divDFTs( const cv::Mat &src1, const cv::Mat &src2 ) const; + void preProcess( cv::Mat &window, cv::Mat &hw) const; + double correlate( const cv::Mat &image_sub, cv::Point &delta_xy, const cv::Mat& Hm ) const; + cv::Mat randWarp( const cv::Mat& a ) const; + bool optimalScale(const cv::Mat& image, cv::Point2f& roiCenter); + void buildCornersPyramid(const cv::Mat& image); + void cornerUpsample(std::vector &corners); + Marker TheMarker; + uint64_t TheMarkerCode; + + cv::Rect2d boundingBox; + cv::Mat roi; //Region of interest + cv::Point2f center; //Center of the marker + cv::Mat hanWin; + cv::Mat G; //goal + cv::Mat H, A, B,C; //state + + cv::Size patchSize; + cv::Size roiSize; + int imgPyrIdx=0; + bool isInit = false; + uint32_t nbits; //Dictionary bits + float trust = 1; //1 is perfecf match, 0 totally different (max distance) + + double pyrfactor = 1.2; // Pyramid scale + int markerWarpSize; // Desired marker size + const double eps=0.00001; // for normalization + const double rate=0.2; // learning rate + double psrThreshold; // no detection, if PSR is smaller than this + const double lambda1=1e-4; // lamda mosse + const double lambda2=20; // lamda contex aware + bool contextAware=true; + int padding = 2; + int multiScaleLevels = 2; // Should be even (2 mean one level up and one down) + + bool track_corners = true; + cv::Size cornerPatchSize; + + cv::Mat cornerhanWin, Gc; + std::vector Ac, Bc, Hc; //state for corners + std::vector cornersPyrs[4]; //Region corners + double cornersPyrFactor = 4; +}; +#endif // TrackerImpl_H diff --git a/thirdparty/aruco-3.1.12/src/debug.cpp b/thirdparty/aruco-3.1.12/src/debug.cpp new file mode 100644 index 0000000..42b498f --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/debug.cpp @@ -0,0 +1,67 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "debug.h" +#include + namespace aruco{ +int Debug::level=2; +std::map Debug::strings; +void Debug::addString(std::string &label, std::string &data){ + strings.insert(make_pair(label,data)); +} + +std::string Debug::getString(std::string &str){ + auto it=strings.find(str); + if (it==strings.end())return ""; + else return it->second; +} + + +bool Debug::isInited=false; + +void Debug::setLevel ( int l ) { + level=l; + isInited=false; + init(); +} +int Debug::getLevel() { + init(); + return level; +} +void Debug::init() { + if ( !isInited ) { + isInited=true; + if ( level>=1 ) { + } + } + +} + + +} + diff --git a/thirdparty/aruco-3.1.12/src/debug.h b/thirdparty/aruco-3.1.12/src/debug.h new file mode 100644 index 0000000..38ad043 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/debug.h @@ -0,0 +1,113 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef __Debug_H +#define __Debug_H +#include +#include +#include +#include "aruco_export.h" +#include +#include +namespace aruco{ + +class ARUCO_EXPORT Debug{ +private: + static int level;//0(no debug), 1 medium, 2 high + static bool isInited; + + static std::map strings; +public: + static void init(); + static void setLevel(int l); + static int getLevel(); + + + + static void addString(std::string &label,std::string &data); + static std::string getString(std::string &str); + + +static std::string getFileName(std::string filepath){ + //go backwards until finding a separator or start + size_t i; + for( i=filepath.size()-1;i!=0;i--){ + if ( filepath[i]=='\\' || filepath[i]=='/') break; + } + std::string fn;fn.reserve( filepath.size()-i); + for(size_t s=i;s=level){x}} +#define _debug_exec_( x) x + #ifndef WIN32 + #define _debug_msg(x,level) {Debug::init();\ + if (Debug::getLevel()>=level)\ + std::cout<<"#" <=5)\ + std::cout<<"#" <=level)\ + std::cout<<__func__<<":"<< Debug::getFileName(__FILE__)<<":"<<__LINE__ <<": "<=5)\ + std::cout<<__func__<<":"<< Debug::getFileName(__FILE__)<<":"<<__LINE__ <<": "< +#include +#include +#include +#include +#include +#include +#include "markermap.h" +#include +#include +using namespace std; +namespace aruco{ + + +Dictionary Dictionary::load(string info){ +if (isPredefinedDictinaryString(info)) + return loadPredefined(info); +else return loadFromFile(info); +} + + +Dictionary Dictionary::loadFromFile(std::string path) { + auto parse=[](const string &str){ + stringstream sstr(str); + string a,b; + sstr>>a>>b;return b; + }; + auto _stoi=[](string str){ + int i;sscanf(str.c_str(),"%d",&i);return i; + }; + + ifstream file(path); + if (!file)throw std::runtime_error("Dictionary::loadFromFile could not open file"); + string line; + std::getline(file,line); + if (line.find("name")==std::string::npos) throw std::runtime_error("Dictionary::loadFromFile 'name' not found"); + Dictionary d; + d._name=parse(line); + std::getline(file,line); + if (line.find("nbits")==std::string::npos) throw std::runtime_error("Dictionary::loadFromFile 'nbits' not found"); + d._nbits=_stoi(parse(line)); + if (d._nbits>64)throw std::runtime_error("Dictionary::loadFromFile no more than 64 bits allowed "); + + + d._type=CUSTOM; + //go reading the data + while(!file.eof()){ + std::getline(file,line); + if (line.size()==d._nbits){ + bitset<64> marker; + //parse the bits + int idx=0; + for(auto it=line.rbegin();it!=line.rend() ;it++){ + marker[idx++]=*it=='1'; + } + d._code_id.insert({marker.to_ullong(), static_cast(d._code_id.size())}); + } + } + d._tau= static_cast(computeDictionaryDistance(d)); + if (d._tau==0){ + cerr<<"IMPORTANT MESSAGE:::: Your dictionary "< codes={0xf89c68ea2UL,0xf021c83fdUL,0x9b2f835a2UL,0xf8ffdb019UL,0xf2d12b272UL,0xf0e6afe8bUL,0xe19dee435UL,0xdbe424132UL,0xa9885a341UL,0x3add1e6caUL,0x600aa0d15UL,0xf9d5c0938UL,0xf85b0f3d4UL,0xf838bcd6fUL,0xfa6c8bf2dUL,0xfb469060cUL,0xfb25238b7UL,0xff8d4dc33UL,0xfc3406a26UL,0xfc57b549dUL,0xfcf361750UL,0xfd7daedbcUL,0xf42d724b4UL,0xf1ccb47aaUL,0xe1fe5da8eUL,0xe2e3c2f56UL,0xe280711edUL,0xe224a5220UL,0xe36d0d5baUL,0xed2cf4e23UL,0xecc188a74UL,0xcf7ea3892UL,0xcca45b03cUL,0xc4de9c015UL,0xc0b1959e7UL,0xd027a870eUL,0xd1a967de2UL,0xd3fd50fa0UL,0xd67f25205UL,0xdcf5013a3UL,0xdea1361e1UL,0xdb8797f89UL,0xd8f9bb4eaUL,0x98321c07aUL,0x9dd3da364UL,0x9162c0972UL,0x91c614abfUL,0x81339aaedUL,0x3f3cd85d4UL,0x3907e6e64UL,0x2231e480aUL,0x287ca74daUL,0xee52d854UL,0x3b94b615UL,0x1ab8cdc82UL,0x4463c9014UL,0x6588d50b0UL,0xf912a744eUL,0xf97114af5UL,0xfb81f7b7aUL,0xfbe2445c1UL,0xff4a2a145UL,0xfea756512UL,0xfe03826dfUL,0xfc90d29ebUL,0xfdbac90caUL,0xfdd97ae71UL,0xf564da32eUL,0xf5c00e0e3UL,0xf679456f6UL,0xf6be22b80UL,0xf6dd9153bUL,0xf7f78ac1aUL,0xf35fe489eUL,0xf33c57625UL,0xf398835e8UL,0xf3fb30b53UL,0xf2b298cc9UL,0xf2164cf04UL,0xf275ff1bfUL,0xf10bd3adcUL,0xf16860467UL,0xe1393a7f8UL,0xe07092062UL,0xe0b7f5d14UL,0xe0d4463afUL,0xe7c56313eUL,0xe7a6d0f85UL,0xe70204c48UL,0xe6281f569UL,0xe47c2872bUL,0xed8820deeUL,0xedeb93355UL,0xed4f47098UL,0xec06ef702UL,0xeef60c68dUL,0xef78c3c61UL,0xefbfa4117UL,0xebb31e65eUL,0xebd0ad8e5UL,0xeb7479b28UL,0xeb17ca593UL,0xea5e62209UL,0xeafab61c4UL,0xe8ae81386UL,0xe9204e96aUL,0xe9e72941cUL,0xe9849aaa7UL,0xc86f86a03UL,0xc9e1490efUL,0xcbb57e2adUL,0xcb7219fdbUL,0xca58026faUL,0xca9f65b8cUL,0xcef06c27eUL,0xce54b81b3UL,0xcf1d10629UL,0xcd8e4091dUL,0xcc633cd4aUL,0xccc7e8e87UL,0xc7c3035cdUL,0xc767d7600UL,0xc62e7f19aUL,0xc64dccf21UL,0xc6e918cecUL,0xc68aab257UL,0xc2861151eUL,0xc24176868UL,0xc15ce9db0UL,0xc0d22675cUL,0xd0e0cfa78UL,0xd0837c4c3UL,0xd16e00094UL,0xd2739f54cUL,0xd2102cbf7UL,0xd6dbf11c8UL,0xd79259652UL,0xd7368d59fUL,0xd562ba7ddUL,0xd5c66e410UL,0xd5a5ddaabUL,0xd48fc638aUL,0xd4ec75d31UL,0xdc96b2d18UL,0xddbca9439UL,0xdf4c4a5b6UL,0xde05e222cUL,0xdaad8c6a8UL,0xdace3f813UL,0xd9b013370UL,0xd9d3a0dcbUL,0xd85d6f727UL,0x9aa14cf4eUL,0x9ac2ff1f5UL,0x9b4c30b19UL,0x9be8e48d4UL,0x9b8b5766fUL,0x99bcd3a96UL,0x99df6042dUL,0x96b482695UL,0x979e99fb4UL,0x95caaedf6UL,0x950dc9080UL,0x956e7ae3bUL,0x9427d29a1UL,0x908fbcd25UL,0x9392238fdUL,0x92b8381dcUL,0x824db618eUL,0x83c379b62UL,0x83041e614UL,0x81f4fd79bUL,0x80dee6ebaUL,0x8019813ccUL,0x853f20da4UL,0x87cfc3c2bUL,0x8e5878855UL,0x8fb504c02UL,0x8b1d6a886UL,0x8a93a526aUL,0xabdc6d103UL,0xaa3111554UL,0xaa52a2befUL,0xa8a241a60UL,0xa8c1f24dbUL,0xa92c8e08cUL,0xad438797eUL,0xa156490a5UL,0xa0d886a49UL,0xb04ebb4a0UL,0xb1c074e4cUL,0xb616361abUL,0xb73c2d88aUL,0xbf2559618UL,0xbd716e45aUL,0xb8347c489UL,0xb890a8744UL,0xba604b6cbUL,0xbb29e3151UL,0x3c42f4eb7UL,0x388929488UL,0x3b94b6150UL,0x33ee71179UL,0x311e920f6UL,0x30905da1aUL,0x343833e9eUL,0x35b6fc472UL,0x360fb7267UL,0x26998ac8eUL,0x271745662UL,0x20a2b473eUL,0x20c107985UL,0x2188afe1fUL,0x23bf2b2e6UL,0x229530bc7UL,0x2a2890698UL,0x2ba65fc74UL,0x281f14a61UL,0x2fc956586UL,0x2f6d8264bUL,0x2e80fe21cUL,0xe869e6efUL,0xc767d760UL,0x8bda0d5fUL,0xae997f1dUL,0xa2ef026bUL,0xb04ebb4aUL,0xb67585f1UL,0x37e2cb63UL,0x25437242UL,0x149a879aUL,0x69fea87dUL,0x71125291UL,0x1609d7694UL,0x143e53a6dUL,0x11bc267c8UL,0x12c20acabUL,0x132f768fcUL,0x134cc5647UL,0x1a1c19f4fUL,0x182b9d3b6UL,0x1c27274ffUL,0x5a10d96a9UL,0x50f94e9b4UL,0x509afd70fUL,0x57e86bb25UL,0x56c270204UL,0x477e565ccUL,0x43115fc3eUL,0x493fafe55UL,0x48b1604b9UL,0x4a2230b8dUL,0x6cd8099b8UL,0x6d3575defUL,0x6bc92cb29UL,0x687067d3cUL,0x652c0137dUL,0x757d5b0e2UL,0x76a7a384cUL,0x72ab19f05UL,0x73e2b189fUL,0x709c9d3fcUL,0x7885e9d6eUL,0x7bfbc560dUL,0x7e79b0ba8UL,0x7dc0fbdbdUL,0x7d642fe70UL,0x7c4e34751UL,0xf9b673783UL,0xfa0f38196UL,0xfac85fce0UL,0xfaabec25bUL,0xff2999ffeUL,0xfec4e5ba9UL,0xfd1e1d307UL,0xf50769d95UL,0xf5a3bde58UL,0xf489a6779UL,0xf44ec1a0fUL,0xf794392a1UL,0xf7535efd7UL,0xf0427bd46UL,0xf1af07911UL,0xe15a89943UL,0xe01321ed9UL,0xe24716c9bUL,0xe30ebeb01UL,0xe3aa6a8ccUL,0xe3c9d9677UL,0xe761b72f3UL,0xe64bacbd2UL,0xe68ccb6a4UL,0xe4d8fc4e6UL,0xe4bb4fa5dUL,0xe41f9b990UL,0xe55633e0aUL,0xe535800b1UL,0xe5915437cUL,0xe5f2e7dc7UL,0xec655c9b9UL,0xeca23b4cfUL,0xee95bf836UL,0xee52d8540UL,0xee316bbfbUL,0xef1b702daUL,0xefdc17facUL,0xea3dd1cb2UL,0xea9905f7fUL,0xe869e6ef0UL,0xe80a5504bUL,0xe943fd7d1UL,0xc8cb529ceUL,0xc8a8e1775UL,0xc80c354b8UL,0xc9459d322UL,0xc982fae54UL,0xcbd6cdc16UL,0xcb11aa160UL,0xca3bb1841UL,0xcafcd6537UL,0xce93dfcc5UL,0xce370bf08UL,0xcfb9c45e4UL,0xcfda77b5fUL,0xcdedf37a6UL,0xcd2a94ad0UL,0xcd492746bUL,0xcc008f3f1UL,0xc4bd2feaeUL,0xc419fbd63UL,0xc533e0442UL,0xc55053af9UL,0xc5f487934UL,0xc5973478fUL,0xc7a0b0b76UL,0xc704648bbUL,0xc2e5a2ba5UL,0xc308deff2UL,0xc19b8e0c6UL,0xc01541a2aUL,0xd0441b9b5UL,0xd1cad4359UL,0xd10db3e2fUL,0xd33a372d6UL,0xd35984c6dUL,0xd2b4f883aUL,0xd2d74b681UL,0xd50109966UL,0xd448a1efcUL,0xdc51d506eUL,0xdd187d7f4UL,0xdd7bce94fUL,0xdf2ff9b0dUL,0xdf8b2d8c0UL,0xdfe89e67bUL,0xdec285f5aUL,0xde6651c97UL,0xda6aebbdeUL,0xda0958565UL,0xdb2343c44UL,0xd97774e06UL,0xd914c70bdUL,0xd89a08a51UL,0xd83edc99cUL,0x9a662b238UL,0x9a0598c83UL,0x997bb47e0UL,0x99180795bUL,0x9851afec1UL,0x9896c83b7UL,0x9c9a724feUL,0x9cf9c1a45UL,0x9c5d15988UL,0x9c3ea6733UL,0x9d14bde12UL,0x9d770e0a9UL,0x9db069ddfUL,0x9f87ed126UL,0x9fe45ef9dUL,0x9f408ac50UL,0x9f23392ebUL,0x9e6a91571UL,0x9ece456bcUL,0x96d73182eUL,0x961056558UL,0x9673e5be3UL,0x9759fe2c2UL,0x973a4dc79UL,0x95a91d34dUL,0x94446171aUL,0x94e0b54d7UL,0x90ec0f39eUL,0x902b68ee8UL,0x9048db053UL,0x9101737c9UL,0x93f190646UL,0x93554458bUL,0x921cec211UL,0x82e962243UL,0x83a0ca5d9UL,0x8367ad8afUL,0x815029456UL,0x81974e920UL,0x80bd55001UL,0x807a32d77UL,0x847688a3eUL,0x84153b485UL,0x84b1ef748UL,0x84d25c9f3UL,0x85f8470d2UL,0x855c9331fUL,0x876b17fe6UL,0x87ac70290UL,0x86e5d850aUL,0x8622bf87cUL,0x8e3bcb6eeUL,0x8efcacb98UL,0x8f7263174UL,0x8f11d0fcfUL,0x8d2654336UL,0x8d45e7d8dUL,0x8de133e40UL,0x8d82800fbUL,0x8ca89b9daUL,0x8c6ffc4acUL,0x8c0c4fa17UL,0x8800f5d5eUL,0x8863463e5UL,0x88c792028UL,0x88a421e93UL,0x898e3a7b2UL,0x89ed89909UL,0x89495dac4UL,0x892aee47fUL,0x8bda0d5f0UL,0x8bb9beb4bUL,0x8af016cd1UL,0x8a54c2f1cUL,0x8a37711a7UL,0xab1b0ac75UL,0xaa95c5699UL,0xa86526716UL,0xa806959adUL,0xa9ebe9dfaUL,0xa94f3de37UL,0xad20347c5UL,0xade753ab3UL,0xaccd48392UL,0xacaefbd29UL,0xac0a2fee4UL,0xae5e18ca6UL,0xae3dab21dUL,0xae997f1d0UL,0xafd0d764aUL,0xafb3648f1UL,0xa70ec45aeUL,0xa76d77b15UL,0xa7c9a38d8UL,0xa7aa10663UL,0xa6800bf42UL,0xa6e3b81f9UL,0xa624dfc8fUL,0xa4135b076UL,0xa4d43cd00UL,0xa59d94a9aUL,0xa5fe27421UL,0xa55af37ecUL,0xa135fae1eUL,0xa1f29d368UL,0xa1912edd3UL,0xa0bb354f2UL,0xa24bd657dUL,0xa2ef026b0UL,0xa28cb180bUL,0xa3a6aa12aUL,0xa361cdc5cUL,0xa3027e2e7UL,0xb39443c0eUL,0xb3f7f02b5UL,0xb35324178UL,0xb33097fc3UL,0xb2793f859UL,0xb2ddebb94UL,0xb089dc9d6UL,0xb0ea6f76dUL,0xb02d08a1bUL,0xb1071333aUL,0xb164a0d81UL,0xb5af7d7beUL,0xb5ccce905UL,0xb5681aac8UL,0xb50ba9473UL,0xb421b2d52UL,0xb442013e9UL,0xb4e6d5024UL,0xb48566e9fUL,0xb6b2e2266UL,0xb6d151cddUL,0xb67585f10UL,0xb75f9e631UL,0xb7fb4a5fcUL,0xbfe23eb6eUL,0xbf46ea8a3UL,0xbe6cf1182UL,0xbeab96cf4UL,0xbec82524fUL,0xbc9c1200dUL,0xbc38c63c0UL,0xbc5b75d7bUL,0xbdb60992cUL,0xbdd5ba797UL,0xb9d9000deUL,0xb9bab3e65UL,0xb91e67da8UL,0xb857cfa32UL,0xb8f31b9ffUL,0xbac49f506UL,0xbaa72cbbdUL,0xba03f8870UL,0xbb4a50feaUL,0xbb8d3729cUL,0xbbee84c27UL,0x3e757024eUL,0x3e16c3cf5UL,0x3eb217f38UL,0x3ed1a4183UL,0x3ffbbf8a2UL,0x3f980c619UL,0x3f5f6bb6fUL,0x3d0b5c92dUL,0x3dcc3b45bUL,0x3ce620d7aUL,0x3c85933c1UL,0x3c214700cUL,0x384e4e9feUL,0x38ea9aa33UL,0x39c081312UL,0x39a332da9UL,0x3964550dfUL,0x3b53d1c26UL,0x3b306229dUL,0x3bf705febUL,0x3a1a79bbcUL,0x3a79ca507UL,0x32030d52eUL,0x3260beb95UL,0x32c46a858UL,0x32a7d96e3UL,0x338dc2fc2UL,0x334aa52b4UL,0x332916c0fUL,0x317d21e4dUL,0x31d9f5d80UL,0x31ba4633bUL,0x30f3ee4a1UL,0x30573a76cUL,0x3034899d7UL,0x345b80025UL,0x34ff543e8UL,0x349ce7d53UL,0x35d54fac9UL,0x3512287bfUL,0x37461f5fdUL,0x37e2cb630UL,0x37817888bUL,0x36ab631aaUL,0x36c8d0f11UL,0x366c04cdcUL,0x265eed1f8UL,0x2774f68d9UL,0x27b3915afUL,0x258415956UL,0x25e7a67edUL,0x254372420UL,0x2520c1a9bUL,0x240ada3baUL,0x246969d01UL,0x24cdbdeccUL,0x24ae0e077UL,0x2006604f3UL,0x23dc98c5dUL,0x23784cf90UL,0x231bff12bUL,0x2252576b1UL,0x22f68357cUL,0x2aeff7beeUL,0x2a8c44555UL,0x2a4b23823UL,0x2b6138102UL,0x29f268e36UL,0x29350f340UL,0x2956bcdfbUL,0x28d873717UL,0x2cd4c905eUL,0x2cb77aee5UL,0x2c13aed28UL,0x2d5a06ab2UL,0x2faae5b3dUL,0x2f0e318f0UL,0x2e4799f6aUL,0x2ee34dca7UL,0xfac85fceUL,0xf6be22b8UL,0xe224a522UL,0xe41f9b99UL,0xcd2a94adUL,0xd3fd50faUL,0x9f408ac5UL,0x950dc908UL,0x9336f7b3UL,0x81974e92UL,0x87ac7029UL,0x8de133e4UL,0xa4d43cd0UL,0xbc38c63cUL,0xba03f887UL,0x3daf88aeUL,0x31d9f5d8UL,0x29350f34UL,0xc767d76UL,0xa4d43cdUL,0x63b3ebbUL,0x12a1b921UL,0x1ed7c457UL,0x526a1e68UL,0x545120d3UL,0x40cba749UL,0x4a86e484UL,0x6fc596c6UL,0x63b3ebb0UL,0x77296c2aUL,0x7b5f115cUL,0x7d642fe7UL,0x17407f10eUL,0x1723ccfb5UL,0x17e4ab2c3UL,0x16ceb0be2UL,0x16ad03559UL,0x14f93471bUL,0x15d32fe3aUL,0x15b09c081UL,0x15144834cUL,0x1577fbdf7UL,0x117b41abeUL,0x1118f2405UL,0x11df95973UL,0x10963dee9UL,0x1032e9d24UL,0x1266def66UL,0x12056d1ddUL,0x12a1b9210UL,0x13e81158aUL,0x1b55b18d5UL,0x1bf165b18UL,0x1b92d65a3UL,0x1adb7e239UL,0x1a7faa1f4UL,0x18482ed0dUL,0x18ecfaec0UL,0x188f4907bUL,0x19a55295aUL,0x190186a97UL,0x1dca5b0a8UL,0x1da9e8e13UL,0x1c83f3732UL,0x1ce040989UL,0x1c4494a44UL,0x1e10a3806UL,0x1ed7c4570UL,0x1eb477bcbUL,0x1f9e6c2eaUL,0x1ffddfc51UL,0x1f590bf9cUL,0x1f3ab8127UL,0x5da5287f5UL,0x5d624fa83UL,0x5c2be7d19UL,0x5edb04c96UL,0x5e1c631e0UL,0x5e7fd0f5bUL,0x5f92acb0cUL,0x5ff11f5b7UL,0x5b9e16c45UL,0x5b5971133UL,0x5a736a812UL,0x5ab40d564UL,0x58e03a726UL,0x58838999dUL,0x596ef5dcaUL,0x59a9920bcUL,0x59ca21e07UL,0x51b0e6e2eUL,0x51d355095UL,0x511432de3UL,0x503e294c2UL,0x52ceca54dUL,0x526a1e680UL,0x5209ad83bUL,0x5323b611aUL,0x53e4d1c6cUL,0x5387622d7UL,0x574cbf8e8UL,0x572f0c653UL,0x560517f72UL,0x56a1c3cbfUL,0x549647046UL,0x54f5f4efdUL,0x545120d30UL,0x557b3b411UL,0x55bc5c967UL,0x45ed06af8UL,0x458eb5443UL,0x44a4aed62UL,0x44007aeafUL,0x4637fe256UL,0x46f099f20UL,0x47da82601UL,0x4372ec285UL,0x429f906d2UL,0x4258f7ba4UL,0x423b4451fUL,0x40cba7490UL,0x40a814a2bUL,0x41820f30aUL,0x41e1bcdb1UL,0x414568e7cUL,0x495c1c0eeUL,0x499b7bd98UL,0x49f8c8323UL,0x4815b4774UL,0x4a4183536UL,0x4a86e4840UL,0x4ae5576fbUL,0x4bcf4cfdaUL,0x4bacff161UL,0x4b082b2acUL,0x4b6b98c17UL,0x4f04915e5UL,0x4fc3f6893UL,0x4ee9ed1b2UL,0x4e8a5ef09UL,0x4e2e8acc4UL,0x4e4d3927fUL,0x4c7abde86UL,0x4c190e03dUL,0x4cbdda3f0UL,0x4cde69d4bUL,0x4df47246aUL,0x4d97c1ad1UL,0x4d331591cUL,0x6c1f6e4ceUL,0x6c7cdda75UL,0x6cbbba703UL,0x6d91a1e22UL,0x6df212099UL,0x6d56c6354UL,0x6f6142fadUL,0x6fc596c60UL,0x6e8c3ebfaUL,0x6eef8d541UL,0x6e28ea837UL,0x6a2450f7eUL,0x6a47e31c5UL,0x6ae337208UL,0x6baa9f592UL,0x6b6df88e4UL,0x6b0e4b65fUL,0x6939cfaa6UL,0x69fea87d0UL,0x699d1b96bUL,0x68b70004aUL,0x68d4b3ef1UL,0x6813d4387UL,0x6069133aeUL,0x60ae74ed8UL,0x60cdc7063UL,0x61846f7f9UL,0x6120bb434UL,0x614308a8fUL,0x63748c676UL,0x63173f8cdUL,0x63d0585bbUL,0x62fa43c9aUL,0x6299f0221UL,0x66522d81eUL,0x66319e6a5UL,0x66954a568UL,0x66f6f9bd3UL,0x67bf51c49UL,0x671b85f84UL,0x67783613fUL,0x654fb2dc6UL,0x64c17d72aUL,0x64a2ce991UL,0x64061aa5cUL,0x6465a94e7UL,0x74f394a0eUL,0x7490274b5UL,0x7434f3778UL,0x77ee0bfd6UL,0x778db816dUL,0x774adfc1bUL,0x7660c453aUL,0x760377b81UL,0x720fcdcc8UL,0x726c7e273UL,0x734665b52UL,0x7325d65e9UL,0x738102624UL,0x71d535466UL,0x711252910UL,0x7171e17abUL,0x705bfae8aUL,0x70ff2ed47UL,0x78213dea3UL,0x790b26782UL,0x79cc41af4UL,0x7b98768b6UL,0x7b5f115c0UL,0x7b3ca2b7bUL,0x7a16b925aUL,0x7a750ace1UL,0x7ad1def2cUL,0x7ab26d197UL,0x7ebed76deUL,0x7edd64865UL,0x7e1a03513UL,0x7f3018c32UL,0x7f53ab289UL,0x7ff77f144UL,0x7f94ccfffUL,0x7da348306UL,0x7d079c0cbUL,0x7c8953a27UL,0xffeefe288UL,0xfe6031864UL,0xf4ea159c2UL,0xf61af684dUL,0xf730ed16cUL,0xf0851c030UL,0xe6ef7881fUL,0xe8cd32d3dUL,0xc9262ed99UL,0xc47a483d8UL,0xc222c56d3UL,0xc36b6d149UL,0xc3ac0ac3fUL,0xc1f83de7dUL,0xc13f5a30bUL,0xc076f2491UL,0xd39ee311bUL,0xd61c96cbeUL,0xd6b842f73UL,0xd7f1ea8e9UL,0xd7553eb24UL,0xd42b12047UL,0xdc3266ed5UL,0xdddf1aa82UL,0x9e0922bcaUL,0x9eadf6807UL,0x97fd2a10fUL,0x948306a6cUL,0x91a5a7404UL,0x9336f7b30UL,0x927f5fcaaUL,0x92db8bf67UL,0x822e05f35UL,0x828ad1cf8UL,0x859bf4e69UL,0x8708a415dUL,0x86866bbb1UL,0x86410c6c7UL,0x8e9f1f523UL,0x8fd6b72b9UL,0x8ccb28761UL,0xabbfdefb8UL,0xaaf676822UL,0xad84e0408UL,0xac699c05fUL,0xaefaccf6bUL,0xaf17b0b3cUL,0xaf7403587UL,0xa6476c234UL,0xa470e8ecdUL,0xa4b78f3bbUL,0xa53940957UL,0xa07c52984UL,0xa01fe173fUL,0xa22865bc6UL,0xa3c519f91UL,0xb21a8c6e2UL,0xb2be5852fUL,0xb1a3c70f7UL,0xb798f9b47UL,0xbf818d5d5UL,0xbe0f42f39UL,0xbcffa1eb6UL,0xbd12ddae1UL,0xb97dd4313UL,0x3d68ef796UL,0x3daf88ae0UL,0x382dfd745UL,0x3abead871UL,0x35719b904UL,0x3725acb46UL,0x26fa39235UL,0x263d5ef43UL,0x27d022b14UL,0x2065d3a48UL,0x212c7bdd2UL,0x214fc8369UL,0x21eb1c0a4UL,0x2b028bfb9UL,0x2bc5ec2cfUL,0x2991db08dUL,0x28bbc09acUL,0x2c701d393UL,0x2d39b5409UL,0x2d9d617c4UL,0x2dfed297fUL,0x2e242a1d1UL,0xfcf36175UL,0xc15ce9dbUL,0xd5c66e41UL,0xdf8b2d8cUL,0x997bb47eUL,0xa8a241a6UL,0x23784cf9UL,0x2f0e318fUL,0x5e1c631eUL,0x58275da5UL,0x4cbdda3fUL,0x6588d50bUL,0x178718c78UL,0x166a6482fUL,0x145de04d6UL,0x149a879a0UL,0x10f58e052UL,0x10515a39fUL,0x138ba2b31UL,0x19c6e17e1UL,0x19623542cUL,0x1d0d3cddeUL,0x1d6e8f365UL,0x1e73106bdUL,0x5dc69b94eUL,0x5d01fc438UL,0x5c48543a2UL,0x5c8f33ed4UL,0x5cec8006fUL,0x5eb8b722dUL,0x5f55cb67aUL,0x5f36788c1UL,0x5bfda52feUL,0x5b3ac2f88UL,0x58275da50UL,0x5844ee4ebUL,0x590d46371UL,0x517781358UL,0x505d9aa79UL,0x52ad79bf6UL,0x534005fa1UL,0x578bd859eUL,0x5666a41c9UL,0x54329338bUL,0x55dfef7dcUL,0x452a6178eUL,0x4549d2935UL,0x44c71d3d9UL,0x46544dcedUL,0x46932a19bUL,0x47b9318baUL,0x471de5b77UL,0x43d638148UL,0x43b58bff3UL,0x42fc23869UL,0x400cc09e6UL,0x406f7375dUL,0x4126db0c7UL,0x48d2d3a02UL,0x4876079cfUL,0x4f6722b5eUL,0x4fa045628UL,0x4d50a67a7UL,0x6f02f1116UL,0x6fa6252dbUL,0x6e4b5968cUL,0x6a8084cb3UL,0x695a7c41dUL,0x61e7dc942UL,0x623d241ecUL,0x625e97f57UL,0x67dce22f2UL,0x65eb66e0bUL,0x7457409c3UL,0x751ee8e59UL,0x75d98f32fUL,0x77296c2a0UL,0x76c4106f7UL,0x72c8aa1beUL,0x71b686addUL,0x703849031UL,0x78e65a3d5UL,0x78428e018UL,0x796895939UL,0x79aff244fUL,0x7c2d879eaUL,0x7ceae049cUL,0xc3cfb9284UL,0xdb40f02ffUL,0x8b7ed963dUL,0xab78b92ceUL,0xf0851c03UL,0xcb11aa16UL,0xd9b01337UL,0x18ecfaecUL,0x46f099f2UL,0x1b360266eUL,0x5ad7bebdfUL,0x551888aaaUL,0x63b3ebb00UL,0x75ba3cd94UL,0x98f57bd0cUL,0xf912a744eUL}; + fromVector(codes,d._code_id); + d._nbits=36; + d._tau=0; + d._type=ARTAG; + d._name="ARTAG"; + + }break; + case ARTOOLKITPLUS:{ + vector codes={0x6dc269c27UL,0x6d4229e26UL,0x6cc2e9825UL,0x6c42a9a24UL,0x6fc369423UL,0x6f4329622UL,0x6ec3e9021UL,0x6e43a9220UL,0x69c068c2fUL,0x694028e2eUL,0x68c0e882dUL,0x6840a8a2cUL,0x6bc16842bUL,0x6b412862aUL,0x6ac1e8029UL,0x6a41a8228UL,0x65c66bc37UL,0x65462be36UL,0x64c6eb835UL,0x6446aba34UL,0x67c76b433UL,0x67472b632UL,0x66c7eb031UL,0x6647ab230UL,0x61c46ac3fUL,0x61442ae3eUL,0x60c4ea83dUL,0x6044aaa3cUL,0x63c56a43bUL,0x63452a63aUL,0x62c5ea039UL,0x6245aa238UL,0x7dca6dc07UL,0x7d4a2de06UL,0x7ccaed805UL,0x7c4aada04UL,0x7fcb6d403UL,0x7f4b2d602UL,0x7ecbed001UL,0x7e4bad200UL,0x79c86cc0fUL,0x79482ce0eUL,0x78c8ec80dUL,0x7848aca0cUL,0x7bc96c40bUL,0x7b492c60aUL,0x7ac9ec009UL,0x7a49ac208UL,0x75ce6fc17UL,0x754e2fe16UL,0x74ceef815UL,0x744eafa14UL,0x77cf6f413UL,0x774f2f612UL,0x76cfef011UL,0x764faf210UL,0x71cc6ec1fUL,0x714c2ee1eUL,0x70ccee81dUL,0x704caea1cUL,0x73cd6e41bUL,0x734d2e61aUL,0x72cdee019UL,0x724dae218UL,0x4dd261c67UL,0x4d5221e66UL,0x4cd2e1865UL,0x4c52a1a64UL,0x4fd361463UL,0x4f5321662UL,0x4ed3e1061UL,0x4e53a1260UL,0x49d060c6fUL,0x495020e6eUL,0x48d0e086dUL,0x4850a0a6cUL,0x4bd16046bUL,0x4b512066aUL,0x4ad1e0069UL,0x4a51a0268UL,0x45d663c77UL,0x455623e76UL,0x44d6e3875UL,0x4456a3a74UL,0x47d763473UL,0x475723672UL,0x46d7e3071UL,0x4657a3270UL,0x41d462c7fUL,0x415422e7eUL,0x40d4e287dUL,0x4054a2a7cUL,0x43d56247bUL,0x43552267aUL,0x42d5e2079UL,0x4255a2278UL,0x5dda65c47UL,0x5d5a25e46UL,0x5cdae5845UL,0x5c5aa5a44UL,0x5fdb65443UL,0x5f5b25642UL,0x5edbe5041UL,0x5e5ba5240UL,0x59d864c4fUL,0x595824e4eUL,0x58d8e484dUL,0x5858a4a4cUL,0x5bd96444bUL,0x5b592464aUL,0x5ad9e4049UL,0x5a59a4248UL,0x55de67c57UL,0x555e27e56UL,0x54dee7855UL,0x545ea7a54UL,0x57df67453UL,0x575f27652UL,0x56dfe7051UL,0x565fa7250UL,0x51dc66c5fUL,0x515c26e5eUL,0x50dce685dUL,0x505ca6a5cUL,0x53dd6645bUL,0x535d2665aUL,0x52dde6059UL,0x525da6258UL,0x2de279ca7UL,0x2d6239ea6UL,0x2ce2f98a5UL,0x2c62b9aa4UL,0x2fe3794a3UL,0x2f63396a2UL,0x2ee3f90a1UL,0x2e63b92a0UL,0x29e078cafUL,0x296038eaeUL,0x28e0f88adUL,0x2860b8aacUL,0x2be1784abUL,0x2b61386aaUL,0x2ae1f80a9UL,0x2a61b82a8UL,0x25e67bcb7UL,0x25663beb6UL,0x24e6fb8b5UL,0x2466bbab4UL,0x27e77b4b3UL,0x27673b6b2UL,0x26e7fb0b1UL,0x2667bb2b0UL,0x21e47acbfUL,0x21643aebeUL,0x20e4fa8bdUL,0x2064baabcUL,0x23e57a4bbUL,0x23653a6baUL,0x22e5fa0b9UL,0x2265ba2b8UL,0x3dea7dc87UL,0x3d6a3de86UL,0x3ceafd885UL,0x3c6abda84UL,0x3feb7d483UL,0x3f6b3d682UL,0x3eebfd081UL,0x3e6bbd280UL,0x39e87cc8fUL,0x39683ce8eUL,0x38e8fc88dUL,0x3868bca8cUL,0x3be97c48bUL,0x3b693c68aUL,0x3ae9fc089UL,0x3a69bc288UL,0x35ee7fc97UL,0x356e3fe96UL,0x34eeff895UL,0x346ebfa94UL,0x37ef7f493UL,0x376f3f692UL,0x36efff091UL,0x366fbf290UL,0x31ec7ec9fUL,0x316c3ee9eUL,0x30ecfe89dUL,0x306cbea9cUL,0x33ed7e49bUL,0x336d3e69aUL,0x32edfe099UL,0x326dbe298UL,0xdf271ce7UL,0xd7231ee6UL,0xcf2f18e5UL,0xc72b1ae4UL,0xff3714e3UL,0xf73316e2UL,0xef3f10e1UL,0xe73b12e0UL,0x9f070cefUL,0x97030eeeUL,0x8f0f08edUL,0x870b0aecUL,0xbf1704ebUL,0xb71306eaUL,0xaf1f00e9UL,0xa71b02e8UL,0x5f673cf7UL,0x57633ef6UL,0x4f6f38f5UL,0x476b3af4UL,0x7f7734f3UL,0x777336f2UL,0x6f7f30f1UL,0x677b32f0UL,0x1f472cffUL,0x17432efeUL,0xf4f28fdUL,0x74b2afcUL,0x3f5724fbUL,0x375326faUL,0x2f5f20f9UL,0x275b22f8UL,0x1dfa75cc7UL,0x1d7a35ec6UL,0x1cfaf58c5UL,0x1c7ab5ac4UL,0x1ffb754c3UL,0x1f7b356c2UL,0x1efbf50c1UL,0x1e7bb52c0UL,0x19f874ccfUL,0x197834eceUL,0x18f8f48cdUL,0x1878b4accUL,0x1bf9744cbUL,0x1b79346caUL,0x1af9f40c9UL,0x1a79b42c8UL,0x15fe77cd7UL,0x157e37ed6UL,0x14fef78d5UL,0x147eb7ad4UL,0x17ff774d3UL,0x177f376d2UL,0x16fff70d1UL,0x167fb72d0UL,0x11fc76cdfUL,0x117c36edeUL,0x10fcf68ddUL,0x107cb6adcUL,0x13fd764dbUL,0x137d366daUL,0x12fdf60d9UL,0x127db62d8UL,0xed8249d27UL,0xed0209f26UL,0xec82c9925UL,0xec0289b24UL,0xef8349523UL,0xef0309722UL,0xee83c9121UL,0xee0389320UL,0xe98048d2fUL,0xe90008f2eUL,0xe880c892dUL,0xe80088b2cUL,0xeb814852bUL,0xeb010872aUL,0xea81c8129UL,0xea0188328UL,0xe5864bd37UL,0xe5060bf36UL,0xe486cb935UL,0xe4068bb34UL,0xe7874b533UL,0xe7070b732UL,0xe687cb131UL,0xe6078b330UL,0xe1844ad3fUL,0xe1040af3eUL,0xe084ca93dUL,0xe0048ab3cUL,0xe3854a53bUL,0xe3050a73aUL,0xe285ca139UL,0xe2058a338UL,0xfd8a4dd07UL,0xfd0a0df06UL,0xfc8acd905UL,0xfc0a8db04UL,0xff8b4d503UL,0xff0b0d702UL,0xfe8bcd101UL,0xfe0b8d300UL,0xf9884cd0fUL,0xf9080cf0eUL,0xf888cc90dUL,0xf8088cb0cUL,0xfb894c50bUL,0xfb090c70aUL,0xfa89cc109UL,0xfa098c308UL,0xf58e4fd17UL,0xf50e0ff16UL,0xf48ecf915UL,0xf40e8fb14UL,0xf78f4f513UL,0xf70f0f712UL,0xf68fcf111UL,0xf60f8f310UL,0xf18c4ed1fUL,0xf10c0ef1eUL,0xf08cce91dUL,0xf00c8eb1cUL,0xf38d4e51bUL,0xf30d0e71aUL,0xf28dce119UL,0xf20d8e318UL,0xcd9241d67UL,0xcd1201f66UL,0xcc92c1965UL,0xcc1281b64UL,0xcf9341563UL,0xcf1301762UL,0xce93c1161UL,0xce1381360UL,0xc99040d6fUL,0xc91000f6eUL,0xc890c096dUL,0xc81080b6cUL,0xcb914056bUL,0xcb110076aUL,0xca91c0169UL,0xca1180368UL,0xc59643d77UL,0xc51603f76UL,0xc496c3975UL,0xc41683b74UL,0xc79743573UL,0xc71703772UL,0xc697c3171UL,0xc61783370UL,0xc19442d7fUL,0xc11402f7eUL,0xc094c297dUL,0xc01482b7cUL,0xc3954257bUL,0xc3150277aUL,0xc295c2179UL,0xc21582378UL,0xdd9a45d47UL,0xdd1a05f46UL,0xdc9ac5945UL,0xdc1a85b44UL,0xdf9b45543UL,0xdf1b05742UL,0xde9bc5141UL,0xde1b85340UL,0xd99844d4fUL,0xd91804f4eUL,0xd898c494dUL,0xd81884b4cUL,0xdb994454bUL,0xdb190474aUL,0xda99c4149UL,0xda1984348UL,0xd59e47d57UL,0xd51e07f56UL,0xd49ec7955UL,0xd41e87b54UL,0xd79f47553UL,0xd71f07752UL,0xd69fc7151UL,0xd61f87350UL,0xd19c46d5fUL,0xd11c06f5eUL,0xd09cc695dUL,0xd01c86b5cUL,0xd39d4655bUL,0xd31d0675aUL,0xd29dc6159UL,0xd21d86358UL,0xada259da7UL,0xad2219fa6UL,0xaca2d99a5UL,0xac2299ba4UL,0xafa3595a3UL,0xaf23197a2UL,0xaea3d91a1UL,0xae23993a0UL,0xa9a058dafUL,0xa92018faeUL,0xa8a0d89adUL,0xa82098bacUL,0xaba1585abUL,0xab21187aaUL,0xaaa1d81a9UL,0xaa21983a8UL,0xa5a65bdb7UL,0xa5261bfb6UL,0xa4a6db9b5UL,0xa4269bbb4UL,0xa7a75b5b3UL,0xa7271b7b2UL,0xa6a7db1b1UL,0xa6279b3b0UL,0xa1a45adbfUL,0xa1241afbeUL,0xa0a4da9bdUL,0xa0249abbcUL,0xa3a55a5bbUL,0xa3251a7baUL,0xa2a5da1b9UL,0xa2259a3b8UL,0xbdaa5dd87UL,0xbd2a1df86UL,0xbcaadd985UL,0xbc2a9db84UL,0xbfab5d583UL,0xbf2b1d782UL,0xbeabdd181UL,0xbe2b9d380UL,0xb9a85cd8fUL,0xb9281cf8eUL,0xb8a8dc98dUL,0xb8289cb8cUL,0xbba95c58bUL,0xbb291c78aUL,0xbaa9dc189UL,0xba299c388UL,0xb5ae5fd97UL,0xb52e1ff96UL,0xb4aedf995UL,0xb42e9fb94UL,0xb7af5f593UL,0xb72f1f792UL,0xb6afdf191UL,0xb62f9f390UL,0xb1ac5ed9fUL,0xb12c1ef9eUL,0xb0acde99dUL,0xb02c9eb9cUL,0xb3ad5e59bUL,0xb32d1e79aUL,0xb2adde199UL,0xb22d9e398UL,0x8db251de7UL,0x8d3211fe6UL,0x8cb2d19e5UL,0x8c3291be4UL,0x8fb3515e3UL,0x8f33117e2UL,0x8eb3d11e1UL,0x8e33913e0UL,0x89b050defUL,0x893010feeUL,0x88b0d09edUL,0x883090becUL,0x8bb1505ebUL,0x8b31107eaUL,0x8ab1d01e9UL,0x8a31903e8UL,0x85b653df7UL,0x853613ff6UL,0x84b6d39f5UL,0x843693bf4UL,0x87b7535f3UL,0x8737137f2UL,0x86b7d31f1UL,0x8637933f0UL,0x81b452dffUL,0x813412ffeUL,0x80b4d29fdUL,0x803492bfcUL,0x83b5525fbUL,0x8335127faUL,0x82b5d21f9UL,0x8235923f8UL,0x9dba55dc7UL,0x9d3a15fc6UL,0x9cbad59c5UL,0x9c3a95bc4UL,0x9fbb555c3UL,0x9f3b157c2UL,0x9ebbd51c1UL,0x9e3b953c0UL,0x99b854dcfUL,0x993814fceUL,0x98b8d49cdUL,0x983894bccUL,0x9bb9545cbUL,0x9b39147caUL,0x9ab9d41c9UL,0x9a39943c8UL,0x95be57dd7UL,0x953e17fd6UL,0x94bed79d5UL,0x943e97bd4UL,0x97bf575d3UL,0x973f177d2UL,0x96bfd71d1UL,0x963f973d0UL,0x91bc56ddfUL,0x913c16fdeUL,0x90bcd69ddUL,0x903c96bdcUL,0x93bd565dbUL,0x933d167daUL,0x92bdd61d9UL,0x923d963d8UL}; + fromVector(codes,d._code_id); + d._nbits=36; + d._tau=4;// + d._type=ARTOOLKITPLUS; + d._name="ARTOOLKITPLUS"; + + }break; + case ARTOOLKITPLUSBCH:{ + vector codes={0x8f80b8750UL,0x8f9d0a027UL,0x8fa66eec9UL,0x8fbbdc9beUL,0x8fcd15462UL,0x8fd0a7315UL,0x8febc3dfbUL,0x8ff671a8cUL,0x8f0650643UL,0x8f1be2134UL,0x8f2086fdaUL,0x8f3d348adUL,0x8f4bfd571UL,0x8f564f206UL,0x8f6d2bce8UL,0x8f7099b9fUL,0x8e8d68576UL,0x8e90da201UL,0x8eabbecefUL,0x8eb60cb98UL,0x8ec0c5644UL,0x8edd77133UL,0x8ee613fddUL,0x8efba18aaUL,0x8e0b80465UL,0x8e1632312UL,0x8e2d56dfcUL,0x8e30e4a8bUL,0x8e462d757UL,0x8e5b9f020UL,0x8e60fbeceUL,0x8e7d499b9UL,0x8d86aa46bUL,0x8d9b1831cUL,0x8da07cdf2UL,0x8dbdcea85UL,0x8dcb07759UL,0x8dd6b502eUL,0x8dedd1ec0UL,0x8df0639b7UL,0x8d0042578UL,0x8d1df020fUL,0x8d2694ce1UL,0x8d3b26b96UL,0x8d4def64aUL,0x8d505d13dUL,0x8d6b39fd3UL,0x8d768b8a4UL,0x8c8b7a64dUL,0x8c96c813aUL,0x8cadacfd4UL,0x8cb01e8a3UL,0x8cc6d757fUL,0x8cdb65208UL,0x8ce001ce6UL,0x8cfdb3b91UL,0x8c0d9275eUL,0x8c1020029UL,0x8c2b44ec7UL,0x8c36f69b0UL,0x8c403f46cUL,0x8c5d8d31bUL,0x8c66e9df5UL,0x8c7b5ba82UL,0x8b8c9c126UL,0x8b912e651UL,0x8baa4a8bfUL,0x8bb7f8fc8UL,0x8bc131214UL,0x8bdc83563UL,0x8be7e7b8dUL,0x8bfa55cfaUL,0x8b0a74035UL,0x8b17c6742UL,0x8b2ca29acUL,0x8b3110edbUL,0x8b47d9307UL,0x8b5a6b470UL,0x8b610fa9eUL,0x8b7cbdde9UL,0x8a814c300UL,0x8a9cfe477UL,0x8aa79aa99UL,0x8aba28deeUL,0x8acce1032UL,0x8ad153745UL,0x8aea379abUL,0x8af785edcUL,0x8a07a4213UL,0x8a1a16564UL,0x8a2172b8aUL,0x8a3cc0cfdUL,0x8a4a09121UL,0x8a57bb656UL,0x8a6cdf8b8UL,0x8a716dfcfUL,0x898a8e21dUL,0x89973c56aUL,0x89ac58b84UL,0x89b1eacf3UL,0x89c72312fUL,0x89da91658UL,0x89e1f58b6UL,0x89fc47fc1UL,0x890c6630eUL,0x8911d4479UL,0x892ab0a97UL,0x893702de0UL,0x8941cb03cUL,0x895c7974bUL,0x89671d9a5UL,0x897aafed2UL,0x88875e03bUL,0x889aec74cUL,0x88a1889a2UL,0x88bc3aed5UL,0x88caf3309UL,0x88d74147eUL,0x88ec25a90UL,0x88f197de7UL,0x8801b6128UL,0x881c0465fUL,0x8827608b1UL,0x883ad2fc6UL,0x884c1b21aUL,0x8851a956dUL,0x886acdb83UL,0x88777fcf4UL,0x878542ccbUL,0x8798f0bbcUL,0x87a394552UL,0x87be26225UL,0x87c8efff9UL,0x87d55d88eUL,0x87ee39660UL,0x87f38b117UL,0x8703aadd8UL,0x871e18aafUL,0x87257c441UL,0x8738ce336UL,0x874e07eeaUL,0x8753b599dUL,0x8768d1773UL,0x877563004UL,0x868892eedUL,0x86952099aUL,0x86ae44774UL,0x86b3f6003UL,0x86c53fddfUL,0x86d88daa8UL,0x86e3e9446UL,0x86fe5b331UL,0x860e7affeUL,0x8613c8889UL,0x8628ac667UL,0x86351e110UL,0x8643d7cccUL,0x865e65bbbUL,0x866501555UL,0x8678b3222UL,0x858350ff0UL,0x859ee2887UL,0x85a586669UL,0x85b83411eUL,0x85cefdcc2UL,0x85d34fbb5UL,0x85e82b55bUL,0x85f59922cUL,0x8505b8ee3UL,0x85180a994UL,0x85236e77aUL,0x853edc00dUL,0x854815dd1UL,0x8555a7aa6UL,0x856ec3448UL,0x85737133fUL,0x848e80dd6UL,0x849332aa1UL,0x84a85644fUL,0x84b5e4338UL,0x84c32dee4UL,0x84de9f993UL,0x84e5fb77dUL,0x84f84900aUL,0x840868cc5UL,0x8415dabb2UL,0x842ebe55cUL,0x84330c22bUL,0x8445c5ff7UL,0x845877880UL,0x84631366eUL,0x847ea1119UL,0x838966abdUL,0x8394d4dcaUL,0x83afb0324UL,0x83b202453UL,0x83c4cb98fUL,0x83d979ef8UL,0x83e21d016UL,0x83ffaf761UL,0x830f8ebaeUL,0x83123ccd9UL,0x832958237UL,0x8334ea540UL,0x83422389cUL,0x835f91febUL,0x8364f5105UL,0x837947672UL,0x8284b689bUL,0x829904fecUL,0x82a260102UL,0x82bfd2675UL,0x82c91bba9UL,0x82d4a9cdeUL,0x82efcd230UL,0x82f27f547UL,0x82025e988UL,0x821feceffUL,0x822488011UL,0x82393a766UL,0x824ff3abaUL,0x825241dcdUL,0x826925323UL,0x827497454UL,0x818f74986UL,0x8192c6ef1UL,0x81a9a201fUL,0x81b410768UL,0x81c2d9ab4UL,0x81df6bdc3UL,0x81e40f32dUL,0x81f9bd45aUL,0x81099c895UL,0x81142efe2UL,0x812f4a10cUL,0x8132f867bUL,0x814431ba7UL,0x815983cd0UL,0x8162e723eUL,0x817f55549UL,0x8082a4ba0UL,0x809f16cd7UL,0x80a472239UL,0x80b9c054eUL,0x80cf09892UL,0x80d2bbfe5UL,0x80e9df10bUL,0x80f46d67cUL,0x80044cab3UL,0x8019fedc4UL,0x80229a32aUL,0x803f2845dUL,0x8049e1981UL,0x805453ef6UL,0x806f37018UL,0x80728576fUL,0x9f8b4d066UL,0x9f96ff711UL,0x9fad9b9ffUL,0x9fb029e88UL,0x9fc6e0354UL,0x9fdb52423UL,0x9fe036acdUL,0x9ffd84dbaUL,0x9f0da5175UL,0x9f1017602UL,0x9f2b738ecUL,0x9f36c1f9bUL,0x9f4008247UL,0x9f5dba530UL,0x9f66debdeUL,0x9f7b6cca9UL,0x9e869d240UL,0x9e9b2f537UL,0x9ea04bbd9UL,0x9ebdf9caeUL,0x9ecb30172UL,0x9ed682605UL,0x9eede68ebUL,0x9ef054f9cUL,0x9e0075353UL,0x9e1dc7424UL,0x9e26a3acaUL,0x9e3b11dbdUL,0x9e4dd8061UL,0x9e506a716UL,0x9e6b0e9f8UL,0x9e76bce8fUL,0x9d8d5f35dUL,0x9d90ed42aUL,0x9dab89ac4UL,0x9db63bdb3UL,0x9dc0f206fUL,0x9ddd40718UL,0x9de6249f6UL,0x9dfb96e81UL,0x9d0bb724eUL,0x9d1605539UL,0x9d2d61bd7UL,0x9d30d3ca0UL,0x9d461a17cUL,0x9d5ba860bUL,0x9d60cc8e5UL,0x9d7d7ef92UL,0x9c808f17bUL,0x9c9d3d60cUL,0x9ca6598e2UL,0x9cbbebf95UL,0x9ccd22249UL,0x9cd09053eUL,0x9cebf4bd0UL,0x9cf646ca7UL,0x9c0667068UL,0x9c1bd571fUL,0x9c20b19f1UL,0x9c3d03e86UL,0x9c4bca35aUL,0x9c567842dUL,0x9c6d1cac3UL,0x9c70aedb4UL,0x9b8769610UL,0x9b9adb167UL,0x9ba1bff89UL,0x9bbc0d8feUL,0x9bcac4522UL,0x9bd776255UL,0x9bec12cbbUL,0x9bf1a0bccUL,0x9b0181703UL,0x9b1c33074UL,0x9b2757e9aUL,0x9b3ae59edUL,0x9b4c2c431UL,0x9b519e346UL,0x9b6afada8UL,0x9b7748adfUL,0x9a8ab9436UL,0x9a970b341UL,0x9aac6fdafUL,0x9ab1ddad8UL,0x9ac714704UL,0x9adaa6073UL,0x9ae1c2e9dUL,0x9afc709eaUL,0x9a0c51525UL,0x9a11e3252UL,0x9a2a87cbcUL,0x9a3735bcbUL,0x9a41fc617UL,0x9a5c4e160UL,0x9a672af8eUL,0x9a7a988f9UL,0x99817b52bUL,0x999cc925cUL,0x99a7adcb2UL,0x99ba1fbc5UL,0x99ccd6619UL,0x99d16416eUL,0x99ea00f80UL,0x99f7b28f7UL,0x990793438UL,0x991a2134fUL,0x992145da1UL,0x993cf7ad6UL,0x994a3e70aUL,0x99578c07dUL,0x996ce8e93UL,0x99715a9e4UL,0x988cab70dUL,0x98911907aUL,0x98aa7de94UL,0x98b7cf9e3UL,0x98c10643fUL,0x98dcb4348UL,0x98e7d0da6UL,0x98fa62ad1UL,0x980a4361eUL,0x9817f1169UL,0x982c95f87UL,0x9831278f0UL,0x9847ee52cUL,0x985a5c25bUL,0x986138cb5UL,0x987c8abc2UL,0x978eb7bfdUL,0x979305c8aUL,0x97a861264UL,0x97b5d3513UL,0x97c31a8cfUL,0x97dea8fb8UL,0x97e5cc156UL,0x97f87e621UL,0x97085faeeUL,0x9715edd99UL,0x972e89377UL,0x97333b400UL,0x9745f29dcUL,0x975840eabUL,0x976324045UL,0x977e96732UL,0x9683679dbUL,0x969ed5eacUL,0x96a5b1042UL,0x96b803735UL,0x96cecaae9UL,0x96d378d9eUL,0x96e81c370UL,0x96f5ae407UL,0x96058f8c8UL,0x96183dfbfUL,0x962359151UL,0x963eeb626UL,0x964822bfaUL,0x965590c8dUL,0x966ef4263UL,0x967346514UL,0x9588a58c6UL,0x959517fb1UL,0x95ae7315fUL,0x95b3c1628UL,0x95c508bf4UL,0x95d8bac83UL,0x95e3de26dUL,0x95fe6c51aUL,0x950e4d9d5UL,0x9513ffea2UL,0x95289b04cUL,0x95352973bUL,0x9543e0ae7UL,0x955e52d90UL,0x95653637eUL,0x957884409UL,0x948575ae0UL,0x9498c7d97UL,0x94a3a3379UL,0x94be1140eUL,0x94c8d89d2UL,0x94d56aea5UL,0x94ee0e04bUL,0x94f3bc73cUL,0x94039dbf3UL,0x941e2fc84UL,0x94254b26aUL,0x9438f951dUL,0x944e308c1UL,0x945382fb6UL,0x9468e6158UL,0x94755462fUL,0x938293d8bUL,0x939f21afcUL,0x93a445412UL,0x93b9f7365UL,0x93cf3eeb9UL,0x93d28c9ceUL,0x93e9e8720UL,0x93f45a057UL,0x93047bc98UL,0x9319c9befUL,0x9322ad501UL,0x933f1f276UL,0x9349d6faaUL,0x9354648ddUL,0x936f00633UL,0x9372b2144UL,0x928f43fadUL,0x9292f18daUL,0x92a995634UL,0x92b427143UL,0x92c2eec9fUL,0x92df5cbe8UL,0x92e438506UL,0x92f98a271UL,0x9209abebeUL,0x9214199c9UL,0x922f7d727UL,0x9232cf050UL,0x924406d8cUL,0x9259b4afbUL,0x9262d0415UL,0x927f62362UL,0x918481eb0UL,0x9199339c7UL,0x91a257729UL,0x91bfe505eUL,0x91c92cd82UL,0x91d49eaf5UL,0x91effa41bUL,0x91f24836cUL,0x910269fa3UL,0x911fdb8d4UL,0x9124bf63aUL,0x91390d14dUL,0x914fc4c91UL,0x915276be6UL,0x916912508UL,0x9174a027fUL,0x908951c96UL,0x9094e3be1UL,0x90af8750fUL,0x90b235278UL,0x90c4fcfa4UL,0x90d94e8d3UL,0x90e22a63dUL,0x90ff9814aUL,0x900fb9d85UL,0x90120baf2UL,0x90296f41cUL,0x9034dd36bUL,0x904214eb7UL,0x905fa69c0UL,0x9064c272eUL,0x907970059UL,0xaf8ae0e4bUL,0xaf975293cUL,0xafac367d2UL,0xafb1840a5UL,0xafc74dd79UL,0xafdaffa0eUL,0xafe19b4e0UL,0xaffc29397UL,0xaf0c08f58UL,0xaf11ba82fUL,0xaf2ade6c1UL,0xaf376c1b6UL,0xaf41a5c6aUL,0xaf5c17b1dUL,0xaf67735f3UL,0xaf7ac1284UL,0xae8730c6dUL,0xae9a82b1aUL,0xaea1e65f4UL,0xaebc54283UL,0xaeca9df5fUL,0xaed72f828UL,0xaeec4b6c6UL,0xaef1f91b1UL,0xae01d8d7eUL,0xae1c6aa09UL,0xae270e4e7UL,0xae3abc390UL,0xae4c75e4cUL,0xae51c793bUL,0xae6aa37d5UL,0xae77110a2UL,0xad8cf2d70UL,0xad9140a07UL,0xadaa244e9UL,0xadb79639eUL,0xadc15fe42UL,0xaddced935UL,0xade7897dbUL,0xadfa3b0acUL,0xad0a1ac63UL,0xad17a8b14UL,0xad2ccc5faUL,0xad317e28dUL,0xad47b7f51UL,0xad5a05826UL,0xad61616c8UL,0xad7cd31bfUL,0xac8122f56UL,0xac9c90821UL,0xaca7f46cfUL,0xacba461b8UL,0xaccc8fc64UL,0xacd13db13UL,0xacea595fdUL,0xacf7eb28aUL,0xac07cae45UL,0xac1a78932UL,0xac211c7dcUL,0xac3cae0abUL,0xac4a67d77UL,0xac57d5a00UL,0xac6cb14eeUL,0xac7103399UL,0xab86c483dUL,0xab9b76f4aUL,0xaba0121a4UL,0xabbda06d3UL,0xabcb69b0fUL,0xabd6dbc78UL,0xabedbf296UL,0xabf00d5e1UL,0xab002c92eUL,0xab1d9ee59UL,0xab26fa0b7UL,0xab3b487c0UL,0xab4d81a1cUL,0xab5033d6bUL,0xab6b57385UL,0xab76e54f2UL,0xaa8b14a1bUL,0xaa96a6d6cUL,0xaaadc2382UL,0xaab0704f5UL,0xaac6b9929UL,0xaadb0be5eUL,0xaae06f0b0UL,0xaafddd7c7UL,0xaa0dfcb08UL,0xaa104ec7fUL,0xaa2b2a291UL,0xaa36985e6UL,0xaa405183aUL,0xaa5de3f4dUL,0xaa66871a3UL,0xaa7b356d4UL,0xa980d6b06UL,0xa99d64c71UL,0xa9a60029fUL,0xa9bbb25e8UL,0xa9cd7b834UL,0xa9d0c9f43UL,0xa9ebad1adUL,0xa9f61f6daUL,0xa9063ea15UL,0xa91b8cd62UL,0xa920e838cUL,0xa93d5a4fbUL,0xa94b93927UL,0xa95621e50UL,0xa96d450beUL,0xa970f77c9UL,0xa88d06920UL,0xa890b4e57UL,0xa8abd00b9UL,0xa8b6627ceUL,0xa8c0aba12UL,0xa8dd19d65UL,0xa8e67d38bUL,0xa8fbcf4fcUL,0xa80bee833UL,0xa8165cf44UL,0xa82d381aaUL,0xa8308a6ddUL,0xa84643b01UL,0xa85bf1c76UL,0xa86095298UL,0xa87d275efUL,0xa78f1a5d0UL,0xa792a82a7UL,0xa7a9ccc49UL,0xa7b47eb3eUL,0xa7c2b76e2UL,0xa7df05195UL,0xa7e461f7bUL,0xa7f9d380cUL,0xa709f24c3UL,0xa714403b4UL,0xa72f24d5aUL,0xa73296a2dUL,0xa7445f7f1UL,0xa759ed086UL,0xa76289e68UL,0xa77f3b91fUL,0xa682ca7f6UL,0xa69f78081UL,0xa6a41ce6fUL,0xa6b9ae918UL,0xa6cf674c4UL,0xa6d2d53b3UL,0xa6e9b1d5dUL,0xa6f403a2aUL,0xa604226e5UL,0xa61990192UL,0xa622f4f7cUL,0xa63f4680bUL,0xa6498f5d7UL,0xa6543d2a0UL,0xa66f59c4eUL,0xa672ebb39UL,0xa589086ebUL,0xa594ba19cUL,0xa5afdef72UL,0xa5b26c805UL,0xa5c4a55d9UL,0xa5d9172aeUL,0xa5e273c40UL,0xa5ffc1b37UL,0xa50fe07f8UL,0xa5125208fUL,0xa52936e61UL,0xa53484916UL,0xa5424d4caUL,0xa55fff3bdUL,0xa5649bd53UL,0xa57929a24UL,0xa484d84cdUL,0xa4996a3baUL,0xa4a20ed54UL,0xa4bfbca23UL,0xa4c9757ffUL,0xa4d4c7088UL,0xa4efa3e66UL,0xa4f211911UL,0xa402305deUL,0xa41f822a9UL,0xa424e6c47UL,0xa43954b30UL,0xa44f9d6ecUL,0xa4522f19bUL,0xa4694bf75UL,0xa474f9802UL,0xa3833e3a6UL,0xa39e8c4d1UL,0xa3a5e8a3fUL,0xa3b85ad48UL,0xa3ce93094UL,0xa3d3217e3UL,0xa3e84590dUL,0xa3f5f7e7aUL,0xa305d62b5UL,0xa318645c2UL,0xa32300b2cUL,0xa33eb2c5bUL,0xa3487b187UL,0xa355c96f0UL,0xa36ead81eUL,0xa3731ff69UL,0xa28eee180UL,0xa2935c6f7UL,0xa2a838819UL,0xa2b58af6eUL,0xa2c3432b2UL,0xa2def15c5UL,0xa2e595b2bUL,0xa2f827c5cUL,0xa20806093UL,0xa215b47e4UL,0xa22ed090aUL,0xa23362e7dUL,0xa245ab3a1UL,0xa258194d6UL,0xa2637da38UL,0xa27ecfd4fUL,0xa1852c09dUL,0xa1989e7eaUL,0xa1a3fa904UL,0xa1be48e73UL,0xa1c8813afUL,0xa1d5334d8UL,0xa1ee57a36UL,0xa1f3e5d41UL,0xa103c418eUL,0xa11e766f9UL,0xa12512817UL,0xa138a0f60UL,0xa14e692bcUL,0xa153db5cbUL,0xa168bfb25UL,0xa1750dc52UL,0xa088fc2bbUL,0xa0954e5ccUL,0xa0ae2ab22UL,0xa0b398c55UL,0xa0c551189UL,0xa0d8e36feUL,0xa0e387810UL,0xa0fe35f67UL,0xa00e143a8UL,0xa013a64dfUL,0xa028c2a31UL,0xa03570d46UL,0xa043b909aUL,0xa05e0b7edUL,0xa0656f903UL,0xa078dde74UL,0xbf811597dUL,0xbf9ca7e0aUL,0xbfa7c30e4UL,0xbfba71793UL,0xbfccb8a4fUL,0xbfd10ad38UL,0xbfea6e3d6UL,0xbff7dc4a1UL,0xbf07fd86eUL,0xbf1a4ff19UL,0xbf212b1f7UL,0xbf3c99680UL,0xbf4a50b5cUL,0xbf57e2c2bUL,0xbf6c862c5UL,0xbf71345b2UL,0xbe8cc5b5bUL,0xbe9177c2cUL,0xbeaa132c2UL,0xbeb7a15b5UL,0xbec168869UL,0xbedcdaf1eUL,0xbee7be1f0UL,0xbefa0c687UL,0xbe0a2da48UL,0xbe179fd3fUL,0xbe2cfb3d1UL,0xbe31494a6UL,0xbe478097aUL,0xbe5a32e0dUL,0xbe61560e3UL,0xbe7ce4794UL,0xbd8707a46UL,0xbd9ab5d31UL,0xbda1d13dfUL,0xbdbc634a8UL,0xbdcaaa974UL,0xbdd718e03UL,0xbdec7c0edUL,0xbdf1ce79aUL,0xbd01efb55UL,0xbd1c5dc22UL,0xbd27392ccUL,0xbd3a8b5bbUL,0xbd4c42867UL,0xbd51f0f10UL,0xbd6a941feUL,0xbd7726689UL,0xbc8ad7860UL,0xbc9765f17UL,0xbcac011f9UL,0xbcb1b368eUL,0xbcc77ab52UL,0xbcdac8c25UL,0xbce1ac2cbUL,0xbcfc1e5bcUL,0xbc0c3f973UL,0xbc118de04UL,0xbc2ae90eaUL,0xbc375b79dUL,0xbc4192a41UL,0xbc5c20d36UL,0xbc67443d8UL,0xbc7af64afUL,0xbb8d31f0bUL,0xbb908387cUL,0xbbabe7692UL,0xbbb6551e5UL,0xbbc09cc39UL,0xbbdd2eb4eUL,0xbbe64a5a0UL,0xbbfbf82d7UL,0xbb0bd9e18UL,0xbb166b96fUL,0xbb2d0f781UL,0xbb30bd0f6UL,0xbb4674d2aUL,0xbb5bc6a5dUL,0xbb60a24b3UL,0xbb7d103c4UL,0xba80e1d2dUL,0xba9d53a5aUL,0xbaa6374b4UL,0xbabb853c3UL,0xbacd4ce1fUL,0xbad0fe968UL,0xbaeb9a786UL,0xbaf6280f1UL,0xba0609c3eUL,0xba1bbbb49UL,0xba20df5a7UL,0xba3d6d2d0UL,0xba4ba4f0cUL,0xba561687bUL,0xba6d72695UL,0xba70c01e2UL,0xb98b23c30UL,0xb99691b47UL,0xb9adf55a9UL,0xb9b0472deUL,0xb9c68ef02UL,0xb9db3c875UL,0xb9e05869bUL,0xb9fdea1ecUL,0xb90dcbd23UL,0xb91079a54UL,0xb92b1d4baUL,0xb936af3cdUL,0xb94066e11UL,0xb95dd4966UL,0xb966b0788UL,0xb97b020ffUL,0xb886f3e16UL,0xb89b41961UL,0xb8a02578fUL,0xb8bd970f8UL,0xb8cb5ed24UL,0xb8d6eca53UL,0xb8ed884bdUL,0xb8f03a3caUL,0xb8001bf05UL,0xb81da9872UL,0xb826cd69cUL,0xb83b7f1ebUL,0xb84db6c37UL,0xb85004b40UL,0xb86b605aeUL,0xb876d22d9UL,0xb784ef2e6UL,0xb7995d591UL,0xb7a239b7fUL,0xb7bf8bc08UL,0xb7c9421d4UL,0xb7d4f06a3UL,0xb7ef9484dUL,0xb7f226f3aUL,0xb702073f5UL,0xb71fb5482UL,0xb724d1a6cUL,0xb73963d1bUL,0xb74faa0c7UL,0xb752187b0UL,0xb7697c95eUL,0xb774cee29UL,0xb6893f0c0UL,0xb6948d7b7UL,0xb6afe9959UL,0xb6b25be2eUL,0xb6c4923f2UL,0xb6d920485UL,0xb6e244a6bUL,0xb6fff6d1cUL,0xb60fd71d3UL,0xb612656a4UL,0xb6290184aUL,0xb634b3f3dUL,0xb6427a2e1UL,0xb65fc8596UL,0xb664acb78UL,0xb6791ec0fUL,0xb582fd1ddUL,0xb59f4f6aaUL,0xb5a42b844UL,0xb5b999f33UL,0xb5cf502efUL,0xb5d2e2598UL,0xb5e986b76UL,0xb5f434c01UL,0xb504150ceUL,0xb519a77b9UL,0xb522c3957UL,0xb53f71e20UL,0xb549b83fcUL,0xb5540a48bUL,0xb56f6ea65UL,0xb572dcd12UL,0xb48f2d3fbUL,0xb4929f48cUL,0xb4a9fba62UL,0xb4b449d15UL,0xb4c2800c9UL,0xb4df327beUL,0xb4e456950UL,0xb4f9e4e27UL,0xb409c52e8UL,0xb4147759fUL,0xb42f13b71UL,0xb432a1c06UL,0xb444681daUL,0xb459da6adUL,0xb462be843UL,0xb47f0cf34UL,0xb388cb490UL,0xb395793e7UL,0xb3ae1dd09UL,0xb3b3afa7eUL,0xb3c5667a2UL,0xb3d8d40d5UL,0xb3e3b0e3bUL,0xb3fe0294cUL,0xb30e23583UL,0xb313912f4UL,0xb328f5c1aUL,0xb33547b6dUL,0xb3438e6b1UL,0xb35e3c1c6UL,0xb36558f28UL,0xb378ea85fUL,0xb2851b6b6UL,0xb298a91c1UL,0xb2a3cdf2fUL,0xb2be7f858UL,0xb2c8b6584UL,0xb2d5042f3UL,0xb2ee60c1dUL,0xb2f3d2b6aUL,0xb203f37a5UL,0xb21e410d2UL,0xb22525e3cUL,0xb2389794bUL,0xb24e5e497UL,0xb253ec3e0UL,0xb26888d0eUL,0xb2753aa79UL,0xb18ed97abUL,0xb1936b0dcUL,0xb1a80fe32UL,0xb1b5bd945UL,0xb1c374499UL,0xb1dec63eeUL,0xb1e5a2d00UL,0xb1f810a77UL,0xb108316b8UL,0xb115831cfUL,0xb12ee7f21UL,0xb13355856UL,0xb1459c58aUL,0xb1582e2fdUL,0xb1634ac13UL,0xb17ef8b64UL,0xb0830958dUL,0xb09ebb2faUL,0xb0a5dfc14UL,0xb0b86db63UL,0xb0cea46bfUL,0xb0d3161c8UL,0xb0e872f26UL,0xb0f5c0851UL,0xb005e149eUL,0xb018533e9UL,0xb02337d07UL,0xb03e85a70UL,0xb0484c7acUL,0xb055fe0dbUL,0xb06e9ae35UL,0xb07328942UL,0xcf89bb211UL,0xcf9409566UL,0xcfaf6db88UL,0xcfb2dfcffUL,0xcfc416123UL,0xcfd9a4654UL,0xcfe2c08baUL,0xcfff72fcdUL,0xcf0f53302UL,0xcf12e1475UL,0xcf2985a9bUL,0xcf3437decUL,0xcf42fe030UL,0xcf5f4c747UL,0xcf64289a9UL,0xcf799aedeUL,0xce846b037UL,0xce99d9740UL,0xcea2bd9aeUL,0xcebf0fed9UL,0xcec9c6305UL,0xced474472UL,0xceef10a9cUL,0xcef2a2debUL,0xce0283124UL,0xce1f31653UL,0xce24558bdUL,0xce39e7fcaUL,0xce4f2e216UL,0xce529c561UL,0xce69f8b8fUL,0xce744acf8UL,0xcd8fa912aUL,0xcd921b65dUL,0xcda97f8b3UL,0xcdb4cdfc4UL,0xcdc204218UL,0xcddfb656fUL,0xcde4d2b81UL,0xcdf960cf6UL,0xcd0941039UL,0xcd14f374eUL,0xcd2f979a0UL,0xcd3225ed7UL,0xcd44ec30bUL,0xcd595e47cUL,0xcd623aa92UL,0xcd7f88de5UL,0xcc827930cUL,0xcc9fcb47bUL,0xcca4afa95UL,0xccb91dde2UL,0xcccfd403eUL,0xccd266749UL,0xcce9029a7UL,0xccf4b0ed0UL,0xcc049121fUL,0xcc1923568UL,0xcc2247b86UL,0xcc3ff5cf1UL,0xcc493c12dUL,0xcc548e65aUL,0xcc6fea8b4UL,0xcc7258fc3UL,0xcb859f467UL,0xcb982d310UL,0xcba349dfeUL,0xcbbefba89UL,0xcbc832755UL,0xcbd580022UL,0xcbeee4eccUL,0xcbf3569bbUL,0xcb0377574UL,0xcb1ec5203UL,0xcb25a1cedUL,0xcb3813b9aUL,0xcb4eda646UL,0xcb5368131UL,0xcb680cfdfUL,0xcb75be8a8UL,0xca884f641UL,0xca95fd136UL,0xcaae99fd8UL,0xcab32b8afUL,0xcac5e2573UL,0xcad850204UL,0xcae334ceaUL,0xcafe86b9dUL,0xca0ea7752UL,0xca1315025UL,0xca2871ecbUL,0xca35c39bcUL,0xca430a460UL,0xca5eb8317UL,0xca65dcdf9UL,0xca786ea8eUL,0xc9838d75cUL,0xc99e3f02bUL,0xc9a55bec5UL,0xc9b8e99b2UL,0xc9ce2046eUL,0xc9d392319UL,0xc9e8f6df7UL,0xc9f544a80UL,0xc9056564fUL,0xc918d7138UL,0xc923b3fd6UL,0xc93e018a1UL,0xc948c857dUL,0xc9557a20aUL,0xc96e1ece4UL,0xc973acb93UL,0xc88e5d57aUL,0xc893ef20dUL,0xc8a88bce3UL,0xc8b539b94UL,0xc8c3f0648UL,0xc8de4213fUL,0xc8e526fd1UL,0xc8f8948a6UL,0xc808b5469UL,0xc8150731eUL,0xc82e63df0UL,0xc833d1a87UL,0xc8451875bUL,0xc858aa02cUL,0xc863ceec2UL,0xc87e7c9b5UL,0xc78c4198aUL,0xc791f3efdUL,0xc7aa97013UL,0xc7b725764UL,0xc7c1ecab8UL,0xc7dc5edcfUL,0xc7e73a321UL,0xc7fa88456UL,0xc70aa9899UL,0xc7171bfeeUL,0xc72c7f100UL,0xc731cd677UL,0xc74704babUL,0xc75ab6cdcUL,0xc761d2232UL,0xc77c60545UL,0xc68191bacUL,0xc69c23cdbUL,0xc6a747235UL,0xc6baf5542UL,0xc6cc3c89eUL,0xc6d18efe9UL,0xc6eaea107UL,0xc6f758670UL,0xc60779abfUL,0xc61acbdc8UL,0xc621af326UL,0xc63c1d451UL,0xc64ad498dUL,0xc65766efaUL,0xc66c02014UL,0xc671b0763UL,0xc58a53ab1UL,0xc597e1dc6UL,0xc5ac85328UL,0xc5b13745fUL,0xc5c7fe983UL,0xc5da4cef4UL,0xc5e12801aUL,0xc5fc9a76dUL,0xc50cbbba2UL,0xc51109cd5UL,0xc52a6d23bUL,0xc537df54cUL,0xc54116890UL,0xc55ca4fe7UL,0xc567c0109UL,0xc57a7267eUL,0xc48783897UL,0xc49a31fe0UL,0xc4a15510eUL,0xc4bce7679UL,0xc4ca2eba5UL,0xc4d79ccd2UL,0xc4ecf823cUL,0xc4f14a54bUL,0xc4016b984UL,0xc41cd9ef3UL,0xc427bd01dUL,0xc43a0f76aUL,0xc44cc6ab6UL,0xc45174dc1UL,0xc46a1032fUL,0xc477a2458UL,0xc38065ffcUL,0xc39dd788bUL,0xc3a6b3665UL,0xc3bb01112UL,0xc3cdc8cceUL,0xc3d07abb9UL,0xc3eb1e557UL,0xc3f6ac220UL,0xc3068deefUL,0xc31b3f998UL,0xc3205b776UL,0xc33de9001UL,0xc34b20dddUL,0xc35692aaaUL,0xc36df6444UL,0xc37044333UL,0xc28db5ddaUL,0xc29007aadUL,0xc2ab63443UL,0xc2b6d1334UL,0xc2c018ee8UL,0xc2ddaa99fUL,0xc2e6ce771UL,0xc2fb7c006UL,0xc20b5dcc9UL,0xc216efbbeUL,0xc22d8b550UL,0xc23039227UL,0xc246f0ffbUL,0xc25b4288cUL,0xc26026662UL,0xc27d94115UL,0xc18677cc7UL,0xc19bc5bb0UL,0xc1a0a155eUL,0xc1bd13229UL,0xc1cbdaff5UL,0xc1d668882UL,0xc1ed0c66cUL,0xc1f0be11bUL,0xc1009fdd4UL,0xc11d2daa3UL,0xc1264944dUL,0xc13bfb33aUL,0xc14d32ee6UL,0xc15080991UL,0xc16be477fUL,0xc17656008UL,0xc08ba7ee1UL,0xc09615996UL,0xc0ad71778UL,0xc0b0c300fUL,0xc0c60add3UL,0xc0dbb8aa4UL,0xc0e0dc44aUL,0xc0fd6e33dUL,0xc00d4fff2UL,0xc010fd885UL,0xc02b9966bUL,0xc0362b11cUL,0xc040e2cc0UL,0xc05d50bb7UL,0xc06634559UL,0xc07b8622eUL,0xdf824e527UL,0xdf9ffc250UL,0xdfa498cbeUL,0xdfb92abc9UL,0xdfcfe3615UL,0xdfd251162UL,0xdfe935f8cUL,0xdff4878fbUL,0xdf04a6434UL,0xdf1914343UL,0xdf2270dadUL,0xdf3fc2adaUL,0xdf490b706UL,0xdf54b9071UL,0xdf6fdde9fUL,0xdf726f9e8UL,0xde8f9e701UL,0xde922c076UL,0xdea948e98UL,0xdeb4fa9efUL,0xdec233433UL,0xdedf81344UL,0xdee4e5daaUL,0xdef957addUL,0xde0976612UL,0xde14c4165UL,0xde2fa0f8bUL,0xde32128fcUL,0xde44db520UL,0xde5969257UL,0xde620dcb9UL,0xde7fbfbceUL,0xdd845c61cUL,0xdd99ee16bUL,0xdda28af85UL,0xddbf388f2UL,0xddc9f152eUL,0xddd443259UL,0xddef27cb7UL,0xddf295bc0UL,0xdd02b470fUL,0xdd1f06078UL,0xdd2462e96UL,0xdd39d09e1UL,0xdd4f1943dUL,0xdd52ab34aUL,0xdd69cfda4UL,0xdd747dad3UL,0xdc898c43aUL,0xdc943e34dUL,0xdcaf5ada3UL,0xdcb2e8ad4UL,0xdcc421708UL,0xdcd99307fUL,0xdce2f7e91UL,0xdcff459e6UL,0xdc0f64529UL,0xdc12d625eUL,0xdc29b2cb0UL,0xdc3400bc7UL,0xdc42c961bUL,0xdc5f7b16cUL,0xdc641ff82UL,0xdc79ad8f5UL,0xdb8e6a351UL,0xdb93d8426UL,0xdba8bcac8UL,0xdbb50edbfUL,0xdbc3c7063UL,0xdbde75714UL,0xdbe5119faUL,0xdbf8a3e8dUL,0xdb0882242UL,0xdb1530535UL,0xdb2e54bdbUL,0xdb33e6cacUL,0xdb452f170UL,0xdb589d607UL,0xdb63f98e9UL,0xdb7e4bf9eUL,0xda83ba177UL,0xda9e08600UL,0xdaa56c8eeUL,0xdab8def99UL,0xdace17245UL,0xdad3a5532UL,0xdae8c1bdcUL,0xdaf573cabUL,0xda0552064UL,0xda18e0713UL,0xda23849fdUL,0xda3e36e8aUL,0xda48ff356UL,0xda554d421UL,0xda6e29acfUL,0xda739bdb8UL,0xd9887806aUL,0xd995ca71dUL,0xd9aeae9f3UL,0xd9b31ce84UL,0xd9c5d5358UL,0xd9d86742fUL,0xd9e303ac1UL,0xd9feb1db6UL,0xd90e90179UL,0xd9132260eUL,0xd928468e0UL,0xd935f4f97UL,0xd9433d24bUL,0xd95e8f53cUL,0xd965ebbd2UL,0xd97859ca5UL,0xd885a824cUL,0xd8981a53bUL,0xd8a37ebd5UL,0xd8beccca2UL,0xd8c80517eUL,0xd8d5b7609UL,0xd8eed38e7UL,0xd8f361f90UL,0xd8034035fUL,0xd81ef2428UL,0xd82596ac6UL,0xd83824db1UL,0xd84eed06dUL,0xd8535f71aUL,0xd8683b9f4UL,0xd87589e83UL,0xd787b4ebcUL,0xd79a069cbUL,0xd7a162725UL,0xd7bcd0052UL,0xd7ca19d8eUL,0xd7d7abaf9UL,0xd7eccf417UL,0xd7f17d360UL,0xd7015cfafUL,0xd71cee8d8UL,0xd7278a636UL,0xd73a38141UL,0xd74cf1c9dUL,0xd75143beaUL,0xd76a27504UL,0xd77795273UL,0xd68a64c9aUL,0xd697d6bedUL,0xd6acb2503UL,0xd6b100274UL,0xd6c7c9fa8UL,0xd6da7b8dfUL,0xd6e11f631UL,0xd6fcad146UL,0xd60c8cd89UL,0xd6113eafeUL,0xd62a5a410UL,0xd637e8367UL,0xd64121ebbUL,0xd65c939ccUL,0xd667f7722UL,0xd67a45055UL,0xd581a6d87UL,0xd59c14af0UL,0xd5a77041eUL,0xd5bac2369UL,0xd5cc0beb5UL,0xd5d1b99c2UL,0xd5eadd72cUL,0xd5f76f05bUL,0xd5074ec94UL,0xd51afcbe3UL,0xd5219850dUL,0xd53c2a27aUL,0xd54ae3fa6UL,0xd557518d1UL,0xd56c3563fUL,0xd57187148UL,0xd48c76fa1UL,0xd491c48d6UL,0xd4aaa0638UL,0xd4b71214fUL,0xd4c1dbc93UL,0xd4dc69be4UL,0xd4e70d50aUL,0xd4fabf27dUL,0xd40a9eeb2UL,0xd4172c9c5UL,0xd42c4872bUL,0xd431fa05cUL,0xd44733d80UL,0xd45a81af7UL,0xd461e5419UL,0xd47c5736eUL,0xd38b908caUL,0xd39622fbdUL,0xd3ad46153UL,0xd3b0f4624UL,0xd3c63dbf8UL,0xd3db8fc8fUL,0xd3e0eb261UL,0xd3fd59516UL,0xd30d789d9UL,0xd310caeaeUL,0xd32bae040UL,0xd3361c737UL,0xd340d5aebUL,0xd35d67d9cUL,0xd36603372UL,0xd37bb1405UL,0xd28640aecUL,0xd29bf2d9bUL,0xd2a096375UL,0xd2bd24402UL,0xd2cbed9deUL,0xd2d65fea9UL,0xd2ed3b047UL,0xd2f089730UL,0xd200a8bffUL,0xd21d1ac88UL,0xd2267e266UL,0xd23bcc511UL,0xd24d058cdUL,0xd250b7fbaUL,0xd26bd3154UL,0xd27661623UL,0xd18d82bf1UL,0xd19030c86UL,0xd1ab54268UL,0xd1b6e651fUL,0xd1c02f8c3UL,0xd1dd9dfb4UL,0xd1e6f915aUL,0xd1fb4b62dUL,0xd10b6aae2UL,0xd116d8d95UL,0xd12dbc37bUL,0xd1300e40cUL,0xd146c79d0UL,0xd15b75ea7UL,0xd16011049UL,0xd17da373eUL,0xd080529d7UL,0xd09de0ea0UL,0xd0a68404eUL,0xd0bb36739UL,0xd0cdffae5UL,0xd0d04dd92UL,0xd0eb2937cUL,0xd0f69b40bUL,0xd006ba8c4UL,0xd01b08fb3UL,0xd0206c15dUL,0xd03dde62aUL,0xd04b17bf6UL,0xd056a5c81UL,0xd06dc126fUL,0xd07073518UL,0xef83e3b0aUL,0xef9e51c7dUL,0xefa535293UL,0xefb8875e4UL,0xefce4e838UL,0xefd3fcf4fUL,0xefe8981a1UL,0xeff52a6d6UL,0xef050ba19UL,0xef18b9d6eUL,0xef23dd380UL,0xef3e6f4f7UL,0xef48a692bUL,0xef5514e5cUL,0xef6e700b2UL,0xef73c27c5UL,0xee8e3392cUL,0xee9381e5bUL,0xeea8e50b5UL,0xeeb5577c2UL,0xeec39ea1eUL,0xeede2cd69UL,0xeee548387UL,0xeef8fa4f0UL,0xee08db83fUL,0xee1569f48UL,0xee2e0d1a6UL,0xee33bf6d1UL,0xee4576b0dUL,0xee58c4c7aUL,0xee63a0294UL,0xee7e125e3UL,0xed85f1831UL,0xed9843f46UL,0xeda3271a8UL,0xedbe956dfUL,0xedc85cb03UL,0xedd5eec74UL,0xedee8a29aUL,0xedf3385edUL,0xed0319922UL,0xed1eabe55UL,0xed25cf0bbUL,0xed387d7ccUL,0xed4eb4a10UL,0xed5306d67UL,0xed6862389UL,0xed75d04feUL,0xec8821a17UL,0xec9593d60UL,0xecaef738eUL,0xecb3454f9UL,0xecc58c925UL,0xecd83ee52UL,0xece35a0bcUL,0xecfee87cbUL,0xec0ec9b04UL,0xec137bc73UL,0xec281f29dUL,0xec35ad5eaUL,0xec4364836UL,0xec5ed6f41UL,0xec65b21afUL,0xec78006d8UL,0xeb8fc7d7cUL,0xeb9275a0bUL,0xeba9114e5UL,0xebb4a3392UL,0xebc26ae4eUL,0xebdfd8939UL,0xebe4bc7d7UL,0xebf90e0a0UL,0xeb092fc6fUL,0xeb149db18UL,0xeb2ff95f6UL,0xeb324b281UL,0xeb4482f5dUL,0xeb593082aUL,0xeb62546c4UL,0xeb7fe61b3UL,0xea8217f5aUL,0xea9fa582dUL,0xeaa4c16c3UL,0xeab9731b4UL,0xeacfbac68UL,0xead208b1fUL,0xeae96c5f1UL,0xeaf4de286UL,0xea04ffe49UL,0xea194d93eUL,0xea22297d0UL,0xea3f9b0a7UL,0xea4952d7bUL,0xea54e0a0cUL,0xea6f844e2UL,0xea7236395UL,0xe989d5e47UL,0xe99467930UL,0xe9af037deUL,0xe9b2b10a9UL,0xe9c478d75UL,0xe9d9caa02UL,0xe9e2ae4ecUL,0xe9ff1c39bUL,0xe90f3df54UL,0xe9128f823UL,0xe929eb6cdUL,0xe934591baUL,0xe94290c66UL,0xe95f22b11UL,0xe964465ffUL,0xe979f4288UL,0xe88405c61UL,0xe899b7b16UL,0xe8a2d35f8UL,0xe8bf6128fUL,0xe8c9a8f53UL,0xe8d41a824UL,0xe8ef7e6caUL,0xe8f2cc1bdUL,0xe802edd72UL,0xe81f5fa05UL,0xe8243b4ebUL,0xe8398939cUL,0xe84f40e40UL,0xe852f2937UL,0xe869967d9UL,0xe874240aeUL,0xe78619091UL,0xe79bab7e6UL,0xe7a0cf908UL,0xe7bd7de7fUL,0xe7cbb43a3UL,0xe7d6064d4UL,0xe7ed62a3aUL,0xe7f0d0d4dUL,0xe700f1182UL,0xe71d436f5UL,0xe7262781bUL,0xe73b95f6cUL,0xe74d5c2b0UL,0xe750ee5c7UL,0xe76b8ab29UL,0xe77638c5eUL,0xe68bc92b7UL,0xe6967b5c0UL,0xe6ad1fb2eUL,0xe6b0adc59UL,0xe6c664185UL,0xe6dbd66f2UL,0xe6e0b281cUL,0xe6fd00f6bUL,0xe60d213a4UL,0xe610934d3UL,0xe62bf7a3dUL,0xe63645d4aUL,0xe6408c096UL,0xe65d3e7e1UL,0xe6665a90fUL,0xe67be8e78UL,0xe5800b3aaUL,0xe59db94ddUL,0xe5a6dda33UL,0xe5bb6fd44UL,0xe5cda6098UL,0xe5d0147efUL,0xe5eb70901UL,0xe5f6c2e76UL,0xe506e32b9UL,0xe51b515ceUL,0xe52035b20UL,0xe53d87c57UL,0xe54b4e18bUL,0xe556fc6fcUL,0xe56d98812UL,0xe5702af65UL,0xe48ddb18cUL,0xe490696fbUL,0xe4ab0d815UL,0xe4b6bff62UL,0xe4c0762beUL,0xe4ddc45c9UL,0xe4e6a0b27UL,0xe4fb12c50UL,0xe40b3309fUL,0xe416817e8UL,0xe42de5906UL,0xe43057e71UL,0xe4469e3adUL,0xe45b2c4daUL,0xe46048a34UL,0xe47dfad43UL,0xe38a3d6e7UL,0xe3978f190UL,0xe3acebf7eUL,0xe3b159809UL,0xe3c7905d5UL,0xe3da222a2UL,0xe3e146c4cUL,0xe3fcf4b3bUL,0xe30cd57f4UL,0xe31167083UL,0xe32a03e6dUL,0xe337b191aUL,0xe341784c6UL,0xe35cca3b1UL,0xe367aed5fUL,0xe37a1ca28UL,0xe287ed4c1UL,0xe29a5f3b6UL,0xe2a13bd58UL,0xe2bc89a2fUL,0xe2ca407f3UL,0xe2d7f2084UL,0xe2ec96e6aUL,0xe2f12491dUL,0xe201055d2UL,0xe21cb72a5UL,0xe227d3c4bUL,0xe23a61b3cUL,0xe24ca86e0UL,0xe2511a197UL,0xe26a7ef79UL,0xe277cc80eUL,0xe18c2f5dcUL,0xe1919d2abUL,0xe1aaf9c45UL,0xe1b74bb32UL,0xe1c1826eeUL,0xe1dc30199UL,0xe1e754f77UL,0xe1fae6800UL,0xe10ac74cfUL,0xe117753b8UL,0xe12c11d56UL,0xe131a3a21UL,0xe1476a7fdUL,0xe15ad808aUL,0xe161bce64UL,0xe17c0e913UL,0xe081ff7faUL,0xe09c4d08dUL,0xe0a729e63UL,0xe0ba9b914UL,0xe0cc524c8UL,0xe0d1e03bfUL,0xe0ea84d51UL,0xe0f736a26UL,0xe007176e9UL,0xe01aa519eUL,0xe021c1f70UL,0xe03c73807UL,0xe04aba5dbUL,0xe057082acUL,0xe06c6cc42UL,0xe071deb35UL,0xff8816c3cUL,0xff95a4b4bUL,0xffaec05a5UL,0xffb3722d2UL,0xffc5bbf0eUL,0xffd809879UL,0xffe36d697UL,0xfffedf1e0UL,0xff0efed2fUL,0xff134ca58UL,0xff28284b6UL,0xff359a3c1UL,0xff4353e1dUL,0xff5ee196aUL,0xff6585784UL,0xff78370f3UL,0xfe85c6e1aUL,0xfe987496dUL,0xfea310783UL,0xfebea20f4UL,0xfec86bd28UL,0xfed5d9a5fUL,0xfeeebd4b1UL,0xfef30f3c6UL,0xfe032ef09UL,0xfe1e9c87eUL,0xfe25f8690UL,0xfe384a1e7UL,0xfe4e83c3bUL,0xfe5331b4cUL,0xfe68555a2UL,0xfe75e72d5UL,0xfd8e04f07UL,0xfd93b6870UL,0xfda8d269eUL,0xfdb5601e9UL,0xfdc3a9c35UL,0xfdde1bb42UL,0xfde57f5acUL,0xfdf8cd2dbUL,0xfd08ece14UL,0xfd155e963UL,0xfd2e3a78dUL,0xfd33880faUL,0xfd4541d26UL,0xfd58f3a51UL,0xfd63974bfUL,0xfd7e253c8UL,0xfc83d4d21UL,0xfc9e66a56UL,0xfca5024b8UL,0xfcb8b03cfUL,0xfcce79e13UL,0xfcd3cb964UL,0xfce8af78aUL,0xfcf51d0fdUL,0xfc053cc32UL,0xfc188eb45UL,0xfc23ea5abUL,0xfc3e582dcUL,0xfc4891f00UL,0xfc5523877UL,0xfc6e47699UL,0xfc73f51eeUL,0xfb8432a4aUL,0xfb9980d3dUL,0xfba2e43d3UL,0xfbbf564a4UL,0xfbc99f978UL,0xfbd42de0fUL,0xfbef490e1UL,0xfbf2fb796UL,0xfb02dab59UL,0xfb1f68c2eUL,0xfb240c2c0UL,0xfb39be5b7UL,0xfb4f7786bUL,0xfb52c5f1cUL,0xfb69a11f2UL,0xfb7413685UL,0xfa89e286cUL,0xfa9450f1bUL,0xfaaf341f5UL,0xfab286682UL,0xfac44fb5eUL,0xfad9fdc29UL,0xfae2992c7UL,0xfaff2b5b0UL,0xfa0f0a97fUL,0xfa12b8e08UL,0xfa29dc0e6UL,0xfa346e791UL,0xfa42a7a4dUL,0xfa5f15d3aUL,0xfa64713d4UL,0xfa79c34a3UL,0xf98220971UL,0xf99f92e06UL,0xf9a4f60e8UL,0xf9b94479fUL,0xf9cf8da43UL,0xf9d23fd34UL,0xf9e95b3daUL,0xf9f4e94adUL,0xf904c8862UL,0xf9197af15UL,0xf9221e1fbUL,0xf93fac68cUL,0xf94965b50UL,0xf954d7c27UL,0xf96fb32c9UL,0xf972015beUL,0xf88ff0b57UL,0xf89242c20UL,0xf8a9262ceUL,0xf8b4945b9UL,0xf8c25d865UL,0xf8dfeff12UL,0xf8e48b1fcUL,0xf8f93968bUL,0xf80918a44UL,0xf814aad33UL,0xf82fce3ddUL,0xf8327c4aaUL,0xf844b5976UL,0xf85907e01UL,0xf862630efUL,0xf87fd1798UL,0xf78dec7a7UL,0xf7905e0d0UL,0xf7ab3ae3eUL,0xf7b688949UL,0xf7c041495UL,0xf7ddf33e2UL,0xf7e697d0cUL,0xf7fb25a7bUL,0xf70b046b4UL,0xf716b61c3UL,0xf72dd2f2dUL,0xf7306085aUL,0xf746a9586UL,0xf75b1b2f1UL,0xf7607fc1fUL,0xf77dcdb68UL,0xf6803c581UL,0xf69d8e2f6UL,0xf6a6eac18UL,0xf6bb58b6fUL,0xf6cd916b3UL,0xf6d0231c4UL,0xf6eb47f2aUL,0xf6f6f585dUL,0xf606d4492UL,0xf61b663e5UL,0xf62002d0bUL,0xf63db0a7cUL,0xf64b797a0UL,0xf656cb0d7UL,0xf66dafe39UL,0xf6701d94eUL,0xf58bfe49cUL,0xf5964c3ebUL,0xf5ad28d05UL,0xf5b09aa72UL,0xf5c6537aeUL,0xf5dbe10d9UL,0xf5e085e37UL,0xf5fd37940UL,0xf50d1658fUL,0xf510a42f8UL,0xf52bc0c16UL,0xf53672b61UL,0xf540bb6bdUL,0xf55d091caUL,0xf5666df24UL,0xf57bdf853UL,0xf4862e6baUL,0xf49b9c1cdUL,0xf4a0f8f23UL,0xf4bd4a854UL,0xf4cb83588UL,0xf4d6312ffUL,0xf4ed55c11UL,0xf4f0e7b66UL,0xf400c67a9UL,0xf41d740deUL,0xf42610e30UL,0xf43ba2947UL,0xf44d6b49bUL,0xf450d93ecUL,0xf46bbdd02UL,0xf4760fa75UL,0xf381c81d1UL,0xf39c7a6a6UL,0xf3a71e848UL,0xf3baacf3fUL,0xf3cc652e3UL,0xf3d1d7594UL,0xf3eab3b7aUL,0xf3f701c0dUL,0xf307200c2UL,0xf31a927b5UL,0xf321f695bUL,0xf33c44e2cUL,0xf34a8d3f0UL,0xf3573f487UL,0xf36c5ba69UL,0xf371e9d1eUL,0xf28c183f7UL,0xf291aa480UL,0xf2aacea6eUL,0xf2b77cd19UL,0xf2c1b50c5UL,0xf2dc077b2UL,0xf2e76395cUL,0xf2fad1e2bUL,0xf20af02e4UL,0xf21742593UL,0xf22c26b7dUL,0xf23194c0aUL,0xf2475d1d6UL,0xf25aef6a1UL,0xf2618b84fUL,0xf27c39f38UL,0xf187da2eaUL,0xf19a6859dUL,0xf1a10cb73UL,0xf1bcbec04UL,0xf1ca771d8UL,0xf1d7c56afUL,0xf1eca1841UL,0xf1f113f36UL,0xf101323f9UL,0xf11c8048eUL,0xf127e4a60UL,0xf13a56d17UL,0xf14c9f0cbUL,0xf1512d7bcUL,0xf16a49952UL,0xf177fbe25UL,0xf08a0a0ccUL,0xf097b87bbUL,0xf0acdc955UL,0xf0b16ee22UL,0xf0c7a73feUL,0xf0da15489UL,0xf0e171a67UL,0xf0fcc3d10UL,0xf00ce21dfUL,0xf011506a8UL,0xf02a34846UL,0xf03786f31UL,0xf0414f2edUL,0xf05cfd59aUL,0xf06799b74UL,0xf07a2bc03UL,0xf8f0caa5UL,0xf92bedd2UL,0xfa9da33cUL,0xfb46844bUL,0xfc2a1997UL,0xfdf13ee0UL,0xfe47700eUL,0xff9c5779UL,0xf09e4bb6UL,0xf1456cc1UL,0xf2f3222fUL,0xf3280558UL,0xf4449884UL,0xf59fbff3UL,0xf629f11dUL,0xf7f2d66aUL,0xe82dc883UL,0xe9f6eff4UL,0xea40a11aUL,0xeb9b866dUL,0xecf71bb1UL,0xed2c3cc6UL,0xee9a7228UL,0xef41555fUL,0xe0434990UL,0xe1986ee7UL,0xe22e2009UL,0xe3f5077eUL,0xe4999aa2UL,0xe542bdd5UL,0xe6f4f33bUL,0xe72fd44cUL,0xd891e99eUL,0xd94acee9UL,0xdafc8007UL,0xdb27a770UL,0xdc4b3aacUL,0xdd901ddbUL,0xde265335UL,0xdffd7442UL,0xd0ff688dUL,0xd1244ffaUL,0xd2920114UL,0xd3492663UL,0xd425bbbfUL,0xd5fe9cc8UL,0xd648d226UL,0xd793f551UL,0xc84cebb8UL,0xc997cccfUL,0xca218221UL,0xcbfaa556UL,0xcc96388aUL,0xcd4d1ffdUL,0xcefb5113UL,0xcf207664UL,0xc0226aabUL,0xc1f94ddcUL,0xc24f0332UL,0xc3942445UL,0xc4f8b999UL,0xc5239eeeUL,0xc695d000UL,0xc74ef777UL,0xb8328cd3UL,0xb9e9aba4UL,0xba5fe54aUL,0xbb84c23dUL,0xbce85fe1UL,0xbd337896UL,0xbe853678UL,0xbf5e110fUL,0xb05c0dc0UL,0xb1872ab7UL,0xb2316459UL,0xb3ea432eUL,0xb486def2UL,0xb55df985UL,0xb6ebb76bUL,0xb730901cUL,0xa8ef8ef5UL,0xa934a982UL,0xaa82e76cUL,0xab59c01bUL,0xac355dc7UL,0xadee7ab0UL,0xae58345eUL,0xaf831329UL,0xa0810fe6UL,0xa15a2891UL,0xa2ec667fUL,0xa3374108UL,0xa45bdcd4UL,0xa580fba3UL,0xa636b54dUL,0xa7ed923aUL,0x9853afe8UL,0x9988889fUL,0x9a3ec671UL,0x9be5e106UL,0x9c897cdaUL,0x9d525badUL,0x9ee41543UL,0x9f3f3234UL,0x903d2efbUL,0x91e6098cUL,0x92504762UL,0x938b6015UL,0x94e7fdc9UL,0x953cdabeUL,0x968a9450UL,0x9751b327UL,0x888eadceUL,0x89558ab9UL,0x8ae3c457UL,0x8b38e320UL,0x8c547efcUL,0x8d8f598bUL,0x8e391765UL,0x8fe23012UL,0x80e02cddUL,0x813b0baaUL,0x828d4544UL,0x83566233UL,0x843affefUL,0x85e1d898UL,0x86579676UL,0x878cb101UL,0x78af613eUL,0x79744649UL,0x7ac208a7UL,0x7b192fd0UL,0x7c75b20cUL,0x7dae957bUL,0x7e18db95UL,0x7fc3fce2UL,0x70c1e02dUL,0x711ac75aUL,0x72ac89b4UL,0x7377aec3UL,0x741b331fUL,0x75c01468UL,0x76765a86UL,0x77ad7df1UL,0x68726318UL,0x69a9446fUL,0x6a1f0a81UL,0x6bc42df6UL,0x6ca8b02aUL,0x6d73975dUL,0x6ec5d9b3UL,0x6f1efec4UL,0x601ce20bUL,0x61c7c57cUL,0x62718b92UL,0x63aaace5UL,0x64c63139UL,0x651d164eUL,0x66ab58a0UL,0x67707fd7UL,0x58ce4205UL,0x59156572UL,0x5aa32b9cUL,0x5b780cebUL,0x5c149137UL,0x5dcfb640UL,0x5e79f8aeUL,0x5fa2dfd9UL,0x50a0c316UL,0x517be461UL,0x52cdaa8fUL,0x53168df8UL,0x547a1024UL,0x55a13753UL,0x561779bdUL,0x57cc5ecaUL,0x48134023UL,0x49c86754UL,0x4a7e29baUL,0x4ba50ecdUL,0x4cc99311UL,0x4d12b466UL,0x4ea4fa88UL,0x4f7fddffUL,0x407dc130UL,0x41a6e647UL,0x4210a8a9UL,0x43cb8fdeUL,0x44a71202UL,0x457c3575UL,0x46ca7b9bUL,0x47115cecUL,0x386d2748UL,0x39b6003fUL,0x3a004ed1UL,0x3bdb69a6UL,0x3cb7f47aUL,0x3d6cd30dUL,0x3eda9de3UL,0x3f01ba94UL,0x3003a65bUL,0x31d8812cUL,0x326ecfc2UL,0x33b5e8b5UL,0x34d97569UL,0x3502521eUL,0x36b41cf0UL,0x376f3b87UL,0x28b0256eUL,0x296b0219UL,0x2add4cf7UL,0x2b066b80UL,0x2c6af65cUL,0x2db1d12bUL,0x2e079fc5UL,0x2fdcb8b2UL,0x20dea47dUL,0x2105830aUL,0x22b3cde4UL,0x2368ea93UL,0x2404774fUL,0x25df5038UL,0x26691ed6UL,0x27b239a1UL,0x180c0473UL,0x19d72304UL,0x1a616deaUL,0x1bba4a9dUL,0x1cd6d741UL,0x1d0df036UL,0x1ebbbed8UL,0x1f6099afUL,0x10628560UL,0x11b9a217UL,0x120fecf9UL,0x13d4cb8eUL,0x14b85652UL,0x15637125UL,0x16d53fcbUL,0x170e18bcUL,0x8d10655UL,0x90a2122UL,0xabc6fccUL,0xb6748bbUL,0xc0bd567UL,0xdd0f210UL,0xe66bcfeUL,0xfbd9b89UL,0xbf8746UL,0x164a031UL,0x2d2eedfUL,0x309c9a8UL,0x4655474UL,0x5be7303UL,0x6083dedUL,0x7d31a9aUL,0x1f84f9d93UL,0x1f994bae4UL,0x1fa22f40aUL,0x1fbf9d37dUL,0x1fc954ea1UL,0x1fd4e69d6UL,0x1fef82738UL,0x1ff23004fUL,0x1f0211c80UL,0x1f1fa3bf7UL,0x1f24c7519UL,0x1f397526eUL,0x1f4fbcfb2UL,0x1f520e8c5UL,0x1f696a62bUL,0x1f74d815cUL,0x1e8929fb5UL,0x1e949b8c2UL,0x1eafff62cUL,0x1eb24d15bUL,0x1ec484c87UL,0x1ed936bf0UL,0x1ee25251eUL,0x1effe0269UL,0x1e0fc1ea6UL,0x1e12739d1UL,0x1e291773fUL,0x1e34a5048UL,0x1e426cd94UL,0x1e5fdeae3UL,0x1e64ba40dUL,0x1e790837aUL,0x1d82ebea8UL,0x1d9f599dfUL,0x1da43d731UL,0x1db98f046UL,0x1dcf46d9aUL,0x1dd2f4aedUL,0x1de990403UL,0x1df422374UL,0x1d0403fbbUL,0x1d19b18ccUL,0x1d22d5622UL,0x1d3f67155UL,0x1d49aec89UL,0x1d541cbfeUL,0x1d6f78510UL,0x1d72ca267UL,0x1c8f3bc8eUL,0x1c9289bf9UL,0x1ca9ed517UL,0x1cb45f260UL,0x1cc296fbcUL,0x1cdf248cbUL,0x1ce440625UL,0x1cf9f2152UL,0x1c09d3d9dUL,0x1c1461aeaUL,0x1c2f05404UL,0x1c32b7373UL,0x1c447eeafUL,0x1c59cc9d8UL,0x1c62a8736UL,0x1c7f1a041UL,0x1b88ddbe5UL,0x1b956fc92UL,0x1bae0b27cUL,0x1bb3b950bUL,0x1bc5708d7UL,0x1bd8c2fa0UL,0x1be3a614eUL,0x1bfe14639UL,0x1b0e35af6UL,0x1b1387d81UL,0x1b28e336fUL,0x1b3551418UL,0x1b43989c4UL,0x1b5e2aeb3UL,0x1b654e05dUL,0x1b78fc72aUL,0x1a850d9c3UL,0x1a98bfeb4UL,0x1aa3db05aUL,0x1abe6972dUL,0x1ac8a0af1UL,0x1ad512d86UL,0x1aee76368UL,0x1af3c441fUL,0x1a03e58d0UL,0x1a1e57fa7UL,0x1a2533149UL,0x1a388163eUL,0x1a4e48be2UL,0x1a53fac95UL,0x1a689e27bUL,0x1a752c50cUL,0x198ecf8deUL,0x19937dfa9UL,0x19a819147UL,0x19b5ab630UL,0x19c362becUL,0x19ded0c9bUL,0x19e5b4275UL,0x19f806502UL,0x1908279cdUL,0x191595ebaUL,0x192ef1054UL,0x193343723UL,0x19458aaffUL,0x195838d88UL,0x19635c366UL,0x197eee411UL,0x18831faf8UL,0x189eadd8fUL,0x18a5c9361UL,0x18b87b416UL,0x18ceb29caUL,0x18d300ebdUL,0x18e864053UL,0x18f5d6724UL,0x1805f7bebUL,0x181845c9cUL,0x182321272UL,0x183e93505UL,0x18485a8d9UL,0x1855e8faeUL,0x186e8c140UL,0x18733e637UL,0x178103608UL,0x179cb117fUL,0x17a7d5f91UL,0x17ba678e6UL,0x17ccae53aUL,0x17d11c24dUL,0x17ea78ca3UL,0x17f7cabd4UL,0x1707eb71bUL,0x171a5906cUL,0x17213de82UL,0x173c8f9f5UL,0x174a46429UL,0x1757f435eUL,0x176c90db0UL,0x177122ac7UL,0x168cd342eUL,0x169161359UL,0x16aa05db7UL,0x16b7b7ac0UL,0x16c17e71cUL,0x16dccc06bUL,0x16e7a8e85UL,0x16fa1a9f2UL,0x160a3b53dUL,0x16178924aUL,0x162cedca4UL,0x16315fbd3UL,0x16479660fUL,0x165a24178UL,0x166140f96UL,0x167cf28e1UL,0x158711533UL,0x159aa3244UL,0x15a1c7caaUL,0x15bc75bddUL,0x15cabc601UL,0x15d70e176UL,0x15ec6af98UL,0x15f1d88efUL,0x1501f9420UL,0x151c4b357UL,0x15272fdb9UL,0x153a9daceUL,0x154c54712UL,0x1551e6065UL,0x156a82e8bUL,0x1577309fcUL,0x148ac1715UL,0x149773062UL,0x14ac17e8cUL,0x14b1a59fbUL,0x14c76c427UL,0x14dade350UL,0x14e1badbeUL,0x14fc08ac9UL,0x140c29606UL,0x14119b171UL,0x142afff9fUL,0x14374d8e8UL,0x144184534UL,0x145c36243UL,0x146752cadUL,0x147ae0bdaUL,0x138d2707eUL,0x139095709UL,0x13abf19e7UL,0x13b643e90UL,0x13c08a34cUL,0x13dd3843bUL,0x13e65cad5UL,0x13fbeeda2UL,0x130bcf16dUL,0x13167d61aUL,0x132d198f4UL,0x1330abf83UL,0x13466225fUL,0x135bd0528UL,0x1360b4bc6UL,0x137d06cb1UL,0x1280f7258UL,0x129d4552fUL,0x12a621bc1UL,0x12bb93cb6UL,0x12cd5a16aUL,0x12d0e861dUL,0x12eb8c8f3UL,0x12f63ef84UL,0x12061f34bUL,0x121bad43cUL,0x1220c9ad2UL,0x123d7bda5UL,0x124bb2079UL,0x12560070eUL,0x126d649e0UL,0x1270d6e97UL,0x118b35345UL,0x119687432UL,0x11ade3adcUL,0x11b051dabUL,0x11c698077UL,0x11db2a700UL,0x11e04e9eeUL,0x11fdfce99UL,0x110ddd256UL,0x11106f521UL,0x112b0bbcfUL,0x1136b9cb8UL,0x114070164UL,0x115dc2613UL,0x1166a68fdUL,0x117b14f8aUL,0x1086e5163UL,0x109b57614UL,0x10a0338faUL,0x10bd81f8dUL,0x10cb48251UL,0x10d6fa526UL,0x10ed9ebc8UL,0x10f02ccbfUL,0x10000d070UL,0x101dbf707UL,0x1026db9e9UL,0x103b69e9eUL,0x104da0342UL,0x105012435UL,0x106b76adbUL,0x1076c4dacUL,0x2f85543beUL,0x2f98e64c9UL,0x2fa382a27UL,0x2fbe30d50UL,0x2fc8f908cUL,0x2fd54b7fbUL,0x2fee2f915UL,0x2ff39de62UL,0x2f03bc2adUL,0x2f1e0e5daUL,0x2f256ab34UL,0x2f38d8c43UL,0x2f4e1119fUL,0x2f53a36e8UL,0x2f68c7806UL,0x2f7575f71UL,0x2e8884198UL,0x2e95366efUL,0x2eae52801UL,0x2eb3e0f76UL,0x2ec5292aaUL,0x2ed89b5ddUL,0x2ee3ffb33UL,0x2efe4dc44UL,0x2e0e6c08bUL,0x2e13de7fcUL,0x2e28ba912UL,0x2e3508e65UL,0x2e43c13b9UL,0x2e5e734ceUL,0x2e6517a20UL,0x2e78a5d57UL,0x2d8346085UL,0x2d9ef47f2UL,0x2da59091cUL,0x2db822e6bUL,0x2dceeb3b7UL,0x2dd3594c0UL,0x2de83da2eUL,0x2df58fd59UL,0x2d05ae196UL,0x2d181c6e1UL,0x2d237880fUL,0x2d3ecaf78UL,0x2d48032a4UL,0x2d55b15d3UL,0x2d6ed5b3dUL,0x2d7367c4aUL,0x2c8e962a3UL,0x2c93245d4UL,0x2ca840b3aUL,0x2cb5f2c4dUL,0x2cc33b191UL,0x2cde896e6UL,0x2ce5ed808UL,0x2cf85ff7fUL,0x2c087e3b0UL,0x2c15cc4c7UL,0x2c2ea8a29UL,0x2c331ad5eUL,0x2c45d3082UL,0x2c58617f5UL,0x2c630591bUL,0x2c7eb7e6cUL,0x2b89705c8UL,0x2b94c22bfUL,0x2bafa6c51UL,0x2bb214b26UL,0x2bc4dd6faUL,0x2bd96f18dUL,0x2be20bf63UL,0x2bffb9814UL,0x2b0f984dbUL,0x2b122a3acUL,0x2b294ed42UL,0x2b34fca35UL,0x2b42357e9UL,0x2b5f8709eUL,0x2b64e3e70UL,0x2b7951907UL,0x2a84a07eeUL,0x2a9912099UL,0x2aa276e77UL,0x2abfc4900UL,0x2ac90d4dcUL,0x2ad4bf3abUL,0x2aefdbd45UL,0x2af269a32UL,0x2a02486fdUL,0x2a1ffa18aUL,0x2a249ef64UL,0x2a392c813UL,0x2a4fe55cfUL,0x2a52572b8UL,0x2a6933c56UL,0x2a7481b21UL,0x298f626f3UL,0x2992d0184UL,0x29a9b4f6aUL,0x29b40681dUL,0x29c2cf5c1UL,0x29df7d2b6UL,0x29e419c58UL,0x29f9abb2fUL,0x29098a7e0UL,0x291438097UL,0x292f5ce79UL,0x2932ee90eUL,0x2944274d2UL,0x2959953a5UL,0x2962f1d4bUL,0x297f43a3cUL,0x2882b24d5UL,0x289f003a2UL,0x28a464d4cUL,0x28b9d6a3bUL,0x28cf1f7e7UL,0x28d2ad090UL,0x28e9c9e7eUL,0x28f47b909UL,0x28045a5c6UL,0x2819e82b1UL,0x28228cc5fUL,0x283f3eb28UL,0x2849f76f4UL,0x285445183UL,0x286f21f6dUL,0x28729381aUL,0x2780ae825UL,0x279d1cf52UL,0x27a6781bcUL,0x27bbca6cbUL,0x27cd03b17UL,0x27d0b1c60UL,0x27ebd528eUL,0x27f6675f9UL,0x270646936UL,0x271bf4e41UL,0x2720900afUL,0x273d227d8UL,0x274beba04UL,0x275659d73UL,0x276d3d39dUL,0x27708f4eaUL,0x268d7ea03UL,0x2690ccd74UL,0x26aba839aUL,0x26b61a4edUL,0x26c0d3931UL,0x26dd61e46UL,0x26e6050a8UL,0x26fbb77dfUL,0x260b96b10UL,0x261624c67UL,0x262d40289UL,0x2630f25feUL,0x26463b822UL,0x265b89f55UL,0x2660ed1bbUL,0x267d5f6ccUL,0x2586bcb1eUL,0x259b0ec69UL,0x25a06a287UL,0x25bdd85f0UL,0x25cb1182cUL,0x25d6a3f5bUL,0x25edc71b5UL,0x25f0756c2UL,0x250054a0dUL,0x251de6d7aUL,0x252682394UL,0x253b304e3UL,0x254df993fUL,0x25504be48UL,0x256b2f0a6UL,0x25769d7d1UL,0x248b6c938UL,0x2496dee4fUL,0x24adba0a1UL,0x24b0087d6UL,0x24c6c1a0aUL,0x24db73d7dUL,0x24e017393UL,0x24fda54e4UL,0x240d8482bUL,0x241036f5cUL,0x242b521b2UL,0x2436e06c5UL,0x244029b19UL,0x245d9bc6eUL,0x2466ff280UL,0x247b4d5f7UL,0x238c8ae53UL,0x239138924UL,0x23aa5c7caUL,0x23b7ee0bdUL,0x23c127d61UL,0x23dc95a16UL,0x23e7f14f8UL,0x23fa4338fUL,0x230a62f40UL,0x2317d0837UL,0x232cb46d9UL,0x2331061aeUL,0x2347cfc72UL,0x235a7db05UL,0x2361195ebUL,0x237cab29cUL,0x22815ac75UL,0x229ce8b02UL,0x22a78c5ecUL,0x22ba3e29bUL,0x22ccf7f47UL,0x22d145830UL,0x22ea216deUL,0x22f7931a9UL,0x2207b2d66UL,0x221a00a11UL,0x2221644ffUL,0x223cd6388UL,0x224a1fe54UL,0x2257ad923UL,0x226cc97cdUL,0x22717b0baUL,0x218a98d68UL,0x21972aa1fUL,0x21ac4e4f1UL,0x21b1fc386UL,0x21c735e5aUL,0x21da8792dUL,0x21e1e37c3UL,0x21fc510b4UL,0x210c70c7bUL,0x2111c2b0cUL,0x212aa65e2UL,0x213714295UL,0x2141ddf49UL,0x215c6f83eUL,0x21670b6d0UL,0x217ab91a7UL,0x208748f4eUL,0x209afa839UL,0x20a19e6d7UL,0x20bc2c1a0UL,0x20cae5c7cUL,0x20d757b0bUL,0x20ec335e5UL,0x20f181292UL,0x2001a0e5dUL,0x201c1292aUL,0x2027767c4UL,0x203ac40b3UL,0x204c0dd6fUL,0x2051bfa18UL,0x206adb4f6UL,0x207769381UL,0x3f8ea1488UL,0x3f93133ffUL,0x3fa877d11UL,0x3fb5c5a66UL,0x3fc30c7baUL,0x3fdebe0cdUL,0x3fe5dae23UL,0x3ff868954UL,0x3f084959bUL,0x3f15fb2ecUL,0x3f2e9fc02UL,0x3f332db75UL,0x3f45e46a9UL,0x3f58561deUL,0x3f6332f30UL,0x3f7e80847UL,0x3e83716aeUL,0x3e9ec31d9UL,0x3ea5a7f37UL,0x3eb815840UL,0x3ecedc59cUL,0x3ed36e2ebUL,0x3ee80ac05UL,0x3ef5b8b72UL,0x3e05997bdUL,0x3e182b0caUL,0x3e234fe24UL,0x3e3efd953UL,0x3e483448fUL,0x3e55863f8UL,0x3e6ee2d16UL,0x3e7350a61UL,0x3d88b37b3UL,0x3d95010c4UL,0x3dae65e2aUL,0x3db3d795dUL,0x3dc51e481UL,0x3dd8ac3f6UL,0x3de3c8d18UL,0x3dfe7aa6fUL,0x3d0e5b6a0UL,0x3d13e91d7UL,0x3d288df39UL,0x3d353f84eUL,0x3d43f6592UL,0x3d5e442e5UL,0x3d6520c0bUL,0x3d7892b7cUL,0x3c8563595UL,0x3c98d12e2UL,0x3ca3b5c0cUL,0x3cbe07b7bUL,0x3cc8ce6a7UL,0x3cd57c1d0UL,0x3cee18f3eUL,0x3cf3aa849UL,0x3c038b486UL,0x3c1e393f1UL,0x3c255dd1fUL,0x3c38efa68UL,0x3c4e267b4UL,0x3c53940c3UL,0x3c68f0e2dUL,0x3c754295aUL,0x3b82852feUL,0x3b9f37589UL,0x3ba453b67UL,0x3bb9e1c10UL,0x3bcf281ccUL,0x3bd29a6bbUL,0x3be9fe855UL,0x3bf44cf22UL,0x3b046d3edUL,0x3b19df49aUL,0x3b22bba74UL,0x3b3f09d03UL,0x3b49c00dfUL,0x3b54727a8UL,0x3b6f16946UL,0x3b72a4e31UL,0x3a8f550d8UL,0x3a92e77afUL,0x3aa983941UL,0x3ab431e36UL,0x3ac2f83eaUL,0x3adf4a49dUL,0x3ae42ea73UL,0x3af99cd04UL,0x3a09bd1cbUL,0x3a140f6bcUL,0x3a2f6b852UL,0x3a32d9f25UL,0x3a44102f9UL,0x3a59a258eUL,0x3a62c6b60UL,0x3a7f74c17UL,0x3984971c5UL,0x3999256b2UL,0x39a24185cUL,0x39bff3f2bUL,0x39c93a2f7UL,0x39d488580UL,0x39efecb6eUL,0x39f25ec19UL,0x39027f0d6UL,0x391fcd7a1UL,0x3924a994fUL,0x39391be38UL,0x394fd23e4UL,0x395260493UL,0x396904a7dUL,0x3974b6d0aUL,0x3889473e3UL,0x3894f5494UL,0x38af91a7aUL,0x38b223d0dUL,0x38c4ea0d1UL,0x38d9587a6UL,0x38e23c948UL,0x38ff8ee3fUL,0x380faf2f0UL,0x38121d587UL,0x382979b69UL,0x3834cbc1eUL,0x3842021c2UL,0x385fb06b5UL,0x3864d485bUL,0x387966f2cUL,0x378b5bf13UL,0x3796e9864UL,0x37ad8d68aUL,0x37b03f1fdUL,0x37c6f6c21UL,0x37db44b56UL,0x37e0205b8UL,0x37fd922cfUL,0x370db3e00UL,0x371001977UL,0x372b65799UL,0x3736d70eeUL,0x37401ed32UL,0x375daca45UL,0x3766c84abUL,0x377b7a3dcUL,0x36868bd35UL,0x369b39a42UL,0x36a05d4acUL,0x36bdef3dbUL,0x36cb26e07UL,0x36d694970UL,0x36edf079eUL,0x36f0420e9UL,0x360063c26UL,0x361dd1b51UL,0x3626b55bfUL,0x363b072c8UL,0x364dcef14UL,0x36507c863UL,0x366b1868dUL,0x3676aa1faUL,0x358d49c28UL,0x3590fbb5fUL,0x35ab9f5b1UL,0x35b62d2c6UL,0x35c0e4f1aUL,0x35dd5686dUL,0x35e632683UL,0x35fb801f4UL,0x350ba1d3bUL,0x351613a4cUL,0x352d774a2UL,0x3530c53d5UL,0x35460ce09UL,0x355bbe97eUL,0x3560da790UL,0x357d680e7UL,0x348099e0eUL,0x349d2b979UL,0x34a64f797UL,0x34bbfd0e0UL,0x34cd34d3cUL,0x34d086a4bUL,0x34ebe24a5UL,0x34f6503d2UL,0x340671f1dUL,0x341bc386aUL,0x3420a7684UL,0x343d151f3UL,0x344bdcc2fUL,0x34566eb58UL,0x346d0a5b6UL,0x3470b82c1UL,0x33877f965UL,0x339acde12UL,0x33a1a90fcUL,0x33bc1b78bUL,0x33cad2a57UL,0x33d760d20UL,0x33ec043ceUL,0x33f1b64b9UL,0x330197876UL,0x331c25f01UL,0x3327411efUL,0x333af3698UL,0x334c3ab44UL,0x335188c33UL,0x336aec2ddUL,0x33775e5aaUL,0x328aafb43UL,0x32971dc34UL,0x32ac792daUL,0x32b1cb5adUL,0x32c702871UL,0x32dab0f06UL,0x32e1d41e8UL,0x32fc6669fUL,0x320c47a50UL,0x3211f5d27UL,0x322a913c9UL,0x3237234beUL,0x3241ea962UL,0x325c58e15UL,0x32673c0fbUL,0x327a8e78cUL,0x31816da5eUL,0x319cdfd29UL,0x31a7bb3c7UL,0x31ba094b0UL,0x31ccc096cUL,0x31d172e1bUL,0x31ea160f5UL,0x31f7a4782UL,0x310785b4dUL,0x311a37c3aUL,0x3121532d4UL,0x313ce15a3UL,0x314a2887fUL,0x31579af08UL,0x316cfe1e6UL,0x31714c691UL,0x308cbd878UL,0x30910ff0fUL,0x30aa6b1e1UL,0x30b7d9696UL,0x30c110b4aUL,0x30dca2c3dUL,0x30e7c62d3UL,0x30fa745a4UL,0x300a5596bUL,0x3017e7e1cUL,0x302c830f2UL,0x303131785UL,0x3047f8a59UL,0x305a4ad2eUL,0x30612e3c0UL,0x307c9c4b7UL,0x4f860ffe4UL,0x4f9bbd893UL,0x4fa0d967dUL,0x4fbd6b10aUL,0x4fcba2cd6UL,0x4fd610ba1UL,0x4fed7454fUL,0x4ff0c6238UL,0x4f00e7ef7UL,0x4f1d55980UL,0x4f263176eUL,0x4f3b83019UL,0x4f4d4adc5UL,0x4f50f8ab2UL,0x4f6b9c45cUL,0x4f762e32bUL,0x4e8bdfdc2UL,0x4e966dab5UL,0x4ead0945bUL,0x4eb0bb32cUL,0x4ec672ef0UL,0x4edbc0987UL,0x4ee0a4769UL,0x4efd1601eUL,0x4e0d37cd1UL,0x4e1085ba6UL,0x4e2be1548UL,0x4e365323fUL,0x4e409afe3UL,0x4e5d28894UL,0x4e664c67aUL,0x4e7bfe10dUL,0x4d801dcdfUL,0x4d9dafba8UL,0x4da6cb546UL,0x4dbb79231UL,0x4dcdb0fedUL,0x4dd00289aUL,0x4deb66674UL,0x4df6d4103UL,0x4d06f5dccUL,0x4d1b47abbUL,0x4d2023455UL,0x4d3d91322UL,0x4d4b58efeUL,0x4d56ea989UL,0x4d6d8e767UL,0x4d703c010UL,0x4c8dcdef9UL,0x4c907f98eUL,0x4cab1b760UL,0x4cb6a9017UL,0x4cc060dcbUL,0x4cddd2abcUL,0x4ce6b6452UL,0x4cfb04325UL,0x4c0b25feaUL,0x4c169789dUL,0x4c2df3673UL,0x4c3041104UL,0x4c4688cd8UL,0x4c5b3abafUL,0x4c605e541UL,0x4c7dec236UL,0x4b8a2b992UL,0x4b9799ee5UL,0x4bacfd00bUL,0x4bb14f77cUL,0x4bc786aa0UL,0x4bda34dd7UL,0x4be150339UL,0x4bfce244eUL,0x4b0cc3881UL,0x4b1171ff6UL,0x4b2a15118UL,0x4b37a766fUL,0x4b416ebb3UL,0x4b5cdccc4UL,0x4b67b822aUL,0x4b7a0a55dUL,0x4a87fbbb4UL,0x4a9a49cc3UL,0x4aa12d22dUL,0x4abc9f55aUL,0x4aca56886UL,0x4ad7e4ff1UL,0x4aec8011fUL,0x4af132668UL,0x4a0113aa7UL,0x4a1ca1dd0UL,0x4a27c533eUL,0x4a3a77449UL,0x4a4cbe995UL,0x4a510cee2UL,0x4a6a6800cUL,0x4a77da77bUL,0x498c39aa9UL,0x49918bddeUL,0x49aaef330UL,0x49b75d447UL,0x49c19499bUL,0x49dc26eecUL,0x49e742002UL,0x49faf0775UL,0x490ad1bbaUL,0x491763ccdUL,0x492c07223UL,0x4931b5554UL,0x49477c888UL,0x495acefffUL,0x4961aa111UL,0x497c18666UL,0x4881e988fUL,0x489c5bff8UL,0x48a73f116UL,0x48ba8d661UL,0x48cc44bbdUL,0x48d1f6ccaUL,0x48ea92224UL,0x48f720553UL,0x48070199cUL,0x481ab3eebUL,0x4821d7005UL,0x483c65772UL,0x484aacaaeUL,0x48571edd9UL,0x486c7a337UL,0x4871c8440UL,0x4783f547fUL,0x479e47308UL,0x47a523de6UL,0x47b891a91UL,0x47ce5874dUL,0x47d3ea03aUL,0x47e88eed4UL,0x47f53c9a3UL,0x47051d56cUL,0x4718af21bUL,0x4723cbcf5UL,0x473e79b82UL,0x4748b065eUL,0x475502129UL,0x476e66fc7UL,0x4773d48b0UL,0x468e25659UL,0x46939712eUL,0x46a8f3fc0UL,0x46b5418b7UL,0x46c38856bUL,0x46de3a21cUL,0x46e55ecf2UL,0x46f8ecb85UL,0x4608cd74aUL,0x46157f03dUL,0x462e1bed3UL,0x4633a99a4UL,0x464560478UL,0x4658d230fUL,0x4663b6de1UL,0x467e04a96UL,0x4585e7744UL,0x459855033UL,0x45a331eddUL,0x45be839aaUL,0x45c84a476UL,0x45d5f8301UL,0x45ee9cdefUL,0x45f32ea98UL,0x45030f657UL,0x451ebd120UL,0x4525d9fceUL,0x45386b8b9UL,0x454ea2565UL,0x455310212UL,0x456874cfcUL,0x4575c6b8bUL,0x448837562UL,0x449585215UL,0x44aee1cfbUL,0x44b353b8cUL,0x44c59a650UL,0x44d828127UL,0x44e34cfc9UL,0x44fefe8beUL,0x440edf471UL,0x44136d306UL,0x442809de8UL,0x4435bba9fUL,0x444372743UL,0x445ec0034UL,0x4465a4edaUL,0x4478169adUL,0x438fd1209UL,0x43926357eUL,0x43a907b90UL,0x43b4b5ce7UL,0x43c27c13bUL,0x43dfce64cUL,0x43e4aa8a2UL,0x43f918fd5UL,0x43093931aUL,0x43148b46dUL,0x432fefa83UL,0x43325ddf4UL,0x434494028UL,0x43592675fUL,0x4362429b1UL,0x437ff0ec6UL,0x42820102fUL,0x429fb3758UL,0x42a4d79b6UL,0x42b965ec1UL,0x42cfac31dUL,0x42d21e46aUL,0x42e97aa84UL,0x42f4c8df3UL,0x4204e913cUL,0x42195b64bUL,0x42223f8a5UL,0x423f8dfd2UL,0x42494420eUL,0x4254f6579UL,0x426f92b97UL,0x427220ce0UL,0x4189c3132UL,0x419471645UL,0x41af158abUL,0x41b2a7fdcUL,0x41c46e200UL,0x41d9dc577UL,0x41e2b8b99UL,0x41ff0aceeUL,0x410f2b021UL,0x411299756UL,0x4129fd9b8UL,0x41344fecfUL,0x414286313UL,0x415f34464UL,0x416450a8aUL,0x4179e2dfdUL,0x408413314UL,0x4099a1463UL,0x40a2c5a8dUL,0x40bf77dfaUL,0x40c9be026UL,0x40d40c751UL,0x40ef689bfUL,0x40f2daec8UL,0x4002fb207UL,0x401f49570UL,0x40242db9eUL,0x40399fce9UL,0x404f56135UL,0x4052e4642UL,0x4069808acUL,0x407432fdbUL,0x5f8dfa8d2UL,0x5f9048fa5UL,0x5fab2c14bUL,0x5fb69e63cUL,0x5fc057be0UL,0x5fdde5c97UL,0x5fe681279UL,0x5ffb3350eUL,0x5f0b129c1UL,0x5f16a0eb6UL,0x5f2dc4058UL,0x5f307672fUL,0x5f46bfaf3UL,0x5f5b0dd84UL,0x5f606936aUL,0x5f7ddb41dUL,0x5e802aaf4UL,0x5e9d98d83UL,0x5ea6fc36dUL,0x5ebb4e41aUL,0x5ecd879c6UL,0x5ed035eb1UL,0x5eeb5105fUL,0x5ef6e3728UL,0x5e06c2be7UL,0x5e1b70c90UL,0x5e201427eUL,0x5e3da6509UL,0x5e4b6f8d5UL,0x5e56ddfa2UL,0x5e6db914cUL,0x5e700b63bUL,0x5d8be8be9UL,0x5d965ac9eUL,0x5dad3e270UL,0x5db08c507UL,0x5dc6458dbUL,0x5ddbf7facUL,0x5de093142UL,0x5dfd21635UL,0x5d0d00afaUL,0x5d10b2d8dUL,0x5d2bd6363UL,0x5d3664414UL,0x5d40ad9c8UL,0x5d5d1febfUL,0x5d667b051UL,0x5d7bc9726UL,0x5c86389cfUL,0x5c9b8aeb8UL,0x5ca0ee056UL,0x5cbd5c721UL,0x5ccb95afdUL,0x5cd627d8aUL,0x5ced43364UL,0x5cf0f1413UL,0x5c00d08dcUL,0x5c1d62fabUL,0x5c2606145UL,0x5c3bb4632UL,0x5c4d7dbeeUL,0x5c50cfc99UL,0x5c6bab277UL,0x5c7619500UL,0x5b81deea4UL,0x5b9c6c9d3UL,0x5ba70873dUL,0x5bbaba04aUL,0x5bcc73d96UL,0x5bd1c1ae1UL,0x5beaa540fUL,0x5bf717378UL,0x5b0736fb7UL,0x5b1a848c0UL,0x5b21e062eUL,0x5b3c52159UL,0x5b4a9bc85UL,0x5b5729bf2UL,0x5b6c4d51cUL,0x5b71ff26bUL,0x5a8c0ec82UL,0x5a91bcbf5UL,0x5aaad851bUL,0x5ab76a26cUL,0x5ac1a3fb0UL,0x5adc118c7UL,0x5ae775629UL,0x5afac715eUL,0x5a0ae6d91UL,0x5a1754ae6UL,0x5a2c30408UL,0x5a318237fUL,0x5a474bea3UL,0x5a5af99d4UL,0x5a619d73aUL,0x5a7c2f04dUL,0x5987ccd9fUL,0x599a7eae8UL,0x59a11a406UL,0x59bca8371UL,0x59ca61eadUL,0x59d7d39daUL,0x59ecb7734UL,0x59f105043UL,0x590124c8cUL,0x591c96bfbUL,0x5927f2515UL,0x593a40262UL,0x594c89fbeUL,0x59513b8c9UL,0x596a5f627UL,0x5977ed150UL,0x588a1cfb9UL,0x5897ae8ceUL,0x58acca620UL,0x58b178157UL,0x58c7b1c8bUL,0x58da03bfcUL,0x58e167512UL,0x58fcd5265UL,0x580cf4eaaUL,0x5811469ddUL,0x582a22733UL,0x583790044UL,0x584159d98UL,0x585cebaefUL,0x58678f401UL,0x587a3d376UL,0x578800349UL,0x5795b243eUL,0x57aed6ad0UL,0x57b364da7UL,0x57c5ad07bUL,0x57d81f70cUL,0x57e37b9e2UL,0x57fec9e95UL,0x570ee825aUL,0x57135a52dUL,0x57283ebc3UL,0x57358ccb4UL,0x574345168UL,0x575ef761fUL,0x5765938f1UL,0x577821f86UL,0x5685d016fUL,0x569862618UL,0x56a3068f6UL,0x56beb4f81UL,0x56c87d25dUL,0x56d5cf52aUL,0x56eeabbc4UL,0x56f319cb3UL,0x56033807cUL,0x561e8a70bUL,0x5625ee9e5UL,0x56385ce92UL,0x564e9534eUL,0x565327439UL,0x566843ad7UL,0x5675f1da0UL,0x558e12072UL,0x5593a0705UL,0x55a8c49ebUL,0x55b576e9cUL,0x55c3bf340UL,0x55de0d437UL,0x55e569ad9UL,0x55f8dbdaeUL,0x5508fa161UL,0x551548616UL,0x552e2c8f8UL,0x55339ef8fUL,0x554557253UL,0x5558e5524UL,0x556381bcaUL,0x557e33cbdUL,0x5483c2254UL,0x549e70523UL,0x54a514bcdUL,0x54b8a6cbaUL,0x54ce6f166UL,0x54d3dd611UL,0x54e8b98ffUL,0x54f50bf88UL,0x54052a347UL,0x541898430UL,0x5423fcadeUL,0x543e4eda9UL,0x544887075UL,0x545535702UL,0x546e519ecUL,0x5473e3e9bUL,0x53842453fUL,0x539996248UL,0x53a2f2ca6UL,0x53bf40bd1UL,0x53c98960dUL,0x53d43b17aUL,0x53ef5ff94UL,0x53f2ed8e3UL,0x5302cc42cUL,0x531f7e35bUL,0x53241adb5UL,0x5339a8ac2UL,0x534f6171eUL,0x5352d3069UL,0x5369b7e87UL,0x5374059f0UL,0x5289f4719UL,0x52944606eUL,0x52af22e80UL,0x52b2909f7UL,0x52c45942bUL,0x52d9eb35cUL,0x52e28fdb2UL,0x52ff3dac5UL,0x520f1c60aUL,0x5212ae17dUL,0x5229caf93UL,0x5234788e4UL,0x5242b1538UL,0x525f0324fUL,0x526467ca1UL,0x5279d5bd6UL,0x518236604UL,0x519f84173UL,0x51a4e0f9dUL,0x51b9528eaUL,0x51cf9b536UL,0x51d229241UL,0x51e94dcafUL,0x51f4ffbd8UL,0x5104de717UL,0x51196c060UL,0x512208e8eUL,0x513fba9f9UL,0x514973425UL,0x5154c1352UL,0x516fa5dbcUL,0x517217acbUL,0x508fe6422UL,0x509254355UL,0x50a930dbbUL,0x50b482accUL,0x50c24b710UL,0x50dff9067UL,0x50e49de89UL,0x50f92f9feUL,0x50090e531UL,0x5014bc246UL,0x502fd8ca8UL,0x50326abdfUL,0x5044a3603UL,0x505911174UL,0x506275f9aUL,0x507fc78edUL,0x6f8c576ffUL,0x6f91e5188UL,0x6faa81f66UL,0x6fb733811UL,0x6fc1fa5cdUL,0x6fdc482baUL,0x6fe72cc54UL,0x6ffa9eb23UL,0x6f0abf7ecUL,0x6f170d09bUL,0x6f2c69e75UL,0x6f31db902UL,0x6f47124deUL,0x6f5aa03a9UL,0x6f61c4d47UL,0x6f7c76a30UL,0x6e81874d9UL,0x6e9c353aeUL,0x6ea751d40UL,0x6ebae3a37UL,0x6ecc2a7ebUL,0x6ed19809cUL,0x6eeafce72UL,0x6ef74e905UL,0x6e076f5caUL,0x6e1add2bdUL,0x6e21b9c53UL,0x6e3c0bb24UL,0x6e4ac26f8UL,0x6e577018fUL,0x6e6c14f61UL,0x6e71a6816UL,0x6d8a455c4UL,0x6d97f72b3UL,0x6dac93c5dUL,0x6db121b2aUL,0x6dc7e86f6UL,0x6dda5a181UL,0x6de13ef6fUL,0x6dfc8c818UL,0x6d0cad4d7UL,0x6d111f3a0UL,0x6d2a7bd4eUL,0x6d37c9a39UL,0x6d41007e5UL,0x6d5cb2092UL,0x6d67d6e7cUL,0x6d7a6490bUL,0x6c87957e2UL,0x6c9a27095UL,0x6ca143e7bUL,0x6cbcf190cUL,0x6cca384d0UL,0x6cd78a3a7UL,0x6ceceed49UL,0x6cf15ca3eUL,0x6c017d6f1UL,0x6c1ccf186UL,0x6c27abf68UL,0x6c3a1981fUL,0x6c4cd05c3UL,0x6c51622b4UL,0x6c6a06c5aUL,0x6c77b4b2dUL,0x6b8073089UL,0x6b9dc17feUL,0x6ba6a5910UL,0x6bbb17e67UL,0x6bcdde3bbUL,0x6bd06c4ccUL,0x6beb08a22UL,0x6bf6bad55UL,0x6b069b19aUL,0x6b1b296edUL,0x6b204d803UL,0x6b3dfff74UL,0x6b4b362a8UL,0x6b56845dfUL,0x6b6de0b31UL,0x6b7052c46UL,0x6a8da32afUL,0x6a90115d8UL,0x6aab75b36UL,0x6ab6c7c41UL,0x6ac00e19dUL,0x6addbc6eaUL,0x6ae6d8804UL,0x6afb6af73UL,0x6a0b4b3bcUL,0x6a16f94cbUL,0x6a2d9da25UL,0x6a302fd52UL,0x6a46e608eUL,0x6a5b547f9UL,0x6a6030917UL,0x6a7d82e60UL,0x6986613b2UL,0x699bd34c5UL,0x69a0b7a2bUL,0x69bd05d5cUL,0x69cbcc080UL,0x69d67e7f7UL,0x69ed1a919UL,0x69f0a8e6eUL,0x6900892a1UL,0x691d3b5d6UL,0x69265fb38UL,0x693bedc4fUL,0x694d24193UL,0x6950966e4UL,0x696bf280aUL,0x697640f7dUL,0x688bb1194UL,0x6896036e3UL,0x68ad6780dUL,0x68b0d5f7aUL,0x68c61c2a6UL,0x68dbae5d1UL,0x68e0cab3fUL,0x68fd78c48UL,0x680d59087UL,0x6810eb7f0UL,0x682b8f91eUL,0x68363de69UL,0x6840f43b5UL,0x685d464c2UL,0x686622a2cUL,0x687b90d5bUL,0x6789add64UL,0x67941fa13UL,0x67af7b4fdUL,0x67b2c938aUL,0x67c400e56UL,0x67d9b2921UL,0x67e2d67cfUL,0x67ff640b8UL,0x670f45c77UL,0x6712f7b00UL,0x6729935eeUL,0x673421299UL,0x6742e8f45UL,0x675f5a832UL,0x67643e6dcUL,0x67798c1abUL,0x66847df42UL,0x6699cf835UL,0x66a2ab6dbUL,0x66bf191acUL,0x66c9d0c70UL,0x66d462b07UL,0x66ef065e9UL,0x66f2b429eUL,0x660295e51UL,0x661f27926UL,0x6624437c8UL,0x6639f10bfUL,0x664f38d63UL,0x66528aa14UL,0x6669ee4faUL,0x66745c38dUL,0x658fbfe5fUL,0x65920d928UL,0x65a9697c6UL,0x65b4db0b1UL,0x65c212d6dUL,0x65dfa0a1aUL,0x65e4c44f4UL,0x65f976383UL,0x650957f4cUL,0x6514e583bUL,0x652f816d5UL,0x6532331a2UL,0x6544fac7eUL,0x655948b09UL,0x65622c5e7UL,0x657f9e290UL,0x64826fc79UL,0x649fddb0eUL,0x64a4b95e0UL,0x64b90b297UL,0x64cfc2f4bUL,0x64d27083cUL,0x64e9146d2UL,0x64f4a61a5UL,0x640487d6aUL,0x641935a1dUL,0x6422514f3UL,0x643fe3384UL,0x64492ae58UL,0x64549892fUL,0x646ffc7c1UL,0x64724e0b6UL,0x638589b12UL,0x63983bc65UL,0x63a35f28bUL,0x63beed5fcUL,0x63c824820UL,0x63d596f57UL,0x63eef21b9UL,0x63f3406ceUL,0x630361a01UL,0x631ed3d76UL,0x6325b7398UL,0x6338054efUL,0x634ecc933UL,0x63537ee44UL,0x63681a0aaUL,0x6375a87ddUL,0x628859934UL,0x6295ebe43UL,0x62ae8f0adUL,0x62b33d7daUL,0x62c5f4a06UL,0x62d846d71UL,0x62e32239fUL,0x62fe904e8UL,0x620eb1827UL,0x621303f50UL,0x6228671beUL,0x6235d56c9UL,0x62431cb15UL,0x625eaec62UL,0x6265ca28cUL,0x6278785fbUL,0x61839b829UL,0x619e29f5eUL,0x61a54d1b0UL,0x61b8ff6c7UL,0x61ce36b1bUL,0x61d384c6cUL,0x61e8e0282UL,0x61f5525f5UL,0x61057393aUL,0x6118c1e4dUL,0x6123a50a3UL,0x613e177d4UL,0x6148dea08UL,0x61556cd7fUL,0x616e08391UL,0x6173ba4e6UL,0x608e4ba0fUL,0x6093f9d78UL,0x60a89d396UL,0x60b52f4e1UL,0x60c3e693dUL,0x60de54e4aUL,0x60e5300a4UL,0x60f8827d3UL,0x6008a3b1cUL,0x601511c6bUL,0x602e75285UL,0x6033c75f2UL,0x60450e82eUL,0x6058bcf59UL,0x6063d81b7UL,0x607e6a6c0UL,0x7f87a21c9UL,0x7f9a106beUL,0x7fa174850UL,0x7fbcc6f27UL,0x7fca0f2fbUL,0x7fd7bd58cUL,0x7fecd9b62UL,0x7ff16bc15UL,0x7f014a0daUL,0x7f1cf87adUL,0x7f279c943UL,0x7f3a2ee34UL,0x7f4ce73e8UL,0x7f515549fUL,0x7f6a31a71UL,0x7f7783d06UL,0x7e8a723efUL,0x7e97c0498UL,0x7eaca4a76UL,0x7eb116d01UL,0x7ec7df0ddUL,0x7eda6d7aaUL,0x7ee109944UL,0x7efcbbe33UL,0x7e0c9a2fcUL,0x7e112858bUL,0x7e2a4cb65UL,0x7e37fec12UL,0x7e41371ceUL,0x7e5c856b9UL,0x7e67e1857UL,0x7e7a53f20UL,0x7d81b02f2UL,0x7d9c02585UL,0x7da766b6bUL,0x7dbad4c1cUL,0x7dcc1d1c0UL,0x7dd1af6b7UL,0x7deacb859UL,0x7df779f2eUL,0x7d07583e1UL,0x7d1aea496UL,0x7d218ea78UL,0x7d3c3cd0fUL,0x7d4af50d3UL,0x7d57477a4UL,0x7d6c2394aUL,0x7d7191e3dUL,0x7c8c600d4UL,0x7c91d27a3UL,0x7caab694dUL,0x7cb704e3aUL,0x7cc1cd3e6UL,0x7cdc7f491UL,0x7ce71ba7fUL,0x7cfaa9d08UL,0x7c0a881c7UL,0x7c173a6b0UL,0x7c2c5e85eUL,0x7c31ecf29UL,0x7c47252f5UL,0x7c5a97582UL,0x7c61f3b6cUL,0x7c7c41c1bUL,0x7b8b867bfUL,0x7b96340c8UL,0x7bad50e26UL,0x7bb0e2951UL,0x7bc62b48dUL,0x7bdb993faUL,0x7be0fdd14UL,0x7bfd4fa63UL,0x7b0d6e6acUL,0x7b10dc1dbUL,0x7b2bb8f35UL,0x7b360a842UL,0x7b40c359eUL,0x7b5d712e9UL,0x7b6615c07UL,0x7b7ba7b70UL,0x7a8656599UL,0x7a9be42eeUL,0x7aa080c00UL,0x7abd32b77UL,0x7acbfb6abUL,0x7ad6491dcUL,0x7aed2df32UL,0x7af09f845UL,0x7a00be48aUL,0x7a1d0c3fdUL,0x7a2668d13UL,0x7a3bdaa64UL,0x7a4d137b8UL,0x7a50a10cfUL,0x7a6bc5e21UL,0x7a7677956UL,0x798d94484UL,0x7990263f3UL,0x79ab42d1dUL,0x79b6f0a6aUL,0x79c0397b6UL,0x79dd8b0c1UL,0x79e6efe2fUL,0x79fb5d958UL,0x790b7c597UL,0x7916ce2e0UL,0x792daac0eUL,0x793018b79UL,0x7946d16a5UL,0x795b631d2UL,0x796007f3cUL,0x797db584bUL,0x7880446a2UL,0x789df61d5UL,0x78a692f3bUL,0x78bb2084cUL,0x78cde9590UL,0x78d05b2e7UL,0x78eb3fc09UL,0x78f68db7eUL,0x7806ac7b1UL,0x781b1e0c6UL,0x78207ae28UL,0x783dc895fUL,0x784b01483UL,0x7856b33f4UL,0x786dd7d1aUL,0x787065a6dUL,0x778258a52UL,0x779fead25UL,0x77a48e3cbUL,0x77b93c4bcUL,0x77cff5960UL,0x77d247e17UL,0x77e9230f9UL,0x77f49178eUL,0x7704b0b41UL,0x771902c36UL,0x7722662d8UL,0x773fd45afUL,0x77491d873UL,0x7754aff04UL,0x776fcb1eaUL,0x77727969dUL,0x768f88874UL,0x76923af03UL,0x76a95e1edUL,0x76b4ec69aUL,0x76c225b46UL,0x76df97c31UL,0x76e4f32dfUL,0x76f9415a8UL,0x760960967UL,0x7614d2e10UL,0x762fb60feUL,0x763204789UL,0x7644cda55UL,0x76597fd22UL,0x76621b3ccUL,0x767fa94bbUL,0x75844a969UL,0x7599f8e1eUL,0x75a29c0f0UL,0x75bf2e787UL,0x75c9e7a5bUL,0x75d455d2cUL,0x75ef313c2UL,0x75f2834b5UL,0x7502a287aUL,0x751f10f0dUL,0x7524741e3UL,0x7539c6694UL,0x754f0fb48UL,0x7552bdc3fUL,0x7569d92d1UL,0x75746b5a6UL,0x74899ab4fUL,0x749428c38UL,0x74af4c2d6UL,0x74b2fe5a1UL,0x74c43787dUL,0x74d985f0aUL,0x74e2e11e4UL,0x74ff53693UL,0x740f72a5cUL,0x7412c0d2bUL,0x7429a43c5UL,0x7434164b2UL,0x7442df96eUL,0x745f6de19UL,0x7464090f7UL,0x7479bb780UL,0x738e7cc24UL,0x7393ceb53UL,0x73a8aa5bdUL,0x73b5182caUL,0x73c3d1f16UL,0x73de63861UL,0x73e50768fUL,0x73f8b51f8UL,0x730894d37UL,0x731526a40UL,0x732e424aeUL,0x7333f03d9UL,0x734539e05UL,0x73588b972UL,0x7363ef79cUL,0x737e5d0ebUL,0x7283ace02UL,0x729e1e975UL,0x72a57a79bUL,0x72b8c80ecUL,0x72ce01d30UL,0x72d3b3a47UL,0x72e8d74a9UL,0x72f5653deUL,0x720544f11UL,0x7218f6866UL,0x722392688UL,0x723e201ffUL,0x7248e9c23UL,0x72555bb54UL,0x726e3f5baUL,0x72738d2cdUL,0x71886ef1fUL,0x7195dc868UL,0x71aeb8686UL,0x71b30a1f1UL,0x71c5c3c2dUL,0x71d871b5aUL,0x71e3155b4UL,0x71fea72c3UL,0x710e86e0cUL,0x71133497bUL,0x712850795UL,0x7135e20e2UL,0x71432bd3eUL,0x715e99a49UL,0x7165fd4a7UL,0x71784f3d0UL,0x7085bed39UL,0x70980ca4eUL,0x70a3684a0UL,0x70beda3d7UL,0x70c813e0bUL,0x70d5a197cUL,0x70eec5792UL,0x70f3770e5UL,0x700356c2aUL,0x701ee4b5dUL,0x7025805b3UL,0x7038322c4UL,0x704efbf18UL,0x70534986fUL,0x70682d681UL,0x70759f1f6UL}; + fromVector(codes,d._code_id); + d._nbits=36; + d._tau=2;// + d._type=ARTOOLKITPLUSBCH; + d._name="ARTOOLKITPLUSBCH"; + + }break; + case TAG36h11:{ +vector codes={0xd5d628584UL,0xd97f18b49UL,0xdd280910eUL,0xe479e9c98UL,0xebcbca822UL,0xf31dab3acUL,0x56a5d085UL,0x10652e1d4UL,0x22b1dfeadUL,0x265ad0472UL,0x34fe91b86UL,0x3ff962cd5UL,0x43a25329aUL,0x474b4385fUL,0x4e9d243e9UL,0x5246149aeUL,0x5997f5538UL,0x683bb6c4cUL,0x6be4a7211UL,0x7e3158eeaUL,0x81da494afUL,0x858339a74UL,0x8cd51a5feUL,0x9f21cc2d7UL,0xa2cabc89cUL,0xadc58d9ebUL,0xb16e7dfb0UL,0xb8c05eb3aUL,0xd25ef139dUL,0xd607e1962UL,0xe4aba3076UL,0x2dde6a3daUL,0x43d40c678UL,0x5620be351UL,0x64c47fa65UL,0x686d7002aUL,0x6c16605efUL,0x6fbf50bb4UL,0x8d06d39dcUL,0x9f53856b5UL,0xadf746dc9UL,0xbc9b084ddUL,0xd290aa77bUL,0xd9e28b305UL,0xe4dd5c454UL,0xfad2fe6f2UL,0x181a8151aUL,0x26be42c2eUL,0x2e10237b8UL,0x405cd5491UL,0x7742eab1cUL,0x85e6ac230UL,0x8d388cdbaUL,0x9f853ea93UL,0xc41ea2445UL,0xcf1973594UL,0x14a34a333UL,0x31eacd15bUL,0x6c79d2dabUL,0x73cbb3935UL,0x89c155bd3UL,0x8d6a46198UL,0x91133675dUL,0xa708d89fbUL,0xae5ab9585UL,0xb9558a6d4UL,0xb98743ab2UL,0xd6cec68daUL,0x1506bcaefUL,0x4becd217aUL,0x4f95c273fUL,0x658b649ddUL,0xa76c4b1b7UL,0xecf621f56UL,0x1c8a56a57UL,0x3628e92baUL,0x53706c0e2UL,0x5e6b3d231UL,0x7809cfa94UL,0xe97eead6fUL,0x5af40604aUL,0x7492988adUL,0xed5994712UL,0x5eceaf9edUL,0x7c1632815UL,0xc1a0095b4UL,0xe9e25d52bUL,0x3a6705419UL,0xa8333012fUL,0x4ce5704d0UL,0x508e60a95UL,0x877476120UL,0xa864e950dUL,0xea45cfce7UL,0x19da047e8UL,0x24d4d5937UL,0x6e079cc9bUL,0x99f2e11d7UL,0x33aa50429UL,0x499ff26c7UL,0x50f1d3251UL,0x66e7754efUL,0x96ad633ceUL,0x9a5653993UL,0xaca30566cUL,0xc298a790aUL,0x8be44b65dUL,0xdc68f354bUL,0x16f7f919bUL,0x4dde0e826UL,0xd548cbd9fUL,0xe0439ceeeUL,0xfd8b1fd16UL,0x76521bb7bUL,0xd92375742UL,0xcab16d40cUL,0x730c9dd72UL,0xad9ba39c2UL,0xb14493f87UL,0x52b15651fUL,0x185409cadUL,0x77ae2c68dUL,0x94f5af4b5UL,0xa13bad55UL,0x61ea437cdUL,0xa022399e2UL,0x203b163d1UL,0x7bba8f40eUL,0x95bc9442dUL,0x41c0b5358UL,0x8e9c6cc81UL,0xeb549670UL,0x9da3a0b51UL,0xd832a67a1UL,0xdcd4350bcUL,0x4aa05fdd2UL,0x60c7bb44eUL,0x4b358b96cUL,0x67299b45UL,0xb9c89b5faUL,0x6975acaeaUL,0x62b8f7afaUL,0x33567c3d7UL,0xbac139950UL,0xa5927c62aUL,0x5c916e6a4UL,0x260ecb7d5UL,0x29b7bbd9aUL,0x903205f26UL,0xae72270a4UL,0x3d2ec51a7UL,0x82ea55324UL,0x11a6f3427UL,0x1ca1c4576UL,0xa40c81aefUL,0xbddccd730UL,0xe617561eUL,0x969317b0fUL,0x67f781364UL,0x610912f96UL,0xb2549fdfcUL,0x6e5aaa6bUL,0xb6c475339UL,0xc56836a4dUL,0x844e351ebUL,0x4647f83b4UL,0x908a04f5UL,0x7f51034c9UL,0xaee537fcaUL,0x5e92494baUL,0xd445808f4UL,0x28d68b563UL,0x4d25374bUL,0x2bc065f65UL,0x96dc3ea0cUL,0x4b2ade817UL,0x7c3fd502UL,0xe768b5cafUL,0x17605cf6cUL,0x182741ee4UL,0x62846097cUL,0x72b5ebf80UL,0x263da6e13UL,0xfa841bcb5UL,0x7e45e8c69UL,0x653c81fa0UL,0x7443b5e70UL,0xa5234afdUL,0x74756f24eUL,0x157ebf02aUL,0x82ef46939UL,0x80d420264UL,0x2aeed3e98UL,0xb0a1dd4f8UL,0xb5436be13UL,0x7b7b4b13bUL,0x1ce80d6d3UL,0x16c08427dUL,0xee54462ddUL,0x1f7644cceUL,0x9c7b5cc92UL,0xe369138f8UL,0x5d5a66e91UL,0x485d62f49UL,0xe6e819e94UL,0xb1f340eb5UL,0x9d198ce2UL,0xd60717437UL,0x196b856cUL,0xf0a6173a5UL,0x12c0e1ec6UL,0x62b82d5cfUL,0xad154c067UL,0xce3778832UL,0x6b0a7b864UL,0x4c7686694UL,0x5058ff3ecUL,0xd5e21ea23UL,0x9ff4a76eeUL,0x9dd981019UL,0x1bad4d30aUL,0xc601896d1UL,0x973439b48UL,0x1ce7431a8UL,0x57a8021d6UL,0xf9dba96e6UL,0x83a2e4e7cUL,0x8ea585380UL,0xaf6c0e744UL,0x875b73babUL,0xda34ca901UL,0x2ab9727efUL,0xd39f21b9aUL,0x8a10b742fUL,0x5f8952dbaUL,0xf8da71ab0UL,0xc25f9df96UL,0x6f8a5d94UL,0xe42e63e1aUL,0xb78409d1bUL,0x792229addUL,0x5acf8c455UL,0x2fc29a9b0UL,0xea486237bUL,0xb0c9685a0UL,0x1ad748a47UL,0x3b4712d5UL,0xf29216d30UL,0x8dad65e49UL,0xa2cf09ddUL,0xb5f174c6UL,0xe54f57743UL,0xb9cf54d78UL,0x4a312a88aUL,0x27babc962UL,0xb86897111UL,0xf2ff6c116UL,0x82274bd8aUL,0x97023505eUL,0x52d46edd1UL,0x585c1f538UL,0xbddd00e43UL,0x5590b74dfUL,0x729404a1fUL,0x65320855eUL,0xd3d4b6956UL,0x7ae374f14UL,0x2d7a60e06UL,0x315cd9b5eUL,0xfd36b4eacUL,0xf1df7642bUL,0x55db27726UL,0x8f15ebc19UL,0x992f8c531UL,0x62dea2a40UL,0x928275cabUL,0x69c263cb9UL,0xa774cca9eUL,0x266b2110eUL,0x1b14acbb8UL,0x624b8a71bUL,0x1c539406bUL,0x3086d529bUL,0x111dd66eUL,0x98cd630bfUL,0x8b9d1ffdcUL,0x72b2f61e7UL,0x9ed9d672bUL,0x96cdd15f3UL,0x6366c2504UL,0x6ca9df73aUL,0xa066d60f0UL,0xe7a4b8addUL,0x8264647efUL,0xaa195bf81UL,0x9a3db8244UL,0x14d2df6aUL,0xb63265b7UL,0x2f010de73UL,0x97e774986UL,0x248affc29UL,0xfb57dcd11UL,0xb1a7e4d9UL,0x4bfa2d07dUL,0x54e5cdf96UL,0x4c15c1c86UL,0xcd9c61166UL,0x499380b2aUL,0x540308d09UL,0x8b63fe66fUL,0xc81aeb35eUL,0x86fe0bd5cUL,0xce2480c2aUL,0x1ab29ee60UL,0x8048daa15UL,0xdbfeb2d39UL,0x567c9858cUL,0x2b6edc5bcUL,0x2078fca82UL,0xadacc22aaUL,0xb92486f49UL,0x51fac5964UL,0x691ee6420UL,0xf63b3e129UL,0x39be7e572UL,0xda2ce6c74UL,0x20cf17a5cUL,0xee55f9b6eUL,0xfb8572726UL,0xb2c2de548UL,0xcaa9bce92UL,0xae9182db3UL,0x74b6e5bd1UL,0x137b252afUL,0x51f686881UL,0xd672f6c02UL,0x654146ce4UL,0xf944bc825UL,0xe8327f809UL,0x76a73fd59UL,0xf79da4cb4UL,0x956f8099bUL,0x7b5f2655cUL,0xd06b114a6UL,0xd0697ca50UL,0x27c390797UL,0xbc61ed9b2UL,0xcc12dd19bUL,0xeb7818d2cUL,0x92fcecdaUL,0x89ded4ea1UL,0x256a0ba34UL,0xb6948e627UL,0x1ef6b1054UL,0x8639294a2UL,0xeda3780a4UL,0x39ee2af1dUL,0xcd257edc5UL,0x2d9d6bc22UL,0x121d3b47dUL,0x37e23f8adUL,0x119f31cf6UL,0x2c97f4f09UL,0xd502abfe0UL,0x10bc3ca77UL,0x53d7190efUL,0x90c3e62a6UL,0x7e9ebf675UL,0x979ce23d1UL,0x27f0c98e9UL,0xeafb4ae59UL,0x7ca7fe2bdUL,0x1490ca8f6UL,0x9123387baUL,0xb3bc73888UL,0x3ea87e325UL,0x4888964aaUL,0xa0188a6b9UL,0xcd383c666UL,0x40029a3fdUL,0xe1c00ac5cUL,0x39e6f2b6eUL,0xde664f622UL,0xe979a75e8UL,0x7c6b4c86cUL,0xfd492e071UL,0x8fbb35118UL,0x40b4a09b7UL,0xaf80bd6daUL,0x70e0b2521UL,0x2f5c54d93UL,0x3f4a118d5UL,0x9c1897b9UL,0x79776eacUL,0x84b00b17UL,0x3a95ad90eUL,0x28c544095UL,0x39d457c05UL,0x7a3791a78UL,0xbb770e22eUL,0x9a822bd6cUL,0x68a4b1fedUL,0xa5fd27b3bUL,0xc3995b79UL,0xd1519dff1UL,0x8e7eee359UL,0xcd3ca50b1UL,0xb73b8b793UL,0x57aca1c43UL,0xec2655277UL,0x785a2c1b3UL,0x75a07985aUL,0xa4b01eb69UL,0xa18a11347UL,0xdb1f28ca3UL,0x877ec3e25UL,0x31f6341b8UL,0x1363a3a4cUL,0x75d8b9baUL,0x7ae0792a9UL,0xa83a21651UL,0x7f08f9fb5UL,0xd0cf73a9UL,0xb04dcc98eUL,0xf65c7b0f8UL,0x65ddaf69aUL,0x2cf9b86b3UL,0x14cb51e25UL,0xf48027b5bUL,0xec26ea8bUL,0x44bafd45cUL,0xb12c7c0c4UL,0x959fd9d82UL,0xc77c9725aUL,0x48a22d462UL,0x8398e8072UL,0xec89b05ceUL,0xbb682d4c9UL,0xe5a86d2ffUL,0x358f01134UL,0x8556ddcf6UL,0x67584b6e2UL,0x11609439fUL,0x8488816eUL,0xaaf1a2c46UL,0xf879898cfUL,0x8bbe5e2f7UL,0x101eee363UL,0x690f69377UL,0xf5bd93cd9UL,0xcea4c2bf6UL,0x9550be706UL,0x2c5b38a60UL,0xe72033547UL,0x4458b0629UL,0xee8d9ed41UL,0xd2f918d72UL,0x78dc39fd3UL,0x8212636f6UL,0x7450a72a7UL,0xc4f0cf4c6UL,0x367bcddcdUL,0xc1caf8cc6UL,0xa7f5b853dUL,0x9d536818bUL,0x535e021b0UL,0xa7eb8729eUL,0x422a67b49UL,0x929e928a6UL,0x48e8aefccUL,0xa9897393cUL,0x5eb81d37eUL,0x1e80287b7UL,0x34770d903UL,0x2eef86728UL,0x59266ccb6UL,0x110bba61UL,0x1dfd284efUL,0x447439d1bUL,0xfece0e599UL,0x9309f3703UL,0x80764d1ddUL,0x353f1e6a0UL,0x2c1c12dccUL,0xc1d21b9d7UL,0x457ee453eUL,0xd66faf540UL,0x44831e652UL,0xcfd49a848UL,0x9312d4133UL,0x3f097d3eeUL,0x8c9ebef7aUL,0xa99e29e88UL,0xe9fab22cUL,0x4e748f4fbUL,0xecdee4288UL,0xabce5f1d0UL,0xc42f6876cUL,0x7ed402ea0UL,0xe5c4242c3UL,0xd5b2c31aeUL,0x286863be6UL,0x160444d94UL,0x5f0f5808eUL,0xae3d44b2aUL,0x9f5c5d109UL,0x8ad9316d7UL,0x3422ba064UL,0x2fed11d56UL,0xbea6e3e04UL,0x4b029eecUL,0x6deed7435UL,0x3718ce17cUL,0x55857f5e2UL,0x2edac7b62UL,0x85d6c512UL,0xd6ca88e0fUL,0x2b7e1fc69UL,0xa699d5c1bUL,0xf05ad74deUL,0x4cf5fb56dUL,0x5725e07e1UL,0x72f18a2deUL,0x1cec52609UL,0x48534243cUL,0x2523a4d69UL,0x35c1b80d1UL,0xa4d7338a7UL,0xdb1af012UL,0xe61a9475dUL,0x5df03f91UL,0x97ae260bbUL,0x32d627fefUL,0xb640f73c2UL,0x45a1ac9c6UL,0x6a2202de1UL,0x57d3e25f2UL,0x5aa9f986eUL,0xcc859d8aUL,0xe3ec6cca8UL,0x54e95e1aeUL,0x446887b06UL,0x7516732beUL,0x3817ac8f5UL,0x3e26d938cUL,0xaa81bc235UL,0xdf387ca1bUL,0xf3a3b3f2UL,0xb4bf69677UL,0xae21868edUL,0x81e1d2d9dUL,0xa0a9ea14cUL,0x8eee297a9UL,0x4740c0559UL,0xe8b141837UL,0xac69e0a3dUL,0x9ed83a1e1UL,0x5edb55ecbUL,0x7340fe81UL,0x50dfbc6bfUL,0x4f583508aUL,0xcb1fb78bcUL,0x4025ced2fUL,0x39791ebecUL,0x53ee388f1UL,0x7d6c0bd23UL,0x93a995fbeUL,0x8a41728deUL,0x2fe70e053UL,0xab3db443aUL,0x1364edb05UL,0x47b6eeed6UL,0x12e71af01UL,0x52ff83587UL,0x3a1575dd8UL,0x3feaa3564UL,0xeacf78ba7UL,0x872b94f8UL,0xda8ddf9a2UL,0x9aa920d2bUL,0x1f350ed36UL,0x18a5e861fUL,0x2c35b89c3UL,0x3347ac48aUL,0x7f23e022eUL,0x2459068fbUL,0xe83be4b73UL}; +fromVector(codes,d._code_id); + d._nbits=36; + d._tau=11; + d._type=TAG36h11; + d._name="TAG36h11"; + + }break; + case TAG36h10:{ + vector codes={0x1ca92a687UL,0x20521ac4cUL,0x27a3fb7d6UL,0x2b4cebd9bUL,0x3647bceeaUL,0x39f0ad4afUL,0x3d999da74UL,0x44eb7e5feUL,0x538f3fd12UL,0x5738302d7UL,0x65dbf19ebUL,0x70d6c2b3aUL,0x7f7a8424eUL,0x832374813UL,0x86cc64dd8UL,0x8a755539dUL,0x9570264ecUL,0x991916ab1UL,0xa06af763bUL,0xab65c878aUL,0xb2b7a9314UL,0xb660998d9UL,0xbdb27a463UL,0xcc563bb77UL,0xe24bdde15UL,0xed46aef64UL,0xf4988faeeUL,0x6e5417c7UL,0x158902edbUL,0x1cdae3a65UL,0x242cc45efUL,0x27d5b4bb4UL,0x2b7ea5179UL,0x32d085d03UL,0x3679762c8UL,0x3a226688dUL,0x3dcb56e52UL,0x48c627fa1UL,0x5769e96b5UL,0x6264ba804UL,0x660daadc9UL,0x6d5f8b953UL,0x74b16c4ddUL,0x7fac3d62cUL,0x91f8ef305UL,0x95a1df8caUL,0x994acfe8fUL,0xa09cb0a19UL,0xa445a0fdeUL,0xa7ee915a3UL,0xab9781b68UL,0xaf407212dUL,0xb69252cb7UL,0xc8df04990UL,0xd3d9d5adfUL,0xd782c60a4UL,0xf12158907UL,0x1d0c9ce43UL,0x20b58d408UL,0x2f594eb1cUL,0x3a541fc6bUL,0x454ef0dbaUL,0x53f2b24ceUL,0x629673be2UL,0x74e3258bbUL,0x8ad8c7b59UL,0x9d2579832UL,0xa8204a981UL,0xaf722b50bUL,0xb6c40c095UL,0xba6cfc65aUL,0xf15311ce5UL,0x748b3f83UL,0xaf1a4548UL,0xe9a94b0dUL,0x2be217935UL,0x3e2ec960eUL,0x4cd28ad22UL,0x507b7b2e7UL,0x54246b8acUL,0x57cd5be71UL,0x6a1a0db4aUL,0x6dc2fe10fUL,0x876190972UL,0x99ae4264bUL,0xabfaf4324UL,0xc9427714cUL,0xd09457cd6UL,0xd43d4829bUL,0xea32ea539UL,0xf52dbb688UL,0x161e2ea75UL,0x286ae074eUL,0x66a2d6963UL,0x8b3c3a315UL,0x8ee52a8daUL,0xa131dc5b3UL,0xe6bbb3352UL,0xf55f74a66UL,0x5a45bb5UL,0x7ac2673fUL,0x1da1c89ddUL,0x289c99b2cUL,0x3ae94b805UL,0x50deedaa3UL,0x5830ce62dUL,0x5bd9bebf2UL,0x632b9f77cUL,0x6e26708cbUL,0x841c12b69UL,0x92bfd427dUL,0x9668c4842UL,0x9dbaa53ccUL,0xb007570a5UL,0xb3b04766aUL,0xc25408d7eUL,0xea965ccf5UL,0xf93a1e409UL,0x434ef558UL,0x1681a1231UL,0x1dd381dbbUL,0x302033a94UL,0x75aa0a833UL,0x92f18d65bUL,0x9a436e1e5UL,0xa1954ed6fUL,0xb78af100dUL,0xbb33e15d2UL,0xc62eb2721UL,0x466a8936UL,0xf6179a85UL,0x16b35a60fUL,0x589440de9UL,0x6738024fdUL,0x847f85325UL,0x9e1e17b88UL,0xacc1d929cUL,0xb06ac9861UL,0xd5042d213UL,0xfd468118aUL,0xf9332e63UL,0x342c96815UL,0x37d586ddaUL,0x551d09c02UL,0x5c6eea78cUL,0x6017dad51UL,0x9354ffe17UL,0x9aa6e09a1UL,0xa94aa20b5UL,0xacf39267aUL,0xbb9753d8eUL,0xbf4044353UL,0x8730b6b7UL,0x1716ccdcbUL,0x22119df1aUL,0x3f5920d42UL,0x58f7b35a5UL,0x6b446527eUL,0x972fa97baUL,0x9e818a344UL,0xa5d36aeceUL,0xd1beaf40aUL,0xdcb980559UL,0xf65812dbcUL,0x139f95be4UL,0x6b761e65cUL,0x6f1f0ec21UL,0xa605242acUL,0xe43d1a4c1UL,0x29c6f1260UL,0x386ab2974UL,0x4e6054c12UL,0x8c984ae27UL,0x97931bf76UL,0x9ee4fcb00UL,0xd22221bc6UL,0xe46ed389fUL,0xebc0b4429UL,0x6f82813ddUL,0x732b719a2UL,0x9072f47caUL,0x941be4d8fUL,0x97c4d5354UL,0x9f16b5edeUL,0xb8b548741UL,0xd253dafa4UL,0xd5fccb569UL,0xd9a5bbb2eUL,0xe4a08cc7dUL,0x3c77156f5UL,0x7aaf0b90aUL,0xb53e1155aUL,0xb8e701b1fUL,0x5c2b9448UL,0x14667ab5cUL,0x47a39fc22UL,0x4ef5807acUL,0x64eb22a4aUL,0x982847b10UL,0xaa74f97e9UL,0xae1de9daeUL,0xb56fca938UL,0xf750b1112UL,0x1bea14ac4UL,0x1f9305089UL,0x233bf564eUL,0x31dfb6d62UL,0x3931978ecUL,0x52d02a14fUL,0x5a220acd9UL,0xb1f893751UL,0xfb2b5aab5UL,0x40b531854UL,0x5a53c40b7UL,0x8d90e917dUL,0x9139d9742UL,0x94e2c9d07UL,0xd6c3b04e1UL,0xe910621baUL,0xf40b33309UL,0x1152b6131UL,0x32432951eUL,0x3d3dfa66dUL,0x65804e5e4UL,0xab0a25383UL,0xb604f64d2UL,0xde474a449UL,0x11846f50fUL,0x6d03e854cUL,0x7455c90d6UL,0xab3bde761UL,0xdad013262UL,0xe973d4976UL,0x2b54bb150UL,0x9577f58a1UL,0x9920e5e66UL,0xb66868c8eUL,0xf4a05eea3UL,0x3dd326207UL,0x5b1aa902fUL,0x99529f244UL,0xb2f131aa7UL,0xd038b48cfUL,0xd3e1a4e94UL,0x24664cd82UL,0x36b2fea5bUL,0x95db6805dUL,0xa0d6391acUL,0xabd10a2fbUL,0x15f444a4cUL,0x2be9e6ceaUL,0x57d52b226UL,0x5f270bdb0UL,0xb6fd94828UL,0x879b19105UL,0xd476d0a2eUL,0xe6c382707UL,0xdbfa6a996UL,0x1689705e6UL,0x3b22d3f98UL,0x636527f0fUL,0x7d03ba772UL,0xee78d5a4dUL,0xbf165a32aUL,0xc2bf4a8efUL,0x517be89f2UL,0x67718ac90UL,0x6b1a7b255UL,0x726c5bddfUL,0xbb9f23143UL,0x1375abbbbUL,0x296b4de59UL,0x8893b745bUL,0xa9842a848UL,0xb827ebf5cUL,0x3840c894bUL,0x6b7deda11UL,0xbc02958ffUL,0x55ba04b51UL,0x76aa77f3eUL,0x9b43db8f0UL,0x9eeccbeb5UL,0xa295bc47aUL,0xb4e26e153UL,0xe476a2c54UL,0x6be1601cdUL,0x6f8a50792UL,0x97cca4709UL,0xbc66080bbUL,0x1093a056eUL,0x6fbc09b70UL,0xb8eed0ed4UL,0xcee473172UL,0x23120b625UL,0x5da111275UL,0xcf162c550UL,0xec8f68756UL,0xb5db0c4a9UL,0x2b2ad1127UL,0x536d2509eUL,0x9c9fec402UL,0xc1394fdb4UL,0x6c326b53UL,0x5e99af5cbUL,0xaf1e574b9UL,0xe6046cb44UL,0x661d49533UL,0x8e5f9d4aaUL,0xdb3b54dd3UL,0xe63625f22UL,0xe9df164e7UL,0x455e8f524UL,0x5b54317c2UL,0xbe258b389UL,0x54340a016UL,0x5b85eaba0UL,0x1284dcc1aUL,0x24d18e8f3UL,0x4d13e286aUL,0x8b4bd8a7fUL,0x215a5770cUL,0x46572d87aUL,0xc2c719ca4UL,0x4ddac77e2UL,0xd19c94796UL,0x2d1c0d7d3UL,0x9ae8384e9UL,0x9e9128aaeUL,0xca7c6cfeaUL,0x16282675UL,0xad985c97eUL,0x4af8bc195UL,0x9f580da26UL,0xa6a9ee5b0UL,0xbc9f9084eUL,0xd63e230b1UL,0xc4232a7b6UL,0xd66fdc48fUL,0xec657e72dUL,0xa364707a7UL,0xf79208c5aUL,0xde88a1f91UL,0x574f9ddf6UL,0x65f35f50aUL,0x69ce08eadUL,0x490f4ee9eUL,0xc9282b88dUL,0x752c4c7b8UL,0xb364429cdUL,0x8b53a7e34UL,0xbe90ccefaUL,0xb0507dfa2UL,0xd525e90UL,0x5c549eecdUL,0xe3bf5c446UL,0x936c6d936UL,0x9747172d9UL,0xca843c39fUL,0xd57f0d4eeUL,0x2d5595f66UL,0xbfbb2462eUL,0x266727b98UL,0x7ac679429UL,0x26fc53732UL,0x656602d25UL,0x2eb1a6a78UL,0x4850392dbUL,0xe5b098af2UL,0xab534c280UL,0x9ce143f4aUL,0xf4b7cc9c2UL,0x35b8e0d6UL,0x871d5b08aUL,0x5b958930aUL,0xb429a7faUL,0x54d8d431aUL,0x7d1b28291UL,0xa1e645021UL,0xb80da069dUL,0xeef3b5d28UL,0x263d3db6fUL,0x9592d503UL,0x4b9d86499UL,0x6c8df9886UL,0xa3740ef11UL,0xc4963b6dcUL,0x6da94672UL,0x53b64bf9bUL,0xb2deb559dUL,0xf116ab7b2UL,0x8ace1aa04UL,0x8ea8c43a7UL,0x6a4119dd3UL,0x99d54e8d4UL,0xc969833d5UL,0xf554c7911UL,0x3ade9e6b0UL,0x6e1bc3776UL,0x7916948c5UL,0xdbe7ee48cUL,0x79484dca3UL,0xf992e3a70UL,0x884f81b73UL,0xc68777d88UL,0x603ee6fdaUL,0x728b98cb3UL,0xb12701684UL,0xd5f21e414UL,0x58652f15UL,0x2dc8a6e8cUL,0x4767396efUL,0xb8dc549caUL,0xf36b5a61aUL,0xd09ece7dUL,0xdda77175aUL,0x5e9c56d1UL,0x73e7a97c5UL,0xb21f9f9daUL,0xde3c9d2f4UL,0x69504ae32UL,0x77f40c546UL,0xed1217de6UL,0x3a1f88aedUL,0xe623a9a18UL,0xaeec67a8UL,0xbea83aa19UL,0x92eeaf8bbUL,0xa5d08d12eUL,0x819a9bf38UL,0x473d4f6c6UL,0xb192431f5UL,0xa6c92b484UL,0x7046885b5UL,0xb9ab08cf7UL,0x782d94cd9UL,0xf158032faUL,0x77f5e976UL,0x12dda2281UL,0xe72417123UL,0x3056de487UL,0xe3de9931aUL,0xeb3079ea4UL,0xe4420bad6UL,0x439c2e4b6UL,0x47da4a615UL,0xd7cfdda3UL,0x56afc5107UL,0xe978c5f8bUL,0x5aede1266UL,0xaf1b79719UL,0xf8b1b3239UL,0x75e8845dbUL,0xbf1b4b93fUL,0xfd5341b54UL,0xa2373b2d3UL,0x5967e672bUL,0xa2cc66e6dUL,0xb17028581UL,0xb54ad1f24UL,0xe91d22b84UL,0xde85c41f1UL,0x53d588e6fUL,0xe9e407afcUL,0xfc6272bb3UL,0xa8ca0629aUL,0xb86665d04UL,0x5a58fde9UL,0x1855b427eUL,0xaabb42946UL,0xe204ca78dUL,0x32897267bUL,0xa78d7ae2UL,0x96536a598UL,0xbf2aea0a9UL,0xc9bcd56cUL,0x81eb921eaUL,0x2732fe125UL,0x2eb69808dUL,0x61f3bd153UL,0x8ddf0168fUL,0x921d1d7eeUL,0x2bd48ca40UL,0x83ab154b8UL,0x5f436aee4UL,0x93dca0abcUL,0x26d75ad1eUL,0x872a1ba54UL,0x373a9f700UL,0x50d931f63UL,0x12d2f512cUL,0xc6efdbb59UL,0x88e99ed22UL,0x903b7f8acUL,0xa9da1210fUL,0xa2eba3d41UL,0xfe9cd615cUL,0xfb8911731UL,0x1cab3defcUL,0xe6289b02dUL,0x66d6a35b6UL,0xb0096a91aUL,0xc9afcc532UL,0x80aebe5acUL,0xd2f2e9768UL,0xcc67edb56UL,0xa51e37f35UL,0x6ac0eb6c3UL,0x6af2a4aa1UL,0xe76290ecbUL,0x37e738db9UL,0x72d9b11c5UL,0x76e613f46UL,0x4f073278bUL,0xe1eea307UL,0xe9e8f9111UL,0x793ee6f5UL,0x17304e15fUL,0x7a3361104UL,0x731339958UL,0x8daa6a511UL,0xa4037ef6bUL,0x210896f2fUL,0xafc535032UL,0xe3025a0f8UL,0x63e21ba5fUL,0x3ebb5b8c8UL,0xf9c6b06c3UL,0xca95ee37eUL,0x81f852bb4UL,0xd6895d823UL,0x7040cca75UL,0x4d66ec391UL,0x4a216e588UL,0x51d6c18ceUL,0x47711c319UL,0x6ae7f794cUL,0x4abe694d7UL,0xbc96f6f6eUL,0x57aa76cd2UL,0xf948f2648UL,0x31bcd1bc3UL,0x94c7b3f1dUL,0x32eef86acUL,0x668f8ff2eUL,0x6de170ab8UL,0x9341b93e2UL,0x974e1c163UL,0x73182af6dUL,0x5d85fb48bUL,0x78b257bdeUL,0x173d0eb29UL,0x5add785d1UL,0xaf6e83240UL,0xdce79166cUL,0x22716840bUL,0x7b408f1d9UL,0xde43a217eUL,0x36e10fb6eUL,0xea9a83ddfUL,0x4dcf50162UL,0x9aab07a8bUL,0x281364431UL,0x8392dd46eUL,0x945e6aceUL,0x8d07b3a82UL,0xd012f1990UL,0xb7098acc7UL,0xc2fcfa16cUL,0x1e7c731a9UL,0xf16ea68eeUL,0x2fa69cb03UL,0x4cee1f92bUL,0x1e20cfda2UL,0x9e9d1ef4dUL,0xe83358a6dUL,0x59a873d48UL,0xa6842b671UL,0x6885bdbefUL,0x73e4014faUL,0x7b9954840UL,0x548157ffdUL,0x8853a8c5dUL,0xeb8874fe0UL,0xb25d4f257UL,0x36b447da5UL,0x71d87958fUL,0xeedd91553UL,0x4af23612aUL,0x278329eacUL,0x191121b76UL,0x6e691175dUL,0xbd140329UL,0x6ed4532ceUL,0xd5e3c8ff4UL,0xe26c64033UL,0x494a2097bUL,0xf5e36d440UL,0xb7818d202UL,0xa63548c34UL,0x682f0bdfdUL,0x3d3c65c17UL,0x4c4399ae7UL,0x6b18e67ffUL,0x4778211a3UL,0x4089b2dd5UL,0x21edee850UL,0x739cede72UL,0x858dfc744UL,0x4bc5dba6cUL,0x21cbd3bdcUL,0xac83de313UL,0x6135f08daUL,0xd3a3a9f0bUL,0xeb2716099UL,0x40b88e413UL,0x992442a25UL,0xc639de695UL,0x683bcc7c7UL,0x6245fc74fUL,0x543766bd5UL,0x375356569UL,0xfa45b7a88UL,0x9f29b1207UL,0xba245457cUL,0x5b98e5ec9UL,0x204aca6b6UL,0xd52e9605bUL,0x504a40d28UL,0xab6e1695eUL,0xa26481ebbUL,0x9455ec341UL,0x2f05f98e9UL,0xead83365cUL,0xb928d8486UL,0x7b860de0bUL,0x964ef7da2UL,0x2422962b9UL,0x28f5ddfb2UL,0x8c5c63713UL,0x9068c6494UL,0x50aad5744UL,0x93e7cca30UL,0x9825e8b8fUL,0x3a8b4947dUL,0xfa06737b5UL,0xcb9c963e8UL,0x91a2bc332UL,0x284666b59UL,0xceb82a1c8UL,0xa2fe9f06aUL,0x6fa50365UL,0x19aa747faUL,0xa6e117dc2UL,0x2b3810910UL,0xff7e857b2UL,0x48b55c3eUL,0x975456ac2UL,0xad200ed37UL,0xe4d4d86efUL,0xb3ec62491UL,0x8cd465c4eUL,0x8a2be2d94UL,0x5a9f7d648UL,0x59e067a85UL,0xb5c35327eUL,0xc4ca8714eUL,0xe927a04cUL,0xf71eac628UL,0x109354e62UL,0x1037b1a5bUL,0x26c27f893UL,0x3597fa385UL,0xee24b62efUL,0x4b31f921cUL,0x650244e5dUL,0xcfec64526UL,0xcd4bb0a21UL,0x648c56197UL,0xbd95056f8UL,0x983c8c183UL,0xddc662f22UL,0x5631bb980UL,0x1203f56f3UL,0x6a0c37549UL,0xa59baa8a4UL,0x6a23a5068UL,0xad609c354UL,0xf6f6d5e74UL,0x36bc95f79UL,0x424c92c62UL,0x3692abf50UL,0xa4bc460dUL,0x1b4434b89UL,0x4eb31302dUL,0x9a3a891f9UL,0x93af8d5e7UL,0xef60bfa02UL,0x607a378d6UL,0x5a5a7d835UL,0x536c0f467UL,0x11f66a7feUL,0x74c7c43c5UL,0x66eae7c29UL,0xf5a785d2cUL,0xd3948a5c0UL,0xec1094aa4UL,0xfcad61c19UL,0x49ca7108aUL,0x77437f4b6UL,0x553083d4aUL,0x5c26c14cdUL,0x6eb4caceeUL,0xe8aded63cUL,0x132aa4b5cUL,0x57603a19eUL,0xee359dda3UL,0xd0f5ea330UL,0x46b0f0b1fUL,0xd47cbfc81UL,0x3ed1b37b0UL,0x4c8c752d8UL,0xac202044bUL,0x207f16128UL,0x53f5c3981UL,0x70e1a33a2UL,0xed8348baaUL,0x798f94a3eUL,0x8896c890eUL,0x521425a3fUL,0xc8329e9eaUL,0x41f238ba5UL,0x1093d3c81UL,0xcab628e90UL,0xa1b4bf356UL,0x92eee2fceUL,0x59d35b9afUL,0x2176e9f53UL,0x7c9abfb89UL,0xb06d107e9UL,0xe94c318d5UL,0x2f397ae30UL,0xd7e5a1a48UL,0x98c4abc47UL,0x574737c29UL,0xd7f5401b2UL,0x852b87bc6UL,0x1180fa957UL,0x501c63328UL,0xfd28c0d13UL,0xf764aa079UL,0xc8a6f8c5aUL,0x975b10240UL,0x6bd33e4c0UL,0xa1113e39cUL,0xabcfa8fb8UL,0x149ea1facUL,0xf6443556fUL,0x959da07e7UL,0x4721a2ac4UL,0x30bde0f15UL,0xc7c4fdef8UL,0xb7feb4465UL,0x56675073cUL,0x96f3f57b9UL,0x876f0386eUL,0x393f0a7e8UL,0x32b40ebd6UL,0x10a11346aUL,0xfb81f48aeUL,0xa892877eUL,0x86f636e08UL,0x4fbc4d72bUL,0x561d5f314UL,0xca18e2835UL,0x7e6f519f5UL,0x8b94e7983UL,0x619adfaf3UL,0x4c9ddbbabUL,0xcb6a461f2UL,0xf6e22456UL,0x858c9b401UL,0x92dc1b3b8UL,0x3a783615bUL,0xc74b66f67UL,0xea90891bcUL,0x31a829e4bUL,0x7bd38f505UL,0xb4476ea80UL,0x1af371feaUL,0x84894ff56UL,0x537584dfUL,0xd4ebdd1d0UL,0xb43cc192bUL,0x76700d287UL,0xaaad9fa58UL,0xfa511710fUL,0x6e54699e5UL,0x6d73391aeUL,0x3c2f1fb49UL,0x4fbd96a75UL,0x252e6304bUL,0xe72826214UL,0x8fe6c9336UL,0x326397743UL,0xe03bc524UL,0xdedac9594UL,0x4ff19096UL,0x409b4cdbbUL,0xb15921888UL,0xa259bcd6dUL,0x43c67f305UL,0x1dc65dcecUL,0x1730b4f85UL,0xf8a48f16aUL,0x57a30e743UL,0x5afd9839UL,0x682d5f3aeUL,0xb694337eUL,0x758c7dacfUL,0x18fa1ae7dUL,0x5ba9b5984UL,0x32e1d45ddUL,0x672f05518UL,0x382ffc5b1UL,0x8f0b08f33UL,0xb4a4d9ff0UL,0x53ba0f980UL,0x2ac0751fbUL,0xab005de73UL,0x61ab7be9bUL,0x63078c9adUL,0x27659d148UL,0xc653c684fUL,0xecee05017UL,0x24378ce5eUL,0xd0f7e5bacUL,0x8b4bf4199UL,0xb7aa495fbUL,0xf3d6b78a5UL,0x98bab1024UL,0x6b4971fadUL,0x723d6c89UL,0xa17071a75UL,0xd55a301f4UL,0x988de925bUL,0x7ca276f45UL,0x321b6e484UL,0x316149ed6UL,0xb8dba5bb9UL,0xaad4df3f4UL,0x178e204f5UL,0x95cd2e357UL,0x73fb8a733UL,0x84ca10c86UL,0x89498492dUL,0x5b9bdf383UL,0x66c58bb10UL,0xf153ac21eUL,0x7ca5d3b04UL,0x637a521c7UL,0xb5ed589c1UL,0xd0a3c644eUL,0x3d5d0754fUL,0xc6b901174UL,0x3e96fd3edUL,0x79c2fdf8cUL,0x6594ae371UL,0x3504fd77aUL,0x32b81dcc7UL,0x57b4f3e35UL,0x4c9808072UL,0xa06c10993UL,0xb059666afUL,0x72b69c034UL,0xe33ae836eUL,0xad6cadf0dUL,0x19573ace1UL,0xd562fd1e7UL,0x847804d9dUL,0x9e32f6734UL,0x2355c580fUL,0x2f177b8d6UL,0xd689ac650UL,0x71b70abcUL,0x254915730UL,0xc5934f949UL,0x134d59d09UL,0x2c731fb06UL,0xd90c6c5cbUL,0xcc3f9bca4UL,0xbf078980cUL,0x80838e95aUL,0x5ccd6f054UL,0x378bae56UL,0x8d1ddb978UL,0x93b875cf4UL,0xe2ce90bc6UL,0xec291b91bUL,0xd0a11bdc1UL,0x751be7244UL,0x579fcd29eUL,0xf76e5ccb1UL,0xeb33da184UL,0xc0ac75b0fUL,0x15454fb33UL,0x8b92a411cUL,0x7da34b476UL,0x4f413d45eUL,0x10ec1dbeaUL,0x650739b93UL,0x315e08a31UL,0xd4fd5f1bdUL,0xb5058a126UL,0x20d5cb63bUL,0xc1598dfe7UL,0xa0a2a338dUL,0xe29af7686UL,0x83fd0cac9UL,0x14a8094d8UL,0x816e0afa3UL,0x499ef5d2cUL,0xbddbd0d95UL,0xa30839ca9UL,0x4fd33fb4cUL,0xef63557b7UL,0x535f06ab2UL,0xd47d352eUL,0xa371e6dc4UL,0x8ac914b17UL,0x6ba9d69d7UL,0x96da89aaUL,0x65bbd5d14UL,0xd41d2c5c4UL,0x52a283583UL,0x4c1f56d26UL,0x86cd9984aUL,0x26cbcedc6UL,0x1aa0eaa03UL,0xb95d5ad2cUL,0xe2eb56b20UL,0xff49d9d5cUL,0xcce338378UL,0x330e9fac7UL,0xe2f53974aUL,0x668d1c6d5UL,0xeca0ba751UL,0x8d48ab5e6UL,0xd205e18cdUL,0x1c391633cUL,0xef5d02e5fUL,0xd12bb5f20UL,0x323215199UL,0x88f5b3ffcUL,0x931445f29UL,0xb893cb727UL,0x32851ecc0UL,0x80b44d81bUL,0x5aa48da98UL,0x46d1e1284UL,0x4c837ba14UL,0xeb22c26deUL,0xe51e9d246UL,0x8d03deee6UL,0x5af8e0909UL,0xbde9773a4UL,0xbf611cabfUL,0xd24ac96e7UL,0x9fe919318UL,0x50d0206a6UL,0xb43b9741cUL,0xba48d4fb3UL,0x6bccd7290UL,0x8bc6bfb9cUL,0xe5a036c9fUL,0xa80a2cfeeUL,0xc193655a7UL,0x7c8e5170dUL,0x6141edbbbUL,0x4d6b990dcUL,0xcc49b5702UL,0x2343fef58UL,0xd50cb593cUL,0x4248a60cdUL,0x901cfbd4cUL,0x64a4c8736UL,0x1b2dcbaeaUL,0xd691e5f4cUL,0xdf352a493UL,0x1991ac7daUL,0x4c4879f45UL,0x9b34aadeeUL,0x52bb3db0dUL,0x7b9a8c9d3UL,0xd7ce6e47eUL,0xec0b922d8UL,0x8079cab6bUL,0xabadc8899UL,0xf57b93b7UL,0x5c4ef219UL,0xd7a438d49UL,0xf55ecca97UL,0xd07899f1dUL,0x260947d6cUL,0xffbd21ab6UL,0xd04ff923eUL,0x964b72033UL,0x31ac3fd7eUL,0xd2c52e2c4UL,0x799a640efUL,0x98dd061edUL,0x5cb2ab7b8UL,0x72f3881c8UL,0xe65ed1164UL,0x34fa0bd5bUL,0x64f9823cdUL,0x3797e1ac0UL,0x2fb8a4751UL,0x6f347342eUL,0x22dd7ea0aUL,0xb19b65e57UL,0x44fe83e8aUL,0x7732732eUL,0x64de20ed7UL,0x6c9ea834UL,0x8ce066650UL,0xc2a685ff0UL,0x64f19b01fUL,0x491ab8a88UL,0x41212fe5aUL,0x6f9916f3bUL,0x694f72e71UL,0xad7a5b35eUL,0xf62795292UL,0xc8cdc3d3aUL,0xfbc6b3518UL,0x67b631901UL,0x5b5ba79d5UL,0xf4fadebddUL,0xac7c802e7UL,0x385712d9dUL,0x64bd375b4UL,0xc9a11df70UL,0x88355bf31UL,0x606ffbb0aUL,0xbda93c2d5UL,0x7c5f94f0aUL,0x76fe26501UL,0x5d8b9153cUL,0x886bbb218UL,0xacee2fecaUL,0x2ad19a925UL,0x83b97855cUL,0xd36608312UL,0x8ac60dbc7UL,0x885c8f58UL,0x8abbdf891UL,0xea1602271UL,0xad654fee1UL,0x6c461195eUL,0x5eeb1a327UL,0x18d743962UL,0x1fc7c55a5UL,0xaba749670UL,0x9c9a59c60UL,0x6e5bafc06UL,0x96977db12UL,0xa97b6ebfaUL,0x63d2d9da6UL,0xfab00cd60UL,0xd7bdf4632UL,0xf83878d59UL,0xb1c2c462eUL,0x14e5144a7UL,0xf4a909b28UL,0xe979a185bUL,0x908090a64UL,0x99eccd798UL,0x348780a96UL,0xfdc7ad169UL,0xa600c2e5bUL,0xb0968cd98UL,0x1a45ec098UL,0x99118c1b4UL,0x8afa5cd5aUL,0x1db7e655eUL,0x9f637e452UL,0x9568504e3UL,0x45b2a662UL,0xf2a1455a2UL,0x6c1ca9e75UL,0x30a4a4639UL,0xc6c2c1a30UL,0x87500b452UL,0x5e338bb2eUL,0xd9dd11dffUL,0x8c4b5d012UL,0x8191194e0UL,0xdd11db867UL,0xc67c151ceUL,0x5cb1a00e4UL,0x98b7a1c6UL,0x369f35cd4UL,0xca2190bdbUL,0x6e14bb3b9UL,0x8d5692f8cUL,0xca4b2f4f8UL,0x787f06877UL,0x8acbb8550UL,0x535f4b56aUL,0xf4caf7ecbUL,0xd4615b258UL,0x347ca7070UL,0x3c798c85dUL,0x460506465UL,0x870d0a5dcUL,0x6510b2464UL,0xd1dba5544UL,0xd57789a33UL,0xe2417c5baUL,0xb5ff8628cUL,0xa3bb22787UL,0xa16b64f34UL,0x421e81d3dUL,0x35b4596a7UL,0x8d7a2dd7eUL,0x50b2d83faUL,0x9ea87e7c2UL,0xd5055e752UL,0xf96aa9da5UL,0xb096e2a07UL,0x49970b44bUL,0x867fb1518UL,0x5d0f5dba2UL,0x1b191d11eUL,0x8e839bb8fUL,0x1cd4aca15UL,0x971ec5615UL,0x7d72a7ebdUL,0x8b1253bfbUL,0xe11de1d25UL,0xa7566839UL,0xf4f3542e0UL,0x1ea791e32UL,0x32a84f759UL,0x646f1844eUL,0x42af26809UL,0x1f4b464ffUL,0xda684d2d9UL,0xd854f5fb9UL,0x4d4d3e91aUL,0x5af3ef4e2UL,0x8a1ef5ce7UL,0x2354febf3UL,0xb3c5a8944UL,0x98b62a144UL,0x9bdba0b4eUL,0x4aa99b42UL,0x8099ea151UL,0x2185463a3UL,0xb0a1ae997UL,0xe628d5770UL,0xb40b5ac89UL,0x27213b17dUL,0x4d21db5b5UL,0x10d0748f7UL,0x2276c7876UL,0xb98bee56dUL,0xbd1ca6ae8UL,0x824ab48faUL,0xc6f35ae62UL,0x3547a563cUL,0xf1fc0d824UL,0x58f55ed75UL,0xaa9d0de01UL,0x4719dde60UL,0xd5386b3ddUL,0x4d8d9f666UL,0xaee36013bUL,0xba4ee322fUL,0x898d2db4eUL,0x9fe364808UL,0xbb13e8045UL,0xbe346d43aUL,0xb4c9f886fUL,0xc9a6f53b8UL,0xed5a7b6fUL,0x2a1fac740UL,0xb8c134a59UL,0xb1f773993UL,0xc4d9d0025UL,0xca905bdcaUL,0x3150a39a7UL,0xe8329fad5UL,0xbd4f98059UL,0x3bc5cf6cdUL,0xc982fdd03UL,0xa372de28UL,0x73fe2e35aUL,0xb9f684ecUL,0xc543ff680UL,0x1bcf5f09aUL,0x51b2a8099UL,0xee53277c2UL,0xb3835a6cUL,0xaed6765c1UL,0x92cfd64c8UL,0xd20c60ed2UL,0x59dbd9f51UL,0xb6acb694bUL,0x427dcd5fdUL,0x646336a75UL,0x8008dea4dUL,0xaf2bdc7cUL,0xb8a46478aUL,0xb02c535b6UL,0xc645d8631UL,0x44b4af3dUL,0xc9edfe6cbUL,0x32ac8ea2aUL,0x79266a23fUL,0xc2d902e93UL,0x6ae5cfbdbUL,0x2c66c633eUL,0xeb7a8a4e3UL,0xcb17281cfUL,0x7ca378680UL,0x7ac81509dUL,0xa59a05073UL,0xc9cb9f18dUL,0xb78100d29UL,0xfab49420aUL,0xd0a4e69c4UL,0xd6c33f722UL,0x68d21bff8UL,0x1fdad8ca3UL,0x2884d6968UL,0xb091ff264UL,0xeb5fb236fUL,0xa3d2a1839UL,0x527db0bc8UL,0x2dc68cd9fUL,0xe3f4ea98aUL,0xa629fe44fUL,0xb73bd7d66UL,0x2abfd7b6bUL,0x1b4056054UL,0xd6efaac28UL,0xd13cc950UL,0xef84ead94UL,0x5b6ee0d50UL,0xf4bec692UL,0xde1b98881UL,0x55ccccd31UL,0x86d9b84dUL,0x5ab736e3dUL,0x167d2f005UL,0x118ed1522UL,0x38bbdc903UL,0x39cd31ac2UL,0x31091bc51UL,0xd66a87d3fUL,0xafdade6d3UL,0x2bd1fe097UL,0x5cf545dd2UL,0x5e0af578eUL,0x6fe6dd4c9UL,0x862bc8fcaUL,0xcbce0b4c6UL,0x8b7fa8ddUL,0x3d108ae9fUL,0xfed2d914aUL,0xbab304bd8UL,0xdebe74f8dUL,0x1e857e3dcUL,0x570340581UL,0x114bbf4f5UL,0xa3cfc0566UL,0x4026cd686UL,0x266fb76cdUL,0xb715773bbUL,0x2fd2785fdUL,0x481b34cadUL,0x11c58d2baUL,0x3a5186f4dUL,0xda55ab71cUL,0xac887db92UL,0x9bd6d5592UL,0x45857d12aUL,0x8c862f0b9UL,0x870c88666UL,0x4a4f4901fUL,0x774a993d0UL,0xc9f16c81dUL,0xeb415e9efUL,0x307aa6302UL,0xa246f21eeUL,0x1a4f8a9c2UL,0xcf09f9b4UL,0xdb30dbb49UL,0x3581be36fUL,0x6919a4318UL,0x8ee677afdUL,0x5944b9d59UL,0x8d5fe61aaUL,0x77c174b1dUL,0x5cff8fa10UL,0xc1ce82f48UL,0x7fbb18e65UL,0xb6737103UL,0xe2d30a9b6UL,0x6481ff469UL,0x5834b4d26UL,0x3bba517d5UL,0xeee6e8080UL,0x5fe4fea5eUL,0xe84e94c8cUL,0xba2ad0a2aUL,0xa7f2aead0UL,0x63cecb46dUL,0x8943d7229UL,0x1d3878b2bUL,0xf2b4efe94UL,0xd9af1949dUL,0xbb5824d39UL,0xb8d8f5090UL,0xed5e19d08UL,0x60287437eUL,0x8fe6ae5c2UL,0x6c85ac058UL,0xb906be1b8UL,0xf9d423f65UL,0x6efed81d6UL,0x781b67fa2UL,0xe1dd437acUL,0x7a9201a8cUL,0xfb444c819UL,0xce75af959UL,0x86df6e72bUL,0x756695aa7UL,0xb7b2bddf2UL,0xf19a1b99eUL,0x9a5790e90UL,0x1d3b3eac0UL,0xa5c5d9d2bUL,0x152850218UL,0x25c4ba6eUL,0xd4a5f4bebUL,0x709cec10eUL,0x94ddbdb6cUL,0x9d1218277UL,0x6190ca34aUL,0x468ed6a3fUL,0x801bda52eUL,0x261b3f1a9UL,0xb3494d9bUL,0x583e2d7e5UL,0x9407a80f2UL,0x58e902456UL,0x9108c2273UL,0x59778ff8cUL,0xd6ce05028UL,0x286adc62UL,0x7ed3060dcUL,0x57b7e03edUL,0x3e3dce5c1UL,0x1bebc2295UL,0x14a17c9aUL,0xc7d90fbdaUL,0x8158ae35aUL,0x69d70a335UL,0xd3ef97931UL,0x5793efb7aUL,0xe6989ef43UL,0xcd15f0116UL,0xf9dbc6e25UL,0xda4a91117UL,0x54d0917aUL,0x60f2c3f15UL,0x7393b0a66UL,0x6630ed79bUL,0xed8589c60UL,0x7db37ab26UL,0xc4631e80aUL,0x1badaf501UL,0x9bdef764dUL,0xdd0949b4bUL,0x86f116771UL,0xacd7ea109UL,0x7cc9d2f6bUL,0x3f5598822UL,0x4ba5a8d0cUL,0x66e7f9c42UL,0x33127fb36UL,0xc85ff976UL,0x9dbb32ddfUL,0x3d06c7a56UL,0xac07601ddUL,0x5fda3d7e9UL,0x40a47aef0UL,0x139928cd0UL,0x183ab75ebUL,0x9dd6d1f4bUL,0x954afec44UL,0x29953fe22UL,0xf947e49b1UL,0xa74266cb0UL,0x3bbb7fdabUL,0x8a72b63d1UL,0x8763e2fbbUL,0x8c9b4f9a2UL,0xa35f5a861UL,0x99e54752cUL,0x2fdb8e16fUL,0x2d083ed68UL,0xa05d36c5eUL,0x5460842feUL,0x173ae0ee6UL,0x38b3c62e5UL,0x476c1ae99UL,0x9a8cb898aUL,0x19d4032acUL,0xa9c01d80bUL,0xca7d5e4deUL,0x295d53115UL,0xb26740e51UL,0xbf21b0988UL,0x167391c15UL,0xd10af35c6UL,0xd94750799UL,0xcb986d117UL,0x1dddf588UL,0x71ed85f46UL,0xa5437d58fUL,0x4029d1e25UL,0xc580ec972UL,0x6847df8baUL,0xe294d997bUL,0xe2e8b10eeUL,0x1593103ddUL,0x222103857UL,0x1e035591dUL,0xb5c9ef2e9UL,0x9f815ec3eUL,0xd1da2a021UL,0x54f171191UL,0xe51f4a05eUL,0xc15e7d603UL,0xba7f16b87UL,0x80b7a83e1UL,0x720e2b18dUL,0x5ec0c069dUL,0xa4f9f689cUL,0x5871cafdaUL,0xc913140a2UL,0x7a8f2efd1UL,0x77064952cUL,0x4ea2d857fUL,0x484523555UL,0x54971a9e3UL,0xeb0694eb2UL,0xb513c8e63UL,0x5c910db58UL,0xca87a4dd7UL,0xb8ca63158UL,0xb4b09431dUL,0x3dc9d50b7UL,0x7d57f02acUL,0x5c595b1b2UL,0x9e0caf698UL,0x136b48555UL,0x687dbcc2bUL,0x54bae2294UL,0x6899bbd7bUL,0x8108f46deUL,0x1dbe8cf08UL,0xa02e1ae1dUL,0xf5f26d59UL,0x805cf202bUL,0xafede5687UL,0x1583d5b30UL,0xda9ed0620UL,0xcf1237338UL,0x3a5a77bc4UL,0xa17ffa0c6UL,0x29de4c387UL,0x7825d431UL,0x2d7b9b38UL,0x8ed0f26aaUL,0x56e54e30dUL,0x9620ab0e7UL,0xc7e3ea94cUL,0xd288a41e2UL,0xf68884f1eUL,0x5ee02df09UL,0xc02dbf645UL,0xeac4c2424UL,0xcab2d51e1UL,0x37439577UL,0x5618ada43UL,0x2683b5859UL,0x8a607c1ceUL,0x795fd9198UL,0xb3edb11b8UL,0x846939c5cUL,0x8b1f6fa23UL,0xb1a2f2bfeUL,0xb63a07ad7UL,0x5f8ea7b00UL,0x4ee9c6d0cUL,0x990f2889bUL,0xb7f7251d0UL,0xac3291369UL,0x9d8f36a7bUL,0xd57342897UL,0xefca98365UL,0xdacc69f0eUL,0x3a70e4b3cUL,0x1e95c34c2UL,0x4caab6c06UL,0x7231f6ee1UL,0x37909aa04UL,0x48c9a9ccUL,0x59cd081bcUL,0x4dd78c2e4UL,0x4979da10fUL,0x4749d0c5UL,0xa17a4283bUL,0xde7e1d52dUL,0xe47cedf1UL,0x4fa48cbffUL,0x545a932a0UL,0x6c2bd9eb8UL,0xdd9bd3b8cUL,0x43332c1baUL,0x501fa761dUL,0x7ec40adbbUL,0x4049f2b33UL,0xcde28f57bUL,0xf68c804b9UL,0x8f50fbd3eUL,0x54e1bc344UL,0x36b26e3a2UL,0x2e5ac9b1UL,0x10837858dUL,0x6ccac9e0bUL,0x625ba8a52UL,0xac4c8b45cUL,0x868678237UL,0x4187235feUL,0xbd62663ceUL,0xea832dfb2UL,0xd5a72f0a7UL,0x659c855eUL,0xbea7f5e48UL,0xff9566715UL,0x1bd06d99aUL,0x9666c578cUL,0xc6527d3ecUL,0xb541f3c61UL,0x678a9ad70UL,0x36eaadfa3UL,0xaf74b01deUL,0x54cc3cdc3UL,0xd2e587ce6UL,0x8694b9349UL,0xd309898feUL,0x5c3250e09UL,0x84dcac28eUL,0xf72add2dfUL,0x1901681a3UL,0x9e6a8fd4UL,0x12f614cd1UL,0x6d7801ac4UL,0x14cf1ca54UL,0x12a7eb608UL,0x5e7a3bf62UL,0xba5056a2UL,0x5bee44c9bUL,0x819d7dc86UL,0x62adc8fdUL,0xbd3155d41UL,0xcd8c6b38aUL,0xe320fd50eUL,0xe189d6655UL,0x6863c2831UL,0xd2b9058fUL,0x23bfad8faUL,0x199bd1216UL,0x56138afd7UL,0xface83a93UL,0x9554da725UL,0x9b614dd91UL,0x98acbca3fUL,0xd5f0d5f21UL,0xeb59039e1UL,0x51d1ec82aUL,0xa366ef3baUL,0x1ad0e01f0UL,0x7f038ad0bUL,0x3ee055321UL,0x3bf2dcbb7UL,0x210e9856cUL,0xe4fea8231UL,0xb89444937UL,0x58852cc34UL,0x1ee29eea9UL,0xb919c79f2UL,0xddc44d3adUL,0xddcbd4777UL,0x3c3982ba1UL,0xdc8ebc45dUL,0x8b97712b1UL,0x9702ea21eUL,0x1f457e726UL,0x27c6f6e26UL,0xa9797770UL,0xd7615f53bUL,0x74f1cb6e1UL,0xa32e4d7dcUL,0x2e89afd1dUL,0xb03704d5UL,0xcca58aab0UL,0x1e5749225UL,0x6e63a36baUL,0x562992099UL,0x64701b950UL,0xf94ed6196UL,0xb3441b5f1UL,0xc64fac247UL,0xd72ebd98bUL,0xfa1985b23UL,0x2df788358UL,0x88838b488UL,0x6091032b4UL,0x25ff2d736UL,0xdce63d3d5UL,0xbb5970414UL,0x44d8b5ffeUL,0xe1a5666d8UL,0xe34129125UL,0xe23854b1UL,0x1b2a6dbeUL,0xd11507bcdUL,0x844531e6bUL,0xd864a8611UL,0xe2a5a7700UL,0x2d178962aUL,0x156b07f01UL,0x48b59fec3UL,0x3d3d9d79cUL,0x1846fb339UL,0xddf1d03caUL,0x998abaf9UL,0xc9d76190bUL,0x67354a1a8UL,0xcc89e2b09UL,0x353356834UL,0x7ad97470eUL,0xf4d560524UL,0x534b7804eUL,0x14290c632UL,0xb67d39d60UL,0x35b166febUL,0x88e6fb681UL,0xa0f82ae1aUL,0x8460ce52UL,0x8b06a9012UL,0xdaf1299dcUL,0x629ab696cUL,0x3113b448aUL,0xdb5ca215UL,0x3e00b1e2dUL,0x85a87f5abUL,0xb3995ff20UL,0x85661554dUL,0xe709c5384UL,0x111ca99bUL,0x49e614279UL,0xf14677ec4UL,0x8f6439bfbUL,0x749faa461UL,0x1c4f9189aUL,0xe8e9015caUL,0xf6e68d510UL,0xb3819319fUL,0xda9f7119fUL,0x7787f40f8UL,0xbc57f5716UL,0x60ff2897eUL,0xb3a28a934UL,0x10b34c97cUL,0xc14f53aedUL,0xd3c4eaf5dUL,0xb3148d39eUL,0x7874ea02UL,0xf86692b4aUL,0x5b03a0e8dUL,0xce6db8cc6UL,0x8233d5908UL,0xf163e3c06UL,0xdff854cceUL,0x26706f1bcUL,0x94c358653UL,0x7384c9821UL,0xe51b8e5d5UL,0xeda32963bUL,0xa073f392fUL,0xc3ccfa213UL,0x34adf5216UL,0xcb8da286bUL,0x3b5fbbf08UL,0x12812d1f8UL,0xc97c54c39UL,0xe1c3e36b9UL,0xabb8dc0edUL,0x19dcbbf6UL,0x25b0d7c4dUL,0x45e6b5ceUL,0x17dc086caUL,0xc3f425e6bUL,0x6fdee14f8UL,0x39155e6b4UL,0xa191ec15UL,0x398fcd7f4UL,0xa6e2b0594UL,0xfe5678d82UL,0xe317eba1fUL,0x2c4f10ca1UL,0xae239c19eUL,0x18e663ed2UL,0x4a040b7e7UL,0xbbca0849cUL,0xce05b3a74UL,0x7cee982fdUL,0x78ee54fa7UL,0x7b47bb0bdUL,0x7e8f19216UL,0xd67d91cedUL,0xef5effe94UL,0xec1d1938dUL,0x4c05ef70eUL,0x324442d9UL,0xfb0183bb4UL,0xfb7a0bd50UL,0x89aa17d87UL,0xe4e6aed89UL,0xdbecf68b4UL,0x683770de4UL,0xb9f41a136UL,0xc7614caceUL,0x89c298386UL,0x959cf09deUL,0xab30b19e3UL,0xdb2e4b614UL,0x26d30d39bUL,0x6ccefe452UL,0x587c5035cUL,0xea73bbbe0UL,0xdd9d91a11UL,0xdd8c5e851UL,0xe8b4aa077UL,0x8ccf8faddUL,0x47ddd3c0bUL,0x635a92f19UL,0xf0edfd1a3UL,0x1f760bf5eUL,0xa83feb68aUL,0x4f74da9ddUL,0x52f759252UL,0x98bee689eUL,0xc5fc8c3d5UL,0x8373d1286UL,0xf5f1cdabdUL,0xada68d3e5UL,0x3bbb9eb5eUL,0x50cde8478UL,0xf01f956e0UL,0xa922f2842UL,0x233a8b25aUL,0x71118b754UL,0xb7f874552UL,0x44d757121UL,0xb873b14ccUL,0x5bcc1db5cUL,0xbf9b895ceUL,0x5e65bb620UL,0xbbd1ed35cUL,0x358e79973UL,0x62aa5a4a5UL,0x81715fc0fUL,0x8df03a76eUL,0x376b7c6c7UL,0xa07a49f2eUL,0x45e159b63UL,0xdae5706b0UL,0xb5e52c7ccUL,0x206935e8eUL,0x39f0c5119UL,0x3cd58c574UL,0x571986d35UL,0xad66da60fUL,0x2b1a6315UL,0xd0131b533UL,0x741a195c5UL,0xb8663437UL,0x1cde52798UL,0x6b8e658b1UL,0xb43c0d44dUL,0x45481d697UL,0x29de93df5UL,0x10549b874UL,0xc056b5828UL,0x3fa830adUL,0x9496d14faUL,0xf540592a0UL,0xf31c8b855UL,0x64f2ba36bUL,0xfe7c6e4f5UL,0x5e42a78b0UL,0x9c2b8b096UL,0xdcb4a6e71UL,0xd63b0e7edUL,0xde1bcbcdaUL,0x68e7161f2UL,0x3e5ddf88dUL,0x419a37501UL,0xfad63e7abUL,0xc6e81b4baUL,0x8329315d3UL,0xc88d267e6UL,0x73a0ac25fUL,0xe7b75690fUL,0xdcbb95be2UL,0x7a1d2a059UL,0xd8fac361eUL,0x6312ff5c9UL,0xd2cf50d54UL,0x8c65fd00fUL,0xaa1636532UL,0x870c7285dUL,0x1894f0b84UL,0x4260cc5c3UL,0xe9997b9ecUL,0x87a052144UL,0x8706babf6UL,0xbd5f62ad3UL,0x1a7895439UL,0xf7e294bbcUL,0xbcc27ca26UL,0x3186a63d4UL,0x7f3ede4a4UL,0xb64e32468UL,0x71f250d53UL,0x7c6513783UL,0xb1778714aUL,0x94bf2c57fUL,0x64a9f893aUL,0x1305be654UL,0x493e0c9f6UL,0x5ba6fed8UL,0xc4a0c7a06UL,0xcc2ec0ddUL,0xd9a6769afUL,0x724c78a49UL,0xc85c981a4UL,0x12553c4cdUL,0x83cb892b1UL,0xbc324ccc7UL,0xef43f6c1dUL,0x2d6748bb7UL,0x5efdce2d7UL,0x94af64f28UL,0xf9d58feb3UL,0xcf547ac63UL,0xceb309febUL,0x30beba8caUL,0x8ab2e486aUL,0x4a95d58adUL,0x25ce07c46UL,0x712b93fd7UL,0x7f46acc81UL,0x64049d4beUL,0x65303aa09UL,0xf3aad21b3UL,0x2903a6cd0UL,0x5a0e0467dUL,0x3c4fa64e4UL,0x5c6655126UL,0xb40a2a67fUL,0xb0c22c6e5UL,0x1507e039bUL,0xb282b16b8UL,0xc0e14a3d3UL,0x93d381427UL,0x6bb55bb87UL,0xb675af72fUL,0xfceb4f95eUL,0x66af6ebbdUL,0x20a44d1f2UL,0x6bc873916UL,0xb8947bee8UL,0x4b6bed8a6UL,0x7012f7867UL,0x7eda3c150UL,0xab3ef1b8eUL,0x6d71466eeUL,0x408c4e225UL,0xe117838b1UL,0xaef3a075UL,0x5a0779d4fUL,0x70a3b1d69UL,0x26ccd31fdUL,0xed64dd1b2UL,0x981d4f60cUL,0x6a6e4fb61UL,0x52f15fc93UL,0x32b3a64dUL,0xecb17d667UL,0xa983fb935UL,0x37d23c88dUL,0xb8590fbcbUL,0xec2f1a277UL,0x90d3053e6UL,0xa36fa8ccdUL,0x44bd08eccUL,0x61dd197d9UL,0xa307cfd82UL,0x1d09c2de4UL,0x5f6d74368UL,0x1327d1b2dUL,0x594cc36b9UL,0xfea1cba7cUL,0x50c31262dUL,0xd99b1a6baUL,0x1bf789cd2UL,0xe2f6f66f9UL,0x13d5edfc6UL,0xbc3a9ab0cUL,0x1da5b2734UL,0x25ef4f2deUL,0xdcb55a50aUL,0x9c6dbc6acUL,0x89a838853UL,0x168f099eeUL,0xd51601760UL,0x89f324f1aUL,0x2cb1ec1eaUL,0x6306de366UL,0x12a2f11eUL,0xb5c0bf797UL,0x5c5f02be4UL,0x5019f54beUL,0x6ae4a096aUL,0x4bce78778UL,0x94b65b97fUL,0xd3f6e7bd2UL,0x1fbd2a84cUL,0x6d0127ab1UL,0x3e82799aaUL,0x4c1264dfeUL,0xcf69c9360UL,0x4b43e5342UL,0x35d1f0372UL,0xd78c18eb4UL,0x262574101UL,0xc2c5c7335UL,0xbad04051aUL,0x1c481f94eUL,0x3285aa0deUL,0x8973e1f69UL,0x5d238c694UL,0x7b71847b9UL,0x242f5675cUL,0xcc5751c2dUL,0xe09bc620bUL,0xe4e904ddUL,0x7ca4f1a7UL,0x2ac79ae43UL,0xe213d4250UL,0xd4137c2b5UL,0xddfce11bcUL,0xd1d658566UL,0x213f5b1bbUL,0xcd35be0a8UL,0xcc67d7f91UL,0x509bde098UL,0x74d3d8f46UL,0x51309c970UL,0x53e2bdf66UL,0xa5dd3fed3UL,0xa4e69b212UL,0xb1d39936dUL,0x6b6c8926bUL,0x46540a7b0UL,0x2eebc599fUL,0x2e54a283eUL,0xf9a328a9cUL,0x7ea9cfc53UL,0x5cffa2bdbUL,0x464d16f8eUL,0xeb09444bcUL,0x3f341b259UL,0x4d112b108UL,0x70cb94242UL,0x974ed4ffdUL,0x1084da291UL,0x85673ca39UL,0xd4d74766fUL,0x64a68e1deUL,0xe35630caeUL,0x2073229dbUL,0x63d3a3902UL,0x31598ee06UL,0x808d61126UL,0x29957984UL,0xd4f5f2649UL,0x9ec8a706bUL,0x349981760UL,0xc93ab23a6UL,0x2c7aa80daUL,0x866f102baUL,0xb15cff7bcUL,0x66a13a4caUL,0x54a755048UL,0xd13fdb8d9UL,0x16ad5edf3UL,0xe043bb154UL,0xcc8755671UL,0xcf9b2bfd5UL,0x3608890b4UL,0x330fef315UL,0xe3299ca65UL,0xb60765e1UL,0xe9bb17dcUL,0x95f474d8bUL,0xe721d3d00UL,0xd4679e565UL,0xc80da6113UL,0x98deeff30UL,0xc293bb871UL,0xe79132f48UL,0xb152dafbbUL,0x55f6a4386UL,0xa1b8a4044UL,0x4f4187b05UL,0xb17c2ed3UL,0x95d75ba04UL,0xbbf12e96dUL,0x6abd1a52fUL,0xf300bc991UL,0xf0a7385d4UL,0x52964f82aUL,0xa9962925fUL,0x613b2eef1UL,0x5fd2c92a8UL,0x9ebecd05UL,0x36002b87aUL,0x902c79eefUL,0x394e63c7eUL,0x133285064UL,0xf7cfe2d4bUL,0x4f068522cUL,0x96fea1a0fUL,0xc5a927b13UL,0xe9a2c1994UL,0x5c53b3803UL,0xf636b6188UL,0x7c656e3UL,0x26af1fc5fUL,0xec2f40b78UL,0xfaa1921e5UL,0x6137a8b30UL,0x28674f7bUL,0x3de184e35UL,0xeeef093e6UL,0xd44b3dae0UL,0xbb7ab7d93UL,0x2ae18c956UL,0xcde492bd6UL,0x1cee0216eUL,0xf1e5830adUL,0x76f6c3299UL,0xdea24af84UL,0x277e75586UL,0xa17318024UL,0x5c4739486UL,0x5e3de4725UL,0x6f67c9f6dUL,0x25f42791dUL,0x3c54d15b3UL,0xef98d9c32UL,0x42f64819dUL,0x16d5fd070UL,0x63cb98d4fUL,0x45a3ad27cUL,0x1b496b0acUL,0xaa471c42dUL,0x599346a2UL,0xdc8d1c2dUL,0x7498928c1UL,0xea06e90ffUL,0xb683baa32UL,0xf93014e16UL,0x20575d56eUL,0x794325589UL,0x1533e9935UL,0x86b8bcb70UL,0xce11faf5dUL,0x36c0bd318UL,0xe5e8c1167UL,0xe1831ba64UL,0xe088dbfa4UL,0x984479674UL,0xafef02b29UL,0x48518c716UL,0x4301564ceUL,0x21cc88710UL,0xd5c995278UL,0xd8367de1cUL,0x4a51125e8UL,0x113e1c226UL,0xef141e076UL,0x44097011dUL,0x4ca9d707cUL,0x40d8831f1UL,0xbd9c3b1d8UL,0x978364177UL,0x10f7606a9UL,0x46a64270aUL,0x42df1b22bUL,0xe906cf2a0UL,0x997da6fa5UL,0xa5722c26fUL,0xb14f58aaaUL,0xafc167ad8UL,0x37be56e60UL,0xde7f80d62UL,0xc3fb0a64UL,0xce8ca802cUL,0x35032ed9dUL,0xaa8ba3ee6UL,0x94b2e707cUL,0x2debbdae1UL,0xf53e25fcfUL,0xe935543ebUL,0x1462f0e90UL,0x54ce7d18cUL,0x2ddafdc5fUL,0x700565deeUL,0xfd408e0afUL,0x17d089decUL,0x833ea2459UL,0x3c8d3776aUL,0x2e5eebac8UL,0x20cbf49b0UL,0xc44675eb7UL,0x3a4b6beb1UL,0xce6f37c1eUL,0x63fba2e7cUL,0x5a05b553dUL,0x1286445b0UL,0x5e07a9b61UL,0x7d8397ea4UL,0x8084b7bbbUL,0xb05b38097UL,0x29c3019eeUL,0xed1d2708bUL,0x9df8a4d47UL,0xe4891e436UL,0x2a762ab72UL,0x92f70600fUL,0x92329a2cdUL,0x3e200c6edUL,0x8c0a7233eUL,0x60866806aUL,0xf4fddd24aUL,0xf78464c71UL,0x9c3d22242UL,0x3877ea6d1UL,0xe2a6d54acUL,0x497d2a5e7UL,0xca82f781eUL,0x481524f4cUL,0xdee088814UL,0xb2a82d3a4UL,0x8e6afe6e5UL,0xd6279a5daUL,0x4567cbc1aUL,0x5bec2b2fdUL,0x4ef452505UL,0x61d992cbaUL,0xab96be0cbUL,0x708ef35d9UL,0xb3f6f3623UL,0x36eb1801dUL,0xbadfee917UL,0xa3db13cd0UL,0x1d1a12828UL,0x2500816ceUL,0xcf7612148UL,0xbe6a3f4bUL,0x74142f3daUL,0xce5deed92UL,0xf9530a786UL,0x47c8bb38UL,0xfcabfe88fUL,0xbc83accb1UL,0x20cd9fb1fUL,0x23dcceb3UL,0x9e969b8c4UL,0x6e28de934UL,0x80a399667UL,0x76a0b85adUL,0x21a84be3cUL,0xa28d028b5UL,0xc4e7690dfUL,0xbfd9621e8UL,0x6f4bc0c24UL,0xaa8e76bd7UL,0xdeb55dac9UL,0xbb344fa8bUL,0xfcaab4decUL,0x146aba6cbUL,0xf49ed6eb8UL,0xdd57e9deaUL,0x225d5d090UL,0xd6e86c1c5UL,0x639be5f39UL,0xf5e7a6132UL,0xd2968b09fUL,0x82b30ba1eUL,0x803fa46ccUL,0xc290fab00UL,0x10df59de5UL,0x51ae9dcfbUL,0x49af8516dUL,0x2b564ce6UL,0xc615a1de0UL,0xfef9864a4UL,0xc16e27341UL,0x39e846736UL,0x1ecbb6746UL,0x588d03a7cUL,0x10a0eaf9cUL,0x671ccea6bUL,0x33a154603UL,0xa7b003bc1UL,0xc5fc3848dUL,0x78e50a9c7UL,0x17dbfb88eUL,0x4fd0ed541UL,0x84221debaUL,0x3132cf7e6UL,0xb67e7ac53UL,0xdf6b28024UL,0x785b9f7edUL,0xe3d35320dUL,0x159c06583UL,0x5c54a80a3UL,0xed4d4533bUL,0xcf16c601aUL,0x5e94efbd1UL,0x5d587126eUL,0xeef2f2807UL,0x9f3c558eUL,0x736cfd539UL,0xf5a922ae1UL,0x4e2ab9959UL,0x6a2dd34e7UL,0x8c9d30d23UL,0xeba20b791UL,0xd5c5095e3UL,0x423d75a82UL,0x40cebaafeUL,0x65e08d288UL,0x2e4f6d767UL,0xfe10d2f21UL,0x110347bdaUL,0xe43a9bfb3UL,0xcdea483ccUL,0xfb1e2d8c6UL,0xd8a0af7a7UL,0x37d05b182UL,0x8d1241d83UL,0xda1ea7b6eUL,0x65bea93dbUL,0x2a02f8753UL,0x454243289UL,0x4150bc5a2UL,0xbbabe5911UL,0x4cbcdbc59UL,0xf0e61340bUL,0x30a2cdea8UL,0x5daecb091UL,0x5dc93d891UL,0xc501b4051UL,0x782cfba78UL,0x4c191b61eUL,0xb7e27ef35UL,0x5a476838UL,0x9b0209574UL,0xa775164cfUL,0xd33d21701UL,0x3afcb7d45UL,0x4df2035cdUL,0x498819a21UL,0x293f9e506UL,0x9a35ff1c8UL,0xc090ebe6bUL,0xa4f0551d4UL,0x5dc0dc194UL,0x1388aeb31UL,0x340b27bf4UL,0x3a0f320abUL,0x996be75dUL,0xb257ecf39UL,0x78d86f2f1UL,0x673f5ff91UL,0x4538d7e3eUL,0xde5bc4369UL}; + fromVector(codes,d._code_id); + d._nbits=36; + d._tau=10; + d._type=TAG36h10; + d._name="TAG36h10"; + + }break; + case TAG16h5:{ + vector codes={0x231bUL,0x2ea5UL,0x346aUL,0x45b9UL,0x79a6UL,0x7f6bUL,0xb358UL,0xe745UL,0xfe59UL,0x156dUL,0x380bUL,0xf0abUL,0xd84UL,0x4736UL,0x8c72UL,0xaf10UL,0x93cUL,0x93b4UL,0xa503UL,0x468fUL,0xe137UL,0x5795UL,0xdf42UL,0x1c1dUL,0xe9dcUL,0x73adUL,0xad5fUL,0xd530UL,0x7caUL,0xaf2eUL}; + fromVector(codes,d._code_id); + d._nbits=16; + d._tau=5; + d._type=TAG16h5; + d._name="TAG16h5"; + + }break; + case TAG25h7:{ + vector codes={0x4b770dUL,0x11693e6UL,0x1a599abUL,0xc3a535UL,0x152aafaUL,0xaccd98UL,0x1cad922UL,0x2c2fadUL,0xbb3572UL,0x14a3b37UL,0x186524bUL,0xc99d4cUL,0x23bfeaUL,0x141cb74UL,0x1d0d139UL,0x1670aebUL,0x851675UL,0x150334eUL,0x6e3ed8UL,0xfd449dUL,0xaa55ecUL,0x1c86176UL,0x15e9b28UL,0x7ca6b2UL,0x147c38bUL,0x1d6c950UL,0x8b0e8cUL,0x11a1451UL,0x1562b65UL,0x13f53c8UL,0xd58d7aUL,0x829ec9UL,0xfaccf1UL,0x136e405UL,0x7a2f06UL,0x10934cbUL,0x16a8b56UL,0x1a6a26aUL,0xf85545UL,0x195c2e4UL,0x24c8a9UL,0x12bfc96UL,0x16813aaUL,0x1a42abeUL,0x1573424UL,0x1044573UL,0xb156c2UL,0x5e6811UL,0x1659bfeUL,0x1d55a63UL,0x5bf065UL,0xe28667UL,0x1e9ba54UL,0x17d7c5aUL,0x1f5aa82UL,0x1a2bbd1UL,0x1ae9f9UL,0x1259e51UL,0x134062bUL,0xe1177aUL,0xed07a8UL,0x162be24UL,0x59128bUL,0x1663e8fUL,0x1a83cbUL,0x45bb59UL,0x189065aUL,0x4bb370UL,0x16fb711UL,0x122c077UL,0xeca17aUL,0xdbc1f4UL,0x88d343UL,0x58ac5dUL,0xba02e8UL,0x1a1d9dUL,0x1c72eecUL,0x924bc5UL,0xdccab3UL,0x886d15UL,0x178c965UL,0x5bc69aUL,0x1716261UL,0x174e2ccUL,0x1ed10f4UL,0x156aa8UL,0x3e2a8aUL,0x2752edUL,0x153c651UL,0x1741670UL,0x765b05UL,0x119c0bbUL,0x172a783UL,0x4faca1UL,0xf31257UL,0x12441fcUL,0xd3748UL,0xc21f15UL,0xac5037UL,0x180e592UL,0x7d3210UL,0xa27187UL,0x2beeafUL,0x26ff57UL,0x690e82UL,0x77765cUL,0x1a9e1d7UL,0x140be1aUL,0x1aa1e3aUL,0x1944f5cUL,0x19b5032UL,0x169897UL,0x1068eb9UL,0xf30dbcUL,0x106a151UL,0x1d53e95UL,0x1348ceeUL,0xcf4fcaUL,0x1728bb5UL,0xdc1eecUL,0x69e8dbUL,0x16e1523UL,0x105fa25UL,0x18abb0cUL,0xc4275dUL,0x6d8e76UL,0xe8d6dbUL,0xe16fd7UL,0x1ac2682UL,0x77435bUL,0xa359ddUL,0x3a9c4eUL,0x123919aUL,0x1e25817UL,0x2a836UL,0x1545a4UL,0x1209c8dUL,0xbb5f69UL,0x1dc1f02UL,0x5d5f7eUL,0x12d0581UL,0x13786c2UL,0xe15409UL,0x1aa3599UL,0x139aad8UL,0xb09d2aUL,0x54488fUL,0x13c351cUL,0x976079UL,0xb25b12UL,0x1addb34UL,0x1cb23aeUL,0x1175738UL,0x1303bb8UL,0xd47716UL,0x188ceeaUL,0xbaf967UL,0x1226d39UL,0x135e99bUL,0x34adc5UL,0x2e384dUL,0x90d3faUL,0x232713UL,0x17d49b1UL,0xaa84d6UL,0xc2ddf8UL,0x1665646UL,0x4f345fUL,0x2276b1UL,0x1255dd7UL,0x16f4cccUL,0x4aaffcUL,0xc46da6UL,0x85c7b3UL,0x1311fcbUL,0x9c6c4fUL,0x187d947UL,0x8578e4UL,0xe2bf0bUL,0xa01b4cUL,0xa1493bUL,0x7ad766UL,0xccfe82UL,0x1981b5bUL,0x1cacc85UL,0x562cdbUL,0x15b0e78UL,0x8f66c5UL,0x3332bfUL,0x12ce754UL,0x96a76UL,0x1d5e3baUL,0x27ea41UL,0x14412dfUL,0x67b9b4UL,0xdaa51aUL,0x1dcb17UL,0x4d4afdUL,0x6335d5UL,0xee2334UL,0x17d4e55UL,0x1b8b0f0UL,0x14999e3UL,0x1513dfaUL,0x765cf2UL,0x56af90UL,0x12e16acUL,0x1d3d86cUL,0xff279bUL,0x18822ddUL,0x99d478UL,0x8dc0d2UL,0x34b666UL,0xcf9526UL,0x186443dUL,0x7a8e29UL,0x19c6aa5UL,0x1f2a27dUL,0x12b2136UL,0xd0cd0dUL,0x12cb320UL,0x17ddb0bUL,0x5353bUL,0x15b2cafUL,0x1e5a507UL,0x120f1e5UL,0x114605aUL,0x14efe4cUL,0x568134UL,0x11b9f92UL,0x174d2a7UL,0x692b1dUL,0x39e4feUL,0xaaff3dUL,0x96224cUL,0x13c9f77UL,0x110ee8fUL,0xf17beaUL,0x99fb5dUL,0x337141UL,0x2b54dUL,0x1233a70UL}; + fromVector(codes,d._code_id); + d._nbits=25; + d._tau=7; + d._type=TAG25h7; + d._name="TAG25h7"; + + }break; + case TAG25h9:{ + vector codes={0x155cbf1UL,0x1e4d1b6UL,0x17b0b68UL,0x1eac9cdUL,0x12e14ceUL,0x3548bbUL,0x7757e6UL,0x1065dabUL,0x1baa2e7UL,0xdea688UL,0x81d927UL,0x51b241UL,0xdbc8aeUL,0x1e50e19UL,0x15819d2UL,0x16d8282UL,0x163e035UL,0x9d9b81UL,0x173eec4UL,0xae3a09UL,0x5f7c51UL,0x1a137fcUL,0xdc9562UL,0x1802e45UL,0x1c3542cUL,0x870fa4UL,0x914709UL,0x16684f0UL,0xc8f2a5UL,0x833ebbUL,0x59717fUL,0x13cd050UL,0xfa0ad1UL,0x1b763b0UL,0xb991ceUL}; + fromVector(codes,d._code_id); + d._nbits=25; + d._tau=9; + d._type=TAG25h9; + d._name="TAG25h9"; + + }break; + case ARUCO_MIP_16h3:{ + + vector codes={0x5867UL,0x8b03UL,0x2537UL,0xb6c7UL,0xe45UL,0x161UL,0x219UL,0x859bUL,0x87UL,0xc93fUL,0x905fUL,0x3e73UL,0x6ab7UL,0x1bafUL,0x6f0fUL,0x23d3UL,0x47a5UL,0x8cf7UL,0x83cfUL,0x9205UL,0x29a5UL,0x8033UL,0x857dUL,0xa4afUL,0x422fUL,0x1d07UL,0x4ee3UL,0x64c5UL,0xaa7fUL,0x4b75UL,0x34dbUL,0x926UL,0x262UL,0x501dUL,0x415UL,0x6201UL,0x2064UL,0x2d5UL,0x10bUL,0x9427UL,0xc16bUL,0xa603UL,0x911UL,0x1043UL,0x87bUL,0xccfUL,0x162bUL,0x9ab3UL,0x30b7UL,0xad0bUL,0x60a6UL,0x3845UL,0xce2bUL,0xadc7UL,0x612UL,0x4253UL,0x9cc3UL,0xc23UL,0x409bUL,0x8e87UL,0x98e5UL,0x20f1UL,0xa807UL,0x1bfUL,0x7023UL,0xdf1UL,0x2957UL,0x26b3UL,0xd80fUL,0x4076UL,0x233bUL,0x32e3UL,0x7a85UL,0x4349UL,0xc857UL,0x41c6UL,0x3813UL,0x6d97UL,0x324fUL,0xe3fUL,0x47ffUL,0x2217UL,0xdd6fUL,0x48b1UL,0x8b95UL,0xd9c7UL,0x1a7dUL,0x867bUL,0xb7ffUL,0x7fa7UL,0x478bUL,0x6d43UL,0x6167UL,0x8f67UL,0xcda7UL,0x5cb7UL,0xf3afUL,0x889fUL,0x2fdfUL,0xd2e7UL,0x553UL,0xbc1fUL,0x7607UL,0x2a61UL,0x11ebUL,0xa457UL,0x789fUL,0x2e95UL,0xb46bUL,0x39ffUL,0x4fb6UL,0x647fUL,0x250dUL,0x5883UL,0xc5d7UL,0xe73fUL,0x129fUL,0x548fUL,0xb253UL,0x635fUL,0x1ed7UL,0xc647UL,0xa1f7UL,0x565fUL,0x7e6fUL,0xe10fUL,0xf56UL,0xf2dfUL,0x87a3UL,0x4b87UL,0x24f6UL,0xafb7UL,0x3bc7UL,0x51a7UL,0x6a47UL,0x4c46UL,0x6723UL,0x2787UL,0x2667UL,0x2f13UL,0x50d7UL,0x680dUL,0x7ad3UL,0x454fUL,0x6c35UL,0xf17fUL,0x6b91UL,0x4011UL,0x16UL,0x2022UL,0xa1UL,0x142UL,0x40fUL,0x1025UL,0xcbUL,0x45UL,0x4427UL,0x8491UL,0x2292UL,0x482bUL,0x8221UL,0x8293UL,0xf7UL,0x7dUL,0x601UL,0x84a5UL,0x2443UL,0x806fUL,0x21c1UL,0x4825UL,0x466UL,0x742dUL,0xca07UL,0x20bbUL,0x88a3UL,0x2251UL,0xa81UL,0x202dUL,0x205bUL,0x1a07UL,0x8119UL,0x6413UL,0x6105UL,0x820bUL,0x8715UL,0x1c0bUL,0xb003UL,0x307UL,0x147bUL,0x135UL,0x62a3UL,0x6c0bUL,0x14b3UL,0x5c05UL,0xd81UL,0x36dUL,0x8d3UL,0x8ba7UL,0xb25UL,0x4c5bUL,0x2867UL,0x242bUL,0x1237UL,0x4206UL,0xa0c3UL,0xa4f3UL,0x984bUL,0x8507UL,0x4529UL,0xf0dUL,0xb2bUL,0x2891UL,0x1a3bUL,0x234bUL,0x8d43UL,0x22e6UL,0x7245UL,0x1c77UL,0x825dUL,0x3487UL,0x60d6UL,0x5403UL,0x90e3UL,0xa43UL,0x6519UL,0x6169UL,0xc397UL,0x2285UL,0xc127UL,0x2c05UL,0x8871UL,0x5a0bUL,0x4e26UL,0x15afUL,0xf97UL,0x258bUL,0x463bUL,0x86c3UL,0x292fUL,0x9e3UL,0x2571UL,0x4ce5UL,0x81d5UL,0x459UL,0x40e2UL,0x5b6UL,0x4a33UL,0x837UL,0xa2a7UL,0xf4bUL};fromVector(codes,d._code_id); + d._nbits=16; + d._tau=3; + d._type=ARUCO_MIP_16h3; + d._name="ARUCO_MIP_16h3"; + + }break; + + case ARUCO_MIP_25h7:{ + vector codes={0x1ad2487UL,0x141b6e9UL,0x1780e66UL,0x1a86ad8UL,0x1146916UL,0x14ebcb6UL,0x1a1893cUL,0xd79525UL,0xf8db53UL,0x1b5928UL,0x127bf52UL,0x1cb3a02UL,0x145ed8cUL,0x3970efUL,0xc8934cUL,0x5badedUL,0x53c9daUL,0x1991f79UL,0x1d40a4dUL,0x1631993UL,0x74d97cUL,0x62c8a5UL,0x1a6ce96UL,0x1c71456UL,0x18d8df2UL,0xe290faUL,0x494061UL,0xedc6cdUL,0x12bd8f0UL,0x1a5d254UL,0x172eb3aUL,0x9d6e92UL,0x10a2172UL,0x1bd085aUL,0x186e528UL,0xb4af45UL,0x55a5b2UL,0x5e5136UL,0x88508bUL,0xea2c6fUL,0x1a9274cUL,0xbbb977UL,0x5ede62UL,0x105ba85UL,0x18a79bcUL,0x18d4b9dUL,0x1a144deUL,0x1ba5390UL,0x1b8a652UL,0x1c4948dUL,0x3ce285UL,0xb5cce7UL,0x16d2cb9UL,0xb7d338UL,0xb650e9UL,0x130e4b1UL,0xd3badcUL,0xf2161dUL,0x17a45a7UL,0x14c5c50UL,0x14be8abUL,0x1230de9UL,0xf7c242UL,0x508697UL,0x14b1cc5UL,0x2c1e94UL,0xf89469UL,0xdc52d8UL,0x6eb5c4UL,0xd8ea08UL,0x16c779dUL,0x1770dbeUL,0x90a4ecUL,0x1bf212eUL,0x1a4b99aUL,0x1d2d3a9UL,0xf4f5aeUL,0x842e89UL,0x1c0e8d4UL,0x1af4a4cUL,0x189044bUL,0xdd3108UL,0x1ab639eUL,0x18836c5UL,0x5f5ad3UL,0x1410f5dUL,0xeb532fUL,0xbda0baUL,0x9edf2dUL,0xd5ae4eUL,0x165d1b8UL,0x187d07aUL,0x1dd616dUL,0x912527UL,0x940c3cUL,0x516b8dUL,0xc1da19UL,0xc7db85UL,0x144cb34UL,0xcd7a34UL}; +fromVector(codes,d._code_id); + d._nbits=25; + d._tau=7; + d._type=ARUCO_MIP_25h7; + d._name="ARUCO_MIP_25h7"; + + + }break; + + case ARUCO_MIP_36h12:{ + vector codes={0xd2b63a09dUL,0x6001134e5UL,0x1206fbe72UL,0xff8ad6cb4UL,0x85da9bc49UL,0xb461afe9cUL,0x6db51fe13UL,0x5248c541fUL,0x8f34503UL,0x8ea462eceUL,0xeac2be76dUL,0x1af615c44UL,0xb48a49f27UL,0x2e4e1283bUL,0x78b1f2fa8UL,0x27d34f57eUL,0x89222fff1UL,0x4c1669406UL,0xbf49b3511UL,0xdc191cd5dUL,0x11d7c3f85UL,0x16a130e35UL,0xe29f27effUL,0x428d8ae0cUL,0x90d548477UL,0x2319cbc93UL,0xc3b0c3dfcUL,0x424bccc9UL,0x2a081d630UL,0x762743d96UL,0xd0645bf19UL,0xf38d7fd60UL,0xc6cbf9a10UL,0x3c1be7c65UL,0x276f75e63UL,0x4490a3f63UL,0xda60acd52UL,0x3cc68df59UL,0xab46f9daeUL,0x88d533d78UL,0xb6d62ec21UL,0xb3c02b646UL,0x22e56d408UL,0xac5f5770aUL,0xaaa993f66UL,0x4caa07c8dUL,0x5c9b4f7b0UL,0xaa9ef0e05UL,0x705c5750UL,0xac81f545eUL,0x735b91e74UL,0x8cc35cee4UL,0xe44694d04UL,0xb5e121de0UL,0x261017d0fUL,0xf1d439eb5UL,0xa1a33ac96UL,0x174c62c02UL,0x1ee27f716UL,0x8b1c5ece9UL,0x6a05b0c6aUL,0xd0568dfcUL,0x192d25e5fUL,0x1adbeccc8UL,0xcfec87f00UL,0xd0b9dde7aUL,0x88dcef81eUL,0x445681cb9UL,0xdbb2ffc83UL,0xa48d96df1UL,0xb72cc2e7dUL,0xc295b53fUL,0xf49832704UL,0x9968edc29UL,0x9e4e1af85UL,0x8683e2d1bUL,0x810b45c04UL,0x6ac44bfe2UL,0x645346615UL,0x3990bd598UL,0x1c9ed0f6aUL,0xc26729d65UL,0x83993f795UL,0x3ac05ac5dUL,0x357adff3bUL,0xd5c05565UL,0x2f547ef44UL,0x86c115041UL,0x640fd9e5fUL,0xce08bbcf7UL,0x109bb343eUL,0xc21435c92UL,0x35b4dfce4UL,0x459752cf2UL,0xec915b82cUL,0x51881eed0UL,0x2dda7dc97UL,0x2e0142144UL,0x42e890f99UL,0x9a8856527UL,0x8e80d9d80UL,0x891cbcf34UL,0x25dd82410UL,0x239551d34UL,0x8fe8f0c70UL,0x94106a970UL,0x82609b40cUL,0xfc9caf36UL,0x688181d11UL,0x718613c08UL,0xf1ab7629UL,0xa357bfc18UL,0x4c03b7a46UL,0x204dedce6UL,0xad6300d37UL,0x84cc4cd09UL,0x42160e5c4UL,0x87d2adfa8UL,0x7850e7749UL,0x4e750fc7cUL,0xbf2e5dfdaUL,0xd88324da5UL,0x234b52f80UL,0x378204514UL,0xabdf2ad53UL,0x365e78ef9UL,0x49caa6ca2UL,0x3c39ddf3UL,0xc68c5385dUL,0x5bfcbbf67UL,0x623241e21UL,0xabc90d5ccUL,0x388c6fe85UL,0xda0e2d62dUL,0x10855dfe9UL,0x4d46efd6bUL,0x76ea12d61UL,0x9db377d3dUL,0xeed0efa71UL,0xe6ec3ae2fUL,0x441faee83UL,0xba19c8ff5UL,0x313035eabUL,0x6ce8f7625UL,0x880dab58dUL,0x8d3409e0dUL,0x2be92ee21UL,0xd60302c6cUL,0x469ffc724UL,0x87eebeed3UL,0x42587ef7aUL,0x7a8cc4e52UL,0x76a437650UL,0x999e41ef4UL,0x7d0969e42UL,0xc02baf46bUL,0x9259f3e47UL,0x2116a1dc0UL,0x9f2de4d84UL,0xeffac29UL,0x7b371ff8cUL,0x668339da9UL,0xd010aee3fUL,0x1cd00b4c0UL,0x95070fc3bUL,0xf84c9a770UL,0x38f863d76UL,0x3646ff045UL,0xce1b96412UL,0x7a5d45da8UL,0x14e00ef6cUL,0x5e95abfd8UL,0xb2e9cb729UL,0x36c47dd7UL,0xb8ee97c6bUL,0xe9e8f657UL,0xd4ad2ef1aUL,0x8811c7f32UL,0x47bde7c31UL,0x3adadfb64UL,0x6e5b28574UL,0x33e67cd91UL,0x2ab9fdd2dUL,0x8afa67f2bUL,0xe6a28fc5eUL,0x72049cdbdUL,0xae65dac12UL,0x1251a4526UL,0x1089ab841UL,0xe2f096ee0UL,0xb0caee573UL,0xfd6677e86UL,0x444b3f518UL,0xbe8b3a56aUL,0x680a75cfcUL,0xac02baea8UL,0x97d815e1cUL,0x1d4386e08UL,0x1a14f5b0eUL,0xe658a8d81UL,0xa3868efa7UL,0x3668a9673UL,0xe8fc53d85UL,0x2e2b7edd5UL,0x8b2470f13UL,0xf69795f32UL,0x4589ffc8eUL,0x2e2080c9cUL,0x64265f7dUL,0x3d714dd10UL,0x1692c6ef1UL,0x3e67f2f49UL,0x5041dad63UL,0x1a1503415UL,0x64c18c742UL,0xa72eec35UL,0x1f0f9dc60UL,0xa9559bc67UL,0xf32911d0dUL,0x21c0d4ffcUL,0xe01cef5b0UL,0x4e23a3520UL,0xaa4f04e49UL,0xe1c4fcc43UL,0x208e8f6e8UL,0x8486774a5UL,0x9e98c7558UL,0x2c59fb7dcUL,0x9446a4613UL,0x8292dcc2eUL,0x4d61631UL,0xd05527809UL,0xa0163852dUL,0x8f657f639UL,0xcca6c3e37UL,0xcb136bc7aUL,0xfc5a83e53UL,0x9aa44fc30UL,0xbdec1bd3cUL,0xe020b9f7cUL,0x4b8f35fb0UL,0xb8165f637UL,0x33dc88d69UL,0x10a2f7e4dUL,0xc8cb5ff53UL,0xde259ff6bUL,0x46d070dd4UL,0x32d3b9741UL,0x7075f1c04UL,0x4d58dbea0UL}; + fromVector(codes,d._code_id); + d._nbits=36; + d._tau=12; + d._type=ARUCO_MIP_36h12; + d._name="ARUCO_MIP_36h12"; + + + }break; + case CHILITAGS:{ + vector codes={0x7c765c6e0c3e00UL,0x7c765c14121e00UL,0x7c765a06763e00UL,0x7c765a7c681e00UL,0x7c764248643e00UL,0x7c7642327a1e00UL,0x7c7644201e3e00UL,0x7c76445a001e00UL,0x7c7626702c3800UL,0x7c76260a321800UL,0x7c762018563800UL,0x7c762062481800UL,0x7c763856443800UL,0x7c76382c5a1800UL,0x7c763e3e3e3800UL,0x7c763e44201800UL,0x7c70346e120000UL,0x7c7034140c2000UL,0x7c703206680000UL,0x7c70327c762000UL,0x7c702a487a0000UL,0x7c702a32642000UL,0x7c702c20000000UL,0x7c702c5a1e2000UL,0x7c704e70320600UL,0x7c704e0a2c2600UL,0x7c704818480600UL,0x7c704862562600UL,0x7c7050565a0600UL,0x7c70502c442600UL,0x7c70563e200600UL,0x7c7056443e2600UL,0x7c687c6e764400UL,0x7c687c14686400UL,0x7c687a060c4400UL,0x7c687a7c126400UL,0x7c6862481e4400UL,0x7c686232006400UL,0x7c686420644400UL,0x7c68645a7a6400UL,0x7c680670564200UL,0x7c68060a486200UL,0x7c6800182c4200UL,0x7c680062326200UL,0x7c6818563e4200UL,0x7c68182c206200UL,0x7c681e3e444200UL,0x7c681e445a6200UL,0x7c6e146e687a00UL,0x7c6e1414765a00UL,0x7c6e1206127a00UL,0x7c6e127c0c5a00UL,0x7c6e0a48007a00UL,0x7c6e0a321e5a00UL,0x7c6e0c207a7a00UL,0x7c6e0c5a645a00UL,0x7c6e6e70487c00UL,0x7c6e6e0a565c00UL,0x7c6e6818327c00UL,0x7c6e68622c5c00UL,0x7c6e7056207c00UL,0x7c6e702c3e5c00UL,0x7c6e763e5a7c00UL,0x7c6e7644445c00UL,0x7c0c5c68625600UL,0x7c0c5c127c7600UL,0x7c0c5a00185600UL,0x7c0c5a7a067600UL,0x7c0c424e0a5600UL,0x7c0c4234147600UL,0x7c0c4426705600UL,0x7c0c445c6e7600UL,0x7c0c2676425000UL,0x7c0c260c5c7000UL,0x7c0c201e385000UL,0x7c0c2064267000UL,0x7c0c38502a5000UL,0x7c0c382a347000UL,0x7c0c3e38505000UL,0x7c0c3e424e7000UL,0x7c0a34687c6800UL,0x7c0a3412624800UL,0x7c0a3200066800UL,0x7c0a327a184800UL,0x7c0a2a4e146800UL,0x7c0a2a340a4800UL,0x7c0a2c266e6800UL,0x7c0a2c5c704800UL,0x7c0a4e765c6e00UL,0x7c0a4e0c424e00UL,0x7c0a481e266e00UL,0x7c0a4864384e00UL,0x7c0a5050346e00UL,0x7c0a502a2a4e00UL,0x7c0a56384e6e00UL,0x7c0a5642504e00UL,0x7c127c68182c00UL,0x7c127c12060c00UL,0x7c127a00622c00UL,0x7c127a7a7c0c00UL,0x7c12624e702c00UL,0x7c1262346e0c00UL,0x7c1264260a2c00UL,0x7c12645c140c00UL,0x7c120676382a00UL,0x7c12060c260a00UL,0x7c12001e422a00UL,0x7c1200645c0a00UL,0x7c121850502a00UL,0x7c12182a4e0a00UL,0x7c121e382a2a00UL,0x7c121e42340a00UL,0x7c141468061200UL,0x7c141412183200UL,0x7c1412007c1200UL,0x7c14127a623200UL,0x7c140a4e6e1200UL,0x7c140a34703200UL,0x7c140c26141200UL,0x7c140c5c0a3200UL,0x7c146e76261400UL,0x7c146e0c383400UL,0x7c14681e5c1400UL,0x7c146864423400UL,0x7c1470504e1400UL,0x7c14702a503400UL,0x7c147638341400UL,0x7c1476422a3400UL,0x7a1e5c70321800UL,0x7a1e5c0a2c3800UL,0x7a1e5a18481800UL,0x7a1e5a62563800UL,0x7a1e42565a1800UL,0x7a1e422c443800UL,0x7a1e443e201800UL,0x7a1e44443e3800UL,0x7a1e266e121e00UL,0x7a1e26140c3e00UL,0x7a1e2006681e00UL,0x7a1e207c763e00UL,0x7a1e38487a1e00UL,0x7a1e3832643e00UL,0x7a1e3e20001e00UL,0x7a1e3e5a1e3e00UL,0x7a1834702c2600UL,0x7a18340a320600UL,0x7a183218562600UL,0x7a183262480600UL,0x7a182a56442600UL,0x7a182a2c5a0600UL,0x7a182c3e3e2600UL,0x7a182c44200600UL,0x7a184e6e0c2000UL,0x7a184e14120000UL,0x7a184806762000UL,0x7a18487c680000UL,0x7a185048642000UL,0x7a1850327a0000UL,0x7a1856201e2000UL,0x7a18565a000000UL,0x7a007c70486200UL,0x7a007c0a564200UL,0x7a007a18326200UL,0x7a007a622c4200UL,0x7a006256206200UL,0x7a00622c3e4200UL,0x7a00643e5a6200UL,0x7a006444444200UL,0x7a00066e686400UL,0x7a000614764400UL,0x7a000006126400UL,0x7a00007c0c4400UL,0x7a001848006400UL,0x7a0018321e4400UL,0x7a001e207a6400UL,0x7a001e5a644400UL,0x7a061470565c00UL,0x7a06140a487c00UL,0x7a0612182c5c00UL,0x7a061262327c00UL,0x7a060a563e5c00UL,0x7a060a2c207c00UL,0x7a060c3e445c00UL,0x7a060c445a7c00UL,0x7a066e6e765a00UL,0x7a066e14687a00UL,0x7a0668060c5a00UL,0x7a06687c127a00UL,0x7a0670481e5a00UL,0x7a067032007a00UL,0x7a067620645a00UL,0x7a06765a7a7a00UL,0x7a645c765c7000UL,0x7a645c0c425000UL,0x7a645a1e267000UL,0x7a645a64385000UL,0x7a644250347000UL,0x7a64422a2a5000UL,0x7a6444384e7000UL,0x7a644442505000UL,0x7a6426687c7600UL,0x7a642612625600UL,0x7a642000067600UL,0x7a64207a185600UL,0x7a64384e147600UL,0x7a6438340a5600UL,0x7a643e266e7600UL,0x7a643e5c705600UL,0x7a623476424e00UL,0x7a62340c5c6e00UL,0x7a62321e384e00UL,0x7a623264266e00UL,0x7a622a502a4e00UL,0x7a622a2a346e00UL,0x7a622c38504e00UL,0x7a622c424e6e00UL,0x7a624e68624800UL,0x7a624e127c6800UL,0x7a624800184800UL,0x7a62487a066800UL,0x7a62504e0a4800UL,0x7a625034146800UL,0x7a625626704800UL,0x7a62565c6e6800UL,0x7a7a7c76260a00UL,0x7a7a7c0c382a00UL,0x7a7a7a1e5c0a00UL,0x7a7a7a64422a00UL,0x7a7a62504e0a00UL,0x7a7a622a502a00UL,0x7a7a6438340a00UL,0x7a7a64422a2a00UL,0x7a7a0668060c00UL,0x7a7a0612182c00UL,0x7a7a00007c0c00UL,0x7a7a007a622c00UL,0x7a7a184e6e0c00UL,0x7a7a1834702c00UL,0x7a7a1e26140c00UL,0x7a7a1e5c0a2c00UL,0x7a7c1476383400UL,0x7a7c140c261400UL,0x7a7c121e423400UL,0x7a7c12645c1400UL,0x7a7c0a50503400UL,0x7a7c0a2a4e1400UL,0x7a7c0c382a3400UL,0x7a7c0c42341400UL,0x7a7c6e68183200UL,0x7a7c6e12061200UL,0x7a7c6800623200UL,0x7a7c687a7c1200UL,0x7a7c704e703200UL,0x7a7c70346e1200UL,0x7a7c76260a3200UL,0x7a7c765c141200UL,0x62565c6e680600UL,0x62565c14762600UL,0x62565a06120600UL,0x62565a7c0c2600UL,0x62564248000600UL,0x625642321e2600UL,0x625644207a0600UL,0x6256445a642600UL,0x62562670480000UL,0x6256260a562000UL,0x62562018320000UL,0x625620622c2000UL,0x62563856200000UL,0x6256382c3e2000UL,0x62563e3e5a0000UL,0x62563e44442000UL,0x6250346e763800UL,0x62503414681800UL,0x625032060c3800UL,0x6250327c121800UL,0x62502a481e3800UL,0x62502a32001800UL,0x62502c20643800UL,0x62502c5a7a1800UL,0x62504e70563e00UL,0x62504e0a481e00UL,0x625048182c3e00UL,0x62504862321e00UL,0x625050563e3e00UL,0x6250502c201e00UL,0x6250563e443e00UL,0x625056445a1e00UL,0x62487c6e127c00UL,0x62487c140c5c00UL,0x62487a06687c00UL,0x62487a7c765c00UL,0x624862487a7c00UL,0x62486232645c00UL,0x62486420007c00UL,0x6248645a1e5c00UL,0x62480670327a00UL,0x6248060a2c5a00UL,0x62480018487a00UL,0x62480062565a00UL,0x624818565a7a00UL,0x6248182c445a00UL,0x62481e3e207a00UL,0x62481e443e5a00UL,0x624e146e0c4200UL,0x624e1414126200UL,0x624e1206764200UL,0x624e127c686200UL,0x624e0a48644200UL,0x624e0a327a6200UL,0x624e0c201e4200UL,0x624e0c5a006200UL,0x624e6e702c4400UL,0x624e6e0a326400UL,0x624e6818564400UL,0x624e6862486400UL,0x624e7056444400UL,0x624e702c5a6400UL,0x624e763e3e4400UL,0x624e7644206400UL,0x622c5c68066e00UL,0x622c5c12184e00UL,0x622c5a007c6e00UL,0x622c5a7a624e00UL,0x622c424e6e6e00UL,0x622c4234704e00UL,0x622c4426146e00UL,0x622c445c0a4e00UL,0x622c2676266800UL,0x622c260c384800UL,0x622c201e5c6800UL,0x622c2064424800UL,0x622c38504e6800UL,0x622c382a504800UL,0x622c3e38346800UL,0x622c3e422a4800UL,0x622a3468185000UL,0x622a3412067000UL,0x622a3200625000UL,0x622a327a7c7000UL,0x622a2a4e705000UL,0x622a2a346e7000UL,0x622a2c260a5000UL,0x622a2c5c147000UL,0x622a4e76385600UL,0x622a4e0c267600UL,0x622a481e425600UL,0x622a48645c7600UL,0x622a5050505600UL,0x622a502a4e7600UL,0x622a56382a5600UL,0x622a5642347600UL,0x62327c687c1400UL,0x62327c12623400UL,0x62327a00061400UL,0x62327a7a183400UL,0x6232624e141400UL,0x623262340a3400UL,0x623264266e1400UL,0x6232645c703400UL,0x623206765c1200UL,0x6232060c423200UL,0x6232001e261200UL,0x62320064383200UL,0x62321850341200UL,0x6232182a2a3200UL,0x62321e384e1200UL,0x62321e42503200UL,0x62341468622a00UL,0x623414127c0a00UL,0x62341200182a00UL,0x6234127a060a00UL,0x62340a4e0a2a00UL,0x62340a34140a00UL,0x62340c26702a00UL,0x62340c5c6e0a00UL,0x62346e76422c00UL,0x62346e0c5c0c00UL,0x6234681e382c00UL,0x62346864260c00UL,0x623470502a2c00UL,0x6234702a340c00UL,0x62347638502c00UL,0x623476424e0c00UL,0x643e5c70562000UL,0x643e5c0a480000UL,0x643e5a182c2000UL,0x643e5a62320000UL,0x643e42563e2000UL,0x643e422c200000UL,0x643e443e442000UL,0x643e44445a0000UL,0x643e266e762600UL,0x643e2614680600UL,0x643e20060c2600UL,0x643e207c120600UL,0x643e38481e2600UL,0x643e3832000600UL,0x643e3e20642600UL,0x643e3e5a7a0600UL,0x64383470481e00UL,0x6438340a563e00UL,0x64383218321e00UL,0x643832622c3e00UL,0x64382a56201e00UL,0x64382a2c3e3e00UL,0x64382c3e5a1e00UL,0x64382c44443e00UL,0x64384e6e681800UL,0x64384e14763800UL,0x64384806121800UL,0x6438487c0c3800UL,0x64385048001800UL,0x643850321e3800UL,0x643856207a1800UL,0x6438565a643800UL,0x64207c702c5a00UL,0x64207c0a327a00UL,0x64207a18565a00UL,0x64207a62487a00UL,0x64206256445a00UL,0x6420622c5a7a00UL,0x6420643e3e5a00UL,0x64206444207a00UL,0x6420066e0c5c00UL,0x64200614127c00UL,0x64200006765c00UL,0x6420007c687c00UL,0x64201848645c00UL,0x642018327a7c00UL,0x64201e201e5c00UL,0x64201e5a007c00UL,0x64261470326400UL,0x6426140a2c4400UL,0x64261218486400UL,0x64261262564400UL,0x64260a565a6400UL,0x64260a2c444400UL,0x64260c3e206400UL,0x64260c443e4400UL,0x64266e6e126200UL,0x64266e140c4200UL,0x64266806686200UL,0x6426687c764200UL,0x642670487a6200UL,0x64267032644200UL,0x64267620006200UL,0x6426765a1e4200UL,0x64445c76384800UL,0x64445c0c266800UL,0x64445a1e424800UL,0x64445a645c6800UL,0x64444250504800UL,0x6444422a4e6800UL,0x644444382a4800UL,0x64444442346800UL,0x64442668184e00UL,0x64442612066e00UL,0x64442000624e00UL,0x6444207a7c6e00UL,0x6444384e704e00UL,0x644438346e6e00UL,0x64443e260a4e00UL,0x64443e5c146e00UL,0x64423476267600UL,0x6442340c385600UL,0x6442321e5c7600UL,0x64423264425600UL,0x64422a504e7600UL,0x64422a2a505600UL,0x64422c38347600UL,0x64422c422a5600UL,0x64424e68067000UL,0x64424e12185000UL,0x644248007c7000UL,0x6442487a625000UL,0x6442504e6e7000UL,0x64425034705000UL,0x64425626147000UL,0x6442565c0a5000UL,0x645a7c76423200UL,0x645a7c0c5c1200UL,0x645a7a1e383200UL,0x645a7a64261200UL,0x645a62502a3200UL,0x645a622a341200UL,0x645a6438503200UL,0x645a64424e1200UL,0x645a0668623400UL,0x645a06127c1400UL,0x645a0000183400UL,0x645a007a061400UL,0x645a184e0a3400UL,0x645a1834141400UL,0x645a1e26703400UL,0x645a1e5c6e1400UL,0x645c14765c0c00UL,0x645c140c422c00UL,0x645c121e260c00UL,0x645c1264382c00UL,0x645c0a50340c00UL,0x645c0a2a2a2c00UL,0x645c0c384e0c00UL,0x645c0c42502c00UL,0x645c6e687c0a00UL,0x645c6e12622a00UL,0x645c6800060a00UL,0x645c687a182a00UL,0x645c704e140a00UL,0x645c70340a2a00UL,0x645c76266e0a00UL,0x645c765c702a00UL,0x6765c681e5a00UL,0x6765c12007a00UL,0x6765a00645a00UL,0x6765a7a7a7a00UL,0x676424e765a00UL,0x6764234687a00UL,0x67644260c5a00UL,0x676445c127a00UL,0x67626763e5c00UL,0x676260c207c00UL,0x676201e445c00UL,0x67620645a7c00UL,0x6763850565c00UL,0x676382a487c00UL,0x6763e382c5c00UL,0x6763e42327c00UL,0x6703468006400UL,0x67034121e4400UL,0x67032007a6400UL,0x670327a644400UL,0x6702a4e686400UL,0x6702a34764400UL,0x6702c26126400UL,0x6702c5c0c4400UL,0x6704e76206200UL,0x6704e0c3e4200UL,0x670481e5a6200UL,0x6704864444200UL,0x6705050486200UL,0x670502a564200UL,0x6705638326200UL,0x67056422c4200UL,0x6687c68642000UL,0x6687c127a0000UL,0x6687a001e2000UL,0x6687a7a000000UL,0x668624e0c2000UL,0x6686234120000UL,0x6686426762000UL,0x668645c680000UL,0x6680676442600UL,0x668060c5a0600UL,0x668001e3e2600UL,0x6680064200600UL,0x66818502c2600UL,0x668182a320600UL,0x6681e38562600UL,0x6681e42480600UL,0x66e14687a1e00UL,0x66e1412643e00UL,0x66e1200001e00UL,0x66e127a1e3e00UL,0x66e0a4e121e00UL,0x66e0a340c3e00UL,0x66e0c26681e00UL,0x66e0c5c763e00UL,0x66e6e765a1800UL,0x66e6e0c443800UL,0x66e681e201800UL,0x66e68643e3800UL,0x66e7050321800UL,0x66e702a2c3800UL,0x66e7638481800UL,0x66e7642563800UL,0x60c5c6e703200UL,0x60c5c146e1200UL,0x60c5a060a3200UL,0x60c5a7c141200UL,0x60c4248183200UL,0x60c4232061200UL,0x60c4420623200UL,0x60c445a7c1200UL,0x60c2670503400UL,0x60c260a4e1400UL,0x60c20182a3400UL,0x60c2062341400UL,0x60c3856383400UL,0x60c382c261400UL,0x60c3e3e423400UL,0x60c3e445c1400UL,0x60a346e6e0c00UL,0x60a3414702c00UL,0x60a3206140c00UL,0x60a327c0a2c00UL,0x60a2a48060c00UL,0x60a2a32182c00UL,0x60a2c207c0c00UL,0x60a2c5a622c00UL,0x60a4e704e0a00UL,0x60a4e0a502a00UL,0x60a4818340a00UL,0x60a48622a2a00UL,0x60a5056260a00UL,0x60a502c382a00UL,0x60a563e5c0a00UL,0x60a5644422a00UL,0x6127c6e0a4800UL,0x6127c14146800UL,0x6127a06704800UL,0x6127a7c6e6800UL,0x6126248624800UL,0x61262327c6800UL,0x6126420184800UL,0x612645a066800UL,0x61206702a4e00UL,0x612060a346e00UL,0x6120018504e00UL,0x61200624e6e00UL,0x6121856424e00UL,0x612182c5c6e00UL,0x6121e3e384e00UL,0x6121e44266e00UL,0x614146e147600UL,0x61414140a5600UL,0x61412066e7600UL,0x614127c705600UL,0x6140a487c7600UL,0x6140a32625600UL,0x6140c20067600UL,0x6140c5a185600UL,0x6146e70347000UL,0x6146e0a2a5000UL,0x61468184e7000UL,0x6146862505000UL,0x61470565c7000UL,0x614702c425000UL,0x614763e267000UL,0x6147644385000UL,0x1e5c76207c00UL,0x1e5c0c3e5c00UL,0x1e5a1e5a7c00UL,0x1e5a64445c00UL,0x1e4250487c00UL,0x1e422a565c00UL,0x1e4438327c00UL,0x1e44422c5c00UL,0x1e2668007a00UL,0x1e26121e5a00UL,0x1e20007a7a00UL,0x1e207a645a00UL,0x1e384e687a00UL,0x1e3834765a00UL,0x1e3e26127a00UL,0x1e3e5c0c5a00UL,0x1834763e4200UL,0x18340c206200UL,0x18321e444200UL,0x1832645a6200UL,0x182a50564200UL,0x182a2a486200UL,0x182c382c4200UL,0x182c42326200UL,0x184e681e4400UL,0x184e12006400UL,0x184800644400UL,0x18487a7a6400UL,0x18504e764400UL,0x185034686400UL,0x1856260c4400UL,0x18565c126400UL,0x7c765a0600UL,0x7c0c442600UL,0x7a1e200600UL,0x7a643e2600UL,0x6250320600UL,0x622a2c2600UL,0x6438480600UL,0x6442562600UL,0x6687a0000UL,0x612642000UL,0x0UL,0x7a1e2000UL,0x184e120000UL,0x18340c2000UL,0x1e26680000UL,0x1e5c762000UL,0x61476443800UL,0x6140c5a1800UL,0x6121e3e3800UL,0x61264201800UL,0x60a502c3800UL,0x60a2a321800UL,0x60c38563800UL,0x60c42481800UL,0x66e68643e00UL,0x66e127a1e00UL,0x668001e3e00UL,0x6687a001e00UL,0x6704e0c3e00UL,0x67034121e00UL,0x67626763e00UL,0x6765c681e00UL,0x645c704e1400UL,0x645c0a503400UL,0x645a18341400UL,0x645a622a3400UL,0x644256261400UL,0x64422c383400UL,0x64443e5c1400UL,0x644444423400UL,0x64266e6e1200UL,0x642614703200UL,0x642006141200UL,0x64207c0a3200UL,0x643848061200UL,0x643832183200UL,0x643e207c1200UL,0x643e5a623200UL,0x623470502a00UL,0x62340a4e0a00UL,0x6232182a2a00UL,0x623262340a00UL,0x622a56382a00UL,0x622a2c260a00UL,0x622c3e422a00UL,0x622c445c0a00UL,0x624e6e702c00UL,0x624e146e0c00UL,0x6248060a2c00UL,0x62487c140c00UL,0x625048182c00UL,0x625032060c00UL,0x625620622c00UL,0x62565a7c0c00UL,0x7a7c70346e00UL,0x7a7c0a2a4e00UL,0x7a7a184e6e00UL,0x7a7a62504e00UL,0x7a62565c6e00UL,0x7a622c424e00UL,0x7a643e266e00UL,0x7a6444384e00UL,0x7a066e146800UL,0x7a06140a4800UL,0x7a00066e6800UL,0x7a007c704800UL,0x7a18487c6800UL,0x7a1832624800UL,0x7a1e20066800UL,0x7a1e5a184800UL,0x7c14702a5000UL,0x7c140a347000UL,0x7c1218505000UL,0x7c12624e7000UL,0x7c0a56425000UL,0x7c0a2c5c7000UL,0x7c0c3e385000UL,0x7c0c44267000UL,0x7c6e6e0a5600UL,0x7c6e14147600UL,0x7c6806705600UL,0x7c687c6e7600UL,0x7c7048625600UL,0x7c70327c7600UL,0x7c7620185600UL,0x7c765a067600UL,0x18565c687a6200UL,0x18565c12644200UL,0x18565a00006200UL,0x18565a7a1e4200UL,0x1856424e126200UL,0x185642340c4200UL,0x18564426686200UL,0x1856445c764200UL,0x185626765a6400UL,0x1856260c444400UL,0x1856201e206400UL,0x185620643e4400UL,0x18563850326400UL,0x1856382a2c4400UL,0x18563e38486400UL,0x18563e42564400UL,0x18503468645c00UL,0x185034127a7c00UL,0x185032001e5c00UL,0x1850327a007c00UL,0x18502a4e0c5c00UL,0x18502a34127c00UL,0x18502c26765c00UL,0x18502c5c687c00UL,0x18504e76445a00UL,0x18504e0c5a7a00UL,0x1850481e3e5a00UL,0x18504864207a00UL,0x185050502c5a00UL,0x1850502a327a00UL,0x18505638565a00UL,0x18505642487a00UL,0x18487c68001800UL,0x18487c121e3800UL,0x18487a007a1800UL,0x18487a7a643800UL,0x1848624e681800UL,0x18486234763800UL,0x18486426121800UL,0x1848645c0c3800UL,0x18480676201e00UL,0x1848060c3e3e00UL,0x1848001e5a1e00UL,0x18480064443e00UL,0x18481850481e00UL,0x1848182a563e00UL,0x18481e38321e00UL,0x18481e422c3e00UL,0x184e14681e2600UL,0x184e1412000600UL,0x184e1200642600UL,0x184e127a7a0600UL,0x184e0a4e762600UL,0x184e0a34680600UL,0x184e0c260c2600UL,0x184e0c5c120600UL,0x184e6e763e2000UL,0x184e6e0c200000UL,0x184e681e442000UL,0x184e68645a0000UL,0x184e7050562000UL,0x184e702a480000UL,0x184e76382c2000UL,0x184e7642320000UL,0x182c5c6e140a00UL,0x182c5c140a2a00UL,0x182c5a066e0a00UL,0x182c5a7c702a00UL,0x182c42487c0a00UL,0x182c4232622a00UL,0x182c4420060a00UL,0x182c445a182a00UL,0x182c2670340c00UL,0x182c260a2a2c00UL,0x182c20184e0c00UL,0x182c2062502c00UL,0x182c38565c0c00UL,0x182c382c422c00UL,0x182c3e3e260c00UL,0x182c3e44382c00UL,0x182a346e0a3400UL,0x182a3414141400UL,0x182a3206703400UL,0x182a327c6e1400UL,0x182a2a48623400UL,0x182a2a327c1400UL,0x182a2c20183400UL,0x182a2c5a061400UL,0x182a4e702a3200UL,0x182a4e0a341200UL,0x182a4818503200UL,0x182a48624e1200UL,0x182a5056423200UL,0x182a502c5c1200UL,0x182a563e383200UL,0x182a5644261200UL,0x18327c6e6e7000UL,0x18327c14705000UL,0x18327a06147000UL,0x18327a7c0a5000UL,0x18326248067000UL,0x18326232185000UL,0x183264207c7000UL,0x1832645a625000UL,0x183206704e7600UL,0x1832060a505600UL,0x18320018347600UL,0x183200622a5600UL,0x18321856267600UL,0x1832182c385600UL,0x18321e3e5c7600UL,0x18321e44425600UL,0x1834146e704e00UL,0x183414146e6e00UL,0x183412060a4e00UL,0x1834127c146e00UL,0x18340a48184e00UL,0x18340a32066e00UL,0x18340c20624e00UL,0x18340c5a7c6e00UL,0x18346e70504800UL,0x18346e0a4e6800UL,0x183468182a4800UL,0x18346862346800UL,0x18347056384800UL,0x1834702c266800UL,0x1834763e424800UL,0x183476445c6800UL,0x1e3e5c76444400UL,0x1e3e5c0c5a6400UL,0x1e3e5a1e3e4400UL,0x1e3e5a64206400UL,0x1e3e42502c4400UL,0x1e3e422a326400UL,0x1e3e4438564400UL,0x1e3e4442486400UL,0x1e3e2668644200UL,0x1e3e26127a6200UL,0x1e3e20001e4200UL,0x1e3e207a006200UL,0x1e3e384e0c4200UL,0x1e3e3834126200UL,0x1e3e3e26764200UL,0x1e3e3e5c686200UL,0x1e3834765a7a00UL,0x1e38340c445a00UL,0x1e38321e207a00UL,0x1e3832643e5a00UL,0x1e382a50327a00UL,0x1e382a2a2c5a00UL,0x1e382c38487a00UL,0x1e382c42565a00UL,0x1e384e687a7c00UL,0x1e384e12645c00UL,0x1e384800007c00UL,0x1e38487a1e5c00UL,0x1e38504e127c00UL,0x1e3850340c5c00UL,0x1e385626687c00UL,0x1e38565c765c00UL,0x1e207c763e3e00UL,0x1e207c0c201e00UL,0x1e207a1e443e00UL,0x1e207a645a1e00UL,0x1e206250563e00UL,0x1e20622a481e00UL,0x1e2064382c3e00UL,0x1e206442321e00UL,0x1e2006681e3800UL,0x1e200612001800UL,0x1e200000643800UL,0x1e20007a7a1800UL,0x1e20184e763800UL,0x1e201834681800UL,0x1e201e260c3800UL,0x1e201e5c121800UL,0x1e261476200000UL,0x1e26140c3e2000UL,0x1e26121e5a0000UL,0x1e261264442000UL,0x1e260a50480000UL,0x1e260a2a562000UL,0x1e260c38320000UL,0x1e260c422c2000UL,0x1e266e68000600UL,0x1e266e121e2600UL,0x1e2668007a0600UL,0x1e26687a642600UL,0x1e26704e680600UL,0x1e267034762600UL,0x1e267626120600UL,0x1e26765c0c2600UL,0x1e445c702a2c00UL,0x1e445c0a340c00UL,0x1e445a18502c00UL,0x1e445a624e0c00UL,0x1e444256422c00UL,0x1e44422c5c0c00UL,0x1e44443e382c00UL,0x1e444444260c00UL,0x1e44266e0a2a00UL,0x1e442614140a00UL,0x1e442006702a00UL,0x1e44207c6e0a00UL,0x1e443848622a00UL,0x1e4438327c0a00UL,0x1e443e20182a00UL,0x1e443e5a060a00UL,0x1e423470341200UL,0x1e42340a2a3200UL,0x1e4232184e1200UL,0x1e423262503200UL,0x1e422a565c1200UL,0x1e422a2c423200UL,0x1e422c3e261200UL,0x1e422c44383200UL,0x1e424e6e141400UL,0x1e424e140a3400UL,0x1e4248066e1400UL,0x1e42487c703400UL,0x1e4250487c1400UL,0x1e425032623400UL,0x1e425620061400UL,0x1e42565a183400UL,0x1e5a7c70505600UL,0x1e5a7c0a4e7600UL,0x1e5a7a182a5600UL,0x1e5a7a62347600UL,0x1e5a6256385600UL,0x1e5a622c267600UL,0x1e5a643e425600UL,0x1e5a64445c7600UL,0x1e5a066e705000UL,0x1e5a06146e7000UL,0x1e5a00060a5000UL,0x1e5a007c147000UL,0x1e5a1848185000UL,0x1e5a1832067000UL,0x1e5a1e20625000UL,0x1e5a1e5a7c7000UL,0x1e5c14704e6800UL,0x1e5c140a504800UL,0x1e5c1218346800UL,0x1e5c12622a4800UL,0x1e5c0a56266800UL,0x1e5c0a2c384800UL,0x1e5c0c3e5c6800UL,0x1e5c0c44424800UL,0x1e5c6e6e6e6e00UL,0x1e5c6e14704e00UL,0x1e5c6806146e00UL,0x1e5c687c0a4e00UL,0x1e5c7048066e00UL,0x1e5c7032184e00UL,0x1e5c76207c6e00UL,0x1e5c765a624e00UL}; + fromVector(codes,d._code_id); + d._nbits=64; + d._tau=5; + d._type=CHILITAGS; + d._name="CHILITAGS"; + + }break; + case CUSTOM: + throw cv::Exception(-1,"CUSTOM type is only set by loading from file","Dictionary::loadPredefined","dictionary.h",-1); + break; + case ALL_DICTS:{ + d._nbits=64; + d._tau=1;// + d._type=ALL_DICTS; + d._name="ALL_DICTS"; + }break; + + default: throw cv::Exception(9001, "Invalid Dictionary type requested", "Dictionary::loadPredefined", __FILE__, __LINE__); + + }; + return d; +} +/** + * @brief Dictionary::getMarkerImage_id + * @param id + * @return + */ +//cv::Mat Dictionary::getMarkerMatrix_id(int id){ +// const int nBitsSquared = static_cast(std::sqrt(nbits())); +// const int A=bit_size*(2+nBitsSquared); + +// cv::Mat img=cv::Mat::zeros(A,A,CV_8UC1); +//} + + +cv::Mat Dictionary::getMarkerImage_id(int id,int bit_size,bool addWaterMark,bool enclosed_corners,bool externalWhiteBorder,bool centralCircle) +{ + const int nBitsSquared = static_cast(std::sqrt(nbits())); + const int A=bit_size*(2+nBitsSquared); + + cv::Mat img=cv::Mat::zeros(A,A,CV_8UC1); + + //find the code in the map + uint64_t code; + bool found=false; + for(auto c_id:_code_id) + if(c_id.second==id){ + found=true; + code=c_id.first; + break; + } + if (!found) { + cerr<<"marker "< bset=code; + + int bidx=0; + for (int y = nBitsSquared-1; y >=0 ; y--) + for (int x = nBitsSquared-1; x >=0; x--){ + if (bset[bidx++]){ + cv::Mat bit_pix=img( cv::Range((1+y)*bit_size,(2+y)*bit_size),cv::Range((1+x)*bit_size,(2+x)*bit_size)); + bit_pix.setTo(cv::Scalar::all(255)); + } + } + + if (addWaterMark){ + char idcad[30]; + sprintf(idcad, "#%d", id); + float ax = static_cast(A) / 100.f; + int linew = 1 + (img.rows / 500); + cv::putText(img, idcad, cv::Point(0, img.rows - img.rows / 40), cv::FONT_HERSHEY_COMPLEX, ax * 0.15f, cv::Scalar::all(30), linew); + + } + + + if (enclosed_corners){ + cv::Mat biggerImage( img.rows+img.rows/2,img.cols+img.cols/2,img.type()); + biggerImage.setTo(cv::Scalar::all(255)); + //set the image in the center + int sy=img.rows/4; + int sx=img.cols/4; + cv::Mat center=biggerImage(cv::Range(sy,sy+img.rows),cv::Range(sx,sx+img.cols)); + img.copyTo(center); + biggerImage(cv::Range(0,sy),cv::Range(0,sx)).setTo(cv::Scalar::all(0)); + biggerImage(cv::Range(biggerImage.rows-sy,biggerImage.rows),cv::Range(0,sx)).setTo(cv::Scalar::all(0)); + biggerImage(cv::Range(biggerImage.rows-sy,biggerImage.rows),cv::Range(biggerImage.cols- sx,biggerImage.cols)).setTo(cv::Scalar::all(0)); + biggerImage(cv::Range(0,sy),cv::Range(biggerImage.cols- sx,biggerImage.cols)).setTo(cv::Scalar::all(0)); + + img=biggerImage; + } + + if (externalWhiteBorder && !enclosed_corners){ + int borderSize=bit_size; + //Create another image in white and put the marker in the middle + cv::Mat biggerImage( img.rows+2*borderSize,img.cols+2*borderSize,img.type()); + biggerImage.setTo(cv::Scalar::all(255)); + //set the image in the center + cv::Mat center=biggerImage(cv::Range(borderSize,borderSize+img.rows),cv::Range(borderSize,borderSize+img.cols)); + img.copyTo(center); + img=biggerImage; + } + + if( centralCircle ){ + + //at the image center, draw a circle to mark the center + cv::Point2f center(img.cols/2,img.rows/2); + cv::Mat mask(img.size(),CV_8UC1); + mask.setTo(cv::Scalar::all(0)); + cv::circle(mask,center,sqrt(bit_size),cv::Scalar::all(255),-1); + //now, invert the color in the image + for(int r=0;r(r); + uchar *maskptr=mask.ptr(r); + for(int c=0;c to convert to Dictionary type", "Dictionary::getTypeFromString", __FILE__, __LINE__); + + } + +bool Dictionary::isPredefinedDictinaryString(string str) { + return getTypeFromString(str)!=CUSTOM; +} + +vector Dictionary::getDicTypes() { +return { "ARUCO","ARUCO_MIP_16h3","ARUCO_MIP_25h7","ARUCO_MIP_36h12", "ARTOOLKITPLUS","ARTOOLKITPLUSBCH","TAG16h5","TAG25h7","TAG25h9" + ,"TAG36h11","TAG36h10","CHILITAGS","ALL_DICTS"}; + +} + +MarkerMap Dictionary::createMarkerMap( cv::Size gridSize,int MarkerSize,int MarkerDistance, const std::vector &ids,bool chess_board) { + if (gridSize.height*gridSize.width!=int(ids.size()))throw cv::Exception(9001, "gridSize != ids.size()Invalid ", "Dictionary::createMarkerMap", __FILE__, __LINE__); + MarkerMap TInfo; + + TInfo.mInfoType=MarkerMap::PIX; + TInfo.setDictionary(getTypeString(_type)); + const float markerSizeFloat = static_cast(MarkerSize); + + + if (!chess_board){ + TInfo.resize(ids.size()); + for (size_t i=0;i(MarkerDistance)+markerSizeFloat; + for (int y=0;y=int(ids.size())) throw cv::Exception(999," FiducidalMarkers::createMarkerMapImage_ChessMarkerMap","INTERNAL ERROR. REWRITE THIS!!",__FILE__,__LINE__); + TInfo.push_back( Marker3DInfo(ids[CurMarkerIdx++])); + /// \todo use const auto & + for(auto p:aruco::Marker::get3DPoints(markerSizeFloat)) + TInfo.back().points.push_back(p+cv::Point3f(x*markerSizeFloat,y*markerSizeFloat,0) ); + } + } + } + } + + // cv::Point3f center(0,0,0); + // double n=0; + // for(auto &ti:TInfo)for(auto p:ti) {center+=p;n++;} + // center*=1./n; + cv::Point3f center(gridSize.width/2.0f * markerSizeFloat - markerSizeFloat/2.0f, gridSize.height/2.0f * markerSizeFloat - markerSizeFloat/2.0f, 0.f); + for(auto &ti:TInfo) for(auto &p:ti.points) p-=center; + + + return TInfo; +} + +/** + * @brief Dictionary::computeDictionaryDistance + * @param dict + * @return + */ +uint64_t Dictionary::computeDictionaryDistance(const Dictionary &dict){ + + auto rotate=[](const cv::Mat &in) { + cv::Mat out; + in.copyTo(out); + for (int i = 0; i < in.rows; i++) { + for (int j = 0; j < in.cols; j++) { + out.at< uchar >(i, j) = in.at< uchar >(in.cols - j - 1, i); + } + } + return out; + }; + auto getImage=[](uint64_t tag,int nbits){ + std::bitset<64> bs(tag); + cv::Mat im(nbits,nbits,CV_8UC1); + int bit=(nbits*nbits)-1; + for(int i=0;i(i,j)=bs[bit--]; + return im; + }; + + auto getCode=[](const cv::Mat &in){ + assert(in.type()==CV_8UC1); + std::bitset<64> bs; + size_t bit= in.total()-1; + for(int i=0;i(i,j); + return bs.to_ullong(); + }; + + + //convert each element into its 4 rorations first + std::vector all_rotations; + + std::vector ids; + //for chilitag + auto code_id2=dict._code_id; + for(auto tag_id:code_id2){ + all_rotations.push_back(tag_id.first); + auto m=getImage(tag_id.first, static_cast(sqrt(dict._nbits))); + for(int i=0;i<3;i++) + { + m=rotate(m); + all_rotations.push_back(getCode(m)); + ids.push_back( tag_id.second); + } + assert( getCode(rotate(m))==tag_id.first ); + } + + //now, compute minimum distance + std::map >errors; + uint64_t mind=std::numeric_limits::max(); + for(size_t i=0;i(all_rotations[i]^all_rotations[j]).count(); + if(d==0){ + if ( errors[ids[i]].count(ids[j])==0 ){ + cerr<<" Dictionary::computeDictionaryDistance ERROR IN YOUR DICT!!!!!"< + +#include +#include +#include +#include +#include + +namespace aruco +{ + class MarkerMap; + /**Represents a set of valid marker ids with a maximum size of 8x8 = 64 bits. + * In our approach, markers are seen as a pair code-id. The code is the internal binary code printed on the marker. + * Maximum size is 8x8 bits. + * The id is a smaller number you can use to identify it. You will use only the id + * + * See enum DICT_TYPES for the set of dicitionaries availables + */ + + class ARUCO_EXPORT Dictionary + { + public: + // loads from a set of predefined ones + enum DICT_TYPES: + uint64_t{ + ALL_DICTS=0, + ARUCO_MIP_36h12=1, //*** recommended + ARUCO=2, // original aruco dictionary. By default + ARUCO_MIP_25h7=3, + ARUCO_MIP_16h3=4, + ARTAG=5, // + ARTOOLKITPLUS=6, + ARTOOLKITPLUSBCH=7, // + TAG16h5=8, + TAG25h7=9, + TAG25h9=10, + TAG36h11=11, + TAG36h10=12, // april tags + CHILITAGS=13, // chili tags dictionary . NOT RECOMMENDED. It has distance 0. Markers 806 and 682 should not be + // used!!! + CUSTOM=14 , // for used defined dictionaries (using loadFromfile). + }; + // indicates if a code is in the dictionary + bool is(uint64_t code) const + { + return _code_id.find(code) != _code_id.end(); + } + + DICT_TYPES getType() const + { + return _type; + } + + // reutnr the numerber of ids + uint64_t size() const + { + return _code_id.size(); + } + // returns the total number of bits of the binary code + uint32_t nbits() const + { + return _nbits; + } + // returns the dictionary distance + uint32_t tau() const + { + return _tau; + } + // returns the name + std::string getName() const + { + return _name; + } + // return the set of ids + const std::map& getMapCode() const + { + return _code_id; + } + + // returns the id of a given code. + int operator[](uint64_t code) + { + return _code_id[code]; + } // returns the id of a given code. + int at(uint64_t code) + { + return _code_id[code]; + } + + // returns the image of the marker indicated by its id. It the id is not, returns empty matrix + //@param id of the marker image to return + //@param bit_size of the image will be AxA, A=(nbits()+2)*bit_size + //@param enclosed_corners if true, extra rectagles are added touching the marker corners. it can be used to + //allow subpixel refinement + cv::Mat getMarkerImage_id(int id, int bit_size, bool addWaterMark = true, bool enclosed_corners = false,bool printExternalWhiteBorder=false,bool centralCircle=false); + + // cv::Mat getMarkerMatrix_id(int id); + + // used for boards + MarkerMap createMarkerMap(cv::Size gridSize, int MarkerSize, int MarkerDistance, const std::vector& Ids, + bool chess_board = false); + + static Dictionary loadPredefined(DICT_TYPES type); + static Dictionary loadPredefined(std::string type); + + /** loads a dictionary defined in a file + * Please note that the parsing is very basic and you must be very strict. + + * Here is an example of a 3x3 dictionary of 3 markers + * 010 111 000 + * 001 101 001 + * 001 010 100 + * + * + * File: myown.dict + *------------------------------------------- + * name MYOWN + * nbits 9 + * 010001001 + * 111101010 + * 000001100 + */ + static Dictionary loadFromFile(std::string path); + + /**Loads a dictioanary using the string passed. If it is a string of the predefined dictionaries, then returns + * it. + * Otherwise, tries to load from a file + */ + static Dictionary load(std::string info); + + // //io functions + // void saveToFile(std::string file); + // void readFromFile(std::string file); + // void saveToStream(std::ostream & str); + // void readFromStream(std::istream &str); + + // returns the dictionary distance + static uint64_t computeDictionaryDistance(const Dictionary& d); + + // given a string,returns the type + static DICT_TYPES getTypeFromString(std::string str); + static std::string getTypeString(DICT_TYPES t); + static bool isPredefinedDictinaryString(std::string str); + static std::vector getDicTypes(); + + private: + //obfuscate start + + void insert(uint64_t code, int id) + { + _code_id.insert(std::make_pair(code, id)); + } + static void fromVector(const std::vector& codes, std::map& code_id_map); + + std::map _code_id; // marker have and code (internal binary code), + // which correspond to an id. + + uint32_t _nbits; // total number of bits . So, there are sqrt(nbits) in each axis + uint32_t _tau; // minimum distance between elements + + DICT_TYPES _type; + std::string _name; + //obfuscate end + + }; +} + +#endif diff --git a/thirdparty/aruco-3.1.12/src/dictionary_based.cpp b/thirdparty/aruco-3.1.12/src/dictionary_based.cpp new file mode 100644 index 0000000..562a3ad --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dictionary_based.cpp @@ -0,0 +1,290 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "dictionary_based.h" + +#include +#include +#include +#include "aruco_cvversioning.h" +namespace aruco +{ + void DictionaryBased::setParams(const Dictionary& dic, float max_correction_rate) + { + + _nsubdivisions=sqrt(dic.nbits())+2; + nbits_dict.clear(); + dicttypename=dic.getName(); + if (dic.getType()==Dictionary::ALL_DICTS){ + for(auto &dt:Dictionary::getDicTypes()) + if (dt!="ALL_DICTS" && dt!="CUSTOM") + vdic.push_back( Dictionary ::loadPredefined(dt)); + } + else vdic.push_back( dic); + + // + for(auto &dic:vdic) + nbits_dict[dic.nbits()].push_back(&dic); + + _max_correction_rate = std::max(0.f, std::min(1.0f, max_correction_rate)); + } + + std::string DictionaryBased::getName() const + { + return dicttypename; + } + + void DictionaryBased::toMat(uint64_t code, int nbits_sq, cv::Mat& out) + { + out.create(nbits_sq, nbits_sq, CV_8UC1); + std::bitset<64> bs(code); + int curbit = 0; + for (int r = 0; r < nbits_sq; r++) + { + uchar* pr = out.ptr(r); + for (int c = 0; c < nbits_sq; c++) + pr[c] = bs[curbit]; + } + } + + int hamm_distance(uint64_t a, uint64_t b) + { + return static_cast(std::bitset<64>(a ^ b).count()); + } + + bool DictionaryBased::detect(const cv::Mat& in, int& marker_id, int& nRotations, std::string &additionalInfo) + { + assert(in.rows == in.cols); + + cv::Mat grey; + if (in.type() == CV_8UC1) grey = in; + else cv::cvtColor(in, grey, CV_BGR2GRAY); + // threshold image + cv::threshold(grey, grey, 125, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); + + + std::map > nbits_ids; + //for each + for(auto &bitsids:nbits_dict){ + int nbits=bitsids.first; + std::vector ids; + getInnerCode(grey,nbits,ids); + if (ids.size()>0){ + if (ids[0]!=0){ + nbits_ids[nbits]=ids; + } + } + } + //how many are there? + + if ( nbits_ids.size()==0)return false; + //check if any dictionary recognizes it + for(auto nbits:nbits_ids){ + const auto &ids=nbits.second; + //check in every dictionary + for(auto &dic:nbits_dict[nbits.first]){ + //try a perfecf match + for(int rot=0;rot<4;rot++) + if ( dic->is( ids[rot])){ + // std::cout<<"MATCH:"<getName()<<" "<at (ids[rot]); + additionalInfo=dic->getName(); + return true; + } + + //try with some error/correction if allowed + if (_max_correction_rate > 0) + { // find distance to map elements + int _maxCorrectionAllowed = static_cast( static_cast(dic->tau()) * _max_correction_rate); + for (auto ci : dic->getMapCode()) + { + for (int i = 0; i < 4; i++) + { + if (hamm_distance(ci.first, ids[i]) < _maxCorrectionAllowed) + { + marker_id = ci.second; + nRotations = i; + additionalInfo=dic->getName(); + return true; + } + } + } + } + + } + + + } + + + return false; + } + + + bool DictionaryBased::getInnerCode(const cv::Mat& thres_img, int total_nbits, std::vector& ids) + { + int bits_noborder = static_cast(std::sqrt(total_nbits)); + int bits_withborder = bits_noborder + 2; + // Markers are divided in (bits_a+2)x(bits_a+2) regions, of which the inner bits_axbits_a belongs to marker + // info + // the external border shoould be entirely black + cv::Mat nonZeros(bits_withborder,bits_withborder,CV_32SC1); + cv::Mat nValues(bits_withborder,bits_withborder,CV_32SC1); + nonZeros.setTo(cv::Scalar::all(0)); + nValues.setTo(cv::Scalar::all(0)); + for (int y = 0; y < thres_img.rows; y++) + { + const uchar *ptr=thres_img.ptr(y); + int my= float(bits_withborder)*float(y)/ float(thres_img.rows); + for (int x = 0; x < thres_img.cols; x++) + { + int mx= float(bits_withborder)*float(x)/ float(thres_img.cols); + if( ptr[x]>125) + nonZeros.at(my,mx)++; + nValues.at(my,mx)++; + } + } + cv::Mat binaryCode(bits_withborder,bits_withborder,CV_8UC1); + //now, make the theshold + for(int y=0;y(y,x)>nValues.at(y,x)/2) + binaryCode.at(y,x)=1; + else + binaryCode.at(y,x)=0; + } + + //check if border is completely black + for (int y = 0; y < bits_withborder; y++) + { + int inc = bits_withborder - 1; + if (y == 0 || y == bits_withborder - 1) + inc = 1; // for first and last row, check the whole border + for (int x = 0; x < bits_withborder; x += inc) + if (binaryCode.at(y,x)!=0 ) return false; + } + + //take the inner code + + cv::Mat _bits(bits_noborder,bits_noborder,CV_8UC1); + for(int y=0;y(y,x)=binaryCode.at(y+1,x+1); + + // now, get the 64bits ids + + int nr = 0; + do + { + ids.push_back(touulong(_bits)); + _bits = rotate(_bits); + nr++; + } while (nr < 4); + return true; + } + + +// bool DictionaryBased::getInnerCode(const cv::Mat& thres_img, int total_nbits, std::vector& ids) +// { +// auto toInt=[](float v){return int(v+0.5);}; +// int bits_a = static_cast(std::sqrt(total_nbits)); +// int bits_a2 = bits_a + 2; +// // Markers are divided in (bits_a+2)x(bits_a+2) regions, of which the inner bits_axbits_a belongs to marker +// // info +// // the external border shoould be entirely black + +// float swidth = float(thres_img.rows) / float(bits_a2); +// for (int y = 0; y < bits_a2; y++) +// { +// int inc = bits_a2 - 1; +// if (y == 0 || y == bits_a2 - 1) +// inc = 1; // for first and last row, check the whole border +// for (int x = 0; x < bits_a2; x += inc) +// { +// cv::Mat square = thres_img(cv::Rect( toInt(float(x) * swidth ), toInt(float(y) * swidth), swidth, swidth)); +// if (cv::countNonZero(square) > (swidth * swidth) / 2) +// return false; // can not be a marker because the border element is not black! +// } +// } + +// // now, +// // get information(for each inner square, determine if it is black or white) + +// // now, +// cv::Mat _bits = cv::Mat::zeros(bits_a, bits_a, CV_8UC1); +// // get information(for each inner square, determine if it is black or white) + +// for (int y = 0; y < bits_a; y++) +// { +// for (int x = 0; x < bits_a; x++) +// { +// int Xstart = toInt(float(x + 1) * swidth); +// int Ystart = toInt(float(y + 1) * swidth); +// cv::Mat square = thres_img(cv::Rect(Xstart, Ystart, swidth, swidth)); +// int nZ = cv::countNonZero(square); +// if (nZ > (swidth * swidth) / 2) +// _bits.at(y, x) = 1; +// } +// } +// // now, get the 64bits ids + +// int nr = 0; +// do +// { +// ids.push_back(touulong(_bits)); +// _bits = rotate(_bits); +// nr++; +// } while (nr < 4); +// return true; +// } + + // convert matrix of (0,1)s in a 64 bit value + uint64_t DictionaryBased::touulong(const cv::Mat& code) + { + std::bitset<64> bits; + int bidx = 0; + for (int y = code.rows - 1; y >= 0; y--) + for (int x = code.cols - 1; x >= 0; x--) + bits[bidx++] = code.at(y, x); + return bits.to_ullong(); + } + cv::Mat DictionaryBased::rotate(const cv::Mat& in) + { + cv::Mat out; + in.copyTo(out); + for (int i = 0; i < in.rows; i++) + { + for (int j = 0; j < in.cols; j++) + { + out.at(i, j) = in.at(in.cols - j - 1, i); + } + } + return out; + } +} diff --git a/thirdparty/aruco-3.1.12/src/dictionary_based.h b/thirdparty/aruco-3.1.12/src/dictionary_based.h new file mode 100644 index 0000000..ec8aff3 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/dictionary_based.h @@ -0,0 +1,73 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef ArucoDictionaryBasedMarkerDetector_H +#define ArucoDictionaryBasedMarkerDetector_H + +#include "dictionary.h" +#include "markerlabeler.h" + +#include +namespace aruco +{ + /**Labeler using a dictionary + */ + class DictionaryBased : public MarkerLabeler + { + public: + virtual ~DictionaryBased() + { + } + // first, dictionary, second the maximum correction rate [0,1]. If 0,no correction, if 1, maximum allowed + // correction + void setParams(const Dictionary& dic, float max_correction_rate); + + // main virtual class to o detection + virtual bool detect(const cv::Mat& in, int& marker_id, int& nRotations,std::string &additionalInfo); + // returns the dictionary name + virtual std::string getName() const; + + virtual int getNSubdivisions()const{return _nsubdivisions;}// + + std::vector getDictionaries()const{return vdic;} + private: + //obfuscate start + bool getInnerCode(const cv::Mat& thres_img, int total_nbits, std::vector& ids); + cv::Mat rotate(const cv::Mat& in); + uint64_t touulong(const cv::Mat& code); + std::vector vdic; + void toMat(uint64_t code, int nbits_sq, cv::Mat& out); + int _nsubdivisions=0; + float _max_correction_rate; + std::string dicttypename; + std::map> nbits_dict; + //obfuscate end + + }; +} +#endif diff --git a/thirdparty/aruco-3.1.12/src/fractaldetector.cpp b/thirdparty/aruco-3.1.12/src/fractaldetector.cpp new file mode 100644 index 0000000..d751512 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractaldetector.cpp @@ -0,0 +1,156 @@ +#include "fractaldetector.h" +#include "opencv2/calib3d/calib3d.hpp" +#include "cvdrawingutils.h" +#include +#include "aruco_cvversioning.h" + +namespace aruco +{ + FractalDetector::FractalDetector() + { + _markerDetector = new MarkerDetector(); + }; + + void FractalDetector::setConfiguration(int params) + { + _fractalLabeler = FractalMarkerLabeler::create((FractalMarkerSet::CONF_TYPES)params); + _params.configuration_type=FractalMarkerSet::getTypeString((FractalMarkerSet::CONF_TYPES)params); + _markerDetector->setMarkerLabeler(_fractalLabeler); + } + + void FractalDetector::setConfiguration(std::string params) + { + _params.configuration_type=params; + _fractalLabeler = FractalMarkerLabeler::create(params); + _markerDetector->setMarkerLabeler(_fractalLabeler); + } + + void FractalDetector::drawMarkers(cv::Mat &img) + { + float size= std::max(1.,float(img.cols)/ 1280.); + for(auto m:Markers) + m.draw(img, cv::Scalar(0, 0, 255), size, false); + } + + void FractalDetector::draw2d(cv::Mat &img){ + if(Markers.size() > 0) + { + std::map id_fmarker = _fractalLabeler->_fractalMarkerSet.fractalMarkerCollection; + + std::vector inners; + std::map> id_innerCorners = _fractalLabeler->_fractalMarkerSet.getInnerCorners(); + for(auto id_innerC:id_innerCorners) + { + std::vector inner3d; + for(auto pt:id_innerC.second) + inners.push_back(cv::Point2f(pt.x,pt.y)); + } + + std::vector points3d; + std::vector points2d; + for(auto m:Markers) + { + for(auto p:id_fmarker[m.id].points) + points3d.push_back(cv::Point2f(p.x, p.y)); + + for(auto p:m) + points2d.push_back(p); + } + + cv::Mat H = cv::findHomography(points3d, points2d); + std::vector dstPnt; + cv::perspectiveTransform(inners, dstPnt, H); + + float size= std::max(1.,float(img.cols)/ 1280.); + for(auto p:dstPnt) + cv::circle(img, p, size, cv::Scalar(0,0,255), CV_FILLED); + } + } + + void FractalDetector::draw3d(cv::Mat &img, bool cube, bool axis){ + if(Tracker.isPoseValid()) + { + cv::Mat rot; + cv::Rodrigues(Tracker.getRvec(), rot); + + std::vector innerPoints3d; + for(auto pt:Tracker.getInner3d()) + { + cv::Mat_ src(3,1,rot.type()); + src(0,0)=pt.x;src(1,0)=pt.y;src(2,0)=pt.z; + + cv::Mat cam_image_point = rot * src + Tracker.getTvec(); + cam_image_point = cam_image_point/cv::norm(cam_image_point); + + if(cam_image_point.at(2,0)>0.85) + innerPoints3d.push_back(pt); + } + //Draw inner points + if(innerPoints3d.size() > 0) + { + std::vector innerPoints; + projectPoints(innerPoints3d, Tracker.getRvec(), Tracker.getTvec(), _cam_params.CameraMatrix, _cam_params.Distorsion, innerPoints); + for(auto p:innerPoints) + circle(img, p, 3 , cv::Scalar(0,0,255),CV_FILLED); + } + //Draw cube + if(cube) + { + std::map id_fmarker = Tracker.getFractal().fractalMarkerCollection; + for(auto m:Markers) + draw3dCube(img, id_fmarker[m.id], _cam_params, 2); + } + + //Draw axes + if(axis) + CvDrawingUtils::draw3dAxis(img, _cam_params, getRvec(), getTvec(), Tracker.getFractal().getFractalSize()/2); + } + } + + void FractalDetector::draw3dCube(cv::Mat& Image, FractalMarker m, const CameraParameters& CP, int lineSize) + { + cv::Mat objectPoints(8, 3, CV_32FC1); + + float msize= m.getMarkerSize(); + float halfSize = msize/2; + + objectPoints.at(0, 0) = -halfSize; + objectPoints.at(0, 1) = -halfSize; + objectPoints.at(0, 2) = 0; + objectPoints.at(1, 0) = halfSize; + objectPoints.at(1, 1) = -halfSize; + objectPoints.at(1, 2) = 0; + objectPoints.at(2, 0) = halfSize; + objectPoints.at(2, 1) = halfSize; + objectPoints.at(2, 2) = 0; + objectPoints.at(3, 0) = -halfSize; + objectPoints.at(3, 1) = halfSize; + objectPoints.at(3, 2) = 0; + + objectPoints.at(4, 0) = -halfSize; + objectPoints.at(4, 1) = -halfSize; + objectPoints.at(4, 2) = msize; + objectPoints.at(5, 0) = halfSize; + objectPoints.at(5, 1) = -halfSize; + objectPoints.at(5, 2) = msize; + objectPoints.at(6, 0) = halfSize; + objectPoints.at(6, 1) = halfSize; + objectPoints.at(6, 2) = msize; + objectPoints.at(7, 0) = -halfSize; + objectPoints.at(7, 1) = halfSize; + objectPoints.at(7, 2) = msize; + + + std::vector imagePoints; + projectPoints(objectPoints, getRvec(), getTvec(), CP.CameraMatrix, CP.Distorsion, imagePoints); + + for (int i = 0; i < 4; i++) + cv::line(Image, imagePoints[i], imagePoints[(i + 1) % 4], cv::Scalar(0, 0, 255, 255), lineSize); + + for (int i = 0; i < 4; i++) + cv::line(Image, imagePoints[i + 4], imagePoints[4 + (i + 1) % 4], cv::Scalar(0, 0, 255, 255), lineSize); + + for (int i = 0; i < 4; i++) + cv::line(Image, imagePoints[i], imagePoints[i + 4], cv::Scalar(0, 0, 255, 255), lineSize); + } +}; diff --git a/thirdparty/aruco-3.1.12/src/fractaldetector.h b/thirdparty/aruco-3.1.12/src/fractaldetector.h new file mode 100644 index 0000000..f9db352 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractaldetector.h @@ -0,0 +1,112 @@ +#ifndef _ARUCO_FractalDetector_H +#define _ARUCO_FractalDetector_H + +#include "markerdetector.h" +#include "fractallabelers/fractallabeler.h" +#include "aruco_export.h" +namespace aruco { + class ARUCO_EXPORT FractalDetector + { + struct ARUCO_EXPORT Params + { + std::string configuration_type; + }; + + public: + FractalDetector(); + + /** + * @brief setConfiguration + * @param configuration fractal id + */ + void setConfiguration(int configuration); + + /** + * @brief setConfiguration + * @param configuration fractal file + */ + void setConfiguration(std::string configuration); + + /** + * @brief setParams + * @param cam_params camera parameters + * @param markerSize in meters + */ + void setParams(const CameraParameters& cam_params, float markerSize) + { + _cam_params = cam_params; + + Tracker.setParams(cam_params, getConfiguration(), markerSize); + } + + // return fractalmarkerset + FractalMarkerSet getConfiguration() + { + return _fractalLabeler->_fractalMarkerSet; + } + + // return true if any marker is detected, false otherwise + bool detect(const cv::Mat& input) + { + Markers = _markerDetector->detect(input); + + if(Markers.size() > 0) return true; + else return false; + } + + // return true if the pose is estimated, false otherwise + bool poseEstimation() + { + if (_cam_params.isValid()) + { + return Tracker.fractalInnerPose(_markerDetector, Markers); + } + else + return false; + } + + // return the rotation vector. Returns an empty matrix if last call to estimatePose returned false + cv::Mat getRvec(){ + return Tracker.getRvec(); + } + // return the translation vector. Returns an empty matrix if last call to estimatePose returned false + cv::Mat getTvec(){ + return Tracker.getTvec(); + } + + void drawImage(cv::Mat &img,cv::Mat &img2); + + // draw borders of markers + void drawMarkers(cv::Mat &img); + + // draw inner corners of markers + void draw2d(cv::Mat &img); + + // draw pose estimation axes + void draw3d(cv::Mat &img, bool cube=true, bool axis=true); + + // draw marker as cube + void draw3dCube(cv::Mat& Image, FractalMarker m, const CameraParameters& CP, int lineSize); + + // return detected markers + std::vector getMarkers() + { + return Markers; + } + + private: + // return image pyramid + std::vector getImagePyramid() + { + return _markerDetector->getImagePyramid(); + } + + std::vector Markers; //detected markers + FractalPoseTracker Tracker; + Params _params; + CameraParameters _cam_params; //Camera parameters + cv::Ptr _fractalLabeler; + cv::Ptr _markerDetector; + }; +} +#endif diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractallabeler.cpp b/thirdparty/aruco-3.1.12/src/fractallabelers/fractallabeler.cpp new file mode 100644 index 0000000..4fc8278 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractallabeler.cpp @@ -0,0 +1,138 @@ +#include "fractallabeler.h" + +#include "../aruco_cvversioning.h" +namespace aruco +{ + + void FractalMarkerLabeler::setConfiguration(const FractalMarkerSet& fractMarkerSet) { + _fractalMarkerSet = fractMarkerSet; + } + + bool FractalMarkerLabeler::detect(const cv::Mat& in, int& marker_id, int& nRotations, std::string &additionalInfo) + { + assert(in.rows == in.cols); + cv::Mat grey; + if (in.type() == CV_8UC1) + grey = in; + else + cv::cvtColor(in, grey, CV_BGR2GRAY); + // threshold image + cv::threshold(grey, grey, 125, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); + + std::map > nbits_innerCodes; + + for(auto bitsids:_fractalMarkerSet.nbits_fractalMarkerIDs){ + + int nbits = bitsids.first; + std::vector innerCodes; + getInnerCode(grey, nbits, innerCodes); + + if (innerCodes.size()>0){ + if (sum(innerCodes[0])[0]!=0){ + nbits_innerCodes[nbits]=innerCodes; + } + } + } + + if ( nbits_innerCodes.size()==0)return false; + + //check if any dictionary recognizes it + for(auto bit_innerCodes:nbits_innerCodes){ + + uint32_t nb = bit_innerCodes.first; + auto innerCodes = bit_innerCodes.second; + + for (int i = 0; i < 4; i++) + { + if (_fractalMarkerSet.isFractalMarker(innerCodes[i], nb, marker_id)) + { + // is in the set? + nRotations = i; // how many rotations are and its id + return true; // bye bye + } + } + } + return false; + } + + + std::string FractalMarkerLabeler::getName() const + { + return "fractal";; + } + + bool FractalMarkerLabeler::getInnerCode(const cv::Mat& thres_img, int total_nbits, std::vector& innerCodes) + { + int bits_noborder = static_cast(std::sqrt(total_nbits)); + int bits_withborder = bits_noborder + 2; + // Markers are divided in (bits_a+2)x(bits_a+2) regions, of which the inner bits_axbits_a belongs to marker + // info + // the external border shoould be entirely black + cv::Mat nonZeros(bits_withborder,bits_withborder,CV_32SC1); + cv::Mat nValues(bits_withborder,bits_withborder,CV_32SC1); + nonZeros.setTo(cv::Scalar::all(0)); + nValues.setTo(cv::Scalar::all(0)); + for (int y = 0; y < thres_img.rows; y++) + { + const uchar *ptr=thres_img.ptr(y); + int my= float(bits_withborder)*float(y)/ float(thres_img.rows); + for (int x = 0; x < thres_img.cols; x++) + { + int mx= float(bits_withborder)*float(x)/ float(thres_img.cols); + if( ptr[x]>125) + nonZeros.at(my,mx)++; + nValues.at(my,mx)++; + } + } + cv::Mat binaryCode(bits_withborder,bits_withborder,CV_8UC1); + //now, make the theshold + for(int y=0;y(y,x)>nValues.at(y,x)/2) + binaryCode.at(y,x)=1; + else + binaryCode.at(y,x)=0; + } + + //check if border is completely black + for (int y = 0; y < bits_withborder; y++) + { + int inc = bits_withborder - 1; + if (y == 0 || y == bits_withborder - 1) + inc = 1; // for first and last row, check the whole border + for (int x = 0; x < bits_withborder; x += inc) + if (binaryCode.at(y,x)!=0 ) return false; + } + + //take the inner code + cv::Mat _bits(bits_noborder,bits_noborder,CV_8UC1); + for(int y=0;y(y,x)=binaryCode.at(y+1,x+1); + + // now, get the 64bits ids + int nr = 0; + do + { + innerCodes.push_back(_bits); + _bits = rotate(_bits); + nr++; + } while (nr < 4); + return true; + } + + cv::Mat FractalMarkerLabeler::rotate(const cv::Mat& in) + { + cv::Mat out; + in.copyTo(out); + for (int i = 0; i < in.rows; i++) + { + for (int j = 0; j < in.cols; j++) + { + out.at(i, j) = in.at(in.cols - j - 1, i); + } + } + return out; + } +} + diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractallabeler.h b/thirdparty/aruco-3.1.12/src/fractallabelers/fractallabeler.h new file mode 100644 index 0000000..4920736 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractallabeler.h @@ -0,0 +1,50 @@ +#include "../markerlabeler.h" +#include "fractalposetracker.h" + +namespace aruco +{ + class FractalMarkerLabeler : public MarkerLabeler + { + public: + static cv::Ptr create(std::string params) + { + FractalMarkerSet fractalMarkerSet = FractalMarkerSet::load(params); + FractalMarkerLabeler* fml=new FractalMarkerLabeler(); + fml->setConfiguration(fractalMarkerSet); + return fml; + } + + static cv::Ptr create(FractalMarkerSet::CONF_TYPES conf) + { + FractalMarkerSet fractalMarkerSet = FractalMarkerSet::loadPredefined(conf); + FractalMarkerLabeler* fml=new FractalMarkerLabeler(); + fml->setConfiguration(fractalMarkerSet); + return fml; + } + + void setConfiguration(const FractalMarkerSet& fractMarkerSet); + + static bool isFractalDictionaryFile(const std::string &path); + + virtual ~FractalMarkerLabeler() + { + } + + bool load(const std::string &path); + + // returns the configuration name + std::string getName() const; + + // main virtual class to o detection + bool detect(const cv::Mat& in, int& marker_id, int& nRotations,std::string &additionalInfo); + + int getNSubdivisions()const{return (sqrt(_fractalMarkerSet.nBits())+2);} + + FractalMarkerSet _fractalMarkerSet; + + private: + bool getInnerCode(const cv::Mat& thres_img, int total_nbits, std::vector& ids); + cv::Mat rotate(const cv::Mat& in); + }; +} + diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarker.cpp b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarker.cpp new file mode 100644 index 0000000..0c79cca --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarker.cpp @@ -0,0 +1,76 @@ +#include "fractalmarker.h" + +#include + + +namespace aruco +{ + FractalMarker::FractalMarker() + { + + } + + FractalMarker::FractalMarker(int id, cv::Mat m, std::vector corners, std::vector id_submarkers) + { + this->id = id; + this->_M = m; + for(auto p:corners) + push_back(p); + + _submarkers = id_submarkers; + _mask = cv::Mat::ones(m.size(), CV_8UC1); + } + + void FractalMarker::addSubFractalMarker(FractalMarker submarker) + { + float bitSize = (at(1).x - at(0).x) / int(mat().cols+2); + float nsubBits = (submarker.at(1).x - submarker.at(0).x) / bitSize; + + int x_min = int(round(submarker[0].x / bitSize + mat().cols/2)); + int x_max = x_min + nsubBits; + int y_min = int(round(-submarker[0].y / bitSize + mat().cols/2)); + int y_max = y_min + nsubBits; + + for(int y=y_min; y(y,x)=0; + } + } + } + + std::vector FractalMarker::findInnerCorners() + { + int nBitsSquared = int(sqrt(mat().total())); + float bitSize = getMarkerSize() / (nBitsSquared+2); + + //Set submarker pixels (=1) and add border + cv::Mat marker; + mat().copyTo(marker); + marker += -1 * (mask()-1); + cv::Mat markerBorder; + copyMakeBorder(marker, markerBorder, 1,1,1,1,cv::BORDER_CONSTANT,0); + + //Get inner corners + std::vector innerCorners; + for(int y=0; y< markerBorder.rows-1; y++) + { + for(int x=0; x< markerBorder.cols-1; x++) + { + + if( ((markerBorder.at(y, x) == markerBorder.at(y+1, x+1)) && + (markerBorder.at(y, x) != markerBorder.at(y, x+1) || + markerBorder.at(y, x) != markerBorder.at(y+1, x))) + + || + + ((markerBorder.at(y, x+1) == markerBorder.at(y+1, x)) && + (markerBorder.at(y, x+1) != markerBorder.at(y, x) || + markerBorder.at(y, x+1) != markerBorder.at(y+1, x+1))) + ) + innerCorners.push_back(cv::Point3f(x-nBitsSquared/2.f, -(y-nBitsSquared/2.f), 0) * bitSize); + } + } + return innerCorners; + } +} + diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarker.h b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarker.h new file mode 100644 index 0000000..f2f9808 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarker.h @@ -0,0 +1,64 @@ +#ifndef FRACTALMARKER_H +#define FRACTALMARKER_H + +#include +#include +#include +#include "../markermap.h" + +namespace aruco +{ + class FractalMarker : public aruco::Marker3DInfo + { + public: + FractalMarker(); + FractalMarker(int id, cv::Mat m, std::vector corners, std::vector id_submarkers); + + //Add new submarker + void addSubFractalMarker(FractalMarker submarker); + + //Find inner corners + std::vector findInnerCorners(); + + //Marker MAT + const cv::Mat mat() const + { + return _M; + } + + //Marker mask (mask applied to submarkers) + const cv::Mat mask() const + { + return _mask; + } + + //Total number of bits + int nBits() + { + return _M.total(); + } + + //Submarkers ids + std::vector subMarkers() + { + return _submarkers; + } + + //Get inner corners + std::vector getInnerCorners() + { + if(innerCorners.empty()) + innerCorners = findInnerCorners(); + + return innerCorners; + } + + private: + cv::Mat _M; + cv::Mat _mask; + std::vector _submarkers; //id subfractalmarkers + std::vector innerCorners; + }; +} + +#endif // FRACTALMARKER_H diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarkerset.cpp b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarkerset.cpp new file mode 100644 index 0000000..ea563f9 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarkerset.cpp @@ -0,0 +1,828 @@ +#include "fractalmarkerset.h" +#include +#include + +namespace aruco +{ + +FractalMarkerSet FractalMarkerSet::load(std::string info){ + if (isPredefinedConfigurationString(info)) + return loadPredefined(info); + else return readFromFile(info); +} + +FractalMarkerSet FractalMarkerSet::loadPredefined(std::string type){ + return loadPredefined(getTypeFromString(type)); +} + +FractalMarkerSet FractalMarkerSet::loadPredefined(CONF_TYPES type){ + FractalMarkerSet fms; + + switch(type){ + case FRACTAL_2L_6:{ + unsigned char _conf_2L_6[] = { + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0xab, 0xaa, 0xaa, 0xbe, 0xab, 0xaa, 0xaa, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0xab, 0xaa, 0xaa, 0x3e, 0xab, 0xaa, 0xaa, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0xab, 0xaa, 0xaa, 0x3e, 0xab, 0xaa, 0xaa, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0xab, 0xaa, 0xaa, 0xbe, 0xab, 0xaa, 0xaa, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + unsigned int _conf_2L_6_len = 272; + + std::stringstream stream; + stream.write((char*) _conf_2L_6, sizeof(unsigned char)*_conf_2L_6_len); + _fromStream(fms, stream); + }break; + case FRACTAL_3L_6:{ + unsigned char _conf_3L_6[] = { + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xb7, 0x6d, 0xdb, 0xbe, 0xb7, 0x6d, 0xdb, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x6d, 0xdb, 0x3e, 0xb7, 0x6d, 0xdb, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x6d, 0xdb, 0x3e, 0xb7, 0x6d, 0xdb, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x6d, 0xdb, 0xbe, 0xb7, 0x6d, 0xdb, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x49, 0x12, 0xbe, + 0x25, 0x49, 0x12, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x25, 0x49, 0x12, 0x3e, + 0x25, 0x49, 0x12, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x25, 0x49, 0x12, 0x3e, + 0x25, 0x49, 0x12, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x25, 0x49, 0x12, 0xbe, + 0x25, 0x49, 0x12, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned int _conf_3L_6_len = 480; + + std::stringstream stream; + stream.write((char*) _conf_3L_6, sizeof(unsigned char)*_conf_3L_6_len); + _fromStream(fms, stream); + }break; + case FRACTAL_4L_6:{ + unsigned char _conf_4L_6[] = { + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, + 0x00, 0xef, 0xee, 0xee, 0xbe, 0xef, 0xee, 0xee, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0xef, 0xee, 0xee, 0x3e, 0xef, 0xee, 0xee, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0xef, 0xee, 0xee, 0x3e, 0xef, 0xee, 0xee, 0xbe, 0x00, 0x00, 0x00, + 0x00, 0xef, 0xee, 0xee, 0xbe, 0xef, 0xee, 0xee, 0xbe, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x64, 0x00, 0x00, 0x00, 0xcd, 0xcc, 0x4c, 0xbe, 0xcd, 0xcc, 0x4c, + 0x3e, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xcc, 0x4c, 0x3e, 0xcd, 0xcc, 0x4c, + 0x3e, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xcc, 0x4c, 0x3e, 0xcd, 0xcc, 0x4c, + 0xbe, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xcc, 0x4c, 0xbe, 0xcd, 0xcc, 0x4c, + 0xbe, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x89, 0x88, 0x88, 0xbd, 0x89, 0x88, 0x88, 0x3d, 0x00, 0x00, 0x00, + 0x00, 0x89, 0x88, 0x88, 0x3d, 0x89, 0x88, 0x88, 0x3d, 0x00, 0x00, 0x00, + 0x00, 0x89, 0x88, 0x88, 0x3d, 0x89, 0x88, 0x88, 0xbd, 0x00, 0x00, 0x00, + 0x00, 0x89, 0x88, 0x88, 0xbd, 0x89, 0x88, 0x88, 0xbd, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned int _conf_4L_6_len = 713; + + std::stringstream stream; + stream.write((char*) _conf_4L_6, sizeof(unsigned char)*_conf_4L_6_len); + _fromStream(fms, stream); + }break; + case FRACTAL_5L_6:{ + unsigned char _conf_5L_6[] = { + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, + 0x00, 0x4f, 0xec, 0xc4, 0xbe, 0x4f, 0xec, 0xc4, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0xec, 0xc4, 0x3e, 0x4f, 0xec, 0xc4, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0xec, 0xc4, 0x3e, 0x4f, 0xec, 0xc4, 0xbe, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0xec, 0xc4, 0xbe, 0x4f, 0xec, 0xc4, 0xbe, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x7d, 0xcb, 0x37, 0xbe, 0x7d, 0xcb, + 0x37, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xcb, 0x37, 0x3e, 0x7d, 0xcb, + 0x37, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xcb, 0x37, 0x3e, 0x7d, 0xcb, + 0x37, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xcb, 0x37, 0xbe, 0x7d, 0xcb, + 0x37, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xd9, 0x89, + 0x9d, 0xbd, 0xd9, 0x89, 0x9d, 0x3d, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x89, + 0x9d, 0x3d, 0xd9, 0x89, 0x9d, 0x3d, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x89, + 0x9d, 0x3d, 0xd9, 0x89, 0x9d, 0xbd, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x89, + 0x9d, 0xbd, 0xd9, 0x89, 0x9d, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x21, 0x0d, 0xd2, 0xbc, 0x21, 0x0d, + 0xd2, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x21, 0x0d, 0xd2, 0x3c, 0x21, 0x0d, + 0xd2, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x21, 0x0d, 0xd2, 0x3c, 0x21, 0x0d, + 0xd2, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x21, 0x0d, 0xd2, 0xbc, 0x21, 0x0d, + 0xd2, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned int _conf_5L_6_len = 898; + + std::stringstream stream; + stream.write((char*) _conf_5L_6, sizeof(unsigned char)*_conf_5L_6_len); + _fromStream(fms, stream); + }break; + case CUSTOM: + throw cv::Exception(-1,"CUSTOM type is only set by loading from file","FractalMarkerSet::loadPredefined","fractalmarkerset.h",-1); + break; + + default: throw cv::Exception(9001, "Invalid Dictionary type requested", "Dictionary::loadPredefined", __FILE__, __LINE__); + + } + return fms; +} + +bool FractalMarkerSet::isPredefinedConfigurationString(std::string str) { + return getTypeFromString(str)!=CUSTOM; +} + +std::string FractalMarkerSet::getTypeString(CONF_TYPES t) { + switch(t){ + case FRACTAL_2L_6:return "FRACTAL_2L_6"; + case FRACTAL_3L_6:return "FRACTAL_3L_6"; + case FRACTAL_4L_6:return "FRACTAL_4L_6"; + case FRACTAL_5L_6:return "FRACTAL_5L_6"; + case CUSTOM:return "CUSTOM"; + }; + return "Non valid CONF_TYPE"; + } + +FractalMarkerSet::CONF_TYPES FractalMarkerSet::getTypeFromString(std::string str) { + if (str=="FRACTAL_2L_6") return FRACTAL_2L_6; + if (str=="FRACTAL_3L_6") return FRACTAL_3L_6; + if (str=="FRACTAL_4L_6") return FRACTAL_4L_6; + if (str=="FRACTAL_5L_6") return FRACTAL_5L_6; + else return CUSTOM; + } + +std::vector FractalMarkerSet::getConfigurations() { + return {"FRACTAL_2L_6","FRACTAL_3L_6","FRACTAL_4L_6","FRACTAL_5L_6"}; +} + +void FractalMarkerSet::_toStream(FractalMarkerSet &configuration, std::ostream &str) +{ + str.write((char*)&configuration.mInfoType,sizeof(mInfoType)); + str.write((char*)&configuration._nmarkers,sizeof(_nmarkers)); + str.write((char*)&configuration._idExternal,sizeof(_idExternal)); + + for(auto fractal:configuration.fractalMarkerCollection) + { + //ID + int id = fractal.first; + str.write((char*)&id,sizeof(id)); + //NBITS + int nbits = fractal.second.nBits(); + str.write((char*)&nbits,sizeof(nbits)); + //CORNERS + std::vector corners = fractal.second.points; + str.write((char*)&corners[0],sizeof(cv::Point3f)*4); + //MAT + cv::Mat mat = fractal.second.mat(); + str.write((char*)mat.data, mat.elemSize() * mat.total()); + //SUBMARKERS + std::vector sub = fractal.second.subMarkers(); + int nsub = sub.size(); + str.write((char*)&nsub,sizeof(nsub)); + str.write((char*)&sub[0],sizeof(int)*nsub); + } +} + +void FractalMarkerSet::_fromStream(FractalMarkerSet &configuration, std::istream &str) +{ + str.read((char*)&configuration.mInfoType,sizeof(mInfoType)); + str.read((char*)&configuration._nmarkers,sizeof(_nmarkers)); + str.read((char*)&configuration._idExternal,sizeof(_idExternal)); + + for(int i=0; i corners(4); + str.read((char*)&corners[0],sizeof(cv::Point3f)*4); + //MAT + cv::Mat mat; + mat.create(sqrt(nbits), sqrt(nbits), CV_8UC1); + str.read((char*)mat.data, mat.elemSize() * mat.total()); + //SUBMARKERS + int nsub; + str.read((char*)&nsub,sizeof(nsub)); + std::vector id_submarkers(nsub); + if (nsub > 0) + str.read((char*)&id_submarkers[0],sizeof(int)*nsub); + + FractalMarker fractalMarker = FractalMarker(id, mat, corners, id_submarkers); + configuration.nbits_fractalMarkerIDs[mat.total()].push_back(fractalMarker.id); + configuration.fractalMarkerCollection[fractalMarker.id] = fractalMarker; + } + + //Add subfractals + for(auto &id_marker:configuration.fractalMarkerCollection) + { + FractalMarker &marker = id_marker.second; + for(auto id:id_marker.second.subMarkers()) + marker.addSubFractalMarker(configuration.fractalMarkerCollection[id]); + } +} + +void FractalMarkerSet::create(std::vector> regionsConfig, float pixSize) +{ + if(pixSize == -1) + { + mInfoType = NORM; + pixSize = 1; + } + else + mInfoType = PIX; + + _nmarkers = regionsConfig.size(); + _idExternal = 0; + + std::vector submarkers; + submarkers.clear(); + float pix = 0; + for(int n=regionsConfig.size()-1; n>=0; n--) + { + int nVal = regionsConfig[n].first; + int kVal = regionsConfig[n].second; + + cv::Mat mat = configureMat(nVal, kVal); + + pix = (nVal + 2) * pixSize; + std::vector corners = { cv::Point3f(-pix/2, pix/2, 0.), + cv::Point3f(pix/2, pix/2, 0.), + cv::Point3f(pix/2, -pix/2, 0.), + cv::Point3f(-pix/2, -pix/2, 0.) + }; + + FractalMarker fractal = FractalMarker(n, mat, corners, submarkers); + fractalMarkerCollection[n] = fractal; + submarkers.clear(); + submarkers.push_back(n); + + + float kValSup = regionsConfig[n-1].second - 2; + float newP = (nVal+2)/(kValSup); + pixSize = newP * pixSize; + } + + //Normalize corners. Fractal marker: (-1,1,0)(1,1,0)(1,-1,0)(-1,-1,0) + if(isNormalize()){ + for(auto &m:fractalMarkerCollection){ + for(auto &c:m.second.points) + { + c.x/=pix/2; + c.y/=pix/2; + } + } + } +} + +cv::Mat FractalMarkerSet::configureMat(int nVal, int kVal, int maxIter) +{ + //Pixels to configure (n-k region) + std::vector pixels; + for(int y=0; y= kVal+(nVal-kVal)/2) + || (y <= ((nVal-kVal)/2)-1 || y >= kVal+(nVal-kVal)/2)){ + pixels.push_back(cv::Point2i(x, y)); + } + } + } + + int dst_mkr = 0; + int dst_set=0; + + cv::Mat m; + int markerIters = 0; + do{ + std::vector conf_pixels = pixels; + m = cv::Mat::ones(nVal, nVal, CV_8UC1); + +// struct timespec ts; +// clock_gettime(CLOCK_MONOTONIC, &ts); +// srand((time_t)ts.tv_nsec); + + //Random delete half of pixels + int npix = conf_pixels.size()/2; + for(int i=0; i(p.y,p.x) = 0; + + //Check marker distance to itself + int new_dst_mkr = dstMarker(m); + if(new_dst_mkr > dst_mkr) + { + //Check marker distance to set + int new_dst_set = dstMarkerToFractalDict(m); + if(new_dst_set > dst_set) + { + dst_mkr = new_dst_mkr; + dst_set = new_dst_set; + } + } + } while(markerIters++(y,x)=0; + } + } + + return m; +} + +/* + * Calculate minimum distance between marker 'm' and the 4 rotations of each word in the dictionary + */ +int FractalMarkerSet::dstMarkerToFractalDict(cv::Mat m){ + int HDist = m.cols * m.rows; //distancia maxima + int HDistTemp; + + for(auto marker:fractalMarkerCollection) { + if(marker.second.nBits() == m.total()) + { + HDistTemp = dstMarkerToMarker(marker.second.mat(), m); + + if(HDistTemp == 0) + return 0; + else if(HDistTemp < HDist) + HDist = HDistTemp; + } + } + return HDist; +} + +/* + * Calculate minimum distance between marker1 'm' and the 4 rotations of marker2 'm2' + */ +auto rotate=[](const cv::Mat &m) { + cv::Mat out; + m.copyTo(out); + for (int i = 0; i < m.rows; i++) { + for (int j = 0; j < m.cols; j++) { + out.at< char >(i, j) = m.at< char >(m.cols - j - 1, i); + } + } + return out; +}; + +int FractalMarkerSet::dstMarker(const cv::Mat m) { + + int HDist = m.cols * m.rows;//maximum distance + int HDistTemp; + + cv::Mat rot; + m.copyTo(rot); + + + for(int i=0; i<3; i++) + { + cv::Mat diff; + rot=rotate(rot); + cv::compare(rot, m, diff, cv::CMP_NE); + HDistTemp = cv::countNonZero(diff); + if(HDistTemp < HDist) + HDist = HDistTemp; + } + return HDist; +} + +int FractalMarkerSet::dstMarkerToMarker(const cv::Mat m1, const cv::Mat m2) { + + int HDist = m2.cols * m2.rows;//maximum distance + int HDistTemp; + + for(int i=0; i<4; i++) + { + cv::Mat diff; + cv::compare(rotate(m2), m1, diff, cv::CMP_NE); + HDistTemp = cv::countNonZero(diff); + if(HDistTemp < HDist) + HDist = HDistTemp; + } + + return HDist; +} + + +FractalMarkerSet FractalMarkerSet::readFromFile(std::string path) +{ + cv::FileStorage fs; + try + { + fs.open(path, cv::FileStorage::READ); + } + catch (std::exception& ex) + { + throw cv::Exception(81818, "FractalMarkerSet::readFromFile", ex.what() + std::string(" file=)") + path, __FILE__, __LINE__); + } + + FractalMarkerSet configuration; + + fs["mInfoType"] >> configuration.mInfoType; + fs["fractal_levels"] >> configuration._nmarkers; + fs["fractal_external_id"] >> configuration._idExternal; + + cv::FileNode markers = fs["markers"]; + int i = 0; + for (cv::FileNodeIterator it = markers.begin(); it != markers.end(); ++it, i++) + { + int id = (*it)["id"]; + std::vector bits; + + std::vector corners; + std::vector submarkers_id; + + cv::FileNode bitsMarker = (*it)["bits"]; + for (cv::FileNodeIterator itb = bitsMarker.begin(); itb != bitsMarker.end(); ++itb) + { + bits.push_back(*itb); + } + cv::Mat m = cv::Mat(sqrt(bits.size()), sqrt(bits.size()), CV_32SC1); + memcpy(m.data, bits.data(), bits.size()*sizeof(int)); + + m.convertTo(m,CV_8UC1); + + cv::FileNode cornersFractal = (*it)["corners"]; + for (cv::FileNodeIterator itp = cornersFractal.begin(); itp != cornersFractal.end(); ++itp) + { + std::vector coordinates3d; + (*itp) >> coordinates3d; + if (coordinates3d.size() != 3) + throw cv::Exception(81818, "FractalMarkerSet::readFromFile", "invalid file type 3", __FILE__, __LINE__); + cv::Point3f point(coordinates3d[0], coordinates3d[1], coordinates3d[2]); + corners.push_back(point); + } + + cv::FileNode submarkersID = (*it)["submarkers_id"]; + for (cv::FileNodeIterator itm = submarkersID.begin(); itm != submarkersID.end(); ++itm) + { + submarkers_id.push_back((*itm)); + } + + FractalMarker fractalMarker = FractalMarker(id, m, corners, submarkers_id); + configuration.nbits_fractalMarkerIDs[m.total()].push_back(fractalMarker.id); + configuration.fractalMarkerCollection[fractalMarker.id] = fractalMarker; + } + + //Add subfractals + for(auto &id_marker:configuration.fractalMarkerCollection) + { + FractalMarker &marker = id_marker.second; + for(auto id:id_marker.second.subMarkers()) + marker.addSubFractalMarker(configuration.fractalMarkerCollection[id]); + } + + return configuration; +} + + void FractalMarkerSet::saveToFile(cv::FileStorage& fs) + { + fs << "codeid" << "fractalmarkers"; + fs << "mInfoType" << mInfoType; + fs << "fractal_levels" << _nmarkers; + fs << "fractal_external_id" << _idExternal; + fs << "markers" + << "["; + for(auto id_marker:fractalMarkerCollection) { + + FractalMarker marker = id_marker.second; + + fs << "{:" + << "id" << (int)id_marker.first; + + fs << "bits" + << "[:"; + cv::Mat m = marker.mat(); + for (int y = 0; y < m.cols; y++) + for (int x = 0; x < m.rows; x++) + if (m.at(y, x) == 2) + fs << 0; + else + fs << m.at(y, x); + fs << "]"; + + fs << "corners" + << "[:"; + for (auto corner:marker.points) + fs << corner; + fs << "]"; + + fs << "submarkers_id" + << "[:"; + for (auto idsub:marker.subMarkers()) + fs << (int) idsub; + fs << "]"; + + fs << "}"; + } + fs << "]"; + } + + bool FractalMarkerSet::isFractalMarker(cv::Mat &m, int nbits, int& markerid) + { + + bool found = false; + + for(auto id:nbits_fractalMarkerIDs[nbits]){ + FractalMarker fm = fractalMarkerCollection[id]; + + //Apply mask to substract submarkers + cv::Mat m2; + m.copyTo(m2, fm.mask()); + + //Code without submarkers == fractal marker? + if (cv::countNonZero(m2 != fm.mat()) == 0){ + found = true; + + //Change new code!! + ////////////// + markerid = fm.id; + ///////////// + + break; + } + } + + return found; + } + + FractalMarkerSet FractalMarkerSet::convertToMeters(float fractalSizeM) + { + if (!(isExpressedInPixels() || isNormalize())) + throw cv::Exception(-1, "The FractalMarkers are not expressed in pixels", "FractalMarkerSet::convertToMeters", __FILE__, + __LINE__); + + FractalMarkerSet BInfo(*this); + BInfo.mInfoType = FractalMarkerSet::METERS; + + // now, get the size of a pixel, and change scale + float pixSizeM = fractalSizeM / float(BInfo.getFractalSize()); + + for (size_t i=0; i < BInfo.fractalMarkerCollection.size(); i++) + { + //Convert to meters the position fractal marker + for (int c = 0; c < 4; c++) + { + BInfo.fractalMarkerCollection[i][c] *= pixSizeM; + } + } + return BInfo; + } + + FractalMarkerSet FractalMarkerSet::normalize() + { + if (!(isExpressedInPixels() || isExpressedInMeters())) + throw cv::Exception(-1, "The FractalMarkers are not expressed in pixels or meters", "FractalMarkerSet::convertToMeters", __FILE__, + __LINE__); + + FractalMarkerSet BInfo(*this); + BInfo.mInfoType = FractalMarkerSet::NORM; + + float currentHalfSize = BInfo.getFractalSize()/2.f; + + for (size_t i=0; i < BInfo.fractalMarkerCollection.size(); i++) + { + //Normalize the position fractal marker + for (size_t c = 0; c < 4; c++) + { + BInfo.fractalMarkerCollection[i][c] /= currentHalfSize; + } + } + + return BInfo; + } + + std::map> FractalMarkerSet::getInnerCorners() + { + std::map> id_innerCorners; + + for(auto id_fm:fractalMarkerCollection) + { + int id = id_fm.first; + FractalMarker fm = id_fm.second; + + for(auto ic:fm.getInnerCorners()) + id_innerCorners[id].push_back(ic); //Conversion + + } + return id_innerCorners; + } + + cv::Mat FractalMarkerSet::getFractalMarkerImage(int pixSize, bool border) + { + if(fractalMarkerCollection.size()<1) + throw cv::Exception(9001, "There is not any fractal marker loaded", + "FractalMarkerSet::getFractalMarkerImage", __FILE__, __LINE__); + + //Smallest fractal marker + FractalMarker innerM = (--fractalMarkerCollection.end())->second; + float bitSize = innerM.getMarkerSize() / (pixSize * (sqrt(innerM.nBits())+2)); + + FractalMarker externMarker = fractalMarkerCollection[_idExternal]; + float markerSize = externMarker.getMarkerSize()/bitSize; + float markerBitSize = markerSize/(sqrt(externMarker.nBits())+2); + cv::Mat img=cv::Mat::zeros(markerSize, markerSize, CV_8U); + + //Asign value pixels + cv::Mat m = externMarker.mat(); + + for (int y = m.cols-1; y >=0 ; y--) + for (int x = m.cols-1; x >=0; x--){ + if (m.at(y,x) == 1){ + cv::Range val1 = cv::Range((1+y)*markerBitSize,(y+2)*markerBitSize); + cv::Range val2 = cv::Range((1+x)*markerBitSize,(x+2)*markerBitSize); + + cv::Mat bit_pix=img(val1, val2); + bit_pix.setTo(cv::Scalar::all(255)); + } + } + + for(auto idSubmarker:externMarker.subMarkers()){ + std::vector idds; + idds.push_back(idSubmarker); + while(!idds.empty()) + { + idSubmarker = idds.back(); + idds.pop_back(); + + FractalMarker submarker = fractalMarkerCollection[idSubmarker]; + + cv::Mat m = submarker.mat(); + cv::Point3f coord = submarker.points[0]; + + float markerSize = submarker.getMarkerSize()/bitSize; + float markerBitSize = markerSize/(sqrt(submarker.nBits())+2); + + //Get position inside fractal marker + float offsetX = fabs(coord.x - externMarker.points[0].x)/bitSize ; + float offsetY = fabs(coord.y - externMarker.points[0].y)/bitSize; + + //Asign value pixels + for (int y = m.cols-1; y >=0 ; y--) + for (int x = m.cols-1; x >=0; x--){ + if (m.at(y,x) == 1){ + cv::Range val1 = cv::Range((1+y)*markerBitSize + offsetY,(2+y)*markerBitSize + offsetY); + cv::Range val2 = cv::Range((1+x)*markerBitSize + offsetX,(2+x)*markerBitSize + offsetX); + + cv::Mat bit_pix=img(val1, val2); + bit_pix.setTo(cv::Scalar::all(255)); + } + } + + //Add submarkers to draw + for(auto idsm:submarker.subMarkers()) + idds.push_back(idsm); + } + } + + if(border) + copyMakeBorder(img,img,markerBitSize,markerBitSize,markerBitSize,markerBitSize,cv::BORDER_CONSTANT,cv::Scalar::all(255)); + + return img; + } +}; + diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarkerset.h b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarkerset.h new file mode 100644 index 0000000..044cad0 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalmarkerset.h @@ -0,0 +1,140 @@ +#include "fractalmarker.h" + +#include +#include +#include +#include "aruco_export.h" + +namespace aruco { + class ARUCO_EXPORT FractalMarkerSet{ + public: + enum CONF_TYPES: + uint64_t{ + FRACTAL_2L_6 = 0, + FRACTAL_3L_6 = 1, + FRACTAL_4L_6 = 2, + FRACTAL_5L_6 = 3, + CUSTOM=4 // for used defined dictionaries (using load). + }; + + /** create set of markers + * @brief create + * @param regionsConfig {N(f1),K(f1)}{N(f2):K(f2)}...{N(fn):K(fn)} + * @param pixSize + */ + void create(std::vector> regionsConfig, float pixSize); + + /** configure bits of inner marker + * @brief configureMat + * @param nVal N region + * @param kVal K region + * @param maxIter Number of iteration + * @return Mat configurated marker + */ + cv::Mat configureMat(int nVal, int kVal, int maxIter=10000); + // computes the distance of a marker to itself + int dstMarker(const cv::Mat m); + + // computes distance between marker to marker + int dstMarkerToMarker(const cv::Mat m1, const cv::Mat m2); + + // computes distance between marker to set of markers + int dstMarkerToFractalDict(cv::Mat m); + + // saves to a binary stream + static void _toStream(FractalMarkerSet &configuration, std::ostream &str); + + // load from a binary stream + static void _fromStream(FractalMarkerSet &configuration, std::istream &str); + + static bool isPredefinedConfigurationString(std::string str); + + static std::string getTypeString(FractalMarkerSet::CONF_TYPES t); + + static CONF_TYPES getTypeFromString(std::string str); + + static FractalMarkerSet load(std::string info); + + static FractalMarkerSet loadPredefined(std::string info); + + static FractalMarkerSet loadPredefined(CONF_TYPES info); + + static FractalMarkerSet readFromFile(std::string path); + + // saves configuration to a text file + void saveToFile(cv::FileStorage& fs); + + //Fractal configuration. id_marker + std::map fractalMarkerCollection; + //Nbits_idmarkers + std::map> nbits_fractalMarkerIDs ; + + enum Fractal3DInfoType + { + NONE = -1, + PIX = 0, + METERS = 1, + NORM = 2 + }; // indicates if the data in Fractal is expressed in meters or in pixels + + /**Indicates if the corners are expressed in meters + */ + bool isExpressedInMeters() const + { + return mInfoType == METERS; + } + /**Indicates if the corners are expressed in meters + */ + bool isExpressedInPixels() const + { + return mInfoType == PIX; + } + /**Indicates if the corners are normalized. -1..1 external marker + */ + bool isNormalize() const + { + return mInfoType == NORM; + } + + //Normalize fractal marker. The corners will go on to take the values (-1,1)(1,1),(1,-1)(-1,-1) + FractalMarkerSet normalize(); + + //Convert marker to meters + FractalMarkerSet convertToMeters(float fractalSize_meters); + + static std::vector getConfigurations(); + + //Get fractal size (external marker) + float getFractalSize() const + { + FractalMarker externalMarker = fractalMarkerCollection.at(_idExternal); + return externalMarker.getMarkerSize(); + } + + //Get number of bits (external marker) + int nBits() const + { + FractalMarker externalMarker = fractalMarkerCollection.at(_idExternal); + return externalMarker.nBits(); + } + + // Check if m is a inner marker, and get its id. + bool isFractalMarker(cv::Mat &m, int nbits, int&id); + + // Get all inners corners + std::map> getInnerCorners(); + + cv::Mat getFractalMarkerImage(int pixSize, bool border=false); + + // variable indicates if the data is expressed in meters or in pixels or are normalized + int mInfoType;/* -1:NONE, 0:PIX, 1:METERS, 2:NORMALIZE*/ + + private: + // Number of levels + int _nmarkers; + //ID external marker + int _idExternal=0; + //Configuration dictionary + std::string config; + }; +} diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractalposetracker.cpp b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalposetracker.cpp new file mode 100644 index 0000000..3bb5532 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalposetracker.cpp @@ -0,0 +1,960 @@ +#include "fractalposetracker.h" +#include "../levmarq.h" +#include "../ippe.h" +#include "../aruco_export.h" +#include "../timers.h" + + +#include "opencv2/calib3d/calib3d.hpp" +#include "../aruco_cvversioning.h" + +namespace aruco { + + /* + * KeyPoint cornersubpixel + */ + void kcornerSubPix(const cv::Mat image, std::vector &kpoints) + { + std::vector points; + cv::KeyPoint::convert(kpoints, points); + + cv::Size winSize = cv::Size(4, 4); + cv::Size zeroZone = cv::Size( -1, -1 ); + cv::TermCriteria criteria( cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 12, 0.005); //cv::cornerSubPix(image, points, winSize, zeroZone, criteria); + cornerSubPix(image, points, winSize, zeroZone, criteria); + + //Update kpoints + uint32_t i=0; + for(auto &k:kpoints){ + k.pt = points[i]; + i++; + } + } + + /* + * KeyPoints Filter. Delete kpoints with low response and duplicated. + */ + void kfilter(std::vector &kpoints) + { + float minResp = kpoints[0].response; + float maxResp = kpoints[0].response; + for (auto &p:kpoints){ + p.size=40; + if(p.response < minResp) minResp = p.response; + if(p.response > maxResp) maxResp = p.response; + } + float thresoldResp = (maxResp - minResp) * 0.20f + minResp; + + //Erase kepoints with low response (20%) + for (uint32_t i=0;i< kpoints.size(); i++) + if(kpoints[i].response < thresoldResp){ + kpoints.erase(kpoints.begin()+i); + i--; + } + + //Duplicated keypoints (closer) + for(uint32_t xi=0; xi markerDetector, int markerWarpPix) + { + //ScopedTimerEvents Timer("fractal-refinement"); + + std::vector imagePyramid = markerDetector->getImagePyramid(); + std::vector _ref_inner3d; + std::vector _ref_inner2d; + + cv::Mat _p_rvec; + _rvec.copyTo(_p_rvec); + cv::Mat _p_tvec; + _tvec.copyTo(_p_tvec); + + cv::Mat rot; + cv::Rodrigues(_rvec, rot); + + for (auto id_marker : _fractalMarker.fractalMarkerCollection) + { + //Check z value for 4 external corners + std::vector marker3d; + for(auto pt : id_marker.second.points) + { + cv::Mat_ src(3,1,rot.type()); + src(0,0)=pt.x;src(1,0)=pt.y;src(2,0)=pt.z; + + cv::Mat cam_image_point = rot * src + _tvec; + cam_image_point = cam_image_point/cv::norm(cam_image_point); + + if(cam_image_point.at(2,0)>0.85) + marker3d.push_back(pt); + else break; + } + + std::vector _inners3d; + std::vector _inners2d; + float area=0; + if(marker3d.size()<4) + { + if(_id_area.find(id_marker.first) != _id_area.end()) + area = _id_area[id_marker.first]; + else + return false; + + for(auto pt : _id_innerp3d[id_marker.first]) + { + cv::Mat_ src(3,1,_rvec.type()); + src(0,0)=pt.x;src(1,0)=pt.y;src(2,0)=pt.z; + + cv::Mat cam_image_point = rot * src +_tvec; + cam_image_point = cam_image_point/cv::norm(cam_image_point); + + if(cam_image_point.at(2,0)>0.85) + _inners3d.push_back(pt); + } + + if(_inners3d.size()==0) + return false; + + cv::projectPoints(_inners3d, _rvec, _tvec, + _cam_params.CameraMatrix, _cam_params.Distorsion, _inners2d); + } + else + { + std::vector marker2d; + cv::projectPoints(marker3d, _rvec, _tvec, + _cam_params.CameraMatrix, _cam_params.Distorsion, marker2d); + + cv::Point2f v01 = marker2d[1] - marker2d[0]; + cv::Point2f v03 = marker2d[3] - marker2d[0]; + float area1 = fabs(v01.x * v03.y - v01.y * v03.x); + cv::Point2f v21 = marker2d[1] - marker2d[2]; + cv::Point2f v23 = marker2d[3] - marker2d[2]; + float area2 = fabs(v21.x * v23.y - v21.y * v23.x); + + area = (area2 + area1) / 2.f; + _id_area[id_marker.first] = area; + + _inners3d = _id_innerp3d[id_marker.first]; + cv::projectPoints(_inners3d, _rvec, _tvec, _cam_params.CameraMatrix, _cam_params.Distorsion, _inners2d); + } + + size_t imgPyrIdx = 0; + auto markerWarpSize = (sqrt(id_marker.second.nBits())+2)*markerWarpPix; + float desiredarea = std::pow(static_cast(markerWarpSize), 2.f); + for (size_t p = 1; p < imagePyramid.size(); p++) + { + if (area/ pow(4, p) >= desiredarea) imgPyrIdx = p; + else break; + } + + float ratio = float(imagePyramid[imgPyrIdx].cols)/float(imagePyramid[0].cols); + + //std::cout << "REFINE["<< id_marker.first <<"], imgPyrId:"< _inners2d_error; + if (ratio == 1 && area >= desiredarea){ + int halfwsize= 4*float(imagePyramid[imgPyrIdx].cols)/float(imagePyramid[imgPyrIdx].cols) +0.5 ; + + std::vector _inners2d_copy; + for(auto pt:_inners2d){ _inners2d_copy.push_back(pt);} + cornerSubPix(imagePyramid[imgPyrIdx], _inners2d, cv::Size(halfwsize,halfwsize), cv::Size(-1, -1),cv::TermCriteria( cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 12, 0.005)); + int idx=0; + for(auto pt:_inners2d) + { + _inners2d_error.push_back(sqrt(pow(_inners2d_copy[idx].x - pt.x,2) + pow(_inners2d_copy[idx].y - pt.y,2))); + idx++; + } + } + else if (ratio != 1 && area >= desiredarea){ + + std::vector _inners2d_copy; + for(auto &pt:_inners2d){ _inners2d_copy.push_back(pt); pt *=ratio;} + + std::vector> vpnts; + vpnts.push_back(_inners2d); + markerDetector->cornerUpsample(vpnts, imagePyramid[imgPyrIdx].size()); + + int idx=0; + for(auto pt:vpnts[0]) + { + _inners2d_error.push_back(sqrt(pow(_inners2d_copy[idx].x - pt.x,2) + pow(_inners2d_copy[idx].y - pt.y,2))); + _inners2d[idx++] = pt; + } + } + + //Elimina puntos que no son esquinas + if (area >= desiredarea) { + //We discard outliers. Points above limit Q3+3*(Q3-1) + std::vector_inners2d_error_copy; + for(auto err:_inners2d_error) + _inners2d_error_copy.push_back(err); + sort(_inners2d_error_copy.begin(), _inners2d_error_copy.end()); + int q1 = (_inners2d_error_copy.size()+1)/4; + int q3 = 3*(_inners2d_error_copy.size()+1)/4; + + double limit = _inners2d_error_copy[q3] + 3*(_inners2d_error_copy[q3]-_inners2d_error_copy[q1]); + + int wsize = 10; + for(int idx=0; idx<_inners2d.size(); idx++) + { + if(_inners2d_error[idx]>limit) + continue; + + float x= int(_inners2d[idx].x+0.5f); + float y= int(_inners2d[idx].y+0.5f); + + cv::Rect r= cv::Rect(x-wsize,y-wsize,wsize*2+1,wsize*2+1); + //Check boundaries + if(r.x<0 || r.x+r.width>imagePyramid[0].cols || r.y<0 || + r.y+r.height>imagePyramid[0].rows) continue; + + int endX=r.x+r.width; + int endY=r.y+r.height; + uchar minV=255,maxV=0; + for(int y=r.y; y(y); + for(int x=r.x; xptr[x]) minV=ptr[x]; + if(maxV4) + { + aruco::solvePnP(_ref_inner3d, _ref_inner2d, _cam_params.CameraMatrix, _cam_params.Distorsion,_rvec,_tvec); + +//#define _fractal_debug_inners +#ifdef _fractal_debug_inners + cv::Mat InImageCopy; + cv::cvtColor(imagePyramid[0], InImageCopy, CV_GRAY2RGB); + + //Show first position (estimation with detected markers) + std::vector preinnersPrj; + for (auto id_marker : _fractalMarker.fractalMarkerCollection) + { + cv::projectPoints(_id_innerp3d[id_marker.first], _p_rvec, _p_tvec, _cam_params.CameraMatrix, _cam_params.Distorsion, preinnersPrj); + for(auto pt:preinnersPrj) + cv::circle(InImageCopy, pt, 5, cv::Scalar(0,0,255),CV_FILLED); + } + + //Show first position with refinement + for(auto p:_ref_inner2d) + cv::circle(InImageCopy, p, 5, cv::Scalar(255,0,0),CV_FILLED); + + _rvec.convertTo(_rvec,CV_32F); + _tvec.convertTo(_tvec,CV_32F); + cv::Rodrigues(_rvec, rot); + + std::vector _inners; + for(auto pt : _inner_corners_3d) + { + cv::Mat_ src(3,1,rot.type()); + src(0,0)=pt.x;src(1,0)=pt.y;src(2,0)=pt.z; + + cv::Mat cam_image_point = rot * src + _tvec ; + cam_image_point = cam_image_point/cv::norm(cam_image_point); + + if(cam_image_point.at(2,0)>0.85f) + _inners.push_back(pt); + } + std::vector _inners_prj; + cv::projectPoints(_inners, _rvec, _tvec, + _cam_params.CameraMatrix, _cam_params.Distorsion, _inners_prj); + + //Show new projection using all inner points + for(auto pt:_inners_prj) + cv::circle(InImageCopy, pt, 5, cv::Scalar(0,255,0),CV_FILLED); + + cv::namedWindow("AA",cv::WINDOW_NORMAL); + imshow("AA", InImageCopy); + cv::waitKey(); +#endif + //Timer.add("solve"); + + return true; + } + else + return false; + } + + bool FractalPoseTracker::fractalInnerPose(const cv::Ptr markerDetector, const std::vector& vmarkers, bool refinement) + { + if(vmarkers.size()>0) + { +// ScopedTimerEvents Timer("pnp"); +// std::cout << "[Case 1]"<< std::endl; + std::vector p2d; + std::vector p3d; + for (auto marker : vmarkers) + { + if (_fractalMarker.fractalMarkerCollection.find(marker.id) != _fractalMarker.fractalMarkerCollection.end()) + { + for (auto p : marker) + p2d.push_back(p); + + for (auto p : _fractalMarker.fractalMarkerCollection[marker.id].points) + p3d.push_back(p); + } + } + + //Initial pose estimation + aruco::solvePnP(p3d, p2d, _cam_params.CameraMatrix, _cam_params.Distorsion,_rvec,_tvec); + +// Timer.add("solve"); + + //REFINE + if(refinement) { + fractalRefinement(markerDetector); +// Timer.add("refine-solution"); + } + + return true; + } + else + { + if(!_rvec.empty()) + { +// std::cout << "[Case 2]"<< std::endl; +// ScopedTimerEvents Timer("ransac"); +// Timer.add("detect"); + + std::vector innerPoints2d; + std::vector innerPoints3d; + + float radius=0; + cv::Mat rot; + cv::Rodrigues(_rvec, rot); + + //Getting the keypoints search radius + for(auto id_marker:_fractalMarker.fractalMarkerCollection) + { + std::vector marker2d; + std::vector marker3d; + for(auto pt:_fractalMarker.fractalMarkerCollection[id_marker.first].points) + { + cv::Mat_ src(3,1,rot.type()); + src(0,0)=pt.x;src(1,0)=pt.y;src(2,0)=pt.z; + cv::Mat cam_image_point = rot * src + _tvec ; + cam_image_point = cam_image_point/cv::norm(cam_image_point); + + if(cam_image_point.at(2,0)>0.85) + marker3d.push_back(pt); + else break; + } + + if(marker3d.size()==4) + { + cv::projectPoints(marker3d, _rvec, _tvec, _cam_params.CameraMatrix, _cam_params.Distorsion, marker2d); + + //Find marker area + cv::Point2f v01 = marker2d[1] - marker2d[0]; + cv::Point2f v03 = marker2d[3] - marker2d[0]; + float area1 = fabs(v01.x * v03.y - v01.y * v03.x); + cv::Point2f v21 = marker2d[1] - marker2d[2]; + cv::Point2f v23 = marker2d[3] - marker2d[2]; + float area2 = fabs(v21.x * v23.y - v21.y * v23.x); + double area = (area2 + area1) / 2.f; + + auto markerWarpSize = (sqrt(_fractalMarker.fractalMarkerCollection[id_marker.first].nBits())+2)*10; + float desiredarea = std::pow(static_cast(markerWarpSize), 2.f); + if(area >= desiredarea) + { + for(auto pt:_id_innerp3d[id_marker.first]) + innerPoints3d.push_back(pt); + } + + if(radius == 0.f) + radius = sqrt(area)/(sqrt(id_marker.second.nBits())+2.f); + } + else + { + for(auto pt:_id_innerp3d[id_marker.first]) + innerPoints3d.push_back(pt); + } + } + + if(radius==0) radius = _preRadius; + _preRadius = radius; + + if(innerPoints3d.size() > 0 && radius > 0) + { + cv::projectPoints(innerPoints3d, _rvec, _tvec, _cam_params.CameraMatrix, _cam_params.Distorsion, innerPoints2d); + + cv::Mat region; + cv::Point2f offset; + float ratio; + if(!ROI(markerDetector->getImagePyramid(), region, innerPoints2d, offset, ratio)) + return false; + radius = radius * ratio; +// Timer.add("roi"); + + + std::cout << "radius: " << radius << std::endl; + +//#define _fractal_debug_region +#ifdef _fractal_debug_region + cv::Mat out; + cv::cvtColor(region, out, CV_GRAY2RGB); + for(uint32_t i=0; i kpoints; + cv::Ptr fd = cv::FastFeatureDetector::create(); + fd->detect(region, kpoints); +// Timer.add("fast"); + + + if(kpoints.size() > 0) + { + //Filter kpoints (low response) and removing duplicated. + kfilter(kpoints); +// Timer.add("fast-filter"); +// std::cout << "fast-filter" << std::endl; + + //Assign class to keypoints + assignClass(region, kpoints); +// Timer.add("fast-class"); + +//#define _fractal_debug_classification +#ifdef _fractal_debug_classification + drawKeyPoints(region, kpoints); + cv::waitKey(); +#endif + //Get keypoints with better response in a radius + picoflann::KdTreeIndex<2,PicoFlann_KeyPointAdapter> kdtreeImg; + kdtreeImg.build(kpoints); + std::vector>> inner_candidates; + for(uint idx=0; idx> res = kdtreeImg.radiusSearch(kpoints, innerPoints2d[idx], radius); + + std::vector candidates; + for(auto r:res){ + if(kpoints[r.first].class_id == _innerkpoints[idx].class_id) + candidates.push_back(r.first); + } + if(candidates.size() > 0) + inner_candidates.push_back(make_pair(idx, candidates)); + } + +// Timer.add("candidates"); + cv::Mat bestModel = fractal_solve_ransac(innerPoints2d.size(), inner_candidates, kpoints); +// Timer.add("find-solution"); + + if(!bestModel.empty()){ + std::vectorp3d; + std::vectorp2d; + + std::vector pnts, pntsDst; + cv::KeyPoint::convert(kpoints, pnts); + perspectiveTransform(pnts, pntsDst, bestModel); + for(uint32_t id=0; id> res = _kdtree.radiusSearch(_innerkpoints, pntsDst[id], _id_radius[0]); + + int i=0; + for(auto r:res) + { + uint32_t innerId = r.first; + double dist = sqrt(r.second); + + uint32_t fmarkerId = _innerkpoints[innerId].octave; + if(dist > _id_radius[fmarkerId]) + res.erase(res.begin()+i); + else i++; + } + if(res.size()>0){ + p3d.push_back(_innerp3d[res[0].first]); + p2d.push_back(cv::Point2f(kpoints[id].pt.x + offset.x, kpoints[id].pt.y + offset.y)/ratio); + } + } + + if(p3d.size() >= 4) + { +// std::cout << "solves" << std::endl; + aruco::solvePnP(p3d, p2d, _cam_params.CameraMatrix, _cam_params.Distorsion, _rvec, _tvec); +// Timer.add("solves"); + + if(refinement) { +// std::cout << "refine-solution" << std::endl; + fractalRefinement(markerDetector); +// Timer.add("refine-solution"); + } + + return true; + } + } + } + } + } + _rvec = cv::Mat(); + _tvec = cv::Mat(); + + return false; + } + } + + bool FractalPoseTracker::ROI(const std::vector imagePyramid, cv::Mat &img, std::vector &innerPoints2d, cv::Point2f &offset, float &ratio) + { + cv::Mat rot; + cv::Rodrigues(_rvec, rot); + + //Biggest marker projection + std::vector biggest_p2d; + std::vector biggest_p3d; + for(int idx=0; idx<_fractalMarker.fractalMarkerCollection.size()&biggest_p3d.size()<4; idx++) + { + biggest_p3d.empty(); + for(auto pt:_fractalMarker.fractalMarkerCollection[idx].points) + { + cv::Mat_ src(3,1,rot.type()); + src(0,0)=pt.x;src(1,0)=pt.y;src(2,0)=pt.z; + cv::Mat cam_image_point = rot * src + _tvec ; + cam_image_point = cam_image_point/cv::norm(cam_image_point); + + if(cam_image_point.at(2,0)>0.85) + biggest_p3d.push_back(pt); + else break; + } + } + + if(!biggest_p3d.empty()) + { + cv::projectPoints(biggest_p3d, _rvec, _tvec, _cam_params.CameraMatrix, _cam_params.Distorsion, biggest_p2d); + + //Smallest marker projection + std::vector smallest_p2d; + std::vector smallest_p3d; + auto marker_smallest = (--_fractalMarker.fractalMarkerCollection.end())->second; + for(auto pt:marker_smallest.points) + smallest_p3d.push_back(pt); + cv::projectPoints(smallest_p3d, _rvec, _tvec, _cam_params.CameraMatrix, _cam_params.Distorsion, smallest_p2d); + + //Smallest marker area + cv::Point2f v01 = smallest_p2d[1] - smallest_p2d[0]; + cv::Point2f v03 = smallest_p2d[3] - smallest_p2d[0]; + float area1 = fabs(v01.x * v03.y - v01.y * v03.x); + cv::Point2f v21 = smallest_p2d[1] - smallest_p2d[2]; + cv::Point2f v23 = smallest_p2d[3] - smallest_p2d[2]; + float area2 = fabs(v21.x * v23.y - v21.y * v23.x); + double area = (area2 + area1) / 2.f; + + + //Compute boundaries region + float border = sqrt(area)/sqrt(marker_smallest.nBits())+2; + int minX=imagePyramid[0].cols, minY=imagePyramid[0].rows, maxX=0, maxY=0; + for(auto p:biggest_p2d) + { + if(p.x < minX) minX = p.x-border; + if(p.x > maxX) maxX = p.x+border; + if(p.y < minY) minY = p.y-border; + if(p.y > maxY) maxY = p.y+border; + } + if(minX < 0) minX=0; + if(minY < 0) minY=0; + if(maxX > imagePyramid[0].cols) maxX=imagePyramid[0].cols; + if(maxY > imagePyramid[0].rows) maxY=imagePyramid[0].rows; + + + //Select imagePyramid + size_t imgPyrIdx = 0; + auto markerWarpSize = (sqrt(marker_smallest.nBits())+2)*10; + float desiredarea = std::pow(static_cast(markerWarpSize), 2.f); + for (size_t p = 1; p < imagePyramid.size(); p++) + { + if (area / pow(4, p) >= desiredarea) imgPyrIdx = p; + else break; + } + + ratio=float(imagePyramid[imgPyrIdx].cols)/float(imagePyramid[0].cols); + offset=cv::Point2i(minX, minY)*ratio; + cv::Rect region = cv::Rect(cv::Point2i(minX, minY)*ratio , cv::Point2i(maxX, maxY)*ratio); + img = imagePyramid[imgPyrIdx](region); + + for(auto &pt:innerPoints2d){ + pt.x = pt.x*ratio - region.x; + pt.y = pt.y*ratio - region.y; + } + return true; + } + else + return false; + } + + cv::Mat FractalPoseTracker::fractal_solve_ransac(int ninners, std::vector>> inner_kpnt, std::vector kpnts, uint32_t maxIter, float _minInliers, float _thresInliers) + { + std::vector pnts; + cv::KeyPoint::convert(kpnts, pnts); + + //Number randomly values selected + uint32_t numInliers=4; + //Number of inliers to consider it good model, stop iterating!!! + uint32_t thresInliers = uint32_t(ninners*_thresInliers); + //Enough number of inliers to consider the model as valid + uint32_t minInliers = uint32_t(ninners*_minInliers); + + //Number of inliers + uint32_t bestInliers = 0; + //Best model + cv::Mat bestH = cv::Mat(); + +// struct timespec ts; +// clock_gettime(CLOCK_MONOTONIC, &ts); +// srand((time_t)ts.tv_nsec); + + uint32_t count=0; + for(auto ik:inner_kpnt) + if(ik.second.size()>0) + count++; + if(count < minInliers) return cv::Mat(); + + uint32_t iter = 0; + do + { + // New stop condition to avoid infinite loop, when it tries to find the initial inliers + // For instance, for innerkpts group: 23{2} 18{3}, 14{3}, 3{7,6,5}, 2{3}, + // when the initial group of inliers is selected: + // 23{2},18{3},3{6} there's no way to find the fourth ... + uint32_t iter2 = 0; + std::vector> inliers; + while(inliers.size() < numInliers && iter2++= maxIter) + return cv::Mat(); + + std::vector srcInliers; + std::vector dstInliers; + for(auto in:inliers){ + dstInliers.push_back(_innerkpoints[in.first].pt); + srcInliers.push_back(kpnts[in.second].pt); + } + //Fit model with initial random inliers + cv::Mat H = findHomography(srcInliers, dstInliers); + + if(!H.empty()) + { + std::vector> newInliers; + std::vector> newInliers2; + + std::vector dstNewInliers; + std::vector srcNewInliers; + std::vector pntsTranf; + perspectiveTransform(pnts, pntsTranf, H); + + for(uint idP=0; idP< pntsTranf.size(); idP++) + { + std::vector>res = _kdtree.radiusSearch(_innerkpoints, pntsTranf[idP], _id_radius[0]); + + int i=0; + for(auto r:res) + { + uint32_t innerId = r.first; + double dist = sqrt(r.second); + + uint32_t fmarkerId = _innerkpoints[innerId].octave; + if(dist > _id_radius[fmarkerId]) + res.erase(res.begin()+i); + else i++; + } + + if(res.size() > 0){ + //avoid duplicate observations + bool exist=false; + for(auto in:newInliers) + { + if(in.first != res[0].first) + continue; + else { + exist=true; + break; + } + } + //if it is a new observation and these belong to the same class, add it! + if(!exist) + { + if(_innerkpoints[res[0].first].class_id == kpnts[idP].class_id) + newInliers.push_back(std::make_pair(res[0].first, idP)); + } + } + } + if(newInliers.size() > bestInliers) + { + bestInliers = newInliers.size(); + bestH = H; + } + } + iter++; + } while(iter kpoints, bool text, bool transf) + { + if(transf){ + //Convert point range from norm (-size/2, size/2) to (0,imageSize) + for(auto &k:kpoints){ + k.pt.x = image.cols * (k.pt.x/_fractalMarker.getFractalSize() + 0.5f); + k.pt.y = image.rows * (-k.pt.y/_fractalMarker.getFractalSize() + 0.5f); + } + } + + cv::Mat out; + cv::cvtColor(image, out, CV_GRAY2BGR); + //drawKeypoints(image, kpoints, out, cv::Scalar(0,0,255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); + + cv::Scalar color; + for(auto kp:kpoints) + { + if(kp.class_id == -1 ) color = cv::Scalar(255,0,255); + if(kp.class_id == 0 ) color = cv::Scalar(0,0,255); + if(kp.class_id == 1 ) color = cv::Scalar(0,255,0); + if(kp.class_id == 2 ) color = cv::Scalar(255,0,0); + circle(out, kp.pt, 2, color,-1); + } + + if(text){ + int nkm=0; + for(auto kp:kpoints) + { + putText( out, std::to_string(nkm++), kp.pt, CV_FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 255), 2, 8 ); + } + } + +//#ifdef _DEBUG +// imshow("KPoints", out); +// cv::waitKey(); +//#endif + } + + void FractalPoseTracker::assignClass(const cv::Mat &im, std::vector& kpoints, bool transf, int wsize) + { + if(im.type()!=CV_8UC1) + throw std::runtime_error("assignClass Input image must be 8UC1"); + int wsizeFull=wsize*2+1; + + cv::Mat labels = cv::Mat::zeros(wsize*2+1,wsize*2+1,CV_8UC1); + cv::Mat thresIm=cv::Mat(wsize*2+1,wsize*2+1,CV_8UC1); + + for(auto &kp:kpoints) + { + float x = kp.pt.x; + float y = kp.pt.y; + + //Convert point range from norm (-size/2, size/2) to (0,imageSize) + if(transf){ + x = im.cols * (x/_fractalMarker.getFractalSize() + 0.5f); + y = im.rows * (-y/_fractalMarker.getFractalSize() + 0.5f); + } + + x= int(x+0.5f); + y= int(y+0.5f); + + cv::Rect r= cv::Rect(x-wsize,y-wsize,wsize*2+1,wsize*2+1); + //Check boundaries + if(r.x<0 || r.x+r.width>im.cols || r.y<0 || + r.y+r.height>im.rows) continue; + + int endX=r.x+r.width; + int endY=r.y+r.height; + uchar minV=255,maxV=0; + for(int y=r.y; y(y); + for(int x=r.x; xptr[x]) minV=ptr[x]; + if(maxV( r.y+y)+r.x; + uchar *thresPtr= thresIm.ptr(y); + for(int x=0; xthres) { + nZ++; + thresPtr[x]=255; + } + else thresPtr[x]=0; + } + } + //set all to zero labels.setTo(cv::Scalar::all(0)); + for(int y=0; y(y); + for(int x=0; x unions; + for(int y=0; y(y); + uchar *labelsPtr=labels.ptr(y); + for(int x=0; x -1) + { + if(reg == thresPtr[x-1]) + lleft_px =labelsPtr[x-1]; + } + + if(y-1 > -1) + { + if(reg ==thresIm.ptr(y-1) [x] + )//thresIm.at(y-1, x) + ltop_px = labels.at(y-1, x); + } + + if(lleft_px==0 && ltop_px==0) + labelsPtr[x] = newLab++; + + else if(lleft_px!=0 && ltop_px!=0) + { + if(lleft_px < ltop_px) + { + labelsPtr[x] = lleft_px; + unions[ltop_px] = lleft_px; + } + else if(lleft_px > ltop_px) + { + labelsPtr[x] = ltop_px; + unions[lleft_px] = ltop_px; + } + else + {//IGuales + labelsPtr[x] = ltop_px; + } + } + else + { + if(lleft_px!=0) labelsPtr[x] = lleft_px; + else labelsPtr[x] = ltop_px; + } + } + } + + int nc= newLab-1 - unions.size(); + if(nc==2) + { + if(nZ > thresIm.total()-nZ) kp.class_id = 0; + else kp.class_id = 1; + } + else if (nc > 2) { + kp.class_id = 2; + } + } + } +} diff --git a/thirdparty/aruco-3.1.12/src/fractallabelers/fractalposetracker.h b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalposetracker.h new file mode 100644 index 0000000..d4bfd38 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/fractallabelers/fractalposetracker.h @@ -0,0 +1,115 @@ +#include +#include "../picoflann.h" +#include "../cameraparameters.h" +#include "../markerdetector.h" +#include "fractalmarkerset.h" +#include "aruco_export.h" +namespace aruco { + struct PicoFlann_KeyPointAdapter{ + inline float operator( )(const cv::KeyPoint &elem, int dim)const { return dim==0?elem.pt.x:elem.pt.y; } + inline float operator( )(const cv::Point2f &elem, int dim)const { return dim==0?elem.x:elem.y; } + }; + + class ARUCO_EXPORT FractalPoseTracker + { + public: + FractalPoseTracker(); + /** init fractlPoseTracker parameters + * @brief setParams + * @param cam_params camera paremeters + * @param msconf FractalMarkerSet configuration + * @param markerSize FractalMarker size + */ + void setParams(const CameraParameters& cam_params, const FractalMarkerSet& msconf, const float markerSize=-1); + /** estimate the pose of the fractal marker. + * @brief fractalInnerPose + * @param markerDetector + * @param detected markers + * @param refinement, use or not pose refinement. True by default. + * @return true if the pose is estimated and false otherwise. If not estimated, the parameters m.Rvec and m.Tvec + * and not set. + */ + bool fractalInnerPose(const cv::Ptr markerDetector, const std::vector& markers, bool refinement=true); + /** extraction of the region of the image where the marker is estimated to be based on the previous pose + * @brief ROI + * @param imagePyramid set images + * @param img original image. The image is scaled according to the selected pyramid image. + * @param innerPoints2d collection fractal inner points. The points are scaled according to the selected pyramid image. + * @param offset. Position of the upper inner corner of the marker. The offset is scaled according to the selected pyramid image. + * @param ratio selected scaling factor + */ + bool ROI(const std::vector imagePyramid, cv::Mat &img, std::vector &innerPoints2d, cv::Point2f &offset, float &ratio); + /** classification of the corners of the marker + * @brief assignClass + * @param im image used in the classification + * @param kpoints to classify + * @param transf. Is it necessary to transform keypoints? (-MarkerSize/2 .. MarkerSize/2) to (0 .. ImageSize) + * @param wsize. Window size + */ + void assignClass(const cv::Mat &im, std::vector& kpoints, bool transf=false, int wsize=5); + + /** estimate the pose of the fractal marker. Method case 2, paper. + * @brief fractal_solve_ransac + * @param ninners (number of total inners points used) + * @param inner_kpnt matches (inner points - detected keypoints) + * @param kpnts keypoints + * @param maxIter maximum number of iterations + * @param _minInliers beta in paper + * @param _thresInliers alpha in paper + * @return Mat best model homography. + */ + cv::Mat fractal_solve_ransac(int ninners, std::vector>> inner_kpnt, std::vector kpnts, uint32_t maxIter=500, float _minInliers=0.2f, float _thresInliers=0.7f); + + /** Draw keypoints + * @brief image + * @param kpoints + * @param text + * @param transf. Is it necessary to transform keypoints? (-MarkerSize/2 .. MarkerSize/2) to (0 .. ImageSize) + */ + void drawKeyPoints(const cv::Mat image, std::vector kpoints, bool text=false, bool transf=false); + + /** Refinement of the internal points of the marker and pose estimation with these. + * @brief fractalRefinement + * @param markerDetector + * @param markerWarpPix. Optimal markerwarpPix used to select the image of the pyramid. Default value 10. + * @return true if the pose is estimated and false otherwise. If not estimated, the parameters m.Rvec and m.Tvec + * and not set. + */ + bool fractalRefinement(const cv::Ptr markerDetector, int markerWarpPix=10); + + // return the rotation vector. Returns an empty matrix if last call to estimatePose returned false + const cv::Mat getRvec() const + { + return _rvec; + } + + // return the translation vector. Returns an empty matrix if last call to estimatePose returned false + const cv::Mat getTvec() const + { + return _tvec; + } + + // return all corners from fractal marker + const std::vector getInner3d() + { + return _innerp3d; + } + + //is the pose valid? + bool isPoseValid()const{return !_rvec.empty() && !_tvec.empty();} + + FractalMarkerSet getFractal(){return _fractalMarker;} + + private: + FractalMarkerSet _fractalMarker; //FractalMarkerSet configuration + aruco::CameraParameters _cam_params; //Camera parameters. + cv::Mat _rvec, _tvec; // current poses + std::map> _id_innerp3d; //Id_marker-Inners_corners + std::vector _innerp3d; //All inners corners + std::vector _innerkpoints; //All inners keypoints + picoflann::KdTreeIndex<2,PicoFlann_KeyPointAdapter> _kdtree; + std::map _id_radius; //Idmarker_Radius(Optimus) + std::map _id_area; //Idmarker_projectedArea(Optimus) + float _preRadius = 0; //radius used previous iteration + }; +} diff --git a/thirdparty/aruco-3.1.12/src/ippe.cpp b/thirdparty/aruco-3.1.12/src/ippe.cpp new file mode 100644 index 0000000..d88e4d5 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/ippe.cpp @@ -0,0 +1,1200 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "ippe.h" +#include + +#include + +using namespace cv; +using namespace std; + + +namespace aruco { + +/****** + * + */ + +cv::Mat getRTMatrix(const cv::Mat& R_, const cv::Mat& T_, int forceType) +{ + cv::Mat M; + cv::Mat R, T; + R_.copyTo(R); + T_.copyTo(T); + if (R.type() == CV_64F) + { + assert(T.type() == CV_64F); + cv::Mat Matrix = cv::Mat::eye(4, 4, CV_64FC1); + + cv::Mat R33 = cv::Mat(Matrix, cv::Rect(0, 0, 3, 3)); + if (R.total() == 3) + { + cv::Rodrigues(R, R33); + } + else if (R.total() == 9) + { + cv::Mat R64; + R.convertTo(R64, CV_64F); + R.copyTo(R33); + } + for (int i = 0; i < 3; i++) + Matrix.at(i, 3) = T.ptr(0)[i]; + M = Matrix; + } + else if (R.depth() == CV_32F) + { + cv::Mat Matrix = cv::Mat::eye(4, 4, CV_32FC1); + cv::Mat R33 = cv::Mat(Matrix, cv::Rect(0, 0, 3, 3)); + if (R.total() == 3) + { + cv::Rodrigues(R, R33); + } + else if (R.total() == 9) + { + cv::Mat R32; + R.convertTo(R32, CV_32F); + R.copyTo(R33); + } + + for (int i = 0; i < 3; i++) + Matrix.at(i, 3) = T.ptr(0)[i]; + M = Matrix; + } + + if (forceType == -1) + return M; + else + { + cv::Mat MTyped; + M.convertTo(MTyped, forceType); + return MTyped; + } +} +vector solvePnP(const vector& objPoints, const std::vector& imgPoints, + cv::InputArray cameraMatrix, cv::InputArray distCoeffs) +{ + + + cv::Mat Rvec, Tvec; + float reprojErr1, reprojErr2; + cv::Mat Rvec2, Tvec2; + + IPPE::PoseSolver Solver; + Solver.solveGeneric(objPoints, imgPoints, cameraMatrix, distCoeffs, Rvec, Tvec, reprojErr1, Rvec2, + Tvec2, reprojErr2); + + return {getRTMatrix(Rvec, Tvec, CV_32F), getRTMatrix(Rvec2, Tvec2, CV_32F)}; +} + +void solvePnP(const vector& objPoints, const std::vector& imgPoints, + cv::InputArray cameraMatrix, cv::InputArray distCoeffs,cv::Mat &rvec,cv::Mat &tvec) +{ + float reprojErr1, reprojErr2; + cv::Mat Rvec2, Tvec2; + + IPPE::PoseSolver Solver; + Solver.solveGeneric(objPoints, imgPoints, cameraMatrix, distCoeffs, rvec, tvec, reprojErr1, Rvec2, + Tvec2, reprojErr2); +} + +std::vector > solvePnP_(float size,const std::vector &imgPoints, cv::InputArray cameraMatrix, cv::InputArray distCoeffs){ + cv::Mat Rvec, Tvec, Rvec2,Tvec2; + float reprojErr1, reprojErr2; + + IPPE::PoseSolver Solver; + Solver.solveSquare(size,imgPoints,cameraMatrix,distCoeffs, Rvec, Tvec,reprojErr1,Rvec2,Tvec2,reprojErr2); + + + return {make_pair(getRTMatrix(Rvec, Tvec, CV_32F), reprojErr1), + make_pair(getRTMatrix(Rvec2, Tvec2, CV_32F), reprojErr2)}; +} + +std::vector> solvePnP_(const std::vector& objPoints, + const std::vector& imgPoints, + cv::InputArray cameraMatrix, cv::InputArray distCoeffs) +{ + cv::Mat Rvec, Tvec; + float reprojErr1, reprojErr2; + cv::Mat Rvec2, Tvec2; + + IPPE::PoseSolver Solver; + Solver.solveGeneric(objPoints, imgPoints, cameraMatrix, distCoeffs, Rvec, Tvec, reprojErr1, Rvec2, + Tvec2, reprojErr2); + + + return {make_pair(getRTMatrix(Rvec, Tvec, CV_32F), reprojErr1), + make_pair(getRTMatrix(Rvec2, Tvec2, CV_32F), reprojErr2)}; + +} +} + +IPPE::PoseSolver::PoseSolver() +{ + +} + +IPPE::PoseSolver::~PoseSolver() +{ + +} + +void IPPE::PoseSolver::solveGeneric(cv::InputArray _objectPoints, cv::InputArray _imagePoints, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, + cv::OutputArray _rvec1, cv::OutputArray _tvec1, float& err1, cv::OutputArray _rvec2, cv::OutputArray _tvec2, float& err2) +{ + cv::Mat normalizedImagePoints; //undistored version of imagePoints + + if (_cameraMatrix.empty()) { + //there is no camera matrix and image points are given in normalized pixel coordinates. + _imagePoints.copyTo(normalizedImagePoints); + } + else { + //undistort the image points (i.e. put them in normalized pixel coordinates): + cv::undistortPoints(_imagePoints, normalizedImagePoints, _cameraMatrix, _distCoeffs); + } + + //solve: + cv::Mat Ma, Mb; + solveGeneric(_objectPoints, normalizedImagePoints, Ma, Mb); + + //the two poses computed by IPPE (sorted): + cv::Mat M1, M2; + + //sort poses by reprojection error: + sortPosesByReprojError(_objectPoints, _imagePoints, _cameraMatrix, _distCoeffs, Ma, Mb, M1, M2, err1, err2); + + //fill outputs + rot2vec(M1.colRange(0, 3).rowRange(0, 3), _rvec1); + rot2vec(M2.colRange(0, 3).rowRange(0, 3), _rvec2); + + M1.colRange(3, 4).rowRange(0, 3).copyTo(_tvec1); + M2.colRange(3, 4).rowRange(0, 3).copyTo(_tvec2); +} + +void IPPE::PoseSolver::solveGeneric(cv::InputArray _objectPoints, cv::InputArray _normalizedInputPoints, + cv::OutputArray _Ma, cv::OutputArray _Mb) +{ + + //argument checking: + size_t n = _objectPoints.rows() * _objectPoints.cols(); //number of points + int objType = _objectPoints.type(); + int type_input = _normalizedInputPoints.type(); + assert((objType == CV_32FC3) | (objType == CV_64FC3)); + assert((type_input == CV_32FC2) | (type_input == CV_64FC2)); + assert((_objectPoints.rows() == 1) | (_objectPoints.cols() == 1)); + assert((_objectPoints.rows() >= 4) | (_objectPoints.cols() >= 4)); + assert((_normalizedInputPoints.rows() == 1) | (_normalizedInputPoints.cols() == 1)); + assert(static_cast(_objectPoints.rows() * _objectPoints.cols()) == n); + + cv::Mat normalizedInputPoints; + if (type_input == CV_32FC2) { + _normalizedInputPoints.getMat().convertTo(normalizedInputPoints, CV_64FC2); + } + else { + normalizedInputPoints = _normalizedInputPoints.getMat(); + } + + cv::Mat objectInputPoints; + if (type_input == CV_32FC3) { + _objectPoints.getMat().convertTo(objectInputPoints, CV_64FC3); + } + else { + objectInputPoints = _objectPoints.getMat(); + } + + cv::Mat canonicalObjPoints; + cv::Mat MmodelPoints2Canonical; + + //transform object points to the canonical position (zero centred and on the plane z=0): + makeCanonicalObjectPoints(objectInputPoints, canonicalObjPoints, MmodelPoints2Canonical); + + //compute the homography mapping the model's points to normalizedInputPoints + cv::Mat H; + HomographyHO::homographyHO(canonicalObjPoints, _normalizedInputPoints, H); + + //now solve + cv::Mat MaCanon, MbCanon; + solveCanonicalForm(canonicalObjPoints, normalizedInputPoints, H, MaCanon, MbCanon); + + //transform computed poses to account for canonical transform: + cv::Mat Ma = MaCanon * MmodelPoints2Canonical; + cv::Mat Mb = MbCanon * MmodelPoints2Canonical; + + //output poses: + Ma.copyTo(_Ma); + Mb.copyTo(_Mb); +} + +void IPPE::PoseSolver::solveCanonicalForm(cv::InputArray _canonicalObjPoints, cv::InputArray _normalizedInputPoints, cv::InputArray _H, + cv::OutputArray _Ma, cv::OutputArray _Mb) +{ + _Ma.create(4, 4, CV_64FC1); + _Mb.create(4, 4, CV_64FC1); + + cv::Mat Ma = _Ma.getMat(); + cv::Mat Mb = _Mb.getMat(); + cv::Mat H = _H.getMat(); + + //initialise poses: + Ma.setTo(0); + Ma.at(3, 3) = 1; + Mb.setTo(0); + Mb.at(3, 3) = 1; + + //Compute the Jacobian J of the homography at (0,0): + double j00, j01, j10, j11, v0, v1; + + j00 = H.at(0, 0) - H.at(2, 0) * H.at(0, 2); + j01 = H.at(0, 1) - H.at(2, 1) * H.at(0, 2); + j10 = H.at(1, 0) - H.at(2, 0) * H.at(1, 2); + j11 = H.at(1, 1) - H.at(2, 1) * H.at(1, 2); + + //Compute the transformation of (0,0) into the image: + v0 = H.at(0, 2); + v1 = H.at(1, 2); + + //compute the two rotation solutions: + cv::Mat Ra = Ma.colRange(0, 3).rowRange(0, 3); + cv::Mat Rb = Mb.colRange(0, 3).rowRange(0, 3); + computeRotations(j00, j01, j10, j11, v0, v1, Ra, Rb); + + //for each rotation solution, compute the corresponding translation solution: + cv::Mat ta = Ma.colRange(3, 4).rowRange(0, 3); + cv::Mat tb = Mb.colRange(3, 4).rowRange(0, 3); + computeTranslation(_canonicalObjPoints, _normalizedInputPoints, Ra, ta); + computeTranslation(_canonicalObjPoints, _normalizedInputPoints, Rb, tb); +} + +void IPPE::PoseSolver::solveSquare(double squareLength, InputArray _imagePoints, InputArray _cameraMatrix, InputArray _distCoeffs, + OutputArray _rvec1, OutputArray _tvec1, float& err1, OutputArray _rvec2, OutputArray _tvec2, float& err2) +{ + //allocate outputs: + _rvec1.create(3, 1, CV_64FC1); + _tvec1.create(3, 1, CV_64FC1); + _rvec2.create(3, 1, CV_64FC1); + _tvec2.create(3, 1, CV_64FC1); + + cv::Mat normalizedInputPoints; //undistored version of imagePoints + cv::Mat objectPoints2D; + + //generate the object points: + generateSquareObjectCorners2D(squareLength, objectPoints2D); + + cv::Mat H; //homography from canonical object points to normalized pixels + + + if (_cameraMatrix.empty()) { + //this means imagePoints are defined in normalized pixel coordinates, so just copy it: + _imagePoints.copyTo(normalizedInputPoints); + } + else { + //undistort the image points (i.e. put them in normalized pixel coordinates). + cv::undistortPoints(_imagePoints, normalizedInputPoints, _cameraMatrix, _distCoeffs); + } + + if (normalizedInputPoints.type() == CV_32FC2) + { + normalizedInputPoints.convertTo(normalizedInputPoints, CV_64F); + } + + //compute H + homographyFromSquarePoints(normalizedInputPoints, squareLength / 2.0, H); + + //now solve + cv::Mat Ma, Mb; + solveCanonicalForm(objectPoints2D, normalizedInputPoints, H, Ma, Mb); + + //sort poses according to reprojection error: + cv::Mat M1, M2; + cv::Mat objectPoints3D; + generateSquareObjectCorners3D(squareLength, objectPoints3D); + + sortPosesByReprojError(objectPoints3D, normalizedInputPoints, _cameraMatrix, _distCoeffs, Ma, Mb, M1, M2, err1, err2); + + //fill outputs + rot2vec(M1.colRange(0, 3).rowRange(0, 3), _rvec1); + rot2vec(M2.colRange(0, 3).rowRange(0, 3), _rvec2); + + M1.colRange(3, 4).rowRange(0, 3).copyTo(_tvec1); + M2.colRange(3, 4).rowRange(0, 3).copyTo(_tvec2); +} + +void IPPE::PoseSolver::generateSquareObjectCorners3D(double squareLength, OutputArray _objectPoints) +{ + _objectPoints.create(1, 4, CV_64FC3); + cv::Mat objectPoints = _objectPoints.getMat(); + objectPoints.ptr(0)[0] = Vec3d(-squareLength / 2.0, squareLength / 2.0, 0.0); + objectPoints.ptr(0)[1] = Vec3d(squareLength / 2.0, squareLength / 2.0, 0.0); + objectPoints.ptr(0)[2] = Vec3d(squareLength / 2.0, -squareLength / 2.0, 0.0); + objectPoints.ptr(0)[3] = Vec3d(-squareLength / 2.0, -squareLength / 2.0, 0.0); +} + +void IPPE::PoseSolver::generateSquareObjectCorners2D(double squareLength, OutputArray _objectPoints) +{ + _objectPoints.create(1, 4, CV_64FC2); + cv::Mat objectPoints = _objectPoints.getMat(); + objectPoints.ptr(0)[0] = Vec2d(-squareLength / 2.0, squareLength / 2.0); + objectPoints.ptr(0)[1] = Vec2d(squareLength / 2.0, squareLength / 2.0); + objectPoints.ptr(0)[2] = Vec2d(squareLength / 2.0, -squareLength / 2.0); + objectPoints.ptr(0)[3] = Vec2d(-squareLength / 2.0, -squareLength / 2.0); +} + +double IPPE::PoseSolver::meanSceneDepth(InputArray _objectPoints, InputArray _rvec, InputArray _tvec) +{ + assert(_objectPoints.type() == CV_64FC3); + + size_t n = static_cast(_objectPoints.rows() * _objectPoints.cols()); + Mat R; + Mat q; + Rodrigues(_rvec, R); + double zBar = 0; + + for (size_t i = 0; i < n; i++) { + cv::Mat p(_objectPoints.getMat().at(i)); + q = R * p + _tvec.getMat(); + double z; + if (q.depth() == CV_64FC1) { + z = q.at(2); + } + else { + z = static_cast(q.at(2)); + } + zBar += z; + + //if (z <= 0) { + // std::cout << "Warning: object point " << i << " projects behind the camera! This should not be allowed. " << std::endl; + //} + } + return zBar / static_cast(n); +} + +void IPPE::PoseSolver::rot2vec(InputArray _R, OutputArray _r) +{ + assert(_R.type() == CV_64FC1); + assert(_R.rows() == 3); + assert(_R.cols() == 3); + + _r.create(3, 1, CV_64FC1); + + cv::Mat R = _R.getMat(); + cv::Mat rvec = _r.getMat(); + + double trace = R.at(0, 0) + R.at(1, 1) + R.at(2, 2); + double w_norm = acos((trace - 1.0) / 2.0); + double c0, c1, c2; + double eps = std::numeric_limits::epsilon(); + double d = 1 / (2 * sin(w_norm)) * w_norm; + if (w_norm < eps) //rotation is the identity + { + rvec.setTo(0); + } + else { + c0 = R.at(2, 1) - R.at(1, 2); + c1 = R.at(0, 2) - R.at(2, 0); + c2 = R.at(1, 0) - R.at(0, 1); + rvec.at(0) = d * c0; + rvec.at(1) = d * c1; + rvec.at(2) = d * c2; + } +} + +void IPPE::PoseSolver::computeTranslation(InputArray _objectPoints, InputArray _normalizedImgPoints, InputArray _R, OutputArray _t) +{ + //This is solved by building the linear system At = b, where t corresponds to the (unknown) translation. + //This is then inverted with the associated normal equations to give t = inv(transpose(A)*A)*transpose(A)*b + //For efficiency we only store the coefficients of (transpose(A)*A) and (transpose(A)*b) + + assert(_objectPoints.type() == CV_64FC2); + assert(_normalizedImgPoints.type() == CV_64FC2); + assert(_R.type() == CV_64FC1); + + assert((_R.rows() == 3) & (_R.cols() == 3)); + assert((_objectPoints.rows() == 1) | (_objectPoints.cols() == 1)); + assert((_normalizedImgPoints.rows() == 1) | (_normalizedImgPoints.cols() == 1)); + + size_t n = static_cast(_normalizedImgPoints.rows() * _normalizedImgPoints.cols()); + assert(n == static_cast(_objectPoints.rows() * _objectPoints.cols())); + + cv::Mat objectPoints = _objectPoints.getMat(); + cv::Mat imgPoints = _normalizedImgPoints.getMat(); + + _t.create(3, 1, CV_64FC1); + + cv::Mat R = _R.getMat(); + + //coefficients of (transpose(A)*A) + double ATA00 = n; + double ATA02 = 0; + double ATA11 = n; + double ATA12 = 0; + double ATA20 = 0; + double ATA21 = 0; + double ATA22 = 0; + + //coefficients of (transpose(A)*b) + double ATb0 = 0; + double ATb1 = 0; + double ATb2 = 0; + + //S gives inv(transpose(A)*A)/det(A)^2 + double S00, S01, S02; + double S10, S11, S12; + double S20, S21, S22; + + double rx, ry, rz; + double a2; + double b2; + double bx, by; + + //now loop through each point and increment the coefficients: + for (size_t i = 0; i < n; i++) { + rx = R.at(0, 0) * objectPoints.at(i)(0) + R.at(0, 1) * objectPoints.at(i)(1); + ry = R.at(1, 0) * objectPoints.at(i)(0) + R.at(1, 1) * objectPoints.at(i)(1); + rz = R.at(2, 0) * objectPoints.at(i)(0) + R.at(2, 1) * objectPoints.at(i)(1); + + a2 = -imgPoints.at(i)(0); + b2 = -imgPoints.at(i)(1); + + ATA02 = ATA02 + a2; + ATA12 = ATA12 + b2; + ATA20 = ATA20 + a2; + ATA21 = ATA21 + b2; + ATA22 = ATA22 + a2 * a2 + b2 * b2; + + bx = -a2 * rz - rx; + by = -b2 * rz - ry; + + ATb0 = ATb0 + bx; + ATb1 = ATb1 + by; + ATb2 = ATb2 + a2 * bx + b2 * by; + } + + double detAInv = 1.0 / (ATA00 * ATA11 * ATA22 - ATA00 * ATA12 * ATA21 - ATA02 * ATA11 * ATA20); + + //construct S: + S00 = ATA11 * ATA22 - ATA12 * ATA21; + S01 = ATA02 * ATA21; + S02 = -ATA02 * ATA11; + S10 = ATA12 * ATA20; + S11 = ATA00 * ATA22 - ATA02 * ATA20; + S12 = -ATA00 * ATA12; + S20 = -ATA11 * ATA20; + S21 = -ATA00 * ATA21; + S22 = ATA00 * ATA11; + + //solve t: + Mat t = _t.getMat(); + t.at(0) = detAInv * (S00 * ATb0 + S01 * ATb1 + S02 * ATb2); + t.at(1) = detAInv * (S10 * ATb0 + S11 * ATb1 + S12 * ATb2); + t.at(2) = detAInv * (S20 * ATb0 + S21 * ATb1 + S22 * ATb2); +} + +void IPPE::PoseSolver::computeRotations(double j00, double j01, double j10, double j11, double p, double q, OutputArray _R1, OutputArray _R2) +{ + //This is fairly optimized code which makes it hard to understand. The matlab code is certainly easier to read. + _R1.create(3, 3, CV_64FC1); + _R2.create(3, 3, CV_64FC1); + + double a00, a01, a10, a11, ata00, ata01, ata11, b00, b01, b10, b11, binv00, binv01, binv10, binv11; + //double rv00, rv01, rv02, rv10, rv11, rv12, rv20, rv21, rv22; + double rtilde00, rtilde01, rtilde10, rtilde11; + double rtilde00_2, rtilde01_2, rtilde10_2, rtilde11_2; + double b0, b1, gamma, dtinv; + double sp; + + Mat Rv; + cv::Mat v(3,1,CV_64FC1); + v.at(0) = p; + v.at(1) = q; + v.at(2) = 1; + rotateVec2ZAxis(v,Rv); + Rv = Rv.t(); + + + //setup the 2x2 SVD decomposition: + double rv00, rv01, rv02; + double rv10, rv11, rv12; + double rv20, rv21, rv22; + rv00 = Rv.at(0,0); + rv01 = Rv.at(0,1); + rv02 = Rv.at(0,2); + + rv10 = Rv.at(1,0); + rv11 = Rv.at(1,1); + rv12 = Rv.at(1,2); + + rv20 = Rv.at(2,0); + rv21 = Rv.at(2,1); + rv22 = Rv.at(2,2); + + b00 = rv00 - p * rv20; + b01 = rv01 - p * rv21; + b10 = rv10 - q * rv20; + b11 = rv11 - q * rv21; + + dtinv = 1.0 / ((b00 * b11 - b01 * b10)); + + binv00 = dtinv * b11; + binv01 = -dtinv * b01; + binv10 = -dtinv * b10; + binv11 = dtinv * b00; + + a00 = binv00 * j00 + binv01 * j10; + a01 = binv00 * j01 + binv01 * j11; + a10 = binv10 * j00 + binv11 * j10; + a11 = binv10 * j01 + binv11 * j11; + + //compute the largest singular value of A: + ata00 = a00 * a00 + a01 * a01; + ata01 = a00 * a10 + a01 * a11; + ata11 = a10 * a10 + a11 * a11; + + gamma = sqrt(0.5 * (ata00 + ata11 + sqrt((ata00 - ata11) * (ata00 - ata11) + 4.0 * ata01 * ata01))); + + //reconstruct the full rotation matrices: + rtilde00 = a00 / gamma; + rtilde01 = a01 / gamma; + rtilde10 = a10 / gamma; + rtilde11 = a11 / gamma; + + rtilde00_2 = rtilde00 * rtilde00; + rtilde01_2 = rtilde01 * rtilde01; + rtilde10_2 = rtilde10 * rtilde10; + rtilde11_2 = rtilde11 * rtilde11; + + b0 = sqrt(-rtilde00_2 - rtilde10_2 + 1); + b1 = sqrt(-rtilde01_2 - rtilde11_2 + 1); + sp = (-rtilde00 * rtilde01 - rtilde10 * rtilde11); + + if (sp < 0) { + b1 = -b1; + } + + //store results: + Mat R1 = _R1.getMat(); + Mat R2 = _R2.getMat(); + + R1.at(0, 0) = (rtilde00)*rv00 + (rtilde10)*rv01 + (b0)*rv02; + R1.at(0, 1) = (rtilde01)*rv00 + (rtilde11)*rv01 + (b1)*rv02; + R1.at(0, 2) = (b1 * rtilde10 - b0 * rtilde11) * rv00 + (b0 * rtilde01 - b1 * rtilde00) * rv01 + (rtilde00 * rtilde11 - rtilde01 * rtilde10) * rv02; + R1.at(1, 0) = (rtilde00)*rv10 + (rtilde10)*rv11 + (b0)*rv12; + R1.at(1, 1) = (rtilde01)*rv10 + (rtilde11)*rv11 + (b1)*rv12; + R1.at(1, 2) = (b1 * rtilde10 - b0 * rtilde11) * rv10 + (b0 * rtilde01 - b1 * rtilde00) * rv11 + (rtilde00 * rtilde11 - rtilde01 * rtilde10) * rv12; + R1.at(2, 0) = (rtilde00)*rv20 + (rtilde10)*rv21 + (b0)*rv22; + R1.at(2, 1) = (rtilde01)*rv20 + (rtilde11)*rv21 + (b1)*rv22; + R1.at(2, 2) = (b1 * rtilde10 - b0 * rtilde11) * rv20 + (b0 * rtilde01 - b1 * rtilde00) * rv21 + (rtilde00 * rtilde11 - rtilde01 * rtilde10) * rv22; + + R2.at(0, 0) = (rtilde00)*rv00 + (rtilde10)*rv01 + (-b0) * rv02; + R2.at(0, 1) = (rtilde01)*rv00 + (rtilde11)*rv01 + (-b1) * rv02; + R2.at(0, 2) = (b0 * rtilde11 - b1 * rtilde10) * rv00 + (b1 * rtilde00 - b0 * rtilde01) * rv01 + (rtilde00 * rtilde11 - rtilde01 * rtilde10) * rv02; + R2.at(1, 0) = (rtilde00)*rv10 + (rtilde10)*rv11 + (-b0) * rv12; + R2.at(1, 1) = (rtilde01)*rv10 + (rtilde11)*rv11 + (-b1) * rv12; + R2.at(1, 2) = (b0 * rtilde11 - b1 * rtilde10) * rv10 + (b1 * rtilde00 - b0 * rtilde01) * rv11 + (rtilde00 * rtilde11 - rtilde01 * rtilde10) * rv12; + R2.at(2, 0) = (rtilde00)*rv20 + (rtilde10)*rv21 + (-b0) * rv22; + R2.at(2, 1) = (rtilde01)*rv20 + (rtilde11)*rv21 + (-b1) * rv22; + R2.at(2, 2) = (b0 * rtilde11 - b1 * rtilde10) * rv20 + (b1 * rtilde00 - b0 * rtilde01) * rv21 + (rtilde00 * rtilde11 - rtilde01 * rtilde10) * rv22; +} + + +void IPPE::PoseSolver::homographyFromSquarePoints(InputArray _targetPoints, double halfLength, OutputArray H_) +{ + + assert((_targetPoints.type() == CV_32FC2) | (_targetPoints.type() == CV_64FC2)); + + cv::Mat pts = _targetPoints.getMat(); + H_.create(3, 3, CV_64FC1); + Mat H = H_.getMat(); + + double p1x, p1y; + double p2x, p2y; + double p3x, p3y; + double p4x, p4y; + + if (_targetPoints.type() == CV_32FC2) { + p1x = -pts.at(0)(0); + p1y = -pts.at(0)(1); + + p2x = -pts.at(1)(0); + p2y = -pts.at(1)(1); + + p3x = -pts.at(2)(0); + p3y = -pts.at(2)(1); + + p4x = -pts.at(3)(0); + p4y = -pts.at(3)(1); + } + else { + p1x = -pts.at(0)(0); + p1y = -pts.at(0)(1); + + p2x = -pts.at(1)(0); + p2y = -pts.at(1)(1); + + p3x = -pts.at(2)(0); + p3y = -pts.at(2)(1); + + p4x = -pts.at(3)(0); + p4y = -pts.at(3)(1); + } + + //analytic solution: + double detsInv = -1 / (halfLength * (p1x * p2y - p2x * p1y - p1x * p4y + p2x * p3y - p3x * p2y + p4x * p1y + p3x * p4y - p4x * p3y)); + + H.at(0, 0) = detsInv * (p1x * p3x * p2y - p2x * p3x * p1y - p1x * p4x * p2y + p2x * p4x * p1y - p1x * p3x * p4y + p1x * p4x * p3y + p2x * p3x * p4y - p2x * p4x * p3y); + H.at(0, 1) = detsInv * (p1x * p2x * p3y - p1x * p3x * p2y - p1x * p2x * p4y + p2x * p4x * p1y + p1x * p3x * p4y - p3x * p4x * p1y - p2x * p4x * p3y + p3x * p4x * p2y); + H.at(0, 2) = detsInv * halfLength * (p1x * p2x * p3y - p2x * p3x * p1y - p1x * p2x * p4y + p1x * p4x * p2y - p1x * p4x * p3y + p3x * p4x * p1y + p2x * p3x * p4y - p3x * p4x * p2y); + H.at(1, 0) = detsInv * (p1x * p2y * p3y - p2x * p1y * p3y - p1x * p2y * p4y + p2x * p1y * p4y - p3x * p1y * p4y + p4x * p1y * p3y + p3x * p2y * p4y - p4x * p2y * p3y); + H.at(1, 1) = detsInv * (p2x * p1y * p3y - p3x * p1y * p2y - p1x * p2y * p4y + p4x * p1y * p2y + p1x * p3y * p4y - p4x * p1y * p3y - p2x * p3y * p4y + p3x * p2y * p4y); + H.at(1, 2) = detsInv * halfLength * (p1x * p2y * p3y - p3x * p1y * p2y - p2x * p1y * p4y + p4x * p1y * p2y - p1x * p3y * p4y + p3x * p1y * p4y + p2x * p3y * p4y - p4x * p2y * p3y); + H.at(2, 0) = -detsInv * (p1x * p3y - p3x * p1y - p1x * p4y - p2x * p3y + p3x * p2y + p4x * p1y + p2x * p4y - p4x * p2y); + H.at(2, 1) = detsInv * (p1x * p2y - p2x * p1y - p1x * p3y + p3x * p1y + p2x * p4y - p4x * p2y - p3x * p4y + p4x * p3y); + H.at(2, 2) = 1.0; +} + +void IPPE::PoseSolver::makeCanonicalObjectPoints(InputArray _objectPoints, OutputArray _canonicalObjPoints, OutputArray _MmodelPoints2Canonical) +{ + + int objType = _objectPoints.type(); + assert((objType == CV_64FC3) | (objType == CV_32FC3)); + + size_t n = _objectPoints.rows() * _objectPoints.cols(); + + _canonicalObjPoints.create(1, n, CV_64FC2); + _MmodelPoints2Canonical.create(4, 4, CV_64FC1); + + cv::Mat objectPoints = _objectPoints.getMat(); + cv::Mat canonicalObjPoints = _canonicalObjPoints.getMat(); + + cv::Mat UZero(3, n, CV_64FC1); + + double xBar = 0; + double yBar = 0; + double zBar = 0; + bool isOnZPlane = true; + for (size_t i = 0; i < n; i++) { + double x, y, z; + if (objType == CV_32FC3) { + x = static_cast(objectPoints.at(i)[0]); + y = static_cast(objectPoints.at(i)[1]); + z = static_cast(objectPoints.at(i)[2]); + } + else { + x = objectPoints.at(i)[0]; + y = objectPoints.at(i)[1]; + z = objectPoints.at(i)[2]; + + if (abs(z) > IPPE_SMALL) { + isOnZPlane = false; + } + } + xBar += x; + yBar += y; + zBar += z; + + UZero.at(0, i) = x; + UZero.at(1, i) = y; + UZero.at(2, i) = z; + } + xBar = xBar / (double)n; + yBar = yBar / (double)n; + zBar = zBar / (double)n; + + for (size_t i = 0; i < n; i++) { + UZero.at(0, i) -= xBar; + UZero.at(1, i) -= yBar; + UZero.at(2, i) -= zBar; + } + + cv::Mat MCenter(4, 4, CV_64FC1); + MCenter.setTo(0); + MCenter.at(0, 0) = 1; + MCenter.at(1, 1) = 1; + MCenter.at(2, 2) = 1; + MCenter.at(3, 3) = 1; + + MCenter.at(0, 3) = -xBar; + MCenter.at(1, 3) = -yBar; + MCenter.at(2, 3) = -zBar; + + if (isOnZPlane) { + //MmodelPoints2Canonical is given by MCenter + MCenter.copyTo(_MmodelPoints2Canonical); + for (size_t i = 0; i < n; i++) { + canonicalObjPoints.at(i)[0] = UZero.at(0, i); + canonicalObjPoints.at(i)[1] = UZero.at(1, i); + } + } + else { + cv::Mat UZeroAligned(3, n, CV_64FC1); + cv::Mat R; //rotation that rotates objectPoints to the plane z=0 + + if (!computeObjextSpaceR3Pts(objectPoints,R)) + { + //we could not compute R, problably because there is a duplicate point in {objectPoints(0),objectPoints(1),objectPoints(2)}. So we compute it with the SVD (which is slower): + computeObjextSpaceRSvD(UZero,R); + } + + UZeroAligned = R * UZero; + + for (size_t i = 0; i < n; i++) { + canonicalObjPoints.at(i)[0] = UZeroAligned.at(0, i); + canonicalObjPoints.at(i)[1] = UZeroAligned.at(1, i); + assert(abs(UZeroAligned.at(2, i))<=IPPE_SMALL); + } + + cv::Mat MRot(4, 4, CV_64FC1); + MRot.setTo(0); + MRot.at(3, 3) = 1; + + R.copyTo(MRot.colRange(0, 3).rowRange(0, 3)); + cv::Mat Mb = MRot * MCenter; + Mb.copyTo(_MmodelPoints2Canonical); + } +} + +void IPPE::PoseSolver::evalReprojError(cv::InputArray _objectPoints, cv::InputArray _imagePoints, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, cv::InputArray _M, float& err) +{ + cv::Mat projectedPoints; + cv::Mat imagePoints = _imagePoints.getMat(); + cv::Mat r; + rot2vec(_M.getMat().colRange(0, 3).rowRange(0, 3), r); + + if (_cameraMatrix.empty()) { + //there is no camera matrix and image points are in normalized pixel coordinates + cv::Mat K(3, 3, CV_64FC1); + K.setTo(0); + K.at(0, 0) = 1; + K.at(1, 1) = 1; + K.at(2, 2) = 1; + cv::Mat kc; + cv::projectPoints(_objectPoints, r, _M.getMat().colRange(3, 4).rowRange(0, 3), K, kc, projectedPoints); + } + else { + cv::projectPoints(_objectPoints, r, _M.getMat().colRange(3, 4).rowRange(0, 3), _cameraMatrix, _distCoeffs, projectedPoints); + } + + err = 0; + size_t n = _objectPoints.rows() * _objectPoints.cols(); + + float dx, dy; + for (size_t i = 0; i < n; i++) { + if (projectedPoints.depth() == CV_32FC1) { + dx = projectedPoints.at(i)[0] - imagePoints.at(i)[0]; + dy = projectedPoints.at(i)[1] - imagePoints.at(i)[1]; + } + else { + dx = projectedPoints.at(i)[0] - imagePoints.at(i)[0]; + dy = projectedPoints.at(i)[1] - imagePoints.at(i)[1]; + } + + err += dx * dx + dy * dy; + } + err = sqrt(err / (2.0f * n)); +} + +void IPPE::PoseSolver::sortPosesByReprojError(cv::InputArray _objectPoints, cv::InputArray _imagePoints, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, cv::InputArray _Ma, cv::InputArray _Mb, cv::OutputArray _M1, cv::OutputArray _M2, float& err1, float& err2) +{ + float erra, errb; + evalReprojError(_objectPoints, _imagePoints, _cameraMatrix, _distCoeffs, _Ma, erra); + evalReprojError(_objectPoints, _imagePoints, _cameraMatrix, _distCoeffs, _Mb, errb); + if (erra < errb) { + err1 = erra; + _Ma.copyTo(_M1); + + err2 = errb; + _Mb.copyTo(_M2); + } + else { + err1 = errb; + _Mb.copyTo(_M1); + + err2 = erra; + _Ma.copyTo(_M2); + } +} + +void HomographyHO::normalizeDataIsotropic(cv::InputArray _Data, cv::OutputArray _DataN, cv::OutputArray _T, cv::OutputArray _Ti) +{ + cv::Mat Data = _Data.getMat(); + int numPoints = Data.rows * Data.cols; + assert((Data.rows == 1) | (Data.cols == 1)); + assert((Data.channels() == 2) | (Data.channels() == 3)); + assert(numPoints >= 4); + + int dataType = _Data.type(); + assert((dataType == CV_64FC2) | (dataType == CV_64FC3) | (dataType == CV_32FC2) | (dataType == CV_32FC3)); + + _DataN.create(2, numPoints, CV_64FC1); + + _T.create(3, 3, CV_64FC1); + _Ti.create(3, 3, CV_64FC1); + + cv::Mat DataN = _DataN.getMat(); + cv::Mat T = _T.getMat(); + cv::Mat Ti = _Ti.getMat(); + + _T.setTo(0); + _Ti.setTo(0); + + double xm, ym; + int numChannels = Data.channels(); + + xm = 0; + ym = 0; + for (int i = 0; i < numPoints; i++) { + if (numChannels == 2) { + if (dataType == CV_32FC2) { + xm = xm + Data.at(i)[0]; + ym = ym + Data.at(i)[1]; + } + else { + xm = xm + Data.at(i)[0]; + ym = ym + Data.at(i)[1]; + } + } + else { + if (dataType == CV_32FC3) { + xm = xm + Data.at(i)[0]; + ym = ym + Data.at(i)[1]; + } + else { + xm = xm + Data.at(i)[0]; + ym = ym + Data.at(i)[1]; + } + } + } + xm = xm / (double)numPoints; + ym = ym / (double)numPoints; + + double kappa = 0; + double xh, yh; + + for (int i = 0; i < numPoints; i++) { + + if (numChannels == 2) { + if (dataType == CV_32FC2) { + xh = Data.at(i)[0] - xm; + yh = Data.at(i)[1] - ym; + } + else { + xh = Data.at(i)[0] - xm; + yh = Data.at(i)[1] - ym; + } + } + else { + if (dataType == CV_32FC3) { + xh = Data.at(i)[0] - xm; + yh = Data.at(i)[1] - ym; + } + else { + xh = Data.at(i)[0] - xm; + yh = Data.at(i)[1] - ym; + } + } + + DataN.at(0, i) = xh; + DataN.at(1, i) = yh; + kappa = kappa + xh * xh + yh * yh; + } + double beta = sqrt(2 * numPoints / kappa); + DataN = DataN * beta; + + T.at(0, 0) = 1.0 / beta; + T.at(1, 1) = 1.0 / beta; + + T.at(0, 2) = xm; + T.at(1, 2) = ym; + + T.at(2, 2) = 1; + + Ti.at(0, 0) = beta; + Ti.at(1, 1) = beta; + + Ti.at(0, 2) = -beta * xm; + Ti.at(1, 2) = -beta * ym; + + Ti.at(2, 2) = 1; +} + +void HomographyHO::homographyHO(cv::InputArray _srcPoints, cv::InputArray _targPoints, cv::OutputArray _H) +{ + + _H.create(3, 3, CV_64FC1); + cv::Mat H = _H.getMat(); + + cv::Mat DataA, DataB, TA, TAi, TB, TBi; + + HomographyHO::normalizeDataIsotropic(_srcPoints, DataA, TA, TAi); + HomographyHO::normalizeDataIsotropic(_targPoints, DataB, TB, TBi); + + int n = DataA.cols; + assert(n == DataB.cols); + + cv::Mat C1(1, n, CV_64FC1); + cv::Mat C2(1, n, CV_64FC1); + cv::Mat C3(1, n, CV_64FC1); + cv::Mat C4(1, n, CV_64FC1); + + cv::Mat Mx(n, 3, CV_64FC1); + cv::Mat My(n, 3, CV_64FC1); + + double mC1, mC2, mC3, mC4; + mC1 = 0; + mC2 = 0; + mC3 = 0; + mC4 = 0; + + for (int i = 0; i < n; i++) { + C1.at(0, i) = -DataB.at(0, i) * DataA.at(0, i); + C2.at(0, i) = -DataB.at(0, i) * DataA.at(1, i); + C3.at(0, i) = -DataB.at(1, i) * DataA.at(0, i); + C4.at(0, i) = -DataB.at(1, i) * DataA.at(1, i); + + mC1 = mC1 + C1.at(0, i); + mC2 = mC2 + C2.at(0, i); + mC3 = mC3 + C3.at(0, i); + mC4 = mC4 + C4.at(0, i); + } + + mC1 = mC1 / n; + mC2 = mC2 / n; + mC3 = mC3 / n; + mC4 = mC4 / n; + + for (int i = 0; i < n; i++) { + Mx.at(i, 0) = C1.at(0, i) - mC1; + Mx.at(i, 1) = C2.at(0, i) - mC2; + Mx.at(i, 2) = -DataB.at(0, i); + + My.at(i, 0) = C3.at(0, i) - mC3; + My.at(i, 1) = C4.at(0, i) - mC4; + My.at(i, 2) = -DataB.at(1, i); + } + + cv::Mat DataAT, DataADataAT, DataADataATi, Pp, Bx, By, Ex, Ey, D; + + cv::transpose(DataA, DataAT); + DataADataAT = DataA * DataAT; + double dt = DataADataAT.at(0, 0) * DataADataAT.at(1, 1) - DataADataAT.at(0, 1) * DataADataAT.at(1, 0); + + DataADataATi = cv::Mat(2, 2, CV_64FC1); + DataADataATi.at(0, 0) = DataADataAT.at(1, 1) / dt; + DataADataATi.at(0, 1) = -DataADataAT.at(0, 1) / dt; + DataADataATi.at(1, 0) = -DataADataAT.at(1, 0) / dt; + DataADataATi.at(1, 1) = DataADataAT.at(0, 0) / dt; + + Pp = DataADataATi * DataA; + + Bx = Pp * Mx; + By = Pp * My; + + Ex = DataAT * Bx; + Ey = DataAT * By; + + D = cv::Mat(2 * n, 3, CV_64FC1); + cv::Mat DT, DDT; + + for (int i = 0; i < n; i++) { + D.at(i, 0) = Mx.at(i, 0) - Ex.at(i, 0); + D.at(i, 1) = Mx.at(i, 1) - Ex.at(i, 1); + D.at(i, 2) = Mx.at(i, 2) - Ex.at(i, 2); + + D.at(i + n, 0) = My.at(i, 0) - Ey.at(i, 0); + D.at(i + n, 1) = My.at(i, 1) - Ey.at(i, 1); + D.at(i + n, 2) = My.at(i, 2) - Ey.at(i, 2); + } + + cv::transpose(D, DT); + DDT = DT * D; + + cv::Mat S, U, V, h12, h45; + double h3, h6; + + cv::eigen(DDT, S, U); + + cv::Mat h789(3, 1, CV_64FC1); + h789.at(0, 0) = U.at(2, 0); + h789.at(1, 0) = U.at(2, 1); + h789.at(2, 0) = U.at(2, 2); + + h12 = -Bx * h789; + h45 = -By * h789; + + h3 = -(mC1 * h789.at(0, 0) + mC2 * h789.at(1, 0)); + h6 = -(mC3 * h789.at(0, 0) + mC4 * h789.at(1, 0)); + + H.at(0, 0) = h12.at(0, 0); + H.at(0, 1) = h12.at(1, 0); + H.at(0, 2) = h3; + + H.at(1, 0) = h45.at(0, 0); + H.at(1, 1) = h45.at(1, 0); + H.at(1, 2) = h6; + + H.at(2, 0) = h789.at(0, 0); + H.at(2, 1) = h789.at(1, 0); + H.at(2, 2) = h789.at(2, 0); + + H = TB * H * TAi; + H = H / H.at(2, 2); +} + + +void IPPE::PoseSolver::rotateVec2ZAxis(InputArray _a, OutputArray _Ra) +{ + _Ra.create(3,3,CV_64FC1); + Mat Ra = _Ra.getMat(); + + double ax = _a.getMat().at(0); + double ay = _a.getMat().at(1); + double az = _a.getMat().at(2); + + double nrm = sqrt(ax*ax + ay*ay + az*az); + ax = ax/nrm; + ay = ay/nrm; + az = az/nrm; + + double c = az; + + if (abs(1.0+c)< std::numeric_limits::epsilon()) + { + Ra.setTo(0.0); + Ra.at(0,0) = 1.0; + Ra.at(1,1) = 1.0; + Ra.at(2,2) = -1.0; + } + else + { + double d = 1.0/(1.0+c); + double ax2 = ax*ax; + double ay2 = ay*ay; + double axay = ax*ay; + + Ra.at(0,0) = - ax2*d + 1.0; + Ra.at(0,1) = -axay*d; + Ra.at(0,2) = -ax; + + Ra.at(1,0) = -axay*d; + Ra.at(1,1) = - ay2*d + 1.0; + Ra.at(1,2) = -ay; + + Ra.at(2,0) = ax; + Ra.at(2,1) = ay; + Ra.at(2,2) = 1.0 - (ax2 + ay2)*d; + } + + +} + +bool IPPE::PoseSolver::computeObjextSpaceR3Pts(InputArray _objectPoints, OutputArray R) +{ + bool ret; //return argument + double p1x,p1y,p1z; + double p2x,p2y,p2z; + double p3x,p3y,p3z; + + cv::Mat objectPoints = _objectPoints.getMat(); + size_t n = objectPoints.rows*objectPoints.cols; + if (objectPoints.type() == CV_32FC3) + { + p1x = objectPoints.at(0)[0]; + p1y = objectPoints.at(0)[1]; + p1z = objectPoints.at(0)[2]; + + p2x = objectPoints.at(1)[0]; + p2y = objectPoints.at(1)[1]; + p2z = objectPoints.at(1)[2]; + + p3x = objectPoints.at(2)[0]; + p3y = objectPoints.at(2)[1]; + p3z = objectPoints.at(2)[2]; + } + else + { + p1x = objectPoints.at(0)[0]; + p1y = objectPoints.at(0)[1]; + p1z = objectPoints.at(0)[2]; + + p2x = objectPoints.at(1)[0]; + p2y = objectPoints.at(1)[1]; + p2z = objectPoints.at(1)[2]; + + p3x = objectPoints.at(2)[0]; + p3y = objectPoints.at(2)[1]; + p3z = objectPoints.at(2)[2]; + } + + double nx = (p1y - p2y)*(p1z - p3z) - (p1y - p3y)*(p1z - p2z); + double ny = (p1x - p3x)*(p1z - p2z) - (p1x - p2x)*(p1z - p3z); + double nz = (p1x - p2x)*(p1y - p3y) - (p1x - p3x)*(p1y - p2y); + + double nrm = sqrt(nx*nx+ ny*ny + nz*nz); + if (nrm>IPPE_SMALL) + { + nx = nx/nrm; + ny = ny/nrm; + nz = nz/nrm; + cv::Mat v(3,1,CV_64FC1); + v.at(0) = nx; + v.at(1) = ny; + v.at(2) = nz; + rotateVec2ZAxis(v,R); + ret = true; + } + else + { + ret = false; + } + return ret; +} + +bool IPPE::PoseSolver::computeObjextSpaceRSvD(InputArray _objectPointsZeroMean, OutputArray _R) +{ + bool ret; //return argument + _R.create(3,3,CV_64FC1); + cv::Mat R = _R.getMat(); + + //we could not compute R with the first three points, so lets use the SVD + cv::SVD s; + cv::Mat W, U, VT; + s.compute(_objectPointsZeroMean.getMat() * _objectPointsZeroMean.getMat().t(), W, U, VT); + double s3 = W.at(2); + double s2 = W.at(1); + + //check if points are coplanar: + assert(s3 / s2 < IPPE_SMALL); + + R = U.t(); + if (cv::determinant(R) < 0) { //this ensures R is a rotation matrix and not a general unitary matrix: + R.at(2, 0) = -R.at(2, 0); + R.at(2, 1) = -R.at(2, 1); + R.at(2, 2) = -R.at(2, 2); + } + ret = true; + return ret; +} diff --git a/thirdparty/aruco-3.1.12/src/ippe.h b/thirdparty/aruco-3.1.12/src/ippe.h new file mode 100644 index 0000000..d53cbb6 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/ippe.h @@ -0,0 +1,278 @@ +// This is the core header file for IPPE. Infinitesimal Plane-based Pose Estimation (IPPE) is a very fast and accurate +// way to compute a camera's pose from a single image of a planar object using point correspondences. This has uses in +// several applications, including augmented reality, 3D tracking and pose estimation with planar markers, and 3D scene +// understanding. +// This package is free and covered by the BSD licence without any warranty. We hope you find this code useful and if so +// please cite our paper in your work: + +//@article{ year={2014}, issn={0920-5691}, journal={International Journal of Computer Vision}, volume={109}, number={3}, +//doi={10.1007/s11263-014-0725-5}, title={Infinitesimal Plane-Based Pose Estimation}, +//url={http://dx.doi.org/10.1007/s11263-014-0725-5}, publisher={Springer US}, keywords={Plane; Pose; SfM; PnP; +//Homography}, author={Collins, Toby and Bartoli, Adrien}, pages={252-286}, language={English} } + +// Please contact Toby (toby.collins@gmail.com) if you have any questions about the code, paper and IPPE. + +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef _IPPE_H_ +#define _IPPE_H_ + +#include +#include +#include "aruco_export.h" +#include + +#define IPPE_SMALL 1e-7 //a small constant used to test 'small' values close to zero. + +namespace aruco +{ + // returns the two solutions + std::vector solvePnP(const std::vector& objPoints, const std::vector& imgPoints, + cv::InputArray cameraMatrix, cv::InputArray distCoeffs); + void solvePnP(const std::vector& objPoints, const std::vector& imgPoints, + cv::InputArray cameraMatrix, cv::InputArray distCoeffs,cv::Mat &rvec,cv::Mat &tvec); + ARUCO_EXPORT std::vector > solvePnP_(float size,const std::vector &imgPoints, cv::InputArray cameraMatrix, cv::InputArray distCoeffs); + + std::vector> solvePnP_(const std::vector& objPoints, + const std::vector& imgPoints, + cv::InputArray cameraMatrix, cv::InputArray distCoeffs); + +} +namespace IPPE { + +class PoseSolver { + +public: + + /** + * @brief PoseSolver constructor + */ + PoseSolver(); + + /** + * @brief PoseSolver destructor + */ + ~PoseSolver(); + + /** + * @brief Finds the two possible poses of a planar object given a set of correspondences and their respective reprojection errors. The poses are sorted with the first having the lowest reprojection error. + * @param _objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points + * @param _imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates. + * @param _cameraMatrix Intrinsic camera matrix (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray() + * @param _distCoeffs Intrinsic camera distortion vector (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray() + * @param _rvec1 First rotation solution (3x1 rotation vector) + * @param _tvec1 First translation solution (3x1 vector) + * @param reprojErr1 Reprojection error of first solution + * @param _rvec2 Second rotation solution (3x1 rotation vector) + * @param _tvec2 Second translation solution (3x1 vector) + * @param reprojErr2 Reprojection error of second solution + */ + void solveGeneric(cv::InputArray _objectPoints, cv::InputArray _imagePoints, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, + cv::OutputArray _rvec1, cv::OutputArray _tvec1, float& reprojErr1, cv::OutputArray _rvec2, cv::OutputArray _tvec2, float& reprojErr2); + + /** @brief Finds the two possible poses of a square planar object and their respective reprojection errors using IPPE. These poses are sorted so that the first one is the one with the lowest reprojection error. + * + * @param _squareLength The square's length (which is also it's width) in object coordinate units (e.g. millimeters, meters, etc.) + * @param _imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates. + * @param _cameraMatrix Intrinsic camera matrix (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray() + * @param _distCoeffs Intrinsic camera distortion vector (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray() + * @param _rvec1 First rotation solution (3x1 rotation vector) + * @param _tvec1 First translation solution (3x1 vector) + * @param reprojErr1 Reprojection error of first solution + * @param _rvec2 Second rotation solution (3x1 rotation vector) + * @param _tvec2 Second translation solution (3x1 vector) + * @param reprojErr2 Reprojection error of second solution + */ + void solveSquare(double squareLength, cv::InputArray _imagePoints, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, + cv::OutputArray _rvec1, cv::OutputArray _tvec1, float& reprojErr1, cv::OutputArray _rvec2, cv::OutputArray _tvec2, float& reprojErr2); + + /** + * @brief Generates the 4 object points of a square planar object + * @param squareLength The square's length (which is also it's width) in object coordinate units (e.g. millimeters, meters, etc.) + * @param _objectPoints Set of 4 object points (1x4 3-channel double) + */ + void generateSquareObjectCorners3D(double squareLength, cv::OutputArray _objectPoints); + + /** + * @brief Generates the 4 object points of a square planar object, without including the z-component (which is z=0 for all points). + * @param squareLength The square's length (which is also it's width) in object coordinate units (e.g. millimeters, meters, etc.) + * @param _objectPoints Set of 4 object points (1x4 2-channel double) + */ + void generateSquareObjectCorners2D(double squareLength, cv::OutputArray _objectPoints); + + /** + * @brief Computes the average depth of an object given its pose in camera coordinates + * @param objectPoints: Object points defined in 3D object space + * @param rvec: Rotation component of pose + * @param tvec: Translation component of pose + * @return: average depth of the object + */ + double meanSceneDepth(cv::InputArray objectPoints, cv::InputArray rvec, cv::InputArray tvec); + +private: + /** + * @brief Finds the two possible poses of a planar object given a set of correspondences in normalized pixel coordinates. These poses are **NOT** sorted on reprojection error. Note that the returned poses are object-to-camera transforms, and not camera-to-object transforms. + * @param _objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double). + * @param _normalizedImagePoints Array of corresponding image points in normalized pixel coordinates, 1xN/Nx1 2-channel (float or double). + * @param _Ma First pose solution (unsorted) + * @param _Mb Second pose solution (unsorted) + */ + void solveGeneric(cv::InputArray _objectPoints, cv::InputArray _normalizedImagePoints, cv::OutputArray _Ma, cv::OutputArray _Mb); + + /** + * @brief Finds the two possible poses of a planar object in its canonical position, given a set of correspondences in normalized pixel coordinates. These poses are **NOT** sorted on reprojection error. Note that the returned poses are object-to-camera transforms, and not camera-to-object transforms. + * @param _canonicalObjPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (double) where N is the number of points + * @param _normalizedInputPoints Array of corresponding image points in normalized pixel coordinates, 1xN/Nx1 2-channel (double) where N is the number of points + * @param _H Homography mapping canonicalObjPoints to normalizedInputPoints. + * @param _Ma + * @param _Mb + */ + void solveCanonicalForm(cv::InputArray _canonicalObjPoints, cv::InputArray _normalizedInputPoints, cv::InputArray _H, + cv::OutputArray _Ma, cv::OutputArray _Mb); + + /** @brief Computes the translation solution for a given rotation solution + * @param _objectPoints Array of corresponding object points, 1xN/Nx1 3-channel where N is the number of points + * @param _normalizedImagePoints Array of corresponding image points (undistorted), 1xN/Nx1 2-channel where N is the number of points + * @param _R Rotation solution (3x1 rotation vector) + * @param _t Translation solution Translation solution (3x1 rotation vector) + */ + void computeTranslation(cv::InputArray _objectPoints, cv::InputArray _normalizedImgPoints, cv::InputArray _R, cv::OutputArray _t); + + /** @brief Computes the two rotation solutions from the Jacobian of a homography matrix H at a point (ux,uy) on the object plane. For highest accuracy the Jacobian should be computed at the centroid of the point correspondences (see the IPPE paper for the explanation of this). For a point (ux,uy) on the object plane, suppose the homography H maps (ux,uy) to a point (p,q) in the image (in normalized pixel coordinates). The Jacobian matrix [J00, J01; J10,J11] is the Jacobian of the mapping evaluated at (ux,uy). + * @param j00 Homography jacobian coefficent at (ux,uy) + * @param j01 Homography jacobian coefficent at (ux,uy) + * @param j10 Homography jacobian coefficent at (ux,uy) + * @param j11 Homography jacobian coefficent at (ux,uy) + * @param p the x coordinate of point (ux,uy) mapped into the image (undistorted and normalized position) + * @param q the y coordinate of point (ux,uy) mapped into the image (undistorted and normalized position) + */ + void computeRotations(double j00, double j01, double j10, double j11, double p, double q, cv::OutputArray _R1, cv::OutputArray _R2); + + /** @brief Closed-form solution for the homography mapping with four corner correspondences of a square (it maps source points to target points). The source points are the four corners of a zero-centred squared defined by: + * point 0: [-squareLength / 2.0, squareLength / 2.0] + * point 1: [squareLength / 2.0, squareLength / 2.0] + * point 2: [squareLength / 2.0, -squareLength / 2.0] + * point 3: [-squareLength / 2.0, -squareLength / 2.0] + * + * @param _targetPoints Array of four corresponding target points, 1x4/4x1 2-channel. Note that the points should be ordered to correspond with points 0, 1, 2 and 3. + * @param halfLength the square's half length (i.e. squareLength/2.0) + * @param _H Homograhy mapping the source points to the target points, 3x3 single channel + */ + void homographyFromSquarePoints(cv::InputArray _targetPoints, double halfLength, cv::OutputArray _H); + + /** @brief Fast conversion from a rotation matrix to a rotation vector using Rodrigues' formula + * @param _R Input rotation matrix, 3x3 1-channel (double) + * @param _r Output rotation vector, 3x1/1x3 1-channel (double) + */ + void rot2vec(cv::InputArray _R, cv::OutputArray _r); + + /** + * @brief Takes a set of planar object points and transforms them to 'canonical' object coordinates This is when they have zero mean and are on the plane z=0 + * @param _objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points + * @param _canonicalObjectPoints Object points in canonical coordinates 1xN/Nx1 2-channel (double) + * @param _MobjectPoints2Canonical Transform matrix mapping _objectPoints to _canonicalObjectPoints: 4x4 1-channel (double) + */ + void makeCanonicalObjectPoints(cv::InputArray _objectPoints, cv::OutputArray _canonicalObjectPoints, cv::OutputArray _MobjectPoints2Canonical); + + /** + * @brief Evaluates the Root Mean Squared (RMS) reprojection error of a pose solution. + * @param _objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points + * @param _imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates. + * @param _cameraMatrix Intrinsic camera matrix (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray(). + * @param _distCoeffs Intrinsic camera distortion vector (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray(). + * @param _M Pose matrix from 3D object to camera coordinates: 4x4 1-channel (double) + * @param err RMS reprojection error + */ + void evalReprojError(cv::InputArray _objectPoints, cv::InputArray _imagePoints, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, cv::InputArray _M, float& err); + + /** + * @brief Sorts two pose solutions according to their RMS reprojection error (lowest first). + * @param _objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points + * @param _imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates. + * @param _cameraMatrix Intrinsic camera matrix (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray(). + * @param _distCoeffs Intrinsic camera distortion vector (same definition as OpenCV). If _imagePoints is in normalized pixel coordinates you must set _cameraMatrix = cv::noArray(). + * @param _Ma Pose matrix 1: 4x4 1-channel + * @param _Mb Pose matrix 2: 4x4 1-channel + * @param _M1 Member of (Ma,Mb} with lowest RMS reprojection error. Performs deep copy. + * @param _M2 Member of (Ma,Mb} with highest RMS reprojection error. Performs deep copy. + * @param err1 RMS reprojection error of _M1 + * @param err2 RMS reprojection error of _M2 + */ + void sortPosesByReprojError(cv::InputArray _objectPoints, cv::InputArray _imagePoints, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, cv::InputArray _Ma, cv::InputArray _Mb, cv::OutputArray _M1, cv::OutputArray _M2, float& err1, float& err2); + + /** + * @brief Finds the rotation _Ra that rotates a vector _a to the z axis (0,0,1) + * @param _a vector: 3x1 mat (double) + * @param _Ra Rotation: 3x3 mat (double) + */ + void rotateVec2ZAxis(cv::InputArray _a, cv::OutputArray _Ra); + + + /** + * @brief Computes the rotation _R that rotates the object points to the plane z=0. This uses the cross-product method with the first three object points. + * @param _objectPoints Array of N>=3 coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points + * @param _R Rotation Mat: 3x3 (double) + * @return success (true) or failure (false) + */ + bool computeObjextSpaceR3Pts(cv::InputArray _objectPoints, cv::OutputArray _R); + + /** + * @brief computeObjextSpaceRSvD Computes the rotation _R that rotates the object points to the plane z=0. This uses the cross-product method with the first three object points. + * @param _objectPointsZeroMean zero-meaned co=planar object points: 3xN matrix (double) where N>=3 + * @param _R Rotation Mat: 3x3 (double) + * @return success (true) or failure (false) + */ + bool computeObjextSpaceRSvD(cv::InputArray _objectPointsZeroMean, cv::OutputArray _R); +}; +} + +namespace HomographyHO { + +/** + * @brief Computes the best-fitting homography matrix from source to target points using Harker and O'Leary's method: + * Harker, M., O'Leary, P., Computation of Homographies, Proceedings of the British Machine Vision Conference 2005, Oxford, England. + * This is not the author's implementation. + * @param srcPoints Array of source points: 1xN/Nx1 2-channel (float or double) where N is the number of points + * @param targPoints Array of target points: 1xN/Nx1 2-channel (float or double) + * @param H Homography from source to target: 3x3 1-channel (double) + */ +void homographyHO(cv::InputArray srcPoints, cv::InputArray targPoints, cv::OutputArray H); + +/** + * @brief Performs data normalization before homography estimation. For details see Hartley, R., Zisserman, A., Multiple View Geometry in Computer Vision, + * Cambridge University Press, Cambridge, 2001 + * @param Data Array of source data points: 1xN/Nx1 2-channel (float or double) where N is the number of points + * @param DataN Normalized data points: 1xN/Nx1 2-channel (float or double) where N is the number of points + * @param T Homogeneous transform from source to normalized: 3x3 1-channel (double) + * @param Ti Homogeneous transform from normalized to source: 3x3 1-channel (double) + */ +void normalizeDataIsotropic(cv::InputArray Data, cv::OutputArray DataN, cv::OutputArray T, cv::OutputArray Ti); +} + +#endif diff --git a/thirdparty/aruco-3.1.12/src/levmarq.h b/thirdparty/aruco-3.1.12/src/levmarq.h new file mode 100644 index 0000000..43230ed --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/levmarq.h @@ -0,0 +1,331 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef ARUCO_MM__LevMarq_H +#define ARUCO_MM__LevMarq_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace aruco{ +// Levenberg-Marquardt method for general problems Inspired in +//@MISC\{IMM2004-03215, +// author = "K. Madsen and H. B. Nielsen and O. Tingleff", +// title = "Methods for Non-Linear Least Squares Problems (2nd ed.)", +// year = "2004", +// pages = "60", +// publisher = "Informatics and Mathematical Modelling, Technical University of Denmark, {DTU}", +// address = "Richard Petersens Plads, Building 321, {DK-}2800 Kgs. Lyngby", +// url = "http://www.ltu.se/cms_fs/1.51590!/nonlinear_least_squares.pdf" +//} +template +class LevMarq{ +public: + + + typedef Eigen::Matrix eVector; + typedef std::function F_z_x; + typedef std::function &)> F_z_J; + LevMarq(); + /** + * @brief Constructor with parms + * @param maxIters maximum number of iterations of the algoritm + * @param minError to stop the algorithm before reaching the max iterations + * @param min_step_error_diff minimum error difference between two iterations. If below this level, then stop. + * @param tau parameter indicating how near the initial solution is estimated to be to the real one. If 1, it means that it is very far and the first + * @param der_epsilon increment to calculate the derivate of the evaluation function + * step will be very short. If near 0, means the opposite. This value is auto calculated in the subsequent iterations. + */ + LevMarq(int maxIters,double minError,double min_step_error_diff=0,double tau=1 ,double der_epsilon=1e-3); + + /** + * @brief setParams + * @param maxIters maximum number of iterations of the algoritm + * @param minError to stop the algorithm before reaching the max iterations + * @param min_step_error_diff minimum error difference between two iterations. If below this level, then stop. + * @param tau parameter indicating how near the initial solution is estimated to be to the real one. If 1, it means that it is very far and the first + * @param der_epsilon increment to calculate the derivate of the evaluation function + * step will be very short. If near 0, means the opposite. This value is auto calculated in the subsequent iterations. + */ + void setParams(int maxIters,double minError,double min_step_error_diff=0,double tau=1 ,double der_epsilon=1e-3); + + /** + * @brief solve non linear minimization problem ||F(z)||, where F(z)=f(z) f(z)^t + * @param z function params 1xP to be estimated. input-output. Contains the result of the optimization + * @param f_z_x evaluation function f(z)=x + * first parameter : z : input. Data is in double precision as a row vector (1xp) + * second parameter : x : output. Data must be returned in double + * @param f_J computes the jacobian of f(z) + * first parameter : z : input. Data is in double precision as a row vector (1xp) + * second parameter : J : output. Data must be returned in double + * @return final error + */ + double solve( eVector &z, F_z_x , F_z_J); + /// Step by step solve mode + + + /** + * @brief init initializes the search engine + * @param z + */ + void init(eVector &z, F_z_x ); + /** + * @brief step gives a step of the search + * @param f_z_x error evaluation function + * @param f_z_J Jacobian function + * @return error of current solution + */ + bool step( F_z_x f_z_x , F_z_J f_z_J); + bool step( F_z_x f_z_x); + /** + * @brief getCurrentSolution returns the current solution + * @param z output + * @return error of the solution + */ + double getCurrentSolution(eVector &z); + /** + * @brief getBestSolution sets in z the best solution up to this moment + * @param z output + * @return error of the solution + */ + double getBestSolution(eVector &z); + + /** Automatic jacobian estimation + * @brief solve non linear minimization problem ||F(z)||, where F(z)=f(z) f(z)^t + * @param z function params 1xP to be estimated. input-output. Contains the result of the optimization + * @param f_z_x evaluation function f(z)=x + * first parameter : z : input. Data is in double precision as a row vector (1xp) + * second parameter : x : output. Data must be returned in double + * @return final error + */ + double solve( eVector &z, F_z_x ); + //to enable verbose mode + bool & verbose(){return _verbose;} + + //sets a callback func call at each step + void setStepCallBackFunc(std::function callback){_step_callback=callback;} + //sets a function that indicates when the algorithm must be stop. returns true if must stop and false otherwise + void setStopFunction( std::function stop_function){_stopFunction=stop_function;} + + void calcDerivates(const eVector & z , Eigen::Matrix &, F_z_x); +private: + int _maxIters; + double _minErrorAllowed,_der_epsilon,_tau,_min_step_error_diff; + bool _verbose; + //-------- + eVector curr_z,x64; + double currErr,prevErr,minErr ; + Eigen::Matrix I,J; + double mu,v; + std::function _step_callback; + std::function _stopFunction; + + +}; + + + +template +LevMarq::LevMarq(){ + _maxIters=1000;_minErrorAllowed=0;_der_epsilon=1e-3;_verbose=false;_tau=1;v=5;_min_step_error_diff=0; +} +/** +* @brief Constructor with parms +* @param maxIters maximum number of iterations of the algoritm +* @param minError to stop the algorithm before reaching the max iterations +* @param min_step_error_diff minimum error difference between two iterations. If below this level, then stop. +* @param tau parameter indicating how near the initial solution is estimated to be to the real one. If 1, it means that it is very far and the first +* @param der_epsilon increment to calculate the derivate of the evaluation function +* step will be very short. If near 0, means the opposite. This value is auto calculated in the subsequent iterations. +*/ +template +LevMarq::LevMarq(int maxIters,double minError,double min_step_error_diff,double tau ,double der_epsilon ){ + _maxIters=maxIters;_minErrorAllowed=minError;_der_epsilon=der_epsilon;_verbose=false;_tau=tau;v=5;_min_step_error_diff=min_step_error_diff; +} + +/** +* @brief setParams +* @param maxIters maximum number of iterations of the algoritm +* @param minError to stop the algorithm before reaching the max iterations +* @param min_step_error_diff minimum error difference between two iterations. If below this level, then stop. +* @param tau parameter indicating how near the initial solution is estimated to be to the real one. If 1, it means that it is very far and the first +* @param der_epsilon increment to calculate the derivate of the evaluation function +* step will be very short. If near 0, means the opposite. This value is auto calculated in the subsequent iterations. +*/ +template +void LevMarq::setParams(int maxIters,double minError,double min_step_error_diff,double tau ,double der_epsilon){ + _maxIters=maxIters; + _minErrorAllowed=minError; + _der_epsilon=der_epsilon; + _tau=tau; + _min_step_error_diff=min_step_error_diff; + +} + + +template +void LevMarq:: calcDerivates(const eVector & z , Eigen::Matrix &J, F_z_x f_z_x) +{ + for (int i=0;i +double LevMarq:: solve( eVector &z, F_z_x f_z_x){ + return solve(z,f_z_x,std::bind(&LevMarq::calcDerivates,this,std::placeholders::_1,std::placeholders::_2,f_z_x)); +} +template +bool LevMarq:: step( F_z_x f_z_x){ + return step(f_z_x,std::bind(&LevMarq::calcDerivates,this,std::placeholders::_1,std::placeholders::_2,f_z_x)); +} + +template +void LevMarq::init(eVector &z, F_z_x f_z_x ){ + curr_z=z; + I.resize(z.rows(),z.rows()); + I.setIdentity(); + f_z_x(curr_z,x64); + minErr=currErr=prevErr=x64.cwiseProduct(x64).sum(); + J.resize(x64.rows(),z.rows()); + mu=-1; + + +} + + +#define splm_get_time(a,b) std::chrono::duration_cast>(a-b).count() + + +template +bool LevMarq::step( F_z_x f_z_x, F_z_J f_J){ + + f_J(curr_z,J); + Eigen::Matrix Jt=J.transpose(); + Eigen::Matrix JtJ=(Jt*J); + + eVector B=-Jt*x64; + if(mu<0){//first time only + int max=0; + for(int j=1;jJtJ(max,max)) max=j; + mu=JtJ(max,max)*_tau; + } + + double gain=0,prev_mu=0; + int ntries=0; + bool isStepAccepted=false; + do{ + //add/update dumping factor to JtJ. + //very efficient in any case, but particularly if initial dump does not produce improvement and must reenter + for(int j=0;j0){ + mu=mu*std::max(double(0.33),1.-pow(2*gain-1,3)); + v=5.f; + currErr=err; + curr_z=estimated_z; + isStepAccepted=true; + } + else{ mu=mu*v; v=v*5;} + + }while(gain<=0 && ntries++<5); + + if (_verbose) std::cout< +#include + +#include +#include +#include "cameraparameters.h" +#include "ippe.h" + +namespace aruco +{ + /** + * + */ + Marker::Marker() + { + id = -1; + ssize = -1; + Rvec.create(3, 1, CV_32FC1); + Tvec.create(3, 1, CV_32FC1); + for (int i = 0; i < 3; i++) + Tvec.at(i, 0) = Rvec.at(i, 0) = -999999; + } + /** + * + */ + Marker::Marker(int _id) + { + id = _id; + ssize = -1; + Rvec.create(3, 1, CV_32FC1); + Tvec.create(3, 1, CV_32FC1); + for (int i = 0; i < 3; i++) + Tvec.at(i, 0) = Rvec.at(i, 0) = -999999; + } + /** + * + */ + Marker::Marker(const Marker& M) + : std::vector(M) + { + M.copyTo(*this); + } + + /** + * + */ + Marker::Marker(const std::vector& corners, int _id) + : std::vector(corners) + { + id = _id; + ssize = -1; + Rvec.create(3, 1, CV_32FC1); + Tvec.create(3, 1, CV_32FC1); + for (int i = 0; i < 3; i++) + Tvec.at(i, 0) = Rvec.at(i, 0) = -999999; + } + + void Marker::copyTo(Marker &m)const{ + m.id=id; + // size of the markers sides in meters + m.ssize=ssize; + // matrices of rotation and translation respect to the camera + Rvec.copyTo(m.Rvec ); + Tvec.copyTo(m.Tvec); + m.resize(size()); + for(size_t i=0;i(i, 0) != -999999) + invalid |= false; + if (Rvec.at(i, 0) != -999999) + invalid |= false; + } + if (invalid) + throw cv::Exception(9003, "extrinsic parameters are not set", "Marker::getModelViewMatrix", __FILE__, + __LINE__); + cv::Mat Rot(3, 3, CV_32FC1), Jacob; + cv::Rodrigues(Rvec, Rot, Jacob); + + double para[3][4]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + para[i][j] = Rot.at(i, j); + // now, add the translation + para[0][3] = Tvec.at(0, 0); + para[1][3] = Tvec.at(1, 0); + para[2][3] = Tvec.at(2, 0); + double scale = 1; + + modelview_matrix[0 + 0 * 4] = para[0][0]; + // R1C2 + modelview_matrix[0 + 1 * 4] = para[0][1]; + modelview_matrix[0 + 2 * 4] = para[0][2]; + modelview_matrix[0 + 3 * 4] = para[0][3]; + // R2 + modelview_matrix[1 + 0 * 4] = para[1][0]; + modelview_matrix[1 + 1 * 4] = para[1][1]; + modelview_matrix[1 + 2 * 4] = para[1][2]; + modelview_matrix[1 + 3 * 4] = para[1][3]; + // R3 + modelview_matrix[2 + 0 * 4] = -para[2][0]; + modelview_matrix[2 + 1 * 4] = -para[2][1]; + modelview_matrix[2 + 2 * 4] = -para[2][2]; + modelview_matrix[2 + 3 * 4] = -para[2][3]; + modelview_matrix[3 + 0 * 4] = 0.0; + modelview_matrix[3 + 1 * 4] = 0.0; + modelview_matrix[3 + 2 * 4] = 0.0; + modelview_matrix[3 + 3 * 4] = 1.0; + if (scale != 0.0) + { + modelview_matrix[12] *= scale; + modelview_matrix[13] *= scale; + modelview_matrix[14] *= scale; + } + } + + /**** + * + */ + void Marker::OgreGetPoseParameters(double position[3], double orientation[4]) + { + // check if paremeters are valid + bool invalid = false; + for (int i = 0; i < 3 && !invalid; i++) + { + if (Tvec.at(i, 0) != -999999) + invalid |= false; + if (Rvec.at(i, 0) != -999999) + invalid |= false; + } + if (invalid) + throw cv::Exception(9003, "extrinsic parameters are not set", "Marker::getModelViewMatrix", __FILE__, + __LINE__); + + // calculate position vector + position[0] = -Tvec.ptr(0)[0]; + position[1] = -Tvec.ptr(0)[1]; + position[2] = +Tvec.ptr(0)[2]; + + // now calculare orientation quaternion + cv::Mat Rot(3, 3, CV_32FC1); + cv::Rodrigues(Rvec, Rot); + + // calculate axes for quaternion + double stAxes[3][3]; + // x axis + stAxes[0][0] = -Rot.at(0, 0); + stAxes[0][1] = -Rot.at(1, 0); + stAxes[0][2] = +Rot.at(2, 0); + // y axis + stAxes[1][0] = -Rot.at(0, 1); + stAxes[1][1] = -Rot.at(1, 1); + stAxes[1][2] = +Rot.at(2, 1); + // for z axis, we use cross product + stAxes[2][0] = stAxes[0][1] * stAxes[1][2] - stAxes[0][2] * stAxes[1][1]; + stAxes[2][1] = -stAxes[0][0] * stAxes[1][2] + stAxes[0][2] * stAxes[1][0]; + stAxes[2][2] = stAxes[0][0] * stAxes[1][1] - stAxes[0][1] * stAxes[1][0]; + + // transposed matrix + double axes[3][3]; + axes[0][0] = stAxes[0][0]; + axes[1][0] = stAxes[0][1]; + axes[2][0] = stAxes[0][2]; + + axes[0][1] = stAxes[1][0]; + axes[1][1] = stAxes[1][1]; + axes[2][1] = stAxes[1][2]; + + axes[0][2] = stAxes[2][0]; + axes[1][2] = stAxes[2][1]; + axes[2][2] = stAxes[2][2]; + + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + double fTrace = axes[0][0] + axes[1][1] + axes[2][2]; + double fRoot; + + if (fTrace > 0.0) + { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = sqrt(fTrace + 1.0); // 2w + orientation[0] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; // 1/(4w) + orientation[1] = (axes[2][1] - axes[1][2]) * fRoot; + orientation[2] = (axes[0][2] - axes[2][0]) * fRoot; + orientation[3] = (axes[1][0] - axes[0][1]) * fRoot; + } + else + { + // |w| <= 1/2 + static unsigned int s_iNext[3] = {1, 2, 0}; + unsigned int i = 0; + if (axes[1][1] > axes[0][0]) + i = 1; + if (axes[2][2] > axes[i][i]) + i = 2; + unsigned int j = s_iNext[i]; + unsigned int k = s_iNext[j]; + + fRoot = sqrt(axes[i][i] - axes[j][j] - axes[k][k] + 1.0); + double* apkQuat[3] = {&orientation[1], &orientation[2], &orientation[3]}; + *apkQuat[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + orientation[0] = (axes[k][j] - axes[j][k]) * fRoot; + *apkQuat[j] = (axes[j][i] + axes[i][j]) * fRoot; + *apkQuat[k] = (axes[k][i] + axes[i][k]) * fRoot; + } + } + + void Marker::draw(cv::Mat& in, cv::Scalar color, int lineWidth, bool writeId, bool writeInfo) const + { + + auto _to_string=[](int i){ + std::stringstream str;str<(lineWidth), 2.f * static_cast(lineWidth)); + cv::rectangle(in, (*this)[0] - p2, (*this)[0] + p2, cv::Scalar(0, 0, 255, 255), -1); + cv::rectangle(in, (*this)[1] - p2, (*this)[1] + p2, cv::Scalar(0, 255, 0, 255), lineWidth); + cv::rectangle(in, (*this)[2] - p2, (*this)[2] + p2, cv::Scalar(255, 0, 0, 255), lineWidth); + + + + if (writeId) + { + // determine the centroid + cv::Point cent(0, 0); + for (int i = 0; i < 4; i++) + { + cent.x += static_cast((*this)[i].x); + cent.y += static_cast((*this)[i].y); + } + cent.x /= 4; + cent.y /= 4; + std::string str; + if(writeInfo) str+= dict_info +":"; + if(writeId)str+=_to_string(id); + float fsize= std::min(3.0f, flineWidth * 0.75f); + cv::putText(in,str, cent-cv::Point(10*flineWidth,0), cv::FONT_HERSHEY_SIMPLEX,fsize, + cv::Scalar(255,255,255)-color, lineWidth,cv::LINE_AA); + } + } + + /** + */ + void Marker::calculateExtrinsics(float markerSize, const CameraParameters& CP, + bool setYPerpendicular) + { + if (!CP.isValid()) + throw cv::Exception(9004, + "!CP.isValid(): invalid camera parameters. It is not possible to calculate extrinsics", + "calculateExtrinsics", __FILE__, __LINE__); + calculateExtrinsics(markerSize, CP.CameraMatrix, CP.Distorsion, setYPerpendicular); + } + + void print(cv::Point3f p, std::string cad) + { + std::cout << cad << " " << p.x << " " << p.y << " " << p.z << std::endl; + } + /** + */ + void Marker::calculateExtrinsics(float markerSizeMeters, cv::Mat camMatrix, cv::Mat distCoeff, + bool setYPerpendicular) + { + if (!isValid()) + throw cv::Exception(9004, "!isValid(): invalid marker. It is not possible to calculate extrinsics", + "calculateExtrinsics", __FILE__, __LINE__); + if (markerSizeMeters <= 0) + throw cv::Exception(9004, "markerSize<=0: invalid markerSize", "calculateExtrinsics", __FILE__, __LINE__); + if (camMatrix.rows == 0 || camMatrix.cols == 0) + throw cv::Exception(9004, "CameraMatrix is empty", "calculateExtrinsics", __FILE__, __LINE__); + + vector objpoints = get3DPoints(markerSizeMeters); + + + cv::Mat raux, taux; +// cv::solvePnP(objpoints, *this, camMatrix, distCoeff, raux, taux); + solvePnP(objpoints, *this,camMatrix, distCoeff,raux,taux); + raux.convertTo(Rvec, CV_32F); + taux.convertTo(Tvec, CV_32F); + // rotate the X axis so that Y is perpendicular to the marker plane + if (setYPerpendicular) + rotateXAxis(Rvec); + ssize = markerSizeMeters; + // cout<<(*this)<(0)[i]=setPrecision(Rvec.ptr(0)[i],100); +// Tvec.ptr(0)[i]=setPrecision(Tvec.ptr(0)[i],1000); +// } + + } + + std::vector Marker::get3DPoints(float msize) + { + float halfSize = msize / 2.f; +// std::vector res(4); +// res[0]=cv::Point3f(-halfSize, halfSize, 0); +// res[1]=cv::Point3f(halfSize, halfSize, 0); +// res[2]=cv::Point3f(halfSize,-halfSize, 0); +// res[3]=cv::Point3f(-halfSize,- halfSize, 0); +// return res; + return {cv::Point3f(-halfSize, halfSize, 0),cv::Point3f(halfSize, halfSize, 0),cv::Point3f(halfSize,-halfSize, 0),cv::Point3f(-halfSize, -halfSize, 0)}; + + } + + void Marker::rotateXAxis( cv::Mat& rotation) + { + cv::Mat R(3, 3, CV_32F); + cv::Rodrigues(rotation, R); + // create a rotation matrix for x axis + cv::Mat RX = cv::Mat::eye(3, 3, CV_32F); + const float angleRad = 3.14159265359f / 2.f; + RX.at(1, 1) = cos(angleRad); + RX.at(1, 2) = -sin(angleRad); + RX.at(2, 1) = sin(angleRad); + RX.at(2, 2) = cos(angleRad); + // now multiply + R = R * RX; + // finally, the the rodrigues back + Rodrigues(R, rotation); + } + + /** + */ + cv::Point2f Marker::getCenter() const + { + cv::Point2f cent(0, 0); + for (size_t i = 0; i < size(); i++) + { + cent.x += (*this)[i].x; + cent.y += (*this)[i].y; + } + cent.x /= float(size()); + cent.y /= float(size()); + return cent; + } + + /** + */ + float Marker::getArea() const + { + assert(size() == 4); + // use the cross products + cv::Point2f v01 = (*this)[1] - (*this)[0]; + cv::Point2f v03 = (*this)[3] - (*this)[0]; + float area1 = fabs(v01.x * v03.y - v01.y * v03.x); + cv::Point2f v21 = (*this)[1] - (*this)[2]; + cv::Point2f v23 = (*this)[3] - (*this)[2]; + float area2 = fabs(v21.x * v23.y - v21.y * v23.x); + return (area2 + area1) / 2.f; + } + /** + */ + float Marker::getPerimeter() const + { + assert(size() == 4); + float sum = 0; + for (int i = 0; i < 4; i++) + sum += static_cast(norm((*this)[i] - (*this)[(i + 1) % 4])); + return sum; + } + + + // saves to a binary stream + void Marker::toStream(std::ostream& str) const + { + assert(Rvec.type() == CV_32F && Tvec.type() == CV_32F); + str.write((char*)&id, sizeof(id)); + str.write((char*)&ssize, sizeof(ssize)); + str.write((char*)Rvec.ptr(0), 3 * sizeof(float)); + str.write((char*)Tvec.ptr(0), 3 * sizeof(float)); + // write the 2d points + uint32_t np = static_cast (size()); + str.write((char*)&np, sizeof(np)); + for (size_t i = 0; i < size(); i++) + str.write((char*)&at(i), sizeof(cv::Point2f)); + //write the additional info + uint32_t s=dict_info.size(); + str.write((char*)&s, sizeof(s)); + str.write((char*)&dict_info[0], dict_info.size()); + s=contourPoints.size(); + str.write((char*)&s, sizeof(s)); + str.write((char*)&contourPoints[0], contourPoints.size()*sizeof(contourPoints[0])); + + } + // reads from a binary stream + void Marker::fromStream(std::istream& str) + { + Rvec.create(1, 3, CV_32F); + Tvec.create(1, 3, CV_32F); + str.read((char*)&id, sizeof(id)); + str.read((char*)&ssize, sizeof(ssize)); + str.read((char*)Rvec.ptr(0), 3 * sizeof(float)); + str.read((char*)Tvec.ptr(0), 3 * sizeof(float)); + uint32_t np; + str.read((char*)&np, sizeof(np)); + resize(np); + for (size_t i = 0; i < size(); i++) + str.read((char*)&(*this)[i], sizeof(cv::Point2f)); + //read the additional info + uint32_t s; + str.read((char*)&s, sizeof(s)); + dict_info.resize(s); + str.read((char*)&dict_info[0], dict_info.size()); + str.read((char*)&s, sizeof(s)); + contourPoints.resize(s); + str.read((char*)&contourPoints[0], contourPoints.size()*sizeof(contourPoints[0])); + } + + cv::Mat Marker::getTransformMatrix()const{ + + cv::Mat T=cv::Mat::eye(4,4,CV_32F); + cv::Mat rot=T.rowRange(0,3).colRange(0,3); + cv::Rodrigues(Rvec,rot); + for(int i=0;i<3;i++) + T.at(i,3)=Tvec.ptr(0)[i]; + return T; + } + + float Marker::getRadius()const{ + + auto center=getCenter(); + + float maxDist=0; + for(auto p:*this) + maxDist=std::max(maxDist, float(cv::norm(p-center)) ); + return maxDist; + } + +} diff --git a/thirdparty/aruco-3.1.12/src/marker.h b/thirdparty/aruco-3.1.12/src/marker.h new file mode 100644 index 0000000..c9ecea4 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/marker.h @@ -0,0 +1,192 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef _Aruco_Marker_H +#define _Aruco_Marker_H + +#include "aruco_export.h" + +#include + +#include +#include +#include + + namespace aruco +{ + /**\brief This class represents a marker. It is a vector of the fours corners ot the marker + * + */ + + class CameraParameters; + class ARUCO_EXPORT Marker : public std::vector + { + public: + // id of the marker + int id; + // size of the markers sides in meters + float ssize; + // matrices of rotation and translation respect to the camera + cv::Mat Rvec, Tvec; + //additional info about the dictionary + std::string dict_info; + //points of the contour + vector contourPoints; + + /** + */ + Marker(); + /** + */ + Marker(int id); + /** + */ + Marker(const Marker& M); + /** + */ + Marker(const std::vector& corners, int _id = -1); + /** + */ + ~Marker() + { + } + /**Indicates if this object is valid + */ + bool isValid() const + { + return id != -1 && size() == 4; + } + + bool isPoseValid()const{return !Rvec.empty() && !Tvec.empty();} + /**Draws this marker in the input image + */ + void draw(cv::Mat& in, cv::Scalar color=cv::Scalar(0,0,255), int lineWidth = -1, bool writeId = true,bool writeInfo=false) const; + + /**Calculates the extrinsics (Rvec and Tvec) of the marker with respect to the camera + * @param markerSize size of the marker side expressed in meters + * @param CP parmeters of the camera + * @param setYPerpendicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z + * axis + */ + void calculateExtrinsics(float markerSize, const CameraParameters& CP, + bool setYPerpendicular = true); + /**Calculates the extrinsics (Rvec and Tvec) of the marker with respect to the camera + * @param markerSize size of the marker side expressed in meters + * @param CameraMatrix matrix with camera parameters (fx,fy,cx,cy) + * @param Distorsion matrix with distorsion parameters (k1,k2,p1,p2) + * @param setYPerpendicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z + * axis + */ + void calculateExtrinsics(float markerSize, cv::Mat CameraMatrix, cv::Mat Distorsion = cv::Mat(), + bool setYPerpendicular = true); + + /**Given the extrinsic camera parameters returns the GL_MODELVIEW matrix for opengl. + * Setting this matrix, the reference coordinate system will be set in this marker + */ + void glGetModelViewMatrix(double modelview_matrix[16]); + + /** + * Returns position vector and orientation quaternion for an Ogre scene node or entity. + * Use: + * ... + * Ogre::Vector3 ogrePos (position[0], position[1], position[2]); + * Ogre::Quaternion ogreOrient (orientation[0], orientation[1], orientation[2], orientation[3]); + * mySceneNode->setPosition( ogrePos ); + * mySceneNode->setOrientation( ogreOrient ); + * ... + */ + void OgreGetPoseParameters(double position[3], double orientation[4]); + + /**Returns the centroid of the marker + */ + cv::Point2f getCenter() const; + /**Returns the perimeter of the marker + */ + float getPerimeter() const; + /**Returns the area + */ + float getArea() const; + /**Returns radius of enclosing circle + */ + float getRadius()const; + /**compares ids + */ + bool operator==(const Marker& m) const + { + return m.id == id; + } + + void copyTo(Marker &m) const; + /**compares ids + */ + Marker & operator=(const Marker& m) ; + + /** + */ + friend bool operator<(const Marker& M1, const Marker& M2) + { + return M1.id < M2.id; + } + /** + */ + friend std::ostream& operator<<(std::ostream& str, const Marker& M){ + str << M.id << "="; + for (int i = 0; i < 4; i++) + str << "(" << M[i].x << "," << M[i].y << ") "; + if( !M.Tvec.empty() && !M.Rvec.empty()){ + str << "Txyz="; + for (int i = 0; i < 3; i++) + str << M.Tvec.ptr(0)[i] << " "; + str << "Rxyz="; + for (int i = 0; i < 3; i++) + str << M.Rvec.ptr(0)[i] << " "; + } + return str; + } + + + // saves to a binary stream + void toStream(std::ostream& str) const; + // reads from a binary stream + void fromStream(std::istream& str); + + // returns the 3d points of a marker wrt its center + static vector get3DPoints(float msize); + //returns the 3d points of this marker wrt its center + inline vector get3DPoints()const{ + return get3DPoints(ssize); + } + + //returns the SE3 (4x4) transform matrix + + cv::Mat getTransformMatrix()const; + private: + void rotateXAxis(cv::Mat& rotation); + }; +} +#endif diff --git a/thirdparty/aruco-3.1.12/src/markerdetector.cpp b/thirdparty/aruco-3.1.12/src/markerdetector.cpp new file mode 100644 index 0000000..48e411d --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markerdetector.cpp @@ -0,0 +1,435 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "markerdetector.h" +#include "cameraparameters.h" +#include "markerlabeler.h" +#include "timers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "aruco_cvversioning.h" +#include "markerdetector_impl.h" + +//#ifdef _DEBUG +//#include +//#endif +using namespace std; +using namespace cv; + +namespace aruco +{ +/************************************ + * + * + * + * + ************************************/ +MarkerDetector::MarkerDetector() +{ + _impl=new MarkerDetector_Impl(); +// _iplm=std::unique_ptr(new MarkerDetector_Impl()); +} +/************************************ + * + * + * + * + ************************************/ +MarkerDetector::MarkerDetector(int dict_type, float error_correction_rate ){ + _impl=new MarkerDetector_Impl(); + setDictionary(dict_type,error_correction_rate); + setDetectionMode(DM_NORMAL); +} +/************************************ + * + * + * + * + ************************************/ +MarkerDetector::MarkerDetector(std::string dict_type, float error_correction_rate ){ + _impl=new MarkerDetector_Impl(); + setDictionary(dict_type,error_correction_rate); + setDetectionMode(DM_NORMAL); + +} +/************************************ + * + * + * + * + ************************************/ + +MarkerDetector::~MarkerDetector() +{ + delete _impl; +} + +void MarkerDetector::setParameters(const Params ¶ms){ + _impl->setParameters(params); +} + +/************************************ + * + * + * + * + ************************************/ +void MarkerDetector::setDetectionMode( DetectionMode dm,float minMarkerSize){ + _impl->_params.setDetectionMode(dm,minMarkerSize); +} + +DetectionMode MarkerDetector::getDetectionMode( ){ +return _impl->_params.detectMode; +} + + + +/************************************ + * + * + * + * + ************************************/ + +std::vector MarkerDetector::detect(const cv::Mat& input) +{ + return _impl->detect(input); +} + +std::vector MarkerDetector::detect(const cv::Mat& input, const CameraParameters& camParams, + float markerSizeMeters, + bool setYPerperdicular) +{ + return _impl->detect(input,camParams,markerSizeMeters,setYPerperdicular); +} + +/************************************ + * + * + * + * + ************************************/ +// void MarkerDetector::detect(const cv::Mat& input, std::vector& detectedMarkers, CameraParameters camParams, +// float markerSizeMeters, bool setYPerpendicular) +// { +// _impl->detect(input,detectedMarkers,camParams,markerSizeMeters,setYPerpendicular); +// } + +void MarkerDetector::detect(const cv::Mat& input, vector>& detectedMarkers, + cv::Mat camMatrix, cv::Mat distCoeff, float markerSizeMeters, + bool setYPerpendicular) +{ + cout<<"\n Camera matrix from markerdetector.cpp: "<detect(input,detectedMarkers,camParams,markerSizeMeters,setYPerpendicular); +} +/**Returns operating params + */ +MarkerDetector::Params MarkerDetector::getParameters() const{return _impl->getParameters();} +/**Returns operating params + */ +MarkerDetector::Params & MarkerDetector::getParameters() {return _impl->getParameters();} + + + +std::vector MarkerDetector::getCandidates()const +{ + return _impl->getCandidates(); +} + +std::vector MarkerDetector::getImagePyramid(){ + return _impl->getImagePyramid(); +} +cv::Ptr MarkerDetector::getMarkerLabeler() +{ + return _impl->getMarkerLabeler(); +} +void MarkerDetector::setMarkerLabeler(cv::Ptr detector) +{ + _impl->setMarkerLabeler(detector); +} + +void MarkerDetector::setDictionary(int dict_type, + float error_correction_rate) +{ + _impl->setDictionary(dict_type,error_correction_rate); +} + + +void MarkerDetector::setDictionary(string dict_type, float error_correction_rate) +{ + _impl->setDictionary(dict_type,error_correction_rate); + +} +cv::Mat MarkerDetector::getThresholdedImage(uint32_t idx) +{ + return _impl->getThresholdedImage(idx); +} + +void MarkerDetector::Params::save(cv::FileStorage &fs)const{ + fs<<"aruco-dictionary"<>aux; + detectMode=getDetectionModeFromString(aux); + } + if ( fs["aruco-thresMethod"].type()!=cv::FileNode::NONE){ + string aux; + fs["aruco-thresMethod"]>>aux; + thresMethod=getCornerThresMethodFromString(aux); + } + if ( fs["aruco-cornerRefinementM"].type()!=cv::FileNode::NONE){ + string aux; + fs["aruco-cornerRefinementM"]>>aux; + cornerRefinementM=getCornerRefinementMethodFromString(aux); + } + + } + + +void MarkerDetector::Params::toStream(std::ostream &str)const{ + str.write((char*)&detectMode,sizeof(detectMode)); + str.write((char*)&maxThreads,sizeof(maxThreads)); + str.write((char*)&borderDistThres,sizeof(borderDistThres)); + str.write((char*)&lowResMarkerSize,sizeof(lowResMarkerSize)); + str.write((char*)&minSize,sizeof(minSize)); + str.write((char*)&minSize_pix,sizeof(minSize_pix)); + str.write((char*)&enclosedMarker,sizeof(enclosedMarker)); + str.write((char*)&thresMethod,sizeof(thresMethod)); + str.write((char*)&NAttemptsAutoThresFix,sizeof(NAttemptsAutoThresFix)); + str.write((char*)&AdaptiveThresWindowSize,sizeof(AdaptiveThresWindowSize)); + str.write((char*)&ThresHold,sizeof(ThresHold)); + str.write((char*)&AdaptiveThresWindowSize_range,sizeof(AdaptiveThresWindowSize_range)); + str.write((char*)&markerWarpPixSize,sizeof(markerWarpPixSize)); + str.write((char*)&cornerRefinementM,sizeof(cornerRefinementM)); + str.write((char*)&autoSize,sizeof(autoSize)); + str.write((char*)&ts,sizeof(pyrfactor)); + str.write((char*)&error_correction_rate,sizeof(error_correction_rate)); + str.write((char*)&trackingMinDetections,sizeof(trackingMinDetections)); + str.write((char*)&closingSize,sizeof(closingSize)); + + + _toStream(dictionary,str); +} +void MarkerDetector::Params::fromStream(std::istream &str) { + str.read((char*)&detectMode,sizeof(detectMode)); + str.read((char*)&maxThreads,sizeof(maxThreads)); + str.read((char*)&borderDistThres,sizeof(borderDistThres)); + str.read((char*)&lowResMarkerSize,sizeof(lowResMarkerSize)); + str.read((char*)&minSize,sizeof(minSize)); + str.read((char*)&minSize_pix,sizeof(minSize_pix)); + str.read((char*)&enclosedMarker,sizeof(enclosedMarker)); + str.read((char*)&thresMethod,sizeof(thresMethod)); + str.read((char*)&NAttemptsAutoThresFix,sizeof(NAttemptsAutoThresFix)); + str.read((char*)&AdaptiveThresWindowSize,sizeof(AdaptiveThresWindowSize)); + str.read((char*)&ThresHold,sizeof(ThresHold)); + str.read((char*)&AdaptiveThresWindowSize_range,sizeof(AdaptiveThresWindowSize_range)); + str.read((char*)&markerWarpPixSize,sizeof(markerWarpPixSize)); + str.read((char*)&cornerRefinementM,sizeof(cornerRefinementM)); + str.read((char*)&autoSize,sizeof(autoSize)); + str.read((char*)&ts,sizeof(pyrfactor)); + str.read((char*)&error_correction_rate,sizeof(error_correction_rate)); + str.read((char*)&trackingMinDetections,sizeof(trackingMinDetections)); + str.read((char*)&closingSize,sizeof(closingSize)); + _fromStream(dictionary,str); +} +/**Saves the configuration of the detector to a file + */ +void MarkerDetector::saveParamsToFile(const std::string &path) const{ + _impl->saveParamsToFile(path); +} + +/**Loads the configuration from a file + */ +void MarkerDetector::loadParamsFromFile(const std::string &path){ + _impl->loadParamsFromFile(path); +} + +void MarkerDetector::toStream(std::ostream &str)const +{ + _impl->toStream(str); + +} + +void MarkerDetector::fromStream(std::istream &str){ + _impl->fromStream(str); +} + +std::string MarkerDetector::Params::toString(DetectionMode dm){ + switch(dm){ + case DM_FAST:return "DM_FAST"; + case DM_NORMAL:return "DM_NORMAL"; + case DM_VIDEO_FAST:return "DM_VIDEO_FAST"; + }; + return "DM_NORMAL"; +} + +DetectionMode MarkerDetector::Params::getDetectionModeFromString(const std::string &str){ + if ( str=="DM_FAST")return DM_FAST; + if ( str=="DM_NORMAL")return DM_NORMAL; + if ( str=="DM_VIDEO_FAST")return DM_VIDEO_FAST; + return DM_NORMAL; + +} + +std::string MarkerDetector::Params::toString(CornerRefinementMethod dm){ + switch(dm){ + case CORNER_LINES:return "CORNER_LINES"; + case CORNER_SUBPIX:return "CORNER_SUBPIX"; + case CORNER_NONE:return "CORNER_NONE"; + }; + return "CORNER_SUBPIX"; +} +CornerRefinementMethod MarkerDetector::Params::getCornerRefinementMethodFromString(const std::string &str){ + if ( str=="CORNER_LINES")return CORNER_LINES; + if ( str=="CORNER_SUBPIX")return CORNER_SUBPIX; + if ( str=="CORNER_NONE")return CORNER_NONE; + return CORNER_SUBPIX; +} +std::string MarkerDetector::Params::toString(MarkerDetector::ThresMethod dm){ + switch(dm){ + case THRES_ADAPTIVE:return "THRES_ADAPTIVE"; + case THRES_AUTO_FIXED:return "THRES_AUTO_FIXED"; + }; + return "THRES_ADAPTIVE"; +} +MarkerDetector::ThresMethod MarkerDetector::Params::getCornerThresMethodFromString(const std::string &str){ + if ( str=="THRES_ADAPTIVE")return THRES_ADAPTIVE; + if ( str=="THRES_AUTO_FIXED")return THRES_AUTO_FIXED; + return THRES_ADAPTIVE; +} +void MarkerDetector::Params::setThresholdMethod( MarkerDetector::ThresMethod method,int thresHold,int wsize,int wsize_range ){ + AdaptiveThresWindowSize=wsize; + thresMethod=method; + if (thresHold==-1){ + if ( method==MarkerDetector::THRES_AUTO_FIXED ) ThresHold=100; + else ThresHold=7; + } + else ThresHold=thresHold; + AdaptiveThresWindowSize_range=wsize_range; +} +void MarkerDetector::Params::setDetectionMode( DetectionMode dm,float minMarkerSize){ + detectMode=dm; + minSize=minMarkerSize; + if(detectMode==DM_NORMAL){ + setAutoSizeSpeedUp(false); + setThresholdMethod(THRES_ADAPTIVE); + } + else if (detectMode==DM_FAST ){ + setAutoSizeSpeedUp(false); + setThresholdMethod(THRES_AUTO_FIXED); + } + else if(detectMode==DM_VIDEO_FAST){ + setThresholdMethod(THRES_AUTO_FIXED); + setAutoSizeSpeedUp(true,0.3); + } +} +void MarkerDetector::Params::setCornerRefinementMethod( CornerRefinementMethod method){ + cornerRefinementM=method; + if(method!=CORNER_SUBPIX) minSize=0; +} + void MarkerDetector::Params::_toStream(const std::string &strg,std::ostream &str){ + uint32_t s=strg.size(); + str.write((char*)&s,sizeof(s)); + str.write(strg.c_str(),strg.size()); +} + void MarkerDetector::Params::_fromStream(std::string &strg,std::istream &str){ + uint32_t s; + str.read((char*)&s,sizeof(s)); + strg.resize(s); + str.read(&strg[0],strg.size()); +} + + void MarkerDetector::cornerUpsample(std::vector >& corners, cv::Size lowResImageSize ){ + _impl->cornerUpsample(corners,lowResImageSize); + } + + + +}; diff --git a/thirdparty/aruco-3.1.12/src/markerdetector.h b/thirdparty/aruco-3.1.12/src/markerdetector.h new file mode 100644 index 0000000..0a295dc --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markerdetector.h @@ -0,0 +1,417 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + + +#ifndef _ARUCO_MarkerDetector_H +#define _ARUCO_MarkerDetector_H + +#include "aruco_export.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "marker.h" + +#include + +namespace aruco +{ + +/** + * @brief The DetectionMode enum defines the different possibilities for detection. + * Specifies the detection mode. We have preset three types of detection modes. These are + * ways to configure the internal parameters for the most typical situations. The modes are: + * - DM_NORMAL: In this mode, the full resolution image is employed for detection and slow threshold method. Use this method when + * you process individual images that are not part of a video sequence and you are not interested in speed. + * + * - DM_FAST: In this mode, there are two main improvements. First, image is threshold using a faster method using a global threshold. + * Also, the full resolution image is employed for detection, but, you could speed up detection even more by indicating a minimum size of the + * markers you will accept. This is set by the variable minMarkerSize which shoud be in range [0,1]. When it is 0, means that you do not set + * a limit in the size of the accepted markers. However, if you set 0.1, it means that markers smaller than 10% of the total image area, will not + * be detected. Then, the detection can be accelated up to orders of magnitude compared to the normal mode. + * + * - DM_VIDEO_FAST: This is similar to DM_FAST, but specially adapted to video processing. In that case, we assume that the observed markers + * when you call to detect() have a size similar to the ones observed in the previous frame. Then, the processing can be speeded up by employing smaller versions + * of the image automatically calculated. + * + */ +enum DetectionMode: int{DM_NORMAL=0,DM_FAST=1,DM_VIDEO_FAST=2}; +/** Method employed to refine the estimation of the corners +* - CORNER_SUBPIX: uses subpixel refinement implemented in opencv +* - CORNER_LINES: uses all the pixels in the corner border to estimate the 4 lines of the square. Then +* estimate the point in which they intersect. In seems that it more robust to noise. However, it only works if input image is not resized. +* So, the value minMarkerSize will be set to 0. +* +* - CORNER_NONE: Does no refinement of the corner. Again, it requires minMakerSize to be 0 +*/ +enum CornerRefinementMethod: int{CORNER_SUBPIX=0,CORNER_LINES=1,CORNER_NONE=2}; + + +class CameraParameters; +class MarkerLabeler; +class MarkerDetector_Impl; +typedef Marker MarkerCandidate; + +/**\brief Main class for marker detection + * + */ + +class ARUCO_EXPORT MarkerDetector +{ + enum ThresMethod: int{THRES_ADAPTIVE=0,THRES_AUTO_FIXED=1 }; + friend class MarkerDetector_Impl; +public: + + /**Operating params + */ + struct ARUCO_EXPORT Params + { + + /**Specifies the detection mode. We have preset three types of detection modes. These are + * ways to configure the internal parameters for the most typical situations. The modes are: + * - DM_NORMAL: In this mode, the full resolution image is employed for detection and slow threshold method. Use this method when + * you process individual images that are not part of a video sequence and you are not interested in speed. + * + * - DM_FAST: In this mode, there are two main improvements. First, image is threshold using a faster method using a global threshold. + * Also, the full resolution image is employed for detection, but, you could speed up detection even more by indicating a minimum size of the + * markers you will accept. This is set by the variable minMarkerSize which shoud be in range [0,1]. When it is 0, means that you do not set + * a limit in the size of the accepted markers. However, if you set 0.1, it means that markers smaller than 10% of the total image area, will not + * be detected. Then, the detection can be accelated up to orders of magnitude compared to the normal mode. + * + * - DM_VIDEO_FAST: This is similar to DM_FAST, but specially adapted to video processing. In that case, we assume that the observed markers + * when you call to detect() have a size similar to the ones observed in the previous frame. Then, the processing can be speeded up by employing smaller versions + * of the image automatically calculated. + * + */ + void setDetectionMode( DetectionMode dm,float minMarkerSize); + + /**Enables/Disbles the detection of enclosed markers. Enclosed markers are markers where corners are like opencv chessboard pattern + */ + void detectEnclosedMarkers(bool do_){enclosedMarker=do_;} + + /**Sets the corner refinement method + * - CORNER_SUBPIX: uses subpixel refinement implemented in opencv + * - CORNER_LINES: uses all the pixels in the corner border to estimate the 4 lines of the square. Then + * estimate the point in which they intersect. In seems that it more robust to noise. However, it only works if input image is not resized. + * So, the value minMarkerSize will be set to 0. + * + * - CORNER_NONE: Does no refinement of the corner. Again, it requires minMakerSize to be 0 + */ + void setCornerRefinementMethod( CornerRefinementMethod method); + + + //----------------------------------------------------------------------------- + // Below this point you probably should not use the functions + /**Sets the thresholding method manually. Do no + */ + void setThresholdMethod(ThresMethod method,int thresHold=-1,int wsize=-1,int wsize_range=0 ); + + + + void setAutoSizeSpeedUp(bool v,float Ts=0.25){autoSize=v;ts=Ts;} + bool getAutoSizeSpeedUp()const{return autoSize;} + + + + + + void save(cv::FileStorage &fs)const; + void load(cv::FileStorage &fs); + + void toStream(std::ostream &str)const; + void fromStream(std::istream &str); + + static std::string toString(DetectionMode dm); + static DetectionMode getDetectionModeFromString(const std::string &str); + static std::string toString(CornerRefinementMethod dm); + static CornerRefinementMethod getCornerRefinementMethodFromString(const std::string &str); + static std::string toString(ThresMethod dm); + static ThresMethod getCornerThresMethodFromString(const std::string &str); + + //Detection mode + + DetectionMode detectMode=DM_NORMAL; + + //maximum number of parallel threads + int maxThreads=1;//-1 means all + + // border around image limits in which corners are not allowed to be detected. (0,1) + float borderDistThres=0.015f; + int lowResMarkerSize=20; //minimum size of a marker in the low resolution image + + // minimum size of a contour lenght. We use the following formula + // minLenght= min ( _minSize_pix , _minSize* Is)*4 + // being Is=max(imageWidth,imageHeight) + // the value _minSize are normalized, thus, depends on camera image size + // However, _minSize_pix is expressed in pixels (you can use the one you prefer) + float minSize=-1;//tau_i in paper + int minSize_pix=-1; + bool enclosedMarker=false;//special treatment for enclosed markers + float error_correction_rate=0; + std::string dictionary="ALL_DICTS"; + //threshold methods + ThresMethod thresMethod=THRES_ADAPTIVE; + int NAttemptsAutoThresFix=3;//number of times that tries a random threshold in case of THRES_AUTO_FIXED + int trackingMinDetections=0;//no tracking + + + // Threshold parameters + int AdaptiveThresWindowSize=-1, ThresHold=7, AdaptiveThresWindowSize_range=0; + // size of the image passedta to the MarkerLabeler + int markerWarpPixSize=5;//tau_c in paper + + CornerRefinementMethod cornerRefinementM=CORNER_SUBPIX; + //enable/disables the method for automatic size estimation for speed up + bool autoSize=false; + float ts=0.25f;//$\tau_s$ is a factor in the range $(0,1]$ that accounts for the camera motion speed. For instance, when $\tau_s=0.1$, it means that in the next frame, $\tau_i$ is such that markers $10\%$ smaller than the smallest marker in the current image will be seek. To avoid loosing track of the markers. If no markers are detected in a frame, $\tau_i$ is set to zero for the next frame so that markers of any size can be detected. + /**Enables automatic image resize according to elements detected in previous frame + * @param v + * @param ts is a factor in the range $(0,1]$ that accounts for the camera motion speed. For instance, when ts=0.1 , it means that in the next frame, $\tau_i$ is such that markers $10\%$ smaller than the smallest marker in the current image will be seek. To avoid loosing track of the markers. + */ + float pyrfactor=2; + int closingSize=0;//enables/disables morph closing operation. The actual param used is closingSize*2+1 + +private: + + static void _toStream(const std::string &strg,std::ostream &str); + static void _fromStream(std::string &strg,std::istream &str); + template + static bool attemtpRead(const std::string &name,Type &var,cv::FileStorage&fs ){ + if ( fs[name].type()!=cv::FileNode::NONE){ + fs[name]>>var; + return true; + } + return false; + } + }; + + /** + * See + */ + MarkerDetector(); + /**Creates indicating the dictionary. See @see setDictionary for further details + * @param dict_type Dictionary employed. See @see setDictionary for further details + * @param error_correction_rate value indicating the correction error allowed. Is in range [0,1]. 0 means no + * correction at all. So + * an erroneous bit will result in discarding the marker. 1, mean full correction. The maximum number of bits + * that can be corrected depends on each ditionary. + * We recommend using values from 0 to 0.5. (in general, this will allow up to 3 bits or correction). */ + MarkerDetector(int dict_type, float error_correction_rate = 0); + MarkerDetector(std::string dict_type, float error_correction_rate = 0); + + /**Saves the configuration of the detector to a file. + */ + void saveParamsToFile(const std::string &path)const; + + /**Loads the configuration from a file. + */ + void loadParamsFromFile(const std::string &path); + + + /** + */ + ~MarkerDetector(); + /**Specifies the detection mode. We have preset three types of detection modes. These are + * ways to configure the internal parameters for the most typical situations. The modes are: + * - DM_NORMAL: In this mode, the full resolution image is employed for detection and slow threshold method. Use this method when + * you process individual images that are not part of a video sequence and you are not interested in speed. + * + * - DM_FAST: In this mode, there are two main improvements. First, image is threshold using a faster method using a global threshold. + * Also, the full resolution image is employed for detection, but, you could speed up detection even more by indicating a minimum size of the + * markers you will accept. This is set by the variable minMarkerSize which shoud be in range [0,1]. When it is 0, means that you do not set + * a limit in the size of the accepted markers. However, if you set 0.1, it means that markers smaller than 10% of the total image area, will not + * be detected. Then, the detection can be accelated up to orders of magnitude compared to the normal mode. + * + * - DM_VIDEO_FAST: This is similar to DM_FAST, but specially adapted to video processing. In that case, we assume that the observed markers + * when you call to detect() have a size similar to the ones observed in the previous frame. Then, the processing can be speeded up by employing smaller versions + * of the image automatically calculated. + * + */ + void setDetectionMode( DetectionMode dm,float minMarkerSize=0); + /**returns current detection mode + */ + DetectionMode getDetectionMode( ); + /**Detects the markers in the image passed + * + * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of + * the markers are detected + * + * @param input input color image + * @param camMatrix intrinsic camera information. + * @param distCoeff camera distorsion coefficient. If set Mat() if is assumed no camera distorion + * @param markerSizeMeters size of the marker sides expressed in meters. If not specified this value, the + * extrinsics of the markers are not detected. + * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z + * axis + * @return vector with the detected markers + */ + std::vector detect(const cv::Mat& input); + std::vector detect(const cv::Mat& input, const CameraParameters& camParams, + float markerSizeMeters, bool setYPerperdicular = false); + + /**Detects the markers in the image passed + * + * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of + * the markers are detected + * + * @param input input color image + * @param detectedMarkers output vector with the markers detected + * @param camParams Camera parameters + * @param markerSizeMeters size of the marker sides expressed in meters + * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the + * Z axis + */ + void detect(const cv::Mat& input, std::vector& detectedMarkers, CameraParameters camParams, + float markerSizeMeters = -1, bool setYPerperdicular = false); + + /**Detects the markers in the image passed + * + * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of + * the markers are detected + * + * NOTE: be sure that the camera matrix is for this image size. If you do not know what I am talking about, use + * functions above and not this one + * @param input input color image + * @param detectedMarkers output vector with the markers detected + * @param camMatrix intrinsic camera information. + * @param distCoeff camera distorsion coefficient. If set Mat() if is assumed no camera distorion + * @param markerSizeMeters size of the marker sides expressed in meters + * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z + * axis + */ + // void detect(const cv::Mat& input, std::vector& detectedMarkers, cv::Mat camMatrix = cv::Mat(), + // cv::Mat distCoeff = cv::Mat(), float markerSizeMeters = -1, + // bool setYPerperdicular = false); + + void detect(const cv::Mat& input, std::vector>& detectedMarkers, cv::Mat camMatrix = cv::Mat(), + cv::Mat distCoeff = cv::Mat(), float markerSizeMeters = -1, + bool setYPerperdicular = false); + + + /**Returns operating params + */ + Params getParameters() const; + /**Returns operating params + */ + Params & getParameters() ; + /** Sets the dictionary to be employed. + * You can choose:ARUCO,//original aruco dictionary. By default + ARUCO_MIP_25h7, + ARUCO_MIP_16h3, + ARUCO_MIP_36h12, **** recommended + ARTAG,// + ARTOOLKITPLUS, + ARTOOLKITPLUSBCH,// + TAG16h5,TAG25h7,TAG25h9,TAG36h11,TAG36h10//APRIL TAGS DICIONARIES + CHILITAGS,//chili tags dictionary . NOT RECOMMENDED. It has distance 0. Markers 806 and 682 + should not be used!!! + + If dict_type is none of the above ones, it is assumed you mean a CUSTOM dicionary saved in a file @see + Dictionary::loadFromFile + Then, it tries to open it + */ + void setDictionary(std::string dict_type, float error_correction_rate = 0); + + /** + * @brief setDictionary Specifies the dictionary you want to use for marker decoding + * @param dict_type dictionary employed for decoding markers @see Dictionary + * @param error_correction_rate value indicating the correction error allowed. Is in range [0,1]. 0 means no + * correction at all. So + * an erroneous bit will result in discarding the marker. 1, mean full correction. The maximum number of bits + * that can be corrected depends on each ditionary. + * We recommend using values from 0 to 0.5. (in general, this will allow up to 3 bits or correction). + */ + void setDictionary(int dict_type, float error_correction_rate = 0); + + /** + * Returns a reference to the internal image thresholded. Since there can be generated many of them, specify which + */ + cv::Mat getThresholdedImage(uint32_t idx=0); + /**returns the number of thresholed images available + */ + // size_t getNhresholdedImages()const{return _thres_Images.size();} + + + + ///------------------------------------------------- + /// Methods you may not need + /// Thesde methods do the hard work. They have been set public in case you want to do customizations + ///------------------------------------------------- + + /** + * @brief setMakerLabeler sets the labeler employed to analyze the squares and extract the inner binary code + * @param detector + */ + void setMarkerLabeler(cv::Ptr detector); + cv::Ptr getMarkerLabeler(); + + + /**Returns a list candidates to be markers (rectangles), for which no valid id was found after calling + * detectRectangles + */ + std::vector getCandidates()const; + + std::vector getImagePyramid(); + /* + * @param corners vectors of vectors + */ + void cornerUpsample(std::vector >& corners, cv::Size lowResImageSize ); + void cornerUpsample(std::vector& corners, cv::Size lowResImageSize ); + + /** + * Given the iput image with markers, creates an output image with it in the canonical position + * @param in input image + * @param out image with the marker + * @param size of out + * @param points 4 corners of the marker in the image in + * @return true if the operation succeed + */ + bool warp(cv::Mat& in, cv::Mat& out, cv::Size size, std::vector points); + + + //serialization in binary mode + void toStream(std::ostream &str)const; + void fromStream(std::istream &str); + //configure the detector from a set of parameters + void setParameters(const Params ¶ms); + + + + + + private: + MarkerDetector_Impl *_impl; + + + }; +}; +#endif diff --git a/thirdparty/aruco-3.1.12/src/markerdetector_impl.cpp b/thirdparty/aruco-3.1.12/src/markerdetector_impl.cpp new file mode 100644 index 0000000..4fb7c44 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markerdetector_impl.cpp @@ -0,0 +1,1659 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "markerdetector_impl.h" +#include "cameraparameters.h" +#include "markerlabeler.h" +#include "timers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "aruco_cvversioning.h" +//#include "picoflann.h" + +//#ifdef _DEBUG +//#include +//#endif +using namespace std; +using namespace cv; + +namespace aruco +{ +/************************************ + * + * + * + * + ************************************/ +MarkerDetector_Impl::MarkerDetector_Impl() +{ + markerIdDetector = aruco::MarkerLabeler::create(Dictionary::ALL_DICTS); + setDetectionMode(DM_NORMAL); +} +/************************************ + * + * + * + * + ************************************/ +MarkerDetector_Impl::MarkerDetector_Impl(int dict_type, float error_correction_rate ){ + setDictionary(dict_type,error_correction_rate); + setDetectionMode(DM_NORMAL); +} +/************************************ + * + * + * + * + ************************************/ +MarkerDetector_Impl::MarkerDetector_Impl(std::string dict_type, float error_correction_rate ){ + setDictionary(dict_type,error_correction_rate); + setDetectionMode(DM_NORMAL); + +} +/************************************ + * + * + * + * + ************************************/ + +MarkerDetector_Impl::~MarkerDetector_Impl() +{ +} + +void MarkerDetector_Impl::setParameters(const MarkerDetector::Params ¶ms){ + _params=params; + setDictionary(_params.dictionary,_params.error_correction_rate); +} + +/************************************ + * + * + * + * + ************************************/ +void MarkerDetector_Impl::setDetectionMode( DetectionMode dm,float minMarkerSize){ + _params.setDetectionMode(dm,minMarkerSize); +} + +DetectionMode MarkerDetector_Impl::getDetectionMode( ){ +return _params.detectMode; +} + + + +/************************************ + * + * + * + * + ************************************/ + +std::vector MarkerDetector_Impl::detect(const cv::Mat& input) +{ + std::vector detectedMarkers; + detect(input, detectedMarkers); + return detectedMarkers; +} + +std::vector MarkerDetector_Impl::detect(const cv::Mat& input, const CameraParameters& camParams, + float markerSizeMeters, + bool setYPerperdicular) +{ + std::vector detectedMarkers; + detect(input, detectedMarkers, camParams, markerSizeMeters, setYPerperdicular); + return detectedMarkers; +} + +/************************************ + * + * + * + * + ************************************/ +void MarkerDetector_Impl::detect(const cv::Mat& input, std::vector& detectedMarkers, CameraParameters camParams, + float markerSizeMeters, bool setYPerpendicular) +{ + if (camParams.CamSize != input.size() && camParams.isValid() && markerSizeMeters > 0) + { + // must resize camera parameters if we want to compute properly marker poses + CameraParameters cp_aux = camParams; + cp_aux.resize(input.size()); + detect(input, detectedMarkers, cp_aux.CameraMatrix, cp_aux.Distorsion, markerSizeMeters, setYPerpendicular); + } + else + { + detect(input, detectedMarkers, camParams.CameraMatrix, camParams.Distorsion, markerSizeMeters,setYPerpendicular); + } +} +int MarkerDetector_Impl::getMarkerWarpSize(){ + + auto bis=markerIdDetector->getBestInputSize(); + if ( bis!=-1) return bis; + + int ndiv= markerIdDetector->getNSubdivisions(); + if (ndiv==-1) ndiv=7;//set any possible value(it is used for non dictionary based labelers) + return _params.markerWarpPixSize*ndiv;//this is the minimum size that the smallest marker will have + +} + + + + + +void MarkerDetector_Impl::buildPyramid(vector &ImagePyramid,const cv::Mat &grey,int minSize){ + //determine numbre of pyramid images + int npyrimg=1; + cv::Size imgpsize=grey.size(); + while (imgpsize.width > minSize) { imgpsize=cv::Size(imgpsize.width/_params.pyrfactor,imgpsize.height/_params.pyrfactor) ;npyrimg++;} + + ImagePyramid.resize(npyrimg); + imagePyramid[0]=grey; + //now, create pyramid images + imgpsize=grey.size(); + for(int i=1;i& kpoints, bool norm, int wsize) + { + if(im.type()!=CV_8UC1)throw std::runtime_error("assignClass_fast Input image must be 8UC1"); + int wsizeFull=wsize*2+1; + + cv::Mat labels = cv::Mat::zeros(wsize*2+1,wsize*2+1,CV_8UC1); + cv::Mat thresIm=cv::Mat(wsize*2+1,wsize*2+1,CV_8UC1); + + for(auto &kp:kpoints) + { + int x = kp.pt.x+0.5f; + int y = kp.pt.y+0.5f; + + cv::Rect r= cv::Rect(x-wsize,y-wsize,wsize*2+1,wsize*2+1); + //Check boundaries + if(r.x<0 || r.x+r.width>im.cols || r.y<0 || r.y+r.height>im.rows) continue; + + + int endX=r.x+r.width; + int endY=r.y+r.height; + uchar minV=255,maxV=0; + for(int y=r.y; y(y); + for(int x=r.x; xptr[x]) minV=ptr[x]; + if(maxV( r.y+y)+r.x; + uchar *thresPtr= thresIm.ptr(y); + for(int x=0; xthres) { + nZ++; + thresPtr[x]=255; + } + else thresPtr[x]=0; + } + } + //set all to zero labels.setTo(cv::Scalar::all(0)); + for(int y=0; y(y); + for(int x=0; x unions; + for(int y=0; y(y); + uchar *labelsPtr=labels.ptr(y); + for(int x=0; x -1) + { + if(reg == thresPtr[x-1]) + lleft_px =labelsPtr[x-1]; + } + + if(y-1 > -1) + { + if(reg ==thresIm.ptr(y-1) [x] )//thresIm.at(y-1, x) + ltop_px = labels.at(y-1, x); + } + + if(lleft_px==0 && ltop_px==0) + labelsPtr[x] = newLab++; + + else if(lleft_px!=0 && ltop_px!=0) + { + if(lleft_px < ltop_px) + { + labelsPtr[x] = lleft_px; + unions[ltop_px] = lleft_px; + } + else if(lleft_px > ltop_px) + { + labelsPtr[x] = ltop_px; + unions[lleft_px] = ltop_px; + } + else + {//IGuales + labelsPtr[x] = ltop_px; + } + } + else + { + if(lleft_px!=0) labelsPtr[x] = lleft_px; + else labelsPtr[x] = ltop_px; + } + } + } + + int nc= newLab-1 - unions.size(); + if(nc==2) + { + if(nZ > thresIm.total()-nZ) kp.class_id = 0; + else kp.class_id = 1; + } + else if (nc > 2) { + kp.class_id = 2; + + + } + } + } + +//void fastCorners{ +// vector kpts; +// cv::FAST(input,kpts,7); + +// // Adapter. +// // Given an Point2f element, it returns the element of the dimension specified such that dim=0 is x and dim=1 is y +// struct PicoFlann_Point2fAdapter{ +// inline float operator( )(const cv::KeyPoint &kp, int dim)const { return dim==0?kp.pt.x:kp.pt.y; } +// }; + +// picoflann::KdTreeIndex<2,PicoFlann_Point2fAdapter> kdtree;//2 is the number of dimensions +// kdtree.build(kpts); +// //search 10 nearest neibors to point (0,0) +// vector maxima(kpts.size(),true); +// for(auto &kpt:kpts){ +// kpt.size=1;//used +// kpt.class_id=-1;//not yet +// } + +// int classIdx=0; +// vector > center_weight; +// center_weight.reserve(kpts.size()); + +// for(size_t i=0;i > res=kdtree.radiusSearch(kpts,kpts[i],7); +// //compute the one with max response +// for(auto p:res){ +// if( kpts[p.first].response>= kpts[maxResponseIdx].response && kpts[p.first].size) +// maxResponseIdx= p.first ; +// } +// auto &maxKp=kpts[ maxResponseIdx]; + +// if(maxKp.class_id==-1){ +// maxKp.class_id=int(center_weight.size()); +// center_weight.push_back( { maxKp.pt*maxKp.response,maxKp.response}); +// } + +// //the others are non maxima +// for(auto p:res){ +// if(p.first==maxResponseIdx) continue; +// auto &kp=kpts[p.first]; +// if( kp.size!=0 && kp.class_id==-1){ +// kp.size=0;//sets as non maxiima +// center_weight[maxKp.class_id].first+=kp.pt*kp.response; +// center_weight[maxKp.class_id].second+=kp.response; +// } +// } +// } +// } + +// //take only the selected ones +// kpts.clear(); +// for(auto cw:center_weight){ +// cv::KeyPoint kp; +// kp.pt= cw.first*(1./cw.second); +// kpts.push_back(kp); +// } + +//// kpts.erase(std::remove_if(kpts.begin(),kpts.end(), [ ](const cv::KeyPoint &kp){return kp.class_id==0;}), kpts.end()); + +// assignClass_fast(input,kpts,false,5); + + +// cv::Mat im2; +// cv::cvtColor(input,im2,CV_GRAY2BGR); +// for(int i=0;i< kpts.size();i++){ +// auto & kp=kpts[i]; + +// if(kp.class_id==2){ + +// cv::rectangle(auxThresImage,kp.pt-cv::Point2f(2,2),kp.pt+cv::Point2f(2,2),cv::Scalar(0,0,0),-1); +// cv::rectangle(im2,kp.pt-cv::Point2f(2,2),kp.pt+cv::Point2f(2,2),cv::Scalar(255,0,123)); +// } +// else +// cv::rectangle(im2,kp.pt-cv::Point2f(2,2),kp.pt+cv::Point2f(2,2),cv::Scalar(125,125,125)); +// } + +//} +/************************************************** + * + */ + +vector< MarkerDetector_Impl::MarkerCandidate> MarkerDetector_Impl::thresholdAndDetectRectangles(const cv::Mat & input, int thres_param1,int thres_param2,bool enclosed,cv::Mat &auxThresImage){ + // ensure that _thresParam1%2==1 + __ARUCO_ADDTIMER__; + if (thres_param1 < 3) thres_param1 = 3; + else if (((int)thres_param1) % 2 != 1) thres_param1 = (int)(thres_param1 + 1); + + int enclosedOffset=-1; + cv::Mat auxImage; + //if ( !erode) + auxImage=auxThresImage; + if (_params.thresMethod==MarkerDetector::THRES_AUTO_FIXED){ + cv::threshold(input, auxImage, static_cast(thres_param2),255,THRESH_BINARY_INV); + if(enclosed){ + cv::Mat aux1; + enclosedOffset=int(std::max(3.0, 3./1920. * float(auxImage.cols) )); + if (enclosedOffset%2==0) enclosedOffset++; + cv::erode(auxImage,aux1, getStructuringElement( MORPH_CROSS,cv::Size( enclosedOffset, enclosedOffset ),cv::Point( enclosedOffset/2,enclosedOffset/2 ) )); + cv::bitwise_xor(aux1,auxImage,auxImage); + + __ARUCO_TIMER_EVENT__("erode"); + } + } + else { + cv::adaptiveThreshold(input, auxImage, 255., ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, static_cast(thres_param1), static_cast(thres_param2)); + if(_params.closingSize>0){ + int p=_params.closingSize*2+1; + cv::Mat im2; + cv::Mat ker=cv::getStructuringElement(cv::MORPH_RECT,cv::Size(p,p) ); + cv::morphologyEx(auxImage,im2,cv::MORPH_OPEN,ker); + auxThresImage=im2; + } + enclosedOffset=thres_param1; + } + __ARUCO_TIMER_EVENT__("thres"); + + +//fastCorners () + + + vector MarkerCanditates; + // calcualte the min_max contour sizes + int thisImageMinSize=int( 3.5*float(_params.lowResMarkerSize)); + //if image is eroded, minSize must be adapted + std::vector hierarchy; + std::vector> contours; + cv::findContours(auxThresImage, contours, cv::noArray(), cv::RETR_LIST, cv::CHAIN_APPROX_NONE); + __ARUCO_TIMER_EVENT__("find-cont"); + vector approxCurve; +//#define _aruco_debug_detectrectangles +#ifdef _aruco_debug_detectrectangles + cv::Mat simage; + cv::cvtColor(input,simage,CV_GRAY2BGR); +#endif + + /// for each contour, analyze if it is a paralelepiped likely to be the marker + for (unsigned int i = 0; i < contours.size(); i++) + { +#ifdef _aruco_debug_detectrectangles + drawContour(simage, contours[i], Scalar(125, 125, 255) ); +#endif + // check it is a possible element by first checking that is is large enough + if (thisImageMinSize < int(contours[i].size()) ) + { + // can approximate to a convex rect? + cv::approxPolyDP(contours[i], approxCurve, double(contours[i].size()) * 0.05, true); + + if (approxCurve.size() == 4 && cv::isContourConvex(approxCurve)) + { +#ifdef _aruco_debug_detectrectangles + drawApproxCurve(simage, approxCurve, Scalar(255, 0, 255),1); +#endif + // ensure that the distace between consecutive points is large enough + float minDist = std::numeric_limits::max(); + for (int j = 0; j < 4; j++) + { + float d = cv::norm(approxCurve[j]-approxCurve[(j + 1) % 4] ); + if (d < minDist) minDist = d; + } + + // add the points + MarkerCanditates.push_back(MarkerCandidate()); + for (int j = 0; j < 4; j++) + MarkerCanditates.back().push_back( Point2f(static_cast(approxCurve[j].x),static_cast(approxCurve[j].y))); + //now, if it is eroded, must enalrge 1 bit the corners to go to the real location + //for each opposite pair, take the line joining them and move one pixel apart + //ideally, Bresenham's algorithm should be used + if (enclosed) { + enlargeMarkerCandidate(MarkerCanditates.back(),float(enclosedOffset)/2.); + } +#ifdef _aruco_debug_detectrectangles + MarkerCanditates.back().draw(simage,Scalar(255, 255, 0),1,false); +#endif + MarkerCanditates.back().contourPoints=contours[i]; + + } + + } + } +#ifdef _aruco_debug_detectrectangles + cv::imshow("contours",simage); +#endif + return MarkerCanditates; +} + + +void MarkerDetector_Impl::thresholdAndDetectRectangles_thread( ){ + while(true){ + bool enclosed=false; + auto tad=_tasks.pop(); + if (tad.task==EXIT_TASK) return; + else if (tad.task==ENCLOSE_TASK) enclosed=true; + _vcandidates[tad.outIdx]=thresholdAndDetectRectangles(_thres_Images[tad.inIdx],tad.param1,tad.param2,enclosed,_thres_Images[tad.outIdx]); + + }; +} + +vector MarkerDetector_Impl::thresholdAndDetectRectangles(const cv::Mat &image ){ + + // compute the different values of param1 + + int adaptiveWindowSize=_params.AdaptiveThresWindowSize; + if (_params.AdaptiveThresWindowSize==-1) + adaptiveWindowSize=max(int(3),int(15*float(image.cols)/1920.)); + if(adaptiveWindowSize%2==0)adaptiveWindowSize++; + + // adaptiveWindowSize=15; + + vector p1_values; + for (int i = static_cast(std::max(3., adaptiveWindowSize- 2. * _params.AdaptiveThresWindowSize_range)); i <= adaptiveWindowSize + 2 * _params.AdaptiveThresWindowSize_range; i += 2) + p1_values.push_back(i); + + size_t nimages=p1_values.size(); + _tooNearDistance=p1_values.back(); + _vcandidates.resize(nimages); + _thres_Images.resize(nimages+1); + _thres_Images.back()=image; //add at the end the original image + //first, thresholded images + ThresAndDetectRectTASK tad; + vector vtad; + + + ThreadTasks task=THRESHOLD_TASK; + if (_params.enclosedMarker) task=ENCLOSE_TASK; + for (size_t i = 0; i < p1_values.size(); i++){ + tad.inIdx=int(_thres_Images.size()-1); + tad.param1=p1_values[i]; + tad.param2=_params.ThresHold; + tad.outIdx=i; + tad.task=task; + _tasks.push(tad); + vtad.push_back(tad); + } + + + //reserve images + for(size_t i=0;i threads; + for(int i=0;i joined; + joinVectors( _vcandidates,joined); + return joined; + +} + +vector< MarkerDetector_Impl::MarkerCandidate> MarkerDetector_Impl::prefilterCandidates( vector &MarkerCanditates,cv::Size imgSize){ + ///////////////////////////////////////////////////////////////////////////////////// + /// CANDIDATE PREFILTERING- Merge and Remove candidates so that only reliable ones are returned + ////////////////////////////////////////////////////////////////////////////////// + /// sort the points in anti-clockwise order + valarray swapped(false, MarkerCanditates.size()); // used later + for (unsigned int i = 0; i < MarkerCanditates.size(); i++) + { + // trace a line between the first and second point. + // if the thrid point is at the right side, then the points are anti-clockwise + double dx1 = MarkerCanditates[i][1].x - MarkerCanditates[i][0].x; + double dy1 = MarkerCanditates[i][1].y - MarkerCanditates[i][0].y; + double dx2 = MarkerCanditates[i][2].x - MarkerCanditates[i][0].x; + double dy2 = MarkerCanditates[i][2].y - MarkerCanditates[i][0].y; + double o = (dx1 * dy2) - (dy1 * dx2); + + if (o < 0.0) + { // if the third point is in the left side, then sort in anti-clockwise order + swap(MarkerCanditates[i][1], MarkerCanditates[i][3]); + swapped[i] = true; + } + } + + if(0){ + + /// remove these elements which corners are too close to each other + // first detect candidates to be removed + vector> TooNearCandidates; + for (unsigned int i = 0; i < MarkerCanditates.size(); i++) + { + // calculate the average distance of each corner to the nearest corner of the other marker candidate + for (unsigned int j = i + 1; j < MarkerCanditates.size(); j++) + { + valarray vdist(4); + for (int c = 0; c < 4; c++) + vdist[c] =cv::norm( MarkerCanditates[i][c] -MarkerCanditates[j][c]); + // if distance is too small + if (vdist[0] < _tooNearDistance && vdist[1] < _tooNearDistance && vdist[2] < _tooNearDistance && vdist[3] < _tooNearDistance) + TooNearCandidates.push_back(pair(i, j)); + } + } + + /// mark for removal the element of the pair with smaller perimeter + vector toRemove( MarkerCanditates.size(),false); + for (unsigned int i = 0; i < TooNearCandidates.size(); i++) + { + if (perimeter(MarkerCanditates[TooNearCandidates[i].first]) > perimeter(MarkerCanditates[TooNearCandidates[i].second])) + toRemove[TooNearCandidates[i].second] = true; + else + toRemove[TooNearCandidates[i].first] = true; + } + + /// find these too near borders and remove them + // remove markers with corners too near the image limits + int borderDistThresX = static_cast(_params.borderDistThres * float(imgSize.width)); + int borderDistThresY = static_cast(_params.borderDistThres * float(imgSize.height)); + for (size_t i = 0; i < MarkerCanditates.size(); i++) + { + // delete if any of the corners is too near image border + for (size_t c = 0; c < MarkerCanditates[i].size(); c++) + { + if (MarkerCanditates[i][c].x < borderDistThresX || MarkerCanditates[i][c].y < borderDistThresY + || MarkerCanditates[i][c].x > imgSize.width - borderDistThresX + || MarkerCanditates[i][c].y > imgSize.height - borderDistThresY) + { + toRemove[i] = true; + } + } + } + + //move to output only valid ones + vector finalCandidates; + finalCandidates.reserve(MarkerCanditates.size()); + for(size_t i=0;i&hist) +{ + for(int y=0;y(y); + for(int x=0;x &hist){ + float sum=0,invsum; + for(auto c:hist) sum+=c; + invsum=1./sum; + for(auto &c:hist) c*=invsum; + + float maxVar=0; + int bestT=-1; + for(int t=1;t<256;t++){ + float w0=0,w1=0,mean0=0,mean1=0; + for(int v=0;v1e-4 && w1>1e-4){ + mean0/=w0; + mean1/=w1; + float var=w0*w1*(mean0-mean1)*(mean0-mean1); + //cout<maxVar){ + maxVar=var; + bestT=t; + } + } + + } + return bestT; +} +/************************************ + * Main detection function. Performs all steps + ************************************/ +void MarkerDetector_Impl::detect(const cv::Mat& input, vector& detectedMarkers, Mat camMatrix, Mat distCoeff, + float markerSizeMeters, bool setYPerpendicular) +{ + // clear input data + detectedMarkers.clear(); + _vcandidates.clear(); + _candidates_wcontour.clear(); + __ARUCO_ADDTIMER__; + + + // it must be a 3 channel image + if (input.type() == CV_8UC3) + cv::cvtColor(input,grey,CV_BGR2GRAY); + // convertToGray(input, grey); + else grey = input; + __ARUCO_TIMER_EVENT__("ConvertGrey"); + + ////////////////////////////////////////////////////////////////////// + ///CREATE LOW RESOLUTION IMAGE IN WHICH MARKERS WILL BE DETECTED + ////////////////////////////////////////////////////////////////////// + float ResizeFactor=1; + //use the minimum and markerWarpSize to determine the optimal image size on which to do rectangle detection + cv::Mat imgToBeThresHolded ; + cv::Size maxImageSize=grey.size(); + auto minpixsize=getMinMarkerSizePix(input.size());//min pixel size of the marker in the original image + if ( _params.lowResMarkerSize1) + buildPyramidThread=std::thread([&]{buildPyramid(imagePyramid,grey ,2*getMarkerWarpSize());}); + else buildPyramid(imagePyramid,grey,2*getMarkerWarpSize()); + __ARUCO_TIMER_EVENT__("BuildPyramid"); + } + else{ + imagePyramid.resize(1); + imagePyramid[0]=grey; + } + + + int nAttemptsAutoFix=0; + bool keepLookingFor=false; + std::vector hist(256,0); + do{ + /////////////////////////////////////////////////////////////////////////// + /// THRESHOLD IMAGES AND DETECT INITIAL RECTANGLES + /////////////////////////////////////////////////////////////////////////// + vector MarkerCanditates; + MarkerCanditates=thresholdAndDetectRectangles(imgToBeThresHolded ); + thres =_thres_Images[0]; + +// _debug_exec(10, +// {//only executes when compiled in DEBUG mode if debug level is at least 10 +// //show the thresholded images +// for (size_t i = 0; i < _thres_Images.size(); i++) { +// stringstream sstr; sstr << "thres-" << i; +// cv::namedWindow(sstr.str(),cv::WINDOW_NORMAL); +// cv::imshow(sstr.str(),_thres_Images[i]); +// }}); + + + __ARUCO_TIMER_EVENT__("Threshold and Detect rectangles"); + //prefilter candidates +// _debug_exec(10,//only executes when compiled in DEBUG mode if debug level is at least 10 +// //show the thresholded images +// cv::Mat imrect; +// cv::cvtColor(imgToBeThresHolded,imrect,CV_GRAY2BGR); +// for(auto m: MarkerCanditates ) +// m.draw(imrect,cv::Scalar(0,245,0)); +// cv::imshow("rect-nofiltered",imrect); +// ); + + MarkerCanditates=prefilterCandidates(MarkerCanditates,imgToBeThresHolded.size()); + + __ARUCO_TIMER_EVENT__("prefilterCandidates"); + +// _debug_exec(10,//only executes when compiled in DEBUG mode if debug level is at least 10 +// //show the thresholded images +// cv::Mat imrect; +// cv::cvtColor(imgToBeThresHolded,imrect,CV_GRAY2BGR); +// for(auto m: MarkerCanditates) +// m.draw(imrect,cv::Scalar(0,245,0)); +// cv::imshow("rect-filtered",imrect); +// ); + //before going on, make sure the piramid is built + if (buildPyramidThread.joinable()) + buildPyramidThread.join(); + + + /////////////////////////////////////////////////////////////////////////// + /// CANDIDATE CLASSIFICATION: Decide which candidates are really markers + /////////////////////////////////////////////////////////////////////////// + + //Debug::setLevel(10); + auto markerWarpSize=getMarkerWarpSize(); + + detectedMarkers.clear(); + _candidates_wcontour.clear(); + for(auto &b:hist) b=0; + float desiredarea = std::pow(static_cast(markerWarpSize), 2.f); + for (size_t i = 0; i < MarkerCanditates.size(); i++) + { + // Find proyective homography + cv::Mat canonicalMarker,canonicalMarkerAux; + + cv::Mat inToWarp=imgToBeThresHolded; + MarkerCandidate points2d_pyr = MarkerCanditates[i]; + if (needPyramid){ + // warping is one of the most time consuming operations, especially when the region is large. + // To reduce computing time, let us find in the image pyramid, the best configuration to save time + // indicates how much bigger observation is wrt to desired patch + size_t imgPyrIdx = 0; + for (size_t p = 1; p < imagePyramid.size(); p++) + { + if (MarkerCanditates[i].getArea() / pow(4, p) >= desiredarea) imgPyrIdx = p; + else break; + } + inToWarp=imagePyramid[imgPyrIdx]; + //move points to the image level p + float ratio=float(inToWarp.cols)/float(imgToBeThresHolded.cols); + for (auto& p : points2d_pyr) p *= ratio;//1. / pow(2, imgPyrIdx); + + } + warp( inToWarp, canonicalMarker, Size(markerWarpSize,markerWarpSize),points2d_pyr); + int id, nRotations; + double min,Max; + cv::minMaxIdx(canonicalMarker,&min,&Max); + canonicalMarker.copyTo(canonicalMarkerAux); + string additionalInfo; +// _debug_exec(10,//only executes when compiled in DEBUG mode if debug level is at least 10 +// //show the thresholded images +// stringstream sstr;sstr<<"test-"<detect(canonicalMarkerAux, id, nRotations,additionalInfo)) + { + detectedMarkers.push_back(MarkerCanditates[i]); + detectedMarkers.back().id = id; + detectedMarkers.back().dict_info=additionalInfo; + detectedMarkers.back().contourPoints=MarkerCanditates[i].contourPoints; + // sort the points so that they are always in the same order no matter the camera orientation + std::rotate(detectedMarkers.back().begin(), + detectedMarkers.back().begin() + 4 - nRotations, + detectedMarkers.back().end()); +// _debug_exec(10,//only executes when compiled in DEBUG mode if debug level is at least 10 +// //show the thresholded images +// stringstream sstr;sstr<<"can-"<0) + _params.ThresHold= float(newThres) ; + } + +#ifdef debug_lines + cv::imshow("image-lines",image); + cv::waitKey(10); +#endif + //now, move the points to the original image (upsample corners) + if (input.cols!=imgToBeThresHolded.cols){ + cornerUpsample(detectedMarkers,imgToBeThresHolded.size()); + __ARUCO_TIMER_EVENT__("Corner Upsample"); + } + + + + ////////////////////////////////////////////////////////////////////// + /// MARKER Tracking + ////////////////////////////////////////////////////////////////////// + + + if( _params.trackingMinDetections>0 ){ + + //update the marker_ndetections + //decrement unseen ones + for(auto &md:marker_ndetections){ + if( std::find(detectedMarkers.begin(),detectedMarkers.end(),Marker(md.first))==detectedMarkers.end()) + md.second=std::max(md.second-1,0); + } + + // Identify the markers in the prev frame not yet tracked + std::vector needsTrack; + for(auto m:_prevMarkers){ + if( std::find(detectedMarkers.begin(),detectedMarkers.end(),Marker(m.id))==detectedMarkers.end()){ + if(marker_ndetections.count(m.id)!=0){ + if(marker_ndetections.at(m.id)>=_params.trackingMinDetections) + needsTrack.push_back(m); + } + } + } + + //look for them in the candidates + if(needsTrack.size()>0){ + struct trackInfo{ + trackInfo(const Marker &m){ + ma.setParams(m); + } + marker_analyzer ma; + int candidate=-1; + double dist=std::numeric_limits::max(); + }; + std::map marker_trackMatches; + for(auto &mnt:needsTrack) + marker_trackMatches.insert({mnt.id,trackInfo(mnt)}); + + for(size_t cand=0;cand<_candidates_wcontour.size();cand++){ + auto &mcnd=_candidates_wcontour[cand]; + marker_analyzer can_ma; + can_ma.setParams(mcnd); + for(auto &mtm:marker_trackMatches){ + if( mtm.second.ma.isInto( can_ma.getCenter())){ + auto dist= cv::norm( mtm.second.ma.getCenter() -can_ma.getCenter()); + auto sizedif= fabs( mtm.second.ma.getArea()-can_ma.getArea())/mtm.second.ma.getArea(); + if( sizedif<0.3f && dist(cand); + mtm.second.dist=dist; + } + } + } + } + + for(auto &mtm:marker_trackMatches){ + if( mtm.second.candidate==-1) continue; + auto it= find(_prevMarkers.begin(),_prevMarkers.end(),Marker(mtm.first)); + assert(it!=_prevMarkers.end()); + detectedMarkers.push_back(_candidates_wcontour[mtm.second.candidate]); + detectedMarkers.back().id = mtm.first; + detectedMarkers.back().dict_info= it->dict_info; + detectedMarkers.back().contourPoints=_candidates_wcontour[mtm.second.candidate].contourPoints; + //find best rotatation + + auto rotError=[](const vector &c1,const vector &c2){ + cv::Point2f direction1=c1[1]-c1[0]; + direction1*=1./cv::norm(direction1); + cv::Point2f direction2=c2[1]-c2[0]; + direction2*=1./cv::norm(direction2); + return direction1.dot(direction2); + }; + auto prev=find(_prevMarkers.begin(),_prevMarkers.end(),Marker(mtm.first)); + pair rot_error(-1,-1); + for(int r=0;r<4;r++){ + vector aux=detectedMarkers.back(); + std::rotate(aux .begin(), + aux .begin() + r, + aux .end()); + auto err=rotError(*prev,aux); + if( err> rot_error.second){ + rot_error={r,err}; + } + } + std::rotate(detectedMarkers.back() .begin(), + detectedMarkers.back().begin() + rot_error.first, + detectedMarkers.back().end()); + //mark for removal + _candidates_wcontour[mtm.second.candidate].clear(); + } + //remove the used candidates + _candidates_wcontour.erase(std::remove_if(_candidates_wcontour.begin(),_candidates_wcontour.end(), [](const vector &m){return m.size()==0;}), _candidates_wcontour.end()); + + } + //update for next + for(auto m:detectedMarkers){ + if( marker_ndetections.count(m.id)==0) + marker_ndetections[m.id]=1; + else marker_ndetections[m.id]++; + } + } + + + ////////////////////////////////////////////////////////////////////// + /// REMOVAL OF DUPLICATED + ////////////////////////////////////////////////////////////////////// + + if(1){ + // sort by id + std::sort(detectedMarkers.begin(), detectedMarkers.end()); + + // there might be still the case that a marker is detected twice because of the double border indicated earlier, + // detect and remove these cases + vector toRemove(detectedMarkers.size(), false); + + for (int i = 0; i < int(detectedMarkers.size()) - 1; i++) + { + for (int j = i + 1; j < int(detectedMarkers.size()) && !toRemove[i]; j++) + { + if (detectedMarkers[i].id == detectedMarkers[j].id && detectedMarkers[i].dict_info == detectedMarkers[j].dict_info) + { + // deletes the one with smaller perimeter + if (perimeter(detectedMarkers[i]) < perimeter(detectedMarkers[j])) + toRemove[i] = true; + else + toRemove[j] = true; + } + } + } + + removeElements(detectedMarkers,toRemove); + } + + + ////////////////////////////////////////////////////////////////////// + /// CORNER REFINEMENT IF REQUIRED + ////////////////////////////////////////////////////////////////////// + /// refine the corner location if enclosed markers and we did not do it via upsampling + if (detectedMarkers.size() > 0 && input.size()==imgToBeThresHolded.size() ){ + + if (_params.cornerRefinementM==CORNER_SUBPIX ){ + + int halfwsize= 4*float(input.cols)/float(imgToBeThresHolded.cols) +0.5 ; + + vector Corners; + for (unsigned int i = 0; i < detectedMarkers.size(); i++) + for (int c = 0; c < 4; c++) + Corners.push_back(detectedMarkers[i][c]); + cornerSubPix(grey, Corners, cv::Size(halfwsize,halfwsize), cv::Size(-1, -1),cv::TermCriteria( cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 12, 0.005)); + // copy back + for (unsigned int i = 0; i < detectedMarkers.size(); i++) + for (int c = 0; c < 4; c++) + detectedMarkers[i][c] = Corners[i * 4 + c]; + + } + else if ( _params.cornerRefinementM==CORNER_LINES){ + //use the lines method for estimation of the corners + for (unsigned int i = 0; i < detectedMarkers.size(); i++) + refineCornerWithContourLines(detectedMarkers[i]); + } + } +__ARUCO_TIMER_EVENT__("Corner Refinement"); +/// + + + + + + ////////////////////////////////////////////////////////////////////// + /// MARKER POSE ESTIMATION + ////////////////////////////////////////////////////////////////////// + + /// detect the position of detected markers if desired + if (camMatrix.rows != 0 && markerSizeMeters > 0) + { + for (unsigned int i = 0; i < detectedMarkers.size(); i++) + detectedMarkers[i].calculateExtrinsics(markerSizeMeters, camMatrix, distCoeff, setYPerpendicular); + __ARUCO_TIMER_EVENT__("Pose Estimation"); + } + + //compute _markerMinSize + float mlength=std::numeric_limits::max(); + for(const auto &marker:detectedMarkers){ + float l=0; + for(int c=0;c<4;c++) + l+=cv::norm(marker[c]-marker[(c+1)%4]); + if (mlength>l)mlength=l; + } + float markerMinSize; + if (mlength!=std::numeric_limits::max() ) + markerMinSize=mlength/(4*std::max(input.cols,input.rows)); + else markerMinSize=0; + if (_params.autoSize){ + _params.minSize= markerMinSize*(1-_params.ts); + } + + +///save for tracking + _prevMarkers=detectedMarkers; + + } +void MarkerDetector_Impl::refineCornerWithContourLines( aruco::Marker &marker,cv::Mat camMatrix,cv::Mat distCoeff ){ + // search corners on the contour vector + + std::vector &contour=marker.contourPoints; + vector< int > cornerIndex(4,-1); + vector dist(4,std::numeric_limits::max()); + for (unsigned int j = 0; j < contour.size(); j++) { + for (unsigned int k = 0; k < 4; k++) { + float d= (contour[j].x-marker[k].x)*(contour[j].x-marker[k].x) + + (contour[j].y-marker[k].y)*(contour[j].y-marker[k].y) ; + if (d cornerIndex[0]) && (cornerIndex[2] > cornerIndex[1] || cornerIndex[2] < cornerIndex[0])) + inverse = false; + else if (cornerIndex[2] > cornerIndex[1] && cornerIndex[2] < cornerIndex[0]) + inverse = false; + else + inverse = true; + + + // get pixel vector for each line of the marker + int inc = 1; + if (inverse) + inc = -1; + + // undistort contour + vector< Point2f > contour2f; + if(!camMatrix.empty() && !distCoeff.empty()){ + for (unsigned int i = 0; i < contour.size(); i++) + contour2f.push_back(cv::Point2f(contour[i].x, contour[i].y)); + if (!camMatrix.empty() && !distCoeff.empty()) + cv::undistortPoints(contour2f, contour2f, camMatrix, distCoeff, cv::Mat(), camMatrix); + + } + else { + contour2f.reserve(contour.size()); + for(auto p:contour) + contour2f.push_back(cv::Point2f(p.x,p.y)); + } + + vector< std::vector< cv::Point2f > > contourLines; + contourLines.resize(4); + for (unsigned int l = 0; l < 4; l++) { + for (int j = (int)cornerIndex[l]; j != (int)cornerIndex[(l + 1) % 4]; j += inc) { + if (j == (int)contour.size() && !inverse) + j = 0; + else if (j == 0 && inverse) + j = contour.size() - 1; + contourLines[l].push_back(contour2f[j]); + if (j == (int)cornerIndex[(l + 1) % 4]) + break; // this has to be added because of the previous ifs + } + } + + // interpolate marker lines + vector< Point3f > lines; + lines.resize(4); + for (unsigned int j = 0; j < lines.size(); j++) + interpolate2Dline(contourLines[j], lines[j]); + + // get cross points of lines + vector< Point2f > crossPoints; + crossPoints.resize(4); + for (unsigned int i = 0; i < 4; i++) + crossPoints[i] = getCrossPoint(lines[(i - 1) % 4], lines[i]); + + // distort corners again if undistortion was performed + if (!camMatrix.empty() && !distCoeff.empty()) + distortPoints(crossPoints, crossPoints, camMatrix, distCoeff); + + // reassing points + for (unsigned int j = 0; j < 4; j++){ + // cout<cand[endp].x){ + swap( startp,endp); + } + const float _180=3.14159f ; + + const float _22=3.14159/8.f; + const float _3_22=3.*3.14159f/8.f; + const float _5_22=5.f*3.14159f/8.f; + const float _7_22=7.f*3.14159f/8.f; + + int incx=0,incy=0; + //compute the angle + auto v1=cand[endp]-cand[startp]; + float angle=atan2( v1.y,v1.x); + if ( _22(_params.minSize)* static_cast(maxDim) ; + if (_params.minSize_pix!=-1) minSize=std::min(_params.minSize_pix, minSize ); + return minSize ; + +} +/************************************ + * + * + * + * + ************************************/ +bool MarkerDetector_Impl::warp(Mat& in, Mat& out, Size size, vector points) +{ + if (points.size() != 4) + throw cv::Exception(9001, "point.size()!=4", "MarkerDetector_Impl::warp", __FILE__, __LINE__); + // obtain the perspective transform + Point2f pointsRes[4], pointsIn[4]; + for (int i = 0; i < 4; i++) + pointsIn[i] = points[i]; + pointsRes[0] = (Point2f(0, 0)); + pointsRes[1] = Point2f(static_cast(size.width - 1), 0.f); + pointsRes[2] = Point2f(static_cast(size.width - 1), static_cast(size.height - 1)); + pointsRes[3] = Point2f(0.f, static_cast(size.height - 1)); + Mat M = getPerspectiveTransform(pointsIn, pointsRes); + cv::warpPerspective(in, out, M, size, cv::INTER_LINEAR); + // cv::warpPerspective(in, out, M, size, cv::INTER_NEAREST); + return true; +} + + +/************************************ + * + * + * + * + ************************************/ +int MarkerDetector_Impl::perimeter(const vector& a) +{ + int sum = 0; + for (unsigned int i = 0; i < a.size(); i++) + { + int i2 = (i + 1) % a.size(); + sum += static_cast( 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))); + } + return sum; +} + + +/** + */ +void MarkerDetector_Impl::interpolate2Dline(const std::vector& inPoints, cv::Point3f& outLine) +{ + float minX, maxX, minY, maxY; + minX = maxX = inPoints[0].x; + minY = maxY = inPoints[0].y; + for (unsigned int i = 1; i < inPoints.size(); i++) + { + if (inPoints[i].x < minX) + minX = inPoints[i].x; + if (inPoints[i].x > maxX) + maxX = inPoints[i].x; + if (inPoints[i].y < minY) + minY = inPoints[i].y; + if (inPoints[i].y > maxY) + maxY = inPoints[i].y; + } + + // create matrices of equation system + const int pointsCount = static_cast(inPoints.size()); + Mat A(pointsCount, 2, CV_32FC1, Scalar(0)); + Mat B(pointsCount, 1, CV_32FC1, Scalar(0)); + Mat X; + + if (maxX - minX > maxY - minY) + { + // Ax + C = y + for (int i = 0; i < pointsCount; i++) + { + A.at(i, 0) = inPoints[i].x; + A.at(i, 1) = 1.; + B.at(i, 0) = inPoints[i].y; + } + + // solve system + solve(A, B, X, DECOMP_SVD); + // return Ax + By + C + outLine = Point3f(X.at(0, 0), -1., X.at(1, 0)); + } + else + { + // By + C = x + for (int i = 0; i < pointsCount; i++) + { + A.at(i, 0) = inPoints[i].y; + A.at(i, 1) = 1.; + B.at(i, 0) = inPoints[i].x; + } + + // solve system + solve(A, B, X, DECOMP_SVD); + // return Ax + By + C + outLine = Point3f(-1., X.at(0, 0), X.at(1, 0)); + } +} + +/** + */ +Point2f MarkerDetector_Impl::getCrossPoint(const cv::Point3f& line1, const cv::Point3f& line2) +{ + // create matrices of equation system + Mat A(2, 2, CV_32FC1, Scalar(0)); + Mat B(2, 1, CV_32FC1, Scalar(0)); + Mat X; + + A.at(0, 0) = line1.x; + A.at(0, 1) = line1.y; + B.at(0, 0) = -line1.z; + + A.at(1, 0) = line2.x; + A.at(1, 1) = line2.y; + B.at(1, 0) = -line2.z; + + // solve system + solve(A, B, X, DECOMP_SVD); + return Point2f(X.at(0, 0), X.at(1, 0)); +} + +//template +//void MarkerDetector_Impl::cornerUpsample(vector& MarkerCanditates, cv::Size lowResImageSize ){ +// cornerUpsample_SUBP(MarkerCanditates,lowResImageSize); +//} + +//template +//void MarkerDetector_Impl::cornerUpsample_SUBP(vector& MarkerCanditates, cv::Size lowResImageSize ){ +// if (MarkerCanditates.size()==0)return; +// //first, determine the image in the pyramid nearest to this one +// int startPyrImg=0; + +// for(size_t i=0;i=0;curpyr--){ +// float factor= float(imagePyramid[curpyr].cols)/float(prevLowResSize.width) ; +// //upsample corner locations +// for(auto &m:MarkerCanditates) +// for(auto &point:m) {point*=factor;} +// int halfwsize= 0.5+2.5*factor; +// vector p2d;//p2d.reserve(MarkerCanditates.size()*4); +// for(auto &m:MarkerCanditates) +// for(auto &point:m) { p2d.push_back(point);} +// cv::cornerSubPix( imagePyramid[curpyr],p2d,cv::Size(halfwsize,halfwsize),cv::Size(-1,-1),cv::TermCriteria(cv::TermCriteria::MAX_ITER , 4,0.5)); +// int cidx=0; +// for(auto &m:MarkerCanditates) +// for(auto &point:m) {point =p2d[cidx++];} + +// prevLowResSize=imagePyramid[curpyr].size(); +// } +//} + + +/************************************ + * + * + * + * + ************************************/ +void MarkerDetector_Impl::drawAllContours(Mat input, std::vector>& contours) +{ + drawContours(input, contours, -1, Scalar(255, 0, 255)); +} + +/************************************ + * + * + * + * + ************************************/ +void MarkerDetector_Impl::drawContour(Mat& in, vector& contour, Scalar color) +{ + for (unsigned int i = 0; i < contour.size(); i++) + { + cv::rectangle(in, contour[i], contour[i], color); + } +} + +void MarkerDetector_Impl::drawApproxCurve(Mat& in, vector& contour, Scalar color,int thickness) +{ + for (unsigned int i = 0; i < contour.size(); i++) + { + cv::line(in, contour[i], contour[(i + 1) % contour.size()], color,thickness); + } +} +/************************************ + * + * + * + * + ************************************/ + +void MarkerDetector_Impl::draw(Mat out, const vector& markers) +{ + for (unsigned int i = 0; i < markers.size(); i++) + { + cv::line(out, markers[i][0], markers[i][1], cv::Scalar(255, 0, 0), 2); + cv::line(out, markers[i][1], markers[i][2], cv::Scalar(255, 0, 0), 2); + cv::line(out, markers[i][2], markers[i][3], cv::Scalar(255, 0, 0), 2); + cv::line(out, markers[i][3], markers[i][0], cv::Scalar(255, 0, 0), 2); + } +} + + +void MarkerDetector_Impl::setMarkerLabeler(cv::Ptr detector) +{ + markerIdDetector = detector; +} + +void MarkerDetector_Impl::setDictionary(int dict_type, + float error_correction_rate) +{ + markerIdDetector = MarkerLabeler::create((Dictionary::DICT_TYPES)dict_type, error_correction_rate); + _params.error_correction_rate=error_correction_rate; + _params.dictionary=aruco::Dictionary::getTypeString((Dictionary::DICT_TYPES)dict_type); +} + + + + +void MarkerDetector_Impl::setDictionary(string dict_type, float error_correction_rate) +{ + auto _to_string=[](float i){ + std::stringstream str;str<=_thres_Images.size()) idx=_thres_Images.size()-1;//last one is the original image + return _thres_Images[idx]; +} + + + +/** + */ +void MarkerDetector_Impl::distortPoints(vector< cv::Point2f > in, vector< cv::Point2f > &out, const Mat &camMatrix, const Mat &distCoeff) { + // trivial extrinsics + cv::Mat Rvec = cv::Mat(3, 1, CV_32FC1, cv::Scalar::all(0)); + cv::Mat Tvec = Rvec.clone(); + // calculate 3d points and then reproject, so opencv makes the distortion internally + vector< cv::Point3f > cornersPoints3d; + for (unsigned int i = 0; i < in.size(); i++) + cornersPoints3d.push_back(cv::Point3f((in[i].x - camMatrix.at< float >(0, 2)) / camMatrix.at< float >(0, 0), // x + (in[i].y - camMatrix.at< float >(1, 2)) / camMatrix.at< float >(1, 1), // y + 1)); // z + cv::projectPoints(cornersPoints3d, Rvec, Tvec, camMatrix, distCoeff, out); +} + + +/**Saves the configuration of the detector to a file + */ +void MarkerDetector_Impl::saveParamsToFile(const std::string &path) const{ + + cv::FileStorage fs(path, cv::FileStorage::WRITE); + if(!fs.isOpened())throw std::runtime_error("Could not open "+path); + _params.save(fs); +} + +/**Loads the configuration from a file + */ +void MarkerDetector_Impl::loadParamsFromFile(const std::string &path){ + cv::FileStorage fs(path, cv::FileStorage::READ); + if(!fs.isOpened())throw std::runtime_error("Could not open "+path); + _params.load(fs); + setDictionary(_params.dictionary,_params.error_correction_rate); +} + +void MarkerDetector_Impl::toStream(std::ostream &str)const +{ + uint64_t sig=13213; + str.write((char*)&sig,sizeof(sig)); + _params.toStream(str); +} + +void MarkerDetector_Impl::fromStream(std::istream &str){ + uint64_t sig=13213; + str.read((char*)&sig,sizeof(sig)); + if (sig!=13213) throw std::runtime_error("MarkerDetector_Impl::fromStream invalid signature"); + _params.fromStream(str); + setDictionary(_params.dictionary,_params.error_correction_rate); + +} + + + +void MarkerDetector_Impl::filter_ambiguous_query( std::vector &matches ){ + if (matches.size()==0)return; + //determine maximum values of queryIdx + int maxT=-1; + for(auto m:matches) maxT=std::max(maxT,m.queryIdx); + + //now, create the vector with the elements + vector used(maxT+1,-1); + vector best_matches(maxT); + int idx=0; + bool needRemove=false; + + for(auto &match:matches ){ + if (used[match.queryIdx]==-1){ + used[match.queryIdx]=idx; + } + else{ + if ( matches[ used[match.queryIdx] ].distance>match.distance){ + matches[ used[match.queryIdx] ].queryIdx=-1;//annulate the other match + used[match.queryIdx]=idx; + needRemove=true; + } + else{ + match.queryIdx=-1;//annulate this match + needRemove=true; + } + } + idx++; + } + + + if (needRemove) + matches.erase(std::remove_if(matches.begin(),matches.end(), [](const cv::DMatch &m){return m.trainIdx==-1 || m.queryIdx==-1;}), matches.end()); + + +} + +void MarkerDetector_Impl:: cornerUpsample(std::vector& MarkerCanditates, cv::Size lowResImageSize ){ + std::vector > corners; + for(const auto &m:MarkerCanditates)corners.push_back(m); + cornerUpsample(corners,lowResImageSize); + for(size_t i=0;i >& MarkerCanditates, cv::Size lowResImageSize ) + { + if (MarkerCanditates.size()==0)return; + //first, determine the image in the pyramid nearest to this one + int startPyrImg=0; + + for(size_t i=0;i=0;curpyr--){ + float factor= float(imagePyramid[curpyr].cols)/float(prevLowResSize.width) ; + //upsample corner locations + for(auto &m:MarkerCanditates) + for(auto &point:m) {point*=factor;} + int halfwsize= 0.5+2.5*factor; + std::vector p2d;//p2d.reserve(MarkerCanditates.size()*4); + for(auto &m:MarkerCanditates) + for(auto &point:m) { p2d.push_back(point);} + cv::cornerSubPix( imagePyramid[curpyr],p2d,cv::Size(halfwsize,halfwsize),cv::Size(-1,-1),cv::TermCriteria(cv::TermCriteria::MAX_ITER , 4,0.5)); + int cidx=0; + for(auto &m:MarkerCanditates) + for(auto &point:m) {point =p2d[cidx++];} + + prevLowResSize=imagePyramid[curpyr].size(); + } + } +}; diff --git a/thirdparty/aruco-3.1.12/src/markerdetector_impl.h b/thirdparty/aruco-3.1.12/src/markerdetector_impl.h new file mode 100644 index 0000000..1d46d48 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markerdetector_impl.h @@ -0,0 +1,465 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + + +#ifndef _ARUCO_MarkerDetector_Impl_H +#define _ARUCO_MarkerDetector_Impl_H + +#include "aruco_export.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "marker.h" +#include "markerdetector.h" +#include + +namespace aruco +{ + +class CameraParameters; +class MarkerLabeler; +/**\brief Main class for marker detection + * + */ +class MarkerDetector_Impl +{ + friend class MarkerDetector; +public: + + /** + * See + */ + MarkerDetector_Impl(); + /**Creates indicating the dictionary. See @see setDictionary for further details + * @param dict_type Dictionary employed. See @see setDictionary for further details + * @param error_correction_rate value indicating the correction error allowed. Is in range [0,1]. 0 means no + * correction at all. So + * an erroneous bit will result in discarding the marker. 1, mean full correction. The maximum number of bits + * that can be corrected depends on each ditionary. + * We recommend using values from 0 to 0.5. (in general, this will allow up to 3 bits or correction). */ + MarkerDetector_Impl(int dict_type, float error_correction_rate = 0); + MarkerDetector_Impl(std::string dict_type, float error_correction_rate = 0); + + /**Saves the configuration of the detector to a file. + */ + void saveParamsToFile(const std::string &path)const; + + /**Loads the configuration from a file. + */ + void loadParamsFromFile(const std::string &path); + + + /** + */ + ~MarkerDetector_Impl(); + /**Specifies the detection mode. We have preset three types of detection modes. These are + * ways to configure the internal parameters for the most typical situations. The modes are: + * - DM_NORMAL: In this mode, the full resolution image is employed for detection and slow threshold method. Use this method when + * you process individual images that are not part of a video sequence and you are not interested in speed. + * + * - DM_FAST: In this mode, there are two main improvements. First, image is threshold using a faster method using a global threshold. + * Also, the full resolution image is employed for detection, but, you could speed up detection even more by indicating a minimum size of the + * markers you will accept. This is set by the variable minMarkerSize which shoud be in range [0,1]. When it is 0, means that you do not set + * a limit in the size of the accepted markers. However, if you set 0.1, it means that markers smaller than 10% of the total image area, will not + * be detected. Then, the detection can be accelated up to orders of magnitude compared to the normal mode. + * + * - DM_VIDEO_FAST: This is similar to DM_FAST, but specially adapted to video processing. In that case, we assume that the observed markers + * when you call to detect() have a size similar to the ones observed in the previous frame. Then, the processing can be speeded up by employing smaller versions + * of the image automatically calculated. + * + */ + void setDetectionMode( DetectionMode dm,float minMarkerSize=0); + /**returns current detection mode + */ + DetectionMode getDetectionMode( ); + /**Detects the markers in the image passed + * + * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of + * the markers are detected + * + * @param input input color image + * @param camMatrix intrinsic camera information. + * @param distCoeff camera distorsion coefficient. If set Mat() if is assumed no camera distorion + * @param markerSizeMeters size of the marker sides expressed in meters. If not specified this value, the + * extrinsics of the markers are not detected. + * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z + * axis + * @return vector with the detected markers + */ + std::vector detect(const cv::Mat& input); + std::vector detect(const cv::Mat& input, const CameraParameters& camParams, + float markerSizeMeters, bool setYPerperdicular = false); + + /**Detects the markers in the image passed + * + * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of + * the markers are detected + * + * @param input input color image + * @param detectedMarkers output vector with the markers detected + * @param camParams Camera parameters + * @param markerSizeMeters size of the marker sides expressed in meters + * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the + * Z axis + */ + void detect(const cv::Mat& input, std::vector& detectedMarkers, CameraParameters camParams, + float markerSizeMeters = -1, bool setYPerperdicular = false); + + /**Detects the markers in the image passed + * + * If you provide information about the camera parameters and the size of the marker, then, the extrinsics of + * the markers are detected + * + * NOTE: be sure that the camera matrix is for this image size. If you do not know what I am talking about, use + * functions above and not this one + * @param input input color image + * @param detectedMarkers output vector with the markers detected + * @param camMatrix intrinsic camera information. + * @param distCoeff camera distorsion coefficient. If set Mat() if is assumed no camera distorion + * @param markerSizeMeters size of the marker sides expressed in meters + * @param setYPerperdicular If set the Y axis will be perpendicular to the surface. Otherwise, it will be the Z + * axis + */ + void detect(const cv::Mat& input, std::vector& detectedMarkers, cv::Mat camMatrix = cv::Mat(), + cv::Mat distCoeff = cv::Mat(), float markerSizeMeters = -1, + bool setYPerperdicular = false); + + + /**Returns operating params + */ + MarkerDetector::Params getParameters() const{return _params;} + /**Returns operating params + */ + MarkerDetector::Params & getParameters() {return _params;} + /** Sets the dictionary to be employed. + * You can choose:ARUCO,//original aruco dictionary. By default + ARUCO_MIP_25h7, + ARUCO_MIP_16h3, + ARUCO_MIP_36h12, **** recommended + ARTAG,// + ARTOOLKITPLUS, + ARTOOLKITPLUSBCH,// + TAG16h5,TAG25h7,TAG25h9,TAG36h11,TAG36h10//APRIL TAGS DICIONARIES + CHILITAGS,//chili tags dictionary . NOT RECOMMENDED. It has distance 0. Markers 806 and 682 + should not be used!!! + + If dict_type is none of the above ones, it is assumed you mean a CUSTOM dicionary saved in a file @see + Dictionary::loadFromFile + Then, it tries to open it + */ + void setDictionary(std::string dict_type, float error_correction_rate = 0); + + /** + * @brief setDictionary Specifies the dictionary you want to use for marker decoding + * @param dict_type dictionary employed for decoding markers @see Dictionary + * @param error_correction_rate value indicating the correction error allowed. Is in range [0,1]. 0 means no + * correction at all. So + * an erroneous bit will result in discarding the marker. 1, mean full correction. The maximum number of bits + * that can be corrected depends on each ditionary. + * We recommend using values from 0 to 0.5. (in general, this will allow up to 3 bits or correction). + */ + void setDictionary(int dict_type, float error_correction_rate = 0); + + /** + * Returns a reference to the internal image thresholded. Since there can be generated many of them, specify which + */ + cv::Mat getThresholdedImage(uint32_t idx=0); + /**returns the number of thresholed images available + */ + // size_t getNhresholdedImages()const{return _thres_Images.size();} + + + + ///------------------------------------------------- + /// Methods you may not need + /// Thesde methods do the hard work. They have been set public in case you want to do customizations + ///------------------------------------------------- + + /** + * @brief setMakerLabeler sets the labeler employed to analyze the squares and extract the inner binary code + * @param detector + */ + void setMarkerLabeler(cv::Ptr detector); + cv::Ptr getMarkerLabeler() + { + return markerIdDetector; + } + typedef Marker MarkerCandidate; + + + /**Returns a list candidates to be markers (rectangles), for which no valid id was found after calling + * detectRectangles + */ + std::vector getCandidates()const + { + return _candidates_wcontour; + } + + /** + * Given the iput image with markers, creates an output image with it in the canonical position + * @param in input image + * @param out image with the marker + * @param size of out + * @param points 4 corners of the marker in the image in + * @return true if the operation succeed + */ + bool warp(cv::Mat& in, cv::Mat& out, cv::Size size, std::vector points); + + + //serialization in binary mode + void toStream(std::ostream &str)const; + void fromStream(std::istream &str); + //configure the detector from a set of parameters + void setParameters(const MarkerDetector::Params ¶ms); + + std::vector getImagePyramid(){ + return imagePyramid; + } + + + + private: + //obfuscate start + + // operating params + MarkerDetector::Params _params; + + // Images + cv::Mat grey, thres; + // pointer to the function that analizes a rectangular region so as to detect its internal marker + cv::Ptr markerIdDetector; + /** + */ + int perimeter(const std::vector &a); + + // auxiliar functions to perform LINES refinement + void interpolate2Dline(const std::vector& inPoints, cv::Point3f& outLine); + cv::Point2f getCrossPoint(const cv::Point3f& line1, const cv::Point3f& line2); + void distortPoints(std::vector in, std::vector& out, const cv::Mat& camMatrix, + const cv::Mat& distCoeff); + + //returns the number of pixels that the smallest and largest allowed markers have + int getMinMarkerSizePix(cv::Size orginput_imageSize)const; + + //returns the markerWarpSize + int getMarkerWarpSize(); + /**Given a vector vinout with elements and a boolean vector indicating the lements from it to remove, + * this function remove the elements + * @param vinout + * @param toRemove + */ + template + void removeElements(std::vector& vinout, const std::vector& toRemove) + { + // remove the invalid ones by setting the valid in the positions left by the invalids + size_t indexValid = 0; + for (size_t i = 0; i < toRemove.size(); i++) + { + if (!toRemove[i]) + { + if (indexValid != i) + vinout[indexValid] = vinout[i]; + indexValid++; + } + } + vinout.resize(indexValid); + } + + + template + void joinVectors(std::vector>& vv, std::vector& v, bool clearv = false) + { + if (clearv) + v.clear(); + for (size_t i = 0; i < vv.size(); i++) + for (size_t j = 0; j < vv[i].size(); j++) + v.push_back(vv[i][j]); + } + + std::vector imagePyramid; + void enlargeMarkerCandidate(MarkerCandidate &cand, int fact=1); + + void cornerUpsample(std::vector >& MarkerCanditates, cv::Size lowResImageSize ); + void cornerUpsample(std::vector& MarkerCanditates, cv::Size lowResImageSize ); + + + + void buildPyramid(std::vector &imagePyramid,const cv::Mat &grey,int minSize); + + + + + std::vector thresholdAndDetectRectangles(const cv::Mat & input, int thres_param1, int thres_param2,bool erode,cv::Mat &auxThresImage); + std::vector thresholdAndDetectRectangles(const cv::Mat &image); + std::vector< aruco::MarkerCandidate> prefilterCandidates( std::vector< MarkerCandidate> &candidates,cv::Size orgImageSize); + + + std::vector _thres_Images; + std::vector > _vcandidates; + //std::vector > _candidates; + std::vector _candidates_wcontour; + std::vector _prevMarkers;//employed for tracking + std::map marker_ndetections;//used to keeps track only of markers with a minimum number of detections + + // graphical debug + void drawApproxCurve(cv::Mat& in, std::vector& approxCurve, cv::Scalar color, int thickness=1); + void drawContour(cv::Mat& in, std::vector& contour, cv::Scalar); + void drawAllContours(cv::Mat input, std::vector>& contours); + void draw(cv::Mat out, const std::vector& markers); + + + + enum ThreadTasks {THRESHOLD_TASK,ENCLOSE_TASK,EXIT_TASK}; + struct ThresAndDetectRectTASK{ + int inIdx,outIdx; + int param1,param2; + ThreadTasks task; + }; + void thresholdAndDetectRectangles_thread(); + + //thread safe queue to implement producer-consumer + template + class Queue + { + public: + + T pop() + { + std::unique_lock mlock(mutex_); + while (queue_.empty()) + { + cond_.wait(mlock); + } + auto item = queue_.front(); + queue_.pop(); + return item; + } + + void push(const T& item) + { + std::unique_lock mlock(mutex_); + queue_.push(item); + mlock.unlock(); + cond_.notify_one(); + } + + size_t size() + { + std::unique_lock mlock(mutex_); + size_t s=queue_.size(); + return s; + } + private: + std::queue queue_; + std::mutex mutex_; + std::condition_variable cond_; + }; + Queue _tasks; + void refineCornerWithContourLines( aruco::Marker &marker,cv::Mat cameraMatrix=cv::Mat(),cv::Mat distCoef=cv::Mat()); + + inline float pointSqdist(cv::Point &p,cv::Point2f&p2){ + float dx=p.x-p2.x; + float dy=p.y-p2.y; + return dx*dx+dy*dy; + } + + float _tooNearDistance=-1;//pixel distance between nearr rectangle. Computed automatically based on the params + + + + + + + + + + struct marker_analyzer{ + + void setParams( const std::vector &m){ + corners=m; + bax = m[1].x - m[0].x; + bay = m[1].y - m[0].y; + dax = m[2].x - m[0].x; + day = m[2].y - m[0].y; + a=m[0];b=m[1];d=m[2]; + area=_getArea(); + + center=cv::Point2f(0,0); + for(auto &p:corners) + center+=p; + center*=1./4.; + } + + bool isInto(const cv::Point2f &p)const{ + + + if( signD(corners[0],corners[1],p)<0)return false; + if( signD(corners[1],corners[2],p)<0)return false; + if( signD(corners[2],corners[3],p)<0)return false; + if( signD(corners[3],corners[0],p)<0)return false; + return true; + } + cv::Point2f getCenter()const{return center;} + float getArea()const{return area;} + float _getArea( ) const + { + // use the cross products + cv::Point2f v01 = corners[1] - corners[0]; + cv::Point2f v03 = corners[3] - corners[0]; + float area1 = fabs(v01.x * v03.y - v01.y * v03.x); + cv::Point2f v21 = corners[1] - corners[2]; + cv::Point2f v23 = corners[3] - corners[2]; + float area2 = fabs(v21.x * v23.y - v21.y * v23.x); + return (area2 + area1) / 2.f; + } + float bax, bay , dax ,day; + cv::Point2f a,b,d; + float area; + cv::Point2f center; + + std::vector corners; + + inline float signD(cv::Point2f p0,cv::Point2f p1,cv::Point2f p)const{ + return ((p0.y-p1.y)*p.x + (p1.x-p0.x)*p.y + (p0.x*p1.y-p1.x*p0.y)) / sqrt( (p1.x-p0.x)*(p1.x-p0.x)+(p1.y-p0.y)*(p1.y-p0.y)); + } + + }; + void addToImageHist(cv::Mat &im,std::vector&hist); + int Otsu(std::vector &hist); + + void filter_ambiguous_query( std::vector &matches ); + //obfuscate end + + }; +}; +#endif diff --git a/thirdparty/aruco-3.1.12/src/markerlabeler.cpp b/thirdparty/aruco-3.1.12/src/markerlabeler.cpp new file mode 100644 index 0000000..29fbe46 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markerlabeler.cpp @@ -0,0 +1,47 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + 2. 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. +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ +#include "markerlabeler.h" +#include "dictionary_based.h" + namespace aruco +{ + cv::Ptr MarkerLabeler::create(Dictionary::DICT_TYPES dict_type, + float error_correction_rate) + { + Dictionary dict = Dictionary::loadPredefined(dict_type); + DictionaryBased* db = new DictionaryBased(); + db->setParams(dict, error_correction_rate); + return db; + } + cv::Ptr MarkerLabeler::create(std::string detector, std::string params) + { + auto _stof=[](std::string str){ + float f;sscanf(str.c_str(),"%f",&f);return f; + }; + (void)params; + Dictionary dict = Dictionary::load(detector); + // try with one from file + DictionaryBased* db = new DictionaryBased(); + db->setParams(dict, _stof(params)); + return db; + } + } diff --git a/thirdparty/aruco-3.1.12/src/markerlabeler.h b/thirdparty/aruco-3.1.12/src/markerlabeler.h new file mode 100644 index 0000000..79c4224 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markerlabeler.h @@ -0,0 +1,107 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef _aruco_MarkerLabeler_ +#define _aruco_MarkerLabeler_ + +#include "aruco_export.h" +#include "dictionary.h" +#include + +namespace aruco +{ + /**\brief Base class of labelers. A labelers receive a square of the image and determines if it has a valid marker, + * its id and rotation + * Additionally, it implements the factory model + */ + + class Marker; + class ARUCO_EXPORT MarkerLabeler + { + public: + /** Factory function that returns a labeler for a given dictionary + * @param dict_type type of dictionary + * @param error_correction_rate some dictionaries are subsceptible of error correction. This params specify the + * correction rate. + * 0 means no correction at all. 1 means full correction (maximum correction bits = (tau-1) /2, tau= predefined + * mimum intermarker distance). + * + * If you want correction capabilities and not sure how much, use 0.5 in this parameter + */ + static cv::Ptr create(Dictionary::DICT_TYPES dict_type, + float error_correction_rate = 0); + + /** Factory function that returns the desired detector + + * + * @brief create Factory function that returns the desired detector + * @param detector + * * Possible names implemented are: + * ARUCO,CHILITAGS....: original aruco markers (0-1024) + http://www.sciencedirect.com/science/article/pii/S0031320314000235 + * SVM: + * @return + */ + static cv::Ptr create(std::string detector, std::string params = ""); + + /** function that identifies a marker. + * @param in input image to analyze + * @param marker_id id of the marker (if valid) + * @param nRotations : output parameter nRotations must indicate how many times the marker must be rotated + * clockwise 90 deg to be in its ideal position. (The way you would see it when you print it). This is employed + * to know + * always which is the corner that acts as reference system. + * @return true marker valid, false otherwise + */ + virtual bool detect(const cv::Mat& in, int& marker_id, int& nRotations,std::string &additionalInfo) = 0; + /** + * @brief getBestInputSize if desired, you can set the desired input size to the detect function + * @return -1 if detect accept any type of input, or a size otherwise + */ + virtual int getBestInputSize() + { + return -1; + } + + /** + * @brief getNSubdivisions returns the number of subdivisions in each axis that the iamge will be subject to. + * This is in dictionary based labelers, equals to the number of bits in each dimension plus the border bits. + * @return + */ + virtual int getNSubdivisions()const{ + return -1; + } + + // returns an string that describes the labeler and can be used to create it + virtual std::string getName() const = 0; + virtual ~MarkerLabeler() + { + } + }; +}; +#endif diff --git a/thirdparty/aruco-3.1.12/src/markermap.cpp b/thirdparty/aruco-3.1.12/src/markermap.cpp new file mode 100644 index 0000000..0935f8f --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markermap.cpp @@ -0,0 +1,468 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "markermap.h" +#include "dictionary.h" + +#include +#include + +#include + +using namespace std; +using namespace cv; + +namespace aruco +{ +Marker3DInfo::Marker3DInfo(){} +Marker3DInfo::Marker3DInfo(int _id):id(_id){} + /** + * + * + */ + MarkerMap::MarkerMap() + { + mInfoType = NONE; + } + /** + * + * + */ + MarkerMap::MarkerMap(string filePath) + { + mInfoType = NONE; + readFromFile(filePath); + } + + /** + * + * + */ + void MarkerMap::saveToFile(string sfile) + { + cv::FileStorage fs(sfile, cv::FileStorage::WRITE); + saveToFile(fs); + } + /**Saves the board info to a file + */ + void MarkerMap::saveToFile(cv::FileStorage& fs) + { + fs << "aruco_bc_dict" << dictionary; + fs << "aruco_bc_nmarkers" << (int)size(); + fs << "aruco_bc_mInfoType" << (int)mInfoType; + fs << "aruco_bc_markers" + << "["; + for (size_t i = 0; i < size(); i++) + { + fs << "{:" + << "id" << at(i).id; + + fs << "corners" + << "[:"; + for (size_t c = 0; c < at(i).size(); c++) + fs << at(i)[c]; + fs << "]"; + fs << "}"; + } + fs << "]"; + } + + /** + * + * + */ + void MarkerMap::readFromFile(string sfile) + { + try + { + cv::FileStorage fs(sfile, cv::FileStorage::READ); + if (!fs.isOpened()) + throw cv::Exception(81818, "MarkerMap::readFromFile", string(" file not opened ") + sfile, __FILE__,__LINE__); + readFromFile(fs); + } + catch (std::exception& ex) + { + throw cv::Exception(81818, "MarkerMap::readFromFile", ex.what() + string(" file=)") + sfile, __FILE__,__LINE__); + } + } + + /**Reads board info from a file + */ + void MarkerMap::readFromFile(cv::FileStorage& fs) + { + int aux = 0; + // look for the nmarkers + if (fs["aruco_bc_nmarkers"].name() != "aruco_bc_nmarkers") + throw cv::Exception(81818, "MarkerMap::readFromFile", "invalid file type", __FILE__, __LINE__); + fs["aruco_bc_nmarkers"] >> aux; + resize(aux); + fs["aruco_bc_mInfoType"] >> mInfoType; + cv::FileNode markers = fs["aruco_bc_markers"]; + int i = 0; + for (FileNodeIterator it = markers.begin(); it != markers.end(); ++it, i++) + { + at(i).id = (*it)["id"]; + FileNode FnCorners = (*it)["corners"]; + for (FileNodeIterator itc = FnCorners.begin(); itc != FnCorners.end(); ++itc) + { + vector coordinates3d; + (*itc) >> coordinates3d; + if (coordinates3d.size() != 3) + throw cv::Exception(81818, "MarkerMap::readFromFile", "invalid file type 3", __FILE__, __LINE__); + cv::Point3f point(coordinates3d[0], coordinates3d[1], coordinates3d[2]); + at(i).push_back(point); + } + } + + if (fs["aruco_bc_dict"].name() == "aruco_bc_dict") + fs["aruco_bc_dict"] >> dictionary; + } + + /** + */ + int MarkerMap::getIndexOfMarkerId(int id) const + { + for (size_t i = 0; i < size(); i++) + if (at(i).id == id) + return static_cast(i); + return -1; + } + + /** + */ + const Marker3DInfo& MarkerMap::getMarker3DInfo(int id) const + { + for (size_t i = 0; i < size(); i++) + if (at(i).id == id) + return at(i); + throw cv::Exception(111, "MarkerMap::getMarker3DInfo", "Marker with the id given is not found", __FILE__, + __LINE__); + } + + void __glGetModelViewMatrix(double modelview_matrix[16], const cv::Mat& Rvec, + const cv::Mat& Tvec) + { + // check if paremeters are valid + bool invalid = false; + for (int i = 0; i < 3 && !invalid; i++) + { + if (Tvec.at(i, 0) != -999999) + invalid |= false; + if (Rvec.at(i, 0) != -999999) + invalid |= false; + } + if (invalid) + throw cv::Exception(9002, "extrinsic parameters are not set", "Marker::getModelViewMatrix", __FILE__, + __LINE__); + Mat Rot(3, 3, CV_32FC1), Jacob; + Rodrigues(Rvec, Rot, Jacob); + + double para[3][4]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + para[i][j] = Rot.at(i, j); + // now, add the translation + para[0][3] = Tvec.at(0, 0); + para[1][3] = Tvec.at(1, 0); + para[2][3] = Tvec.at(2, 0); + double scale = 1; + + modelview_matrix[0 + 0 * 4] = para[0][0]; + // R1C2 + modelview_matrix[0 + 1 * 4] = para[0][1]; + modelview_matrix[0 + 2 * 4] = para[0][2]; + modelview_matrix[0 + 3 * 4] = para[0][3]; + // R2 + modelview_matrix[1 + 0 * 4] = para[1][0]; + modelview_matrix[1 + 1 * 4] = para[1][1]; + modelview_matrix[1 + 2 * 4] = para[1][2]; + modelview_matrix[1 + 3 * 4] = para[1][3]; + // R3 + modelview_matrix[2 + 0 * 4] = -para[2][0]; + modelview_matrix[2 + 1 * 4] = -para[2][1]; + modelview_matrix[2 + 2 * 4] = -para[2][2]; + modelview_matrix[2 + 3 * 4] = -para[2][3]; + modelview_matrix[3 + 0 * 4] = 0.0; + modelview_matrix[3 + 1 * 4] = 0.0; + modelview_matrix[3 + 2 * 4] = 0.0; + modelview_matrix[3 + 3 * 4] = 1.0; + if (scale != 0.0) + { + modelview_matrix[12] *= scale; + modelview_matrix[13] *= scale; + modelview_matrix[14] *= scale; + } + } + + /**** + * + */ + void __OgreGetPoseParameters(double position[3], double orientation[4], const cv::Mat& Rvec, + const cv::Mat& Tvec) + { + // check if paremeters are valid + bool invalid = false; + for (int i = 0; i < 3 && !invalid; i++) + { + if (Tvec.at(i, 0) != -999999) + invalid |= false; + if (Rvec.at(i, 0) != -999999) + invalid |= false; + } + if (invalid) + throw cv::Exception(9003, "extrinsic parameters are not set", "Marker::getModelViewMatrix", __FILE__, + __LINE__); + + // calculate position vector + position[0] = -Tvec.ptr(0)[0]; + position[1] = -Tvec.ptr(0)[1]; + position[2] = +Tvec.ptr(0)[2]; + + // now calculare orientation quaternion + cv::Mat Rot(3, 3, CV_32FC1); + cv::Rodrigues(Rvec, Rot); + + // calculate axes for quaternion + double stAxes[3][3]; + // x axis + stAxes[0][0] = -Rot.at(0, 0); + stAxes[0][1] = -Rot.at(1, 0); + stAxes[0][2] = +Rot.at(2, 0); + // y axis + stAxes[1][0] = -Rot.at(0, 1); + stAxes[1][1] = -Rot.at(1, 1); + stAxes[1][2] = +Rot.at(2, 1); + // for z axis, we use cross product + stAxes[2][0] = stAxes[0][1] * stAxes[1][2] - stAxes[0][2] * stAxes[1][1]; + stAxes[2][1] = -stAxes[0][0] * stAxes[1][2] + stAxes[0][2] * stAxes[1][0]; + stAxes[2][2] = stAxes[0][0] * stAxes[1][1] - stAxes[0][1] * stAxes[1][0]; + + // transposed matrix + double axes[3][3]; + axes[0][0] = stAxes[0][0]; + axes[1][0] = stAxes[0][1]; + axes[2][0] = stAxes[0][2]; + + axes[0][1] = stAxes[1][0]; + axes[1][1] = stAxes[1][1]; + axes[2][1] = stAxes[1][2]; + + axes[0][2] = stAxes[2][0]; + axes[1][2] = stAxes[2][1]; + axes[2][2] = stAxes[2][2]; + + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + double fTrace = axes[0][0] + axes[1][1] + axes[2][2]; + double fRoot; + + if (fTrace > 0.0) + { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = sqrt(fTrace + 1.0); // 2w + orientation[0] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; // 1/(4w) + orientation[1] = (axes[2][1] - axes[1][2]) * fRoot; + orientation[2] = (axes[0][2] - axes[2][0]) * fRoot; + orientation[3] = (axes[1][0] - axes[0][1]) * fRoot; + } + else + { + // |w| <= 1/2 + static unsigned int s_iNext[3] = {1, 2, 0}; + unsigned int i = 0; + if (axes[1][1] > axes[0][0]) + i = 1; + if (axes[2][2] > axes[i][i]) + i = 2; + unsigned int j = s_iNext[i]; + unsigned int k = s_iNext[j]; + + fRoot = sqrt(axes[i][i] - axes[j][j] - axes[k][k] + 1.0); + double* apkQuat[3] = {&orientation[1], &orientation[2], &orientation[3]}; + *apkQuat[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + orientation[0] = (axes[k][j] - axes[j][k]) * fRoot; + *apkQuat[j] = (axes[j][i] + axes[i][j]) * fRoot; + *apkQuat[k] = (axes[k][i] + axes[i][k]) * fRoot; + } + } + + /** + */ + void MarkerMap::getIdList(std::vector& ids, bool append) const + { + if (!append) + ids.clear(); + for (size_t i = 0; i < size(); i++) + ids.push_back(at(i).id); + } + + MarkerMap MarkerMap::convertToMeters(float markerSize_meters)const + { + if (!isExpressedInPixels()) + throw cv::Exception(-1, "The board is not expressed in pixels", "MarkerMap::convertToMeters", __FILE__, + __LINE__); + // first, we are assuming all markers are equally sized. So, lets get the size in pixels + int markerSizePix = static_cast(cv::norm(at(0)[0] - at(0)[1])); + MarkerMap BInfo(*this); + BInfo.mInfoType = MarkerMap::METERS; + // now, get the size of a pixel, and change scale + float pixSize = markerSize_meters / float(markerSizePix); + cout << markerSize_meters << " " << float(markerSizePix) << " " << pixSize << endl; + for (size_t i = 0; i < BInfo.size(); i++) + for (int c = 0; c < 4; c++) + { + BInfo[i][c] *= pixSize; + } + return BInfo; + } + cv::Mat MarkerMap::getImage(float METER2PIX) const + { + if (mInfoType == NONE) + throw cv::Exception(-1, "The board is not valid mInfoType==NONE ", "MarkerMap::getImage", __FILE__, + __LINE__); + if (METER2PIX <= 0 && mInfoType != PIX) + throw cv::Exception(-1, "The board is not expressed in pixels and not METER2PIX indicated", + "MarkerMap::getImage", __FILE__, __LINE__); + + auto Dict = Dictionary::loadPredefined(dictionary); + + // get image limits + cv::Point pmin(std::numeric_limits::max(), std::numeric_limits::max()), + pmax(std::numeric_limits::lowest(), std::numeric_limits::lowest()); + for (auto b : *this) + { + for (auto p : b.points) + { + pmin.x = min(int(p.x), pmin.x); + pmin.y = min(int(p.y), pmin.y); + pmax.x = max(int(p.x + 0.5), pmax.x); + pmax.y = max(int(p.y + 0.5), pmax.y); + assert(p.z == 0); + } + } + + cv::Point psize = pmax - pmin; + cv::Mat image(cv::Size(psize.x, psize.y), CV_8UC1); + image.setTo(cv::Scalar::all(255)); + + vector p3d = *this; + // the points must be moved from a real reference system to image reference sysmte (y positive is inverse) + for (auto& m : p3d) + for (auto& p : m.points) + { + p -= cv::Point3f(static_cast(pmin.x), static_cast(pmax.y), 0.f); + // now, use inverse y + p.y = -p.y; + } + for (auto m : p3d) + { + // get size and find size of this + const float size = static_cast(cv::norm(m[0] - m[1])); + auto im1 = Dict.getMarkerImage_id(m.id, int(size / 8)); + cv::Mat im2; + // now resize to fit + cv::resize(im1, im2, cv::Size(static_cast(size), static_cast(size))); + // copy in correct position + auto ry = cv::Range(int(m[0].y), int(m[2].y)); + auto rx = cv::Range(int(m[0].x), int(m[2].x)); + cv::Mat sub = image(ry, rx); + im2.copyTo(sub); + } + return image; + } + + std::vector MarkerMap::getIndices(const vector& markers)const + { + std::vector indices; + for (size_t i = 0; i < markers.size(); i++) + { + bool found = false; + for (size_t j = 0; j < size() && !found; j++) + { + if (markers[i].id == at(j).id) + { + found = true; + indices.push_back(static_cast(i)); + } + } + } + return indices; + } + void MarkerMap::toStream(std::ostream& str) + { + str << mInfoType << " " << size() << " "; + for (size_t i = 0; i < size(); i++) + { + at(i).toStream(str); + } + // write dic string info + str << dictionary; + } + void MarkerMap::fromStream(std::istream& str) + { + int s; + str >> mInfoType >> s; + resize(s); + for (size_t i = 0; i < size(); i++) + at(i).fromStream(str); + str >> dictionary; + } + pair MarkerMap::calculateExtrinsics(const std::vector& markers, float markerSize, + cv::Mat CameraMatrix, cv::Mat Distorsion) + { + vector p2d; + MarkerMap m_meters; + if (isExpressedInPixels()) + m_meters = convertToMeters(markerSize); + else + m_meters = *this; + vector p3d; + for (auto marker : markers) + { + auto it = find(m_meters.begin(), m_meters.end(), marker.id); + if (it != m_meters.end()) + { // is the marker part of the map? + for (auto p : marker) + p2d.push_back(p); + for (auto p : it->points) + p3d.push_back(p); + } + } + + cv::Mat rvec, tvec; + if (p2d.size() != 0) + { // no points in the vector + cv::solvePnPRansac(p3d, p2d, CameraMatrix, Distorsion, rvec, tvec); + } + if(rvec.type()==CV_64F) rvec.convertTo(rvec,CV_64F); + if(tvec.type()==CV_64F) tvec.convertTo(tvec,CV_64F); + return make_pair(rvec, tvec); + } +}; diff --git a/thirdparty/aruco-3.1.12/src/markermap.h b/thirdparty/aruco-3.1.12/src/markermap.h new file mode 100644 index 0000000..81ce808 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/markermap.h @@ -0,0 +1,211 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef _Aruco_MarkerMap_h +#define _Aruco_MarkerMap_h + +#include "aruco_export.h" +#include "marker.h" + +#include + +#include +#include + + namespace aruco +{ + /** + * 3d representation of a marker + */ + class ARUCO_EXPORT Marker3DInfo + { + public: + std::vector points; + int id; // maker id + + Marker3DInfo(); + Marker3DInfo(int _id); + inline bool operator==(const Marker3DInfo& MI) + { + return id == MI.id; + } + + // returns the distance of the marker side + inline float getMarkerSize() const + { + return static_cast(cv::norm(points[0] - points[1])); + } + inline cv::Point3f at(size_t idx)const{return points.at(idx);} + inline cv::Point3f & operator[](size_t idx) {return points[idx];} + inline const cv::Point3f & operator[](size_t idx)const {return points[idx];} + inline void push_back(const cv::Point3f &p){ points.push_back(p);} + inline size_t size()const{return points.size();} + + public: + + inline void toStream(std::ostream& str) + { + str << id << " " << size() << " "; + for (size_t i = 0; i < size(); i++) + str << at(i).x << " " << at(i).y << " " << at(i).z << " "; + } + inline void fromStream(std::istream& str) + { + int s; + str >> id >> s; + points.resize(s); + for (size_t i = 0; i < size(); i++) + str >> points[i].x >> points[i].y >> points[i].z; + } + + }; + + /**\brief This class defines a set of markers whose locations are attached to a common reference system, i.e., they + * do not move wrt each other. + * A MarkerMap contains several markers so that they are more robustly detected. + * + * A MarkerMap is only a list of the id of the markers along with the position of their corners. + * A MarkerMap may have information about the dictionary the markers belongs to @see getDictionary() + * + * The position of the corners can be specified either in pixels (in a non-specific size) or in meters. + * The first is the typical case in which you generate the image of board and the print it. Since you do not know + * in advance the real + * size of the markers, their corners are specified in pixels, and then, the translation to meters can be made once + * you know the real size. + * + * On the other hand, you may want to have the information of your boards in meters. The MarkerMap allows you to do + * so. + * + * The point is in the mInfoType variable. It can be either PIX or METERS according to your needs. + * + */ + + class ARUCO_EXPORT MarkerMap : public std::vector + { + public: + /** + */ + MarkerMap(); + + /**Loads from file + * @param filePath to the config file + */ + MarkerMap(std::string filePath); + + /**Indicates if the corners are expressed in meters + */ + bool isExpressedInMeters() const + { + return mInfoType == METERS; + } + /**Indicates if the corners are expressed in meters + */ + bool isExpressedInPixels() const + { + return mInfoType == PIX; + } + /**converts the passed board into meters + */ + MarkerMap convertToMeters(float markerSize) const; + // simple way of knowing which elements detected in an image are from this markermap + // returns the indices of the elements in the vector 'markers' that belong to this set + // Example: The set has the elements with ids 10,21,31,41,92 + // The input vector has the markers with ids 10,88,9,12,41 + // function returns {0,4}, because element 0 (10) of the vector belongs to the set, and also element 4 (41) + // belongs to the set + std::vector getIndices(const vector &markers) const; + + /**Returns the Info of the marker with id specified. If not in the set, throws exception + */ + const Marker3DInfo& getMarker3DInfo(int id) const; + + /**Returns the index of the marker (in this object) with id indicated, if is in the vector + */ + int getIndexOfMarkerId(int id) const; + /**Set in the list passed the set of the ids + */ + void getIdList(vector& ids, bool append = true) const; + + /**Returns an image of this to be printed. This object must be in pixels @see isExpressedInPixels(). If + * not,please provide the METER2PIX conversion parameter + */ + cv::Mat getImage(float METER2PIX = 0) const; + + /**Saves the board info to a file + */ + void saveToFile(std::string sfile); + /**Reads board info from a file + */ + void readFromFile(std::string sfile); + + // calculates the camera location w.r.t. the map using the information provided + // returns the + std::pair calculateExtrinsics(const std::vector& markers, float markerSize, + cv::Mat CameraMatrix, cv::Mat Distorsion); + + // returns string indicating the dictionary + inline std::string getDictionary() const + { + return dictionary; + } + + enum Marker3DInfoType + { + NONE = -1, + PIX = 0, + METERS = 1 + }; // indicates if the data in MakersInfo is expressed in meters or in pixels so as to do conversion internally + // returns string indicating the dictionary + void setDictionary(std::string d) + { + dictionary = d; + } + + // variable indicates if the data in MakersInfo is expressed in meters or in pixels so as to do conversion + // internally + int mInfoType; + + private: + // dictionary it belongs to (if any) + std::string dictionary; + + private: + /**Saves the board info to a file + */ + void saveToFile(cv::FileStorage& fs); + /**Reads board info from a file + */ + void readFromFile(cv::FileStorage& fs); + + public: + void toStream(std::ostream& str); + void fromStream(std::istream& str); + }; +} + +#endif diff --git a/thirdparty/aruco-3.1.12/src/picoflann.h b/thirdparty/aruco-3.1.12/src/picoflann.h new file mode 100644 index 0000000..59e9e2a --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/picoflann.h @@ -0,0 +1,647 @@ +#ifndef PicoFlann_H +#define PicoFlann_H +#include +#include +#include +#include +#include +#include +#include +namespace picoflann { + + +/** + * @brief The KdTreeIndex class is the simplest an minimal method to use kdtrees. + * You only must define an adapter, that tells the class how to access the i-th dimension of your element. Here are two examples showing how easy it is: + * + * You can use any container providing the methods size() ant at(). + * You must create the adapter that must implement the method : T operator( )(const E &elem, int dim)const; T=float or double, E is your element type + * + * You can easily save/read the index to/from an stream using toStream() and fromStream(). Please notice that the data of the container is not copied in the KdTreeIndex so you + * must be sure it is available when doing the searchs + * + * See examples below + + * +#include +#include +#include +#include "picoflann.h" +void example1(){ + //Data type + struct Point2f{ + Point2f(float X,float Y) { x=X;y=Y; } + float x,y; + }; + + // Adapter. + // Given an Point2f element, it returns the element of the dimension specified such that dim=0 is x and dim=1 is y + struct PicoFlann_Point2fAdapter{ + inline float operator( )(const Point2f &elem, int dim)const { return dim==0?elem.x:elem.y; } + }; + + //create the points randomly + std::default_random_engine generator; + std::uniform_real_distribution distribution(-1000.0,1000.0); + std::vector data; + for(size_t i=0;i<1000;i++) + data.push_back( Point2f ( distribution(generator),distribution(generator))); + ///------------------------------------------------------------ + /// Create the kdtree + picoflann::KdTreeIndex<2,PicoFlann_Point2fAdapter> kdtree;//2 is the number of dimensions + kdtree.build(data); + //search 10 nearest neibors to point (0,0) + std::vector > res=kdtree.searchKnn(data,Point2f(0,0),10); + + //radius search in a radius of 30 (the resulting distances are squared) + res=kdtree.radiusSearch(data,Point2f(0,0),30); + //another version + kdtree.radiusSearch(res,data,Point2f(0,0),30); + + //you can save to a file + std::ofstream file_out("out.bin",std::ios::binary); + kdtree.toStream(file_out); + + //recover from the file + picoflann::KdTreeIndex<2,PicoFlann_Point2fAdapter> kdtree2; + std::ifstream file_in("out.bin",std::ios::binary); + kdtree2.fromStream(file_in); + res=kdtree2.radiusSearch(data,Point2f(0,0),30); + +} + + +//Using an array of 3d points +void example2(){ + + struct Point3f{ + Point3f(float X,float Y,float Z) { data[0]=X;data[1]=Y;data[2]=Z; } + float data[3]; + }; + struct PicoFlann_Array3f_Adapter{ + inline float operator( )(const Point3f &elem, int dim)const{ return elem.data[dim]; } + }; + struct PicoFlann_Array3f_Container{ + const Point3f *_array; + size_t _size; + PicoFlann_Array3f_Container(float *array,size_t Size):_array((Point3f*)array),_size(Size){} + inline size_t size()const{return _size;} + inline const Point3f &at(int idx)const{ return _array [idx];} + }; + std::default_random_engine generator; + std::uniform_real_distribution distribution(-1000.0,1000.0); + + int nPoints=1000; + float *array=new float[nPoints*3]; + for(size_t i=0;i<1000*3;i++) + array[i]= distribution(generator); + + ///------------------------------------------------------------ + picoflann::KdTreeIndex<3,PicoFlann_Array3f_Adapter> kdtree;// 3 is the number of dimensions, L2 is the type of distance + kdtree.build( PicoFlann_Array3f_Container(array,nPoints)); + PicoFlann_Array3f_Container p3container(array,nPoints); + std::vector > res=kdtree.searchKnn(p3container,Point3f(0,0,0),10); + res=kdtree.radiusSearch(p3container,Point3f(0,0,0),30); +} + * + */ +struct L2{ + + template + double compute_distance( const ElementType &elema,const ElementType2 &elemb,const Adapter & adapter,int ndims ,double worstDist)const + { + //compute dist + double sqd=0; + for(int i=0;iworstDist) return sqd; + } + return sqd; + } +}; + +template +class KdTreeIndex{ + +public: + /** + *Builds the index using the data passes in your container and the adapter + */ + template + inline void build(const Container &container ){ + _index.clear(); + _index.reserve(container.size()*2); + _index.dims=DIMS; + _index.nValues=container.size(); + //Create root and assign all items + all_indices.resize(container.size()); + for(size_t i=0;i(_index.rootBBox,0,all_indices.size(),container); + _index.push_back(Node()); + divideTree(_index,0,0,all_indices.size(),_index.rootBBox ,container); + } + + + inline void clear(){ + _index.clear(); + all_indices.clear(); + } + + //saves to a stream. Note that the container is not saved! + inline void toStream (std::ostream &str)const; + //reads from an stream. Note that the container is not readed! + inline void fromStream (std::istream &str); + + template< typename Type,typename Container > + inline std::vector > searchKnn(const Container &container,const Type &val, int nn,bool sorted=true){ + std::vector > res; + generalSearch(res,container,val,-1,sorted,nn); + return res; + } + + + template< typename Type,typename Container > + inline std::vector > radiusSearch(const Container &container,const Type &val, double dist,bool sorted=true, int maxNN=-1)const{ + std::vector > res; + generalSearch< Type,Container>(res,container,val,dist,sorted,maxNN); + return res; + } + + + template< typename Type,typename Container > + inline void radiusSearch(std::vector > &res,const Container &container,const Type &val, double dist,bool sorted=true, int maxNN=-1){ + generalSearch(res,container,val,dist,sorted,maxNN); + } + + + +private: + + struct Node{ + inline bool isLeaf()const{return _ileft==-1 && _iright==-1;} + inline void setNodesInfo(uint32_t l,uint32_t r){_ileft=l; _iright=r;} + double div_val; + uint16_t col_index;//column index of the feature vector + std::vector idx; + float divhigh,divlow; + int64_t _ileft=-1,_iright=-1;//children + void toStream(std::ostream &str) const; + void fromStream(std::istream &str); + }; + + + typedef std::vector > BoundingBox; + + struct Index:public std::vector{ + BoundingBox rootBBox; + int dims=0; + int nValues=0;//number of elements of the set when call to build + inline void toStream(std::ostream &str)const; + inline void fromStream(std::istream &str); + }; + Index _index; + DistanceType _distance; + Adapter adapter; + //next are only used during build + std::vector all_indices; + int _maxLeafSize=10 ; + + + + //temporal used during creation of the tree + template< typename Container > + void divideTree(Index &index,uint64_t nodeIdx,int startIndex,int endIndex ,BoundingBox &bbox,const Container&container){ + // std::cout<<"CREATE="<(bbox,startIndex,endIndex,container); + // std::cout<(_bbox,startIndex,endIndex,container); + // //get the dimension with highest distnaces + double max_spread=-1; + currNode.col_index=0; + for(int i=0;imax_spread){ + max_spread=spread; + currNode.col_index=i; + } + } + //select the split val + double split_val= (bbox[currNode.col_index].first + bbox[currNode.col_index].second) / 2; + if (split_val < _bbox[currNode.col_index].first) currNode.div_val = _bbox[currNode.col_index].first; + else if (split_val > _bbox[currNode.col_index].second ) currNode.div_val = _bbox[currNode.col_index].second ; + else currNode.div_val = split_val; + } + else{ + ///SELECT THE COL (DIMENSION) ON WHICH PARTITION IS MADE + double var[DIMS],mean[DIMS]; + //compute the variance of the features to select the highest one + mean_var_calculate(startIndex,endIndex, var, mean,container); + currNode.col_index=0; + //select element with highest variance + for(int i=1;ivar[currNode.col_index]) currNode.col_index=i; + + //now sort all indices according to the selected value + + currNode.div_val=mean[currNode.col_index]; + } + + + + + + //compute the variance of the features to select the highest one + //now sort all indices according to the selected value + + //std::cout<<" CUT FEAT="< +void KdTreeIndex::toStream (std::ostream &str)const{ +_index.toStream(str); +} + +template +void KdTreeIndex::fromStream(std::istream &str){ +_index.fromStream(str); +} +} + +#endif + diff --git a/thirdparty/aruco-3.1.12/src/posetracker.cpp b/thirdparty/aruco-3.1.12/src/posetracker.cpp new file mode 100644 index 0000000..61bffe8 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/posetracker.cpp @@ -0,0 +1,553 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "posetracker.h" +#include "ippe.h" +#include +#include "levmarq.h" //solve pnp of opencv is not trustworthy. Create our own +#include +namespace aruco +{ + +namespace aruco_private { + +cv::Mat impl__aruco_getRTMatrix(const cv::Mat& _rvec, const cv::Mat& _tvec) +{ + assert(_rvec.type()==CV_32F && _rvec.total()==3); + assert(_tvec.type()==CV_32F && _tvec.total()==3); + + cv::Mat Matrix(4,4,CV_32F); + float *rt_44=Matrix.ptr(0); + //makes a fast conversion to the 4x4 array passed + float rx=_rvec.ptr(0)[0]; + float ry=_rvec.ptr(0)[1]; + float rz=_rvec.ptr(0)[2]; + float tx=_tvec.ptr(0)[0]; + float ty=_tvec.ptr(0)[1]; + float tz=_tvec.ptr(0)[2]; + float nsqa=rx*rx + ry*ry + rz*rz; + float a=std::sqrt(nsqa); + float i_a=a?1./a:0; + float rnx=rx*i_a; + float rny=ry*i_a; + float rnz=rz*i_a; + float cos_a=cos(a); + float sin_a=sin(a); + float _1_cos_a=1.-cos_a; + rt_44[0] =cos_a+rnx*rnx*_1_cos_a; + rt_44[1]=rnx*rny*_1_cos_a- rnz*sin_a; + rt_44[2]=rny*sin_a + rnx*rnz*_1_cos_a; + rt_44[3]=tx; + rt_44[4]=rnz*sin_a +rnx*rny*_1_cos_a; + rt_44[5]=cos_a+rny*rny*_1_cos_a; + rt_44[6]= -rnx*sin_a+ rny*rnz*_1_cos_a; + rt_44[7]=ty; + rt_44[8]= -rny*sin_a + rnx*rnz*_1_cos_a; + rt_44[9]= rnx*sin_a + rny*rnz*_1_cos_a; + rt_44[10]=cos_a+rnz*rnz*_1_cos_a; + rt_44[11]=tz; + rt_44[12]=rt_44[13]=rt_44[14]=0; + rt_44[15]=1; + return Matrix; +} +void impl__aruco_getRTfromMatrix44(const cv::Mat& M, cv::Mat& R, cv::Mat& T) +{ + assert(M.cols == M.rows && M.cols == 4); + assert(M.type() == CV_32F || M.type() == CV_64F); + // extract the rotation part + cv::Mat r33 = cv::Mat(M, cv::Rect(0, 0, 3, 3)); + cv::SVD svd(r33); + cv::Mat Rpure = svd.u * svd.vt; + cv::Rodrigues(Rpure, R); + T.create(1, 3, M.type()); + if (M.type() == CV_32F) + for (int i = 0; i < 3; i++) + T.ptr(0)[i] = M.at(i, 3); + else + for (int i = 0; i < 3; i++) + T.ptr(0)[i] = M.at(i, 3); +} + +double reprj_error( const std::vector &objPoints, const std::vectorpoints2d, const CameraParameters &imp,const cv::Mat &rt44){ + std::vector prepj; + cv::Mat rv,tv; + impl__aruco_getRTfromMatrix44(rt44,rv,tv); + cv::projectPoints(objPoints,rv,tv,imp.CameraMatrix,imp.Distorsion,prepj); + double sum=0; + int nvalid=0; + for(size_t i=0;i& POrg, const std::vector& PDst, cv::Mat& RT_4x4) +{ + struct Quaternion + { + Quaternion(float q0, float q1, float q2, float q3) + { + q[0] = q0; + q[1] = q1; + q[2] = q2; + q[3] = q3; + } + cv::Mat getRotation() const + { + cv::Mat R(3, 3, CV_32F); + R.at(0, 0) = q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]; + R.at(0, 1) = 2.f * (q[1] * q[2] - q[0] * q[3]); + R.at(0, 2) = 2.f * (q[1] * q[3] + q[0] * q[2]); + + R.at(1, 0) = 2.f * (q[1] * q[2] + q[0] * q[3]); + R.at(1, 1) = q[0] * q[0] + q[2] * q[2] - q[1] * q[1] - q[3] * q[3]; + R.at(1, 2) = 2.f * (q[2] * q[3] - q[0] * q[1]); + + R.at(2, 0) = 2.f * (q[1] * q[3] - q[0] * q[2]); + R.at(2, 1) = 2.f * (q[2] * q[3] + q[0] * q[1]); + R.at(2, 2) = q[0] * q[0] + q[3] * q[3] - q[1] * q[1] - q[2] * q[2]; + return R; + } + float q[4]; + }; + assert(POrg.size()== PDst.size()); + + cv::Mat _org(POrg.size(),3,CV_32F,(float*)&POrg[0]); + cv::Mat _dst(PDst.size(),3,CV_32F,(float*)&PDst[0]); + + +// _org = _org.reshape(1); +// _dst = _dst.reshape(1); + cv::Mat Mu_s = cv::Mat::zeros(1, 3, CV_32F); + cv::Mat Mu_m = cv::Mat::zeros(1, 3, CV_32F); + // cout<<_s<(0)[i] /= float(_org.rows); + Mu_m.ptr(0)[i] /= float(_dst.rows); + } + + // cout<<"Mu_s="<(0, 0), eigenvectors.at(0, 1), eigenvectors.at(0, 2), + eigenvectors.at(0, 3)); + cv::Mat RR = rot.getRotation(); + // cout<<"RESULT="<& v_m){ + + + + //get the markers in v_m that are in the map + std::vector mapMarkers; + for (auto marker : v_m) + { + if (_map_mm.find(marker.id) != _map_mm.end()) + mapMarkers.push_back(marker); + } + + if( mapMarkers.size()==0)return cv::Mat(); + struct minfo{ + int id; + cv::Mat rt_f2m; + double err; + }; + struct se3{float rt[6];}; + + cv::Mat pose_f2g_out;//result + //estimate the markers locations and see if there is at least one good enough + std::vector good_marker_locations; + std::vector all_marker_locations; + + for(const Marker &marker:mapMarkers){//for ech visible marker + auto mpi=solvePnP_(_map_mm[marker.id]. getMarkerSize(),marker,_cam_params.CameraMatrix,_cam_params.Distorsion); + minfo mi; + mi.id=marker.id; + mi.err=mpi[0].second; + mi.rt_f2m=mpi[0].first; + all_marker_locations.push_back(mi); + if(mpi[1].second/mpi[0].second > aruco_minerrratio_valid) + good_marker_locations.push_back(mi); + mi.rt_f2m=mpi[1].first; + mi.err=mpi[1].second; + all_marker_locations.push_back(mi); + + } + + + //try using more than one marker approach + if (mapMarkers.size()>=2) { + //collect all the markers 3d locations + std::vector markerPoints2d; + std::vector markerPoints3d; + for(const Marker &marker:mapMarkers){ + markerPoints2d.insert(markerPoints2d.end(),marker.begin(),marker.end()); + auto p3d= _map_mm[marker.id].points; + markerPoints3d.insert(markerPoints3d.end(),p3d.begin(),p3d.end()); + } + + //take the all poses and select the one that minimizes the global reproj error + for(auto & ml:all_marker_locations){ + auto pose= ml.rt_f2m *marker_m2g[ml.id]; + //now, compute the repj error of all markers using this info + ml.err=aruco_private::reprj_error(markerPoints3d,markerPoints2d,_cam_params, pose); + } + //sort and get the best + std::sort(all_marker_locations.begin(),all_marker_locations.end(),[](const minfo &a,const minfo &b){return a.err +#include + +namespace aruco +{ + /**Tracks the position of a marker. Instead of trying to calculate the position from scratch everytime, it uses past + * observations to + * estimate the pose. It should solve the problem with ambiguities that arises in some circumstances + * + * To solve ambiguity we follow the following idea. We are using the IPPE method, which returns the two possible + * solutions s0,s1. + * Error solution has a reprojection error e(s_i) and it is assumed that e(s0)& p3d, const std::vector& p2d, + const cv::Mat& cam_matrix, const cv::Mat& dist, cv::Mat& r_io, cv::Mat& t_io); + }; + /**Tracks the position of a markermap + */ + + class ARUCO_EXPORT MarkerMapPoseTracker + { + public: + MarkerMapPoseTracker(); + // Sets the parameters required for operation + // If the msconf has data expressed in meters, then the markerSize parameter is not required. If it is in + // pixels, the markersize will be used to + // transform to meters + // Throws exception if wrong configuraiton + void setParams(const CameraParameters& cam_params, const MarkerMap& msconf, + float markerSize = -1); + // indicates if the call to setParams has been successfull and this object is ready to call estimatePose + bool isValid() const + { + return _isValid; + } + + //resets current state + void reset(){ + _isValid=false; + _rvec=cv::Mat(); + _tvec=cv::Mat(); + } + + // estimates camera pose wrt the markermap + // returns true if pose has been obtained and false otherwise + bool estimatePose(const std::vector& v_m); + + // returns the 4x4 transform matrix. Returns an empty matrix if last call to estimatePose returned false + cv::Mat getRTMatrix() const; + // return the rotation vector. Returns an empty matrix if last call to estimatePose returned false + const cv::Mat getRvec() const + { + return _rvec; + } + // return the translation vector. Returns an empty matrix if last call to estimatePose returned false + const cv::Mat getTvec() const + { + return _tvec; + } + //prevents from big jumps. If the difference between current and previous positions are greater than the value indicated + //assumes no good tracking and the pose will be set as null + void setMaxTrackingDifference(float maxTranslation,float maxAngle){ + _maxTranslation=maxTranslation; + _maxAngle=maxAngle; + } + + protected: + cv::Mat _rvec, _tvec; // current poses + aruco::CameraParameters _cam_params; + MarkerMap _msconf; + std::map _map_mm; + bool _isValid; + cv::Mat relocalization(const std::vector& v_m); + float aruco_minerrratio_valid;/*tau_e in paper*/ + std::map marker_m2g;//for each marker, the transform from the global ref system to the marker ref system + float _maxTranslation=-1,_maxAngle=-1; + }; +}; + +#endif diff --git a/thirdparty/aruco-3.1.12/src/timers.h b/thirdparty/aruco-3.1.12/src/timers.h new file mode 100644 index 0000000..aa50eb5 --- /dev/null +++ b/thirdparty/aruco-3.1.12/src/timers.h @@ -0,0 +1,209 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef ARUCO_TIMERS_H +#define ARUCO_TIMERS_H + + +#include +#include +#include +#include +#include "aruco_export.h" + namespace aruco{ + +//timer +struct ScopeTimer +{ + std::chrono::high_resolution_clock::time_point begin,end; + + std::string name; + bool use; + enum SCALE {NSEC,MSEC,SEC}; + SCALE sc; + ScopeTimer(std::string name_,bool use_=true,SCALE _sc=MSEC) + { +#ifdef USE_TIMERS + name=name_; + use=use_; + sc=_sc; + begin= std::chrono::high_resolution_clock::now(); +#else + (void)name_; + (void)use_; + (void)_sc; + +#endif + } + ~ScopeTimer() + { +#ifdef USE_TIMERS + if (use){ + end= std::chrono::high_resolution_clock::now(); + double fact=1; + std::string str; + switch(sc) + { + case NSEC:fact=1;str="ns";break; + case MSEC:fact=1e6;str="ms";break; + case SEC:fact=1e9;str="s";break; + }; + + std::cout << "Time("<(end-begin).count())/fact< vtimes; + std::vector names; + std::string _name; + + ScopedTimerEvents(std::string name="",bool start=true,SCALE _sc=MSEC){ +#ifdef USE_TIMERS + if(start) add("start");sc=_sc;_name=name; +#else + (void)name; + (void)start; + (void)_sc; +#endif + } + + void add(std::string name){ +#ifdef USE_TIMERS + vtimes.push_back(std::chrono::high_resolution_clock::now()); + names.push_back(name); +#else + (void)name; +#endif + } + void addspaces(std::vector &str ){ + //get max size + size_t m=0; + for(auto &s:str)m=std::max(size_t(s.size()),m); + for(auto &s:str){ + while(s.size()(vtimes[i]-vtimes[i-1]).count())/fact<(vtimes[i]-vtimes[0]).count())/fact<(e-_s).count()); + n++; + } + + void print(SCALE sc=MSEC){ +#ifdef USE_TIMERS + double fact=1; + std::string str; + switch(sc) + { + case NSEC:fact=1;str="ns";break; + case MSEC:fact=1e6;str="ms";break; + case SEC:fact=1e9;str="s";break; + }; + std::cout<<"Time("<<_name<<")= "<< ( sum/n)/fact< +#else + +#include +#endif +#include "aruco.h" +#include "ippe.h" +#include +#include +#include +#include +#include +using namespace cv; +using namespace std; +using namespace aruco; + +vector readDir(string path){ + DIR *dir; + struct dirent *ent; + vector res; + if ((dir = opendir (path.c_str())) != NULL) { + /* print all the files and directories within directory */ + while ((ent = readdir (dir)) != NULL) + res.push_back(path+string("/")+string(ent->d_name)); + closedir (dir); + } + return res; +} + + +//dist : K1,K2,K3,P1,P2,b1,b2 + +vector correctDistortionFraser(const vector &inpoints,const cv::Mat &cameraMatrix,const cv::Mat &distortionModel){ + vector res; + cv::Mat cm32,dm64; + cameraMatrix.convertTo(cm32,CV_64F); + distortionModel.convertTo(dm64,CV_64F); + double cx=cameraMatrix.at(0,2); + double cy=cameraMatrix.at(1,0); + double f=cameraMatrix.at(0,0)+cameraMatrix.at(1,1); + f*=0.5; + double K1=distortionModel.ptr(0)[0]; + double K2=distortionModel.ptr(0)[1]; + double K3=distortionModel.ptr(0)[2]; + double P1=distortionModel.ptr(0)[3]; + double P2=distortionModel.ptr(0)[4]; + double b1=distortionModel.ptr(0)[5]; + double b2=distortionModel.ptr(0)[6]; + for(auto p:inpoints ){ + double x,y; + x= (p.x-cx)/f; + y= (p.y-cy)/f; + float r2=x*x + y*y; + float r4=r2*r2; + float r6=r4*r2; + float ax= x *r2*K1 + x*r4*K2 +x*r6*K3 + (2*x*x+r2)*P1 + 2*P2*x*y+b1*x+b2*y; + float ay=y*r2*K1+ y*r4*K2 +y*r6*K3+ 2*P1*x*y+ (2*y*y+r2)*P2; + cv::Point2f pout(p); + pout.x+=ax; + pout.y+=ay; + res.push_back(pout); + } + return res; +} + +// class for parsing command line +// operator [](string cmd) return whether cmd is present //string operator ()(string cmd) return the value as a string: +// -cmd value +class CmdLineParser +{ + int argc; + char** argv; +public: + CmdLineParser(int _argc, char** _argv) + : argc(_argc) + , argv(_argv) + { + } + bool operator[](string param) + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + return (idx != -1); + } + string operator()(string param, string defvalue = "-1") + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + if (idx == -1) + return defvalue; + else + return (argv[idx + 1]); + } +}; +cv::Mat __resize(const cv::Mat& in, int width) +{ + if (in.size().width <= width) + return in; + float yf = float(width) / float(in.size().width); + cv::Mat im2; + cv::resize(in, im2, cv::Size(width, static_cast(in.size().height * yf))); + return im2; +} +void parseFraserString(string str,cv::Mat &cameraMatrix,cv::Mat &DistCoeff){ + +for(auto &c:str) + if (c==':') c=' '; +stringstream sstr(str); +DistCoeff.create(1,7,CV_64F); +cameraMatrix.create(3,3,CV_64F); +cameraMatrix.setTo(cv::Scalar::all(0)); +sstr>>cameraMatrix.at(0,2);//CX +sstr>>cameraMatrix.at(1,2);//CY +sstr>>cameraMatrix.at(0,0);//F +cameraMatrix.at(1,1)=cameraMatrix.at(0,0); +sstr>>DistCoeff.at(0,0);//K1 +sstr>>DistCoeff.at(0,1);//K2 +sstr>>DistCoeff.at(0,2);//K3 +sstr>>DistCoeff.at(0,3);//P1 +sstr>>DistCoeff.at(0,4);//P2 +sstr>>DistCoeff.at(0,5);//b1 +sstr>>DistCoeff.at(0,6);//b2 + +} +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc <3 || cml["-h"]) + { + cerr << "Usage: readDir outFile [params] \n" + "[-d :ALL_DICTS default] " + "[-minSizeImage : minimum size of a marker in the image. Range (0,1) 1 whole image] " + "[-Fraser CX:CY:F:K1:K2:K3:P1:P2:b1:b2] fraser calibration model" + "[-MSize ] indicates the size of the marker" + "[-fast] uses the fast mode" + << endl; + cerr << "\tDictionaries: "; + for (auto dict : aruco::Dictionary::getDicTypes()) + cerr << dict << " "; + cerr << endl; + cerr << "\t Instead of these, you can directly indicate the path to a file with your own generated " + "dictionary" + << endl; + cout << "Example to work with apriltags dictionary : video.avi -d TAG36h11" << endl << endl; + return 0; + } + + + // read the input image + + // Create the detector + MarkerDetector MDetector; + cv::Mat cameraMatrix,FraserDist; + + + if (cml["-Fraser"]) + parseFraserString(cml("-Fraser"),cameraMatrix,FraserDist); + float msize=stod(cml("-MSize","1.0")); + + aruco::DetectionMode dmode=aruco::DM_NORMAL; + if(cml["-fast"]) dmode=aruco::DM_FAST; + + // Set the dictionary you want to work with + // see dictionary.h for all types + MDetector.setDictionary(cml("-d","ALL_DICTS"), 0.f); + + MDetector.setDetectionMode(dmode,msize); + + vector imagesPath=readDir(argv[1]); + for(auto ip:imagesPath){ + cv::Mat InImage=cv::imread(ip); + if (!InImage.empty()){ + cerr<<"processing "< Markers = MDetector.detect(InImage); + ofstream file(argv[2],ios::app); + file< +#include +#include +#include +#include +#include "markerdetector.h" +#include "dictionary_based.h" +using namespace std; + +std::vector getM0InnerCorners(vector marker0Corners) +{ + auto homotransform=[](cv::Point2f p,cv::Mat H){ + double *h=H.ptr(); + cv::Point3f res; + res.x=p.x*h[0]+p.y*h[1]+h[2]; + res.y=p.x*h[3]+p.y*h[4]+h[5]; + res.z=p.x*h[6]+p.y*h[7]+h[8]; + return cv::Point2f(res.x/res.z,res.y/res.z); + }; + uchar m0[64]= { 0, 0,0,0,0,0,0, 0, + 0, 1,1,0,1,0,0, 0, + 0, 1,0,1,0,1,1, 0, + 0, 0,1,1,0,0,0, 0, + 0, 1,1,1,0,1,0, 0, + 0, 0,0,0,0,1,0, 0, + 0, 0,1,1,1,0,1, 0, + 0, 0,0,0,0,0,0, 0 + }; + cv::Mat mat(8,8,CV_8UC1,m0); + int nBitsSquared = int(sqrt(mat.total())); + float bitSize = 1. / (nBitsSquared); + + //Get inner corners + std::vector innerCorners; + for(int y=0; y< mat.rows-1; y++) + { + for(int x=0; x< mat.cols-1; x++) + { + + if( ((mat.at(y, x) == mat.at(y+1, x+1)) && + (mat.at(y, x) != mat.at(y, x+1) || + mat.at(y, x) != mat.at(y+1, x))) + + || + + ((mat.at(y, x+1) == mat.at(y+1, x)) && + (mat.at(y, x+1) != mat.at(y, x) || + mat.at(y, x+1) != mat.at(y+1, x+1))) + ) + innerCorners.push_back(cv::Point2f(1+x-nBitsSquared/2.f, -(1+y-nBitsSquared/2.f)) * bitSize); + } + } + + std::vector borderCorners; + + + borderCorners.emplace_back( -0.5,0.5); + borderCorners.emplace_back( 0.5,0.5); + borderCorners.emplace_back( 0.5,-0.5); + borderCorners.emplace_back( -0.5,-0.5); + + + cv::Mat H=findHomography(borderCorners,marker0Corners); + //now compute the homography for the inner coners + for(auto &c:innerCorners) + c=homotransform(c,H); + + return innerCorners; +} + +int main(int argc,char **argv){ + try { + if(argc==1)throw std::runtime_error("Usage: image"); + aruco::MarkerDetector Detector; + auto img=cv::imread(argv[1]); + cv::Mat grey; + cv::cvtColor(img,grey,cv::COLOR_BGRA2GRAY); + + if(img.empty())throw std::runtime_error ("Bad image"); + auto markers=Detector.detect(img); + cv::Point2f pOff (5,5); + + aruco::DictionaryBased *db=dynamic_cast( Detector.getMarkerLabeler().get()); + if(db!=nullptr){ + auto mc=db->getDictionaries()[0].getMapCode(); + cout< +#include +#include +using namespace std; +class CmdLineParser +{ + int argc; + char** argv; +public: + CmdLineParser(int _argc, char** _argv) + : argc(_argc) + , argv(_argv) + { + } + bool operator[](string param) + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + return (idx != -1); + } + string operator()(string param, string defvalue = "-1") + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + if (idx == -1) + return defvalue; + else + return (argv[idx + 1]); + } +}; + +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc == 1) + { + cerr << "Usage: outdir dictionary [ -s bit_image_size: 75 default] " << endl; + auto dict_names = aruco::Dictionary::getDicTypes(); + cerr << "\t\tDictionaries: "; + for (auto dict : dict_names) + cerr << dict << " "; + cerr << endl; + cerr << "\t Instead of these, you can directly indicate the path to a file with your own generated " + "dictionary" + << endl; + return -1; + } + aruco::Dictionary dict = aruco::Dictionary::load(argv[2]); + int pixSize = stoi(cml("-s", "75")); + + string dict_name = dict.getName(); + std::transform(dict_name.begin(), dict_name.end(), dict_name.begin(), ::tolower); + // + for (auto m : dict.getMapCode()) + { + string number = std::to_string(m.second); + while (number.size() != 5) + number = "0" + number; + stringstream name; + name << argv[1] << "/" + dict_name + "_" << number << ".png"; + cout << name.str() << endl; + cv::imwrite(name.str(), dict.getMarkerImage_id(m.second, pixSize,true,false,true)); + } + } + catch (std::exception& ex) + { + cerr << ex.what() << endl; + } +} diff --git a/thirdparty/aruco-3.1.12/utils/aruco_print_marker.cpp b/thirdparty/aruco-3.1.12/utils/aruco_print_marker.cpp new file mode 100644 index 0000000..6750816 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils/aruco_print_marker.cpp @@ -0,0 +1,106 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +// saves to file an image of the indicated marker from the dictionary indicated + +#include "aruco.h" +#include + +#include +#include +using namespace cv; +using namespace std; + +// convinience command line parser +class CmdLineParser +{ + int argc; + char** argv; +public: + CmdLineParser(int _argc, char** _argv) + : argc(_argc) + , argv(_argv) + { + } + bool operator[](string param) + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + return (idx != -1); + } + string operator()(string param, string defvalue = "-1") + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + if (idx == -1) + return defvalue; + else + return (argv[idx + 1]); + } +}; + +int main(int argc, char** argv) +{ + try + { CmdLineParser cml(argc, argv); + + if (argc < 2) + { + // You can also use ids 2000-2007 but it is not safe since there are a lot of false positives. + cerr << "Usage: outfile.(jpg|png|ppm|bmp) [options] \n\t[-e use enclsing corners]\n\t[-bs :bit size in pixels. 50 by " + "default ] \n\t[-d : ARUCO_MIP_36h12 default] \n\t[-border: adds the white border around]" + "\n\t [-center: highlights the center] " + << endl; + auto dict_names = aruco::Dictionary::getDicTypes(); + cerr << "\t\tDictionaries: "; + for (auto dict : dict_names) + cerr << dict << " "; + cerr << endl; + cerr << "\t Instead of these, you can directly indicate the path to a file with your own generated " + "dictionary" + << endl; + + return -1; + } + int pixSize = std::stoi(cml("-bs", "75")); // pixel size each each bit + bool enclosingCorners = cml["-e"]; + bool waterMark = true; + // loads the desired dictionary + aruco::Dictionary dic = aruco::Dictionary::load(cml("-d", "ARUCO_MIP_36h12")); + + cv::imwrite(argv[2], dic.getMarkerImage_id(stoi(argv[1]), pixSize, waterMark, enclosingCorners,cml["-border"],cml["-center"])); + } + catch (std::exception& ex) + { + cout << ex.what() << endl; + } +} diff --git a/thirdparty/aruco-3.1.12/utils/aruco_simple.cpp b/thirdparty/aruco-3.1.12/utils/aruco_simple.cpp new file mode 100644 index 0000000..ea85056 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils/aruco_simple.cpp @@ -0,0 +1,135 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + + +#include "aruco.h" +#include +#include +#include +#include +#include + +using namespace cv; +using namespace std; +using namespace aruco; +// class for parsing command line +// operator [](string cmd) return whether cmd is present //string operator ()(string cmd) return the value as a string: +// -cmd value +class CmdLineParser{int argc;char** argv;public:CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} bool operator[](string param) {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;return (idx != -1);} string operator()(string param, string defvalue = "-1") {int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;if (idx == -1)return defvalue;else return (argv[idx + 1]);}}; + +cv::Mat __resize(const cv::Mat& in, int width) +{ + if (in.size().width <= width) + return in; + float yf = float(width) / float(in.size().width); + cv::Mat im2; + cv::resize(in, im2, cv::Size(width, static_cast(in.size().height * yf))); + return im2; +} + +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc == 1 || cml["-h"]) + { + cerr << "Usage: (in_image|video.avi) [-c cameraParams.yml] [-s markerSize] [-d :ALL_DICTS default] [-f arucoConfig.yml] " << endl; + cerr << "\tDictionaries: "; + for (auto dict : aruco::Dictionary::getDicTypes()) + cerr << dict << " "; + cerr << endl; + cerr << "\t Instead of these, you can directly indicate the path to a file with your own generated " + "dictionary" + << endl; + cout << "Example to work with apriltags dictionary : video.avi -d TAG36h11" << endl << endl; + return 0; + } + + aruco::CameraParameters CamParam; + // read the input image + cv::Mat InImage; + // Open input and read image + VideoCapture vreader(argv[1]); + if (vreader.isOpened()) vreader >> InImage; + else throw std::runtime_error("Could not open input"); + + // read camera parameters if specifed + if (cml["-c"]) + CamParam.readFromXMLFile(cml("-c")); + + // read marker size if specified (default value -1) + float MarkerSize = std::stof(cml("-s", "-1")); + // Create the detector + MarkerDetector MDetector; + if(cml["-f"]){//uses a configuration file. YOu can create it from aruco_test application + MDetector.loadParamsFromFile(cml("-f")); + } + else{ + + // Set the dictionary you want to work with, if you included option -d in command line + //By default, all valid dictionaries are examined + if (cml["-d"]) + MDetector.setDictionary(cml("-d"), 0.f); + + } + // Ok, let's detect + vector Markers = MDetector.detect(InImage, CamParam, MarkerSize); + + // for each marker, draw info and its boundaries in the image + for (unsigned int i = 0; i < Markers.size(); i++) + { + cout << Markers[i] << endl; + Markers[i].draw(InImage, Scalar(0, 0, 255), 2); + } + // draw a 3d cube in each marker if there is 3d info + if (CamParam.isValid() && MarkerSize != -1) + for (unsigned int i = 0; i < Markers.size(); i++) + { + if(Markers[i].id==229 || Markers[i].id==161) + cout<< "Camera Location= "< +#include +#include +#include +#include +#include +#include + +#if CV_MAJOR_VERSION >= 4 +#define CV_CAP_PROP_FRAME_COUNT cv::CAP_PROP_FRAME_COUNT +#define CV_CAP_PROP_POS_FRAMES cv::CAP_PROP_POS_FRAMES +#endif +using namespace std; +using namespace cv; +using namespace aruco; + +MarkerDetector MDetector; +VideoCapture TheVideoCapturer; +vector TheMarkers; +Mat TheInputImage,TheInputImageGrey, TheInputImageCopy; +CameraParameters TheCameraParameters; +void cvTackBarEvents(int pos, void*); +string dictionaryString; +int iDetectMode=0,iMinMarkerSize=0,iCorrectionRate=0,iShowAllCandidates=0,iEnclosed=0,iThreshold,iCornerMode,iDictionaryIndex,iTrack=0; + +int waitTime = 0; +bool showMennu=false,bPrintHelp=false,isVideo=false; +class CmdLineParser{int argc;char** argv;public:CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} bool operator[](string param) {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;return (idx != -1);} string operator()(string param, string defvalue = "-1") {int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;if (idx == -1)return defvalue;else return (argv[idx + 1]);}}; +struct TimerAvrg{std::vector times;size_t curr=0,n; std::chrono::high_resolution_clock::time_point begin,end; TimerAvrg(int _n=30){n=_n;times.reserve(n); }inline void start(){begin= std::chrono::high_resolution_clock::now(); }inline void stop(){end= std::chrono::high_resolution_clock::now();double duration=double(std::chrono::duration_cast(end-begin).count())*1e-6;if ( times.size()=times.size()) curr=0;}}double getAvrg(){double sum=0;for(auto t:times) sum+=t;return sum/double(times.size());}}; + +TimerAvrg Fps; + +cv::Mat resize(const cv::Mat& in, cv::Size s){ +if(s.width==-1 || s.height==-1)return in; +cv::Mat im2; +cv::resize(in, im2, s); +return im2; +} + + + +cv::Mat resize(const cv::Mat& in, int width) +{ + if (in.size().width <= width) + return in; + float yf = float(width) / float(in.size().width); + cv::Mat im2; + cv::resize(in, im2, cv::Size(width, static_cast(in.size().height * yf))); + return im2; +} +cv::Mat resizeImage(cv::Mat &in,float resizeFactor){ + if (fabs(1-resizeFactor)<1e-3 )return in; + float nc=float(in.cols)*resizeFactor; + float nr=float(in.rows)*resizeFactor; + cv::Mat imres; + cv::resize(in,imres,cv::Size(nc,nr)); + cout<<"Imagesize="<>s.width>>s.height) + return s; + return cv::Size(-1,-1); + +} +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc < 2 || cml["-h"]) + { + cerr << "Invalid number of arguments" << endl; + cerr << "Usage: (in.avi|live[:camera_index(e.g 0 or 1)]) [-c camera_params.yml] [-s marker_size_in_meters] [-d " + "dictionary:ALL_DICTS by default] [-h] [-ws w:h] [-skip frames]" + << endl; + cerr << "\tDictionaries: "; + for (auto dict : aruco::Dictionary::getDicTypes()) + cerr << dict << " "; + cerr << endl; + cerr << "\t Instead of these, you can directly indicate the path to a file with your own generated " + "dictionary" + << endl; + return false; + } + + /////////// PARSE ARGUMENTS + string TheInputVideo = argv[1]; + // read camera parameters if passed + if (cml["-c"]) + TheCameraParameters.readFromXMLFile(cml("-c")); + + float TheMarkerSize = std::stof(cml("-s", "-1")); + //resize factor + float resizeFactor=stof(cml("-rf","1")); + + iMinMarkerSize=stof(cml("-mms","0.0")); + + + /////////// OPEN VIDEO + // read from camera or from file + if (TheInputVideo.find("live") != string::npos) + { + int vIdx = 0; + // check if the :idx is here + char cad[100]; + if (TheInputVideo.find(":") != string::npos) + { + std::replace(TheInputVideo.begin(), TheInputVideo.end(), ':', ' '); + sscanf(TheInputVideo.c_str(), "%s %d", cad, &vIdx); + } + cout << "Opening camera index " << vIdx << endl; + TheVideoCapturer.open(vIdx); + waitTime = 10; + isVideo=true; + } + else{ + TheVideoCapturer.open(TheInputVideo); + if ( TheVideoCapturer.get(CV_CAP_PROP_FRAME_COUNT)>=2) isVideo=true; + if(cml["-skip"]) + TheVideoCapturer.set(CV_CAP_PROP_POS_FRAMES,stoi(cml("-skip"))); + + } + // check video is open + if (!TheVideoCapturer.isOpened()) + throw std::runtime_error("Could not open video"); + + + //create windows + if(cml["-fs"]){ + cv::namedWindow("in", cv::WINDOW_FULLSCREEN); + cv::namedWindow("thres", cv::WINDOW_FULLSCREEN); + } + else if(cml["-ws"]) + { + cv::namedWindow("in",cv::WINDOW_NORMAL); + cv::Size s=parseSize(cml("-ws")); + cv::resizeWindow("in",s.width,s.height); + cv::namedWindow("thres",cv::WINDOW_NORMAL); + resizeWindow("thres",s.width,s.height); + + } + + else { + cv::namedWindow("in",cv::WINDOW_NORMAL); + cv::resizeWindow("in",640,480); + float w=std::min(int(1920),int(TheInputImage.cols)); + float f=w/float(TheInputImage.cols); + resizeWindow("in",w,float(TheInputImage.rows)*f); + } + + ///// CONFIGURE DATA + // read first image to get the dimensions + TheVideoCapturer >> TheInputImage; + if (TheCameraParameters.isValid()) + TheCameraParameters.resize(TheInputImage.size()); + dictionaryString=cml("-d", "ALL_DICTS"); + iDictionaryIndex=(uint64_t)aruco::Dictionary::getTypeFromString(dictionaryString); + MDetector.setDictionary(dictionaryString,float(iCorrectionRate)/10. ); // sets the dictionary to be employed (ARUCO,APRILTAGS,ARTOOLKIT,etc) + iThreshold=MDetector.getParameters().ThresHold; + iCornerMode= MDetector.getParameters().cornerRefinementM; + + + setParamsFromGlobalVariables(MDetector); + + + // go! + char key = 0; + int index = 0,indexSave=0; + // capture until press ESC or until the end of the video + + do + { + + TheVideoCapturer.retrieve(TheInputImage); + std::cout<<"Frame:"< + +#include "aruco.h" +#include "cvdrawingutils.h" +#include +#include +using namespace std; +using namespace cv; +using namespace aruco; +// class for parsing command line +class CmdLineParser{int argc;char** argv;public:CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} bool operator[](string param) {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;return (idx != -1);} string operator()(string param, string defvalue = "-1") {int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;if (idx == -1)return defvalue;else return (argv[idx + 1]);}}; + +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc < 4 || cml["-h"]) + { + cerr << "Usage: video.avi cameraParams.yml markerSize [-d :ALL_DICTS default] " << endl; + cerr << "\tDictionaries: "; + for (auto dict : aruco::Dictionary::getDicTypes()) + cerr << dict << " "; + cerr << endl; + cerr << "\t Instead of these, you can directly indicate the path to a file with your own generated " + "dictionary" + << endl; + cout << "Example to work with apriltags dictionary : video.avi -d TAG36h11" << endl << endl; + return 0; + } + + aruco::CameraParameters CamParam; + + // read the input image + cv::Mat InImage; + // Open input and read image + VideoCapture vreader(argv[1]); +// VideoCapture vreader(1); + if (vreader.isOpened()) + vreader >> InImage; + else + { + cerr << "Could not open input" << endl; + return -1; + } + cout<<"JJJJ"< + MTracker; // use a map so that for each id, we use a different pose tracker + // Set the dictionary you want to work with, if you included option -d in command line + // see dictionary.h for all types + MDetector.setDictionary(cml("-d","ALL_DICTS"), 0.f); + + do + { + vreader.retrieve(InImage); + // Ok, let's detect + vector Markers = MDetector.detect(InImage); + for (auto& marker : Markers) // for each marker + MTracker[marker.id].estimatePose(marker, CamParam, MarkerSize); // call its tracker and estimate the pose + + // for each marker, draw info and its boundaries in the image + for (unsigned int i = 0; i < Markers.size(); i++) + { + cout << Markers[i] << endl; + Markers[i].draw(InImage, Scalar(0, 0, 255), 2); + } + // draw a 3d cube in each marker if there is 3d info + if (CamParam.isValid() && MarkerSize != -1) + { + for (unsigned int i = 0; i < Markers.size(); i++) + { + if (Markers[i].isPoseValid()){ + CvDrawingUtils::draw3dCube(InImage, Markers[i], CamParam); + CvDrawingUtils::draw3dAxis(InImage, Markers[i], CamParam); + } + } + } + // show input with augmented information + cv::namedWindow("in", 1); + cv::imshow("in", InImage); + } while (char(cv::waitKey(0)) != 27 && vreader.grab()); // wait for esc to be pressed + + if (cml["-o"]) + cv::imwrite(cml("-o"), InImage); + } + catch (std::exception& ex) + + { + cout << "Exception :" << ex.what() << endl; + } +} diff --git a/thirdparty/aruco-3.1.12/utils/common.h b/thirdparty/aruco-3.1.12/utils/common.h new file mode 100644 index 0000000..5b33650 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils/common.h @@ -0,0 +1,95 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef _COMMON_ARUCO_ +#define _COMMON_ARUCO_ +#include +using namespace cv; +/**This function reads the matrix intrinsics and the distorsion coefficients from a file. + * The format of the file is + * \code + * # comments + * fx fy cx cy k1 k2 p1 p2 width height 1 + * \endcode + * @param TheIntrinsicFile path to the file with the info + * @param TheIntriscCameraMatrix output matrix with the intrinsics + * @param TheDistorsionCameraParams output vector with distorsion params + * @param size of the images captured. Note that the images you are using might be different from these employed for calibration (which are in the file). + * If so, the intrinsic must be adapted properly. That is why you must pass here the size of the images you are employing + * @return true if params are readed properly + */ + +bool readIntrinsicFile(string TheIntrinsicFile,Mat & TheIntriscCameraMatrix,Mat &TheDistorsionCameraParams,Size size) +{ + //open file + ifstream InFile(TheIntrinsicFile.c_str()); + if (!InFile) return false; + char line[1024]; + InFile.getline(line,1024); //skype first line that should contain only comments + InFile.getline(line,1024);//read the line with real info + + //transfer to a proper container + stringstream InLine; + InLine<>TheIntriscCameraMatrix.at(0,0);//fx + InLine>>TheIntriscCameraMatrix.at(1,1); //fy + InLine>>TheIntriscCameraMatrix.at(0,2); //cx + InLine>>TheIntriscCameraMatrix.at(1,2);//cy + //read distorion parameters + for(int i=0;i<4;i++) InLine>>TheDistorsionCameraParams.at(i,0); + + //now, read the camera size + float width,height; + InLine>>width>>height; + //resize the camera parameters to fit this image size + float AxFactor= float(size.width)/ width; + float AyFactor= float(size.height)/ height; + TheIntriscCameraMatrix.at(0,0)*=AxFactor; + TheIntriscCameraMatrix.at(0,2)*=AxFactor; + TheIntriscCameraMatrix.at(1,1)*=AyFactor; + TheIntriscCameraMatrix.at(1,2)*=AyFactor; + + //debug + cout<<"fx="<(0,0)<\n" +"\n" +"\n" +"view000.png\n" +"view001.png\n" +"\n" +"view003.png\n" +"view010.png\n" +"one_extra_view.jpg\n" +"\n" +"\n"; + + + + +const char* liveCaptureHelp = + "When the live video from camera is used as input, the following hot-keys may be used:\n" + " , 'q' - quit the program\n" + " 'g' - start capturing images\n" + " 'u' - switch undistortion on/off\n"; + +static void help() +{ + printf( "This is a camera calibration sample.\n" + "Usage: calibration\n" + " -w # the number of inner corners per one of board dimension\n" + " -h # the number of inner corners per another board dimension\n" + " [-pt ] # the type of pattern: chessboard or circles' grid\n" + " [-n ] # the number of frames to use for calibration\n" + " # (if not specified, it will be set to the number\n" + " # of board views actually available)\n" + " [-d ] # a minimum delay in ms between subsequent attempts to capture a next view\n" + " # (used only for video capturing)\n" + " [-s ] # square size in some user-defined units (1 by default)\n" + " [-o ] # the output filename for intrinsic [and extrinsic] parameters\n" + " [-op] # write detected feature points\n" + " [-oe] # write extrinsic parameters\n" + " [-zt] # assume zero tangential distortion\n" + " [-a ] # fix aspect ratio (fx/fy)\n" + " [-p] # fix the principal point at the center\n" + " [-v] # flip the captured images around the horizontal axis\n" + " [-V] # use a video file, and not an image list, uses\n" + " # [input_data] string for the video file name\n" + " [-su] # show undistorted images after calibration\n" + " [input_data] # input data, one of the following:\n" + " # - text file with a list of the images of the board\n" + " # the text file can be generated with imagelist_creator\n" + " # - name of video file with a video of the board\n" + " # if input_data not specified, a live view from the camera is used\n" + "\n" ); + printf("\n%s",usage); + printf( "\n%s", liveCaptureHelp ); +} + +enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 }; +enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID }; + +static double computeReprojectionErrors( + const vector >& objectPoints, + const vector >& imagePoints, + const vector& rvecs, const vector& tvecs, + const Mat& cameraMatrix, const Mat& distCoeffs, + vector& perViewErrors ) +{ + vector imagePoints2; + int i, totalPoints = 0; + double totalErr = 0, err; + perViewErrors.resize(objectPoints.size()); + + for( i = 0; i < (int)objectPoints.size(); i++ ) + { + projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i], + cameraMatrix, distCoeffs, imagePoints2); + err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2); + int n = (int)objectPoints[i].size(); + perViewErrors[i] = (float)std::sqrt(err*err/n); + totalErr += err*err; + totalPoints += n; + } + + return std::sqrt(totalErr/totalPoints); +} + +static void calcChessboardCorners(Size boardSize, float squareSize, vector& corners, Pattern patternType = CHESSBOARD) +{ + corners.resize(0); + + switch(patternType) + { + case CHESSBOARD: + case CIRCLES_GRID: + for( int i = 0; i < boardSize.height; i++ ) + for( int j = 0; j < boardSize.width; j++ ) + corners.push_back(Point3f(float(j*squareSize), + float(i*squareSize), 0)); + break; + + case ASYMMETRIC_CIRCLES_GRID: + for( int i = 0; i < boardSize.height; i++ ) + for( int j = 0; j < boardSize.width; j++ ) + corners.push_back(Point3f(float((2*j + i % 2)*squareSize), + float(i*squareSize), 0)); + break; + + default: + std::cerr<< "Unknown pattern type\n"; + } +} + +static bool runCalibration( vector > imagePoints, + Size imageSize, Size boardSize, Pattern patternType, + float squareSize, float aspectRatio, + int flags, Mat& cameraMatrix, Mat& distCoeffs, + vector& rvecs, vector& tvecs, + vector& reprojErrs, + double& totalAvgErr) +{ + cameraMatrix = Mat::eye(3, 3, CV_64F); + if( flags & CALIB_FIX_ASPECT_RATIO ) + cameraMatrix.at(0,0) = aspectRatio; + + distCoeffs = Mat::zeros(8, 1, CV_64F); + + vector > objectPoints(1); + calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType); + + objectPoints.resize(imagePoints.size(),objectPoints[0]); + + double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, + distCoeffs, rvecs, tvecs, flags|CALIB_FIX_K4|CALIB_FIX_K5); + ///*|CALIB_FIX_K3*/|CALIB_FIX_K4|CALIB_FIX_K5); + printf("RMS error reported by calibrateCamera: %g\n", rms); + + bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs); + + totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, + rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs); + + return ok; +} + + +static void saveCameraParams( const string& filename, + Size imageSize, Size boardSize, + float squareSize, float aspectRatio, int flags, + const Mat& cameraMatrix, const Mat& distCoeffs, + const vector& rvecs, const vector& tvecs, + const vector& reprojErrs, + const vector >& imagePoints, + double totalAvgErr ) +{ + FileStorage fs( filename, FileStorage::WRITE ); + + time_t tt; + time( &tt ); + struct tm *t2 = localtime( &tt ); + char buf[1024]; + strftime( buf, sizeof(buf)-1, "%c", t2 ); + + fs << "calibration_time" << buf; + + if( !rvecs.empty() || !reprojErrs.empty() ) + fs << "nframes" << (int)std::max(rvecs.size(), reprojErrs.size()); + fs << "image_width" << imageSize.width; + fs << "image_height" << imageSize.height; + fs << "board_width" << boardSize.width; + fs << "board_height" << boardSize.height; + fs << "square_size" << squareSize; + + if( flags & CALIB_FIX_ASPECT_RATIO ) + fs << "aspectRatio" << aspectRatio; + + if( flags != 0 ) + { + sprintf( buf, "flags: %s%s%s%s", + flags & CALIB_USE_INTRINSIC_GUESS ? "+use_intrinsic_guess" : "", + flags & CALIB_FIX_ASPECT_RATIO ? "+fix_aspectRatio" : "", + flags & CALIB_FIX_PRINCIPAL_POINT ? "+fix_principal_point" : "", + flags & CALIB_ZERO_TANGENT_DIST ? "+zero_tangent_dist" : "" ); + //cvWriteComment( *fs, buf, 0 ); + } + + fs << "flags" << flags; + + fs << "camera_matrix" << cameraMatrix; + fs << "distortion_coefficients" << distCoeffs; + + fs << "avg_reprojection_error" << totalAvgErr; + if( !reprojErrs.empty() ) + fs << "per_view_reprojection_errors" << Mat(reprojErrs); + + if( !rvecs.empty() && !tvecs.empty() ) + { + CV_Assert(rvecs[0].type() == tvecs[0].type()); + Mat bigmat((int)rvecs.size(), 6, rvecs[0].type()); + for( int i = 0; i < (int)rvecs.size(); i++ ) + { + Mat r = bigmat(Range(i, i+1), Range(0,3)); + Mat t = bigmat(Range(i, i+1), Range(3,6)); + + CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1); + CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1); + //*.t() is MatExpr (not Mat) so we can use assignment operator + r = rvecs[i].t(); + t = tvecs[i].t(); + } + //cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0 ); + fs << "extrinsic_parameters" << bigmat; + } + + if( !imagePoints.empty() ) + { + Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2); + for( int i = 0; i < (int)imagePoints.size(); i++ ) + { + Mat r = imagePtMat.row(i).reshape(2, imagePtMat.cols); + Mat imgpti(imagePoints[i]); + imgpti.copyTo(r); + } + fs << "image_points" << imagePtMat; + } +} + +static bool readStringList( const string& filename, vector& l ) +{ + try{ + FileStorage fs(filename, FileStorage::READ); + if( !fs.isOpened() ) + return false; + FileNode n = fs.getFirstTopLevelNode(); + if( n.type() != FileNode::SEQ ) + return false; + FileNodeIterator it = n.begin(), it_end = n.end(); + for( ; it != it_end; ++it ) + l.push_back((string)*it); + }catch(std::exception &ex){ + cerr<<"ex:"<& l )throw(std::exception) +{ + + l.resize(0); +if( !readStringList(filename,l)){ + cout<<"read txt file"<>str; + while(!file.eof()){ + l.push_back(str); + file>>str; + + } + return true; + +} +else return true; +} + + +static bool runAndSave(const string& outputFilename, + const vector >& imagePoints, + Size imageSize, Size boardSize, Pattern patternType, float squareSize, + float aspectRatio, int flags, Mat& cameraMatrix, + Mat& distCoeffs, bool writeExtrinsics, bool writePoints ) +{ + vector rvecs, tvecs; + vector reprojErrs; + double totalAvgErr = 0; + + bool ok = runCalibration(imagePoints, imageSize, boardSize, patternType, squareSize, + aspectRatio, flags, cameraMatrix, distCoeffs, + rvecs, tvecs, reprojErrs, totalAvgErr); + printf("%s. avg reprojection error = %.2f\n", + ok ? "Calibration succeeded" : "Calibration failed", + totalAvgErr); + + if( ok ) + saveCameraParams( outputFilename, imageSize, + boardSize, squareSize, aspectRatio, + flags, cameraMatrix, distCoeffs, + writeExtrinsics ? rvecs : vector(), + writeExtrinsics ? tvecs : vector(), + writeExtrinsics ? reprojErrs : vector(), + writePoints ? imagePoints : vector >(), + totalAvgErr ); + return ok; +} + + +int main( int argc, char** argv ) +{ + Size boardSize, imageSize; + float squareSize = 1.f, aspectRatio = 1.f; + Mat cameraMatrix, distCoeffs; + const char* outputFilename = "out_camera_data.yml"; + const char* inputFilename = 0; + + int i, nframes = 10; + bool writeExtrinsics = false, writePoints = false; + bool undistortImage = false; + int flags = 0; + cv::VideoCapture capture; + bool flipVertical = false; + bool showUndistorted = false; + bool videofile = false; + int delay = 1000; + clock_t prevTimestamp = 0; + int mode = DETECTION; + int cameraId = 0; + vector > imagePoints; + vector imageList; + Pattern pattern = CHESSBOARD; + + if( argc < 2 ) + { + help(); + return 0; + } + + for( i = 1; i < argc; i++ ) + { + const char* s = argv[i]; + if( strcmp( s, "-w" ) == 0 ) + { + if( sscanf( argv[++i], "%u", &boardSize.width ) != 1 || boardSize.width <= 0 ) + return fprintf( stderr, "Invalid board width\n" ), -1; + } + else if( strcmp( s, "-h" ) == 0 ) + { + if( sscanf( argv[++i], "%u", &boardSize.height ) != 1 || boardSize.height <= 0 ) + return fprintf( stderr, "Invalid board height\n" ), -1; + } + else if( strcmp( s, "-pt" ) == 0 ) + { + i++; + if( !strcmp( argv[i], "circles" ) ) + pattern = CIRCLES_GRID; + else if( !strcmp( argv[i], "acircles" ) ) + pattern = ASYMMETRIC_CIRCLES_GRID; + else if( !strcmp( argv[i], "chessboard" ) ) + pattern = CHESSBOARD; + else + return fprintf( stderr, "Invalid pattern type: must be chessboard or circles\n" ), -1; + } + else if( strcmp( s, "-s" ) == 0 ) + { + if( sscanf( argv[++i], "%f", &squareSize ) != 1 || squareSize <= 0 ) + return fprintf( stderr, "Invalid board square width\n" ), -1; + } + else if( strcmp( s, "-n" ) == 0 ) + { + if( sscanf( argv[++i], "%u", &nframes ) != 1 || nframes <= 3 ) + return printf("Invalid number of images\n" ), -1; + } + else if( strcmp( s, "-a" ) == 0 ) + { + if( sscanf( argv[++i], "%f", &aspectRatio ) != 1 || aspectRatio <= 0 ) + return printf("Invalid aspect ratio\n" ), -1; + flags |= CALIB_FIX_ASPECT_RATIO; + } + else if( strcmp( s, "-d" ) == 0 ) + { + if( sscanf( argv[++i], "%u", &delay ) != 1 || delay <= 0 ) + return printf("Invalid delay\n" ), -1; + } + else if( strcmp( s, "-op" ) == 0 ) + { + writePoints = true; + } + else if( strcmp( s, "-oe" ) == 0 ) + { + writeExtrinsics = true; + } + else if( strcmp( s, "-zt" ) == 0 ) + { + flags |= CALIB_ZERO_TANGENT_DIST; + } + else if( strcmp( s, "-p" ) == 0 ) + { + flags |= CALIB_FIX_PRINCIPAL_POINT; + } + else if( strcmp( s, "-v" ) == 0 ) + { + flipVertical = true; + } + else if( strcmp( s, "-V" ) == 0 ) + { + videofile = true; + } + else if( strcmp( s, "-o" ) == 0 ) + { + outputFilename = argv[++i]; + } + else if( strcmp( s, "-su" ) == 0 ) + { + showUndistorted = true; + } + else if( s[0] != '-' ) + { + if( isdigit(s[0]) ) + sscanf(s, "%d", &cameraId); + else + inputFilename = s; + } + else + return fprintf( stderr, "Unknown option %s", s ), -1; + } + + if( inputFilename ) + { + if( !videofile && readInputFile(inputFilename, imageList) ) + mode = CAPTURING; + else + capture.open(inputFilename); + } + else + capture.open(cameraId); + + if( !capture.isOpened() && imageList.empty() ) + return fprintf( stderr, "Could not initialize video (%d) capture\n",cameraId ), -2; + + if( !imageList.empty() ) + nframes = (int)imageList.size(); + + if( capture.isOpened() ) + printf( "%s", liveCaptureHelp ); + + namedWindow( "Image View", 1 ); + + for(i = 0;;i++) + { + Mat view, viewGray; + bool blink = false; + + if( capture.isOpened() ) + { + Mat view0; + capture >> view0; + view0.copyTo(view); + } + else if( i < (int)imageList.size() ) + view = imread(imageList[i], 1); + + if(view.empty()) + { + if( imagePoints.size() > 0 ) + runAndSave(outputFilename, imagePoints, imageSize, + boardSize, pattern, squareSize, aspectRatio, + flags, cameraMatrix, distCoeffs, + writeExtrinsics, writePoints); + break; + } + + imageSize = view.size(); + + if( flipVertical ) + flip( view, view, 0 ); + + vector pointbuf; + cvtColor(view, viewGray, COLOR_BGR2GRAY); + + bool found; + switch( pattern ) + { + case CHESSBOARD: + found = findChessboardCorners( view, boardSize, pointbuf, + CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE); + break; + case CIRCLES_GRID: + found = findCirclesGrid( view, boardSize, pointbuf ); + break; + case ASYMMETRIC_CIRCLES_GRID: + found = findCirclesGrid( view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID ); + break; + default: + return fprintf( stderr, "Unknown pattern type\n" ), -1; + } + + // improve the found corners' coordinate accuracy + if( pattern == CHESSBOARD && found) cornerSubPix( viewGray, pointbuf, Size(11,11), + Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 )); + + if( mode == CAPTURING && found && + (!capture.isOpened() || clock() - prevTimestamp > delay*1e-3*CLOCKS_PER_SEC) ) + { + imagePoints.push_back(pointbuf); + prevTimestamp = clock(); + blink = capture.isOpened(); + } + + if(found) + drawChessboardCorners( view, boardSize, Mat(pointbuf), found ); + + string msg = mode == CAPTURING ? "100/100" : + mode == CALIBRATED ? "Calibrated" : "Press 'g' to start"; + int baseLine = 0; + Size textSize = getTextSize(msg, 1, 1, 1, &baseLine); + Point textOrigin(view.cols - 2*textSize.width - 10, view.rows - 2*baseLine - 10); + + if( mode == CAPTURING ) + { + if(undistortImage) + msg = format( "%d/%d Undist", (int)imagePoints.size(), nframes ); + else + msg = format( "%d/%d", (int)imagePoints.size(), nframes ); + } + + putText( view, msg, textOrigin, 1, 1, + mode != CALIBRATED ? Scalar(0,0,255) : Scalar(0,255,0)); + + if( blink ) + bitwise_not(view, view); + + if( mode == CALIBRATED && undistortImage ) + { + Mat temp = view.clone(); + undistort(temp, view, cameraMatrix, distCoeffs); + } + + imshow("Image View", view); + int key = 0xff & waitKey(capture.isOpened() ? 50 : 500); + + if( (key & 255) == 27 ) + break; + + if( key == 'u' && mode == CALIBRATED ) + undistortImage = !undistortImage; + + if( capture.isOpened() && key == 'g' ) + { + mode = CAPTURING; + imagePoints.clear(); + } + + if( mode == CAPTURING && imagePoints.size() >= (unsigned)nframes ) + { + if( runAndSave(outputFilename, imagePoints, imageSize, + boardSize, pattern, squareSize, aspectRatio, + flags, cameraMatrix, distCoeffs, + writeExtrinsics, writePoints)) + mode = CALIBRATED; + else + mode = DETECTION; + if( !capture.isOpened() ) + break; + } + } + + if( !capture.isOpened() && showUndistorted ) + { + Mat view, rview, map1, map2; + initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), + getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0), + imageSize, CV_16SC2, map1, map2); + + for( i = 0; i < (int)imageList.size(); i++ ) + { + view = imread(imageList[i], 1); + if(view.empty()) + continue; + //undistort( view, rview, cameraMatrix, distCoeffs, cameraMatrix ); + remap(view, rview, map1, map2, INTER_LINEAR); + imshow("Image View", rview); + int c = 0xff & waitKey(); + if( (c & 255) == 27 || c == 'q' || c == 'Q' ) + break; + } + } + + return 0; +} + diff --git a/thirdparty/aruco-3.1.12/utils/win_dirent.h b/thirdparty/aruco-3.1.12/utils/win_dirent.h new file mode 100644 index 0000000..38b1599 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils/win_dirent.h @@ -0,0 +1,1158 @@ + +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 2006-2012 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_ALL_DICTS 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const void *, const void *)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp = NULL; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (dirp != NULL) { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + n = wcslen(dirname); +# else + n = GetFullPathNameW (dirname, 0, NULL, NULL); +# endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt) { + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + wcsncpy_s(dirp->patt, n+1, dirname, n); +# else + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); +# endif + if (n > 0) { + wchar_t *p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) { + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first (dirp)) { + /* Directory stream opened successfully */ + error = 0; + } else { + /* Cannot retrieve first entry */ + error = 1; + dirent_set_errno (ENOENT); + } + + } else { + /* Cannot retrieve full path name */ + dirent_set_errno (ENOENT); + error = 1; + } + + } else { + /* Cannot allocate memory for search pattern */ + error = 1; + } + + } else { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + _wclosedir (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritted by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) { + free (dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occured */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (dirp) { + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (!error) { + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (dirp->wdirp) { + /* Directory stream opened */ + error = 0; + } else { + /* Failed to open directory stream */ + error = 1; + } + + } else { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + error = 1; + } + + } else { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + free (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on sucess. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an errornous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_ALL_DICTS; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const void*, const void*)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we got an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + if (tmp) { + free (tmp); + } + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resuting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/thirdparty/aruco-3.1.12/utils_calibration/CMakeLists.txt b/thirdparty/aruco-3.1.12/utils_calibration/CMakeLists.txt new file mode 100644 index 0000000..75b5203 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_calibration/CMakeLists.txt @@ -0,0 +1,21 @@ +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) +if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR ${CMAKE_CXX_COMPILER_ID} STREQUAL Clang) +SET(THREADLIB "pthread") +ENDIF() +add_executable(aruco_calibration aruco_calibration.cpp calibrator.h calibrator.cpp ) +add_executable(aruco_calibration_fromimages aruco_calibration_fromimages.cpp dirreader.h) + +target_link_libraries(aruco_calibration aruco opencv_calib3d opencv_highgui ${THREADLIB}) +target_link_libraries(aruco_calibration_fromimages aruco opencv_calib3d opencv_highgui ${THREADLIB}) + +install(TARGETS aruco_calibration aruco_calibration_fromimages RUNTIME DESTINATION bin) + +IF(WIN32) +install(FILES "${PROJECT_SOURCE_DIR}/utils_calibration/aruco_calibration_grid_board_a4.pdf" DESTINATION bin/) +#install(FILES "${PROJECT_SOURCE_DIR}/utils/myown.dict" DESTINATION bin/) + +elseif(UNIX) + install(FILES "aruco_calibration_grid_board_a4.pdf" DESTINATION share/${PROJECT_NAME}) + #install(FILES "${PROJECT_SOURCE_DIR}/utils/myown.dict" DESTINATION shared/${PROJECT_NAME}) +ENDIF() + diff --git a/thirdparty/aruco-3.1.12/utils_calibration/REAME_CALIBRATION b/thirdparty/aruco-3.1.12/utils_calibration/REAME_CALIBRATION new file mode 100644 index 0000000..0cf508f --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_calibration/REAME_CALIBRATION @@ -0,0 +1,32 @@ +Version 1.3.1 adds a new calibration method using aruco chessboards. +The functionality is implemented in the application utils/aruco_calibration + +How to: +open the pdf utils/aruco_calibration_board_a4.pdf and print it +use a measurer tape and determine the lenght of the squares (in meters if you want everything resulting in meters). + + + +LIVE CALIBRATION: + +To do live calibration with your camera: plug it, and type +aruco_calibration live aruco_calibration_board_a4.yml camera_result.yml -size + + +It everything is fine you'll see your camera images and when markers are detected, youll see them printed. + +Press 'a' to add the visible pattern to the pool of images used for calibration +Press 'c' to perform calibration +Use 's' to start or stop the video capture + + +CALIBRATION FROM VIDEO +aruco_calibration video.avi aruco_calibration_board_a4.yml camera_result.yml -size + + + + + + + + diff --git a/thirdparty/aruco-3.1.12/utils_calibration/aruco_calibration.cpp b/thirdparty/aruco-3.1.12/utils_calibration/aruco_calibration.cpp new file mode 100644 index 0000000..b945343 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_calibration/aruco_calibration.cpp @@ -0,0 +1,181 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "aruco.h" +#include "calibrator.h" +#include +#include +#include +#include +#include +#include + + +#if CV_MAJOR_VERSION >= 4 +#define CV_FOURCC(a,b,c,d) VideoWriter::fourcc(a,b,c,d) +#define CV_CAP_PROP_FOURCC cv::CAP_PROP_FOURCC +#define CV_CAP_PROP_AUTOFOCUS cv::CAP_PROP_AUTOFOCUS +#endif +using namespace std; +using namespace cv; +using namespace aruco; +#ifndef CV_CAP_PROP_AUTOFOCUS +#define CV_CAP_PROP_AUTOFOCUS -1 +#endif +float TheMarkerSize = -1; +VideoCapture TheVideoCapturer; +Mat TheInputImage, TheInputImageCopy; +CameraParameters TheCameraParameters; +MarkerDetector TheMarkerDetector; +vector > allMarkers; +string TheOutCameraParams; +aruco::CameraParameters camp; // camera parameters estimated +Calibrator calibrator; + +aruco::CameraParameters cameraCalibrate(std::vector > &allMarkers, int imageWidth,int imageHeight,float markerSize,float *currRepjErr=0, aruco::MarkerMap *inmmap=0); + +class CmdLineParser +{ int argc;char** argv;public: + CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} + bool operator[](string param) {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;return (idx != -1); } + string operator()(string param, string defvalue = "-1"){int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;if (idx == -1) return defvalue;else return (argv[idx + 1]);} +}; +/************************************ + * + * + ************************************/ +int main(int argc, char** argv) +{ + try + { + bool isLive=false; + CmdLineParser cml(argc, argv); + if (argc < 3 || cml["-h"]) + { + cerr << "Usage: (in.avi|live[:camera_index(e.g 0 or 1)])) out_camera_calibration.yml [-m markermapConfig.yml (configuration of the " + "board. If use default one (in utils), no need to set this)] [-size :(value in meters " + "of a marker. If you provide a board that contains that information, this is ommited) ] " + "[-save: if enabled, saves the calibration images used] " + << endl; + return -1; + } + // read from camera or from file + string TheInputVideo=string(argv[1]); + if ( TheInputVideo.find( "live")!=std::string::npos) + { + int vIdx = 0; + // check if the :idx is here + char cad[100]; + if (TheInputVideo.find(":") != string::npos) + { + std::replace(TheInputVideo.begin(), TheInputVideo.end(), ':', ' '); + sscanf(TheInputVideo.c_str(), "%s %d", cad, &vIdx); + } + cout << "Opening camera index " << vIdx << endl; + TheVideoCapturer.open(vIdx); + TheVideoCapturer.set(CV_CAP_PROP_AUTOFOCUS, 0); + + isLive=true; + } + else + TheVideoCapturer.open(argv[1]); + // check video is open + if (!TheVideoCapturer.isOpened()) + { + cerr << "Could not open video" << endl; + return -1; + } + TheVideoCapturer.set(CV_CAP_PROP_FOURCC ,CV_FOURCC('M', 'J', 'P', 'G') ); + TheVideoCapturer.set(CV_CAP_PROP_AUTOFOCUS,0); +// TheVideoCapturer.set(CV_CAP_PROP_FRAME_WIDTH,1280); +// TheVideoCapturer.set(CV_CAP_PROP_FRAME_HEIGHT,720); + bool saveImages=cml["-save"]; + // read first image to get the dimensions + TheVideoCapturer >> TheInputImage; + + //configure the calibrator + calibrator.setParams(TheInputImage.size(),stof(cml("-size","1")),cml("-m","")); + + + // set specific parameters for this configuration + TheMarkerDetector.setDictionary( "ARUCO_MIP_36h12"); + TheMarkerDetector.setDetectionMode(aruco::DM_NORMAL); + cv::namedWindow("in", cv::WINDOW_NORMAL); + cv::resizeWindow("in",800,600); + + char key = 0 ; + int waitKeyTime= isLive?10:-10; + // capture until press ESC or until the end of the video + do + { + TheVideoCapturer.retrieve(TheInputImage); // get image + // detect and print + vector detected_markers = TheMarkerDetector.detect(TheInputImage); + // print markers from the board + for (auto m: detected_markers) + m.draw(TheInputImage, Scalar(0, 0, 255), 1); + + // draw help + cv::putText(TheInputImage,"'a' add current image for calibration",cv::Point(10,20),FONT_HERSHEY_SIMPLEX, 0.5f,cv::Scalar(125,255,255),1); + cv::putText(TheInputImage,"'s' start/stop capture",cv::Point(10,40),FONT_HERSHEY_SIMPLEX, 0.5f,cv::Scalar(125,255,255),1); + cv::putText(TheInputImage,calibrator.getInfo(),cv::Point(10,60),FONT_HERSHEY_SIMPLEX, 0.5f,cv::Scalar(125,255,255),1); + cv::imshow("in", TheInputImage); + + key = cv::waitKey(waitKeyTime); // wait for key to be pressed + if (key == 'a'){ + calibrator.addView(detected_markers); + if (saveImages){ + string number=std::to_string(calibrator.getNumberOfViews()-1); + while(number.size()!=5) number="0"+number; + cv::imwrite("calib-"+number+".png",TheInputImage); + } + } + // set waitTime in start/stop mode + if (key == 's') waitKeyTime*=-1; + + } while (key != 27 && TheVideoCapturer.grab()); + + + + + + aruco::CameraParameters camp; + if ( calibrator.getCalibrationResults(camp)){ + camp.saveToFile(argv[2]); + cout<<"results saved to "< + + +#include +#include +#include +#include +#include +#include +#include "aruco.h" +#include "aruco_calibration_grid_board_a4.h" +#include "dirreader.h" +#include + +using namespace std; +using namespace cv; +using namespace aruco; + +Mat TheInputImage, TheInputImageCopy; +CameraParameters TheCameraParameters; +MarkerMap TheMarkerMapConfig; +MarkerDetector TheMarkerDetector; +string TheOutFile; +string TheMarkerMapConfigFile; +float TheMarkerSize = -1; +string TheOutCameraParams; +bool autoOrient=false; +// given the set of markers detected, the function determines the get the 2d-3d correspondes +void getMarker2d_3d(vector& p2d, vector& p3d, const vector& markers_detected, + const MarkerMap& bc) +{ + p2d.clear(); + p3d.clear(); + + // for each detected marker + for (size_t i = 0; i < markers_detected.size(); i++) + { + // find it in the bc + auto fidx = std::string::npos; + for (size_t j = 0; j < bc.size() && fidx == std::string::npos; j++) + if (bc[j].id == markers_detected[i].id) + fidx = j; + if (fidx != std::string::npos) + { + for (int j = 0; j < 4; j++) + { + p2d.push_back(markers_detected[i][j]); + p3d.push_back(bc[fidx][j]); + } + } + } + cout << "points added" << endl; +} +vector> calib_p2d; +vector> calib_p3d; +aruco::CameraParameters camp; // camera parameters estimated + +class CmdLineParser +{ int argc;char** argv;public: + CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} + bool operator[](string param) {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;return (idx != -1); } + string operator()(string param, string defvalue = "-1"){int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;if (idx == -1) return defvalue;else return (argv[idx + 1]);} +}; + + + + + +/************************************ + * + * + * + * + ************************************/ +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc,argv); + if (argc < 3 || cml["-h"]) + { + cerr << "Usage: out_camera_calibration.yml directory_with_images [options] " << endl; + cerr << "options:" << endl; + cerr << "-size maker_size : Size of the markers in meters. " << endl; + cerr << "-m markersetconfig.yml : By default, the one in utils is assumed. Otherwise, set the file here "< images=DirReader::read(argv[2],"",DirReader::Params(true)); + for(auto i:images)cout< detected_markers = TheMarkerDetector.detect(TheInputImage); + vector markers_from_set = TheMarkerMapConfig.getIndices(detected_markers); + TheInputImage.copyTo(TheInputImageCopy); + for (auto idx : markers_from_set) + detected_markers[idx].draw(TheInputImageCopy, Scalar(0, 0, 255), + static_cast( max(float(1.f), 1.5f * float(TheInputImageCopy.cols) / 1000.f))); + + if (TheInputImageCopy.cols > 1280) + cv::resize(TheInputImageCopy, TheInputImage, + cv::Size(1280, static_cast(1280.f * float(TheInputImageCopy.rows) / float(TheInputImageCopy.cols)))); + else + TheInputImageCopy.copyTo(TheInputImage); + + vector p2d; + vector p3d; + + getMarker2d_3d(p2d, p3d, detected_markers, TheMarkerMapConfig); + if (p3d.size() > 0) + { + calib_p2d.push_back(p2d); + calib_p3d.push_back(p3d); + } + + // show input with augmented information and the thresholded image + cv::imshow("in", TheInputImage); + // write to video if required + cv::waitKey(100); // wait for key to be pressed + + } ; + + cout << "Starting calibration" << endl; + vector vr, vt; + camp.CamSize = imageSize; + cout << calib_p2d.size() << endl; + cv::calibrateCamera(calib_p3d, calib_p2d, imageSize, camp.CameraMatrix, camp.Distorsion, vr, vt); + + //compute the average reprojection error + std::pair sum={0,0}; + for(size_t v=0;v repj; + cv::projectPoints(calib_p3d[v],vr[v],vt[v],camp.CameraMatrix, camp.Distorsion, repj); + for(size_t p=0;p vr;"<(0); + cout<<"rv.push_back( (cv::Mat_(3,1) << "< vt;"<(0); + cout<<"vt.push_back( (cv::Mat_(3,1) << "< +using namespace std; +using namespace aruco; +//portable calibration function +aruco::CameraParameters cameraCalibrate_(std::vector > &allMarkers, int imageWidth,int imageHeight,float markerSize,float *currRepjErr){ + + + + unsigned char default_a4_board[] = { + 0x30, 0x20, 0x32, 0x34, 0x20, 0x31, 0x36, 0x31, 0x20, 0x34, 0x20, 0x2d, + 0x31, 0x30, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x32, 0x37, 0x20, 0x34, 0x20, 0x2d, + 0x34, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x38, 0x35, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x36, 0x36, 0x20, 0x34, 0x20, + 0x38, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x33, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x32, 0x34, 0x34, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, 0x30, + 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, + 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, + 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, + 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x34, + 0x34, 0x20, 0x34, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x34, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x39, 0x30, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, + 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x34, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x32, 0x31, 0x34, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, + 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, + 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, + 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, + 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x35, 0x33, 0x20, 0x34, + 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x35, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x37, 0x20, 0x34, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x32, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x31, 0x34, 0x33, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x32, 0x31, 0x39, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x32, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x37, 0x38, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, 0x30, + 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, + 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x35, 0x39, 0x20, 0x34, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x30, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, + 0x20, 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x39, 0x20, 0x34, 0x20, + 0x32, 0x30, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, + 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x33, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, + 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, + 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x33, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x33, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x32, 0x34, 0x37, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, + 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, + 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, + 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, + 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x33, 0x37, + 0x20, 0x34, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x30, 0x30, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x32, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x36, + 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x38, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x37, 0x37, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x31, + 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, + 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x39, 0x33, 0x20, 0x34, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x34, + 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x38, 0x36, + 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x37, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x32, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, + 0x32, 0x39, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x41, 0x52, 0x55, 0x43, 0x4f, 0x5f, 0x4d, 0x49, 0x50, + 0x5f, 0x33, 0x36, 0x68, 0x31, 0x32 + }; + unsigned int default_a4_board_size = 1254; + + // given the set of markers detected, the function determines the get the 2d-3d correspondes + auto getMarker2d_3d_=[](vector& p2d, vector& p3d, const vector& markers_detected, + const MarkerMap& bc) + { + p2d.clear(); + p3d.clear(); + // for each detected marker + for (size_t i = 0; i < markers_detected.size(); i++) + { + // find it in the bc + auto fidx = std::string::npos; + for (size_t j = 0; j < bc.size() && fidx == std::string::npos; j++) + if (bc[j].id == markers_detected[i].id) + fidx = j; + if (fidx != std::string::npos) + { + for (int j = 0; j < 4; j++) + { + p2d.push_back(markers_detected[i][j]); + p3d.push_back(bc[fidx][j]); + } + } + } + }; + + aruco::MarkerMap mmap; + stringstream sstr; + sstr.write((char*)default_a4_board, default_a4_board_size); + mmap.fromStream(sstr); + if (!mmap.isExpressedInMeters()) + mmap = mmap.convertToMeters(static_cast( markerSize)); + + + + vector >calib_p2d; + vector > calib_p3d; + + for(auto &detected_markers:allMarkers){ + vector p2d; + vector p3d; + + getMarker2d_3d_(p2d, p3d, detected_markers, mmap); + if (p3d.size() > 0) + { + calib_p2d.push_back(p2d); + calib_p3d.push_back(p3d); + } + } + + vector vr, vt; + CameraParameters cameraParams; + cameraParams.CamSize = cv::Size(imageWidth,imageHeight); + float err=cv::calibrateCamera(calib_p3d, calib_p2d, cameraParams.CamSize, cameraParams.CameraMatrix, cameraParams.Distorsion, vr, vt); + if (currRepjErr!=0) *currRepjErr=err; + // cerr << "repj error=" << curRepjerr << endl; + return cameraParams; +} + diff --git a/thirdparty/aruco-3.1.12/utils_calibration/calibrator.cpp b/thirdparty/aruco-3.1.12/utils_calibration/calibrator.cpp new file mode 100644 index 0000000..7f9d3d7 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_calibration/calibrator.cpp @@ -0,0 +1,362 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "calibrator.h" +#include +using namespace std; +namespace aruco{ +//portable calibration function + + + +Calibrator::Calibrator(){ + strinfo="Not calibrated. Need at least 4 images"; + _mmap=getDefaultCalibrationBoard(); + +} +Calibrator::~Calibrator(){ + stopThread(); +} + +void Calibrator::setParams(cv::Size imageSize,float markerSize,std::string markerMap ){ + try{ + stopThread(); + _imageSize=imageSize; + if (!markerMap.empty()) _mmap.readFromFile(markerMap); + + _msize=markerSize; + startThread(); + }catch(std::exception &ex){throw ex;} +} +//add a detection of the calibration markerset and do calibration +void Calibrator::addView(const std::vector &markers){ + + if ( _mmap.getIndices(markers).size()>=3){//at least three markers of the markermap + std::unique_lock lock(markerstmpMutex); + std::unique_lockvmarkerslock(markersMutex); + vmarkerstmp.push_back(markers); + if (vmarkers.size()<4){ + int nneeded=4-(vmarkerstmp.size()+vmarkers.size()); + setInfoString("Not calibrated. Need at least "+to_string(nneeded)+" more images"); + } + } +} + +//returns a string with info about the status +std::string Calibrator::getInfo(){ + + std::unique_lock lock(infoMutex); + std::string str=strinfo; + return str; +} +void Calibrator::setInfoString(const std::string &str){ + std::unique_lock lock(infoMutex); + strinfo=str; +} +//obtain the camera calibration results. It is blocking function +bool Calibrator::getCalibrationResults( aruco::CameraParameters &camp){ + bool isOk=true; + + aruco::CameraParameters ret; + calibrationMutex.lock(); + if ( vmarkers.size()<3) isOk=false; + camp=cameraParams; + calibrationMutex.unlock(); + + return isOk; +} + +int Calibrator::getNumberOfViews(){ + std::unique_lock lock(markerstmpMutex); + std::unique_lockvmarkerslock(markersMutex); + int sum= (vmarkerstmp.size()+vmarkers.size()); + return sum; +} +void Calibrator::calibration_function(){ + + while(keepCalibrating){ + calibrationMutex.lock(); + + int nnewMarkers=0,nTotalViews=0; + markerstmpMutex.lock(); + nnewMarkers=vmarkerstmp.size(); + if (vmarkerstmp.size()!=0){ + unique_lock vmarkerslock(markersMutex); + vmarkers.insert(vmarkers.end(),vmarkerstmp.begin(),vmarkerstmp.end()); + vmarkerstmp.clear(); + nTotalViews=vmarkers.size(); + } + markerstmpMutex.unlock(); + + if ( nnewMarkers!=0 && nTotalViews>=4){ + setInfoString("calibrating..."); + calibError=cameraCalibrate(vmarkers,_imageSize.width,_imageSize.height,_msize,_mmap,cameraParams); + setInfoString("calibration error:"+std::to_string(calibError)+" using "+std::to_string(vmarkers.size())+" images"); + } + calibrationMutex.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +void Calibrator::stopThread(){ + + if (_calibThread.joinable()){ + keepCalibrating=false; + _calibThread.join(); + } +} + +void Calibrator::startThread(){ + if (_calibThread.joinable()) stopThread(); + keepCalibrating=true; + _calibThread=std::thread([this]{this->calibration_function();}); + +} + + +inline cv::Point2f project_cv_(const cv::Point3f &p3d,const cv::Mat &cameraMatrix,const cv::Mat &RT,const cv::Mat & Distortion=cv::Mat()){ + assert(cameraMatrix.type()==CV_32F); + assert(RT.type()==CV_32F); + assert( (Distortion.type()==CV_32F && Distortion.total()>=5 )|| Distortion.total()==0); + + const float *cm=cameraMatrix.ptr(0); + const float *rt=RT.ptr(0); + const float *k=0; + if ( Distortion.total()!=0) k=Distortion.ptr(0); + + //project point first + float x= p3d.x* rt[0]+p3d.y* rt[1]+p3d.z* rt[2]+rt[3]; + float y= p3d.x* rt[4]+p3d.y* rt[5]+p3d.z* rt[6]+rt[7]; + float z= p3d.x* rt[8]+p3d.y* rt[9]+p3d.z* rt[10]+rt[11]; + + float xx=x/z; + float yy=y/z; + + if (k!=0){//apply distortion // k1,k2,p1,p2[,k3 + float r2=xx*xx+yy*yy; + float r4=r2*r2; + float comm=1+k[0]*r2+k[1]*r4+k[4]*(r4*r2); + float xxx = xx * comm + 2*k[2] *xx*yy+ k[3]*(r2+2*xx*xx); + float yyy= yy*comm+ k[2]*(r2+2*yy*yy)+2*k[3]*xx*yy; + xx=xxx; + yy=yyy; + } + return cv::Point2f((xx*cm[0])+cm[2],(yy*cm[4])+cm[5] ); +} + + + +float Calibrator::cameraCalibrate(std::vector > &allMarkers, int imageWidth,int imageHeight,float markerSize,aruco::MarkerMap &mmap, aruco::CameraParameters &iocam){ + + // given the set of markers detected, the function determines the get the 2d-3d correspondes + auto getMarker2d_3d_=[](std::vector& p2d, vector& p3d, const vector& markers_detected, + const MarkerMap& bc) + { + p2d.clear(); + p3d.clear(); + // for each detected marker + for (size_t i = 0; i < markers_detected.size(); i++) + { + // find it in the bc + auto fidx = std::string::npos; + for (size_t j = 0; j < bc.size() && fidx == std::string::npos; j++) + if (bc[j].id == markers_detected[i].id) + fidx = j; + if (fidx != std::string::npos) + { + for (int j = 0; j < 4; j++) + { + p2d.push_back(markers_detected[i][j]); + p3d.push_back(bc[fidx][j]); + } + } + } + }; + + int calibflags=0; + + if (!iocam.isValid()){ + //first time + iocam.CamSize = cv::Size(imageWidth,imageHeight); + } + else calibflags= cv::CALIB_USE_INTRINSIC_GUESS; + + + + if (!mmap.isExpressedInMeters()) + mmap = mmap.convertToMeters(static_cast( markerSize)); + + + + vector >calib_p2d; + vector > calib_p3d; + + for(auto &detected_markers:allMarkers){ + vector p2d; + vector p3d; + + getMarker2d_3d_(p2d, p3d, detected_markers, mmap); + if (p3d.size() > 0) + { + calib_p2d.push_back(p2d); + calib_p3d.push_back(p3d); + } + } + + vector vr, vt; + float err=cv::calibrateCamera(calib_p3d, calib_p2d, iocam.CamSize, iocam.CameraMatrix, iocam.Distorsion, vr, vt,calibflags); + + iocam.CameraMatrix.convertTo(iocam.CameraMatrix,CV_32F); + iocam.Distorsion.convertTo(iocam.Distorsion,CV_32F); + +return err; + + // cerr << "repj error=" << curRepjerr << endl; + } + +aruco::MarkerMap Calibrator::getDefaultCalibrationBoard(){ + + + + unsigned char default_a4_board[] = { + 0x30, 0x20, 0x32, 0x34, 0x20, 0x31, 0x36, 0x31, 0x20, 0x34, 0x20, 0x2d, + 0x31, 0x30, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x32, 0x37, 0x20, 0x34, 0x20, 0x2d, + 0x34, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x38, 0x35, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x31, + 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x36, 0x36, 0x20, 0x34, 0x20, + 0x38, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x33, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x2d, 0x31, 0x35, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x32, 0x34, 0x34, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, 0x30, + 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, + 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, + 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, + 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x34, + 0x34, 0x20, 0x34, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x34, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x39, 0x30, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, + 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x34, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x39, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x32, 0x31, 0x34, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, + 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, + 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, + 0x20, 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, + 0x2d, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x35, 0x33, 0x20, 0x34, + 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x35, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x37, 0x20, 0x34, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x32, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x31, 0x34, 0x33, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x32, 0x31, 0x39, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x32, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x2d, 0x33, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x37, 0x38, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, 0x30, + 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, + 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x35, 0x39, 0x20, 0x34, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x30, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, + 0x20, 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x39, 0x20, 0x34, 0x20, + 0x32, 0x30, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, + 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, + 0x33, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x33, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, + 0x20, 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, + 0x38, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x33, + 0x30, 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x33, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x32, 0x34, 0x37, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, + 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, + 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, + 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, + 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, 0x33, 0x37, + 0x20, 0x34, 0x20, 0x2d, 0x34, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x30, 0x30, 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x32, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x36, + 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x38, 0x30, 0x30, 0x20, 0x39, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x37, 0x37, 0x20, 0x34, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x35, 0x30, 0x30, 0x20, 0x31, + 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20, + 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x39, 0x33, 0x20, 0x34, 0x20, + 0x2d, 0x34, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x31, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, 0x30, 0x20, 0x31, + 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x34, + 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x38, 0x36, + 0x20, 0x34, 0x20, 0x32, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x37, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20, 0x30, + 0x20, 0x37, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, + 0x32, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, 0x20, 0x30, 0x20, 0x32, + 0x32, 0x39, 0x20, 0x34, 0x20, 0x38, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x32, 0x30, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x31, 0x33, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, + 0x30, 0x20, 0x30, 0x20, 0x38, 0x30, 0x30, 0x20, 0x31, 0x35, 0x30, 0x30, + 0x20, 0x30, 0x20, 0x41, 0x52, 0x55, 0x43, 0x4f, 0x5f, 0x4d, 0x49, 0x50, + 0x5f, 0x33, 0x36, 0x68, 0x31, 0x32 + }; + unsigned int default_a4_board_size = 1254; + + + aruco::MarkerMap mmap; + std::stringstream sstr; + sstr.write((char*)default_a4_board, default_a4_board_size); + mmap.fromStream(sstr); + return mmap; + } +} diff --git a/thirdparty/aruco-3.1.12/utils_calibration/calibrator.h b/thirdparty/aruco-3.1.12/utils_calibration/calibrator.h new file mode 100644 index 0000000..3f84b13 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_calibration/calibrator.h @@ -0,0 +1,84 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#ifndef ArucoCalibrator_H +#define ArucoCalibrator_H +#include +#include +#include "marker.h" +#include "markermap.h" +#include "cameraparameters.h" +#include +#include +namespace aruco { +/** + * @brief The Calibrator class implements a calibration thread + */ +class Calibrator{ + + std::vector > vmarkers,vmarkerstmp; + float _msize; + aruco::MarkerMap _mmap; + cv::Size _imageSize; + std::thread _calibThread; + std::mutex markersMutex,markerstmpMutex,infoMutex,calibrationMutex; + std::string strinfo; + bool keepCalibrating; + aruco::CameraParameters cameraParams; + float calibError=-1; +public: + + Calibrator(); + ~Calibrator(); + void setParams(cv::Size imageSize, float markerSize=1, std::string markerMap=""); + //add a detection of the calibration markerset and do calibration + //returns the current number of views + void addView(const std::vector &markers); + + //returns a string with info about the status + std::string getInfo(); + + //obtain the camera calibration results. It is blocking function + //returns true if calibration was ok + bool getCalibrationResults( aruco::CameraParameters &camp); + int getNumberOfViews(); + + float getReprjError()const{return calibError;} +private: + void calibration_function(); + void setInfoString(const std::string &str); + void stopThread(); + void startThread(); + + float cameraCalibrate(std::vector > &allMarkers, int imageWidth,int imageHeight,float markerSize, aruco::MarkerMap &inmmap, aruco::CameraParameters&io); + aruco::MarkerMap getDefaultCalibrationBoard(); + +}; +} + +#endif diff --git a/thirdparty/aruco-3.1.12/utils_calibration/dirreader.h b/thirdparty/aruco-3.1.12/utils_calibration/dirreader.h new file mode 100644 index 0000000..ee033e2 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_calibration/dirreader.h @@ -0,0 +1,1232 @@ +#ifndef _DIR_READER_H +#define _DIR_READER_H +#include +#include + + +#ifdef WIN32 + +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 2006-2012 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_ALL_DICTS 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const void *, const void *)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp = NULL; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (dirp != NULL) { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + n = wcslen(dirname); +# else + n = GetFullPathNameW (dirname, 0, NULL, NULL); +# endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt) { + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + wcsncpy_s(dirp->patt, n+1, dirname, n); +# else + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); +# endif + if (n > 0) { + wchar_t *p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) { + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first (dirp)) { + /* Directory stream opened successfully */ + error = 0; + } else { + /* Cannot retrieve first entry */ + error = 1; + dirent_set_errno (ENOENT); + } + + } else { + /* Cannot retrieve full path name */ + dirent_set_errno (ENOENT); + error = 1; + } + + } else { + /* Cannot allocate memory for search pattern */ + error = 1; + } + + } else { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + _wclosedir (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritted by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) { + free (dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occured */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (dirp) { + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (!error) { + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (dirp->wdirp) { + /* Directory stream opened */ + error = 0; + } else { + /* Failed to open directory stream */ + error = 1; + } + + } else { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + error = 1; + } + + } else { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + free (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on sucess. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an errornous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_ALL_DICTS; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const void*, const void*)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we got an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + if (tmp) { + free (tmp); + } + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resuting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ +#else +#include +#endif +#include // std::sort + + + +class DirReader{ +public: + + struct Params{ + Params(){} + Params(bool SetFullPath){setFullPath=SetFullPath;} + bool setFullPath=false; + bool removeParentAndThis=true; + }; + +static std::vector read(std::string path,std::string ext="",const Params ¶ms=Params()){ + DIR *dir; + struct dirent *ent; + std::vector res; + if ((dir = opendir (path.c_str())) != NULL) { + /* print all the files and directories within directory */ + while ((ent = readdir (dir)) != NULL){ + std::string fname(ent->d_name); + bool addit=true; + if ( params.removeParentAndThis){ + if ( fname=="." || fname=="..") + addit=false; + } + if (addit && !ext.empty()){//check extension + std::string thisExt=std::string(fname.begin()+(fname.size()-ext.size()),fname.end()); + if (thisExt!=ext) + addit=false; + + } + + if(addit){ + if(params.setFullPath) fname=path+getSeparator()+fname; + res.push_back(fname); + } + } + closedir (dir); + } + std::sort(res.begin(),res.end()); + //check + return res; +} + +static char getSeparator(){ +#ifdef WIN32 + return '\\'; +#else + return '/'; +#endif +} + +static std::string basename( std::string const& pathname ) +{ + + return std::string( + std::find_if( pathname.rbegin(), pathname.rend(),[](char c){return c==getSeparator();} ).base(), + pathname.end() ); +} + +}; +#endif diff --git a/thirdparty/aruco-3.1.12/utils_dcf/CMakeLists.txt b/thirdparty/aruco-3.1.12/utils_dcf/CMakeLists.txt new file mode 100644 index 0000000..22a2260 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_dcf/CMakeLists.txt @@ -0,0 +1,12 @@ +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) +if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR ${CMAKE_CXX_COMPILER_ID} STREQUAL Clang) +SET(THREADLIB "pthread") +ENDIF() + +add_executable(aruco_dcf aruco_dcf.cpp) +add_executable(aruco_dcf_mm aruco_dcf_markermap.cpp) + +target_link_libraries(aruco_dcf aruco opencv_calib3d opencv_highgui ${THREADLIB}) +target_link_libraries(aruco_dcf_mm aruco opencv_calib3d opencv_highgui ${THREADLIB}) + +INSTALL(TARGETS aruco_dcf aruco_dcf_mm RUNTIME DESTINATION bin) diff --git a/thirdparty/aruco-3.1.12/utils_dcf/aruco_dcf.cpp b/thirdparty/aruco-3.1.12/utils_dcf/aruco_dcf.cpp new file mode 100644 index 0000000..d682337 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_dcf/aruco_dcf.cpp @@ -0,0 +1,104 @@ +#include "aruco_cvversioning.h" +#include +#include +#include +#include +#include +#include "aruco.h" +#include + +#include "dcf/dcfmarkertracker.h" + +using namespace aruco; +using namespace std; + +class CmdLineParser{int argc;char** argv;public:CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} bool operator[](string param) {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;return (idx != -1);} string operator()(string param, string defvalue = "-1") {int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;if (idx == -1)return defvalue;else return (argv[idx + 1]);}}; +struct TimerAvrg{std::vector times;size_t curr=0,n; std::chrono::high_resolution_clock::time_point begin,end; TimerAvrg(int _n=30){n=_n;times.reserve(n); }inline void start(){begin= std::chrono::high_resolution_clock::now(); }inline void stop(){end= std::chrono::high_resolution_clock::now();double duration=double(std::chrono::duration_cast(end-begin).count())*1e-6;if ( times.size()=times.size()) curr=0;}}double getAvrg(){double sum=0;for(auto t:times) sum+=t;return sum/double(times.size());}}; + +TimerAvrg timer; +DFCMarkerTracker TheTracker; + +int main(int argc,char **argv){ + try { + CmdLineParser cml(argc, argv); + if(argc<2 || cml["-h"]) + { + cerr << "Usage: (invideo|cameraindex) [-c cameraParams.yml] [-s markerSize] [-start frame] [-d :ALL_DICTS default] [-f arucoConfig.yml]" << std::endl; + cerr << "\tDictionaries: "; + for (auto dict : aruco::Dictionary::getDicTypes()) + cerr << dict << " "; + cerr << endl; + cerr << "\t Instead of these, you can directly indicate the path to a file with your own generated " + "dictionary" + << endl; + cout << "Example to work with apriltags dictionary : video.avi -d TAG36h11" << endl << endl; + return 0; + } + + aruco::CameraParameters CamParam; + // read camera parameters if passed + if (cml["-c"]) + CamParam.readFromXMLFile(cml("-c")); + + float MarkerSize = std::stof(cml("-s", "-1")); + + cv::VideoCapture video;; + //open video/camera + try { + int camIndex=std::stoi(argv[1]); + video.open(camIndex); + } catch (const std::exception &e) { + video.open(argv[1]); + } + if(!video.isOpened()) throw std::runtime_error("Could not open video"); + + cv::Mat image; + + int frameid =std::stoi(cml("-start", "0")); + + video.set(CV_CAP_PROP_POS_FRAMES,frameid); + while(image.empty()) video>>image; + + + if(cml["-f"])//uses a configuration file. YOu can create it from aruco_test application + TheTracker.loadParamsFromFile(cml("-f")); + else + TheTracker.setDictionary(cml("-d","ALL_DICTS"), 0.f); + + if (CamParam.isValid() && MarkerSize != -1) + { + CamParam.resize(image.size()); + TheTracker.setParams(CamParam, MarkerSize); + } + + int maxNFrames=video.get(CV_CAP_PROP_FRAME_COUNT)-1; + + char key=0; + int waitTime=10; + do{ + video.retrieve(image); + + timer.start(); + std::map> setTrackers = TheTracker.track(image); + TheTracker.estimatePose(); + timer.stop(); + + for(auto m:setTrackers) + std::cout << m.second->getMarker() << " W="<getTrustVal() << std::endl; + + std::cout <<"|@ Frame:"< +#include +#include +#include +#include +#include +#include +#include +#include "sglviewer.h" +#include "pcdwriter.cpp" + +#include "dcf/dcfmarkermaptracker.h" + +using namespace cv; +using namespace aruco; +using namespace std; + +string TheMarkerMapConfigFile; +float TheMarkerSize = -1; +VideoCapture TheVideoCapturer; +Mat TheInputImage; +CameraParameters TheCameraParameters; +MarkerMap TheMarkerMapConfig; + +DFCMarkerMapTracker TheTracker; + +struct TimerAvrg{std::vector times;size_t curr=0,n; std::chrono::high_resolution_clock::time_point begin,end; TimerAvrg(int _n=30){n=_n;times.reserve(n); }inline void start(){begin= std::chrono::high_resolution_clock::now(); }inline void stop(){end= std::chrono::high_resolution_clock::now();double duration=double(std::chrono::duration_cast(end-begin).count())*1e-6;if ( times.size()=times.size()) curr=0;}}double getAvrg(){double sum=0;for(auto t:times) sum+=t;return sum/double(times.size());}}; +TimerAvrg timer; + +int waitTime = 0; +std::map frame_pose_map; // set of poses and the frames they were detected +sgl_OpenCV_Viewer Viewer; +class CmdLineParser +{ + int argc;char** argv;public: + CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} + //is the param? + bool operator[](string param) + {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;return (idx != -1); } + //return the value of a param using a default value if it is not present + string operator()(string param, string defvalue = "-1"){int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;if (idx == -1) return defvalue;else return (argv[idx + 1]);} +}; + +void savePCDFile(string fpath, const aruco::MarkerMap& ms, + const std::map frame_pose_map); +void savePosesToFile(string filename, const std::map& fmp); + + + +/************************************ + * + * + * + * + ************************************/ +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc < 4 || cml["-h"]) + { + cerr << "Invalid number of arguments" << endl; + cerr << "Usage: (in.avi|live[:camera_index(e.g 0 or 1)])) markersetconfig.yml camera_intrinsics.yml [optional_arguments] " + "\n\t[-s marker_size] \n\t[-pcd out_pcd_file_with_camera_poses] \n\t[-poses out_file_with_poses] " + "\n\t[-f arucoConfig.yml: Loads the detector configuration from file ] " + << endl; + return 0; + } + + TheMarkerMapConfig.readFromFile(argv[2]); + + TheMarkerMapConfigFile = argv[2]; + TheMarkerSize = stof(cml("-s", "1")); + // read from camera or from file + string TheInputVideo=string(argv[1]); + if ( TheInputVideo.find( "live")!=std::string::npos) + { + int vIdx = 0; + // check if the :idx is here + char cad[100]; + if (TheInputVideo.find(":") != string::npos) + { + std::replace(TheInputVideo.begin(), TheInputVideo.end(), ':', ' '); + sscanf(TheInputVideo.c_str(), "%s %d", cad, &vIdx); + } + cout << "Opening camera index " << vIdx << endl; + TheVideoCapturer.open(vIdx); + waitTime = 10; + } + else + TheVideoCapturer.open(argv[1]); // check video is open + if (!TheVideoCapturer.isOpened()) + throw std::runtime_error("Could not open video"); + + // read first image to get the dimensions + TheVideoCapturer >> TheInputImage; + + // read camera parameters if passed + TheCameraParameters.readFromXMLFile(argv[3]); + TheCameraParameters.resize(TheInputImage.size()); + + // dictionary + TheTracker.setDictionary( TheMarkerMapConfig.getDictionary()); + + // set config parameters + if (cml["-f"]) + TheTracker.loadParamsFromFile(cml("-f")); + + // prepare the pose tracker if possible + // if the camera parameers are avaiable, and the markerset can be expressed in meters, then go + if (TheMarkerMapConfig.isExpressedInPixels() && TheMarkerSize > 0) + TheMarkerMapConfig = TheMarkerMapConfig.convertToMeters(TheMarkerSize); + + if (TheCameraParameters.isValid() && TheMarkerMapConfig.isExpressedInMeters()){ + TheTracker.setParams(TheCameraParameters, TheMarkerMapConfig); + TheMarkerSize=cv::norm(TheMarkerMapConfig[0][0]- TheMarkerMapConfig[0][1]); + } + + // Create gui + Viewer.setParams(1.5,1280,960,"map_viewer",TheMarkerSize); + + char key = 0; + // capture until press ESC or until the end of the video + //cout << "Press 's' to start/stop video" << endl; + + int frameid =std::stoi(cml("-fstart", "0")); + int frameStop =std::stoi(cml("-fstop", "-1")); + + TheVideoCapturer.set(CV_CAP_PROP_POS_FRAMES,frameid); + + int maxNFrames=TheVideoCapturer.get(CV_CAP_PROP_FRAME_COUNT)-1; + + + TheVideoCapturer.set(CV_CAP_PROP_POS_FRAMES,frameid); + do + { + if (frameStop == frameid) break; + + TheVideoCapturer.retrieve(TheInputImage); + + timer.start(); + // Detection and tracking of the markers + TheTracker.track(TheInputImage); + bool pose = TheTracker.estimatePose(); + timer.stop(); + + std::cout <<"|@ Frame:"<(i,j); + + //now, move to a angle axis + Eigen::Quaternionf q(e_r33); + qx=q.x(); + qy=q.y(); + qz=q.z(); + qw=q.w(); + + tx=M.at(0,3); + ty=M.at(1,3); + tz=M.at(2,3); +} + +void savePosesToFile(string filename, const std::map& fmp) +{ + std::ofstream file(filename); + double qx, qy, qz, qw, tx, ty, tz; + for (auto frame : fmp) + { + if (!frame.second.empty()) + { + cv::Mat minv=frame.second.inv(); + getQuaternionAndTranslationfromMatrix44(minv, qx, qy, qz, qw, tx, ty, tz); + file << frame.first << " " << tx << " " << ty << " " << tz << " " << qx << " " << qy << " " << qz << " " + << qw << endl; + } + } +} diff --git a/thirdparty/aruco-3.1.12/utils_dcf/pcdwriter.cpp b/thirdparty/aruco-3.1.12/utils_dcf/pcdwriter.cpp new file mode 100644 index 0000000..2f956e6 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_dcf/pcdwriter.cpp @@ -0,0 +1,424 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + +#include "markermap.h" +#include +#include +#include +#include +using namespace std; + +void getRTfromMatrix44(const cv::Mat& M, cv::Mat& R, cv::Mat& T) +{ + assert(M.cols == M.rows && M.cols == 4); + assert(M.type() == CV_32F || M.type() == CV_64F); + // extract the rotation part + cv::Mat r33 = cv::Mat(M, cv::Rect(0, 0, 3, 3)); + cv::SVD svd(r33); + cv::Mat Rpure = svd.u * svd.vt; + cv::Rodrigues(Rpure, R); + T.create(1, 3, M.type()); + if (M.type() == CV_32F) + for (int i = 0; i < 3; i++) + T.ptr(0)[i] = M.at(i, 3); + else + for (int i = 0; i < 3; i++) + T.ptr(0)[i] = M.at(i, 3); +} + +/********************** + * + * + **********************/ +struct Quaternion +{ + Quaternion(float q0, float q1, float q2, float q3) + { + q[0] = q0; + q[1] = q1; + q[2] = q2; + q[3] = q3; + } + cv::Mat getRotation() const + { + cv::Mat R(3, 3, CV_32F); + R.at(0, 0) = q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]; + R.at(0, 1) = 2.f * (q[1] * q[2] - q[0] * q[3]); + R.at(0, 2) = 2.f * (q[1] * q[3] + q[0] * q[2]); + + R.at(1, 0) = 2.f * (q[1] * q[2] + q[0] * q[3]); + R.at(1, 1) = q[0] * q[0] + q[2] * q[2] - q[1] * q[1] - q[3] * q[3]; + R.at(1, 2) = 2.f * (q[2] * q[3] - q[0] * q[1]); + + R.at(2, 0) = 2.f * (q[1] * q[3] - q[0] * q[2]); + R.at(2, 1) = 2.f * (q[2] * q[3] + q[0] * q[1]); + R.at(2, 2) = q[0] * q[0] + q[3] * q[3] - q[1] * q[1] - q[2] * q[2]; + return R; + } + float q[4]; +}; + +/********************** + * + * + **********************/ +float rigidBodyTransformation_Horn1987(cv::Mat& S, cv::Mat& M, cv::Mat& RT_4x4) +{ + assert(S.total() == M.total()); + assert(S.type() == M.type()); + assert(S.rows > S.cols && M.rows > M.cols); + + cv::Mat _s, _m; + S.convertTo(_s, CV_32F); + M.convertTo(_m, CV_32F); + _s = _s.reshape(1); + _m = _m.reshape(1); + cv::Mat Mu_s = cv::Mat::zeros(1, 3, CV_32F); + cv::Mat Mu_m = cv::Mat::zeros(1, 3, CV_32F); + // cout<<_s<(0)[i] /= float(_s.rows); + Mu_m.ptr(0)[i] /= float(_m.rows); + } + + // cout<<"Mu_s="<(0, 0), eigenvectors.at(0, 1), eigenvectors.at(0, 2), + eigenvectors.at(0, 3)); + cv::Mat RR = rot.getRotation(); + // cout<<"RESULT="< +#include +#include +#include +#include +#include +//Simplest graphics library +//Version 1.0.1 +//Author. Rafael Muñoz Salinas (rmsalinas@uco.es) 2017 + +//Extremmely simple yet efficient device independent graphic library for points and lines +namespace sgl{ +struct Point3{ + inline Point3( ){ } + inline Point3(float X,float Y,float Z){x=X;y=Y;z=Z;} + inline float norm()const{return sqrt( (x*x)+(y*y)+(z*z));} + inline Point3 operator-(const Point3 &p)const{return Point3( x-p.x, y-p.y,z-p.z);} + inline Point3 operator+(const Point3 &p)const{return Point3( x+p.x, y+p.y,z+p.z);} + inline Point3 operator*(float v)const{return Point3( x*v,y*v,z*v);} + float x,y,z; + friend std::ostream & operator<<(std::ostream &str, const Point3 &p){ + str<<"["< modelMatrices; + Matrix44 _cameraMatrix; + Matrix44 TM;//transform from global coordinates to camera ones + Color *colorbuffer=0; + bool mustFreeBuffer=true; + + float _focal; + int _width,_height; + +public: + Scene(){ + modelMatrices.resize(1); + modelMatrices[0]= Matrix44();//set identity + TM=_viewMatrix; + } + + ~Scene(){if (mustFreeBuffer&& colorbuffer) delete colorbuffer;} + + //sets external buffer + + inline void setCameraParams(float focal,int width,int height,void *external_buffer=0){ + _focal=focal; + _width=width; + _height=height; + _cameraMatrix(0,0)=_focal*float(_width); + _cameraMatrix(1,1)=_focal*float(_height); + _cameraMatrix(0,2)=float(_width)/2.; + _cameraMatrix(1,2)=float(_height)/2.; + + + if (colorbuffer!=0 && mustFreeBuffer ) { + delete colorbuffer; + colorbuffer=0; + } + + if(external_buffer!=0){ + colorbuffer=(Color*)external_buffer; + mustFreeBuffer=false; + } + else{ + colorbuffer=new Color[_width*_height*3]; + mustFreeBuffer=false; + } + } + //this will erase the transform matrix + inline void setViewMatrix(const Matrix44&vp){_viewMatrix=vp; TM=_viewMatrix*modelMatrices.back();} + + inline void setModelMatrix(const Matrix44 &M=Matrix44()){ + modelMatrices.resize(1); + modelMatrices[0]=M; + TM=_viewMatrix*modelMatrices.back(); + } + inline void pushModelMatrix(const Matrix44 &M=Matrix44()){ + modelMatrices.push_back(modelMatrices.back()*M); + TM=_viewMatrix*modelMatrices.back(); + } + inline void popModelMatrix(){ + if (modelMatrices.size()>1){ + modelMatrices.pop_back(); + TM=_viewMatrix*modelMatrices.back(); + } + } + + + + inline void clear(const Color &backgroundColor){ + + const int size=_width*_height; + for(int i=0;i &vp,const Color &c,int size=1){ + (void)size; + for(size_t i=0;i0){ + pres=_cameraMatrix*pres; + float invz=1./pres.z; + pres.x*=invz;pres.y*=invz; + if ( pres.x>= 0 && pres.x<_width && pres.y>=0 && pres.y<_height) + colorbuffer[int(pres.y)*_width+int(pres.x)]=c; + } + } + + } + + inline void drawPoints(const std::vector &vp,const std::vector &vc,int size=1){ + (void)size; + auto M=_cameraMatrix*TM; + for(size_t i=0;i0){ + pres=_cameraMatrix*pres; + float invz=1./pres.z; + pres.x*=invz;pres.y*=invz; + if ( pres.x>= 0 && pres.x<_width && pres.y>=0 && pres.y<_height) + colorbuffer[int(pres.y)*_width+int(pres.x)]=vc[i]; + } + } + } + inline void drawPoint(const Point3 *p,const Color &c,int size=1){ + Point2 pres; + Point3 p3d=TM*(*p); + if ( p3d.z<0 ) return; + if ( project(p3d,pres)){ + switch (size) { + case 1: + if ( pres.x>=0 && pres.x<_width && pres.y>=0 && pres.y<_height){ + colorbuffer[int(pres.y)*_width+int(pres.x)]=c; + } + break; + case 2: + if ( pres.x> 0 && pres.x<_width-1 && pres.y>0 && pres.y<_height-1){ + int stx=(int(pres.x)-1); + int sty=(int(pres.y)-1)*_width; + colorbuffer[sty+stx]=c;colorbuffer[sty+stx+1]=c;colorbuffer[sty+stx+2]=c; + sty=(int(pres.y))*_width; + colorbuffer[sty+stx]=c;colorbuffer[sty+stx+1]=c;colorbuffer[sty+stx+2]=c; + sty=(int(pres.y+1))*_width; + colorbuffer[sty+stx]=c;colorbuffer[sty+stx+1]=c;colorbuffer[sty+stx+2]=c; + }break; + + } + } + (void)size; + } + + inline void drawLine(const Point3 &p1,const Point3 &p2,const Color &color,int size=1){drawLine(&p1,&p2,color,size);} + inline void drawLine(const Point3 *p1,const Point3 *p2,const Color &color,int size=1){ + Point3 p1t=TM*(*p1); if ( p1t.z<0 ) return; + Point3 p2t=TM*(*p2); if(p2t.z<0) return;//check that both are in front of camera(otherwise, do not draw) + Point2 pr1,pr2; + if(! project(p1t,pr1))return; + if(! project(p2t,pr2))return; + drawline(pr1,pr2,color,size);//project line bweten projected points + } + + //returns the internal frame buffer + inline unsigned char* getBuffer()const{return (unsigned char*)colorbuffer;} + + inline int getWidth()const{return _width;} + inline int getHeight()const{return _height;} +private: + + + inline bool project(const Point3 &p,Point2 &res){ + Point3 pres=_cameraMatrix*(p); + if(pres.z==0) return false; + float invz=1./pres.z; + res.x=invz*pres.x+0.5; res.y=invz*pres.y+0.5; + return true; + } + + //Bresenham's algorithm + + inline bool inLimits(int x,int y){ + if (x<0)return false; + if (y<0)return false; + if(x>=_width)return false; + if(y>=_height)return false; + return true; + } + + void drawline(Point2 start, Point2 end, const Color& color ,int size=1) + { + + + (void)size; + int x0=start.x,y0=start.y,x1=end.x,y1=end.y; + + if (!inLimits(x0,y0) && !inLimits(x1,y1) ) return; + int dx = abs(x1 - x0), dy = abs(y1 - y0); + int sx = (x0 < x1) ? 1 : -1, sy = (y0 < y1) ? 1 : -1; + int err = dx - dy; + while (true) + { + if(y0>=0 && x0>=0 && y0<_height && x0<_width) colorbuffer[y0*_width+x0]=color; + if (x0 == x1 && y0 == y1) return; + int e2 = (err << 1); + if (e2 > -dy){err -= dy;x0 += sx;} + if (e2 < dx){err += dx;y0 += sy;} + } + } +}; + +} + + +#endif diff --git a/thirdparty/aruco-3.1.12/utils_dcf/sglviewer.h b/thirdparty/aruco-3.1.12/utils_dcf/sglviewer.h new file mode 100644 index 0000000..3008219 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_dcf/sglviewer.h @@ -0,0 +1,522 @@ +/** +Copyright 2017 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +*/ + + + +///--------------------------------------------------------------------------------------------- +#ifndef _SGL_OpenCV_VIewer_H +#define _SGL_OpenCV_VIewer_H +#include "sgl.h" +#include +#include +#include "markermap.h" +#include +#include +#include + namespace aruco{ + //Class using an opencv window to render and manipulate a sgl scene + class sgl_OpenCV_Viewer { + sgl::Scene _Scene; + std::string _wname; + float _f; + int _w,_h; + cv::Mat _imshow; + int mode=1;//mode 0:big 3d, 1 big image + float _subw_nsize=0.3; + bool showingHelp=false; + cv::Mat _cameraImage; + bool showNumbers=true; + aruco::MarkerMap _mmap; + float cameraSize=0.1; + std::map > marker_points; + bool canLeave=true; + cv::Mat _resizedInImage; + cv::Mat _camPose; + bool followCamera=false; + public: + + + sgl_OpenCV_Viewer(){ + + } + + + + void setParams(float f,int width,int height,std::string wname,float CameraSize=0.1){ + _imshow.create(height,width,CV_8UC3); + _f=f; + _w=width;_h=height; + _wname=wname; + cv::namedWindow(_wname,cv::WINDOW_NORMAL); + cv::resizeWindow(_wname,width,height); + cv::setMouseCallback(_wname, &sgl_OpenCV_Viewer::mouseCallBackFunc , this); + + cameraSize=CameraSize; + sgl::Matrix44 cam; + cam.translate({0,4,0}); + cam.rotateX(3.1415/2.); + _Scene.setViewMatrix(cam); + + } + + int show( aruco::MarkerMap &mmap,const cv::Mat &cameraPose,const cv::Mat &cameraImage,int waitKeyTime=0){ + _cameraImage=cameraImage; + _mmap=mmap; + + //set the marker points + for(auto m:mmap) + if(marker_points.count(m.id)==0) + marker_points[m.id]=getMarkerIdPcd(m,0.5); + + if (!cameraPose.empty()) _camPose=cameraPose.inv(); + else _camPose=cv::Mat(); + + //first creation of the image + + createImage(); + + + int k; + bool isUsedKey=false; + do{ + cv::imshow(_wname,_imshow); + k=cv::waitKey(waitKeyTime); + isUsedKey=true; +// if (k!=255) std::cout<& POrg, const std::vector& PDst, cv::Mat& RT_4x4); + + +private: + + std::vector getMarkerIdPcd(aruco::Marker3DInfo &minfo , float perct); + + struct mouseInfo{ + sgl::Point2 pos; + bool isTranslating=false,isZooming=false,isRotating=false; + }mi; + + + static void mouseCallBackFunc(int event, int x, int y, int flags, void* userdata){ + sgl_OpenCV_Viewer *Sv=(sgl_OpenCV_Viewer*)userdata; + bool redraw=false; + if ( event == cv::EVENT_LBUTTONDOWN ){ + Sv->mi.isRotating=Sv->mi.isTranslating=Sv->mi.isZooming=false; + if ( flags&cv::EVENT_FLAG_CTRLKEY) + Sv->mi.isZooming=true; + else if ( flags&cv::EVENT_FLAG_SHIFTKEY) Sv->mi.isTranslating=true; + else Sv->mi.isRotating=true; + } + else if ( event == cv::EVENT_MBUTTONDOWN ) Sv->mi.isTranslating=true; + else if ( event == cv::EVENT_LBUTTONUP ) { Sv->mi.isRotating=Sv->mi.isTranslating=Sv->mi.isZooming=false; + } + else if ( event == cv::EVENT_MBUTTONUP ) Sv->mi.isTranslating=false; + else if ( event == cv::EVENT_MOUSEMOVE ) + { + sgl::Point2 dif(Sv-> mi.pos.x-x,Sv-> mi.pos.y-y); + sgl::Matrix44 tm;//=Sv->_Scene.getTransformMatrix(); + + if (Sv->mi.isRotating){ + tm.rotateX(-float(dif.y)/100); + tm.rotateZ(-float(dif.x)/100); + } + else if (Sv->mi.isZooming){ + auto vp=Sv->_Scene.getViewMatrix(); + vp.translate({0,0, float(-dif.y*0.01)}); + Sv->_Scene.setViewMatrix(vp); + redraw=true; + } + else if (Sv->mi.isTranslating){ + auto vp=Sv->_Scene.getViewMatrix(); + vp.translate(sgl::Point3(float(-dif.x)/100, float(-dif.y)/100,0.f)); + Sv->_Scene.setViewMatrix(vp); + redraw=true; + } + if (Sv->mi.isRotating||Sv->mi.isZooming ||Sv->mi.isTranslating) { + sgl::Matrix44 res= tm*Sv->_Scene.getModelMatrix() ; + Sv->_Scene.setModelMatrix(res); + redraw=true; + } + } + Sv->mi.pos=sgl::Point2(x,y); + if (redraw) { + Sv->updateImage(); + cv::imshow(Sv->_wname,Sv->_imshow); + } + + } + + + +}; +void sgl_OpenCV_Viewer::drawScene(){ + auto drawMarker=[](sgl::Scene &Scn, const aruco::Marker3DInfo &m , int width){ + const auto &points= m.points; + Scn.drawLine((sgl::Point3*)&points[0],(sgl::Point3*)&points[1],{0,0,255},width); + Scn.drawLine((sgl::Point3*)&points[1],(sgl::Point3*)&points[2],{0,255,0},width); + Scn.drawLine((sgl::Point3*)&points[2],(sgl::Point3*)&points[3],{255,0,0},width); + Scn.drawLine((sgl::Point3*)&points[3],(sgl::Point3*)&points[0],{155,0,155},width); + }; + + + auto drawPyramid=[](sgl::Scene &Scn,float w,float h,float z,const sgl::Color &color,int width){ + Scn.drawLine( {0,0,0}, {w,h,z},color,width); + Scn.drawLine( {0,0,0}, {w,-h,z},color,width); + Scn.drawLine( {0,0,0}, {-w,-h,z},color,width); + Scn.drawLine( {0,0,0}, {-w,h,z},color,width); + Scn.drawLine( {w,h,z}, {w,-h,z},color,width); + Scn.drawLine( {-w,h,z}, {-w,-h,z},color,width); + Scn.drawLine( {-w,h,z}, {w,h,z},color,width); + Scn.drawLine( {-w,-h,z}, {w,-h,z},color,width); + }; + + _Scene.clear(sgl::Color(255,255,255)); + if(followCamera && !_camPose.empty()){ + cv::Mat aa=_camPose.inv(); + _Scene.setViewMatrix(sgl::Matrix44(aa.ptr(0))); + _Scene.setModelMatrix(); + } + + + for(auto m:_mmap){ + drawMarker(_Scene,m,1); + if(showNumbers) + _Scene.drawPoints(marker_points[m.id],{125,0,255},1); + } + if (!followCamera){ + + //draw camera if it is possible + if (!_camPose.empty()){ + _Scene.pushModelMatrix(sgl::Matrix44(_camPose.ptr(0))); + drawPyramid(_Scene,cameraSize,cameraSize/2,cameraSize/2,{0,0,255},1); + _Scene.popModelMatrix(); + } +} + +} + +void sgl_OpenCV_Viewer::printHelp(){ + + //print help commands + if(!showingHelp) + cv::putText(_imshow, "'h' showhelp", cv::Point(30,20), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(200,200,250), 1); + else{ + cv::putText(_imshow, "'h' hide help", cv::Point(30,20), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(200,200,250), 1); + cv::putText(_imshow, "'s' start/stop video", cv::Point(30,40), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(200,200,250), 1); + cv::putText(_imshow, "'m' change view mode", cv::Point(30,60), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(200,200,250), 1); + cv::putText(_imshow, "'MOUSE[+SHIFT|CTRL]' change view", cv::Point(30,80), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(200,200,250), 1); + cv::putText(_imshow, "'n' show/hide marker numbers", cv::Point(30,100), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(200,200,250), 1); + cv::putText(_imshow, "'c' camera mode on/off", cv::Point(30,120), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(200,200,250), 1); + + } +} + +void sgl_OpenCV_Viewer::createImage( ) { + + + cv::Size subrectsize(_w*_subw_nsize,_h*_subw_nsize); + if (mode==0) _Scene.setCameraParams(_f,_w,_h,_imshow.ptr(0)); + else _Scene.setCameraParams(_f,subrectsize.width,subrectsize.height); + + drawScene(); + + + + + + //copy 3d image and color image + if (mode==0){ + if (!_cameraImage.empty()){ + auto subrect=_imshow(cv::Range(_h-subrectsize.height,_h),cv::Range(_w-subrectsize.width,_w)); + cv::resize( _cameraImage,_resizedInImage,subrectsize); + _resizedInImage.copyTo(subrect); + } + } + else{ + if (!_cameraImage.empty()){ + cv::resize( _cameraImage,_imshow, cv::Size(_w,_h)); + } + else _imshow.setTo(cv::Scalar::all(0)); + auto subrect=_imshow(cv::Range(_h-subrectsize.height,_h),cv::Range(_w-subrectsize.width,_w)); + cv::Mat im3d=cv::Mat(_Scene.getHeight(),_Scene.getWidth(),CV_8UC3,_Scene.getBuffer()); + im3d.copyTo(subrect); + } + + printHelp(); + +} + + +void sgl_OpenCV_Viewer::updateImage( ) { + + + + drawScene(); + cv::Size subrectsize(_w*_subw_nsize,_h*_subw_nsize); + auto subrect=_imshow(cv::Range(_h-subrectsize.height,_h),cv::Range(_w-subrectsize.width,_w)); + //copy 3d image and color image + if (mode==0){ + if (!_cameraImage.empty()) _resizedInImage.copyTo(subrect); + } + else{ + cv::Mat im3d=cv::Mat(_Scene.getHeight(),_Scene.getWidth(),CV_8UC3,_Scene.getBuffer()); + im3d.copyTo(subrect); + } + printHelp(); +} + +std::vector sgl_OpenCV_Viewer::getMarkerIdPcd(aruco::Marker3DInfo &minfo, float perct=1 ) +{ + auto mult=[](const cv::Mat& m, sgl::Point3 p) + { + assert(m.isContinuous()); + assert(m.type()==CV_32F); + sgl::Point3 res; + const float* ptr = m.ptr(0); + res.x = ptr[0] * p.x + ptr[1] * p.y + ptr[2] * p.z + ptr[3]; + res.y = ptr[4] * p.x + ptr[5] * p.y + ptr[6] * p.z + ptr[7]; + res.z = ptr[8] * p.x + ptr[9] * p.y + ptr[10] * p.z + ptr[11]; + return res; + }; + + int id = minfo.id; + // marker id as a set of points + std::string text = std::to_string(id); + int fontFace = cv::FONT_HERSHEY_SCRIPT_SIMPLEX; + double fontScale = 2; + int thickness = 3; + int baseline = 0; + float markerSize_2 = minfo.getMarkerSize() / 2.f; + cv::Size textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline); + cv::Mat img(textSize + cv::Size(0, baseline / 2), CV_8UC1, cv::Scalar::all(0)); + // center the text + // then put the text itself + cv::putText(img, text, cv::Point(0, textSize.height + baseline / 4), fontFace, fontScale, cv::Scalar::all(255), + thickness, 8); + // raster 2d points as 3d points + std::vector points_id; + for (int y = 0; y < img.rows; y++) + for (int x = 0; x < img.cols; x++) + if (img.at(y, x) != 0) + points_id.push_back(sgl::Point3( (float(x) / float(img.cols)) - 0.5f, (float(img.rows - y) / float(img.rows)) - 0.5f, 0.f)); + + // now,scale + for (auto& p : points_id) + p = p*markerSize_2; + // finally, translate + auto orgPoints=aruco::Marker::get3DPoints( minfo.getMarkerSize()); + cv::Mat RT44; + rigidBodyTransformation_Horn1987(orgPoints,minfo.points,RT44); + + for (auto& p : points_id) + p = mult(RT44, p); + + + //select only a fraction of them number of them + if(perct!=1){ + int n_used=float(points_id.size())*(perct); + std::random_shuffle(points_id.begin(),points_id.end()); + points_id.resize( n_used); + } + + return points_id; +} + + + +/********************** + * + * + **********************/ +float sgl_OpenCV_Viewer::rigidBodyTransformation_Horn1987(const std::vector& POrg, const std::vector& PDst, cv::Mat& RT_4x4) +{ + struct Quaternion + { + Quaternion(float q0, float q1, float q2, float q3) + { + q[0] = q0; + q[1] = q1; + q[2] = q2; + q[3] = q3; + } + cv::Mat getRotation() const + { + cv::Mat R(3, 3, CV_32F); + R.at(0, 0) = q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]; + R.at(0, 1) = 2.f * (q[1] * q[2] - q[0] * q[3]); + R.at(0, 2) = 2.f * (q[1] * q[3] + q[0] * q[2]); + + R.at(1, 0) = 2.f * (q[1] * q[2] + q[0] * q[3]); + R.at(1, 1) = q[0] * q[0] + q[2] * q[2] - q[1] * q[1] - q[3] * q[3]; + R.at(1, 2) = 2.f * (q[2] * q[3] - q[0] * q[1]); + + R.at(2, 0) = 2.f * (q[1] * q[3] - q[0] * q[2]); + R.at(2, 1) = 2.f * (q[2] * q[3] + q[0] * q[1]); + R.at(2, 2) = q[0] * q[0] + q[3] * q[3] - q[1] * q[1] - q[2] * q[2]; + return R; + } + float q[4]; + }; + assert(POrg.size()== PDst.size()); + + cv::Mat _org(POrg.size(),3,CV_32F,(float*)&POrg[0]); + cv::Mat _dst(PDst.size(),3,CV_32F,(float*)&PDst[0]); + + +// _org = _org.reshape(1); +// _dst = _dst.reshape(1); + cv::Mat Mu_s = cv::Mat::zeros(1, 3, CV_32F); + cv::Mat Mu_m = cv::Mat::zeros(1, 3, CV_32F); + // cout<<_s<(0)[i] /= float(_org.rows); + Mu_m.ptr(0)[i] /= float(_dst.rows); + } + + // cout<<"Mu_s="<(0, 0), eigenvectors.at(0, 1), eigenvectors.at(0, 2), + eigenvectors.at(0, 3)); + cv::Mat RR = rot.getRotation(); + // cout<<"RESULT="< +#include +#include + +using namespace std; + +class CmdLineParser +{ + int argc;char** argv;public: + CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} + bool operator[](string param) + {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;return (idx != -1); } + string operator()(string param, string defvalue = "-1"){int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param) idx = i;if (idx == -1) return defvalue;else return (argv[idx + 1]);} +}; + +std::vector> getRegionsConfig(string configuration){ + if (configuration.empty())return {}; + for(auto &c:configuration) if (c==',') c=' '; + stringstream sstr(configuration); + string markerConfig; + std::vector> n_k; + while(!sstr.eof()){ + if (sstr>>markerConfig) + { + int nVal, kVal; + if (sscanf(markerConfig.c_str(), "%d:%d", &nVal, &kVal) != 2) + { + cerr << "Incorrect N:K specification" << endl; + return {}; + } + + if(nVal <= kVal) + { + cerr << "Incorrect N:K specification. N should be > than K" << endl; + return {}; + } + + n_k.push_back(make_pair(nVal,kVal)); + } + } + + return n_k; +} + +using namespace std; +using namespace cv; + +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc < 3 || cml["-h"]) + { + cerr << "Usage: fractal_config.yml " + "n(f1):k(f1),n(f2):k(f2),...,n(fm),k(fm) " + "[-s bitSize (For the last level, in px. Default: -1, normalized marker)>]" << endl; + + cerr << endl; + return -1; + } + + //n(f1):k(f1),n(f2):k(f2),...,n(fm),k(fm) + //Example fractal marker with 3 levels -> 10:8,14:10,6:0 + std::vector> regionsConfig; + regionsConfig = getRegionsConfig(argv[2]); + if(regionsConfig.size()<1) return -1; + + //bixSize (last level) + int bitSize = stoi(cml("-s", "-1")); + + //Create configuration + aruco::FractalMarkerSet fractalmarkerset; + fractalmarkerset.create(regionsConfig, bitSize); + + //Save configuration file + cv::FileStorage fs(argv[1], cv::FileStorage::WRITE); + fractalmarkerset.saveToFile(fs); + } + catch (std::exception& ex) + { + cout << ex.what() << endl; + } +} diff --git a/thirdparty/aruco-3.1.12/utils_fractal/fractal_pix2meters.cpp b/thirdparty/aruco-3.1.12/utils_fractal/fractal_pix2meters.cpp new file mode 100644 index 0000000..246b95e --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_fractal/fractal_pix2meters.cpp @@ -0,0 +1,28 @@ +// This program converts a boardconfiguration file expressed in pixel to another one expressed in meters +#include "fractallabelers/fractalmarkerset.h" +#include + +using namespace std; +using namespace aruco; +int main(int argc, char** argv) +{ + try + { + if (argc < 4) + { + cerr << "Usage: in_configuration.yml fractal_size(meters) out_configuration.yml" << endl; + return -1; + } + + FractalMarkerSet BInfo; + BInfo = FractalMarkerSet::load(argv[1]); + + //Save file + cv::FileStorage fs(argv[3], cv::FileStorage::WRITE); + BInfo.convertToMeters(static_cast(atof(argv[2]))).saveToFile(fs); + } + catch (std::exception& ex) + { + cout << ex.what() << endl; + } +} diff --git a/thirdparty/aruco-3.1.12/utils_fractal/fractal_print_marker.cpp b/thirdparty/aruco-3.1.12/utils_fractal/fractal_print_marker.cpp new file mode 100644 index 0000000..b731205 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_fractal/fractal_print_marker.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +// convinience command line parser +class CmdLineParser +{ + int argc; + char** argv; +public: + CmdLineParser(int _argc, char** _argv) + : argc(_argc) + , argv(_argv) + { + } + bool operator[](string param) + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + return (idx != -1); + } + string operator()(string param, string defvalue = "-1") + { + int idx = -1; + for (int i = 0; i < argc && idx == -1; i++) + if (string(argv[i]) == param) + idx = i; + if (idx == -1) + return defvalue; + else + return (argv[idx + 1]); + } +}; + +int main(int argc, char** argv) +{ + try + { CmdLineParser cml(argc, argv); + + if (argc < 2) + { + cerr << "Usage: outfile.(jpg|png|ppm|bmp) [-c :FRACTAL_2L_6 default] [-bs:bitsize (smaller marker) 75 by default]" + << " [-noborder: removes the white border around the marker]" << endl; + cerr << "\tConfigurations: "; + for (auto config : aruco::FractalMarkerSet::getConfigurations()) + cerr << config << " "; + return -1; + } + + aruco::FractalMarkerSet fractalmarkerSet = aruco::FractalMarkerSet::load(cml("-c","FRACTAL_2L_6")); + int pixSize = std::stoi(cml("-bs", "75")); // pixel size each bit from smaller marker + + cv::Mat result = fractalmarkerSet.getFractalMarkerImage(pixSize,!cml["-noborder"]); + cv::imwrite(argv[1], result); + } + catch (std::exception& ex) + { + cout << ex.what() << endl; + } +} diff --git a/thirdparty/aruco-3.1.12/utils_fractal/fractal_tracker.cpp b/thirdparty/aruco-3.1.12/utils_fractal/fractal_tracker.cpp new file mode 100644 index 0000000..76ff7af --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_fractal/fractal_tracker.cpp @@ -0,0 +1,133 @@ +#include "cvdrawingutils.h" +#include +#include +#include +#include + +#include "fractaldetector.h" +#include "aruco_cvversioning.h" + +using namespace std; +using namespace cv; +using namespace aruco; +struct TimerAvrg{std::vector times;size_t curr=0,n; std::chrono::high_resolution_clock::time_point begin,end; TimerAvrg(int _n=30){n=_n;times.reserve(n); }inline void start(){begin= std::chrono::high_resolution_clock::now(); }inline void stop(){end= std::chrono::high_resolution_clock::now();double duration=double(std::chrono::duration_cast(end-begin).count())*1e-6;if ( times.size()=times.size()) curr=0;}}double getAvrg(){double sum=0;for(auto t:times) sum+=t;return sum/double(times.size());}}; +static TimerAvrg Tdetect, Tpose; + +cv::Mat __resize(const cv::Mat& in, int width) +{ + if (in.size().width <= width) + return in; + float yf = float(width) / float(in.size().width); + cv::Mat im2; + cv::resize(in, im2, cv::Size(width, static_cast(in.size().height * yf))); + return im2; +} + +// class for parsing command line +class CmdLineParser{int argc;char** argv;public:CmdLineParser(int _argc, char** _argv): argc(_argc), argv(_argv){} bool operator[](string param) {int idx = -1; for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;return (idx != -1);} string operator()(string param, string defvalue = "-1") {int idx = -1;for (int i = 0; i < argc && idx == -1; i++)if (string(argv[i]) == param)idx = i;if (idx == -1)return defvalue;else return (argv[idx + 1]);}}; + +int main(int argc, char** argv) +{ + try + { + CmdLineParser cml(argc, argv); + if (argc < 2 || cml["-h"]) + { + cerr << "Usage: (video.avi|live[:index]) [-s fractalSize] [-cam cameraParams.yml] [-c :FRACTAL_2L_6 default]" << endl; + cerr << "\tConfigurations: "; + for (auto config : aruco::FractalMarkerSet::getConfigurations()) + cerr << config << " "; + return 0; + } + + aruco::CameraParameters CamParam; + + cv::Mat InImage; + // Open input and read image + VideoCapture vreader; + bool isVideo=false; + + if(string(argv[1]).find("live")==std::string::npos){ + vreader.open(argv[1]); + isVideo=true; + } + else{ + string livestr=argv[1]; + for(auto &c:livestr)if(c==':')c=' '; + stringstream sstr;sstr<>aux>>n; + vreader.open(n); + if ( vreader.get(CV_CAP_PROP_FRAME_COUNT)>=2) isVideo=true; + } + + + if (vreader.isOpened()) + vreader >> InImage; + else + { + cerr << "Could not open input" << endl; + return -1; + } + + // read camera parameters if passed + if (cml["-cam"]) + CamParam.readFromXMLFile(cml("-cam")); + + // read marker size + float MarkerSize = std::stof(cml("-s", "-1")); + + FractalDetector FDetector; + FDetector.setConfiguration(cml("-c","FRACTAL_2L_6")); + + if (CamParam.isValid()) + { + CamParam.resize(InImage.size()); + FDetector.setParams(CamParam, MarkerSize); + } + + int frameId = 0; + char key = 0; + int waitTime=10; + do + { + std::cout << "\r\rFrameId: " << frameId++<(0,0),2) + pow(tvec.at(1,0), 2) + + pow(tvec.at(2,0),2)); + std::cout << "Distance to fractal marker: " << Z << " meters. "<< std::endl; + FDetector.draw3d(InImage); //3d + } + else + FDetector.draw2d(InImage); //Ok, show me at least the inner corners! + + imshow("in", __resize(InImage, 1800)); + key = cv::waitKey(waitTime); // wait for key to be pressed + if (key == 's') + waitTime = waitTime == 0 ? 10 : 0; + + } while (key != 27 && vreader.grab()); + } + catch (std::exception& ex) + { + cout << "Exception :" << ex.what() << endl; + } +} diff --git a/thirdparty/aruco-3.1.12/utils_gl/CMakeLists.txt b/thirdparty/aruco-3.1.12/utils_gl/CMakeLists.txt new file mode 100644 index 0000000..e9676e0 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_gl/CMakeLists.txt @@ -0,0 +1,11 @@ +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) +add_executable(aruco_test_gl aruco_test_gl.cpp) +add_executable(aruco_test_markermap_gl aruco_test_markermap_gl.cpp) + +target_link_libraries(aruco_test_gl aruco ${OPENGL_LIBRARIES} ${GLUT_glut_LIBRARY} opencv_imgproc opencv_highgui opencv_videoio) +target_link_libraries(aruco_test_markermap_gl aruco ${OPENGL_LIBRARIES} ${GLUT_glut_LIBRARY} opencv_calib3d) + +#add_executable(aruco_test_board_gl_mask aruco_test_board_gl_mask.cpp) +#target_link_libraries(aruco_test_board_gl_mask ${OPENGL_LIBRARIES}) + +install(TARGETS aruco_test_gl aruco_test_markermap_gl RUNTIME DESTINATION bin) diff --git a/thirdparty/aruco-3.1.12/utils_gl/aruco_test_board_gl_mask.cpp b/thirdparty/aruco-3.1.12/utils_gl/aruco_test_board_gl_mask.cpp new file mode 100644 index 0000000..4eba858 --- /dev/null +++ b/thirdparty/aruco-3.1.12/utils_gl/aruco_test_board_gl_mask.cpp @@ -0,0 +1,396 @@ +/***************************** +Copyright 2011 Rafael Muñoz Salinas. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''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 Rafael Muñoz Salinas 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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Rafael Muñoz Salinas. +********************************/ +#include +#include +#include +#ifdef __APPLE__ +#include +#elif _MSC_VER +//http://social.msdn.microsoft.com/Forums/eu/vcgeneral/thread/7d6e6fa5-afc2-4370-9a1f-991a76ccb5b7 +#include +#include +#include +#else +#include +#include +#endif + +#include +#include +#include +#include "aruco.h" +#include "markermap.h" + +void __glGetModelViewMatrix(double modelview_matrix[16],const cv::Mat &Rvec,const cv::Mat &Tvec) throw(cv::Exception) ; +using namespace cv; +using namespace aruco; + + bool The3DInfoAvailable=false; +float TheMarkerSize=-1; +MarkerDetector MDetector; +VideoCapture TheVideoCapturer; +vector TheMarkers; +MarkerDetector TheMarkerDetector; +MarkerMapPoseTracker MMPoseTracker; +MarkerMap TheMMConfig; +Mat TheInputImage,TheUndInputImage,TheResizedImage,TheMask; +CameraParameters TheCameraParams; +Size TheGlWindowSize; +bool TheCaptureFlag=true; +void vDrawScene(); +void vIdle(); +void vResize( GLsizei iWidth, GLsizei iHeight ); +void vMouse(int b,int s,int x,int y); + + + +/************************************ + * + * + * + * + ************************************/ + +int main(int argc,char **argv) +{ + try + { + if (argc!=5) { + cerr<<"Invalid number of arguments"<>TheInputImage; + //read camera paramters if passed + TheCameraParams.readFromXMLFile(argv[3]); + TheCameraParams.resize( TheInputImage.size()); + + TheMarkerDetector.setThresholdParams(25,7); + + glutInit(&argc, argv); + glutInitWindowPosition( 0, 0); + glutInitWindowSize(TheInputImage.size().width,TheInputImage.size().height); + glutInitDisplayMode( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE ); + glutCreateWindow( "ArUco" ); + glutDisplayFunc( vDrawScene ); + glutIdleFunc( vIdle ); + glutReshapeFunc( vResize ); + glutMouseFunc(vMouse); + glClearColor( 0.0, 0.0, 0.0, 1.0 ); + glClearDepth( 1.0 ); + + // these two are necesary for the mask effect + glEnable( GL_ALPHA_TEST ); + glAlphaFunc( GL_GREATER, 0.5 ); + + TheGlWindowSize=TheInputImage.size(); + vResize(TheGlWindowSize.width,TheGlWindowSize.height); + glutMainLoop(); + + } catch (std::exception &ex) + + { + cout<<"Exception :"<