intfeaturemap_test.cc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // (C) Copyright 2017, Google Inc.
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. // http://www.apache.org/licenses/LICENSE-2.0
  6. // Unless required by applicable law or agreed to in writing, software
  7. // distributed under the License is distributed on an "AS IS" BASIS,
  8. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. // See the License for the specific language governing permissions and
  10. // limitations under the License.
  11. #include "intfeaturemap.h"
  12. #include "intfeaturespace.h"
  13. #include "include_gunit.h"
  14. // Random re-quantization to test that they don't have to be easy.
  15. // WARNING! Change these and change the expected_misses calculation below.
  16. const int kXBuckets = 16;
  17. const int kYBuckets = 24;
  18. const int kThetaBuckets = 13;
  19. namespace tesseract {
  20. class IntFeatureMapTest : public testing::Test {
  21. protected:
  22. void SetUp() override {
  23. std::locale::global(std::locale(""));
  24. }
  25. public:
  26. // Expects that the given vector has contiguous integer values in the
  27. // range [start, end).
  28. void ExpectContiguous(const std::vector<int> &v, int start, int end) {
  29. for (int i = start; i < end; ++i) {
  30. EXPECT_EQ(i, v[i - start]);
  31. }
  32. }
  33. };
  34. // Tests the IntFeatureMap and implicitly the IntFeatureSpace underneath.
  35. TEST_F(IntFeatureMapTest, Exhaustive) {
  36. #ifdef DISABLED_LEGACY_ENGINE
  37. // Skip test because IntFeatureSpace is missing.
  38. GTEST_SKIP();
  39. #else
  40. IntFeatureSpace space;
  41. space.Init(kXBuckets, kYBuckets, kThetaBuckets);
  42. IntFeatureMap map;
  43. map.Init(space);
  44. int total_size = kIntFeatureExtent * kIntFeatureExtent * kIntFeatureExtent;
  45. auto features = std::make_unique<INT_FEATURE_STRUCT[]>(total_size);
  46. // Fill the features with every value.
  47. for (int y = 0; y < kIntFeatureExtent; ++y) {
  48. for (int x = 0; x < kIntFeatureExtent; ++x) {
  49. for (int theta = 0; theta < kIntFeatureExtent; ++theta) {
  50. int f_index = (y * kIntFeatureExtent + x) * kIntFeatureExtent + theta;
  51. features[f_index].X = x;
  52. features[f_index].Y = y;
  53. features[f_index].Theta = theta;
  54. }
  55. }
  56. }
  57. std::vector<int> index_features;
  58. map.IndexAndSortFeatures(features.get(), total_size, &index_features);
  59. EXPECT_EQ(total_size, index_features.size());
  60. int total_buckets = kXBuckets * kYBuckets * kThetaBuckets;
  61. std::vector<int> map_features;
  62. int misses = map.MapIndexedFeatures(index_features, &map_features);
  63. EXPECT_EQ(0, misses);
  64. EXPECT_EQ(total_buckets, map_features.size());
  65. ExpectContiguous(map_features, 0, total_buckets);
  66. EXPECT_EQ(total_buckets, map.compact_size());
  67. EXPECT_EQ(total_buckets, map.sparse_size());
  68. // Every offset should be within dx, dy, dtheta of the start point.
  69. int dx = kIntFeatureExtent / kXBuckets + 1;
  70. int dy = kIntFeatureExtent / kYBuckets + 1;
  71. int dtheta = kIntFeatureExtent / kThetaBuckets + 1;
  72. int bad_offsets = 0;
  73. for (int index = 0; index < total_buckets; ++index) {
  74. for (int dir = -tesseract::kNumOffsetMaps; dir <= tesseract::kNumOffsetMaps; ++dir) {
  75. int offset_index = map.OffsetFeature(index, dir);
  76. if (dir == 0) {
  77. EXPECT_EQ(index, offset_index);
  78. } else if (offset_index >= 0) {
  79. INT_FEATURE_STRUCT f = map.InverseIndexFeature(index);
  80. INT_FEATURE_STRUCT f2 = map.InverseIndexFeature(offset_index);
  81. EXPECT_TRUE(f.X != f2.X || f.Y != f2.Y || f.Theta != f2.Theta);
  82. EXPECT_LE(abs(f.X - f2.X), dx);
  83. EXPECT_LE(abs(f.Y - f2.Y), dy);
  84. int theta_delta = abs(f.Theta - f2.Theta);
  85. if (theta_delta > kIntFeatureExtent / 2) {
  86. theta_delta = kIntFeatureExtent - theta_delta;
  87. }
  88. EXPECT_LE(theta_delta, dtheta);
  89. } else {
  90. ++bad_offsets;
  91. INT_FEATURE_STRUCT f = map.InverseIndexFeature(index);
  92. }
  93. }
  94. }
  95. EXPECT_LE(bad_offsets, (kXBuckets + kYBuckets) * kThetaBuckets);
  96. // To test the mapping further, delete the 1st and last map feature, and
  97. // test again.
  98. map.DeleteMapFeature(0);
  99. map.DeleteMapFeature(total_buckets - 1);
  100. map.FinalizeMapping(nullptr);
  101. map.IndexAndSortFeatures(features.get(), total_size, &index_features);
  102. // Has no effect on index features.
  103. EXPECT_EQ(total_size, index_features.size());
  104. misses = map.MapIndexedFeatures(index_features, &map_features);
  105. int expected_misses = (kIntFeatureExtent / kXBuckets) * (kIntFeatureExtent / kYBuckets) *
  106. (kIntFeatureExtent / kThetaBuckets + 1);
  107. expected_misses += (kIntFeatureExtent / kXBuckets) * (kIntFeatureExtent / kYBuckets + 1) *
  108. (kIntFeatureExtent / kThetaBuckets);
  109. EXPECT_EQ(expected_misses, misses);
  110. EXPECT_EQ(total_buckets - 2, map_features.size());
  111. ExpectContiguous(map_features, 0, total_buckets - 2);
  112. EXPECT_EQ(total_buckets - 2, map.compact_size());
  113. EXPECT_EQ(total_buckets, map.sparse_size());
  114. #endif
  115. }
  116. } // namespace tesseract