New shadows/highlights tool with enhanced halo control

I have done some further work on the shadows/highlights tool, and committed an improved version. The changes are mostly in the way the blurred mask is generated, and in the tone mapping function that is applied to compress the dynamic range.

The edge-preserving blur is based on a “guided filter pyramid”, in which guided blurs are applied to log-encoded luminance values at increasing radius and decreasing threshold. The resulting mask can effectively blur the low-contrast textures and preserve sharp edges, much better than a single high-radius guided blur.
This is probably the most relevant change in this new tool, and has been the most difficult part to develop… my suggestion is to gradually lower the threshold from its default value, until residual halos almost disappear but textures still retain their contrast.

The tone-mapping is essentially based on a power function, with different exponents for the shadows and highlights portions. However, a simple power function usually has a too strong effect in the deep shadows. I have therefore introduced a third adjustment that straightens the curve toward zero, restoring some contrast in the deep shadows.

An anchor parameter defines the split point between shadows and highlights, and is set to 50% (mid-gray) by default.

Here is how the interface looks like:

The tool tends to reduce the overall image contrast, which can be re-introduced with a slight S-shaped curve, for example using the tone-mapping tool:


I am in the process of writing a detailed blog post on the subject, meanwhile you can already find some side-by-side before/after comparisons.

Any feedback is very much appreciated! In particular regarding the halos suppression…


One thing in common in most of my PlayRaw entries is that they exhibit good artifact and halo control, or at least I try, unless I am trying to do something out of the ordinary. Things I have learned from my own custom G’MIC processing adventures:

  1. It is the nature of certain operations to generate halos (and / or artifacts). I choose those that aren’t as bad; really, it is a trade off for other disadvantages.

  2. Introduce these operations later in the pipe, as the haloing tends to worsen as you pile on more processing.

  3. The severity of the haloing might depend on the nature of the input data. The colour space, kurtosis, etc.

  4. There are ways to attenuate, mitigate or cancel out the haloing but these methods aren’t universal and require fine grained tweaking (and custom analysis) a GUI app cannot offer.

I know you may be aware of most if not all of these points.

One thing that might help a GUI app is offer a view mode. Basically, something to exaggerate things so that it is easy to gauge the impact of the settings on the image. I do it in G’MIC in a very custom matter that can’t be done in a GUI but I think you can learn from RT and perhaps dt.

E.g., RT has a transmission view in its dehaze and Retinex modules, and contrast view for its sharpening modules.

1 Like

So, in other word, it is a long way to say halos are almost impossible to avoid.

It is more about accepting them for what they are and examining what you are doing about them. Sometimes, I think I am doing the right™ thing only to learn that I have been doing it wrong all along. Very humbling.

In fact, my love affair with the guided filter is reaching the end of its two year magical arc. Now reality is settling in. :broken_heart: :rofl:


very interesting idea! where can I find the code? I’d like to take a look…

Hi @agriggio, I was planning to PM you later today with some in-depth explanations…

1 Like

Could you include me in your explanation? I might not understand the code but I am interested in your method so that I may compare and contrast to what I have been doing. Also wondering whether you have tried adaptive guided filtering; I haven’t exactly but it may be yet another subject to explore.

It will be then easier to just explain here. Anyhow there is no secret :wink:
I have been looking into adaptive median filters, but they are much harder to optimize than plain median, and too expensive at large radius.
Will check adaptive guided as well…

1 Like

@afre @agriggio you can already read an explanation of why I am running the guided filter on log-encoded pixel values: PhotoFlow Image Editor Blog: New shadows/highlights and tone-mapping tools

The rest will come as soon as possible…

Oh, the problem is different from the one that I imagined. Am I correct to say that you are trying to evenly blur both sides of edges as defined by a threshold?

Another filtering method to throw out there is the rolling guidance filter.

Yes, or in other words to achieve an equivalent degree of blurring at any brightness level, using a single threshold value.

But this is only one part of the problem, the other one being to obtain a good blur also very close to sharp edges. This is where the incremental guided blurs at increasing radius values enter in the game…

I know you still have much more to share but these are my preliminary uninformed thoughts on the matter.

A feature of the guided filter is that it preserves structures; however, where there is noise, the response is to increase the radius and / or regularization. Raising the radius without doing the same for regularization would only soften the noise. So I see that your regularization is quite high to rid the bright block of the noise.

Here is what I mean by rolling guidance. Basically, I would use rolling guidance to smooth the noise and edges that don’t make the threshold first. This preprocessing would help reduce the need for a higher regularization by the guided filter. Images are as follows: input, roll, guided64, guided64roll, guided255, guided255roll. Obviously, this is a rough take. You could customize and tweak both filtering techniques to taste.







Interesting… however, it seems that the rolling version produces “rounded corners” artefacts, similar to those that one gets with the median filter when a threshold is applied. See for example the corner in the second image.

This is the blur mask that I obtain with my “incremental guided filter” method, with the same initial threshold as the other images in my blog post:


The rolling guidance filter in G’MIC uses the bilateral filter to drive it, so it inherits some of its weaknesses. It has potential because it can use other filters to suit the needs of the implementer. Besides the slightly rounded corner, the rest of the blurring is very nice. Yours has banding that I am a little worried about.

Not saying one way is better than the other. The incremental nature of your method just reminded me of the iterative nature of the rolling one. Edit: This can be a downside for the latter because it can be expensive. I have other methods in mind such as generating depth maps but that might be too complex for your purposes. Ha ha, sorry for geeking out. :nerd_face:

PS What about the case of the inner corner? Examples of that would be nice.

Thanks! :+1: Waiting for part 2 (which is the most interesting to me… :slight_smile:

Shadow/highlights works with big delay, it’s normal?

I have just finished drafting the second part, any feedback is welcome!


Which operating system and which PhotoFlow package are you using?

Linux 5.0.10-arch1-1-ARCH #1 SMP PREEMPT Sat Apr 27 20:06:45 UTC 2019 x86_64 GNU/Linux
Photoflow Version : continuous.r1217.gf2215732-1
12Gb RAM
Intel(R) Core™2 Quad CPU Q9550 @ 2.83GHz

I make new layer for Shadow/highlights and try to change values.

@Chawoosh Are you using the AppImage, or the ARCH package from AUR?