diff --git a/cpp/include/Image.hpp b/cpp/include/Image.hpp index cc7945d..6522852 100644 --- a/cpp/include/Image.hpp +++ b/cpp/include/Image.hpp @@ -1,6 +1,7 @@ #ifndef H_Image #define H_Image +#include #include #include #include @@ -26,6 +27,10 @@ class Image { /// @param depth_ Depth of the image. Image(int width_, int height_, int depth_ = 1); + /// @brief Copies another image. Performs a deep copy. + /// @param other The image to copy. + Image(Image const& other); + /// @brief Frees the allocated memory of the image. ~Image(); @@ -106,9 +111,34 @@ class Image { /// @return The value at the given index of the raw image data. unsigned char operator[](int i) const; - private: - Image(Image const& other) = delete; // Copy constructor (deleted) + /// @brief Returns a new image that has been downsampled by a given factor. + /// @param factor Factor by which the image is downsampled. A factor of 2 will halve the width and height, thus reducing the pixel count by a factor of 2^2 = 4. + Image Downsampled(int factor) const; + /// @brief Returns a new image that represents the grayscale version of the current image. + /// @return A new image that represents the grayscale version of the current image (average of RGB components). + Image Grayscale() const; + + /// @brief Returns a new image that represents the Luma version of the current image, following the REC709 standard. + /// @return A new image that represents the Luma version of the current image, following the REC709 standard. + Image LumaREC709() const; + + /// @brief Performs histrogram normalization on the image (in place). Histogram normalization is a contrast enhancement technique that ensures that the whole range of values is used. + /// All channels are used to compute the histogram, and all channels are normalized using the same global histogram. + /// @param downsampling_factor Downsampling factor to use while computing the histogram. + /// @return A reference to the image. + Image & HistogramNormalize(int downsampling_factor=1); + + /// @brief Computes the histogram of the image. + /// All channels are used to compute the histogram. If the downscale factor is a multiple of the number of channels, the histogram will be skewed and will only use the first channel. + /// @param downscale_factor Factor by which the image is downsampled before computing the histogram. + /// @return The histogram of the image. + std::vector ComputeHistogram(int downscale_factor = 1) const; + + /// @brief Computes the cumulative histogram of the image from its histogram. + static std::vector CumulativeHistogram(std::vector const& histogram); + + private: int width; int height; int depth; diff --git a/cpp/src/Image.cpp b/cpp/src/Image.cpp index 294c467..b5119be 100644 --- a/cpp/src/Image.cpp +++ b/cpp/src/Image.cpp @@ -4,6 +4,11 @@ Image::Image(int width_, int height_, int depth_) : width(width_), height(height data = new unsigned char[width * height * depth]; } +Image::Image(Image const& other) : width(other.width), height(other.height), depth(other.depth) { + data = new unsigned char[width * height * depth]; + std::memcpy(data, other.data, width * height * depth); +} + Image::~Image() { delete[] data; } @@ -111,3 +116,62 @@ Image & Image::SetPixel(Eigen::Vector2i const& pos, Eigen::Vector3i const& rgb) unsigned char Image::operator[](int i) const { return data[i]; } + +Image Image::Downsampled(int factor) const { + factor = std::max(factor, 1); + Image downsampled(width / factor, height / factor, depth); + for(int i = 0 ; i < downsampled.GetHeight() ; i++) + for(int j = 0 ; j < downsampled.GetWidth() ; j++) + downsampled.SetPixel(i, j, GetPixel(i * factor, j * factor)); + return downsampled; +} + +Image Image::Grayscale() const { + Image grayscale(width, height, 1); + for(int i = 0 ; i < height ; i++) + for(int j = 0 ; j < width ; j++) { + Eigen::Vector3i rgb = GetPixel(i, j); + grayscale.SetPixelValue(i, j, 0, ((int)rgb[0] + (int)rgb[1] + (int)rgb[2]) / 3); + } + return grayscale; +} + +Image Image::LumaREC709() const { + Image grayscale(width, height, 1); + for(int i = 0 ; i < height ; i++) + for(int j = 0 ; j < width ; j++) { + Eigen::Vector3i rgb = GetPixel(i, j); + grayscale.SetPixelValue(i, j, 0, ((double)rgb[0]*0.2126 + (double)rgb[1]*0.7152 + (double)rgb[2]*0.0722)); + } + return grayscale; +} + +Image & Image::HistogramNormalize(int downsampling_factor) { + // First, compute the histogram + std::vector hist = ComputeHistogram(downsampling_factor); + // Then, compute the cumulative histogram + std::vector cumul_hist = CumulativeHistogram(hist); + // Finally, normalize the image + uint64_t max_cum_hist_val = cumul_hist[cumul_hist.size()-1]; + for(int i = 0 ; i < height ; i++) + for(int j = 0 ; j < width ; j++) { + Eigen::Vector3i rgb = GetPixel(i, j); + SetPixel(i, j, Eigen::Vector3i(cumul_hist[rgb[0]] * 255 / max_cum_hist_val, cumul_hist[rgb[1]] * 255 / max_cum_hist_val, cumul_hist[rgb[2]] * 255 / max_cum_hist_val)); + } + return *this; +} + +std::vector Image::ComputeHistogram(int downsampling_factor) const { + std::vector hist(256, 0); + for(int i = 0 ; i < width*height*depth ; i += downsampling_factor) + hist[data[i]]++; + return hist; +} + +std::vector Image::CumulativeHistogram(std::vector const& histogram) { + std::vector cumul_hist(histogram.size(), 0); + cumul_hist[0] = histogram[0]; + for(unsigned int i = 1 ; i < histogram.size() ; i++) + cumul_hist[i] = cumul_hist[i - 1] + histogram[i]; + return cumul_hist; +} \ No newline at end of file diff --git a/cpp/src/tests.cpp b/cpp/src/tests.cpp index 6445909..25f98a2 100644 --- a/cpp/src/tests.cpp +++ b/cpp/src/tests.cpp @@ -292,6 +292,22 @@ int main() { image2.Save("interp_nearest_512x512.png"); image3.Save("interp_bilinear_512x512.png"); } + + { + std::cout << "Grayscale conversion\n"; + Image image("../venise.jpg"); + Image image_gray = image.Grayscale(); + Image image_luma = image.LumaREC709(); + image_gray.Save("venise_gray.jpg"); + image_luma.Save("venise_luma.jpg"); + } + + { + std::cout << "Image normalization\n"; + Image image("../venise.jpg"); + image.HistogramNormalize(5); + image.Save("venise_normalized.jpg"); + } } PRINT_TESTS_SUMMARY();