Color balance in HSV mode
The color balance module in darktable allows to adjust the color cast for highlights, mid-tones, and shadows but in RGB mode and with weird labels (lift, gain, gamma). This makes it quite tricky to understand what you do, firstly because the labels don't mean anything to most users, then because RGB is not the most intuitive way to adjust colors.
Meanwhile, Capture One has a much praised color balance tool, with explicit labels and color wheels, allowing fast color grading : https://help.phaseone.com/en/CO8/Editing-photos/Working-with-colors/Color-balance.aspx
While the wheels feel unnecessary and are probably patented, the idea of having 3 simple HSV sliders to adjust main/highlights/mid-tones/shadows would enable users to play with color-grading and achieve cinematic looks more easily.
#1 Updated by Tobias Ellinghaus 7 months ago
- The terms "lift", "gamma" and "gain" are the industry standard for that module. There is no way we will change that to something less precise.
- The standard for that very module is to work in sRGB space. I would be surprised if Capture One used HSV for it.
- Color wheels are nice indeed, and there is some incomplete work to have them in dt, too. I just never found a nice UI for the whole thing that still allowed precise adjustments. And the color wheels are not enough, you still need sliders, otherwise there is no way to achieve negative numbers.
#2 Updated by Aurélien PIERRE 7 months ago
1. I never saw them elsewhere, and I know they confuse many users. Maybe just a contextual label to translate them into simpler words would be enough.
2. The point is to have a GUI in HSV (with HSV sliders/cursors), the actual color space used to perform the modification on pixels doesn't matter.
3. As I said, color wheels feel like a design candy for hipsters, 3 sliders could do just as fine for that purpose.
#3 Updated by Tobias Ellinghaus 7 months ago
With three sliders you mean one for highlights, one for midtones and one for shadows? I don't see how that would work, you can't set the same data that we have in 4 sliders right now with a single one. And as I said, the way the module currently works is basically what every program used for color grading video uses. Sometimes in the form of sliders, more often as color wheels with extra sliders.
#4 Updated by Aurélien PIERRE 7 months ago
No, the three sliders would be for hue/saturation/value and you replicate them for highlights/mid-tones/shadows (and maybe for the main, although it sort of overlaps the white balance feature, but with a different interface). So that's 9 to 12 sliders in total. With 9 sliders, it would look exactly like the current color balance UI, only the settings would change.
The wheels are convenient to set-up saturation and hue as once in 2D, using polar coordinates (saturation:= radius, hue:= angle, I guess), but as a general paradigm, I prefer independent 1D coordinates with sliders.
#6 Updated by Aurélien PIERRE 7 months ago
The benefit is well displayed here : https://www.youtube.com/watch?v=VIyG0DJx02U. Color-grading in RGB is non-sense, it's a lot of guess and trial work to get the correct color-cast without messing the saturation along or affecting the luminance as well.
I tried to reproduce the result achieved in the video with the color zones module, in luma mode, it did quite poorly and shifted all the tints of a certain amount, instead of shifting them toward a certain tint. The color-shift module does a similar job but with less control and an overall over-cooked result.
Similarly as the local-contrast module, it would be possible to have both modes (RGB/HSV) in the color-balance module.
#10 Updated by Tobias Ellinghaus 7 months ago
The algo is trivial, just do hsv->rgb in commit_params and keep using the current process(). The problem is finding a good UI and coding it. The one from Capture One has some nice features, even though it's not as capable as our current UI. Adapting it to support the whole feature set of the current one is the hard part. At the moment I lack any great ideas how to make it work without having as many sliders as we currently have plus the additional color wheels.
#11 Updated by Aurélien PIERRE 7 months ago
The Capture One UI is just a combination of 2 controls (hue + saturation) into a single one with 2 axis (a wheel = angle + radius) + another redundant fine saturation control with a slider. You can achieve exactly the same with only 3 sliders, especially as the benefit of the wheel is not obvious compared to the sliders and both offer the same functionnality.
It may not be that trivial, because you don't want to shift every hue of the same amount, but rather retarget them to another hue, so it's not a translation but a selective minimization.
Put into maths, the naive way to do it is :
original_hue + (target_hue - original_hue) * amount
#12 Updated by Aurélien PIERRE 7 months ago
I began prototyping algos to adjust the hue : https://github.com/aurelienpierre/Image-Cases-Studies/blob/beb72a189d627014281ee75bf6a1fdd28c96b83a/notebooks/HSV%20color%20balance.ipynb
Although I'm not happy with the luma masks (designed from scratch, I don't know what's used in dt), the algorithm works well for the main hue. The core is :
def gaussian_weights(source, target, sigma): return np.exp(-(source - target)**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi)) def hue(source, target, amount): """Move the hue of source closer to the target, assuming source and target angles between [-pi; pi], according to their distance """ if amount != 0: sigma = np.pi x = np.cos(source) + amount * (np.cos(target) - np.cos(source)) * np.pi * gaussian_weights(np.cos(source), np.cos(target), sigma) y = np.sin(source) + amount * (np.sin(target) - np.sin(source)) * np.pi * gaussian_weights(np.sin(source), np.sin(target), sigma) return np.arctan2(y, x) else : return source def normal2rad(theta): """Remap the hue channel to [-pi; pi] radians assuming source is in [0;1]""" # Rescale [0; 1] to [0; 2 pi] theta = theta * 2 * np.pi # Remap [pi; 2 pi] to [-pi; 0] negative = theta > np.pi # Boolean array : put 1 where theta > pi theta[negative] = - (2 * np.pi - theta[negative]) return theta def rad2normal(theta): """Remap the hue channel to [0; 1] assuming source is in [-pi; pi] radians""" # Remap [-pi; 0] to [0; 2 pi] negative = theta < 0 # Boolean array : put 1 where theta < 0 theta[negative] = 2 * np.pi + theta[negative] # Rescale to [0;1] theta = theta / (2 * np.pi) return theta
then, the execution :
# Convert hue to radians H = normal2rad(H) # Apply hue remapping at full-throttle H_bis = hue(H, np.pi, 1) # Convert hue back to normal [0;1] H_bis = rad2normal(H_bis)
#15 Updated by Aurélien PIERRE 7 months ago
apply a gamma correction to the red channel with that? Or lower the blacks/dark part of the blue channel?
I don't, for that you can use the current module in RGB. What I do in my code is to remap the current hue to another target without breaking the tonal variations.
For example, let's say you want a teal/orange color grading (orange in highlights, cyan in shadows). Now you have to tweak the gains on the 3 RGB channels on shadows and highlights for hours because none of these colors are pure RGB values, and RGB values affect luminance, color and saturation altogether.
With my code, you just set-up a hue of 180° in shadows, around 45° in highlights, and that's it. To avoid unnatural look, the tints are shifted accordingly to their distance to the target (the farther the target is from the source, the less the algo shifts the hue of the source), so that the pixels that already have the target hue are untouched as well as those whose color is opposite (180° on the chroma wheel) to the target (like, shifting blue to red would be weird, so it's shifted to magenta instead).
The shifts use gaussian weights on the hue distance between the source and the target.
#17 Updated by Aurélien PIERRE 7 months ago
What I suggest is achieving the same purpose in a different way. A color balance just balances colors, the way it does it only affect us geeks. The interface it displays affects its use. The core principle of design is the interface directs the use, and even if HSV is not the good old way to do it, it is very efficient, especially to achieve natural results in just a few settings tweaks. We could perfectly have both modes in the same module, the same way Profile denoising or Local contrast have. If I hadn't been an hardcore dt user since v0.7, I could switch to Capture One just for that feature.
The novelty of my approach, if I dare say so, is that hues are remapped according to their distance to the target hue, which does not mess up complementary colors and respects the original gradation. So you can go all the way without messing your picture, that's pretty dummy-proof.
Anyway, I will work on this if nobody wants to, although I think it would be a just a warm-up for experienced dt devs whereas I'm not a C dev at all.
Dropping this for future reference : https://bugs.launchpad.net/inkscape/+bug/1071354