test_qrcode_encode.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. #include "test_precomp.hpp"
  5. namespace opencv_test { namespace {
  6. std::string encode_qrcode_images_name[] = {
  7. "version1_mode1.png", "version1_mode2.png", "version1_mode4.png",
  8. "version2_mode1.png", "version2_mode2.png", "version2_mode4.png",
  9. "version3_mode2.png", "version3_mode4.png",
  10. "version4_mode4.png"
  11. };
  12. std::string encode_qrcode_eci_images_name[] = {
  13. "version1_mode7.png",
  14. "version2_mode7.png",
  15. "version3_mode7.png",
  16. "version4_mode7.png",
  17. "version5_mode7.png"
  18. };
  19. const Size fixed_size = Size(200, 200);
  20. const float border_width = 2.0;
  21. int establishCapacity(QRCodeEncoder::EncodeMode mode, int version, int capacity)
  22. {
  23. int result = 0;
  24. capacity *= 8;
  25. capacity -= 4;
  26. switch (mode)
  27. {
  28. case QRCodeEncoder::MODE_NUMERIC:
  29. {
  30. if (version >= 10)
  31. capacity -= 12;
  32. else
  33. capacity -= 10;
  34. int tmp = capacity / 10;
  35. result = tmp * 3;
  36. if (tmp * 10 + 7 <= capacity)
  37. result += 2;
  38. else if (tmp * 10 + 4 <= capacity)
  39. result += 1;
  40. break;
  41. }
  42. case QRCodeEncoder::MODE_ALPHANUMERIC:
  43. {
  44. if (version < 10)
  45. capacity -= 9;
  46. else
  47. capacity -= 13;
  48. int tmp = capacity / 11;
  49. result = tmp * 2;
  50. if (tmp * 11 + 6 <= capacity)
  51. result++;
  52. break;
  53. }
  54. case QRCodeEncoder::MODE_BYTE:
  55. {
  56. if (version > 9)
  57. capacity -= 16;
  58. else
  59. capacity -= 8;
  60. result = capacity / 8;
  61. break;
  62. }
  63. default:
  64. break;
  65. }
  66. return result;
  67. }
  68. // #define UPDATE_TEST_DATA
  69. #ifdef UPDATE_TEST_DATA
  70. TEST(Objdetect_QRCode_Encode, generate_test_data)
  71. {
  72. const std::string root = "qrcode/encode";
  73. const std::string dataset_config = findDataFile(root + "/" + "dataset_config.json");
  74. FileStorage file_config(dataset_config, FileStorage::WRITE);
  75. file_config << "test_images" << "[";
  76. size_t images_count = sizeof(encode_qrcode_images_name) / sizeof(encode_qrcode_images_name[0]);
  77. for (size_t i = 0; i < images_count; i++)
  78. {
  79. file_config << "{:" << "image_name" << encode_qrcode_images_name[i];
  80. std::string image_path = findDataFile(root + "/" + encode_qrcode_images_name[i]);
  81. Mat src = imread(image_path, IMREAD_GRAYSCALE);
  82. Mat straight_barcode;
  83. EXPECT_TRUE(!src.empty()) << "Can't read image: " << image_path;
  84. std::vector<Point2f> corners(4);
  85. corners[0] = Point2f(border_width, border_width);
  86. corners[1] = Point2f(qrcode.cols * 1.0f - border_width, border_width);
  87. corners[2] = Point2f(qrcode.cols * 1.0f - border_width, qrcode.rows * 1.0f - border_width);
  88. corners[3] = Point2f(border_width, qrcode.rows * 1.0f - border_width);
  89. Mat resized_src;
  90. resize(qrcode, resized_src, fixed_size, 0, 0, INTER_AREA);
  91. float width_ratio = resized_src.cols * 1.0f / qrcode.cols;
  92. float height_ratio = resized_src.rows * 1.0f / qrcode.rows;
  93. for(size_t j = 0; j < corners.size(); j++)
  94. {
  95. corners[j].x = corners[j].x * width_ratio;
  96. corners[j].y = corners[j].y * height_ratio;
  97. }
  98. std::string decoded_info = "";
  99. #ifdef HAVE_QUIRC
  100. EXPECT_TRUE(decodeQRCode(resized_src, corners, decoded_info, straight_barcode)) << "The QR code cannot be decoded: " << image_path;
  101. #endif
  102. file_config << "info" << decoded_info;
  103. file_config << "}";
  104. }
  105. file_config << "]";
  106. file_config.release();
  107. }
  108. #else
  109. typedef testing::TestWithParam< std::string > Objdetect_QRCode_Encode;
  110. TEST_P(Objdetect_QRCode_Encode, regression) {
  111. const int pixels_error = 3;
  112. const std::string name_current_image = GetParam();
  113. const std::string root = "qrcode/encode";
  114. std::string image_path = findDataFile(root + "/" + name_current_image);
  115. const std::string dataset_config = findDataFile(root + "/" + "dataset_config.json");
  116. FileStorage file_config(dataset_config, FileStorage::READ);
  117. ASSERT_TRUE(file_config.isOpened()) << "Can't read validation data: " << dataset_config;
  118. {
  119. FileNode images_list = file_config["test_images"];
  120. size_t images_count = static_cast<size_t>(images_list.size());
  121. ASSERT_GT(images_count, 0u) << "Can't find validation data entries in 'test_images': " << dataset_config;
  122. for (size_t index = 0; index < images_count; index++)
  123. {
  124. FileNode config = images_list[(int)index];
  125. std::string name_test_image = config["image_name"];
  126. if (name_test_image == name_current_image)
  127. {
  128. std::string original_info = config["info"];
  129. Ptr<QRCodeEncoder> encoder = QRCodeEncoder::create();
  130. Mat result;
  131. encoder->encode(original_info, result);
  132. EXPECT_FALSE(result.empty()) << "Can't generate QR code image";
  133. Mat src = imread(image_path, IMREAD_GRAYSCALE);
  134. Mat straight_barcode;
  135. EXPECT_TRUE(!src.empty()) << "Can't read image: " << image_path;
  136. double diff_norm = cvtest::norm(result - src, NORM_L1);
  137. EXPECT_NEAR(diff_norm, 0.0, pixels_error) << "The generated QRcode is not same as test data. The difference: " << diff_norm;
  138. return; // done
  139. }
  140. }
  141. FAIL() << "Not found results in config file:" << dataset_config
  142. << "\nRe-run tests with enabled UPDATE_ENCODE_TEST_DATA macro to update test data.";
  143. }
  144. }
  145. typedef testing::TestWithParam< std::string > Objdetect_QRCode_Encode_ECI;
  146. TEST_P(Objdetect_QRCode_Encode_ECI, regression) {
  147. const int pixels_error = 3;
  148. const std::string name_current_image = GetParam();
  149. const std::string root = "qrcode/encode";
  150. std::string image_path = findDataFile(root + "/" + name_current_image);
  151. const std::string dataset_config = findDataFile(root + "/" + "dataset_config.json");
  152. FileStorage file_config(dataset_config, FileStorage::READ);
  153. ASSERT_TRUE(file_config.isOpened()) << "Can't read validation data: " << dataset_config;
  154. {
  155. FileNode images_list = file_config["test_images"];
  156. size_t images_count = static_cast<size_t>(images_list.size());
  157. ASSERT_GT(images_count, 0u) << "Can't find validation data entries in 'test_images': " << dataset_config;
  158. QRCodeEncoder::Params params;
  159. params.mode = QRCodeEncoder::MODE_ECI;
  160. for (size_t index = 0; index < images_count; index++)
  161. {
  162. FileNode config = images_list[(int)index];
  163. std::string name_test_image = config["image_name"];
  164. if (name_test_image == name_current_image)
  165. {
  166. std::string original_info = config["info"];
  167. Mat result;
  168. Ptr<QRCodeEncoder> encoder = QRCodeEncoder::create(params);
  169. encoder->encode(original_info, result);
  170. EXPECT_FALSE(result.empty()) << "Can't generate QR code image";
  171. Mat src = imread(image_path, IMREAD_GRAYSCALE);
  172. Mat straight_barcode;
  173. EXPECT_TRUE(!src.empty()) << "Can't read image: " << image_path;
  174. double diff_norm = cvtest::norm(result - src, NORM_L1);
  175. EXPECT_NEAR(diff_norm, 0.0, pixels_error) << "The generated QRcode is not same as test data. The difference: " << diff_norm;
  176. return; // done
  177. }
  178. }
  179. FAIL() << "Not found results in config file:" << dataset_config
  180. << "\nRe-run tests with enabled UPDATE_ENCODE_TEST_DATA macro to update test data.";
  181. }
  182. }
  183. INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Encode, testing::ValuesIn(encode_qrcode_images_name));
  184. INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Encode_ECI, testing::ValuesIn(encode_qrcode_eci_images_name));
  185. TEST(Objdetect_QRCode_Encode_Decode, regression)
  186. {
  187. const std::string root = "qrcode/decode_encode";
  188. const int min_version = 1;
  189. const int test_max_version = 5;
  190. const int max_ec_level = 3;
  191. const std::string dataset_config = findDataFile(root + "/" + "symbol_sets.json");
  192. const std::string version_config = findDataFile(root + "/" + "capacity.json");
  193. FileStorage file_config(dataset_config, FileStorage::READ);
  194. FileStorage capacity_config(version_config, FileStorage::READ);
  195. ASSERT_TRUE(file_config.isOpened()) << "Can't read validation data: " << dataset_config;
  196. ASSERT_TRUE(capacity_config.isOpened()) << "Can't read validation data: " << version_config;
  197. FileNode mode_list = file_config["symbols_sets"];
  198. FileNode capacity_list = capacity_config["version_ecc_capacity"];
  199. size_t mode_count = static_cast<size_t>(mode_list.size());
  200. ASSERT_GT(mode_count, 0u) << "Can't find validation data entries in 'test_images': " << dataset_config;
  201. const int testing_modes = 3;
  202. QRCodeEncoder::EncodeMode modes[testing_modes] = {
  203. QRCodeEncoder::MODE_NUMERIC,
  204. QRCodeEncoder::MODE_ALPHANUMERIC,
  205. QRCodeEncoder::MODE_BYTE
  206. };
  207. for (int i = 0; i < testing_modes; i++)
  208. {
  209. QRCodeEncoder::EncodeMode mode = modes[i];
  210. FileNode config = mode_list[i];
  211. std::string symbol_set = config["symbols_set"];
  212. for(int version = min_version; version <= test_max_version; version++)
  213. {
  214. FileNode capa_config = capacity_list[version - 1];
  215. for(int level = 0; level <= max_ec_level; level++)
  216. {
  217. const int cur_capacity = capa_config["ecc_level"][level];
  218. int true_capacity = establishCapacity(mode, version, cur_capacity);
  219. std::string input_info = symbol_set;
  220. std::random_shuffle(input_info.begin(),input_info.end());
  221. int count = 0;
  222. if((int)input_info.length() > true_capacity)
  223. {
  224. input_info = input_info.substr(0, true_capacity);
  225. }
  226. else
  227. {
  228. while ((int)input_info.length() != true_capacity)
  229. {
  230. input_info += input_info.substr(count%(int)input_info.length(), 1);
  231. count++;
  232. }
  233. }
  234. QRCodeEncoder::Params params;
  235. params.version = version;
  236. params.correction_level = static_cast<QRCodeEncoder::CorrectionLevel>(level);
  237. params.mode = mode;
  238. Ptr<QRCodeEncoder> encoder = QRCodeEncoder::create(params);
  239. Mat qrcode;
  240. encoder->encode(input_info, qrcode);
  241. EXPECT_TRUE(!qrcode.empty()) << "Can't generate this QR image (" << "mode: " << (int)mode <<
  242. " version: "<< version <<" error correction level: "<< (int)level <<")";
  243. std::vector<Point2f> corners(4);
  244. corners[0] = Point2f(border_width, border_width);
  245. corners[1] = Point2f(qrcode.cols * 1.0f - border_width, border_width);
  246. corners[2] = Point2f(qrcode.cols * 1.0f - border_width, qrcode.rows * 1.0f - border_width);
  247. corners[3] = Point2f(border_width, qrcode.rows * 1.0f - border_width);
  248. Mat resized_src;
  249. resize(qrcode, resized_src, fixed_size, 0, 0, INTER_AREA);
  250. float width_ratio = resized_src.cols * 1.0f / qrcode.cols;
  251. float height_ratio = resized_src.rows * 1.0f / qrcode.rows;
  252. for(size_t k = 0; k < corners.size(); k++)
  253. {
  254. corners[k].x = corners[k].x * width_ratio;
  255. corners[k].y = corners[k].y * height_ratio;
  256. }
  257. #ifdef HAVE_QUIRC
  258. Mat straight_barcode;
  259. std::string output_info = QRCodeDetector().decode(resized_src, corners, straight_barcode);
  260. EXPECT_FALSE(output_info.empty())
  261. << "The generated QRcode cannot be decoded." << " Mode: " << (int)mode
  262. << " version: " << version << " error correction level: " << (int)level;
  263. EXPECT_EQ(input_info, output_info) << "The generated QRcode is not same as test data." << " Mode: " << (int)mode <<
  264. " version: " << version << " error correction level: " << (int)level;
  265. #endif
  266. }
  267. }
  268. }
  269. }
  270. TEST(Objdetect_QRCode_Encode_Kanji, regression)
  271. {
  272. QRCodeEncoder::Params params;
  273. params.mode = QRCodeEncoder::MODE_KANJI;
  274. Mat qrcode;
  275. const int testing_versions = 3;
  276. std::string input_infos[testing_versions] = {"\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x90\xa2\x8a\x45", // こんにちは世界
  277. "\x82\xa8\x95\xa0\x82\xaa\x8b\xf3\x82\xa2\x82\xc4\x82\xa2\x82\xdc\x82\xb7", // お腹が空いています
  278. "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x81\x41\x8e\x84\x82\xcd\x8f\xad\x82\xb5\x93\xfa\x96\x7b\x8c\xea\x82\xf0\x98\x62\x82\xb5\x82\xdc\x82\xb7" // こんにちは、私は少し日本語を話します
  279. };
  280. for (int i = 0; i < testing_versions; i++)
  281. {
  282. std::string input_info = input_infos[i];
  283. Ptr<QRCodeEncoder> encoder = QRCodeEncoder::create(params);
  284. encoder->encode(input_info, qrcode);
  285. std::vector<Point2f> corners(4);
  286. corners[0] = Point2f(border_width, border_width);
  287. corners[1] = Point2f(qrcode.cols * 1.0f - border_width, border_width);
  288. corners[2] = Point2f(qrcode.cols * 1.0f - border_width, qrcode.rows * 1.0f - border_width);
  289. corners[3] = Point2f(border_width, qrcode.rows * 1.0f - border_width);
  290. Mat resized_src;
  291. resize(qrcode, resized_src, fixed_size, 0, 0, INTER_AREA);
  292. float width_ratio = resized_src.cols * 1.0f / qrcode.cols;
  293. float height_ratio = resized_src.rows * 1.0f / qrcode.rows;
  294. for(size_t j = 0; j < corners.size(); j++)
  295. {
  296. corners[j].x = corners[j].x * width_ratio;
  297. corners[j].y = corners[j].y * height_ratio;
  298. }
  299. #ifdef HAVE_QUIRC
  300. Mat straight_barcode;
  301. std::string decoded_info = QRCodeDetector().decode(resized_src, corners, straight_barcode);
  302. EXPECT_FALSE(decoded_info.empty()) << "The generated QRcode cannot be decoded.";
  303. EXPECT_EQ(input_info, decoded_info);
  304. #endif
  305. }
  306. }
  307. TEST(Objdetect_QRCode_Encode_Decode_Structured_Append, DISABLED_regression)
  308. {
  309. // disabled since QR decoder probably doesn't support structured append mode qr codes
  310. const std::string root = "qrcode/decode_encode";
  311. const std::string dataset_config = findDataFile(root + "/" + "symbol_sets.json");
  312. const std::string version_config = findDataFile(root + "/" + "capacity.json");
  313. FileStorage file_config(dataset_config, FileStorage::READ);
  314. ASSERT_TRUE(file_config.isOpened()) << "Can't read validation data: " << dataset_config;
  315. FileNode mode_list = file_config["symbols_sets"];
  316. size_t mode_count = static_cast<size_t>(mode_list.size());
  317. ASSERT_GT(mode_count, 0u) << "Can't find validation data entries in 'test_images': " << dataset_config;
  318. int modes[] = {1, 2, 4};
  319. const int min_stuctures_num = 2;
  320. const int max_stuctures_num = 5;
  321. for (int i = 0; i < 3; i++)
  322. {
  323. int mode = modes[i];
  324. FileNode config = mode_list[i];
  325. std::string symbol_set = config["symbols_set"];
  326. std::string input_info = symbol_set;
  327. std::random_shuffle(input_info.begin(), input_info.end());
  328. for (int j = min_stuctures_num; j < max_stuctures_num; j++)
  329. {
  330. QRCodeEncoder::Params params;
  331. params.structure_number = j;
  332. Ptr<QRCodeEncoder> encoder = QRCodeEncoder::create(params);
  333. vector<Mat> qrcodes;
  334. encoder->encodeStructuredAppend(input_info, qrcodes);
  335. EXPECT_TRUE(!qrcodes.empty()) << "Can't generate this QR images";
  336. std::string output_info = "";
  337. for (size_t k = 0; k < qrcodes.size(); k++)
  338. {
  339. Mat qrcode = qrcodes[k];
  340. std::vector<Point2f> corners(4);
  341. corners[0] = Point2f(border_width, border_width);
  342. corners[1] = Point2f(qrcode.cols * 1.0f - border_width, border_width);
  343. corners[2] = Point2f(qrcode.cols * 1.0f - border_width, qrcode.rows * 1.0f - border_width);
  344. corners[3] = Point2f(border_width, qrcode.rows * 1.0f - border_width);
  345. Mat resized_src;
  346. resize(qrcode, resized_src, fixed_size, 0, 0, INTER_AREA);
  347. float width_ratio = resized_src.cols * 1.0f / qrcode.cols;
  348. float height_ratio = resized_src.rows * 1.0f / qrcode.rows;
  349. for(size_t m = 0; m < corners.size(); m++)
  350. {
  351. corners[m].x = corners[m].x * width_ratio;
  352. corners[m].y = corners[m].y * height_ratio;
  353. }
  354. #ifdef HAVE_QUIRC
  355. Mat straight_barcode;
  356. std::string decoded_info = QRCodeDetector().decode(resized_src, corners, straight_barcode);
  357. EXPECT_FALSE(decoded_info.empty())
  358. << "The generated QRcode cannot be decoded." << " Mode: " << modes[i]
  359. << " structures number: " << k << "/" << j;
  360. output_info += decoded_info;
  361. #endif
  362. }
  363. #ifdef HAVE_QUIRC
  364. EXPECT_EQ(input_info, output_info) << "The generated QRcode is not same as test data." << " Mode: " << mode <<
  365. " structures number: " << j;
  366. #else
  367. std::cout << "Mode=" << mode << ": Unable to verify generated QR codes - QUIRC is disabled" << std::endl;
  368. #endif
  369. }
  370. }
  371. }
  372. #endif // UPDATE_QRCODE_TEST_DATA
  373. }} // namespace