Chroma key é uma técnica de pós-produção para juntar duas imagens ou sequência de vídeo anulando uma cor padrão. Este exemplo demonstra como remover o fundo (pano verde) and lidar com ruído e reflexos que podem adicionar um tom esverdeado a elementos da cena.

Abaixo é apresentada a imagem de entrada deste exemplo. Esta imagem apresenta vários tons de cinza no pano de fundo assim como ruído em função da compressão.

A abordagem tem três passos:

1. Converte pixels verde em transparência

Neste passo todos os pixels são processdor no espaço de cor HSV. Pixels verdes são convertidos em transparência. Dado um pixel p, este é considerado "verde" se:

p.hue >= 60 AND p.hue <= 130 AND p.saturation >= 0.4 AND p.lightness >= 0.3

resultado:

2. Processa os pixels verdes restantes resultantes de refelxos e ruído de compressão

Alguns pixels verdes permancem na imagem e não podem ser removidos uma vez que fazem parte do objeto à frente. Isto é muito comum acontecer com cabelo:


Neste caso os pixels são filtrados e sua cor é balanceada para reduzir o tom de verde.

3. Alpha Boundary

O último passo adiciona transparência gradual para todo pixel e seus vizinhos que não são transparentes e que estão nas bordas do objeto à frente.

Resultado:



Source:
package chromaToTransparency;

import marvin.color.MarvinColorModelConverter;
import marvin.image.MarvinImage;
import marvin.io.MarvinImageIO;

	import static marvin.MarvinPluginCollection.*;
	
	public class ChromaToTransparency {
	
		public ChromaToTransparency(){
			MarvinImage image = MarvinImageIO.loadImage("./res/person_chroma.jpg");
			MarvinImage imageOut = new MarvinImage(image.getWidth(), image.getHeight());
			// 1. Convert green to transparency
			greenToTransparency(image, imageOut);
			MarvinImageIO.saveImage(imageOut, "./res/person_chroma_out1.png");
			// 2. Reduce remaining green pixels
			reduceGreen(imageOut);
			MarvinImageIO.saveImage(imageOut, "./res/person_chroma_out2.png");
			// 3. Apply alpha to the boundary
			alphaBoundary(imageOut, 6);
			MarvinImageIO.saveImage(imageOut, "./res/person_chroma_out3.png");
			
		}
		
		private void greenToTransparency(MarvinImage imageIn, MarvinImage imageOut){
			for(int y=0; y<imageIn.getHeight(); y++){
				for(int x=0; x<imageIn.getWidth(); x++){
					
					int color = imageIn.getIntColor(x, y);
					int r = imageIn.getIntComponent0(x, y);
					int g = imageIn.getIntComponent1(x, y);
					int b = imageIn.getIntComponent2(x, y);
					
					double[] hsv = MarvinColorModelConverter.rgbToHsv(new int[]{color});
					
					if(hsv[0] >= 60 && hsv[0] <= 130 && hsv[1] >= 0.4 && hsv[2] >= 0.3){
						imageOut.setIntColor(x, y, 0, 127, 127, 127);
					}
					else{
						imageOut.setIntColor(x, y, color);
					}
					
				}
			}
		}
		
		private void reduceGreen(MarvinImage image){
			for(int y=0; y<image.getHeight(); y++){
				for(int x=0; x<image.getWidth(); x++){
					int r = image.getIntComponent0(x, y);
					int g = image.getIntComponent1(x, y);
					int b = image.getIntComponent2(x, y);
					int color = image.getIntColor(x, y);
					double[] hsv = MarvinColorModelConverter.rgbToHsv(new int[]{color});
					
					if(hsv[0] >= 60 && hsv[0] <= 130 && hsv[1] >= 0.15 && hsv[2] >= 0.15){
						if((r*b) !=0 && (g*g) / (r*b) > 1.5){
							image.setIntColor(x, y, 255, (int)(r*1.4), (int)g, (int)(b*1.4));
						} else{
							image.setIntColor(x, y, 255, (int)(r*1.2), g, (int)(b*1.2));
						}
					}
				}
			}
		}
		
		public static void main(String[] args) {
			new ChromaToTransparency();
		}
	}