This is a follow-up question for An Updated Multi-dimensional Image Data Structure with Variadic Template Functions in C++, histogram Template Function Implementation for Image in C++, Histogram of Image using std::map in C++, histogram_normalized and histogram_with_bins Template Functions Implementation for Image in C++, normalize_histogram Template Function Implementation for Image in C++ and otsu_threshold Template Function Implementation for Image in C++.In G. Sliepen's answer menthoned that:
Create a
class Histogram
that hides how the histogram is actually stored internally, and provides a uniform way to access that data.
I created a class Histogram
that includes getCount
and addCount
member functions.
The experimental implementation
Histogram
class implementationnamespace TinyDIP { template<class ElementT> class Histogram { private: std::map<ElementT, std::size_t> histogram; public: Histogram() = default; Histogram(const std::map<ElementT, std::size_t>& input) { histogram = input; } constexpr std::size_t getCount(const ElementT& input) { if (histogram.contains(input)) { return histogram.at(input); } else { return std::size_t{ 0 }; } } constexpr Histogram& addCount(const ElementT& input) { if (histogram.contains(input)) { ++histogram[input]; } else { histogram.emplace(input, std::size_t{ 1 }); } return *this; } using iterator = typename std::map<ElementT, std::size_t>::iterator; using const_iterator = typename std::map<ElementT, std::size_t>::const_iterator; const_iterator cbegin() const { return histogram.cbegin(); } const_iterator cend() const { return histogram.cend(); } const_iterator begin() const { return histogram.cbegin(); } const_iterator end() const { return histogram.cend(); } iterator begin() { return histogram.begin(); } iterator end() { return histogram.end(); } // + operator to add two Histograms Histogram operator+(const Histogram& other) const { Histogram result = *this; for (const auto& [key, value] : other.histogram) { result.histogram[key] += value; } return result; } // - operator to subtract two Histograms Histogram operator-(const Histogram& other) const { Histogram result = *this; for (const auto& [key, value] : other.histogram) { if (result.histogram.contains(key)) { if (result.histogram[key] >= value) { result.histogram[key] -= value; } else { result.histogram[key] = 0; } } } return result; } }; }
The usage of Histogram
class:
// histogram template function implementation
// https://codereview.stackexchange.com/q/295448/231235
template<class ElementT = int>
constexpr static auto histogram(const Image<ElementT>& input)
{
Histogram<ElementT> output;
auto image_data = input.getImageData();
for (std::size_t i = 0; i < image_data.size(); ++i)
{
output.addCount(image_data[i]);
}
return output;
}
The main function:
/* Developed by Jimmy Hu */
#include <chrono>
#include <ostream>
#include "../base_types.h"
#include "../basic_functions.h"
#include "../image.h"
#include "../image_io.h"
#include "../image_operations.h"
#include "../timer.h"
template<class ExPo, class ElementT>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr static auto HistogramTest(
ExPo execution_policy,
const TinyDIP::Image<ElementT>& input,
std::ostream& os = std::cout
)
{
auto hsv_image = TinyDIP::rgb2hsv(execution_policy, input);
auto v_plane = TinyDIP::getVplane(hsv_image);
TinyDIP::Timer timer1;
os << "***** histogram of the image *****\n";
auto histogram_result = TinyDIP::histogram(v_plane);
double sum = 0.0;
for (const auto& [key, value] : histogram_result)
{
os << "key = " << key << ", value = " << value << '\n';
sum += value;
}
os << "sum = " << sum << '\n';
return;
}
int main()
{
TinyDIP::Timer timer1;
std::string image_filename = "../InputImages/1.bmp";
auto image_input = TinyDIP::bmp_read(image_filename.c_str(), true);
image_input = TinyDIP::copyResizeBicubic(image_input, 3 * image_input.getWidth(), 3 * image_input.getHeight());
HistogramTest(std::execution::par, image_input);
return EXIT_SUCCESS;
}
The output of the test code above:
Width of the input image: 512
Height of the input image: 512
Size of the input image(Byte): 786432
***** histogram of the image *****
key = 46, value = 1
key = 48, value = 2
key = 49, value = 1
key = 50, value = 5
key = 51, value = 3
key = 52, value = 6
key = 53, value = 7
key = 54, value = 10
key = 55, value = 12
key = 56, value = 16
key = 57, value = 33
key = 58, value = 58
key = 59, value = 66
key = 60, value = 126
key = 61, value = 153
key = 62, value = 224
key = 63, value = 295
key = 64, value = 404
key = 65, value = 502
key = 66, value = 580
key = 67, value = 772
key = 68, value = 1000
key = 69, value = 1169
key = 70, value = 1531
key = 71, value = 1784
key = 72, value = 2001
key = 73, value = 2410
key = 74, value = 2763
key = 75, value = 3208
key = 76, value = 3631
key = 77, value = 4275
key = 78, value = 4834
key = 79, value = 5505
key = 80, value = 6232
key = 81, value = 6968
key = 82, value = 7840
key = 83, value = 9120
key = 84, value = 9836
key = 85, value = 10661
key = 86, value = 11600
key = 87, value = 12338
key = 88, value = 13199
key = 89, value = 13437
key = 90, value = 13590
key = 91, value = 13943
key = 92, value = 14053
key = 93, value = 14216
key = 94, value = 13968
key = 95, value = 14044
key = 96, value = 13882
key = 97, value = 13967
key = 98, value = 13409
key = 99, value = 13215
key = 100, value = 12688
key = 101, value = 11978
key = 102, value = 11292
key = 103, value = 10905
key = 104, value = 10115
key = 105, value = 9597
key = 106, value = 8496
key = 107, value = 8222
key = 108, value = 7606
key = 109, value = 7260
key = 110, value = 6784
key = 111, value = 6434
key = 112, value = 6119
key = 113, value = 5888
key = 114, value = 5709
key = 115, value = 5609
key = 116, value = 5448
key = 117, value = 5294
key = 118, value = 5106
key = 119, value = 5081
key = 120, value = 4972
key = 121, value = 4998
key = 122, value = 4943
key = 123, value = 5104
key = 124, value = 5015
key = 125, value = 5101
key = 126, value = 5184
key = 127, value = 5235
key = 128, value = 5283
key = 129, value = 5358
key = 130, value = 5451
key = 131, value = 5493
key = 132, value = 5631
key = 133, value = 5828
key = 134, value = 6011
key = 135, value = 5833
key = 136, value = 6042
key = 137, value = 6000
key = 138, value = 6138
key = 139, value = 6298
key = 140, value = 6390
key = 141, value = 6501
key = 142, value = 6500
key = 143, value = 6614
key = 144, value = 6635
key = 145, value = 6923
key = 146, value = 6678
key = 147, value = 6813
key = 148, value = 6730
key = 149, value = 6909
key = 150, value = 7063
key = 151, value = 7219
key = 152, value = 7233
key = 153, value = 7520
key = 154, value = 7565
key = 155, value = 7881
key = 156, value = 8026
key = 157, value = 8114
key = 158, value = 7960
key = 159, value = 8703
key = 160, value = 8383
key = 161, value = 8620
key = 162, value = 8615
key = 163, value = 8890
key = 164, value = 8878
key = 165, value = 9275
key = 166, value = 9317
key = 167, value = 9998
key = 168, value = 10437
key = 169, value = 11598
key = 170, value = 12296
key = 171, value = 13328
key = 172, value = 14616
key = 173, value = 15616
key = 174, value = 16266
key = 175, value = 16644
key = 176, value = 16349
key = 177, value = 15965
key = 178, value = 15098
key = 179, value = 14668
key = 180, value = 13547
key = 181, value = 13298
key = 182, value = 13014
key = 183, value = 12802
key = 184, value = 12914
key = 185, value = 13299
key = 186, value = 13307
key = 187, value = 13829
key = 188, value = 14569
key = 189, value = 15167
key = 190, value = 15472
key = 191, value = 16155
key = 192, value = 17008
key = 193, value = 16731
key = 194, value = 16807
key = 195, value = 17156
key = 196, value = 16915
key = 197, value = 17381
key = 198, value = 17651
key = 199, value = 18499
key = 200, value = 19264
key = 201, value = 20976
key = 202, value = 22612
key = 203, value = 24892
key = 204, value = 26546
key = 205, value = 29127
key = 206, value = 29949
key = 207, value = 30658
key = 208, value = 30772
key = 209, value = 30274
key = 210, value = 28422
key = 211, value = 27030
key = 212, value = 25470
key = 213, value = 24549
key = 214, value = 24140
key = 215, value = 23917
key = 216, value = 24793
key = 217, value = 26640
key = 218, value = 28707
key = 219, value = 31407
key = 220, value = 34373
key = 221, value = 36521
key = 222, value = 37484
key = 223, value = 37724
key = 224, value = 35813
key = 225, value = 31366
key = 226, value = 27789
key = 227, value = 24688
key = 228, value = 22476
key = 229, value = 20738
key = 230, value = 19779
key = 231, value = 19260
key = 232, value = 18380
key = 233, value = 19396
key = 234, value = 20517
key = 235, value = 21465
key = 236, value = 21721
key = 237, value = 21324
key = 238, value = 19178
key = 239, value = 16891
key = 240, value = 15506
key = 241, value = 11898
key = 242, value = 9560
key = 243, value = 7952
key = 244, value = 6834
key = 245, value = 6631
key = 246, value = 5632
key = 247, value = 4487
key = 248, value = 3592
key = 249, value = 3271
key = 250, value = 2811
key = 251, value = 2401
key = 252, value = 1947
key = 253, value = 1269
key = 254, value = 784
key = 255, value = 884
sum = 2.3593e+06
Computation finished at Fri Mar 28 22:27:34 2025
elapsed time: 3.8231177 seconds.
Computation finished at Fri Mar 28 22:27:34 2025
elapsed time: 4.9043645 seconds.
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
An Updated Multi-dimensional Image Data Structure with Variadic Template Functions in C++,
histogram Template Function Implementation for Image in C++,
Histogram of Image using std::map in C++,
histogram_normalized and histogram_with_bins Template Functions Implementation for Image in C++,
normalize_histogram Template Function Implementation for Image in C++ and
otsu_threshold Template Function Implementation for Image in C++
What changes has been made in the code since last question?
I implemented
Histogram
template class in this post.Why a new review is being asked for?
Please review the implementation of
Histogram
template class and its tests.
1 Answer 1
This is a neat class with a clear interface.
I would consider adding a constructor that takes an image as input, and computes the full histogram for the image.
The only issue I see with this class is that it always uses a map, even for the example uint8 image where a vector would be much more efficient (and which you’ve used in your previous posts on histogram computation). One solution is to have both a vector and a map as class members, and a Boolean that says which of the two is being used. For some histograms (eg the color image in a previous post of yours) the data is stored in a map, for others (uint8 and uint16) in a vector. This does add a test for each access, but that might be OK, and still faster than a lookup in a map. Another solution could be to have a single histogram
member, but have its type be selected at compile time (see G. Sliepen’s comment below).
In this bit of code:
if (histogram.contains(input))
{
return histogram.at(input);
}
you are doing the lookup twice. Instead, use std::map::find()
to get an iterator to the element. If you don’t get the end iterator back, you can dereference it read the value.
This bit of code is also redundant:
if (histogram.contains(input))
{
++histogram[input];
}
else
{
histogram.emplace(input, std::size_t{ 1 });
}
If input
is not in the map, then histogram[input]
creates that item with a default-initialized size_t
, which is zero. So you should be able to just do ++histogram[input]
in either case. You are making us of this already in the operator+
case!
-
1\$\begingroup\$ Why not use
std::variant<std::map<...>, std::vector<...>>
? Also, since theElementT
is known at compile time, you could also make the choice of map or vector made at compile time. \$\endgroup\$G. Sliepen– G. Sliepen2025年03月30日 21:57:13 +00:00Commented Mar 30 at 21:57 -
\$\begingroup\$ @G.Sliepen That’s right, it can be a compile-time choice.
std::variant
adds complexities that are not really necessary. You save a couple of bytes in a data structure that is at least 1KB. I prefer to keep things simple. \$\endgroup\$Cris Luengo– Cris Luengo2025年03月31日 02:36:13 +00:00Commented Mar 31 at 2:36