Downsampling, grayscale, and histogram normalization added.
This commit is contained in:
parent
1e5ffeb4b4
commit
1b93bcc4e3
3 changed files with 112 additions and 2 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef H_Image
|
||||
#define H_Image
|
||||
|
||||
#include <vector>
|
||||
#include <Eigen/Dense>
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_write.h>
|
||||
|
|
@ -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<uint64_t> ComputeHistogram(int downscale_factor = 1) const;
|
||||
|
||||
/// @brief Computes the cumulative histogram of the image from its histogram.
|
||||
static std::vector<uint64_t> CumulativeHistogram(std::vector<uint64_t> const& histogram);
|
||||
|
||||
private:
|
||||
int width;
|
||||
int height;
|
||||
int depth;
|
||||
|
|
|
|||
|
|
@ -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<uint64_t> hist = ComputeHistogram(downsampling_factor);
|
||||
// Then, compute the cumulative histogram
|
||||
std::vector<uint64_t> 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<uint64_t> Image::ComputeHistogram(int downsampling_factor) const {
|
||||
std::vector<uint64_t> hist(256, 0);
|
||||
for(int i = 0 ; i < width*height*depth ; i += downsampling_factor)
|
||||
hist[data[i]]++;
|
||||
return hist;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> Image::CumulativeHistogram(std::vector<uint64_t> const& histogram) {
|
||||
std::vector<uint64_t> 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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue