# The Chroma Key to Success

The RGB color value #00FF00 is a rather important one: it is used to make movies, TV shows, weather announcements, and more. It is the famous "TV green" or "green screen" color.

## The Challenge

Your task is to write a program that takes two input images, both in PNG format (or in your image library's image object type) and of the same dimensions. One image can be any old image. The other one is the image that will have a background of the color #00FF00. The output image will consist of the second image overlaid over the first, with no #00FF00 color present (except in the first image). Input and output may be done with files, a GUI, etc. You are allowed to take an array of RGB values as input, as seen here. You may assume that an image has only pixels of full opacity.

## Basically...

Make a program that takes every #00FF00 pixel in one image and replace it with the corresponding pixel in the background image.

## Test Cases

Generously provided by @dzaima: Background:

Foreground:

Output:

Of course, standard loopholes are strictly forbidden. This includes using an online resource to do it for you.
This is , so may the shortest code win and the best programmer prosper...

• May we take an image object in the native format of the language/library as input, or do we have to read the image via filename? – notjagan Jul 16 '17 at 19:20
• @notjagan You may take image objects as input. – ckjbgames Jul 16 '17 at 19:21
• Is I/O of arrays of arrays of integers acceptable or are we actually restricted to some other set of image I/O? – Jonathan Allan Jul 16 '17 at 21:26
• @PeterCordes I will allow that. – ckjbgames Jul 17 '17 at 19:27
• @PeterCordes ok – ckjbgames Jul 17 '17 at 19:35

# x86-64 (and x86-32) machine code, 1315 13 bytes

changelog:

1. Bugfix: the first version was only checking G=0xff, not requiring R and B to be 0. I changed to modifying the background in place so I could use lodsd on the foreground to have fg pixels in eax for the short-form cmp eax, imm32 encoding (5 bytes), instead of cmp dh,0xff (3 bytes).

2. Save 2 bytes: noticed that modifying the bg in place allowed using a memory operand for cmov, saving a 2-byte mov load (and saving a register, in case that matters).

This is a function following the x86-64 System V calling convention, callable directly from C or C++ (on x86-64 non-Windows systems) with this signature:

void chromakey_blend_RGB32(uint32_t *background /*rdi*/,
const uint32_t *foreground /*rsi*/,
int dummy, size_t pixel_count /*rcx*/);

The image format is RGB0 32bpp, with the green component at the 2nd lowest memory address within each pixel. The foreground background image is modified in-place. pixel_count is rows*columns. It doesn't care about rows/columns; it just chromekey blends however many dwords of memory you specify.

RGBA (with A required to be 0xFF) would require using a different constant, but no change in function size. Foreground DWORDs are compared for exact equality against an arbitrary 32-bit constant stored in 4 bytes, so any pixel-order or chroma-key colour can be easily supported.

The same machine code also works in 32-bit mode. To assemble as 32-bit, change rdi to edi in the source. All other registers that become 64-bit are implicit (lodsd/stosd, and loop), and the other explicit regs stay 32-bit. But note that you'll need a wrapper to call from 32-bit C, because none of the standard x86-32 calling conventions use the same regs as x86-64 SysV.

NASM listing (machine-code + source), commented for asm beginners with descriptions of what the more complex instructions do. (Duplicating the instruction reference manual is bad style in normal usage.)

1                       ;; inputs:
2                       ;; Background image pointed to by RDI, RGB0 format  (32bpp)
3                       ;; Foreground image pointed to by RSI, RGBA or RGBx (32bpp)
4          machine      ;; Pixel count in RCX
5          code         global chromakey_blend_RGB32
6          bytes        chromakey_blend_RGB32:
9 00000001 3D00FF0000       cmp    eax, 0x0000ff00  ; check for chromakey
10 00000006 0F4407           cmove  eax, [rdi]       ; eax = (fg==key) ? bg : fg
11 00000009 AB               stosd                   ; [rdi]=eax, edi+=4. store into bg++
12 0000000A E2F4             loop .loop              ;} while(--rcx)
13
14 0000000C C3               ret

##  next byte starts at 0x0D, function length is 0xD = 13 bytes

To get the original NASM source out of this listing, strip the leading 26 characters of each line with <chromakey.lst cut -b 26- > chromakey.asm. I generated this with

## ImageMagick: 28 bytes?

I could have submitted this as an ImageMagick answer but it's not clear how to deal with the arguments. If you want to posit that ImageMagick is a stack-based language (which is kinda sorta not really true but almost... it's weird) then -transparent lime -composite is a function that expects two images on the stack and leaves one merged image on the stack... maybe that's good enough to count?

# MATL, 4037 31 bytes

,jYio255/]tFTF1&!-&3a*5M~b*+3YG

Example run with the offline interpreter. The images are input by their URL's (local filenames could also be provided).

### Explanation

,        % Do this twice
j      %   Input string with URL or filename
Yi     %   Read image as an M×N×3 uint8 array
o      %  Convert to double
255/   %   Divide by 255
]        % End
t        % Duplicate the second image
FTF      % Push 1×3 vector [0 1 0]
1&!      % Permute dimensions to give a 1×1×3 vector
-        % Subtract from the second image (M×N×3 array), with broadcast
&3a      % "Any" along 3rd dim. This gives a M×N mask that contains
% 0 for pure green and 1 for other colours
*        % Mulltiply. This sets green pixels to zero
5M       % Push mask M×N again
~        % Negate
b        % Bubble up the first image
*        % Multiply. This sets non-green pixels to zero
+        % Add the two images
3YG      % Show image in a window

# Pyth, 27 bytes

M?q(Z255Z)GHG.wmgVhded,V'E'

It takes quoted input. The input are the two paths of the image files. Output a file o.png Unfortunately that cannot be tested on the online interpreter for safety reason (' is disabled on it). You will need to get Pyth on your computer to test it.

Explanation

M?q(Z255Z)GHG                  # Define a function g which takes two tuples G and H and returns G if G != (0, 255, 0), H otherwise
V'E'    # Read the images. They are returned as lists of lists of colour tuples
,        # Zip both images
m  hded         # For each couple of lists in the zipped list...
gV             # Zip the lists using the function g
.w                # Write the resulting image to o.png
• The chroma-key blend function on its own is 13 bytes, same as my x86 machine-code answer. I didn't realize earlier that this was a complete program handing image I/O as well. – Peter Cordes Jul 30 '17 at 3:03

# Matlab 2016b and Octave, 62 59 bytes

Input: A = MxNx3 unit8 foreground matrix, B = MxNx3 unit8 background matrix.

k=sum(A(:,:,2)-A(:,:,[1 3]),3)==510.*ones(1,1,3);A(k)=B(k);

Output: A = MxNx3 unit8 matrix

Sample use:

k=sum(A(:,:,2)-A(:,:,[1 3]),3)==510.*ones(1,1,3);A(k)=B(k);

imshow(A)

# C++, 339 bytes

This uses CImg, and it can take files in other formats, too. The result is displayed in a window.

#include<CImg.h>
using namespace cimg_library;
int main(int g,char** v){CImg<unsigned char> f(v[1]),b(v[2]);for(int c=0;c<f.width();c++){for(int r=0;r<f.height();r++){if((f(c,r)==0)&&(f(c,r,0,1)==255)&&(f(c,r,0,2)==0)){f(c,r)=b(c,r);f(c,r,0,1)=b(c,r,0,1);f(c,r,0,2) = b(c,r,0,2);}}}CImgDisplay dis(f);while(!dis.is_closed()){dis.wait();}}

Compile with g++ chromakey.cpp -g -L/usr/lib/i386-linux-gnu -lX11 -o chromakey -pthread.

# R, 135 bytes

Anonymous function, takes 2 png file paths as arguments and output a png picture called a.png.

Slightly ungolfed, with explanations:

function(x,y){
library(png)
# readPNG output a 3D array corresponding to RGBA values on a [0,1] scale:
# Logical mask, telling which pixel is equal to c(0, 1, 0, 1),
# i.e. #00FF00 with an alpha of 1:
m = apply(a, 1:2, function(x) all(x==0:1))
# For each RGB layer, replace that part with the equivalent part of 2nd png:
for(i in 1:4) a[,,i][m] = readPNG(y)[,,i][m]
writePNG(a,"a.png")
}

# SmileBASIC, 90 bytes whats the key

DEF C I,J
DIM T[LEN(I)]ARYOP.,T,I,16711936ARYOP 2,T,T,T
ARYOP 6,T,T,0,1ARYOP 5,I,I,J,T
END

I is the foreground and output, J is the background. Both are integer arrays of pixels, in 32 bit ARGB format.

## Ungolfed

DEF C IMAGE,BACKGROUND 'function
DIM TEMP[LEN(IMAGE)]  'create array "temp"
ARYOP #AOPADD,TEMP,IMAGE,-RGB(0,255,0)    'temp = image - RGB(0,255,0)
ARYOP #AOPCLP,TEMP,TEMP,-1,1              'temp = clamp(temp, -1, 1)
ARYOP #AOPMUL,TEMP,TEMP,TEMP              'temp = temp * temp
ARYOP #AOPLIP,IMAGE,IMAGE,BACKGROUND,TEMP 'image = linear_interpolate(image, background, temp)
END

## Explanation:

ARYOP is a function that applies a simple operation to every element in an array.
It is called like ARYOP mode, output_array, input_array_1, input_array_2, ...

First, to determine which pixels in the image are green, -16711936 (the RGBA representation of the green color) is subtracted from each pixel in the foreground image. This gives an array where 0 represents green pixels, and any other number represents non-green pixels.

To convert all nonzero values to 1, they are squared (to remove negative numbers), then clamped to between 0 and 1.

This results in an array with only 0s and 1s.
0s represent green pixels in the foreground image, and should be replaced with pixels from the background.
1s represent non-green pixels, and those will need to be replaced with pixels from the foreground.

This can easily be done using linear interpolation.

# PHP, 187 bytes

for($y=imagesy($a=($p=imagecreatefrompng)($argv[1]))-1,$b=$p($argv[2]);$x<imagesx($a)?:$y--+$x=0;$x++)($t=imagecolorat)($b,$x,$y)-65280?:imagesetpixel($b,$x,$y,$t($a,$x,$y));imagepng($b);

assumes 24bit PNG files; takes file names from command lines arguments, writes to stdout.
Run with -r.

breakdown

for($y=imagesy( # 2. set$y to image height-1
$a=($p=imagecreatefrompng)($argv[1]) # 1. import first image to$a
)-1,
$b=$p($argv[2]); # 3. import second image to$b
$x<imagesx($a)?:                            # Loop 1: $x from 0 to width-1$y--+$x=0; # Loop 2:$y from height-1 to 0
$x++) ($t=imagecolorat)($b,$x,$y)-65280?: # if color in$b is #00ff00
imagesetpixel($b,$x,$y,$t($a,$x,$y)); # then copy pixel from$a to $b imagepng($b);                                   # 5. output

# JavaScript (ES6), 290 bytes

a=>b=>(c=document.createElementcanvas,w=c.width=a.width,h=c.height=a.height,x=c.getContext2d,x.drawImage(a,0,0),d=x.getImageData(0,0,w,h),o=d.data,o.map((_,i)=>i%4?0:o[i+3]=o[i++]|o[i++]<255|o[i]?255:0),x.drawImage(b,0,0),createImageBitmap(d).then(m=>x.drawImage(m,0,0)||c.toDataURL()))

Takes input as two Image objects (in currying syntax), which can be created with an HTML <image> element. Returns a Promise that resolves to the Base64 data URL of the resulting image, which can be applied to the src of an <image>.

The idea here was to set the alpha value for each #00FF00 pixel to 0 and then paint the foreground, with its background keyed out, on top of the background.

## Test Snippet

Including the foreground and background by their data URLs was too large to post here, so it was moved to CodePen:

Try it online!

# OSL, 83 bytes

shader a(color a=0,color b=0,output color c=0){if(a==color(0,1,0)){c=b;}else{c=a;}}

Takes two inputs. The first is the foreground, and the second, the background.