AliceVision
Photogrammetric Computer Vision Framework
Hamming.hpp
1 // This file is part of the AliceVision project.
2 // Copyright (c) 2016 AliceVision contributors.
3 // Copyright (c) 2012 openMVG contributors.
4 // This Source Code Form is subject to the terms of the Mozilla Public License,
5 // v. 2.0. If a copy of the MPL was not distributed with this file,
6 // You can obtain one at https://mozilla.org/MPL/2.0/.
7 
8 #pragma once
9 
10 #include "metric.hpp"
11 
12 #include <bitset>
13 
14 #ifdef _MSC_VER
15 typedef unsigned __int32 uint32_t;
16 typedef unsigned __int64 uint64_t;
17  #include <intrin.h>
18 #else
19  #include <stdint.h>
20 #endif
21 
22 #ifdef __ARM_NEON__
23  #include "arm_neon.h"
24 #endif
25 
26 // Brief:
27 // Hamming distance count the number of bits in common between descriptors
28 // by using a XOR operation + a count.
29 // For maximal performance SSE4 must be enable for builtin popcount activation.
30 
31 namespace aliceVision {
32 namespace feature {
33 
34 #undef PLATFORM_64_BIT
35 #undef PLATFORM_32_BIT
36 #if __amd64__ || __x86_64__ || _WIN64 || _M_X64
37  #define PLATFORM_64_BIT
38 #else
39  #define PLATFORM_32_BIT
40 #endif
41 
44 template<typename TBitset>
46 {
47  typedef TBitset ElementType;
48  typedef size_t ResultType;
49 
50  // Returns the Hamming Distance between two binary descriptors
51  template<typename Iterator1, typename Iterator2>
52  inline ResultType operator()(Iterator1 a, Iterator2 b, size_t size) const
53  {
54  return (*a ^ *b).count();
55  }
56 };
57 
58 // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable
59 // Lookup table to count the number of common 1 bits on unsigned char values
60 static const unsigned char pop_count_LUT[256] = {
61 #define ALICEVISION_B2(n) n, n + 1, n + 1, n + 2
62 #define ALICEVISION_B4(n) ALICEVISION_B2(n), ALICEVISION_B2(n + 1), ALICEVISION_B2(n + 1), ALICEVISION_B2(n + 2)
63 #define ALICEVISION_B6(n) ALICEVISION_B4(n), ALICEVISION_B4(n + 1), ALICEVISION_B4(n + 1), ALICEVISION_B4(n + 2)
64  ALICEVISION_B6(0),
65  ALICEVISION_B6(1),
66  ALICEVISION_B6(1),
67  ALICEVISION_B6(2)
68 
69 #undef ALICEVISION_B2
70 #undef ALICEVISION_B4
71 #undef ALICEVISION_B6
72 };
73 
74 // Hamming distance to work on raw memory
75 // like unsigned char *
76 template<typename T>
77 struct Hamming
78 {
79  typedef T ElementType;
80  typedef unsigned int ResultType;
81 
84  static inline unsigned int popcnt32(uint32_t n)
85  {
86 #ifdef _MSC_VER
87  return __popcnt(n);
88 #else
89  #if (defined __GNUC__ || defined __clang__)
90  return __builtin_popcountl(n);
91  #endif
92  n -= ((n >> 1) & 0x55555555);
93  n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
94  return (((n + (n >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
95 #endif
96  }
97 
98  static inline unsigned int popcnt64(uint64_t n)
99  {
100 #if defined _MSC_VER && defined PLATFORM_64_BIT
101  return __popcnt64(n);
102 #else
103  #if (defined __GNUC__ || defined __clang__)
104  return __builtin_popcountll(n);
105  #endif
106  n -= ((n >> 1) & 0x5555555555555555LL);
107  n = (n & 0x3333333333333333LL) + ((n >> 2) & 0x3333333333333333LL);
108  return (((n + (n >> 4)) & 0x0f0f0f0f0f0f0f0fLL) * 0x0101010101010101LL) >> 56;
109 #endif
110  }
111 
112  // Size must be equal to number of ElementType
113  template<typename Iterator1, typename Iterator2>
114  inline ResultType operator()(Iterator1 a, Iterator2 b, size_t size) const
115  {
116  ResultType result = 0;
117  // Windows & generic platforms:
118 
119 #ifdef PLATFORM_64_BIT
120  if (size % sizeof(uint64_t) == 0)
121  {
122  const uint64_t* pa = reinterpret_cast<const uint64_t*>(a);
123  const uint64_t* pb = reinterpret_cast<const uint64_t*>(b);
124  size /= (sizeof(uint64_t) / sizeof(unsigned char));
125  for (size_t i = 0; i < size; ++i, ++pa, ++pb)
126  {
127  result += popcnt64(*pa ^ *pb);
128  }
129  }
130  else if (size % sizeof(uint32_t) == 0)
131  {
132  const uint32_t* pa = reinterpret_cast<const uint32_t*>(a);
133  const uint32_t* pb = reinterpret_cast<const uint32_t*>(b);
134  size /= (sizeof(uint32_t) / sizeof(unsigned char));
135  for (size_t i = 0; i < size; ++i, ++pa, ++pb)
136  {
137  result += popcnt32(*pa ^ *pb);
138  }
139  }
140  else
141  {
142  const ElementType* a2 = reinterpret_cast<const ElementType*>(a);
143  const ElementType* b2 = reinterpret_cast<const ElementType*>(b);
144  for (size_t i = 0; i < size / (sizeof(unsigned char)); ++i)
145  {
146  result += pop_count_LUT[a2[i] ^ b2[i]];
147  }
148  }
149 #else // PLATFORM_64_BIT
150  if (size % sizeof(uint32_t) == 0)
151  {
152  const uint32_t* pa = reinterpret_cast<const uint32_t*>(a);
153  const uint32_t* pb = reinterpret_cast<const uint32_t*>(b);
154  size /= (sizeof(uint32_t) / sizeof(unsigned char));
155  for (size_t i = 0; i < size; ++i, ++pa, ++pb)
156  {
157  result += popcnt32(*pa ^ *pb);
158  }
159  }
160  else
161  {
162  const ElementType* a2 = reinterpret_cast<const ElementType*>(a);
163  const ElementType* b2 = reinterpret_cast<const ElementType*>(b);
164  for (size_t i = 0; i < size / (sizeof(unsigned char)); ++i)
165  {
166  result += pop_count_LUT[a2[i] ^ b2[i]];
167  }
168  }
169 #endif // PLATFORM_64_BIT
170  return result;
171  }
172 };
173 
174 template<typename T>
176 {
177  // Size must be equal to number of ElementType
178  template<typename Iterator1, typename Iterator2>
179  inline double operator()(Iterator1 a, Iterator2 b, size_t size) const
180  {
181  Hamming<T> metric;
182  typename Hamming<T>::ResultType h = metric(a, b, size);
183  return h * h;
184  }
185 };
186 
187 } // namespace feature
188 } // namespace aliceVision
aliceVision::feature::Hamming::popcnt32
static unsigned int popcnt32(uint32_t n)
Definition: Hamming.hpp:84
aliceVision
Definition: checkerDetector.cpp:32
aliceVision::feature::HammingBitSet
Definition: Hamming.hpp:45
aliceVision::feature::SquaredHamming
Definition: Hamming.hpp:175
aliceVision::feature::Hamming
Definition: Hamming.hpp:77