Histogram.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/Logger.h>
  9. #include <algorithm>
  10. #include <vector>
  11. namespace WwiseGTE
  12. {
  13. class Histogram
  14. {
  15. public:
  16. // In the constructor with input 'int const* samples', set noRescaling
  17. // to 'true' when you want the sample values mapped directly to the
  18. // buckets. Typically, you know that the sample values are in the set
  19. // of numbers {0,1,...,numBuckets-1}, but in the event of out-of-range
  20. // values, the histogram stores a count for those numbers smaller than
  21. // 0 and those numbers larger or equal to numBuckets.
  22. Histogram(int numBuckets, int numSamples, int const* samples, bool noRescaling)
  23. :
  24. mBuckets(numBuckets),
  25. mExcessLess(0),
  26. mExcessGreater(0)
  27. {
  28. LogAssert(numBuckets > 0 && numSamples > 0 && samples != nullptr, "Invalid input.");
  29. std::fill(mBuckets.begin(), mBuckets.end(), 0);
  30. if (noRescaling)
  31. {
  32. // Map to the buckets, also counting out-of-range pixels.
  33. for (int i = 0; i < numSamples; ++i)
  34. {
  35. int value = samples[i];
  36. if (0 <= value)
  37. {
  38. if (value < numBuckets)
  39. {
  40. ++mBuckets[value];
  41. }
  42. else
  43. {
  44. ++mExcessGreater;
  45. }
  46. }
  47. else
  48. {
  49. ++mExcessLess;
  50. }
  51. }
  52. }
  53. else
  54. {
  55. // Compute the extremes.
  56. int minValue = samples[0], maxValue = minValue;
  57. for (int i = 1; i < numSamples; ++i)
  58. {
  59. int value = samples[i];
  60. if (value < minValue)
  61. {
  62. minValue = value;
  63. }
  64. else if (value > maxValue)
  65. {
  66. maxValue = value;
  67. }
  68. }
  69. // Map to the buckets.
  70. if (minValue < maxValue)
  71. {
  72. // The image is not constant.
  73. double numer = static_cast<double>(numBuckets - 1);
  74. double denom = static_cast<double>(maxValue - minValue);
  75. double mult = numer / denom;
  76. for (int i = 0; i < numSamples; ++i)
  77. {
  78. int index = static_cast<int>(mult * static_cast<double>(samples[i] - minValue));
  79. ++mBuckets[index];
  80. }
  81. }
  82. else
  83. {
  84. // The image is constant.
  85. mBuckets[0] = numSamples;
  86. }
  87. }
  88. }
  89. Histogram(int numBuckets, int numSamples, float const* samples)
  90. :
  91. mBuckets(numBuckets),
  92. mExcessLess(0),
  93. mExcessGreater(0)
  94. {
  95. LogAssert(numBuckets > 0 && numSamples > 0 && samples != nullptr, "Invalid input.");
  96. std::fill(mBuckets.begin(), mBuckets.end(), 0);
  97. // Compute the extremes.
  98. float minValue = samples[0], maxValue = minValue;
  99. for (int i = 1; i < numSamples; ++i)
  100. {
  101. float value = samples[i];
  102. if (value < minValue)
  103. {
  104. minValue = value;
  105. }
  106. else if (value > maxValue)
  107. {
  108. maxValue = value;
  109. }
  110. }
  111. // Map to the buckets.
  112. if (minValue < maxValue)
  113. {
  114. // The image is not constant.
  115. double numer = static_cast<double>(numBuckets - 1);
  116. double denom = static_cast<double>(maxValue - minValue);
  117. double mult = numer / denom;
  118. for (int i = 0; i < numSamples; ++i)
  119. {
  120. int index = static_cast<int>(mult * static_cast<double>(samples[i] - minValue));
  121. ++mBuckets[index];
  122. }
  123. }
  124. else
  125. {
  126. // The image is constant.
  127. mBuckets[0] = numSamples;
  128. }
  129. }
  130. Histogram(int numBuckets, int numSamples, double const* samples)
  131. :
  132. mBuckets(numBuckets),
  133. mExcessLess(0),
  134. mExcessGreater(0)
  135. {
  136. LogAssert(numBuckets > 0 && numSamples > 0 && samples != nullptr, "Invalid input.");
  137. std::fill(mBuckets.begin(), mBuckets.end(), 0);
  138. // Compute the extremes.
  139. double minValue = samples[0], maxValue = minValue;
  140. for (int i = 1; i < numSamples; ++i)
  141. {
  142. double value = samples[i];
  143. if (value < minValue)
  144. {
  145. minValue = value;
  146. }
  147. else if (value > maxValue)
  148. {
  149. maxValue = value;
  150. }
  151. }
  152. // Map to the buckets.
  153. if (minValue < maxValue)
  154. {
  155. // The image is not constant.
  156. double numer = static_cast<double>(numBuckets - 1);
  157. double denom = maxValue - minValue;
  158. double mult = numer / denom;
  159. for (int i = 0; i < numSamples; ++i)
  160. {
  161. int index = static_cast<int>(mult * (samples[i] - minValue));
  162. ++mBuckets[index];
  163. }
  164. }
  165. else
  166. {
  167. // The image is constant.
  168. mBuckets[0] = numSamples;
  169. }
  170. }
  171. // Construction when you plan on updating the histogram incrementally.
  172. // The incremental update is implemented only for integer samples and
  173. // no rescaling.
  174. Histogram(int numBuckets)
  175. :
  176. mBuckets(numBuckets),
  177. mExcessLess(0),
  178. mExcessGreater(0)
  179. {
  180. LogAssert(numBuckets > 0, "Invalid input.");
  181. std::fill(mBuckets.begin(), mBuckets.end(), 0);
  182. }
  183. // This function is called when you have used the Histogram(int)
  184. // constructor. No bounds checking is used; you must ensure that the
  185. // input value is in {0,...,numBuckets-1}.
  186. inline void Insert(int value)
  187. {
  188. ++mBuckets[value];
  189. }
  190. // This function is called when you have used the Histogram(int)
  191. // constructor. Bounds checking is used.
  192. void InsertCheck(int value)
  193. {
  194. if (0 <= value)
  195. {
  196. if (value < static_cast<int>(mBuckets.size()))
  197. {
  198. ++mBuckets[value];
  199. }
  200. else
  201. {
  202. ++mExcessGreater;
  203. }
  204. }
  205. else
  206. {
  207. ++mExcessLess;
  208. }
  209. }
  210. // Member access.
  211. inline std::vector<int> const& GetBuckets() const
  212. {
  213. return mBuckets;
  214. }
  215. inline int GetExcessLess() const
  216. {
  217. return mExcessLess;
  218. }
  219. inline int GetExcessGreater() const
  220. {
  221. return mExcessGreater;
  222. }
  223. // In the following, define cdf(V) = sum_{i=0}^{V} bucket[i], where
  224. // 0 <= V < B and B is the number of buckets. Define N = cdf(B-1),
  225. // which must be the number of pixels in the image.
  226. // Get the lower tail of the histogram. The returned index L has the
  227. // properties: cdf(L-1)/N < tailAmount and cdf(L)/N >= tailAmount.
  228. int GetLowerTail(double tailAmount)
  229. {
  230. int const numBuckets = static_cast<int>(mBuckets.size());
  231. int hSum = 0;
  232. for (int i = 0; i < numBuckets; ++i)
  233. {
  234. hSum += mBuckets[i];
  235. }
  236. int hTailSum = static_cast<int>(tailAmount * hSum);
  237. int hLowerSum = 0;
  238. int lower;
  239. for (lower = 0; lower < numBuckets; ++lower)
  240. {
  241. hLowerSum += mBuckets[lower];
  242. if (hLowerSum >= hTailSum)
  243. {
  244. break;
  245. }
  246. }
  247. return lower;
  248. }
  249. // Get the upper tail of the histogram. The returned index U has the
  250. // properties: cdf(U)/N >= 1-tailAmount and cdf(U+1) < 1-tailAmount.
  251. int GetUpperTail(double tailAmount)
  252. {
  253. int const numBuckets = static_cast<int>(mBuckets.size());
  254. int hSum = 0;
  255. for (int i = 0; i < numBuckets; ++i)
  256. {
  257. hSum += mBuckets[i];
  258. }
  259. int hTailSum = static_cast<int>(tailAmount * hSum);
  260. int hUpperSum = 0;
  261. int upper;
  262. for (upper = numBuckets - 1; upper >= 0; --upper)
  263. {
  264. hUpperSum += mBuckets[upper];
  265. if (hUpperSum >= hTailSum)
  266. {
  267. break;
  268. }
  269. }
  270. return upper;
  271. }
  272. // Get the lower and upper tails of the histogram. The returned
  273. // indices are L and U and have the properties:
  274. // cdf(L-1)/N < tailAmount/2, cdf(L)/N >= tailAmount/2,
  275. // cdf(U)/N >= 1-tailAmount/2, and cdf(U+1) < 1-tailAmount/2.
  276. void GetTails(double tailAmount, int& lower, int& upper)
  277. {
  278. int const numBuckets = static_cast<int>(mBuckets.size());
  279. int hSum = 0;
  280. for (int i = 0; i < numBuckets; ++i)
  281. {
  282. hSum += mBuckets[i];
  283. }
  284. int hTailSum = static_cast<int>(0.5 * tailAmount * hSum);
  285. int hLowerSum = 0;
  286. for (lower = 0; lower < numBuckets; ++lower)
  287. {
  288. hLowerSum += mBuckets[lower];
  289. if (hLowerSum >= hTailSum)
  290. {
  291. break;
  292. }
  293. }
  294. int hUpperSum = 0;
  295. for (upper = numBuckets - 1; upper >= 0; --upper)
  296. {
  297. hUpperSum += mBuckets[upper];
  298. if (hUpperSum >= hTailSum)
  299. {
  300. break;
  301. }
  302. }
  303. }
  304. private:
  305. std::vector<int> mBuckets;
  306. int mExcessLess, mExcessGreater;
  307. };
  308. }