当前位置:   article > 正文

【opencv】示例-aruco_dict_utils.cpp 计算 ArUco 字典度量

【opencv】示例-aruco_dict_utils.cpp 计算 ArUco 字典度量

4016747f97a8a4fca15b65323d64f7e6.png

该程序可用于计算 ArUco 字典度量。

要计算考虑翻转标记的指标,请使用 -'r' 标志。

该程序可用于创建和编写自定义 ArUco 词典。

12c62f192220aefee57f8be0b298a5cf.png

1ebaa80dcafb8732a4bbb5f6135590af.png

  1. #include <opencv2/objdetect/aruco_detector.hpp> // 包含aruco marker检测相关功能的头文件
  2. #include <iostream> // 包含输入输出流相关功能的头文件
  3. using namespace cv; // 使用命名空间cv,这样我们就可以直接使用OpenCV的函数和类而不需要加cv::前缀
  4. using namespace std; // 使用命名空间std,标准的C++库函数比如std::cout可以直接写成cout
  5. // 下面是静态函数的定义,因为我们不需要实例化对象就可以直接用类名调用它们
  6. //为了确保ArUco标记的独一无二,这个函数计算了一个标记的自身最小汉明距离(即标记的不同旋转形态之间的最小差异度量),这有助于保证即使在不同的旋转下,标记也能被准确地检测和识别。
  7. static int _getSelfDistance(const Mat &marker) {
  8. // 计算单个marker自身的汉明距离(marker之间的差异程度)
  9. Mat bytes = aruco::Dictionary::getByteListFromBits(marker); // 将marker的位图转换为字节列表
  10. double minHamming = (double)marker.total() + 1; // 初始化最小汉明距离为marker的总数+1
  11. for(int r = 1; r < 4; r++) { // 对每个旋转的标记进行遍历(共有四种旋转,不包括未旋转)
  12. // 创建两个临时的字节行,用于存储转换后的字节信息
  13. cv::Mat tmp1(1, bytes.cols, CV_8UC1, Scalar::all(0));
  14. cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0));
  15. uchar* rot0 = tmp1.ptr(); // 获取行tmp1的指针
  16. uchar* rot1 = tmp2.ptr(); // 获取行tmp2的指针
  17. for (int i = 0; i < bytes.cols; ++i) { // 将marker的字节旋转r*90度并拷贝到临时字节行中
  18. rot0[i] = bytes.ptr()[i];
  19. rot1[i] = bytes.ptr()[bytes.cols*r + i];
  20. }
  21. // 计算两个临时行的汉明距离
  22. double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
  23. // 更新最小汉明距离
  24. if (currentHamming < minHamming) minHamming = currentHamming;
  25. }
  26. // 对marker进行水平翻转和垂直翻转后的处理逻辑与上面类似,不再重复。
  27. // 在marker的所有可能的变换后,返回最小的汉明距离
  28. // 检查水平翻转后的情况
  29. Mat b; // 定义一个矩阵用于存储翻转的结果
  30. flip(marker, b, 0); // 对marker进行水平翻转
  31. Mat flipBytes = aruco::Dictionary::getByteListFromBits(b); // 获取翻转后marker的字节列表
  32. // 对翻转后的marker进行相似度检查,逻辑与上面类似
  33. for(int r = 0; r < 4; r++) {
  34. // ... 代码逻辑与上面类似 ...
  35. cv::Mat tmp1(1, flipBytes.cols, CV_8UC1, Scalar::all(0));
  36. cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0));
  37. uchar* rot0 = tmp1.ptr();
  38. uchar* rot1 = tmp2.ptr();
  39. for (int i = 0; i < bytes.cols; ++i) {
  40. rot0[i] = flipBytes.ptr()[i];
  41. rot1[i] = bytes.ptr()[bytes.cols*r + i];
  42. }
  43. double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
  44. if(currentHamming < minHamming) minHamming = currentHamming;
  45. }
  46. // 检查垂直翻转后的情况
  47. flip(marker, b, 1); // 对marker进行垂直翻转
  48. flipBytes = aruco::Dictionary::getByteListFromBits(b); // 获取翻转后marker的字节列表
  49. // 对垂直翻转后的marker进行相似度检查,逻辑与上面类似
  50. for(int r = 0; r < 4; r++) {
  51. // ... 代码逻辑与上面类似 ...
  52. cv::Mat tmp1(1, flipBytes.cols, CV_8UC1, Scalar::all(0));
  53. cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0));
  54. uchar* rot0 = tmp1.ptr();
  55. uchar* rot1 = tmp2.ptr();
  56. for (int i = 0; i < bytes.cols; ++i) {
  57. rot0[i] = flipBytes.ptr()[i];
  58. rot1[i] = bytes.ptr()[bytes.cols*r + i];
  59. }
  60. double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
  61. if(currentHamming < minHamming) minHamming = currentHamming;
  62. }
  63. // 返回最小汉明距离的四舍五入结果
  64. return cvRound(minHamming);
  65. }
  66. //计算给定字典中,某个ArUco标记与特定ID的其他标记之间在全部或部分旋转的情况下的最小汉明距离。这个函数可以用于评估字典中标记的独一无二性,以及是否可以准确识别翻转后的标记。
  67. static inline int getFlipDistanceToId(const aruco::Dictionary& dict, InputArray bits, int id, bool allRotations = true) {
  68. // 根据给定的ID计算字典中一个标记与其余标记的汉明距离,包括考虑标记的翻转
  69. Mat bytesList = dict.bytesList; // 获取字典中所有标记的字节列表
  70. CV_Assert(id >= 0 && id < bytesList.rows); // 检查输入的ID是否有效
  71. unsigned int nRotations = 4; // 默认情况下,考虑所有4个旋转
  72. if(!allRotations) nRotations = 1; // 如果不考虑旋转,只需要计算未旋转的情况
  73. Mat candidateBytes = aruco::Dictionary::getByteListFromBits(bits.getMat()); // 获取候选标记的字节列表
  74. double currentMinDistance = int(bits.total() * bits.total()); // 初始化当前的最小汉明距离
  75. for(unsigned int r = 0; r < nRotations; r++) { // 遍历所有的旋转(可能包括未旋转)
  76. // 创建两个临时字节行
  77. cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
  78. cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
  79. uchar* rot0 = tmp1.ptr(); // 行tmp1的指针
  80. uchar* rot1 = tmp2.ptr(); // 行tmp2的指针
  81. for (int i = 0; i < candidateBytes.cols; ++i) { // 将字典中的标记旋转后,与候选标记的字节进行比较
  82. rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i];
  83. rot1[i] = candidateBytes.ptr()[i];
  84. }
  85. // 计算当前的汉明距离
  86. double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
  87. // 更新最小汉明距离
  88. if(currentHamming < currentMinDistance) {
  89. currentMinDistance = currentHamming;
  90. }
  91. }
  92. // 对候选标记进行水平翻转和垂直翻转后的处理逻辑与上面类似,不再重复。
  93. // 返回分配给字典中特定ID的汉明距离
  94. Mat b; // 定义一个Mat对象b用于存储翻转后的图像
  95. flip(bits.getMat(), b, 0); // 将输入的图像bits沿着水平轴翻转并存入b
  96. candidateBytes = aruco::Dictionary::getByteListFromBits(b); // 将翻转后的图像b转换为字节列表
  97. for(unsigned int r = 0; r < nRotations; r++) { // 循环遍历每种旋转状态
  98. cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); // 创建一个用于储存旋转的字节的临时Mat对象tmp1
  99. cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0)); // 创建一个用于储存原始字节的临时Mat对象tmp2
  100. uchar* rot0 = tmp1.ptr(); // 获取tmp1的指针
  101. uchar* rot1 = tmp2.ptr(); // 获取tmp2的指针
  102. for (int i = 0; i < candidateBytes.cols; ++i) { // 循环遍历字节的每一列
  103. rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i]; // 读取固定ID的翻转状态的字节
  104. rot1[i] = candidateBytes.ptr()[i]; // 从候选字节中读取对应列的字节
  105. }
  106. double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING); // 计算tmp1和tmp2之间的汉明距离
  107. if (currentHamming < currentMinDistance) { // 如果当前汉明距离小于当前记录的最小距离
  108. currentMinDistance = currentHamming; // 更新最小汉明距离
  109. }
  110. }
  111. flip(bits.getMat(), b, 1); // 将输入的图像bits沿着垂直轴翻转并存入b
  112. candidateBytes = aruco::Dictionary::getByteListFromBits(b); // 将翻转后的图像b转换为字节列表
  113. for(unsigned int r = 0; r < nRotations; r++) { // 循环遍历每种旋转状态,逻辑与上述相同
  114. // 对翻转的图像执行与上面相同的操作,检查汉明距离,并更新最小值
  115. // ... (代码逻辑与上面相同,未显示) ...
  116. cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
  117. cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
  118. uchar* rot0 = tmp1.ptr();
  119. uchar* rot1 = tmp2.ptr();
  120. for (int i = 0; i < candidateBytes.cols; ++i) {
  121. rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i];
  122. rot1[i] = candidateBytes.ptr()[i];
  123. }
  124. double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
  125. if (currentHamming < currentMinDistance) {
  126. currentMinDistance = currentHamming;
  127. }
  128. }
  129.     return cvRound(currentMinDistance);
  130. }
  131. // 以下函数用于生成定制的非对称ArUco字典
  132. static inline aruco::Dictionary generateCustomAsymmetricDictionary(int nMarkers, int markerSize,
  133. const aruco::Dictionary &baseDictionary,
  134. int randomSeed) {
  135. // 定义一个静态内联函数,用于生成定制的非对称的ArUco标记字典
  136. RNG rng((uint64)(randomSeed)); // 基于随机种子初始化一个随机数生成器
  137. aruco::Dictionary out; // 创建一个空的ArUco字典用于输出
  138. out.markerSize = markerSize; // 设置输出字典中标记的大小
  139. // 理论上最大的标记间汉明距离
  140. // 论文参考:S. Garrido-Jurado, et al., 2014. "Automatic generation and detection of highly reliable fiducial markers under occlusion".
  141. int C = (int)std::floor(float(markerSize * markerSize) / 4.f); // 计算理论最大标记间距
  142. int tau = 2 * (int)std::floor(float(C) * 4.f / 3.f); // 计算arity的临界值
  143. // 如果提供了基础字典,计算它的标记间距
  144. if(baseDictionary.bytesList.rows > 0) {
  145. CV_Assert(baseDictionary.markerSize == markerSize); // 确认基础字典的尺寸匹配
  146. out.bytesList = baseDictionary.bytesList.clone(); // 克隆基础字典的字节列表
  147. int minDistance = markerSize * markerSize + 1; // 初始化最小距离
  148. for(int i = 0; i < out.bytesList.rows; i++) { // 遍历基础字典所有标记
  149. Mat markerBytes = out.bytesList.rowRange(i, i + 1); // 获取当前标记的字节行
  150. Mat markerBits = aruco::Dictionary::getBitsFromByteList(markerBytes, markerSize); // 将当前标记的字节转换成位矩阵
  151. minDistance = min(minDistance, _getSelfDistance(markerBits)); // 更新最短汉明距离
  152. for(int j = i + 1; j < out.bytesList.rows; j++) { // 计算当前标记与其他标记的距离
  153. minDistance = min(minDistance, getFlipDistanceToId(out, markerBits, j)); // 更新最短汉明距离
  154. }
  155. }
  156. tau = minDistance; // 更新临界值tau
  157. }
  158. // 当前最佳的选项
  159. int bestTau = 0; // 初始化最佳tau
  160. Mat bestMarker; // 存储当前最佳标记
  161. // 经过指定次数未产生结果的迭代后,接受最佳选项
  162. const int maxUnproductiveIterations = 5000; // 最大无产出迭代次数
  163. int unproductiveIterations = 0; // 无产出迭代计数
  164. while(out.bytesList.rows < nMarkers) { // 当生成的标记数量还未满足要求时
  165. Mat currentMarker(markerSize, markerSize, CV_8UC1, Scalar::all(0)); // 创建一个新的空标记
  166. rng.fill(currentMarker, RNG::UNIFORM, 0, 2); // 使用随机数填充当前标记
  167. int selfDistance = _getSelfDistance(currentMarker); // 计算当前标记的自汉明距离
  168. int minDistance = selfDistance; // 将其设置为最小距离参考值
  169. // 如果自汉明距离大于或等于当前最佳,计算与之前接受的标记之间的距离
  170. if(selfDistance >= bestTau) {
  171. for(int i = 0; i < out.bytesList.rows; i++) { // 遍历之前接受的所有标记
  172. int currentDistance = getFlipDistanceToId(out, currentMarker, i); // 计算距离
  173. minDistance = min(currentDistance, minDistance); // 更新最小距离
  174. // 如果最小距离小于或等于最佳tau,则跳出循环
  175. if(minDistance <= bestTau) {
  176. break;
  177. }
  178. }
  179. }
  180. // 如果距离足够大,接受当前标记
  181. // 如果距离足够大,则接受当前标记
  182. if(minDistance >= tau) { // 如果最小汉明距离大于等于预定的阈值tau
  183. unproductiveIterations = 0; // 重置无产出迭代计数器
  184. bestTau = 0; // 重置最好的tau值
  185. Mat bytes = aruco::Dictionary::getByteListFromBits(currentMarker); // 获取当前marker的字节列表
  186. out.bytesList.push_back(bytes); // 将当前marker添加到输出字典的字节列表中
  187. } else {
  188. // 如果距离不够大,则进入下面的流程
  189. unproductiveIterations++; // 无产出迭代计数器加一
  190. // 如果距离尚不够大,但比当前最佳选择要好
  191. if(minDistance > bestTau) { // 如果最小汉明距离大于当前最佳的tau值
  192. bestTau = minDistance; // 更新最好的tau值为当前的最小距离
  193. bestMarker = currentMarker; // 更新最好的marker为当前的marker
  194. }
  195. // 如果达到了无产出迭代的设定极限,接受当前最佳选择
  196. if(unproductiveIterations == maxUnproductiveIterations) { // 如果无产出迭代计数等于最大无产出迭代次数
  197. unproductiveIterations = 0; // 重置无产出迭代计数器
  198. tau = bestTau; // 设置tau为当前最好的tau值
  199. bestTau = 0; // 重置最好的tau值
  200. Mat bytes = aruco::Dictionary::getByteListFromBits(bestMarker); // 获取当前最佳marker的字节列表
  201. out.bytesList.push_back(bytes); // 将最佳marker添加到输出字典的字节列表中
  202. }
  203. }
  204. }
  205. // 更新生成字典的最大误差修正位数
  206. out.maxCorrectionBits = (tau - 1) / 2; // 计算并设置最大纠错位数
  207. return out; // 返回生成的自定义字典
  208. }
  209. // 以下函数用于获取字典中的最小汉明距离
  210. static inline int getMinDistForDict(const aruco::Dictionary& dict) {
  211. // 定义一个静态内联函数,用于计算ArUco字典中所有标记的最小汉明距离
  212. const int dict_size = dict.bytesList.rows; // 获取字典中标记的数量
  213. const int marker_size = dict.markerSize; // 获取字典中标记的尺寸
  214. int minDist = marker_size * marker_size; // 初始化最小距离为标记尺寸的平方
  215. // 双重循环遍历每一对标记
  216. for (int i = 0; i < dict_size; i++) {
  217. Mat row = dict.bytesList.row(i); // 获取第i个标记的字节行
  218. Mat marker = dict.getBitsFromByteList(row, marker_size); // 将字节行转换为二进制位矩阵
  219. for (int j = 0; j < dict_size; j++) {
  220. // 确保不与自身比较
  221. if (j != i) {
  222. // 获取第i个标记与第j个标记的汉明距离,并更新最小距离
  223. minDist = min(dict.getDistanceToId(marker, j), minDist);
  224. }
  225. }
  226. }
  227. return minDist; // 返回最小距离
  228. }
  229. // 以下函数用于获取字典在考虑翻转marker的情况下的最小汉明距离
  230. static inline int getMinAsymDistForDict(const aruco::Dictionary& dict) {
  231. // 定义一个静态内联函数,用于计算考虑翻转的情况下ArUco字典中所有标记的最小汉明距离
  232. const int dict_size = dict.bytesList.rows; // 获取字典中标记的数量
  233. const int marker_size = dict.markerSize; // 获取字典中标记的尺寸
  234. int minDist = marker_size * marker_size; // 初始化最小距离为标记尺寸的平方
  235. // 双重循环遍历每一对标记
  236. for (int i = 0; i < dict_size; i++)
  237. {
  238. Mat row = dict.bytesList.row(i); // 获取第i个标记的字节行
  239. Mat marker = dict.getBitsFromByteList(row, marker_size); // 将字节行转换为二进制位矩阵
  240. for (int j = 0; j < dict_size; j++)
  241. {
  242. if (j != i) // 确保不与自身比较
  243. {
  244. // 获取考虑翻转的情况下第i个标记与第j个标记的汉明距离,并更新最小距离
  245. minDist = min(getFlipDistanceToId(dict, marker, j), minDist);
  246. }
  247. }
  248. }
  249. return minDist; // 返回考虑翻转的最小距离
  250. }
  251. // 命令行参数定义字符串
  252. const char* keys =
  253. "{@outfile |<none> | Output file with custom dict }" // 输出文件参数,输出自定义字典到该文件
  254. "{r | false | Calculate the metric considering flipped markers }" // 计算考虑翻转标记的度量标准
  255. "{d | | Dictionary Name: ...}" // 字典名称参数
  256. "{nMarkers | | Number of markers in the dictionary }" // 字典中标记的数量参数
  257. "{markerSize | | Marker size }" // 标记大小参数
  258. "{cd | | Input file with custom dictionary }"; // 自定义字典的输入文件参数
  259. // 程序简介
  260. const char* about =
  261. "This program can be used to calculate the ArUco dictionary metric.\n"
  262. "To calculate the metric considering flipped markers use -'r' flag.\n"
  263. "This program can be used to create and write the custom ArUco dictionary.\n";
  264. int main(int argc, char *argv[])
  265. {
  266. CommandLineParser parser(argc, argv, keys); // 初始化命令行解析器
  267. parser.about(about); // 设置关于程序的信息
  268. if(argc < 2) {
  269. parser.printMessage(); // 如果参数数量不够,打印帮助信息
  270. return 0;
  271. }
  272. // 从解析器中取得命令行参数
  273. string outputFile = parser.get<String>(0); // 获取输出文件名
  274. int nMarkers = parser.get<int>("nMarkers"); // 获取标记数量
  275. int markerSize = parser.get<int>("markerSize"); // 获取标记大小
  276. bool checkFlippedMarkers = parser.get<bool>("r"); // 获取是否检查翻转标记的标志
  277. // 创建一个默认的ArUco字典
  278. aruco::Dictionary dictionary = aruco::getPredefinedDictionary(0);
  279. if (parser.has("d")) {
  280. // 如果提供了字典名称参数,获取相应的预定义字典
  281. string arucoDictName = parser.get<string>("d"); // 获取字典名称
  282. cv::aruco::PredefinedDictionaryType arucoDict; // 定义预定义字典类型变量
  283. // 根据提供的字典名称设置预定义字典类型变量
  284.         if (arucoDictName == "DICT_4X4_50") { arucoDict = cv::aruco::DICT_4X4_50; } // 如果字典名称是"DICT_4X4_50"
  285. else if (arucoDictName == "DICT_4X4_100") { arucoDict = cv::aruco::DICT_4X4_100; } // 如果字典名称是"DICT_4X4_100"
  286. else if (arucoDictName == "DICT_4X4_250") { arucoDict = cv::aruco::DICT_4X4_250; } // 如果字典名称是"DICT_4X4_250"
  287. else if (arucoDictName == "DICT_4X4_1000") { arucoDict = cv::aruco::DICT_4X4_1000; } // 如果字典名称是"DICT_4X4_1000"
  288. else if (arucoDictName == "DICT_5X5_50") { arucoDict = cv::aruco::DICT_5X5_50; } // 如果字典名称是"DICT_5X5_50"
  289. else if (arucoDictName == "DICT_5X5_100") { arucoDict = cv::aruco::DICT_5X5_100; } // 如果字典名称是"DICT_5X5_100"
  290. else if (arucoDictName == "DICT_5X5_250") { arucoDict = cv::aruco::DICT_5X5_250; } // 如果字典名称是"DICT_5X5_250"
  291. else if (arucoDictName == "DICT_5X5_1000") { arucoDict = cv::aruco::DICT_5X5_1000; } // 如果字典名称是"DICT_5X5_1000"
  292. else if (arucoDictName == "DICT_6X6_50") { arucoDict = cv::aruco::DICT_6X6_50; } // 如果字典名称是"DICT_6X6_50"
  293. else if (arucoDictName == "DICT_6X6_100") { arucoDict = cv::aruco::DICT_6X6_100; } // 如果字典名称是"DICT_6X6_100"
  294. else if (arucoDictName == "DICT_6X6_250") { arucoDict = cv::aruco::DICT_6X6_250; } // 如果字典名称是"DICT_6X6_250"
  295. else if (arucoDictName == "DICT_6X6_1000") { arucoDict = cv::aruco::DICT_6X6_1000; } // 如果字典名称是"DICT_6X6_1000"
  296. else if (arucoDictName == "DICT_7X7_50") { arucoDict = cv::aruco::DICT_7X7_50; } // 如果字典名称是"DICT_7X7_50"
  297. else if (arucoDictName == "DICT_7X7_100") { arucoDict = cv::aruco::DICT_7X7_100; } // 如果字典名称是"DICT_7X7_100"
  298. else if (arucoDictName == "DICT_7X7_250") { arucoDict = cv::aruco::DICT_7X7_250; } // 如果字典名称是"DICT_7X7_250"
  299. else if (arucoDictName == "DICT_7X7_1000") { arucoDict = cv::aruco::DICT_7X7_1000; } // 如果字典名称是"DICT_7X7_1000"
  300. else if (arucoDictName == "DICT_ARUCO_ORIGINAL") { arucoDict = cv::aruco::DICT_ARUCO_ORIGINAL; } // 如果字典名称是"DICT_ARUCO_ORIGINAL"
  301. else if (arucoDictName == "DICT_APRILTAG_16h5") { arucoDict = cv::aruco::DICT_APRILTAG_16h5; } // 如果字典名称是"DICT_APRILTAG_16h5"
  302. else if (arucoDictName == "DICT_APRILTAG_25h9") { arucoDict = cv::aruco::DICT_APRILTAG_25h9; } // 如果字典名称是"DICT_APRILTAG_25h9"
  303. else if (arucoDictName == "DICT_APRILTAG_36h10") { arucoDict = cv::aruco::DICT_APRILTAG_36h10; } // 如果字典名称是"DICT_APRILTAG_36h10"
  304. else if (arucoDictName == "DICT_APRILTAG_36h11") { arucoDict = cv::aruco::DICT_APRILTAG_36h11; } // 如果字典名称是"DICT_APRILTAG_36h11"
  305. else {
  306. cout << "incorrect name of aruco dictionary \n"; // 如果没有匹配的字典名称,打印错误信息
  307. return 1; // 返回非零值表示错误
  308. }
  309. dictionary = aruco::getPredefinedDictionary(arucoDict); // 从预定义字典类型获取预定义字典
  310. }
  311. else if (parser.has("cd")) {
  312. // 如果提供了自定义字典文件参数,读取自定义字典
  313. FileStorage fs(parser.get<std::string>("cd"), FileStorage::READ);
  314. bool readOk = dictionary.readDictionary(fs.root());
  315. if(!readOk) {
  316. cerr << "Invalid dictionary file" << endl; // 如果读取失败,打印错误信息
  317. return 0;
  318. }
  319. }
  320. else if (outputFile.empty() || nMarkers == 0 || markerSize == 0) {
  321. cerr << "Dictionary not specified" << endl; // 如果必要的参数没有提供,打印错误信息
  322. return 0;
  323. }
  324. if (!outputFile.empty() && nMarkers > 0 && markerSize > 0)
  325. {
  326. // 如果提供了所需要的参数,则生成自定义字典并写入文件
  327. FileStorage fs(outputFile, FileStorage::WRITE);
  328. if (checkFlippedMarkers)
  329. dictionary = generateCustomAsymmetricDictionary(nMarkers, markerSize, aruco::Dictionary(), 0); // 生成考虑翻转的自定义字典
  330. else
  331. dictionary = aruco::extendDictionary(nMarkers, markerSize, aruco::Dictionary(), 0); // 扩展字典
  332. dictionary.writeDictionary(fs); // 写入字典到文件
  333. }
  334. // 计算并打印字典的最小汉明距离
  335. if (checkFlippedMarkers) {
  336. cout << "Hamming distance: " << getMinAsymDistForDict(dictionary) << endl; // 考虑翻转的最小汉明距离
  337. }
  338. else {
  339. cout << "Hamming distance: " << getMinDistForDict(dictionary) << endl; // 不考虑翻转的最小汉明距离
  340. }
  341. return 0; // 程序结束
  342. }

a95473030047b144a27dd2c7dc85f85a.png

  1. PS V:\learn\opencv\github\opencv\sources\test\x64\Debug> ./test.exe @outfile=custom_dict.yml -nMarkers=50 -markerSize=6
  2. Hamming distance: 12

c18005d654991924cef7f3497dcf74f5.png

@outfile=custom_dict.yml

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/380044
推荐阅读
相关标签
  

闽ICP备14008679号