From d9e3fd43031f9fcae43d85b649f15799c3f197a6 Mon Sep 17 00:00:00 2001 From: Vladimir Ponomarev Date: Fri, 19 May 2023 20:25:59 +0300 Subject: [PATCH] Merge pull request #23363 from vovka643:4.x_generate_charuco Added charuco board generation to gen_pattern.py #23363 added charuco board generation in gen_pattern.py moved aruco_dict_utils.cpp to samples from opencv_contrib (https://github.com/opencv/opencv_contrib/pull/3464) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- apps/python_app_test.py | 56 +++ doc/CMakeLists.txt | 5 + doc/charuco_board_pattern.png | Bin 0 -> 60003 bytes doc/pattern_tools/DICT_4X4_100.json.gz | Bin 0 -> 845 bytes doc/pattern_tools/DICT_4X4_1000.json.gz | Bin 0 -> 7434 bytes doc/pattern_tools/DICT_4X4_250.json.gz | Bin 0 -> 1967 bytes doc/pattern_tools/DICT_4X4_50.json.gz | Bin 0 -> 477 bytes doc/pattern_tools/DICT_5X5_100.json.gz | Bin 0 -> 1053 bytes doc/pattern_tools/DICT_5X5_1000.json.gz | Bin 0 -> 9496 bytes doc/pattern_tools/DICT_5X5_250.json.gz | Bin 0 -> 2472 bytes doc/pattern_tools/DICT_5X5_50.json.gz | Bin 0 -> 587 bytes doc/pattern_tools/DICT_6X6_100.json.gz | Bin 0 -> 1311 bytes doc/pattern_tools/DICT_6X6_1000.json.gz | Bin 0 -> 11811 bytes doc/pattern_tools/DICT_6X6_250.json.gz | Bin 0 -> 3077 bytes doc/pattern_tools/DICT_6X6_50.json.gz | Bin 0 -> 709 bytes doc/pattern_tools/DICT_7X7_100.json.gz | Bin 0 -> 1598 bytes doc/pattern_tools/DICT_7X7_1000.json.gz | Bin 0 -> 14731 bytes doc/pattern_tools/DICT_7X7_250.json.gz | Bin 0 -> 3828 bytes doc/pattern_tools/DICT_7X7_50.json.gz | Bin 0 -> 858 bytes doc/pattern_tools/DICT_APRILTAG_16h5.json.gz | Bin 0 -> 333 bytes doc/pattern_tools/DICT_APRILTAG_25h9.json.gz | Bin 0 -> 442 bytes doc/pattern_tools/DICT_APRILTAG_36h10.json.gz | Bin 0 -> 27496 bytes doc/pattern_tools/DICT_APRILTAG_36h11.json.gz | Bin 0 -> 7080 bytes doc/pattern_tools/DICT_ARUCO_ORIGINAL.json.gz | Bin 0 -> 6388 bytes doc/pattern_tools/gen_pattern.py | 92 ++++- doc/pattern_tools/test_charuco_board.py | 118 ++++++ doc/pattern_tools/test_requirements.txt | 2 + .../camera_calibration_pattern.markdown | 22 +- samples/cpp/aruco_dict_utils.cpp | 348 ++++++++++++++++++ 29 files changed, 629 insertions(+), 14 deletions(-) create mode 100755 apps/python_app_test.py create mode 100644 doc/charuco_board_pattern.png create mode 100644 doc/pattern_tools/DICT_4X4_100.json.gz create mode 100644 doc/pattern_tools/DICT_4X4_1000.json.gz create mode 100644 doc/pattern_tools/DICT_4X4_250.json.gz create mode 100644 doc/pattern_tools/DICT_4X4_50.json.gz create mode 100644 doc/pattern_tools/DICT_5X5_100.json.gz create mode 100644 doc/pattern_tools/DICT_5X5_1000.json.gz create mode 100644 doc/pattern_tools/DICT_5X5_250.json.gz create mode 100644 doc/pattern_tools/DICT_5X5_50.json.gz create mode 100644 doc/pattern_tools/DICT_6X6_100.json.gz create mode 100644 doc/pattern_tools/DICT_6X6_1000.json.gz create mode 100644 doc/pattern_tools/DICT_6X6_250.json.gz create mode 100644 doc/pattern_tools/DICT_6X6_50.json.gz create mode 100644 doc/pattern_tools/DICT_7X7_100.json.gz create mode 100644 doc/pattern_tools/DICT_7X7_1000.json.gz create mode 100644 doc/pattern_tools/DICT_7X7_250.json.gz create mode 100644 doc/pattern_tools/DICT_7X7_50.json.gz create mode 100644 doc/pattern_tools/DICT_APRILTAG_16h5.json.gz create mode 100644 doc/pattern_tools/DICT_APRILTAG_25h9.json.gz create mode 100644 doc/pattern_tools/DICT_APRILTAG_36h10.json.gz create mode 100644 doc/pattern_tools/DICT_APRILTAG_36h11.json.gz create mode 100644 doc/pattern_tools/DICT_ARUCO_ORIGINAL.json.gz create mode 100644 doc/pattern_tools/test_charuco_board.py create mode 100644 doc/pattern_tools/test_requirements.txt create mode 100644 samples/cpp/aruco_dict_utils.cpp diff --git a/apps/python_app_test.py b/apps/python_app_test.py new file mode 100755 index 000000000000..03e49784fa5c --- /dev/null +++ b/apps/python_app_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import sys +sys.dont_write_bytecode = True # Don't generate .pyc files / __pycache__ directories + +import os +import sys +import unittest + +# Python 3 moved urlopen to urllib.requests +try: + from urllib.request import urlopen +except ImportError: + from urllib import urlopen + +basedir = os.path.abspath(os.path.dirname(__file__)) + +sys.path.append(os.path.join(os.path.split(basedir)[0], "modules", "python", "test")) +from tests_common import NewOpenCVTests + +def load_tests(loader, tests, pattern): + cwd = os.getcwd() + config_file = 'opencv_apps_python_tests.cfg' + locations = [cwd, basedir] + if os.path.exists(config_file): + with open(config_file, 'r') as f: + locations += [str(s).strip() for s in f.readlines()] + else: + print('WARNING: OpenCV tests config file ({}) is missing, running subset of tests'.format(config_file)) + + tests_pattern = os.environ.get('OPENCV_APPS_TEST_FILTER', 'test_*') + '.py' + if tests_pattern != 'test_*.py': + print('Tests filter: {}'.format(tests_pattern)) + + processed = set() + for l in locations: + if not os.path.isabs(l): + l = os.path.normpath(os.path.join(cwd, l)) + if l in processed: + continue + processed.add(l) + print('Discovering python tests from: {}'.format(l)) + sys_path_modify = l not in sys.path + if sys_path_modify: + sys.path.append(l) # Hack python loader + discovered_tests = loader.discover(l, pattern=tests_pattern, top_level_dir=l) + print(' found {} tests'.format(discovered_tests.countTestCases())) + tests.addTests(loader.discover(l, pattern=tests_pattern)) + if sys_path_modify: + sys.path.remove(l) + return tests + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index c19fe967b7e3..64a065217c0e 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,3 +1,8 @@ +if (NOT CMAKE_CROSSCOMPILING) + file(RELATIVE_PATH __loc_relative "${OpenCV_BINARY_DIR}" "${CMAKE_CURRENT_LIST_DIR}/pattern_tools\n") + file(APPEND "${OpenCV_BINARY_DIR}/opencv_apps_python_tests.cfg" "${__loc_relative}") +endif() + if(NOT BUILD_DOCS) return() endif() diff --git a/doc/charuco_board_pattern.png b/doc/charuco_board_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..e97418d1ef2d266fcc06ca3b80e518e7426ab27c GIT binary patch literal 60003 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 literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_4X4_100.json.gz b/doc/pattern_tools/DICT_4X4_100.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..54a202303d676f05a5b188be936c0199ccf5fcb5 GIT binary patch literal 845 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 I-<~@4KXaS)jsO4v literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_4X4_250.json.gz b/doc/pattern_tools/DICT_4X4_250.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..bb70d2c58f1c79e21e6b48489c126bd454fe3018 GIT binary patch literal 1967 zcmV;g2T=GQiwFP!000003UygcZ)8ObyysV}=FDNbXWo2pgdc(s${|ARN?{KO#DC{K zSC!pkn{1?wGLG9_T`t?*|Ga(KzfNEO`1SMm_s>61KYp3p`3L-xzx(+6egE~aEoX7sX|2zKQ|2w_fjKk)K4L?4d z?cd4I1#5AU{fFbHWOFq?iUvz&n+-OMg#jf$H(%=r$>J$~?q-9JPgi8ffdg^S1a2A#fVd-R+bsEQ zh!bjfYeWDX2hfD79PkB*Gb_2nvK$cy*g&AhM2>S`y%1c;@g)^%a$dl3tmMg;B@P@H zlsxHdM8pB7P{>9^T;~tyvswai=80mCzXQ&Qi1Qq^Rlx3y)XjxSnJRb>kuSAsTyhmNzZG*sE7<2+jIFIRFR zPBkDD*@%dQdIaQH-GDo_P2vdkP&_qBllrR3yBHR`x_4r=(KI zvk_Vq>aL1HHbSow6~joLjP9zOYI{0n-o0{y;OK0!CnAmyC5zdJhe zWg{Zacvxhz5fBI0sN{7y?+uxA-2va(9IEz!=y^GHhhEJZjz8EPs*JUs9zYzzLrp2m zJ-k=J_5ekS>7mML7LuTB6urt)7qbx%XAM*HlZ}WtKIV$0(I(%JrC2}^3$p}E4l3n}4%sMAUM=lhqYK}8$&f?mi%s6|8 z$g?w86Aa`0(v&NsV#gIIBr?qXyG)_AU`ThW3{gqfHE{s#fX~;f2zdeeH*@ zzLb$QYJFg{SW)>3ULyujD0(L`W03KZS!8~RYi-Y|4t3lcOMf1Pr1)~|Nr$eEjIm-5 z5oxd=!k*dyg(xdVkIvfGD9yki%0s4h*VD!%x>|M2nZh1<$TE=Qn!?`Yy~7^EC=XBt zyxukldytCu;IjX-mk4{@#VayCUwKXxH#{jr?AbwF6#{!kZ5un zL#c0$cobj3dsXSG&*S2}M@>X*#aCz!gwFU{tQPDg(2dnHnX+8hGBqyFd*ao}{kS;q zy@P7b@#?%+DLb&s(O-l2tc+bgUY++i93WPF1@DR10a&pL-V4<(`ndP&(5tW=j z4zsLC?4$YVSjZvOwH^sxeCZ}e9gyW)w&Iw|+QRcLzBs31MHHgWm#zjNg0_i@>bBfC zvLrM}Y<@YQ!|v+P7PkZ52Q@1Sd+>obsDZ>c9j+BU$W!wyjT8o-Q(S{S0mTy*FCUTO>Q#n z9@s-FjE5PCyYpTd+i~LVyl11(c0(9pjegFKd&s6@KQY&O_OOFY*iX#0 zkz;LNI5F4y@$k%xFJVtWQR0i0u#ehNm1%OWl|J|V>O2d307l&@D++swQ>TTKbFI}V z%4O}DCg)mdsxha&@^P+}FU#4I9g}k{O19Q4Pk1A)a;N}}eofA`Y$oI{BQe&;Rd1V$ zm3VJJkkvJ=;Ju$@eKs8N5+ccIP~}{!h?tT&F3z=!S~gwn>0koBcoNH-?{;GMc zb>4$oXa-7L&U;iV<3zPu9z=p<#nFwXQ{{TM5-=X&;003N0 B)H478 literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_4X4_50.json.gz b/doc/pattern_tools/DICT_4X4_50.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..26046ff55f67a1c9fab6b2c04136633f29a2483f GIT binary patch literal 477 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 JJh4<%{|8Az(76Br literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_5X5_250.json.gz b/doc/pattern_tools/DICT_5X5_250.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..4c33bd61fa6d0e28dba4d2ea45f74f1f3997001e GIT binary patch literal 2472 zcmV;Z30L+XiwFP!000003U!*zjvGe|gztU|!{;0%?Ml0wWAY$D5cpsOu>mKCAVJHzW(v+_uoH%|MSz2A1~L_5Bep)`|{7{`>(I@ z%fEmA_Wk?k-~RgY?dwlp{#utjT=2{3{;N;t)A@9||2wb0$2a${$9JC|jn9Yi-SYU~ zwfoZi6fL>#QLVEtK|Letz4PS0Ouo+QZWa4PUy=?^D4OW3`6MhOZBQ2V zTjPEfmg$p(&y4&mEX~&@AxN(I7M5s?_#QeuqN&E{OEas8Tm!X+E)2bgE+cA}nzg0XaQ&_=aP*K$L9RSL=8fF+kZkU0q+$OVgw) zmnN#d(aQ%VOF6FAaBTQCdn)v5ZM~$~Eb`K{cFMgkp$4U6)k^hkEM0+7P|LhpgQe)l z&S9fw{Tf(O9wAaw7r(}HrNxhsba9E~mDz_fq6X=Q9!X$H;1AtaPx)#rqiL8Pbe9?l zvk!v3z9fY=Yhg(bZ_KUos=htPx=@^Wl|Z%j9IK|MErTMewddF%9imq67wb3Au`pD* zs9NV7YyG1aR$SHESgN%m*?eEEjU@$5A^O5&rwFcU$(@nDO)4Yd%qPLG)@GrAtvI zmuT~DRLPwhDte@u=U7A|nW!h3K3`(LCo-qz-L}oUQAGeeYs6qpU6K%|J`H*cvIC6->@3&HKJK%2OhM{Y6#T8--}k z?Qz>)v@8zrw4pX{!Q|#>n@eJ1ZRW8$!f{mnx>qyz?nqLXD2W=Hc`h969(o$3%-bWe zkrc+=G{EHbk>`e}1qp2p=Ed(p;iZT~@HX>&o9*m5sKdUQ8&%bNFosa-=J7^*SKrW` z<~`n@`H-%fMN-Y&s45hNKqjX|#zxSQ2e8w`@v^N1Q@{=3(b5(HKdQ)={$2`x3p^{P)+S|(QYZ|Xr%+Z2C=zlx3pIwITXGn z_RDGSRS{Wq;$$An!U#VqXENVDjgn|u2753MyCoWf>+Vst*mJkE_lQfVna6Gks&jkm z)XhAeS0Ms-sj9M?vs+4^QM(I^X1+bIhS)$ilXpL_qGU;}i@%$9Kd+J(<#7FQeA~?Z zWMk4x+d&R4=XhtD0jrVbVDhD@YEaq=fArX621a}px}~bc?$@Rvs&R)g@9}=d3Qt3= zs9G?5(1>&g*|-O5FnrR;r7m@Kr}VsE@^u?sXX;+*Y~CMr)p_v&ntAA|lO9?>jBXcy z+9>LFBbuSLFY%g2eY&AK@eI4AZ78G`be9(U_M!&g!A(QGU$!?jVwXkOG)qe)yCv%` ziW9t7gJHLX$2hE+xS8jRw|ydXM9uu+Evkjs!*Gv1U%dsR(%Hv3n)i73CXB3Ph)6R} zR2MKhEbw-i_~a4HC=O83uS=TuKs6^hrC#b>j>7#Hb zh6xF5=KJeZ^QnU$yeF!VsRjrdym{YVs5%fbKlSSKhAL98g3$q*_pMh&NksCF`NKO^ m0L`9i-V;^f4wo^6kP69y?MfZFKVeMib;A^QLVvJ!yjF5~jjK+U=y6&m! zp?U~}VYppWUH8$yo9+DCoj+cu_v7@rd)ywn8{Om9>E~GP#?9~N%QPL|zD}3()9I_M z7_Sz*bn~rU-}PNLe|meDi}|};?XJOn0nf52|TI|N-lb^ z@(H|OR;KN;1HBXYAh7j8eLclmN#I-Cpn)|34T8BO@Eu^yP9>Shyefh3V}n%H0_L5- z4}fjTw4!dxGcqYq2xV%v(v#7~p=t%=qP@H_8WLgHrNM@qPK-sB)rxq?iavU1)O>x; z65mQE#$%naN;^GGClDFE&TU%U8-QJ21v{BU{F10b#?Rr`9x?3{BVBxjK3j>}+&*1_#kaNVi zMAB7QF0G6fmobRi`f5grOOGb`%az+EnsJFG@h_Gy%W|X{mto+DO8lA8o~>kLWQy9! zXycODQDsR>JIUzqCGL`BTrL@HTrxluMRb={8STFGdY(y6N&3*61Wv$7;*Asa!EA z;mg`MXwjL`;xaVFVMY`+XmMFj8p4FM%C-HlB%#9k3XP18(^9f?VIkX$US@0vmC3Zb ZgiFq8MVv?Z+{xx|^A8ENtR*@L001@FEYJV| literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_6X6_100.json.gz b/doc/pattern_tools/DICT_6X6_100.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..39172ddb444bf4891f1dc513d7bd72cc2d6d53a5 GIT binary patch literal 1311 zcmV+)1>pK0iwFP!000003U!#vjwDqKMECs_qgu15e$##|N8mc z=jZ!x-#@;6{psWTW^#Hk_~m$CIvkJ3=ljQB+hzQ^FUP;_*WuCb8N1CejN|WiZ+vf- z`qAuO>~Gtcd*nT=S-gKWyVuPQ138g7VG`wx^9{Sq$iZrdFb}uQ-^q5)s4^o%vTb(f z$d7HqhaBjFOn3ZU!k_9!&DkY_bQeY+uBvC&A!f=r?+QJH|{VuUKpsOs77MB37k9;!ftoje0h#c8|` zk3voMu$W-5L6%`C*g%_P1GKE7&UOTDsw7LQCL^HhIX(i%pc9a&nJ&AbkdxFkrc(v7 zJe~PQj$5uOf_QgmM`~U`PQo*(EKSd6$*V|^#$Zu zjzlG^K?neq>&3C&D3Q)$^W$#b4FfqvfAh#fBIVclG5ja!ajNzH1GzTFp z0>fo@abopVn#0DcE?b}h-w4&3W4!<_fYACs72plcDYbIc3KnY2s=k%xP!?dfbWpu% z?Rc~1*o@g0ZA7OOb~hAqw10jCA#7ntHxzPGW-PCW9D1|ac0(b@MMRsRqjhUHR1P_* z0f{;z9T1Hi^OPf^BZf(Qvuzefnv*><|B5dX=$WH5$Ag0EW>r1zCUQ>F9LX^(q&be8 ztoy++kdrV&*T^h6vu)Z913AJ-{h_TnfA$QSR!$CPJ|IHX=^j0;3UR46OOiW=KIbIp zXeK;U3fq5nkL$ErMig!hgek};rqyG^JxmivlaD8+724*~E^IcvdZnh=m`VcN`G_^a*{(9$UF^z;-NWbAqQ~L%o^v6k8PoQ= zo~_oSLJg^`QTGUPaHXci&FW+Q>V`a(`zGj^ia_vlc3kTZ%vO0mQs*q4rLK9x(%*6iFvVZC|0t?zRRJEF7! z9QEyn`923wTOyGHIx?|(Fb`D|q|&Vj`RP#U4uvy}-_ V(c)cDI6VFP^dGTywt|Be00056f?xmu literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_6X6_1000.json.gz b/doc/pattern_tools/DICT_6X6_1000.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1091b013ec93489803ff0546d07405096f0fc6a8 GIT binary patch literal 11811 zcmW++cQ{;M6DEkZqKjUFL|MIs=sntE^)C8~5G7&tE~1Oh>Q?W92tp)8FN+X0tQx(n z?z_Kl|Jvu?x#u}|&dj{;ymMJT5@7xRN92e3JI%f^V(wZ}aa@4(@N3gKsy3 zFK@P|koP4{e#b2Lm;;uv`+?N^&F;HDa`%^*`=f*Vi`L*;^_OoY8_FRZCi4|G$&Fheh2!Kluyp6DLd!9nYiwb8f!@ZH~u29X! zLMlP_ex*n<=hntNLew^2 z!ISK;V`6uHh`zkKIQ31k!^>P#D~~H9q8@pvggY9OBeWo?;YHTVCTQ{QF+-JgsU)NC zZX+K3p<($9<1U^DcErrRQHZrQbR$;Bw8GiOM1uPaV~)@t9fk?+ z$Xih=Pw$%>@`(CKHi#12GhsVFmG$wQjJ+w{fw$xnPq&;GzeEkb)DdMZ7n#J?LuWQI zk~0Z`jz&K7Cx||i5xI{Npx7GK%U)AZ@PQg8cBqcNcS%^MrI}>x*Pa=p><`cV2C`&t z^4RbhN3JR)2nq*J%naw}uuTpA>KT?$E|-ENB8N(rDVr5BD4QM-AS0$rvp+k+pBjvK9HDv| z5jra}*7`_M=ed38@EjHw|HviVIu?Jne>ik-+@kt5`&`=>Of<0{QuauqiR=o)=|$|{-+D}N-gctutBw%r#PY&_M^o|4+&&Rh z?xwow_yCs_Y$54-`y|&74=SXm{~OsKh!E*}W~l4Wb0>Q0&s!>Nr#JqRff!3}vvXS3 zFq18fUPdO2(V~KT^T*EG&AI5hWxF*lQ!xSVa?{!@cun08$$a$exwQ1%F-F$OQ8RYH z&b8<;{87PYxh=3fq;5cIBI_miL^HIZ`4FYi zq#AmWWMc=Np8P1Qhp3QG>UjAyL*_4TmIg^Y_yXd*+J?C9^DssSX6RT2jQ*)Ic)%B$D^hGt@&4Y;O(B~diHOsH&girI2*cN&6SqDV0oC*|c(ykkE6(7*% zd8dR4DRXHGt``r{2|iJ4ICr@u$2(BKmM$I(W1!wa+jlD&7v1n_m?wLGI?i%Xu%Emv z@{?3Fr&o&di^%KM{i2C5)i0~xQXFJFt9JIQbC9HIe_}~-HkH|k%BhifC22R_kF2 z)T-I68fgyw?R*Z_Gmc-#A0oTHaagKhC}$3Je2V95#PxBARel7A)V-f)=&Lp6AFiBt zfB2m~n;=!v2v-T`Q}`vWk}Ew~@e_pokY}d@OUcx|^Hj@czcKy|Csy53XOqH%-LEdj zYRTU0b`SrEnAHfGi{M1Xq}-v)0_YDln2x&ZgqZg(TD(u05F_CZ3~LlmG7`6!_WmEX6m2PdT> zR42nqo5%|J;tMBohZUxvr`-7`%s~^WdR+58bB)8BJAXZ3xTU2Xt?kErPN;D63Y^<| z*n8suS^J4TLM6(mPDbNqFIjtTTWMtvm(wZXq0#l?=XAV+s=qSOTV$N{qJ*iHkoV~8 zEnT?0PkA!x;IGJT^o4McnlHDf$}XOABh-a*bH~Hf5ij|tP_#U=MVv0~^ zv5&a7`DY%4?DG+m0#>AU$JTD_#7c z{;%M0wx%ggl|r1A#?!%5F6I{LG+{gbc?>U0bm5e4UpUm+SH@KK#4qzta<(#ua!%vv zUvJg#UWz8RgOkP#qisxh$AqJV7?)qirzIjMiCz@-sPwHQ7pBwDr>$q6p$tvKFhUy( z+cUHon^C!wXH?mVJX57KCYoU;LW}LKVGP%h4MC)_xk{R(Q2S+WqQNrf3{}rwsp83H z6q0;X(XqY${V%V;au`jtmGFa$G^bjVY4wm4Mbgy6*gpc*P`gw`{>7%*=rQ2`5cQ8Z zo2oBYNGId20O2UGq~tHbg~i->&gOjw!AHVewSx*oz^S}2QCRBVi^eQy99Visdn5gqDow>5;kOwy7+^sq&kkn>M;1#8gc=1h=}RMyN^ zkm8Y>vX8N&V=n|)^gLP?zNv)djpG$NMapi53zM;|h>S;67>x+z-@eQeKIPJ@IHg+P z&%Y}N#j<_n&_-dLZ%yZXPjh|k&17fAPA1y9le2<)_BJg(I5YDTd`)W$xLRv0ZD44T z)ZVHcJ{$XMdWf%0$B`C-?$(3LHSs@)ovc zpK7#%H$9CY{Gbs5I`O9Nb&=UD)&_Fh)$=LjOa;00zz6}+7Cm+aNQdDLw$4pXdTN5!S#%7&%7|}ldRh~HWGJ}$9Q$lCt1Ag z&W`WQiq<}r%6Z9@26*Gg)6fnHyT*-Sktlj0!JR*!v*wGc9Sdwe_0l^9xwQK9+=Ugk z^UUK6KPkvHJTgaknKn!YM<*tJ{m8%P+9Q{=Dw&ldTA zkff3|>Z$J3*<*Y05}cK*Hrnm1J-aAw4$9y3e|r{buf%sz{zZaWaRUr*x@&xPTBjbM z0u_VbsqCUAd`NM^r9h7^buIbe_C6=5(FHb8@)2&5kPm2GSN{Gc_Lb_(D#BUjxB)MWI zFQf*#rny&=FRLag*tTM{iz=?)&%aI%wYI!Wc9nDV?P*_;U#ytlm`^M$V}JPhuci5D zUimKgz?*$dF^@lg_bJIl_}gyeM1`s8eurr0SUPKh(tAXDmwmRq4qw@1?lVT3G>84E zL#+5RGJ(Dd=N26%SS^nv(etJB_PQiv;eiO;(P*7J>KdWG3tUnvT4l?QBA*?~nx~ol zr9Q^@-X|^~@?4#t)hfx~iKD#H&4HEj-x*0OX32TREaa4VnHn4faK3^!uh=G=clfgr zT!?ftWP;gT10reQM!~&Br_=k*cSxiBuR$k4PoQhsrs9(8lEqD=7Y=TwY05&_@Dpt z_%oAMEMg4ehc>@BXdM+QPguH|n%26Gk51e!*Bk2m+598s?6~%A{H+CelU`K)L5>;` z_Zt;gP^3vL2|@UMPS~w@l=N8%&XhS*xzo9E4OC6IUf#dF<Q=wN$?*4d% zyy?b_5Cj`F{}+$u`turOF4^wXM>x&9QVs1@%ksc6E7+!Ygl8x)H#Q%E3yoN$vEyUmkzvIdWD&y`Yhx=K_tYMt}Ubbpa)ysiG-N)%{y{Ut_czwoGP zfgv!zsY%lyf!_4x#P#SDt+2H5Gwc^`6-0DOg9a@sc9a;uF92*|Z@r@NmL!6v|J1eb zxmXPIZd!-$M2?Z;P~e(E^39NsHpG=2udKyFcPmIt%5Z)=^p(Fxi_ktPkAsUWdh-q~ z^zr;aDN|%hvyP_jo$Q&JbUwY0&XrQVT$t_kGz;r}rYTkpo(*Dhy|HhOkux5$Abz1O z_~M_rr@pKgU-jmL~))1rcF+w9Wm(qa~h zBk^9)P7lqeX*WPscx1E(D(M|JwsV%C`atk$R4-q4xeodH7z~g0c8~;&Ra}G*~xdA)-{%LJ@ca7V?r9Zs-G15O8H;kIyTc=5XJ5^@|0b4AnDDtF7 z6z4q-@|0=e@WJHcWHw|F?J-3ZZK^V5d%h}n-aQevvn9U?DFL7O&B-TtD^Pmsr@Q9S z(}hkAnhyFQ;8(*^EmnVpgynE*8UwzMn@94!TImLF!gp$Lk%hQCziEum&rp7)A6wKs zHrwlvs?|%_ahUVVciOVgCGN@-5H-@XVwVIDL{;lfp zZwnD=pM`$k1ubzZC>~BZ)gqtD+eADH-E@uglo3n8uX3uL zs4=3ffnsisq~AjLCq}`=@-r!T-O#E*5h?qGEEk0a1Q+NM4%@ysFlmgY6ks(e|cn1mke z)yja4rGh@XJPz-h40Lajc2uz}FZ_o=`fdOh`h!QuP@Tt+r`^Eb^DB)FNll(1A9r2c zythR~FC+OU_(5nD#P4FF*7-LeZH5{rZ;a1y>^X_j&Q?Jp56hhlxcd^mLoSQ+7#o-4 zN1A&VV&21cUVl+x|A*&!{Dvqj^~V?gUne;;6?{H4E@L#!=Sj84x&Gl{S7e-0!M1a) zkORy^CM=2rg^-_fEuX#I$?7IA+a{j*wXxp)LC)3i^vQ3oRz;Q`4|gg!$2irLZ25L0 zYy+-8`G((0p%$Hp3Fw+a}9%~ht=;hpPHH0cJ^;~Fswc2Xpi)* zMXK&c4Zz|YYsrjcC8CnBz9b6lx|lwSa?aIdU0JzgEC$%j_t$L{j0okSUuFO#HU9Ya$JwC)Il40r!B#aXOSSnqV$zt3b&KAx{LO=7X z4a|b!N^uq?`?A0zN*nvPTrAgJSscI$wTWXMIH$Y)iOa7u)Q;1-jxydoChL~ZU(@`z z;S}@ofA1A#P(?bJ$Q>}{TLTqfVs)I45nUiV*el)JbDLb#pVX&@o3mtZadF-HsaU1& zGXDt8^|XHl-D+IGaG9A^#CMdKWuU*Py|ulQR~P0n5q;C5qwVI>?KJ-3cMUh-T0TGG z7+e`MjXA^wkmICpZu<7HiojlpugZG987CJAI7reK@qGP#CgfBJSi7PBUcB2V^i~R+ zsW8_47TWZYuGsQVe4zm)%)i>wCuX;TsRliRWmTk4O%x?PqkUxf7h2N@iYlp7?=5PU zpRibk!@{TZpJ?J-MjV}9-tNg7t3Deyf8EX1a(;VA;MH8*B1_Vt%m7m0O}29pM$x@zXpA1j!qV}I~g)L<{XIyUk>`dQl>G4EN9cTHM zVoZFY!XXCf;cNkJ?HP@PY7W3fC-v@ME&Zl!0O4eRKR&{#VwXK9ClRF;JW413!mgtz z&%bn$?EOTeCcQ)ZLE2{xQi~6lKZOsj3q_u}o@$9|I(z-tMQN%^GpnvZiV+j=JTqB^ zN#D)p4a2HN3(TK24$6B*d5BfgiVrORqq-s3@zYtQQuoa5xxP+*Qb{{wb5Y*)9$I5g zmZGb(YeD|nrD#;Qx7*{W@&r5YisWvfhOPgL^Fm8a2bwu9 zC$8yA(h-PIT917y9gDWl!adpwx8xCJfaX`~C-x-tyR3=7fXab>2_kJD54A5Z{TqIM zs-8?`i16yZ&Hf5%7*4N3cP%e1+2nZq(=(CX4D#pgg*{?zD-!qvHCNx-)}o@H_8Ucz zS>`SLj zz3U5q@c|Ex$DN<0(#GbRur+PZgp~cI0-W;bAF`Xicqf4jO$3R$v<4Sj`!ypdUa<&4u`D+{cJKv3A3AD~ZS^XkYVT(foSZ}&Mpl=$ z*-K_OERRus64JPE9ps^k<%-WvLMjlCn?AL@R&{Ka=AiN1BrW=KNU12OsgpK7k(-qYo0zA z?EEVijXRM+A;SkaVHzVid7&SfS?SQ;b;3#H_?t5>Bha+=Hd=TiS%dWue_;o{Bj$`# zRQSPJR~BDMH{xZmbSd<)*kzXx?yJnG&nn?Vn>Jiib8wfUN*ka|_8wchXDze_jh>+% zByWmOy?DlGS=f1>be2bn%i!88UF?u&_Dt^h*$r2SSnA-Uf4}cVDFeG55uo7shYNqi z8o54o8~P{LJik8xVVoTPNw$m1+7p34JCifux+aZe-F6+--1i%0HqzYg(*)mB?ahDZ znWR@$yOgW0`rLF+_Ux&a6}2jN?yC&6MQYt=9Ne8Xerv%#sWbnVI4{Pc7${7TGl$`W z*ogL^M~89gyA^vPoM`#Ki|Ycq)pCM1pPvrx=L#a` z-*VaN2&ettfLu&c{u!9)g7fF_kD0neu~7C59)oiv61zlqO76X#?7;%Fb^? zzVl4j1UEDt->mxVoWKc7>Ltp5oUo6vSRtcEWP%1C(xR;^>Z+}Osv3z3Tzg;8AG;pQR@Gp9{f_oq)U&vmiVjiA1pZ@TBcC`GFDO(-1m~J@r)kXh_ z105)|^?kj+`YF{-VW1rDgHKF!*q!jZFxfgN6mC<;KiC&v;XE4@=fZ6#JN|qFu*ch| z8isRer8{_Y#f$jXaK@E4TF@#!UWq~zcT)qJRVa_)uRbfO`_t{)3Kyb5HTcYpE5ZtC zq=b1b7-(=*TZ2K0lhG{Nf3to(6j>-kQXRCivKEr&bZ$rWB6{_P-j93eUq3+|Za^Y2 z&T3eMz>?-z*vbiok+y}B??-bSkcN66TrTzvdD#!a+{Kb#*=DC`-Uw9er&ip@A& zGI{4~kN~h7ZkGrG@YlOi@Uo!2p{~G;XxAkp=tz+sw%WMRI@Y7hm}N>F{OOd*!e=MHN}CCmE~)W7S7KG@DbI<6f9L{HKfr2)c{ z3H!oDa=DmRpBWE}_@pfWC^Bsg>Q1tyReK_|W=>RGW1xeabAYcPqpVcK4DBoCn9-v5lQOn|fZW9bXNnrsYT!Ld01%5+gKFsA#6qRpInu~;{bGaE!?3xj z_0YcTxg`hOAib&8gUZt&NVLN={kFm~XN@j7dv%_z_`bGOYD|rLoKc1U13s;%}ma@A+kGlzedsnY$-TppCKOw2D7|J}rM-090~) zfm2T}pKs&p7j&RGeMQf^_r3M$X=66dJj5R3yf8?b3&_8Zp~n;f4?nm`(h{{+)DruwEWnOkZPi0bR)(R5okdI_ z?q|nlEl)lIqYdaxPojTOGcej{r_-Y-9`}F&)21#hNyEkDH00P^es+eW;X_+93)<}4 z3Q1{urG>WvO3VKsGPl-miT>ehQ1AT@ZyBFhp#m03EN!%xV8l{ME}f=upvfIds>QYL z>VmB5E6Z5A!};^pbaq0?)MLFN&vOMkqRc-xJFN3gf_)8!S$&|>?0mTv9TM>@ZYcf7 z`CWNxH$l--Jv%73nNfNV*?OXmxs*)#eN0>C?f z!#>ufcjeSAq`BKy<`?oKsJMVb% ziIomwrgvS2M?CG+l|Si*DLWq2C^9IGr;ZW@=N5i?bc@1pA%Zv4RC~u&OpOsg$76N7 zGPUDAe1zC>*>Z~@C3)^of8C)%NIdFdqdchjuA%-~qaGe^9`h58axK`Srg6I}kVGF9 zSEjJbV#UvmDQTrb%Gv`|SdJWljP>Anw$@jwZi+??LzA?fm5HE=_%PYV?n?AB)NA|z< z;YV~#gSZB-ToMl7HZ0HzkC$@V>_A|DeRfvlI|p75uzdlX?~canS@~RiCUtfZ2O`k^ z*5(8BI1Gx#jvkx8<&p~mH*9pFedcA2`*gW)~Ie@&6+ z(nQt)igs=tC?i_>Atl$x)ZEgMmClicA82YGk~wbinQiOhehVm3{Q0wg3Js;DP{oEj ze*6+HEe((YtD@ACWHIQQVaL2|A_bb5LHUvfv{#qJK%^)xjkCK{ABIbHqfd)~{?Z>$ z4`5sLO3il@Q<`$7NF%w(2hF?+l=47!#P=nTnY;;FX#JE_6)FC>3fp z7h3)$9<4it=%^)hKnFAZgCEc+5jg zTCS2e1uP*Wno)6d_Uv~6m6+R3e~|Q^NEfIrx|Cb?{FS=@hy!6*ltN*C5**6WAa)IgTkH`-Hb7$_l^4j= zJO}dkmG}$UvDH*GrKVK@V^6m_(_+b@j7{Z~Qwu zXZfoQ?RDOK2Bq?+WV(~9&OleyL-eBx>vVQE<3Ez)%ALf=>ZjHrgp0)&u)}f2KXfY- zTk$&Azj=Y`bztHX5(OklKrE`I=0LB6D~vJbIbM(vfW^EzFT^%fa2{6!{_0I+P?Rt% zc15_;hC!9y{CN97AV0iNPm_@ZpWh4pcowQ8VE6PbaSX<}CyCV+WMUe!7HXqH990i8 zG30Kij76kh`%^`mO(^UH=J1}##D?m&rLpJ5ra^-75r!NvSHuUH4AX``RgHWPBX!f} zF<}&q#xw;YWHDpDQK#lurHkX0U2u*A@Fam+62oVO!&n+7MNgC>6s-4kAp;0EA=rQm z_UqD74A(6sm@_-zR;kpv5g#Dz5>_7oM0Fs=Yr0T0g5D>kZP#8fTyJ7QIQda&? zI1erbaAAFbnyF|leYO~+ILi6aM^qzIfGi)oAJ1M{j0M05g(L-U zxRQAcl(vGH_n24!`;@UQM$~1XVfT?F|Y`l}tVwqo;5Y<~s)-RBPR&-xgr8>Lw4Spx^37t}g-Mfs3h`rrL@^C|z?kXM`?*vof>Yn!D!Wc1|7TJ$-ZBDt1K&o@Run!)nXz-m z*GR_~4!F%fNMLoFeP=r$o*i0S<%F3649H1FWa4+Tgq#Y}&;iftUrVq1aN!*NN4GTv zCM*(c_a;Gn0Nya;vVGXRr&h8;TJ=h34dYiQj&g8f30wtL_6IY=Fpf!H2>Rhhr(44mi0F_ZJF&KYr9 zVNB2UpR7S`_ZGPp#zK{Je*&GQAHsXrNAw%u=Y;~urcnhS*eWi9ZtuxEgD!yJCx&8;ewf?=YMH8D$i4S^+0$;I zP#9s2MHO@CtVo0NZ3gPhap`KUu77p0(6G9O7w*e0!ahEFxykrSIkBnuBkXSG3^mbV zFOKWs#S0Pqv`sh<&L=AuyTdpMa%hru8MvITZ%-{3JAWg_{|@24Ckeb`RsoZNb@BD6 zG%qcdXOkyd{MX>E=zpn+Dbp~-8~cvddiR|X%fer1$mSc;Nu)47xy-ej-bR*+z$ z9qPPyPA|8ouAx)U>uoA4J&C@8I|ZssHD8jKXBTS#8o;Ev6K#xu&$QnB<{xGw@&}jN zn0@j9q{7lxPA(q)lUkGUn|?sZ|#0Rv0l3T~j-Hau0I10To& z#P2|@#EuZ%yIRT%jOVh%TF42a-t2kplhD75a>Tl*%mzeiljyt^TUHdc875S1jsOCiam0ND22XpF{hF1;&Rpdy+{rG`|{HI~tiZZIhf}Cw&6U zR=LMD0n_UTFFP*y-JnF`~>exA$me1z_f(fI1>!9f{9PZS74d2nK zU*npJ^D|2K(hiEXQFw421nTh6Vj$@xHvX#9iN|GeB-wAM4GhS5)>DfnT-J(@ zEoMnoY{d?I!1%iVRk>H4;}q?p4#|th?X?O+nQD%q1wp*p15#pYOJ)L=hxy0tbu4C` zwx>CY8MXF#W?wQkT!rOZp|wZ|AkP?02thufK2LM(qBe>N(?dyK|Je+%+K4ri^JZ47 z;+MrZ)rtg(FqI$lCk}cAZ+y{lk>=7ZPP9u$UsZ?0MqhixBKsWL<#yG1L8h(SX{x}7 rD@+eAb~H7SGE^_VjKcDWGZ@KvQ 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 Tuh5@9{P*F1OEHZne>eaDL&Wz2 literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_6X6_50.json.gz b/doc/pattern_tools/DICT_6X6_50.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..bb8fbd32d9ec97f26813fead83f892ee0a63122c GIT binary patch literal 709 zcmV;$0y_O4iwFP!000003T;+NPQySDyyq1tXAaKFa)gIO2;~qV5-D&%ApVZ0YZ=q+ zAdzHnxtChZ&*El$Ezj>S*SF*KWBGWq8*XvNqto|sJl*^7>-loM9$!CCm-Ex0kx8=V=hZS)R7~uHbu8sGG#FvCyt6sY?uR--d^Kg7i zxJ|1f&}vhk55jo`sDnb>Z9B=lLok|(+)F#q3RabYP>f_DKCl{` zg8l-8WUMNy(t_oO(ke8At($HuZNf-y6`~;yl_^g2DZQ79P>nh4PLSAWmI8!qXdAnM zw(Lu-bcAmDN+zSqj1*m-@xO2+ItYn7!^aGSa%^2`+9}}mWampcq8wYK;>FNR)U2r- z%hOg88z7}DXWf;<{gYD~lJ=hoDJjQx5EG&TtrxAXsT{=-tfVxE0O02Ga+Eh1B)eGt z7~vTh%IWr(j}#=8B;lb;t=P9`T5!)ld$f67+w3!;bI-2!?X@ zhjL_GNt~(Cy;-AU?wz9dR9#b+tqg!+`&_`qjhH@A=8mY4vdYZM>CX}N9 zaEjt$l7xKB6+<~nfPQ}mmDE&PW#*U=h|}RF4c8fwISj&XV0u|BR;_N3IlS>SmleB*=Mzgd=-Riby4%b52O&A0>iH@x@M=(Cyf=1>P+Ri{I z2lw|25+ark$v`N_F{5~8$T4pkHUps?Es@zoJ1VzkTjf7F&H$@UN~eg*jQ%eNMrRn> r_;Ooa4l>7_$$#B12IqZX8DpgztU|L+2c{^6D?gil%af_D8^UQ=YXd_S< zQ}O}(v+lgmo!lf$Ddw0h?#-6TE8P*jV3-@bB2tL&Jh>4^Y;)P6oFD~colm+OWhZCd zlBK6JfOP;5&FQV6%OtI@TDr2RCV7}hyL@16n3l9jhPq&(+1EuV+Pa70xv6@fvZOL*S9l%xevWb&U!(19nKCuXLwC&7>kI!Dwq^>)hx2}e_;%gnCU+IZ^t5ox*^Im?+$(wzcP*GT)WN7$m$+Tsb1`=p z?{Fwpm;2j!$Dtl&ayKrSCZxIMa>Y2YsE5hjvUUNO9GnTa0p3kV58|#`Y7yJLbs&Pz zMA>l>ckQ_3PgJ{>Qq`wu+;N%Qwc9T3ZU$U#b!OcU*lzCTNn}S@fI{LUo1Tk&*NV?n zqX-|5DI@YeTC*x3TM#yH-7U=0BeF;FuHZHW58E1+0K0Lj9!1N7%mXnk&8P%Pif;BO z;%?Edd^X-v>E%{Ein)vML2q$3(T*NsdR>iQv@gI?d^_~96qd9!?0PPfyS`iK9$Dv% z-Ca5NDE8e<&>&qTE37&Ux(&@`OQ1d( zgy1^c8Lr~)(LGF)Wwa&2sgADV?yQEJRe6eLz2gsOljKp{6(~$m+lhFmB1Bn_t!$J3lPOgJ5^>N9_!uMHq@#(5SFL>1`Eh zXK;hf44dtOw2_@G=(u=ih&ft8q~~p+bTrayQJ$8a!9b)rNDwKa`WdcA$-cWVw7658 zU)XmNrT$j)EMfW}E%Ye=vH5mfp77l=HHiqrp*L_SR<%_m-$nj(+*2shOn)qmj!W$f z3-L;mizlp=_9*MlfXO6=Cz;HuTEESsr*&sow4BLU<^^qMz@AHX279lZ4vgUlxx{0( zDC`XO%zR($vz*ht+8&#*Gr$LdiH0f0EMX#?sqr~GLwvB>a7#+myzvF}mX84f4};?--? z{Bf{Fd0}T@wx_4dj8q}@IWFx{%6Fq#^UfMnX{OPP-=i$g$jl5+Hb@LU*r=M&qvSb5 zQqIC4>a6k%-aU%@5WtMP_0Gfsf6|@%f8D+fs?nC3aIFIpyl$KeV43_jgce< z)6uiHN6~WzJAepb-$TPbXkXv=m)F{NJqFMcoKy2GE6cOzlIOebq)N$4evJQ&vpov? z?$MlWNJx;5hdO(d!!t4u>Wq>}MNnHPwB@oqBMZqQYC}?1DS)<4=NXwj#E2B6C_;Qw wW*wJ2Ba;J!X;{_LDRW>%>rs~fo3KA0gB5=;!vWRZr~f|v4@Tkd>6aS-08bPyh5!Hn literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_7X7_1000.json.gz b/doc/pattern_tools/DICT_7X7_1000.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..a62f83c15ecc7461d72c1f5c5c9b7fde5c8f87f6 GIT binary patch literal 14731 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 literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_7X7_250.json.gz b/doc/pattern_tools/DICT_7X7_250.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..03d1f83ef94e21ce87dd6f84f396e05043fa31b4 GIT binary patch literal 3828 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 literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_APRILTAG_16h5.json.gz b/doc/pattern_tools/DICT_APRILTAG_16h5.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..4367b30ed32ee77350772326b8927969815c573e GIT binary patch literal 333 zcmV-T0kZxdiwFP!000003T>0kZUQk3gzx(lC}$4ILg~+uJ_th99#*IzZ8=m$ynExB zaU>if*h4ZI+n=4=u&OJazmLmlT7J^UYLnO8)7Jbq)!l8|{QbNv%k*`fFX#PywHuEY z4!LfnQgZ!eeR56@riTJet_u4UQ+JxtG&(mYcm$TOTvdk=K-dfJ6pDSqI<)f-tS!CEi*E3s|b0;Ecqzn@Tj7~)D=WJT% z1lHuESUu|^*iC?r#BiXET$GRH7Dd91PH<43^#M+p};a4iB}ruWl4^ zArQ?FZfvvdvt}Wq$cJ0eIKNS(>1I-nG>S-EFF-@yIdQ&ybk)D;1mYZhjdzkqFPJ29f1Xvg{7j7IBm?z2rTcxCid^s7nk^wLd zAT`xH@Da@50ZtouR&wAwd%$Lgb{4b)-y1V+sO}XpLB<2Y)n3Xra>x8wt!oyWaf6qU z7b>-~pofqV(8!SgT6DAm_V~aVqBWvMtpa$oQY4>=uug?CDMmh`vb2uG>LEuaZHc!g z25tmNe`y2*RRg_lCRuT+p*7K?5hbjsv6K71Vh_lYPE)y5Fn-59ws90cIBhaLn)9fx z5~+jAtw$?M$)<^>Ix4Y=R8MSx<)!Clmhi&@GVkO3j?7Y{s9aiIJh!rJQ*H%-evd|$ z7Wl>uit}iWWqT>WOMB$N%`9!y)hIPAD4A?7ExpYfB2L<*EH~v=4UwtSJt0d?Et^!^ kmGK^pEN!&v6tFL$C)4ir+RTvrzWeR|0Q5h2*oX!I03g%W%m4rY literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_APRILTAG_36h10.json.gz b/doc/pattern_tools/DICT_APRILTAG_36h10.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1ed5057b66e03861c7af12fbe1b10bd0cedbcd08 GIT binary patch literal 27496 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

o-|wiGCaPWh4HQb^O@ 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 literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/DICT_APRILTAG_36h11.json.gz b/doc/pattern_tools/DICT_APRILTAG_36h11.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1928dfcf4da7850c0832942380203c93765192f7 GIT binary patch literal 7080 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 B%hCV< literal 0 HcmV?d00001 diff --git a/doc/pattern_tools/gen_pattern.py b/doc/pattern_tools/gen_pattern.py index 4618bc318b54..bec535baf6bd 100755 --- a/doc/pattern_tools/gen_pattern.py +++ b/doc/pattern_tools/gen_pattern.py @@ -6,7 +6,7 @@ -o, --output - output file (default out.svg) -r, --rows - pattern rows (default 11) -c, --columns - pattern columns (default 8) --T, --type - type of pattern, circles, acircles, checkerboard, radon_checkerboard (default circles) +-T, --type - type of pattern: circles, acircles, checkerboard, radon_checkerboard, charuco_board. default circles. -s, --square_size - size of squares in pattern (default 20.0) -R, --radius_rate - circles_radius = square_size/radius_rate (default 5.0) -u, --units - mm, inches, px, m (default mm) @@ -14,16 +14,20 @@ -h, --page_height - page height in units (default 279) -a, --page_size - page size (default A4), supersedes -h -w arguments -m, --markers - list of cells with markers for the radon checkerboard +-p, --aruco_marker_size - aruco markers size for ChAruco pattern (default 10.0) +-f, --dict_file - file name of custom aruco dictionary for ChAruco pattern -H, --help - show help """ import argparse - +import numpy as np +import json +import gzip from svgfig import * class PatternMaker: - def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height, markers): + def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height, markers, aruco_marker_size, dict_file): self.cols = cols self.rows = rows self.output = output @@ -33,6 +37,9 @@ def __init__(self, cols, rows, output, units, square_size, radius_rate, page_wid self.width = page_width self.height = page_height self.markers = markers + self.aruco_marker_size = aruco_marker_size #for charuco boards only + self.dict_file = dict_file + self.g = SVG("g") # the svg group container def make_circles_pattern(self): @@ -124,7 +131,7 @@ def make_radon_checkerboard_pattern(self): height=spacing, fill="black", stroke="none") else: square = SVG("path", d=self._make_round_rect(x * spacing + xspacing, y * spacing + yspacing, - spacing, corner_types), fill="black", stroke="none") + spacing, corner_types), fill="black", stroke="none") self.g.append(square) if self.markers is not None: r = self.square_size * 0.17 @@ -140,6 +147,69 @@ def make_radon_checkerboard_pattern(self): cy=(y * spacing) + y_spacing + r, r=r, fill=color, stroke="none") self.g.append(dot) + @staticmethod + def _create_marker_bits(markerSize_bits, byteList): + + marker = np.zeros((markerSize_bits+2, markerSize_bits+2)) + bits = marker[1:markerSize_bits+1, 1:markerSize_bits+1] + + for i in range(markerSize_bits): + for j in range(markerSize_bits): + bits[i][j] = int(byteList[i*markerSize_bits+j]) + + return marker + + def make_charuco_board(self): + if (self.aruco_marker_size>self.square_size): + print("Error: Aruco marker cannot be lager than chessboard square!") + return + + if (self.dict_file.split(".")[-1] == "gz"): + with gzip.open(self.dict_file, 'r') as fin: + json_bytes = fin.read() + json_str = json_bytes.decode('utf-8') + dictionary = json.loads(json_str) + + else: + f = open(self.dict_file) + dictionary = json.load(f) + + if (dictionary["nmarkers"] < int(self.cols*self.rows/2)): + print("Error: Aruco dictionary contains less markers than it needs for chosen board. Please choose another dictionary or use smaller board than required for chosen board") + return + + markerSize_bits = dictionary["markersize"] + + side = self.aruco_marker_size / (markerSize_bits+2) + spacing = self.square_size + xspacing = (self.width - self.cols * self.square_size) / 2.0 + yspacing = (self.height - self.rows * self.square_size) / 2.0 + + ch_ar_border = (self.square_size - self.aruco_marker_size)/2 + marker_id = 0 + for y in range(0, self.rows): + for x in range(0, self.cols): + + if x % 2 == y % 2: + square = SVG("rect", x=x * spacing + xspacing, y=y * spacing + yspacing, width=spacing, + height=spacing, fill="black", stroke="none") + self.g.append(square) + else: + img_mark = self._create_marker_bits(markerSize_bits, dictionary["marker_"+str(marker_id)]) + marker_id +=1 + x_pos = x * spacing + xspacing + y_pos = y * spacing + yspacing + + square = SVG("rect", x=x_pos+ch_ar_border, y=y_pos+ch_ar_border, width=self.aruco_marker_size, + height=self.aruco_marker_size, fill="black", stroke="none") + self.g.append(square) + for x_ in range(len(img_mark[0])): + for y_ in range(len(img_mark)): + if (img_mark[y_][x_] != 0): + square = SVG("rect", x=x_pos+ch_ar_border+(x_)*side, y=y_pos+ch_ar_border+(y_)*side, width=side, + height=side, fill="white", stroke="white", stroke_width = spacing*0.01) + self.g.append(square) + def save(self): c = canvas(self.g, width="%d%s" % (self.width, self.units), height="%d%s" % (self.height, self.units), viewBox="0 0 %d %d" % (self.width, self.height)) @@ -155,7 +225,7 @@ def main(): type=int) parser.add_argument("-r", "--rows", help="pattern rows", default="11", action="store", dest="rows", type=int) parser.add_argument("-T", "--type", help="type of pattern", default="circles", action="store", dest="p_type", - choices=["circles", "acircles", "checkerboard", "radon_checkerboard"]) + choices=["circles", "acircles", "checkerboard", "radon_checkerboard", "charuco_board"]) parser.add_argument("-u", "--units", help="length unit", default="mm", action="store", dest="units", choices=["mm", "inches", "px", "m"]) parser.add_argument("-s", "--square_size", help="size of squares in pattern", default="20.0", action="store", @@ -172,6 +242,10 @@ def main(): "coordinates as list of numbers: -m 1 2 3 4 means markers in cells " "[1, 2] and [3, 4]", default=argparse.SUPPRESS, action="store", dest="markers", nargs="+", type=int) + parser.add_argument("-p", "--marker_size", help="aruco markers size for ChAruco pattern (default 10.0)", default="10.0", + action="store", dest="aruco_marker_size", type=float) + parser.add_argument("-f", "--dict_file", help="file name of custom aruco dictionary for ChAruco pattern", default="DICT_ARUCO_ORIGINAL.json", + action="store", dest="dict_file", type=str) args = parser.parse_args() show_help = args.show_help @@ -185,6 +259,9 @@ def main(): units = args.units square_size = args.square_size radius_rate = args.radius_rate + aruco_marker_size = args.aruco_marker_size + dict_file = args.dict_file + if 'page_width' and 'page_height' in args: page_width = args.page_width page_height = args.page_height @@ -206,10 +283,11 @@ def main(): else: raise ValueError("The marker {},{} is outside the checkerboard".format(x, y)) - pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height, markers) + pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height, markers, aruco_marker_size, dict_file) # dict for easy lookup of pattern type mp = {"circles": pm.make_circles_pattern, "acircles": pm.make_acircles_pattern, - "checkerboard": pm.make_checkerboard_pattern, "radon_checkerboard": pm.make_radon_checkerboard_pattern} + "checkerboard": pm.make_checkerboard_pattern, "radon_checkerboard": pm.make_radon_checkerboard_pattern, + "charuco_board": pm.make_charuco_board} mp[p_type]() # this should save pattern to output pm.save() diff --git a/doc/pattern_tools/test_charuco_board.py b/doc/pattern_tools/test_charuco_board.py new file mode 100644 index 000000000000..83d355b7a6fc --- /dev/null +++ b/doc/pattern_tools/test_charuco_board.py @@ -0,0 +1,118 @@ +from __future__ import print_function + +import os, tempfile, numpy as np + +import sys +import cv2 as cv +from tests_common import NewOpenCVTests +import gen_pattern + +class aruco_objdetect_test(NewOpenCVTests): + + def test_aruco_dicts(self): + try: + from svglib.svglib import svg2rlg + from reportlab.graphics import renderPM + except: + raise self.skipTest("libraies svglib and reportlab not found") + else: + cols = 3 + rows = 5 + square_size = 100 + aruco_type = [cv.aruco.DICT_4X4_1000, cv.aruco.DICT_5X5_1000, cv.aruco.DICT_6X6_1000, + cv.aruco.DICT_7X7_1000, cv.aruco.DICT_ARUCO_ORIGINAL, cv.aruco.DICT_APRILTAG_16h5, + cv.aruco.DICT_APRILTAG_25h9, cv.aruco.DICT_APRILTAG_36h10, cv.aruco.DICT_APRILTAG_36h11] + aruco_type_str = ['DICT_4X4_1000','DICT_5X5_1000', 'DICT_6X6_1000', + 'DICT_7X7_1000', 'DICT_ARUCO_ORIGINAL', 'DICT_APRILTAG_16h5', + 'DICT_APRILTAG_25h9', 'DICT_APRILTAG_36h10', 'DICT_APRILTAG_36h11'] + marker_size = 0.8*square_size + board_width = cols*square_size + board_height = rows*square_size + + for aruco_type_i in range(len(aruco_type)): + #draw desk using opencv + aruco_dict = cv.aruco.getPredefinedDictionary(aruco_type[aruco_type_i]) + board = cv.aruco.CharucoBoard((cols, rows), square_size, marker_size, aruco_dict) + charuco_detector = cv.aruco.CharucoDetector(board) + from_cv_img = board.generateImage((cols*square_size*10, rows*square_size*10)) + + #draw desk using svg + fd1, filesvg = tempfile.mkstemp(prefix="out", suffix=".svg") + os.close(fd1) + fd2, filepng = tempfile.mkstemp(prefix="svg_marker", suffix=".png") + os.close(fd2) + + try: + basedir = os.path.abspath(os.path.dirname(__file__)) + pm = gen_pattern.PatternMaker(cols, rows, filesvg, "px", square_size, 0, board_width, + board_height, "charuco_checkboard", marker_size, + os.path.join(basedir, aruco_type_str[aruco_type_i]+'.json.gz')) + pm.make_charuco_board() + pm.save() + drawing = svg2rlg(filesvg) + renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=720) + from_svg_img = cv.imread(filepng) + + #test + _charucoCorners, _charucoIds, markerCorners_svg, markerIds_svg = charuco_detector.detectBoard(from_svg_img) + _charucoCorners, _charucoIds, markerCorners_cv, markerIds_cv = charuco_detector.detectBoard(from_cv_img) + + np.testing.assert_allclose(markerCorners_svg, markerCorners_cv, 0.1, 0.1) + np.testing.assert_allclose(markerIds_svg, markerIds_cv, 0.1, 0.1) + finally: + if os.path.exists(filesvg): + os.remove(filesvg) + if os.path.exists(filepng): + os.remove(filepng) + + def test_aruco_marker_sizes(self): + try: + from svglib.svglib import svg2rlg + from reportlab.graphics import renderPM + except: + raise self.skipTest("libraies svglib and reportlab not found") + else: + cols = 3 + rows = 5 + square_size = 100 + aruco_type = cv.aruco.DICT_5X5_1000 + aruco_type_str = 'DICT_5X5_1000' + marker_sizes_rate = [0.25, 0.5, 0.75, 0.9] + board_width = cols*square_size + board_height = rows*square_size + + for marker_s_rate in marker_sizes_rate: + marker_size = marker_s_rate*square_size + #draw desk using opencv + aruco_dict = cv.aruco.getPredefinedDictionary(aruco_type) + board = cv.aruco.CharucoBoard((cols, rows), square_size, marker_size, aruco_dict) + charuco_detector = cv.aruco.CharucoDetector(board) + from_cv_img = board.generateImage((cols*square_size*10, rows*square_size*10)) + + #draw desk using svg + fd1, filesvg = tempfile.mkstemp(prefix="out", suffix=".svg") + os.close(fd1) + fd2, filepng = tempfile.mkstemp(prefix="svg_marker", suffix=".png") + os.close(fd2) + + try: + basedir = os.path.abspath(os.path.dirname(__file__)) + pm = gen_pattern.PatternMaker(cols, rows, filesvg, "px", square_size, 0, board_width, + board_height, "charuco_checkboard", marker_size, os.path.join(basedir, aruco_type_str+'.json.gz')) + pm.make_charuco_board() + pm.save() + drawing = svg2rlg(filesvg) + renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=720) + from_svg_img = cv.imread(filepng) + + #test + _charucoCorners, _charucoIds, markerCorners_svg, markerIds_svg = charuco_detector.detectBoard(from_svg_img) + _charucoCorners, _charucoIds, markerCorners_cv, markerIds_cv = charuco_detector.detectBoard(from_cv_img) + + np.testing.assert_allclose(markerCorners_svg, markerCorners_cv, 0.1, 0.1) + np.testing.assert_allclose(markerIds_svg, markerIds_cv, 0.1, 0.1) + finally: + if os.path.exists(filesvg): + os.remove(filesvg) + if os.path.exists(filepng): + os.remove(filepng) diff --git a/doc/pattern_tools/test_requirements.txt b/doc/pattern_tools/test_requirements.txt new file mode 100644 index 000000000000..0c169a71e307 --- /dev/null +++ b/doc/pattern_tools/test_requirements.txt @@ -0,0 +1,2 @@ +svglib>=1.5.1 +reportlab>=4.0.0 diff --git a/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown b/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown index 8f4c1848d2e6..54b0c0e1cd4d 100644 --- a/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown +++ b/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown @@ -17,6 +17,9 @@ You can find a chessboard pattern in https://github.com/opencv/opencv/blob/4.x/d You can find a circleboard pattern in https://github.com/opencv/opencv/blob/4.x/doc/acircles_pattern.png +You can find a ChAruco board pattern in https://github.com/opencv/opencv/blob/4.x/doc/charuco_board_pattern.png +(7X5 ChAruco board, square size: 30 mm , marker size: 15 mm, aruco dict: DICT_5X5_100, page width: 210 mm, page height: 297 mm) + Create your own pattern --------------- @@ -28,7 +31,7 @@ create a checkerboard pattern in file chessboard.svg with 9 rows, 6 columns and python gen_pattern.py -o chessboard.svg --rows 9 --columns 6 --type checkerboard --square_size 20 -create a circle board pattern in file circleboard.svg with 7 rows, 5 columns and a radius of 15mm: +create a circle board pattern in file circleboard.svg with 7 rows, 5 columns and a radius of 15 mm: python gen_pattern.py -o circleboard.svg --rows 7 --columns 5 --type circles --square_size 15 @@ -40,13 +43,18 @@ create a radon checkerboard for findChessboardCornersSB() with markers in (7 4), python gen_pattern.py -o radon_checkerboard.svg --rows 10 --columns 15 --type radon_checkerboard -s 12.1 -m 7 4 7 5 8 5 +create a ChAruco board pattern in charuco_board.svg with 7 rows, 5 columns, square size 30 mm, aruco marker size 15 mm and using DICT_5X5_100 as dictionary for aruco markers (it contains in DICT_ARUCO.json file): + + python gen_pattern.py -o charuco_board.svg --rows 7 --columns 5 -T charuco_board --square_size 30 --marker_size 15 -f DICT_5X5_100.json.gz + If you want to change unit use -u option (mm inches, px, m) If you want to change page size use -w and -h options -@cond HAVE_opencv_aruco -If you want to create a ChArUco board read @ref tutorial_charuco_detection "tutorial Detection of ChArUco Corners" in opencv_contrib tutorial. -@endcond -@cond !HAVE_opencv_aruco -If you want to create a ChArUco board read tutorial Detection of ChArUco Corners in opencv_contrib tutorial. -@endcond +If you want to use your own dictionary for ChAruco board your should write name of file with your dictionary. For example + + python gen_pattern.py -o charuco_board.svg --rows 7 --columns 5 -T charuco_board -f my_dictionary.json + +You can generate your dictionary in my_dictionary.json file with number of markers 30 and markers size 5 bits by using opencv/samples/cpp/aruco_dict_utils.cpp. + + bin/example_cpp_aruco_dict_utils.exe my_dict.json -nMarkers=30 -markerSize=5 diff --git a/samples/cpp/aruco_dict_utils.cpp b/samples/cpp/aruco_dict_utils.cpp new file mode 100644 index 000000000000..4a33f15bbfee --- /dev/null +++ b/samples/cpp/aruco_dict_utils.cpp @@ -0,0 +1,348 @@ +#include +#include + +using namespace cv; +using namespace std; + +static int _getSelfDistance(const Mat &marker) { + + Mat bytes = aruco::Dictionary::getByteListFromBits(marker); + + double minHamming = (double)marker.total() + 1; + for(int r = 1; r < 4; r++) { + cv::Mat tmp1(1, bytes.cols, CV_8UC1, Scalar::all(0)); + cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0)); + uchar* rot0 = tmp1.ptr(); + uchar* rot1 = tmp2.ptr(); + + for (int i = 0; i < bytes.cols; ++i) { + rot0[i] = bytes.ptr()[i]; + rot1[i] = bytes.ptr()[bytes.cols*r + i]; + } + + double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING); + if (currentHamming < minHamming) minHamming = currentHamming; + } + Mat b; + flip(marker, b, 0); + Mat flipBytes = aruco::Dictionary::getByteListFromBits(b); + for(int r = 0; r < 4; r++) { + cv::Mat tmp1(1, flipBytes.cols, CV_8UC1, Scalar::all(0)); + cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0)); + uchar* rot0 = tmp1.ptr(); + uchar* rot1 = tmp2.ptr(); + + for (int i = 0; i < bytes.cols; ++i) { + rot0[i] = flipBytes.ptr()[i]; + rot1[i] = bytes.ptr()[bytes.cols*r + i]; + } + + double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING); + if(currentHamming < minHamming) minHamming = currentHamming; + } + flip(marker, b, 1); + flipBytes = aruco::Dictionary::getByteListFromBits(b); + for(int r = 0; r < 4; r++) { + cv::Mat tmp1(1, flipBytes.cols, CV_8UC1, Scalar::all(0)); + cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0)); + uchar* rot0 = tmp1.ptr(); + uchar* rot1 = tmp2.ptr(); + + for (int i = 0; i < bytes.cols; ++i) { + rot0[i] = flipBytes.ptr()[i]; + rot1[i] = bytes.ptr()[bytes.cols*r + i]; + } + + double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING); + if(currentHamming < minHamming) minHamming = currentHamming; + } + return cvRound(minHamming); +} + +static inline int getFlipDistanceToId(const aruco::Dictionary& dict, InputArray bits, int id, bool allRotations = true) { + Mat bytesList = dict.bytesList; + CV_Assert(id >= 0 && id < bytesList.rows); + + unsigned int nRotations = 4; + if(!allRotations) nRotations = 1; + + Mat candidateBytes = aruco::Dictionary::getByteListFromBits(bits.getMat()); + double currentMinDistance = int(bits.total() * bits.total()); + for(unsigned int r = 0; r < nRotations; r++) { + + cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); + cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); + uchar* rot0 = tmp1.ptr(); + uchar* rot1 = tmp2.ptr(); + + for (int i = 0; i < candidateBytes.cols; ++i) { + rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i]; + rot1[i] = candidateBytes.ptr()[i]; + } + + double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING); + if(currentHamming < currentMinDistance) { + currentMinDistance = currentHamming; + } + } + Mat b; + flip(bits.getMat(), b, 0); + candidateBytes = aruco::Dictionary::getByteListFromBits(b); + for(unsigned int r = 0; r < nRotations; r++) { + cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); + cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); + uchar* rot0 = tmp1.ptr(); + uchar* rot1 = tmp2.ptr(); + + for (int i = 0; i < candidateBytes.cols; ++i) { + rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i]; + rot1[i] = candidateBytes.ptr()[i]; + } + + double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING); + if (currentHamming < currentMinDistance) { + currentMinDistance = currentHamming; + } + } + + flip(bits.getMat(), b, 1); + candidateBytes = aruco::Dictionary::getByteListFromBits(b); + for(unsigned int r = 0; r < nRotations; r++) { + cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); + cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); + uchar* rot0 = tmp1.ptr(); + uchar* rot1 = tmp2.ptr(); + + for (int i = 0; i < candidateBytes.cols; ++i) { + rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i]; + rot1[i] = candidateBytes.ptr()[i]; + } + + double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING); + if (currentHamming < currentMinDistance) { + currentMinDistance = currentHamming; + } + } + return cvRound(currentMinDistance); +} + +static inline aruco::Dictionary generateCustomAsymmetricDictionary(int nMarkers, int markerSize, + const aruco::Dictionary &baseDictionary, + int randomSeed) { + RNG rng((uint64)(randomSeed)); + + aruco::Dictionary out; + out.markerSize = markerSize; + + // theoretical maximum intermarker distance + // See S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014. + // "Automatic generation and detection of highly reliable fiducial markers under occlusion". + // Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005 + int C = (int)std::floor(float(markerSize * markerSize) / 4.f); + int tau = 2 * (int)std::floor(float(C) * 4.f / 3.f); + + // if baseDictionary is provided, calculate its intermarker distance + if(baseDictionary.bytesList.rows > 0) { + CV_Assert(baseDictionary.markerSize == markerSize); + out.bytesList = baseDictionary.bytesList.clone(); + + int minDistance = markerSize * markerSize + 1; + for(int i = 0; i < out.bytesList.rows; i++) { + Mat markerBytes = out.bytesList.rowRange(i, i + 1); + Mat markerBits = aruco::Dictionary::getBitsFromByteList(markerBytes, markerSize); + minDistance = min(minDistance, _getSelfDistance(markerBits)); + for(int j = i + 1; j < out.bytesList.rows; j++) { + minDistance = min(minDistance, getFlipDistanceToId(out, markerBits, j)); + } + } + tau = minDistance; + } + + // current best option + int bestTau = 0; + Mat bestMarker; + + // after these number of unproductive iterations, the best option is accepted + const int maxUnproductiveIterations = 5000; + int unproductiveIterations = 0; + + while(out.bytesList.rows < nMarkers) { + Mat currentMarker(markerSize, markerSize, CV_8UC1, Scalar::all(0)); + rng.fill(currentMarker, RNG::UNIFORM, 0, 2); + + int selfDistance = _getSelfDistance(currentMarker); + int minDistance = selfDistance; + + // if self distance is better or equal than current best option, calculate distance + // to previous accepted markers + if(selfDistance >= bestTau) { + for(int i = 0; i < out.bytesList.rows; i++) { + int currentDistance = getFlipDistanceToId(out, currentMarker, i); + minDistance = min(currentDistance, minDistance); + if(minDistance <= bestTau) { + break; + } + } + } + + // if distance is high enough, accept the marker + if(minDistance >= tau) { + unproductiveIterations = 0; + bestTau = 0; + Mat bytes = aruco::Dictionary::getByteListFromBits(currentMarker); + out.bytesList.push_back(bytes); + } else { + unproductiveIterations++; + + // if distance is not enough, but is better than the current best option + if(minDistance > bestTau) { + bestTau = minDistance; + bestMarker = currentMarker; + } + + // if number of unproductive iterarions has been reached, accept the current best option + if(unproductiveIterations == maxUnproductiveIterations) { + unproductiveIterations = 0; + tau = bestTau; + bestTau = 0; + Mat bytes = aruco::Dictionary::getByteListFromBits(bestMarker); + out.bytesList.push_back(bytes); + } + } + } + + // update the maximum number of correction bits for the generated dictionary + out.maxCorrectionBits = (tau - 1) / 2; + + return out; +} + +static inline int getMinDistForDict(const aruco::Dictionary& dict) { + const int dict_size = dict.bytesList.rows; + const int marker_size = dict.markerSize; + int minDist = marker_size * marker_size; + for (int i = 0; i < dict_size; i++) { + Mat row = dict.bytesList.row(i); + Mat marker = dict.getBitsFromByteList(row, marker_size); + for (int j = 0; j < dict_size; j++) { + if (j != i) { + minDist = min(dict.getDistanceToId(marker, j), minDist); + } + } + } + return minDist; +} + +static inline int getMinAsymDistForDict(const aruco::Dictionary& dict) { + const int dict_size = dict.bytesList.rows; + const int marker_size = dict.markerSize; + int minDist = marker_size * marker_size; + for (int i = 0; i < dict_size; i++) + { + Mat row = dict.bytesList.row(i); + Mat marker = dict.getBitsFromByteList(row, marker_size); + for (int j = 0; j < dict_size; j++) + { + if (j != i) + { + minDist = min(getFlipDistanceToId(dict, marker, j), minDist); + } + } + } + return minDist; +} + +const char* keys = + "{@outfile | | Output file with custom dict }" + "{r | false | Calculate the metric considering flipped markers }" + "{d | | Dictionary Name: DICT_4X4_50, DICT_4X4_100, DICT_4X4_250," + "DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, DICT_5X5_1000, " + "DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50," + "DICT_7X7_100, DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL," + "DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, DICT_APRILTAG_36h10," + "DICT_APRILTAG_36h11}" + "{nMarkers | | Number of markers in the dictionary }" + "{markerSize | | Marker size }" + "{cd | | Input file with custom dictionary }"; + +const char* about = + "This program can be used to calculate the ArUco dictionary metric.\n" + "To calculate the metric considering flipped markers use -'r' flag.\n" + "This program can be used to create and write the custom ArUco dictionary.\n"; + +int main(int argc, char *argv[]) +{ + CommandLineParser parser(argc, argv, keys); + parser.about(about); + if(argc < 2) { + parser.printMessage(); + return 0; + } + string outputFile = parser.get(0); + int nMarkers = parser.get("nMarkers"); + int markerSize = parser.get("markerSize"); + bool checkFlippedMarkers = parser.get("r"); + + aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0); + + if (parser.has("d")) { + string arucoDictName = parser.get("d"); + cv::aruco::PredefinedDictionaryType arucoDict; + if (arucoDictName == "DICT_4X4_50") { arucoDict = cv::aruco::DICT_4X4_50; } + else if (arucoDictName == "DICT_4X4_100") { arucoDict = cv::aruco::DICT_4X4_100; } + else if (arucoDictName == "DICT_4X4_250") { arucoDict = cv::aruco::DICT_4X4_250; } + else if (arucoDictName == "DICT_4X4_1000") { arucoDict = cv::aruco::DICT_4X4_1000; } + else if (arucoDictName == "DICT_5X5_50") { arucoDict = cv::aruco::DICT_5X5_50; } + else if (arucoDictName == "DICT_5X5_100") { arucoDict = cv::aruco::DICT_5X5_100; } + else if (arucoDictName == "DICT_5X5_250") { arucoDict = cv::aruco::DICT_5X5_250; } + else if (arucoDictName == "DICT_5X5_1000") { arucoDict = cv::aruco::DICT_5X5_1000; } + else if (arucoDictName == "DICT_6X6_50") { arucoDict = cv::aruco::DICT_6X6_50; } + else if (arucoDictName == "DICT_6X6_100") { arucoDict = cv::aruco::DICT_6X6_100; } + else if (arucoDictName == "DICT_6X6_250") { arucoDict = cv::aruco::DICT_6X6_250; } + else if (arucoDictName == "DICT_6X6_1000") { arucoDict = cv::aruco::DICT_6X6_1000; } + else if (arucoDictName == "DICT_7X7_50") { arucoDict = cv::aruco::DICT_7X7_50; } + else if (arucoDictName == "DICT_7X7_100") { arucoDict = cv::aruco::DICT_7X7_100; } + else if (arucoDictName == "DICT_7X7_250") { arucoDict = cv::aruco::DICT_7X7_250; } + else if (arucoDictName == "DICT_7X7_1000") { arucoDict = cv::aruco::DICT_7X7_1000; } + else if (arucoDictName == "DICT_ARUCO_ORIGINAL") { arucoDict = cv::aruco::DICT_ARUCO_ORIGINAL; } + else if (arucoDictName == "DICT_APRILTAG_16h5") { arucoDict = cv::aruco::DICT_APRILTAG_16h5; } + else if (arucoDictName == "DICT_APRILTAG_25h9") { arucoDict = cv::aruco::DICT_APRILTAG_25h9; } + else if (arucoDictName == "DICT_APRILTAG_36h10") { arucoDict = cv::aruco::DICT_APRILTAG_36h10; } + else if (arucoDictName == "DICT_APRILTAG_36h11") { arucoDict = cv::aruco::DICT_APRILTAG_36h11; } + else { + cout << "incorrect name of aruco dictionary \n"; + return 1; + } + + dictionary = aruco::getPredefinedDictionary(arucoDict); + } + else if (parser.has("cd")) { + FileStorage fs(parser.get("cd"), FileStorage::READ); + bool readOk = dictionary.readDictionary(fs.root()); + if(!readOk) { + cerr << "Invalid dictionary file" << endl; + return 0; + } + } + else if (outputFile.empty() || nMarkers == 0 || markerSize == 0) { + cerr << "Dictionary not specified" << endl; + return 0; + } + if (!outputFile.empty() && nMarkers > 0 && markerSize > 0) + { + FileStorage fs(outputFile, FileStorage::WRITE); + if (checkFlippedMarkers) + dictionary = generateCustomAsymmetricDictionary(nMarkers, markerSize, aruco::Dictionary(), 0); + else + dictionary = aruco::extendDictionary(nMarkers, markerSize, aruco::Dictionary(), 0); + dictionary.writeDictionary(fs); + } + + if (checkFlippedMarkers) { + cout << "Hamming distance: " << getMinAsymDistForDict(dictionary) << endl; + } + else { + cout << "Hamming distance: " << getMinDistForDict(dictionary) << endl; + } + return 0; +}