Changed colour extractor to use HSL

This commit is contained in:
nedko 2022-12-07 13:11:50 +02:00
parent 4b7c8f266f
commit 6b60b15289
7 changed files with 127 additions and 141 deletions

View File

@ -5,36 +5,36 @@
// CONSTRUCTORS
ColourExtractor::ColourExtractor(size_t h_levels, size_t s_levels,
size_t v_levels)
size_t l_levels)
:m_pixels(NULL), m_max_colour_count(0), m_h_levels(h_levels),
m_s_levels(s_levels), m_v_levels(v_levels)
m_s_levels(s_levels), m_l_levels(l_levels)
{
m_pixels = new size_t[m_h_levels * m_s_levels * m_v_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_v_levels * sizeof(size_t));
m_pixels = new size_t[m_h_levels * m_s_levels * m_l_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_l_levels * sizeof(size_t));
}
// PROTECTED FUNCS
size_t ColourExtractor::quantize(struct ColourHSV hsv) const
size_t ColourExtractor::quantize(struct ColourHSL hsl) const
{
size_t result = 0;
size_t h_quant = 0;
size_t s_quant = 0;
size_t v_quant = 0;
size_t l_quant = 0;
v_quant = static_cast<size_t>(round(hsv.v * (m_v_levels - 1)));
if (0 == v_quant)
l_quant = static_cast<size_t>(round(hsl.l * (m_l_levels - 1)));
if (0 == l_quant)
{
goto end;
}
s_quant = static_cast<size_t>(round(hsv.s * (m_s_levels - 1)));
s_quant = static_cast<size_t>(round(hsl.s * (m_s_levels - 1)));
if (0 == s_quant)
{
goto end;
}
h_quant = static_cast<size_t>(round(hsv.h / 360.0 * (m_h_levels - 1)));
h_quant = static_cast<size_t>(round(hsl.h / 360.0 * (m_h_levels - 1)));
end:
result += h_quant;
@ -42,21 +42,21 @@ size_t ColourExtractor::quantize(struct ColourHSV hsv) const
result *= m_s_levels;
result += s_quant;
result *= m_v_levels;
result += v_quant;
result *= m_l_levels;
result += l_quant;
return result;
}
struct ColourHSV ColourExtractor::dequantize(size_t colour) const
struct ColourHSL ColourExtractor::dequantize(size_t colour) const
{
struct ColourHSV result;
struct ColourHSL result;
size_t h_quant;
size_t s_quant;
size_t v_quant;
size_t l_quant;
v_quant = colour % m_v_levels;
colour /= m_v_levels;
l_quant = colour % m_l_levels;
colour /= m_l_levels;
s_quant = colour % m_s_levels;
colour /= m_s_levels;
@ -65,31 +65,31 @@ struct ColourHSV ColourExtractor::dequantize(size_t colour) const
result.h = static_cast<double>(h_quant) * 360.0 / (m_h_levels - 1);
result.s = static_cast<double>(s_quant) / (m_s_levels - 1);
result.v = static_cast<double>(v_quant) / (m_v_levels - 1);
result.l = static_cast<double>(l_quant) / (m_l_levels - 1);
return result;
}
// PUBLIC FUNCS
void ColourExtractor::get_quantization_levels(size_t& h_levels,
size_t& s_levels, size_t& v_levels) const
size_t& s_levels, size_t& l_levels) const
{
h_levels = m_h_levels;
s_levels = m_s_levels;
v_levels = m_v_levels;
l_levels = m_l_levels;
}
void ColourExtractor::set_quantization_levels(size_t h_levels, size_t s_levels,
size_t v_levels)
size_t l_levels)
{
delete[] m_pixels;
m_h_levels = h_levels;
m_s_levels = s_levels;
m_v_levels = v_levels;
m_l_levels = l_levels;
m_pixels = new size_t[m_h_levels * m_s_levels * m_v_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_v_levels * sizeof(size_t));
m_pixels = new size_t[m_h_levels * m_s_levels * m_l_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_l_levels * sizeof(size_t));
}
size_t ColourExtractor::get_h_quantization_levels() const
@ -103,8 +103,8 @@ void ColourExtractor::set_h_quantization_levels(size_t h_levels)
m_h_levels = h_levels;
m_pixels = new size_t[m_h_levels * m_s_levels * m_v_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_v_levels * sizeof(size_t));
m_pixels = new size_t[m_h_levels * m_s_levels * m_l_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_l_levels * sizeof(size_t));
}
size_t ColourExtractor::get_s_quantization_levels() const
@ -118,30 +118,30 @@ void ColourExtractor::set_s_quantization_levels(size_t s_levels)
m_s_levels = s_levels;
m_pixels = new size_t[m_h_levels * m_s_levels * m_v_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_v_levels * sizeof(size_t));
m_pixels = new size_t[m_h_levels * m_s_levels * m_l_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_l_levels * sizeof(size_t));
}
size_t ColourExtractor::get_v_quantization_levels() const
{
return m_v_levels;
return m_l_levels;
}
void ColourExtractor::set_v_quantization_levels(size_t v_levels)
void ColourExtractor::set_v_quantization_levels(size_t l_levels)
{
delete[] m_pixels;
m_v_levels = v_levels;
m_l_levels = l_levels;
m_pixels = new size_t[m_h_levels * m_s_levels * m_v_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_v_levels * sizeof(size_t));
m_pixels = new size_t[m_h_levels * m_s_levels * m_l_levels];
memset(m_pixels, 0, m_h_levels * m_s_levels * m_l_levels * sizeof(size_t));
}
void ColourExtractor::add_pixel(struct ColourHSV hsv)
void ColourExtractor::add_pixel(struct ColourHSL hsl)
{
size_t colour_quant;
colour_quant = quantize(hsv);
colour_quant = quantize(hsl);
m_pixels[colour_quant] += 1;
if (m_max_colour_count < m_pixels[colour_quant])
@ -152,22 +152,22 @@ void ColourExtractor::add_pixel(struct ColourHSV hsv)
void ColourExtractor::clear_pixels()
{
for (size_t i = 0; i < m_h_levels * m_s_levels * m_v_levels; ++i)
for (size_t i = 0; i < m_h_levels * m_s_levels * m_l_levels; ++i)
{
m_pixels[i] = 0;
}
m_max_colour_count = 0;
}
struct ColourHSV ColourExtractor::extract_colour() const
struct ColourHSL ColourExtractor::extract_colour() const
{
struct ColourHSV result = {0.0, 0.0, 0.0};
struct ColourHSV temp_colour;
struct ColourHSL result = {0.0, 0.0, 0.0};
struct ColourHSL temp_colour;
size_t max_colour_value = 0;
size_t temp_colour_value;
double temp_colour_weight;
for (size_t i = 0; i < m_h_levels * m_s_levels * m_v_levels; ++i)
for (size_t i = 0; i < m_h_levels * m_s_levels * m_l_levels; ++i)
{
if (0 == m_pixels[i])
{

View File

@ -7,7 +7,7 @@
class ColourExtractor
{
private:
protected:
// Holds all quantized pixels
size_t* m_pixels;
@ -17,30 +17,30 @@ private:
// Quantization levels for the HSV colours
size_t m_h_levels;
size_t m_s_levels;
size_t m_v_levels;
size_t m_l_levels;
// Quantizes the colour
size_t quantize(struct ColourHSV hsv) const;
size_t quantize(struct ColourHSL hsl) const;
// Dequantizes the colour
struct ColourHSV dequantize(size_t colour) const;
struct ColourHSL dequantize(size_t colour) const;
protected:
// Evaluates the given colour based on its weight
virtual size_t evaluate_colour(struct ColourHSV hsv, double weight)
virtual size_t evaluate_colour(struct ColourHSL hsl, double weight)
const = 0;
public:
// Params: HSV quantization levels
ColourExtractor(size_t h_levels = 36, size_t s_levels = 10,
size_t v_levels = 10);
size_t l_levels = 10);
void get_quantization_levels(size_t& h_levels, size_t& s_levels,
size_t& v_levels) const;
size_t& l_levels) const;
// Note: clears all pixels
void set_quantization_levels(size_t h_levels, size_t s_levels,
size_t v_levels);
size_t l_levels);
size_t get_h_quantization_levels() const;
@ -55,16 +55,16 @@ public:
size_t get_v_quantization_levels() const;
// Note: clears all pixels
void set_v_quantization_levels(size_t v_levels);
void set_v_quantization_levels(size_t l_levels);
// Adds a pixel to be considered for extraction
void add_pixel(struct ColourHSV hsv);
void add_pixel(struct ColourHSL hsl);
// Clears all pixels
void clear_pixels();
// Extracts a single colour from all added pixels
struct ColourHSV extract_colour() const;
struct ColourHSL extract_colour() const;
~ColourExtractor();
};

View File

@ -3,53 +3,63 @@
#include <math.h>
// CONSTRUCTORS
ComplimentaryColourExtractor::ComplimentaryColourExtractor(struct ColourHSV hsv,
ComplimentaryColourExtractor::ComplimentaryColourExtractor(struct ColourHSL hsl,
size_t h_levels, size_t s_levels, size_t v_levels)
:ColourExtractor(h_levels, s_levels, v_levels), m_main_colour(hsv)
:ColourExtractor(h_levels, s_levels, v_levels), m_main_colour(hsl)
{
}
// PROTECTED FUNCS
size_t ComplimentaryColourExtractor::evaluate_colour(struct ColourHSV hsv,
size_t ComplimentaryColourExtractor::evaluate_colour(struct ColourHSL hsl,
double weight) const
{
size_t result = 0;
double angle_delta;
double additional_colour_weight;
// double additional_colour_weight;
if ((0.0 == hsv.s) || (0.0 == m_main_colour.s))
{
angle_delta = 180.0;
additional_colour_weight = 0.5;
}
else
{
additional_colour_weight = 1.0;
/* SECTION BEGIN */
angle_delta = fabs(hsv.h - m_main_colour.h);
// if ((0.0 == hsl.s) || (0.0 == m_main_colour.s))
// {
// angle_delta = 180.0;
// additional_colour_weight = 0.5;
// }
// else
// {
// additional_colour_weight = 1.0;
// /* SECTION BEGIN */
// angle_delta = fabs(hsl.h - m_main_colour.h);
// if (angle_delta > 180.0F)
// {
// angle_delta = 360.0F - angle_delta;
// }
// /* SECTION END */
// }
angle_delta = fabs(hsl.h - m_main_colour.h);
if (angle_delta > 180.0F)
{
angle_delta = 360.0F - angle_delta;
}
/* SECTION END */
}
// result = static_cast<size_t>(round(1000.0 * pow(weight, 0.3) * hsv.v *
// fmin(1.0, hsv.v + hsv.s) * additional_colour_weight * angle_delta));
result = static_cast<size_t>(round(1000.0 * pow(weight, 0.3) * hsv.v *
additional_colour_weight * angle_delta));
// result = static_cast<size_t>(round(1000.0 * pow(weight, 0.3) * hsl.l *
// additional_colour_weight * angle_delta));
result = static_cast<size_t>(round(1000.0 * pow(weight, 0.3) *
((1.0 - fabs(2 * hsl.l - 1.0)) * (1 - 1.0 / m_l_levels) + 1.0 / m_l_levels) *
angle_delta));
return result;
}
// PUBLIC FUNCS
struct ColourHSV ComplimentaryColourExtractor::get_main_colour() const
struct ColourHSL ComplimentaryColourExtractor::get_main_colour() const
{
return m_main_colour;
}
void ComplimentaryColourExtractor::set_main_colour(struct ColourHSV hsv)
void ComplimentaryColourExtractor::set_main_colour(struct ColourHSL hsl)
{
m_main_colour = hsv;
m_main_colour = hsl;
}

View File

@ -7,18 +7,18 @@ class ComplimentaryColourExtractor : public ColourExtractor
{
private:
// Main colour against which we are finding the complimentary
struct ColourHSV m_main_colour;
struct ColourHSL m_main_colour;
protected:
// Implemented - Evaluates the given colour based on its weight
virtual size_t evaluate_colour(struct ColourHSV hsv, double weight) const;
virtual size_t evaluate_colour(struct ColourHSL hsl, double weight) const;
public:
ComplimentaryColourExtractor(struct ColourHSV hsv, size_t h_levels = 36,
ComplimentaryColourExtractor(struct ColourHSL hsl, size_t h_levels = 36,
size_t s_levels = 10, size_t v_levels = 10);
struct ColourHSV get_main_colour() const;
void set_main_colour(struct ColourHSV hsv);
struct ColourHSL get_main_colour() const;
void set_main_colour(struct ColourHSL hsl);
};
#endif // COMPLIMENTARY_COLOUR_EXTRACTOR_H_

View File

@ -1,4 +1,5 @@
#include <iostream>
#include <iomanip>
#include <string>
#define STB_IMAGE_IMPLEMENTATION
@ -11,6 +12,16 @@
using namespace std;
void print_pixel(ColourRGB rgb)
{
uint8_t r = static_cast<uint8_t>(round(rgb.r * 255));
uint8_t g = static_cast<uint8_t>(round(rgb.g * 255));
uint8_t b = static_cast<uint8_t>(round(rgb.b * 255));
cout << "#" << std::hex << (int)r << (int)g << (int)b;
cout << std::dec << " - " << (int)r << " " << (int)g << " " << (int)b;
}
void usage(char *name)
{
cout << "Usage: " << name << " [input image]" << endl;
@ -27,8 +38,8 @@ int main(int argc, char **argv)
unsigned char *image = nullptr;
ColourRGB pixel;
ColourRGB pixel2;
ColourHSV pixel_hsv;
MainColourExtractor main_extractor(0.2, 36, 10, 10);
ColourHSL pixel_hsl;
MainColourExtractor main_extractor(36, 10, 10);
ComplimentaryColourExtractor comp_extractor({0, 0, 0}, 36, 10, 10);
if (argc < 2)
@ -75,19 +86,23 @@ int main(int argc, char **argv)
// TODO: Handle alpha here
}
// TODO: Add pixel to evaluator here
pixel_hsv = RGBtoHSV(pixel);
main_extractor.add_pixel(pixel_hsv);
comp_extractor.add_pixel(pixel_hsv);
pixel_hsl = RGBtoHSL(pixel);
main_extractor.add_pixel(pixel_hsl);
comp_extractor.add_pixel(pixel_hsl);
}
stbi_image_free(image);
pixel_hsv = main_extractor.extract_colour();
comp_extractor.set_main_colour(pixel_hsv);
pixel = HSVtoRGB(pixel_hsv);
pixel2 = HSVtoRGB(comp_extractor.extract_colour());
pixel_hsl = main_extractor.extract_colour();
comp_extractor.set_main_colour(pixel_hsl);
pixel = HSLtoRGB(pixel_hsl);
pixel2 = HSLtoRGB(comp_extractor.extract_colour());
// TODO: Print them to console
cout << "Main - ";
print_pixel(pixel);
cout << endl;
cout << "Comp - ";
print_pixel(pixel2);
cout << endl;
x_size = 16;
y_size = 16;
@ -106,7 +121,7 @@ int main(int argc, char **argv)
image[i * channels + 2] = static_cast<unsigned char>(round(pixel.b * 255));
}
stbi_write_png("main.png", x_size, y_size, channels, image, x_size * channels);
stbi_write_png("0 - main.png", x_size, y_size, channels, image, x_size * channels);
for (int i = 0; i < x_size * y_size; ++i)
{
@ -115,7 +130,7 @@ int main(int argc, char **argv)
image[i * channels + 2] = static_cast<unsigned char>(round(pixel2.b * 255));
}
stbi_write_png("comp.png", x_size, y_size, channels, image, x_size * channels);
stbi_write_png("1 - comp.png", x_size, y_size, channels, image, x_size * channels);
delete[] image;

View File

@ -3,49 +3,21 @@
#include <math.h>
// CONSTRUCTORS
MainColourExtractor::MainColourExtractor(double lightness_threshold,
size_t h_levels, size_t s_levels, size_t v_levels)
:ColourExtractor(h_levels, s_levels, v_levels),
m_lightness_threshold(lightness_threshold)
MainColourExtractor::MainColourExtractor(size_t h_levels, size_t s_levels,
size_t l_levels)
: ColourExtractor(h_levels, s_levels, l_levels)
{
}
// PRIVATE FUNCS
double MainColourExtractor::calculate_lightness(struct ColourHSV hsv) const
{
double result = 0.0;
struct ColourRGB rgb;
rgb = HSVtoRGB(hsv);
result = 0.299F * rgb.r + 0.587F * rgb.g + 0.114F * rgb.b;
return result;
}
// PROTECTED FUNCS
size_t MainColourExtractor::evaluate_colour(struct ColourHSV hsv, double weight)
size_t MainColourExtractor::evaluate_colour(struct ColourHSL hsl, double weight)
const
{
size_t result = 0;
double lightness;
lightness = calculate_lightness(hsv);
if (lightness >= m_lightness_threshold)
{
result = static_cast<size_t>(round(1000.0 * (pow(weight, 0.3) +
0.009999999776482582) * hsv.v * (hsv.s + 0.1)));
}
result = static_cast<size_t>(round(1000.0 * pow(weight, 0.3) *
(hsl.s + 1.0 / m_h_levels) *
((1.0 - fabs(2 * hsl.l - 1.0)) * (1 - 1.0 / m_l_levels) + 1.0 / m_l_levels)));
return result;
}
// PUBLIC FUNCS
double MainColourExtractor::get_lightness_threshold() const
{
return m_lightness_threshold;
}
void MainColourExtractor::set_lightness_threshold(double value)
{
m_lightness_threshold = value;
}

View File

@ -5,24 +5,13 @@
class MainColourExtractor : public ColourExtractor
{
private:
// The minimum lightness a colour needs to have to be considered for
// evaluation
double m_lightness_threshold;
// Calculates the lightness of a colour
double calculate_lightness(struct ColourHSV hsv) const;
protected:
// Implemented - Evaluates the given colour based on its weight
virtual size_t evaluate_colour(struct ColourHSV hsv, double weight) const;
virtual size_t evaluate_colour(struct ColourHSL hsv, double weight) const;
public:
MainColourExtractor(double lightness_threshold = 0.2, size_t h_levels = 36,
size_t s_levels = 10, size_t v_levels = 10);
double get_lightness_threshold() const;
void set_lightness_threshold(double value);
MainColourExtractor(size_t h_levels = 36, size_t s_levels = 10,
size_t l_levels = 10);
};
#endif // MAIN_COLOUR_EXTRACTOR_H_