mirror of
https://github.com/SatDump/SatDump
synced 2026-08-13 17:47:30 -04:00
258 lines
No EOL
8.1 KiB
C++
258 lines
No EOL
8.1 KiB
C++
#include "hue_saturation.h"
|
|
#include "core/exception.h"
|
|
#include <limits>
|
|
|
|
namespace satdump
|
|
{
|
|
namespace image
|
|
{
|
|
template <typename T>
|
|
T clamp(T value, T max, T min)
|
|
{
|
|
if (value > max)
|
|
value = max;
|
|
else if (value < min)
|
|
value = min;
|
|
return value;
|
|
};
|
|
|
|
HueSaturation::HueSaturation()
|
|
{
|
|
overlap = 0.0f;
|
|
|
|
for (int i = HUE_RANGE_ALL; i <= HUE_RANGE_MAGENTA; i++)
|
|
{
|
|
HueRange partition = (HueRange)i;
|
|
hue[partition] = 0.0;
|
|
lightness[partition] = 0.0;
|
|
saturation[partition] = 0.0;
|
|
}
|
|
}
|
|
|
|
double map_hue(HueSaturation config, HueRange range, double value)
|
|
{
|
|
value += (config.hue[HUE_RANGE_ALL] + config.hue[range]) / 2.0;
|
|
|
|
if (value < 0)
|
|
return value + 1.0;
|
|
else if (value > 1.0)
|
|
return value - 1.0;
|
|
else
|
|
return value;
|
|
}
|
|
|
|
double map_hue_overlap(HueSaturation config, HueRange primary_range, HueRange secondary_range, double value, double primary_intensity, double secondary_intensity)
|
|
{
|
|
double v = config.hue[primary_range] * primary_intensity + config.hue[secondary_range] * secondary_intensity;
|
|
|
|
value += (config.hue[HUE_RANGE_ALL] + v) / 2.0;
|
|
|
|
if (value < 0)
|
|
return value + 1.0;
|
|
else if (value > 1.0)
|
|
return value - 1.0;
|
|
else
|
|
return value;
|
|
}
|
|
|
|
double map_saturation(HueSaturation config, HueRange range, double value)
|
|
{
|
|
double v = config.saturation[HUE_RANGE_ALL] + config.saturation[range];
|
|
value *= (v + 1.0);
|
|
return clamp<double>(value, 1.0, 0.0);
|
|
}
|
|
|
|
double map_lightness(HueSaturation config, HueRange range, double value)
|
|
{
|
|
double v = (config.lightness[HUE_RANGE_ALL] + config.lightness[range]);
|
|
if (v < 0)
|
|
return value * (v + 1.0);
|
|
else
|
|
return value + (v * (1.0 - value));
|
|
}
|
|
|
|
void hue_saturation(Image &image, HueSaturation config)
|
|
{
|
|
if (image.channels() < 3)
|
|
throw satdump_exception("Hue Saturation requires as least 3 channels!");
|
|
|
|
float scale = image.maxval() - 1;
|
|
|
|
double rgb_r, rgb_g, rgb_b;
|
|
double hsl_h, hsl_s, hsl_l;
|
|
|
|
float overlap = (config.overlap / 100.0) / 2.0;
|
|
|
|
for (size_t pixel = 0; pixel < image.width() * image.height(); pixel++)
|
|
{
|
|
rgb_r = image.get(image.width() * image.height() * 0 + pixel) / scale;
|
|
rgb_g = image.get(image.width() * image.height() * 1 + pixel) / scale;
|
|
rgb_b = image.get(image.width() * image.height() * 2 + pixel) / scale;
|
|
|
|
rgb_to_hsl(rgb_r, rgb_g, rgb_b, hsl_h, hsl_s, hsl_l);
|
|
{
|
|
double h;
|
|
int hue_counter;
|
|
int hue = 0;
|
|
int secondary_hue = 0;
|
|
bool use_secondary_hue = false;
|
|
float primary_intensity = 0.0;
|
|
float secondary_intensity = 0.0;
|
|
|
|
h = hsl_h * 6.0;
|
|
|
|
for (hue_counter = 0; hue_counter < 7; hue_counter++)
|
|
{
|
|
double hue_threshold = (double)hue_counter + 0.5;
|
|
|
|
if (h < ((double)hue_threshold + overlap))
|
|
{
|
|
hue = hue_counter;
|
|
|
|
if (overlap > 0.0 && h > ((double)hue_threshold - overlap))
|
|
{
|
|
use_secondary_hue = true;
|
|
|
|
secondary_hue = hue_counter + 1;
|
|
|
|
secondary_intensity = (h - (double)hue_threshold + overlap) / (2.0 * overlap);
|
|
|
|
primary_intensity = 1.0 - secondary_intensity;
|
|
}
|
|
else
|
|
{
|
|
use_secondary_hue = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hue >= 6)
|
|
{
|
|
hue = 0;
|
|
use_secondary_hue = false;
|
|
}
|
|
|
|
if (secondary_hue >= 6)
|
|
{
|
|
secondary_hue = 0;
|
|
}
|
|
|
|
hue++;
|
|
secondary_hue++;
|
|
|
|
if (use_secondary_hue)
|
|
{
|
|
hsl_h = map_hue_overlap(config, (HueRange)hue, (HueRange)secondary_hue, hsl_h, primary_intensity, secondary_intensity);
|
|
hsl_s = (map_saturation(config, (HueRange)hue, hsl_s) * primary_intensity + map_saturation(config, (HueRange)secondary_hue, hsl_s) * secondary_intensity);
|
|
hsl_l = (map_lightness(config, (HueRange)hue, hsl_l) * primary_intensity + map_lightness(config, (HueRange)secondary_hue, hsl_l) * secondary_intensity);
|
|
}
|
|
else
|
|
{
|
|
hsl_h = map_hue(config, (HueRange)hue, hsl_h);
|
|
hsl_s = map_saturation(config, (HueRange)hue, hsl_s);
|
|
hsl_l = map_lightness(config, (HueRange)hue, hsl_l);
|
|
}
|
|
}
|
|
hsl_to_rgb(hsl_h, hsl_s, hsl_l, rgb_r, rgb_g, rgb_b);
|
|
|
|
image.set(image.width() * image.height() * 0 + pixel, rgb_r * scale);
|
|
image.set(image.width() * image.height() * 1 + pixel, rgb_g * scale);
|
|
image.set(image.width() * image.height() * 2 + pixel, rgb_b * scale);
|
|
}
|
|
}
|
|
|
|
void rgb_to_hsl(double r, double g, double b, double &h, double &s, double &l)
|
|
{
|
|
double max, min, delta;
|
|
|
|
max = std::max<double>(r, std::max<double>(g, b));
|
|
min = std::min<double>(r, std::min<double>(g, b));
|
|
|
|
l = (max + min) / 2.0;
|
|
|
|
if (max == min)
|
|
{
|
|
s = 0.0;
|
|
h = -1.0;
|
|
}
|
|
else
|
|
{
|
|
if (l <= 0.5)
|
|
s = (max - min) / (max + min);
|
|
else
|
|
s = (max - min) / (2.0 - max - min);
|
|
|
|
delta = max - min;
|
|
|
|
if (delta == 0.0)
|
|
delta = 1.0;
|
|
|
|
if (r == max)
|
|
{
|
|
h = (g - b) / delta;
|
|
}
|
|
else if (g == max)
|
|
{
|
|
h = 2.0 + (b - r) / delta;
|
|
}
|
|
else
|
|
{
|
|
h = 4.0 + (r - g) / delta;
|
|
}
|
|
|
|
h /= 6.0;
|
|
|
|
if (h < 0.0)
|
|
h += 1.0;
|
|
}
|
|
}
|
|
|
|
double hsl_value(double n1, double n2, double hue)
|
|
{
|
|
double val;
|
|
|
|
if (hue > 6.0)
|
|
hue -= 6.0;
|
|
else if (hue < 0.0)
|
|
hue += 6.0;
|
|
|
|
if (hue < 1.0)
|
|
val = n1 + (n2 - n1) * hue;
|
|
else if (hue < 3.0)
|
|
val = n2;
|
|
else if (hue < 4.0)
|
|
val = n1 + (n2 - n1) * (4.0 - hue);
|
|
else
|
|
val = n1;
|
|
|
|
return val;
|
|
}
|
|
|
|
void hsl_to_rgb(double h, double s, double l, double &r, double &g, double &b)
|
|
{
|
|
if (s == 0)
|
|
{
|
|
r = l;
|
|
g = l;
|
|
b = l;
|
|
}
|
|
else
|
|
{
|
|
double m1, m2;
|
|
|
|
if (l <= 0.5)
|
|
m2 = l * (1.0 + s);
|
|
else
|
|
m2 = l + s - l * s;
|
|
|
|
m1 = 2.0 * l - m2;
|
|
|
|
r = hsl_value(m1, m2, h * 6.0 + 2.0);
|
|
g = hsl_value(m1, m2, h * 6.0);
|
|
b = hsl_value(m1, m2, h * 6.0 - 2.0);
|
|
}
|
|
}
|
|
} // namespace image
|
|
} // namespace satdump
|