Dr. Dobb's Journal September 1999
Gamma correction, a concept central to displaying computer graphics, is essential to quality image generation for visual simulations. Gamma correction requires a system-wide approach encompassing every aspect of the visual solution, including the database, graphics software, graphics hardware, video hardware, and display system. However, many simulated environments are susceptible to quality issues related to gamma correction. Ideally, you would like to be able to anticipate these issues and specify a system suitable for the task at hand. This takes an understanding and consistent treatment of gamma correction throughout a system.
With the move to commercial off-the-shelf solutions, the onus for the correct treatment of gamma correction at a system level falls on integrators who must select and integrate components. With many newer graphics systems paying scant attention to gamma correction and new display technologies emerging, close attention to gamma correction early in the design of a simulator visual system is critical.
In this article, I'll examine the problems associated with gamma correction, focusing on why it is important to simulation in particular. In the process, I'll present a Win32-based OpenGL program (available electronically; see "Resource Center," page 5) that illustrates the effect of correction.
At the heart of the need to gamma correct is the technology used by most display systems -- the cathode ray tube (CRT). This device (found in most monitors, televisions, and projectors) relies on an electron beam from the cathode exciting a phosphor coating on the inside of the tube, which then emits light visible to observers. The voltage of the signal to the CRT is modulated to control brightness as the electron beam sweeps across the tube surface. But there is a problem: For a range of input voltage across the cathode and anode, the resulting brightness is not a linear response. Consequently, by changing the voltage of the signal input to a CRT-based display system there is a corresponding nonlinear change in brightness on the display. By convention, this nonlinearity is denoted by the g (gamma) symbol. To approximately correct this nonlinear display response, the signal level is raised to the power of some compensating value that produces a response, which is the inverse of the display before it is presented to the CRT. The final radiance displayed is the correct response described by the original signal information. This adjustment is called "gamma correction."
Figure 1 shows the results of displaying an image on a monitor and the effects of gamma correction using a power function that raises each value over the range of the scale to the power of the gamma correction value. The amount of gamma correction is expressed as the exponent used in this power function. As Figure 1 demonstrates, without gamma correction, all pictures displayed on CRT devices would simply look wrong, with most portions of displayed images appearing much too dark. Exactly how dark depends on the monitor and the viewing conditions, but a useful exponent value that approximates the nonlinear response characteristics of monitors is around 2.2. Consequently, a gamma correction exponent of around 0.455 is about what is required to compensate, although some displays may require much more or less correction. Other factors, such as ambient light levels and desired contrast levels, may also influence the final amount of gamma correction required.
There is another important effect of gamma correction that helps graphics hardware (with its limited numeric precision) present images of acceptable quality to human beings. To understand this effect, you must first understand some basics of human perception. "Contrast sensitivity" is the visual system's ability to discern a difference between the radiance of two adjacent patches, measured as a ratio of the difference in radiance between the two patches over the absolute radiance of the patches. This value, known as the "Weber Fraction," has been experimentally measured at around 1/50 for most ranges of brightness, although there is a significant drop in sensitivity toward the darker and lighter extents of the adapted visible scale. In other words, the human eye can detect incremental differences of about 1/50th in brightness levels over most of the range of information it's viewing. "Adaptation" is the eye's ability to adapt to different ranges of brightness, depending on current viewing conditions. For our purposes, the discernible information scales from peak white (at whatever level) to around 1/100th of peak white, below which you can assume any information will not be visible.
So what does this have to do with gamma correction? Once your eye has adapted to a displayed image represented by values with the kinds of precision used in conventional graphics (8 bits per component), the image will show banding if the scale used to store those values is linear. Why? Because the 256 discrete-level values represented cannot overcome your eye's contrast sensitivity at the lower end of the scale. In other words, you will be able to detect artifacts due to discrete changes in the brightness of the image due to the magnitude of those changes exceeding the contrast sensitivity of the human eye. When gamma correction is applied, you can improve the quality of a displayed image for a given precision depending, of course, on the precision at each stage in the process of producing and displaying the image. If gamma correction is applied at the wrong place or with inappropriate precision, then quality can be adversely affected. Figure 2 illustrates this. To help visualize the problem, the discrete nature of values in an image is exaggerated by using only 4 bits of data to represent the levels in the image. The top row of Figure 2 shows that an image of limited precision, which is gamma corrected to an image of the same number of bits, will suffer additional banding after display. This may not be a significant problem because the information loss is mainly at the brighter portion of the image, where contrast sensitivity is not a problem.
The center row in Figure 2 illustrates that any amount of additional precision in and after the gamma correction process can never correct the banding artifacts introduced in the original image. More precision can reduce the magnitude of the discrete increments in contrast level in the brighter portions of the image. However, this is not where the problem with respect to contrast sensitivity exists, and may offer no visible improvement to the final displayed image. Although you retain more discrete changes in level after gamma correction, those changes in level do not relate well to human contrast sensitivity.
The bottom row of Figure 2 shows that an image that is gamma corrected from high to low precision has extremely favorable characteristics when displayed. Even though the gamma correction output has the same precision as the top row, you have more precision going into the gamma correction process, so the quantizing of data occurs in a nonuniform space after correction. The final display output shows that the contrast ratios between discrete displayable values are more consistent across the range of displayed information. In other words, where the level is low, the contrast difference between discrete increments is small; where the brightness is high, the contrast between discrete level increments is proportionately large. This means that for a given precision (at least after gamma correction), this system can more readily support the display of images that do not exhibit visible banding artifacts because it "spends" its bits wisely. The bottom sequence would therefore appear to be the ideal gamma correction scenario. However, it is also clear that you either need more precision before gamma correction or the ability to somehow render a gamma-corrected image from the outset to support this scenario. Certainly, more precision in and after gamma correction is the wrong thing to pursue, as is more precision in the digital-to-analog converters (DAC) of a video system. It's way too late in either case to fix visible artifacts and both suggestions highlight the danger of ignoring what is known about the human response to visual stimulus.
Using this knowledge, you can draw some conclusions about the conditions where you are likely to encounter the most serious problems with gamma-related issues. Under dusk conditions in a simulator, the eye should adapt to the range of visual information displayed, and a solution that has acceptable precision during a daytime simulation may produce noticeable artifacts at dusk or dawn. But the ratio for contrast sensitivity is always 1/50 regardless of the particular levels involved, so why should this loss of precision matter? It's important because at the darker end of any scale of viewable conditions, if you have a display black level that is very dark, then you are going to have potentially high radiance increments that exceed the contrast sensitivity of 1/50 toward the darker range of displayed values. The drop off in contrast sensitivity at the dark end of the scale, and the fact that any information below 1/100th the brightness of peak white is largely unobserved, mitigate against exceeding contrast sensitivity thresholds in the lower portion of any range during a daytime simulation. When the eye adapts to a generally dark simulation, the high contrast changes in level, and the darker portions of the scene are no longer in the darkest portion of the adapted scale. This is because the eye has adapted to the darkened visual environment and the high contrast discrete increments in displayable radiance at the lower end of the scale potentially become noticeable. Another contributing factor in some displays is the increased contrast, particularly in the darker portions of the scene, due to reduced ambient light levels when displaying a darker image. In a dome, for example, reducing the overall brightness of a scene will lower the black level of the display and therefore increase the contrast between displayable levels, particularly for the darker portions of the displayed information. This improves the overall contrast ratio at the risk of increasing banding artifacts.
Looking at simulator visual systems, you can consider how gamma correction might be accomplished. Viewing the process from the creation of a database through rendering to displaying the rendered contents of the framebuffer, it is clear that there are several places where gamma correction could be implemented. It is even possible for a single system to mix more than one approach to gamma correction so that the net effect applies the desired correction for the display.
Gamma Correcting in the Database. This is a powerful option that lets the bottom Figure 2 scenario be implemented, even on hardware that has no built-in gamma correction capability. The idea is that a database can be constructed with built-in gamma correction that allows contrast sensitivity problems to be solved with modest precision (say, 8-bits per component) throughout a system.
The problem with correcting a database is that there are a number of things you'd like to be able to do in a database and having to gamma correct it in advance curtails some of them. For instance, if you want to apply a lighting calculation as you render a database, then that lighting calculation had better produce a gamma-corrected result. That requires attention to gamma correction elsewhere; unfortunately, the database cannot solve this problem. Another potential problem is with something as simple as the shading capabilities of the hardware. If you have Gouraud-shaded polygons, then the interpolation on the graphics hardware produces linear interpolation results -- not gamma corrected. Given the magnitude of other problems with most graphics hardware (such as not Gouraud shading perspective correctly), you may not feel that this is a significant obstacle.
Of more serious concern to the gamma correction of databases is the inability to correctly support concepts such as antialiasing or blending. The reason for this is that there is no gamma correction after rendering. Gamma correction is applied in the database. So, if you draw a scene and attempt antialiasing polygon edges, then the fragments accumulate linearly. When displayed, however, the weighting of the various edge fragments will not be gamma corrected. The linear arithmetic of your framebuffer will therefore alias badly because the display response is nonlinear and the fragment weighting at the edges will be wrong. Blending operations to support transparency effects will also suffer. The arithmetic in the framebuffer cannot apply the correcting exponent and it is impossible to apply the correction independently in the database because you can't anticipate the result, which will vary across pixels and over time as the scene moves.
Other simple matters, such as the modulation of a polygon by a texture, also fail. Sure, you can make it work for one case, but if you try to display the texture on multiple polygons of different color or try Gouraud shade and texture, the nonlinear response of the display defeats the linear arithmetic. Various multipass algorithms also suffer. Graphics engines that use light maps applied over textured walls make it appear as though you can gamma correct the texture images and generally improve the overall brightness levels in the scene. Unfortunately, this method cannot produce accurate lighting levels because, typically, the hardware only supports linear, fragment-blending arithmetic in the framebuffer.
Gamma Correcting in the Shading Hardware. Limitations of database gamma correction naturally lead to graphics hardware. One option would be to support everything sent to the graphics hardware with sufficient precision before it was written to the framebuffer. All lighting calculations would produce gamma-corrected values and these would be interpolated nonlinearly in the shading operations. Multiplication, or blending, of texture fragments with interpolated shading results would have to apply suitable correction. Blending additions and multiplications would also have to apply correction. Features such as antialiasing hardware would have to accumulate samples with gamma correction. This is a complex solution and there are simpler options.
Gamma Correcting after Shading with a Lookup Table. Possibly the simplest and most feasible option for effective gamma correction is to have more precision in the framebuffer, gamma correct this to some lower number of bits to the DAC, then output to video. As Figure 2 demonstrates, high precision in the gamma table being corrected to some lower precision is required to solve contrast sensitivity problems. The pitfall here is to conclude that you need more precision out, rather than in. As the middle row in Figure 2 shows, following this logic would retain some additional information, but not information that can be seen by human observers. On the other hand, more bits into the gamma table is essential to an artifact-free solution.
Gamma Correcting in the Display. Behind this approach is the idea that life would be easier if gamma correction just went away. This is desirable because many computer systems have unique gamma correction settings. Add to this the availability of many different multimedia formats that people want to view on many different systems, each of which may include some level of correction. As should be clear from Figure 2, it would be impossible to display artifact-free images using 8-bit level data without gamma correction because this would require a linear display response that would not work with the contrast sensitivity of the eye.
If applied incorrectly, gamma correction will cause changes in the color of objects. Gamma correction is a correction for the monitor guns, and if you apply it, then you display the right image levels. It's the physical nature of the display that determines that gamma correction should be performed on the red, green, and blue components of an image to match the nonlinear response of a display's red, green, and blue guns.
If there is a color shift, then you're probably doing something wrong. For example, you might be building a database in a tool that doesn't gamma correct properly for the display being used. Or you may not be reviewing the database under conditions that match the final simulation-training conditions (that is, ambient light levels set differently and/or incorrect black levels set on the display).
In reality, human response to visual stimulus is far more complex than I've presented here, and there are other areas of interest for gamma correction. For example, the ability to detect contrast changes varies with the frequency of the information, so with higher-frequency information contrast sensitivity drops off substantially. What this means for visual simulation is that you can use dithering to improve display quality, hide low-frequency banding artifacts, and reasonably expect that the new higher frequency artifacts -- due to the dithering -- will be less visible than those you've managed to eliminate would have been.
If you have software or hardware that may be shown on a variety of displays, you need to be able to handle different gamma-correction factors. Some operating systems support system-level gamma correction; others do not. Few systems share the same treatment of gamma correction. One platform might not have system gamma correction, another might have a user-adjustable correction level, and still others might have a fixed gamma correction (say, 0.55 or 0.4).
Even on a single platform there can be differences. Some PC cards have adjustable gamma tables; others do not. The solution to setting gamma correctly either in software or hardware has usually been that users look at the display and adjust the gamma correction exponent, using a test image to help get the setting correct. This is done with system software on platforms which support that kind of thing. On others, application software must provide the interface to specify any supplemental gamma correction required to produce the appropriate net-linear response on the display.
Microsoft Windows, for instance, does not have system-level support for gamma, yet some cards support gamma correction. Compounding this problem, data (such as images) might have gamma correction applied to the data. Unfortunately, most image formats don't have fields to store what the built-in gamma correction is -- and that's important. Without this information, it's impossible to be sure about displaying the data correctly (I have already explained why 8-bit level data needs to be gamma corrected before it is truncated or rounded to 8 bits to avoid quality problems). Thus, any good quality image, almost by definition, should be gamma corrected. Gamma correction is often desirable, but without system support, it can be an unexpected wrench in the works for unsuspecting users who have software that assumes no correction.
By now, you're probably thinking that support for gamma correction is in an almighty mess. Well, it is. With even the basics being handled badly or inconsistently, good support, which tackles the more subtle issues I've described here, seems like a distant prospect.
GAMMA.EXE (available electronically in both source and executable form; see "Resource Center," page 5) is a Win32-based OpenGL program that implements gamma correction by loading a .bmp file, then correcting it. To run the program, you simply drag a 24-bit bmp image onto it, and it will gamma correct and display the image with nine different exponents and plot the gamma correction function.
The modules bmp.h and bmp.c implement the LoadBMP routine, which loads a 24-bit color .bmp file (such as coast.bmp, a sample file that is also available electronically), and creates an array of three-component RGB data, 8-bits per component. The program gamma.c contains the rest of the program.
The draw_graph_image and draw_image functions contain the OpenGL drawing and gamma-correction code. The draw_graph routine draws the power function graph by raising a value between 0 and 1 to the power of the specified gamma value. The draw_graph_image function calls draw_graph, but first it gamma corrects the image and displays it. The gamma correction is performed by building a lookup table of values with 256 entries, which covers all values you might encounter with 8-bit component values. This is faster than computing the exponent for all three components of every pixel.
The gamma correction involves raising each pixel component level to the power of gamma after normalizing between 0.0 and 1.0, then scaling back to the 8-bit format range. Hence the division by 255 first, followed by the multiplication after the power function; see Example 1.
Glassner, Andrew S. Principles of Digital Image Synthesis. San Francisco, CA: Morgan Kaufman, 1995.
Poynton, Charles A. A Technical Introduction to Digital Video. New York, NY: John Wiley & Sons, 1995.
DDJ