| |
|
|
||||
![]() |
||||||
| |
|
|||||
|
What
Happened to My Colors!?! By now, I'm sure that you recognize this waveform as being a highly saturated signal with relatively low luminance. This means it must be red or blue. The phase of the waveform makes it obvious that it is red. Okay, maybe not so obvious, but the sine wave is clearly shifted to the right a bit compared to yellow, thus indicating a non-yellow hue. Saturated colors that use two primaries tend to go off the top of the graph, because they are naturally brighter. Saturated colors that have a lot of just one primary tend to go off the bottom of the graph. This signal went down to -0.333, but was clamped at -0.20.
The problem is, the NTSC signal isn't allowed to go up 33% higher than white. That would have destroyed backwards compatibility with the millions of monochrome sets in use when color NTSC was designed. Scaling all of the signals down would have allowed pure yellow to be transmitted, but it would have reduced the precision for other colors, and would have made color TV signals dim when compared with monochrome signals. So, the designers of the NTSC color system compromised. Pure yellow and cyan are pretty rare in nature, so they specified that the signal could go 20% above white and 20% below black. In other words, they said that you are not allowed to broadcast certain highly saturated colors. In some cases, it's not even a matter of not being allowed to; it's actually physically impossible! On broadcast TV, the voltage encoding for 20% higher-than-white is zero volts (high voltage is used for black). So, pure yellow would require negative voltage, an impossible voltage to broadcast. Clamping the signal causes all the same problems you get with clipping when recording loud sounds. The signal usually gets terribly distorted, and it's not worth doing. We're not trying to broadcast our graphics, so our bright yellows and pure reds might actually make it to the television, but TVs were never designed to handle those signals so it is unlikely that they will handle them well. These highly saturated colors tend to throb and pulsate. If the signal is clipped to the broadcast limits, then the signal will be distorted and nearby pixels may show unpredictable problems. Highly saturated colors also increase problems with chroma and luminance cross talk by making it harder to cleanly separate chroma and luminance.
1, 1, 0 - yellow You can fix red by reducing the luminance, but you have to reduce it to 0.60 to get it just barely into legal range. This changes the color a lot. Alternately you can fix red by reducing the saturation, while holding the luminance constant. This means subtracting 0.15 from the red and adding about 0.06 to blue and green. This changes the color much less. Finally, you can fix red by just adding 0.10 to green and blue. There is no mathematical justification for this. Instead, you're actually increasing the brightness, but it looks very good. It fixes the problem because increasing the green and blue lowers the saturation more than it increases the brightness. Similar fixes work for the other problematic colors. The saturation fix seems to be the best programmatic solution, but an artist's touch can give even better results. The following code is a simple function for detecting illegal NTSC colors: #include <math.h> const double pi = 3.14159265358979323846; // Specify the maximum
amount you are willing to go // Returns zero if
all is well. If the signal will go too high it returns how //
Convert from YUV to YIQ space. This could be combined with //
Calculate the amplitude of the chroma signal. Once you have identified a hot color there are two easy programmatic ways to fix it - reduce the luminance, or reduce the saturation. Reducing the luminance is easy, just multiply R', G' and B' by the desired reduction ratio. Reducing chrominance is only slightly harder. You need to convert to YIQ (although YUV actually works just as well) then scale I and Q or U and V and then convert back to RGB (Martindale 91). The code snippets below will calculate and use the appropriate reduction factors to get the colors exactly into legal range. // This epsilon value
is necessary because the standard color space conversion void FixAmplitude(double&
r, double& g, double& b) void FixSaturation(double&
r, double& g, double& b) Complete code is available on my web site. Included is a sample app that lets you interactively adjust RGB and HSV (hue, saturation, value) values while viewing the corresponding composite waveform. The program also lets you invoke the FixAmplitude() and FixSaturation() functions, to compare their results. It's worth noting that there are also YIQ colors that represent RGB colors outside of the unit RGB cube. Video editors who do some of their work in YIQ or YUV need to deal with this, but we don't have to. Overall,
it's best to avoid using colors that will go out of range. Many image-editing
programs have a tool that will find and fix 'illegal' or 'hot' NTSC colors.
If you want more control, a tool that finds and fixes illegal colors is
available on my web site. One advantage of a home grown tool is that you
can decide, based on your target console and artistic sensibilities, how
close to the maximum you want to go, and how to fix colors when you go
out of range. The reason our images are mostly gamma corrected is that TVs and computer monitors naturally do a translation from gamma corrected RGB to linear light (Poynton 98). This is a good thing because it means that the available RGB values are more evenly spread out according to what our eyes can see. Since we create our texture maps and RGB values by making them look good on a monitor, they are naturally gamma corrected (although some adjustments are needed because monitors and televisions may have different gamma values). However,
if we do a lot of alpha blending and antialiasing then we are doing non-gamma
corrected pixel math. If we do a 50% blend between a white pixel and a
black pixel, presumably we want the result to be a pixel that represents
half the light density, or half as many photons per second. However, the
answer that we typically get is 127, which does not represent half as
many photons, because the frame buffer values don't represent linear light.
The correct answer that you can verify by playing around with various
sized black white dither patterns and comparing them to various grays,
is typically around a 186 gray. It's not a huge error, and unless graphics
hardware starts supporting gamma corrected alpha blending, there is nothing
we can do. ______________________________________________________ |
||||||||||||||||||||||||||
|
|