Airbrush/Brush Banding Effect At Low Pressure/Opacity
This discussion is connected to the gimp-developer-list.gnome.org mailing list which is provided by the GIMP developers and not related to gimpusers.com.
This is a read-only list on gimpusers.com so this discussion thread is read-only, too.
4B047412.5050901@indicium.us | Christopher Howard | 18 Nov 23:24 |
Airbrush/Brush Banding Effect At Low Pressure/Opacity | James Cox | 19 Nov 01:47 |
Airbrush/Brush Banding Effect At Low Pressure/Opacity | David Gowers | 19 Nov 04:31 |
Airbrush/Brush Banding Effect At Low Pressure/Opacity | Martin Renold | 21 Nov 23:09 |
Airbrush/Brush Banding Effect At Low Pressure/Opacity
Airbrush/Brush Banding Effect At Low Pressure/Opacity
Christopher Howard wrote:
Jay Cox wrote:
On Oct 20, 2009, at 1:39 PM, Sven Neumann wrote:
Hi,
On Tue, 2009-10-20 at 12:22 -0800, Christopher Howard wrote:
Though having a far from sufficient understanding of how the GIMP brush painting process works, it seems to me like this is the fundamental problem: In making black-and-white brushes semi-transparent, GIMP somehow "levels out" the grey-scale, so that certain ranges of darkness become the same. On a fuzzy brush, the result is that the fine graduation is changed to appear stepped or banded.
The problem here is that the brush masks are 8bit only. With such a limited set of values to start with, banding is basically unavoidable.
Sven
You should be able to get rid of most of the banding by performing error diffusion when multiplying the opacity into the brush mask. I suspect that a simple 1d error diffusion algorithm will be sufficient and I wouldn't bother with a Floyd-Steinberg type algorithm unless you could come up with an example where the simpler algorithm showed visible artifacts.
I believe the function that introduces the banding is apply_mask_to_sub_region in paint-funcs.c or in the function apply_mask_to_alpha_channel depending on your point of view. I have not worked with that code in a long time and I could be wrong about that.
You will need to make the starting error term effectively random or you run the risk of letting the upper-left pixel in each tile get special treatment.
Good luck,
Jay Cox
jaycox@gimp.orgThank you for your guidance in this matter. However, I am wondering: Wouldn't error diffusion tend to sharpen, rather than smooth out, the banding? It seems like some kind of blurring is more what we need.
I played around in apply_mask_to_alpha_channel() a little bit and my results seemed to confirm that. But maybe I misunderstand error diffusion or precisely what you mean about how to use it?
Error diffusion will get rid of banding at the cost of a small amount of noise. If you paint with the same error diffused mask multiple times you will amplify the noise. Blurring the mask can not help.
An Example: The input mask is a smooth gradient from 0 to 10 that we are scaling to 10%
current scaling:
0 0 0 0 0 1 1 1 1 1
current painted 10X in the same location:
0 0 0 0 0 10 10 10 10 10
Obviously that leaves a hard break between the 5th and 6th value.
Note that if we blurred the resulting mask we would end up with the exact same values.
Error diffusion (static):
0 0 0 1 0 1 0 1 1 1
Error diffusion (static) painted 10X in the same location:
0 0 0 10 0 10 0 10 10 10
It looks like we have even more banding, though it would likely look like noise in a real image.
Error diffusion with a random initial error term:
0 0 0 1 0 1 0 1 1 1 or
0 1 0 0 1 0 1 0 1 1 or
0 0 1 0 0 1 0 1 0 1 or ......
Error diffusion with a random initial error term painted 10X in the same
location:
0 1 2 3 4 5 6 7 8 9 10 or
0 1 3 3 4 5 6 7 7 9 9 or ......
The results are not mathematically perfect, but should look very good visually.
If you are initializing the error term to a random value and still seeing banding I would suspect that the brush system is caching the results of your function and applying the same mask over again. (simple way to check is to put a printf statement in your function and check to see if it prints out every time you click with the paintbrush)
Thinking about this some more, the error diffusion is probably not even necessary. You should be able to get by with some random dithering like the following (if it wasn't slower than molasses):
result = floor(input * opacity + g_random_double());
Jay Cox jaycox@gimp.org
PS: If this message came through twice I apologize. The other one (if it exists) contains errors (more errors?) and should be ignored.
Airbrush/Brush Banding Effect At Low Pressure/Opacity
On Thu, Nov 19, 2009 at 11:17 AM, James Cox wrote:
Christopher Howard wrote:
Thinking about this some more, the error diffusion is probably not even necessary. You should be able to get by with some random dithering like the following (if it wasn't slower than molasses):
result = floor(input * opacity + g_random_double());
If you pregenerate a lookup table (say 256 entries), keep a counter which cycles 0,1,2,....255,0,1,2,....255, and only add a random integer to that counter at the start of every dab, you could get that going at decent speeds.
Airbrush/Brush Banding Effect At Low Pressure/Opacity
On Thu, Nov 19, 2009 at 02:01:13PM +1030, David Gowers wrote:
On Thu, Nov 19, 2009 at 11:17 AM, James Cox wrote:
result = floor(input * opacity + g_random_double());
If you pregenerate a lookup table (say 256 entries), keep a counter which cycles 0,1,2,....255,0,1,2,....255, and only add a random integer to that counter at the start of every dab, you could get that going at decent speeds.
I have implemented this in (historical) release 0.5.1 of MyPaint. Using 8 bytes of precalculated noise; brush_dab.c line 120 in case anyone cares.
I got sick of the noise, though. It tends to get visible when you use brushes that really need this correction. Not worth the effort I think. The better solution is to calculate in 16bpc, or use different brushes, or don't use incremental mode (MyPaint supports only incremental). See http://mypaint.intilinux.com/?p=19 for a sample.