Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
isp:vibrancy [2018/09/25 19:46] – [Possible optimization] Igor Yefmov | isp:vibrancy [2023/09/05 06:22] (current) – [RGB color space] Igor Yefmov | ||
---|---|---|---|
Line 3: | Line 3: | ||
A vibrancy of a color is a perceived quality that is somewhat similar to Saturation. One could think of it as pure, bright, high chroma color. It is not an absolute measure (like Saturation) and there' | A vibrancy of a color is a perceived quality that is somewhat similar to Saturation. One could think of it as pure, bright, high chroma color. It is not an absolute measure (like Saturation) and there' | ||
- | Having said that we still want to make a distinction between " | + | Having said that we still want to make a distinction between " |
- | ===== Implementation overview | + | ===== RGB color space ===== |
+ | To adjust the saturation of a pixel in RGB space we will follow this general approach: | ||
+ | - create a desaturated version of the pixel by converting it to grayscale | ||
+ | - interpolate/ | ||
- | The goal is to increase the Saturation of each pixel based on its current Saturation level, where the increase factor is inversely proportional to the current value. Given the range of values for '' | + | FIXME |
+ | ===== YUV color space ===== | ||
+ | |||
+ | The goal is to increase the Saturation of each pixel based on its current Saturation level, where the increase factor is inversely proportional to the current value. Given the range of values for '' | ||
There are a few ways to implement this, listed below. | There are a few ways to implement this, listed below. | ||
Line 13: | Line 19: | ||
==== Linear estimation ==== | ==== Linear estimation ==== | ||
- | Let's remember that the YUV color space is using the pair of '' | + | Let's remember that the YUV color space is using the pair of '' |
<code c++> | <code c++> | ||
// center-normalize the U and V | // center-normalize the U and V | ||
Line 19: | Line 25: | ||
const unsigned int cv = abs(128 - _v); | const unsigned int cv = abs(128 - _v); | ||
// use the one that is " | // use the one that is " | ||
- | return 1. + (cu > cv ? cu : cv) / 128. * _vib; | + | return 1. + (128 - max(cu, cv)) / 128. * _vib; |
}</ | }</ | ||
Line 28: | Line 34: | ||
const auto k = scale(pixel.u, | const auto k = scale(pixel.u, | ||
// of course both new values have to be properly capped (omitted here for readability) | // of course both new values have to be properly capped (omitted here for readability) | ||
- | pixel.u = (128 - pixel.u) * k + 128; | + | pixel.u = (pixel.u |
- | pixel.v = (128 - pixel.v) * k + 128; | + | pixel.v = (pixel.v |
} | } | ||
}</ | }</ | ||
Line 35: | Line 41: | ||
==== Vector' | ==== Vector' | ||
- | Another way to see "how saturated is the color" would be to calculate the magnitude of the color vector, represented by its '' | + | Another way to see "how saturated is the color" would be to calculate the magnitude of the color vector, represented by its '' |
<code c++> | <code c++> | ||
// center-normalize the U and V | // center-normalize the U and V | ||
Line 58: | Line 64: | ||
===== Possible optimization ===== | ===== Possible optimization ===== | ||
- | (This is valid for CPU-type architecture and is not very applicable to FPGAs where general | + | (This is valid for CPU-type architecture and is not very applicable to FPGAs where general |
There are over 8 million pixels in a 4K image and only 65K (256*256) possible transformations to either '' | There are over 8 million pixels in a 4K image and only 65K (256*256) possible transformations to either '' | ||
Line 79: | Line 85: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | ===== HSL color space ===== | ||
+ | |||
+ | Moving to HSL for image processing has a wonderful advantage of " | ||
+ | |||
+ | As was described before the main idea of the Vibrancy is that the lowest saturated colors get the most relative boost in Saturation while highly saturated colors get progressively lower boost, up to a "no boost" for the 100% saturated colors. | ||
+ | |||
+ | The scale (boost) multiplier therefore depends on the vibrancy factor and the pixel' | ||
+ | \[ | ||
+ | scale = 1 + \frac{100 - saturation}{100} * (vibrancy-1) \\ | ||
+ | vibrancy \in [0..1]\\ | ||
+ | saturation \in [0..100]\% | ||
+ | \] | ||
+ | <code c++> | ||
+ | return 1. + (100 - _s) / 100. * (_vib - 1); | ||
+ | }</ | ||
+ | Once the scale (boost) value is calculated - just apply it to the pixels: | ||
+ | <code c++>// pseudo-code | ||
+ | void vibrancy(/ | ||
+ | for(const & pixel: image){ | ||
+ | pixel.sat *= scale(pixel.sat, | ||
+ | } | ||
+ | }</ | ||
+ |