Chroma Key is a technique for mixing two images together, in which a color (or a small color range) from the overlay image is made transparent, revealing another image (the background) behind it. This technique is also known as greenscreen or bluescreen, since green and blue screens are often used to denote the overlay image's region which must be removed or not rendered over the background image.
Implementation of chroma key composition or mixing is trivial, but results are often not perfect since it is difficult to have a clean separation between pixels belonging to the foreground (which must be rendered over a background image) and pixels that must be considered transparent or translucent (which shouldn't be rendered) due to antialias effects, compression artifacts or other reasons. Problems may also occur when the transparent or translucent region is not homogeneous, which makes the determination of the transparency/translucency values hard or imprecise.
In this chapter we'll see some simple examples of chroma key and suggestions on how to improve the results. For an example, we'll render a UFO image with a green background (which will not be rendered) over the Johns Hopkins University's Upper Quad (see images below).
Background image -- Johns Hopkins University's Upper Quad
Overlay image (green pixels will not be rendered)
In this chapter transparent pixels are pixels that, when overlaid over others, will not change their colors, being, for all practical purposes, invisible. Translucent pixels, when overlaid over others, may change their colors depending on the amount of translucency. Translucent pixels can be considered partially transparent.
Transparency
One simple way to implement chroma key composition is using a color that will be considered fully transparent or invisible when one image is overlaid into another. We can then loop over all pixels in the overlay image: if a pixel's color is the same as the transparent color then we skip it, otherwise we paint it over the background image.
With this technique, a pixel in the overlay image will be either painted over the background image or not painted at all -- This technique is implemented in the application in the class TransparencyByColors, shown below.
TransparencyByColors.java
1 /*
2 * Part of the Java Image Processing Cookbook, please see
3 * http://www.lac.inpe.br/~rafael.santos/JIPCookbook.jsp
4 * for information on usage and distribution.
5 * Rafael Santos (rafael.santos@lac.inpe.br)
6 */
7 package howto.chromakey;
8
9 import java.awt.image.BufferedImage;
10 import java.awt.image.WritableRaster;
11 import java.io.File;
12 import java.io.IOException;
13
14 import javax.imageio.ImageIO;
15 import javax.swing.JFrame;
16
17 import com.sun.media.jai.widget.DisplayJAI;
18
19 /**
20 * This class demonstrates how to select a color for transparency to create overlays of images.
21 * It reads two images from the disk, drawing the second one over the first one but considering
22 * green pixels on the second one as transparent.
23 */
24 public class TransparencyByColors
25 {
26 /**
27 * @param args the arguments for the application (will be ignored)
28 * @throws IOException
29 */
30 public static void main(String[] args) throws IOException
31 {
32 // Read the input images. We assume that the first image is the background, and that it is larger
33 // than the second image.
34 BufferedImage background = ImageIO.read(new File("dsc00099_large.jpg"));
35 WritableRaster raster = background.getRaster();
36 BufferedImage layer = ImageIO.read(new File("UFO.png"));
37 int width = layer.getWidth();
38 int height = layer.getHeight();
39 // We will shift the overlay image over the background this amount.
40 int shiftX = 72;
41 int shiftY = 80;
42 // Slow method: scan all input (layer) image pixels, plotting only those which are not green.
43 int lPixel,red,green,blue;
44 for(int w=0;w
45 for(int h=0;h
46 {
47 lPixel = layer.getRGB(w,h);
48 if ((lPixel&0x00FFFFFF) != 0x00FF00) // Not pure green!
49 {
50 red = (int)((lPixel&0x00FF0000)>>>16); // Red level
51 green = (int)((lPixel&0x0000FF00)>>>8); // Green level
52 blue = (int) (lPixel&0x000000FF); // Blue level
53 // Set the pixel on the output image's raster.
54 raster.setPixel(w+shiftX,h+shiftY,new int[]{red,green,blue,255});
55 }
56 } // end for
57 // Save the image as a PNG via ImageIO.
58 ImageIO.write(background,"PNG",new File("transparency.png"));
59 // Display the input and output images.
60 JFrame frame = new JFrame("Transparency by Colors");
61 frame.add(new DisplayJAI(background));
62 frame.pack();
63 frame.setVisible(true);
64 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
65 }
66 }
This application will open the background and overlay image, and for each pixel in the overlay image will get its color. If the pixel is not exactly green it will be paint over the raster of the background image, which will be stored in disk and displayed in a JFrame. The resulting image is shown below.
Overlaid image (using transparency)
We can see that using a single color as transparent yields noticeable problems: there are some green streaks on the edges of the UFO, which can be seen in a enlarged region of the overlaid image, shown below. This happened because the UFO image is antialiased, therefore there are some pixels' colors which are a mixture of the edges and the green background, not being pure green, therefore painted over the background image.
Detail of the overlaid image (using transparency)
Translucency
A better way to do a chroma key composition is to use different degrees of transparency, so overlay and background pixels' colors will be mixed accordingly to the similarity of the overlay pixel color to the color defined as transparent.
In order to achieve this, we must calculate the amount of translucency for each pixel, and use this amount to weight how much of the background and overlay colors will influence the final color of a pixel. One simple way do to this is to calculate the distance from a overlay pixel's color to the color that must be transparent, normalizing this value.
With a translucency value between 0 (totally translucent or invisible) and 1 (totally opaque), we can calculate each color component of a pixel as R=L*translucency+B*(1-translucency), where R is the output color value, L is the input overlay color value and B is the input background color value.
This technique is implemented in the application in the class TranslucencyByColors, shown below. The amount of translucency is calculated as the Euclidean distance between the color on the layer image and the color which should be transparent (green), limited to 100 units of distance and normalized to the range [0,1].
TranslucencyByColors.java
1 /*
2 * Part of the Java Image Processing Cookbook, please see
3 * http://www.lac.inpe.br/~rafael.santos/JIPCookbook.jsp
4 * for information on usage and distribution.
5 * Rafael Santos (rafael.santos@lac.inpe.br)
6 */
7 package howto.chromakey;
8
9 import java.awt.image.BufferedImage;
10 import java.awt.image.WritableRaster;
11 import java.io.File;
12 import java.io.IOException;
13
14 import javax.imageio.ImageIO;
15 import javax.swing.JFrame;
16
17 import com.sun.media.jai.widget.DisplayJAI;
18
19 /**
20 * This class demonstrates how to select a color for translucency. It reads two images from the disk,
21 * drawing the second one over the first one but considering green pixels on the second one as
22 * transparent/translucent -- the closer the color is to green, the more transparent it is.
23 */
24 public class TranslucencyByColors
25 {
26 /**
27 * @param args the arguments for the application (will be ignored)
28 * @throws IOException
29 */
30 public static void main(String[] args) throws IOException
31 {
32 // Read the input images. We assume that the first image is the background, and that it is larger
33 // than the second image.
34 BufferedImage background = ImageIO.read(new File("dsc00099_large.jpg"));
35 WritableRaster raster = background.getRaster();
36 BufferedImage layer = ImageIO.read(new File("UFO.png"));
37 int width = layer.getWidth();
38 int height = layer.getHeight();
39 // We will shift the overlay image over the background this amount.
40 int shiftX = 72;
41 int shiftY = 80;
42 // Slow method: scan all input (layer) image pixels and corresponding background pixels.
43 // Calculate its "greenness" and translucency and recreate the pixels' values, plotting
44 // them over the background.
45 int iPixel,lPixel;
46 int refRed = 0;
47 int refGreen = 255;
48 int refBlue = 0;
49 int iRed,iGreen,iBlue,lRed,lGreen,lBlue,oRed,oGreen,oBlue;
50 for(int w=0;w
51 for(int h=0;h
52 {
53 iPixel = background.getRGB(w+shiftX,h+shiftY);
54 iRed = (int)((iPixel&0x00FF0000)>>>16); // Red level
55 iGreen = (int)((iPixel&0x0000FF00)>>>8); // Green level
56 iBlue = (int) (iPixel&0x000000FF); // Blue level
57 lPixel = layer.getRGB(w,h);
58 lRed = (int)((lPixel&0x00FF0000)>>>16); // Red level
59 lGreen = (int)((lPixel&0x0000FF00)>>>8); // Green level
60 lBlue = (int) (lPixel&0x000000FF); // Blue level
61 // Calculate the translucency, based on the green value of the layer. To make calculations
62 // easier, let's assume that the translucency is a value between 0 (invisible) and 1 (opaque).
63 double distance = Math.sqrt((refRed-lRed)*(refRed-lRed)+
64 (refGreen-lGreen)*(refGreen-lGreen)+
65 (refBlue-lBlue)*(refBlue-lBlue));
66 // Convert distance to the range 0-100.
67 distance = Math.min(distance,100f);
68 float translucency = ((float)distance/100f);
69 // Recalculate the RGB coordinates of the layer and background pixels, using the translucency
70 // as a weight.
71 oRed = (int)(translucency*lRed+(1-translucency)*iRed);
72 oGreen = (int)(translucency*lGreen+(1-translucency)*iGreen);
73 oBlue = (int)(translucency*lBlue+(1-translucency)*iBlue);
74 // Set the pixel on the output image's raster.
75 raster.setPixel(w+shiftX,h+shiftY,new int[]{oRed,oGreen,oBlue,255});
76 } // end for
77 // Save the image as a PNG via ImageIO.
78 ImageIO.write(background,"PNG",new File("translucency.png"));
79 // Display the input and output images.
80 JFrame frame = new JFrame("Translucency by Colors");
81 frame.add(new DisplayJAI(background));
82 frame.pack();
83 frame.setVisible(true);
84 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
85 }
86 }
The resulting image is shown below. It is slightly better looking than the overlay created with only transparency, but we still can see some green edges in parts of the image.
Overlaid image (using translucency)
Detail of the overlaid image (using translucency)
Another way to calculate translucency is using a color space based on hue -- this way, we can calculate the differences between the transparent color and a pixel's color in a more intuitive way.
The next example shows how to calculate the translucency using the HSB (hue, saturation, brightness) color space and a user-defined tolerance value. With a high tolerance value, even colors different than the reference green will be considered translucent, while with a low value, only colors really close to the reference green will be translucent. More details on the tolerance value will be shown below.
The example is implemented in the application in class TranslucencyByColorsHSB.
TranslucencyByColorsHSB.java
1 /*
2 * Part of the Java Image Processing Cookbook, please see
3 * http://www.lac.inpe.br/~rafael.santos/JIPCookbook.jsp
4 * for information on usage and distribution.
5 * Rafael Santos (rafael.santos@lac.inpe.br)
6 */
7 package howto.chromakey;
8
9 import java.awt.Color;
10 import java.awt.image.BufferedImage;
11 import java.awt.image.WritableRaster;
12 import java.io.File;
13 import java.io.IOException;
14
15 import javax.imageio.ImageIO;
16 import javax.swing.JFrame;
17
18 import com.sun.media.jai.widget.DisplayJAI;
19
20 /**
21 * This class demonstrates how to select a color for translucency. It reads two images from the disk,
22 * drawing the second one over the first one but considering green pixels on the second one as
23 * transparent/translucent -- the closer the color is to green, the more transparent it is.
24 * In this example, the similarity to green will be calculated using the hue coordinate (HSB color
25 * space).
26 */
27 public class TranslucencyByColorsHSB
28 {
29 /**
30 * @param args the arguments for the application (will be ignored)
31 * @throws IOException
32 */
33 public static void main(String[] args) throws IOException
34 {
35 // Read the input images. We assume that the first image is the background, and that it is larger
36 // than the second image.
37 BufferedImage background = ImageIO.read(new File("dsc00099_large.jpg"));
38 WritableRaster raster = background.getRaster();
39 BufferedImage layer = ImageIO.read(new File("UFO.png"));
40 int width = layer.getWidth();
41 int height = layer.getHeight();
42 // We will shift the overlay image over the background this amount.
43 int shiftX = 72;
44 int shiftY = 80;
45 // Slow method: scan all input (layer) image pixels and corresponding background pixels.
46 // Calculate its "greenness" and translucency and recreate the pixels' values, plotting
47 // them over the background.
48 int iPixel,lPixel;
49 float targetHue = 1f/3f;
50 float tolerance = 0.1f;
51 int iRed,iGreen,iBlue,lRed,lGreen,lBlue,oRed,oGreen,oBlue;
52 for(int w=0;w
53 for(int h=0;h
54 {
55 // Background pixels.
56 iPixel = background.getRGB(w+shiftX,h+shiftY);
57 iRed = (int)((iPixel&0x00FF0000)>>>16); // Red level
58 iGreen = (int)((iPixel&0x0000FF00)>>>8); // Green level
59 iBlue = (int) (iPixel&0x000000FF); // Blue level
60 // Layer pixels.
61 lPixel = layer.getRGB(w,h);
62 lRed = (int)((lPixel&0x00FF0000)>>>16); // Red level
63 lGreen = (int)((lPixel&0x0000FF00)>>>8); // Green level
64 lBlue = (int) (lPixel&0x000000FF); // Blue level
65 float[] lHSB = Color.RGBtoHSB(lRed,lGreen,lBlue,null);
66 // Calculate the translucency, based on the green value of the layer, using HSB coordinates.
67 // To make calculations easier, let's assume that the translucency is a value between 0
68 // (invisible) and 1 (opaque).
69 float deltaHue = Math.abs(lHSB[0]-targetHue);
70 float translucency = (deltaHue/tolerance);
71 translucency = Math.min(translucency,1f);
72 // Recalculate the RGB coordinates of the layer and background pixels, using the translucency
73 // as a weight.
74 oRed = (int)(translucency*lRed+(1-translucency)*iRed);
75 oGreen = (int)(translucency*lGreen+(1-translucency)*iGreen);
76 oBlue = (int)(translucency*lBlue+(1-translucency)*iBlue);
77 // Set the pixel on the output image's raster.
78 raster.setPixel(w+shiftX,h+shiftY,new int[]{oRed,oGreen,oBlue,255});
79 } // end for
80 // Save the image as a PNG via ImageIO.
81 ImageIO.write(background,"PNG",new File("translucencyHSB.png"));
82 // Display the input and output images.
83 JFrame frame = new JFrame("Translucency by Colors (HSB)");
84 frame.add(new DisplayJAI(background));
85 frame.pack();
86 frame.setVisible(true);
87 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
88 }
89
90 }
The resulting image (shown below) appears even better than the obtained with other approaches in this chapter. The green tint in the UFO's edge is almost unnoticeable.
Overlaid image (using translucency calculated in the HSB color space)
Detail of overlaid image (using translucency calculated in the HSB color space)
A low tolerance value will ensure that only pixels which color is very close to green will be translucent, probably causing some greenish pixels to be only partially translucent. A large value will make greenish pixels more transparent, but can affect also other colors, making them translucent too. The figure below shows details on the resulting images obtained with three different tolerance values. We can see that a value of 0.4 removes all the greenish tint around the edges of the UFO, but also makes the other colors partially translucent, specially the ones which hue is close to green.
Composition using tolerance value 0.01 |
Composition using tolerance value 0.1 |
Composition using tolerance value 0.4 |
Tips and tricks
In the examples in this chapter we used a PNG overlay image (lossless compression) which was set over a JPEG image. If we use a JPEG or other lossy compressed image as the overlay, the lossy compression artifacts may cause smudges and other artifacts when overlaid.
This is shown in the example below, where a highly compressed image was overlaid into another. Even with a good composition algorithm the compression artifacts causes the green halo and other defects. To avoid this, we must use losslessly compressed images as overlays, or at least images compressed without too much loss of information.
Overlaid image (with a compressed overlay image)
Detail of overlaid image (with a compressed overlay image)
Some problems that may occur (and possible solutions) are:
Some other possible ways to get better looking results are:
Copyright © 2011 - All Rights Reserved - Softron.in
Template by Softron Technology