I noticed that the following line,

Image[Table[x, {y, 1, 250}, {x, 30/256, 40/256, 0.02/256}],
Magnification -> 1]


results in the following image:

In the preceding image, there are 10 vertical bars, each a different shade of grey; based on the range I used in Table, this means that there are 256 shades of grey when going from black to white (ie, 8-bit depth per channel).

Similarly, this effect also shows up in full-color images.

Is there a way to make the colors produced by Image have less of this "staircase effect"? This related question asks about boosting the channel depth beyond 8-bits per RGB channel, but the highest-voted answer does not appear to computationally scale well when applied to image sizes of $3000\times3000$ or larger.

-
Ten grays taken from the range 30/256 to 40/256 are not distinguishable to most human eyes. What do you really want? – m_goldberg Jul 5 '14 at 1:57
@m_goldberg: Apologies, I guess I wasn't being clear. Basically what I would like would be a way to have Image display render an array of numbers with more than 256 shades per color channel of color resolution. I'm not sure if this is actually possible (I don't know much about graphics hardware and what its limitations are). – DumpsterDoofus Jul 5 '14 at 2:05
@DumpsterDoofus You can represent more than 256 shades of grey. In fact by default Image uses floating point values for pixels which can represent a very large number of shades (see ImageType). However, Mathematica cannot display more than 256 shades of grey. (Certain programs on certain operating system can display 1024=2^10 shades if the hardware supports it---that means video card and screen, I think.) So the question: is your aim to represent more than 256 shades, or to display them? The first is easy, the second can be approximated by dithering. – Szabolcs Jul 5 '14 at 3:26
As @m_goldberg says, usually 256 shades are enough, as the screen (!) and eye can't distinguish more. But it can indeed happen on some screens that it's possible to see the posterization effect with only 256 shades and no dithering. I can sometimes see this on my screen, and if I look very very carefully at your image and keep moving the screen (or my head) left and right, then I can (barely) see posterization in your image too. But just barely. There must be some processing in the human eye that accentuates the difference when I move the screen, but not otherwise. – Szabolcs Jul 5 '14 at 3:30
@Szabolcs: My aim was to display them, in which case the dithering suggestion works great. Thanks! – DumpsterDoofus Jul 5 '14 at 16:28

Its not true that a human can not see banding in 256 colors. Whether or not you do depends quite much on what the transfer curve of the monitor is and if its a low quality LCD with diminished color spectrum, like in many phones. Also human eyes are really well adapted to sensing just this kind of thing so while 256 colors in general is fine its not so good when you have straight fronts, Mach bands and all that.

You can use a trick commonly used in 3d rendering, make the color slightly inpure. What happens is the linear interpolation between the edges now cross boundaries at different points for different channels leaving you with more colors to discrete color values travel trough.

Another option is to jitter your sampling pattern a bit so that not all pixels reach the same value at once. This dithers the color causing a more natural spread since human eyes dont pay attention to individual pixels.

Listing 1: Noise pattern jittered per channel you could also jitter only gray

rGray[x_] := IntegerPart[256*(x + RandomReal[{-3.0/256, 3.0/256}])]
dColor[x_] := {rGray[x], rGray[x], rGray[x]}

Image[{
Map[dColor, Range[0, 1, 0.02]],
Map[dColor, Range[0, 1, 0.02]],
Map[dColor, Range[0, 1, 0.02]]
},
"Byte"]


Listing 1 is on purpose a bit exaggerated so that you can more easily see the effect.

Image 1: Result of the code the number of pixels is small and the effect exaggerated zoomed so its easier to spot every pixel and see the effect.

You could also use noise only on one of the color channels or just on the gray to alter the effect to more subtle and not so color noisy. Here's a more subtle real size example

Image 2: a wider gradient noise toned down and in right scale. Open the image to a separate tab so you can see it unscaled

Yo might do better with a pseudo stochastic pattern. Other things to do is do the computation gamma corrected etc etc.

-
Haha, in retrospect the use of random noise dithering should have been obvious, thanks! – DumpsterDoofus Jul 5 '14 at 16:31
For example, using Image[Table[ x + RandomReal[{-1/256, 1/256} 0.2], {y, 1, 250}, {x, 30/256, 40/256, 0.02/256}], Magnification -> 1] just barely smooths away the bars. – DumpsterDoofus Jul 5 '14 at 16:35
@DumpsterDoofus the dither isnt entirely centered you should probably use Image[Table[ x + RandomReal[{-1/256, 2/256} ], {y, 1, 250}, {x, 30/256, 40/256, 0.02/256}]] – joojaa Jul 5 '14 at 16:57

To get an easily distinguishable scale of grays, you could do

ArrayPlot[{Table[x, {x, 0, .9, .1}]}, AspectRatio -> .3]


To get 256 shades of gray

ArrayPlot[{Table[x, {x, 1/256, 1, 1/256}]}, AspectRatio -> .3]


To get 1024 shades of gray

ArrayPlot[{Table[x, {x, 1/1024, 1, 1/1024}]}, AspectRatio -> .3]


I exported this as a tiff (usual formats png and jpeg are 8-bit) and got

Can you see any difference between the 256-shade and the 1024-shade images? As reproduced here I don't see much if any difference. On my computer screen the 1024-shade image looks a little smoother.

I don't think web images can be more than 8-bits per channel, but I could be wrong about that. If I'm right, then the two gray gradients will display the same here no matter what I format I upload in.

-
Is ArrayPlot smoother than Image because the former is not rasterized? I tried ArrayPlot[{Table[x, {x, 30/256, 40/256, 0.02/256}]}, AspectRatio -> 0.3], and the banding is much less significant, even though there still is a certain amount of banding. – seismatica Jul 5 '14 at 2:59
@seismatica. Maybe, but I really don't know. – m_goldberg Jul 5 '14 at 3:40
add a bit hue to your black so its say slightly blue (Cool) or yellow (Warm) then inyerpolate that to white and you sudddenly have far more colors to play with. Pretty standard way to handle banding. or add a bit of noise. – joojaa Jul 5 '14 at 3:49
@joojaa that seems worty of working up as an answer. – george2079 Jul 5 '14 at 12:46
@george2079 Done – joojaa Jul 5 '14 at 16:22
Column[Framed@Graphics[Raster[{Range@#/#},
ColorFunction -> (Blend[{{0, White}, {1, Black}}, #] &)],
AspectRatio -> 0.2,
ImageSize -> 400] & /@
{3^2, 3^3, 3^4, 3^5}]


-