La clave de croma para el éxito

23

El valor de color RGB #00FF00es bastante importante: se utiliza para hacer películas, programas de televisión, anuncios meteorológicos y más. Es el famoso color "TV verde" o "pantalla verde".

El reto

Su tarea es escribir un programa que tome dos imágenes de entrada, tanto en formato PNG (o en el tipo de objeto de imagen de su biblioteca de imágenes) como de las mismas dimensiones. Una imagen puede ser cualquier imagen antigua. La otra es la imagen que tendrá un fondo del color #00FF00. La imagen de salida consistirá en la segunda imagen superpuesta sobre la primera, sin #00FF00color presente (excepto en la primera imagen). La entrada y la salida se pueden hacer con archivos, una GUI, etc. Se le permite tomar una matriz de valores RGB como entrada, como se ve aquí . Puede suponer que una imagen solo tiene píxeles de opacidad total.

Básicamente...

Haga un programa que tome cada #00FF00píxel en una imagen y reemplácelo con el píxel correspondiente en la imagen de fondo.

Casos de prueba

Proporcionado generosamente por @dzaima: Fondo: Primer plano: Salida:
mi imagen de perfil

dennis

salida


Por supuesto, las lagunas estándar están estrictamente prohibidas . Esto incluye el uso de un recurso en línea para hacerlo por usted.
Este es el , por lo que puede ganar el código más corto y prosperar el mejor programador ...

ckjbgames
fuente
2
¿Podemos tomar un objeto de imagen en el formato nativo del idioma / biblioteca como entrada, o tenemos que leer la imagen a través del nombre del archivo?
notjagan
@notjagan Puede tomar objetos de imagen como entrada.
ckjbgames
3
¿Es aceptable la E / S de las matrices de matrices de enteros o estamos realmente restringidos a algún otro conjunto de E / S de imagen?
Jonathan Allan
1
@PeterCordes lo permitiré.
ckjbgames
1
@PeterCordes ok
ckjbgames

Respuestas:

14

Código de máquina x86-64 (y x86-32), 13 15 13 bytes

registro de cambios:

  1. Corrección de errores: la primera versión solo verificaba G = 0xff, no requería que R y B fueran 0. Cambié a modificar el fondo en su lugar para poder usar lodsden primer plano para tener píxeles fg eaxpara la cmp eax, imm32codificación de forma corta (5 bytes ), en lugar de cmp dh,0xff(3 bytes).

  2. Guardar 2 bytes: noté que modificar el bg en su lugar permitía usar un operando de memoria para cmov, guardar una movcarga de 2 bytes (y guardar un registro, en caso de que sea importante).


Esta es una función que sigue la convención de llamadas del Sistema V x86-64, que se puede llamar directamente desde C o C ++ (en sistemas x86-64 que no son Windows) con esta firma:

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

El formato de imagen es RGB0 32bpp, con el componente verde en la segunda dirección de memoria más baja dentro de cada píxel. La imagen de fondo en primer plano se modifica in situ. pixel_countes filas * columnas. No le importan las filas / columnas; solo combina chromekey sin importar la cantidad de palabras de memoria que especifique.

RGBA (con A requerido para ser 0xFF) requeriría el uso de una constante diferente, pero ningún cambio en el tamaño de la función. Las DWORD en primer plano se comparan para una igualdad exacta frente a una constante arbitraria de 32 bits almacenada en 4 bytes, por lo que cualquier orden de píxeles o color de clave de croma puede ser fácilmente compatible.

El mismo código de máquina también funciona en modo de 32 bits. Para ensamblar como 32 bits, cambie rdia edien la fuente. Todos los demás registros que se convierten en 64 bits son implícitos (lodsd / stosd y loop), y los otros registros explícitos permanecen en 32 bits. Pero tenga en cuenta que necesitará un contenedor para llamar desde C de 32 bits, porque ninguna de las convenciones de llamadas estándar x86-32 usa los mismos registros que SysV x86-64.

Listado NASM (código de máquina + fuente), comentado para principiantes de ASM con descripciones de lo que hacen las instrucciones más complejas. (Duplicar el manual de referencia de instrucciones es un mal estilo en uso normal).

 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:
 7 address               .loop:                      ;do {
 8 00000000 AD               lodsd                   ; eax=[rsi], esi+=4. load fg++
 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

Para obtener la fuente NASM original de esta lista, elimine los 26 caracteres principales de cada línea con <chromakey.lst cut -b 26- > chromakey.asm. Generé esto con
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))- listados NASM, dejando más columnas en blanco de las que quiero entre el código de máquina y la fuente. Para crear un archivo de objeto que pueda vincular con C o C ++, use nasm -felf64 chromakey.asm. (O yasm -felf64 chromakey.asm)

no probado , pero estoy bastante seguro de que la idea básica de load / load / cmov / store es sólida, porque es muy simple.

Podría ahorrar 3 bytes si pudiera requerir que la persona que llama pase la constante de clave de croma (0x00ff00) como un argumento adicional, en lugar de codificar la constante en la función. No creo que las reglas habituales permitan escribir una función más genérica que tenga las llamadas configuradas constantes para ello. Pero si lo hizo, el tercer argumento (actualmente dummy) se pasa edxen el x86-64 SysV ABI. Simplemente cambie cmp eax, 0x0000ff00(5B) a cmp eax, edx(2B).


Con SSE4 o AVX, puede hacerlo más rápido (pero con un tamaño de código más grande) con pcmpeqdy blendvpspara hacer una combinación variable de tamaño de elemento de 32 bits controlada por la máscara de comparación. (Con pand, podrías ignorar el byte alto). Para RGB24 empaquetado, puede usar pcmpeqby luego 2x pshufb+ pandpara obtener VERDADERO en bytes donde coinciden los 3 componentes de ese píxel pblendvb.

(Sé que esto es código-golf, pero consideré probar MMX antes de usar un entero escalar).

Peter Cordes
fuente
¿Podría enviarme un ejecutable hecho con este código de máquina?
ckjbgames
x86_32, por favor.
ckjbgames
@ckjbgames: no he escrito una llamada que cargue / guarde imágenes, solo la parte de modificar píxeles en el lugar. Tendría que hacer eso antes de que tenga sentido construir un ejecutable. Pero si lo hiciera, ¿qué tipo de ejecutable? Windows PE32? Linux ELF32? FreeBSD ??
Peter Cordes
ELF32, si quieres.
ckjbgames
@ckjbgames: Si encuentro tiempo, buscaré una biblioteca de carga de imágenes y escribiré algo. Agregué un párrafo sobre cómo volver a convertir la lista en código con el que pueda ensamblar nasm -felf32. (Para 32 bits, también necesitará una función de contenedor para llamar desde C, porque todavía está utilizando los mismos registros que el x86-64 SysV ABI.)
Peter Cordes
13

Mathematica 57 35 bytes

actualización: de forma predeterminada, se elimina un fondo verde con RemoveBackground. La primera presentación incluyó el segundo parámetro innecesario, `{" Fondo ", Verde}".


#~ImageCompose~RemoveBackground@#2&

Elimina el fondo de la imagen 2 y compone el resultado con la imagen 1.


Ejemplo

i1

Lo siguiente, en forma de prefijo en lugar de infijo, muestra más claramente cómo funciona el código.

i2

DavidC
fuente
44
¿Funcionaría esto para imágenes donde no es el "fondo" que es verde? (Parece que queda un pequeño parche verde en su salida)
DBS
Si hubiera una "isla" verde en la imagen, se requeriría el parámetro adicional, `{" Fondo ", Verde}", que elevaría el total a 57 bytes. Esa fue mi primera presentación. Porque no veo verde aislado en el primer plano de la imagen, ese parámetro se eliminó.
DavidC
11

Python 3 + numpy , 59 bytes

lambda f,b:copyto(f,b,'no',f==[0,255,0])
from numpy import*

Pruébalo en línea!

La entrada se da en el formato de una numpymatriz, con tripletes enteros que representan píxeles (donde el #00FF00código de color hexadecimal es equivalente a [0, 255, 0]). La matriz de entrada se modifica en su lugar, lo que está permitido por meta .

Imágenes de ejemplo

Entrada (de la pregunta)

Fondo:

Foto del perfil de ckjbgames

Primer plano:

Foto de perfil de Dennis

Imagen de primer plano después de ejecutar la función:

Imagen fusionada con # 00FF00 reemplazada por píxeles de fondo

Implementación de referencia (se usa opencvpara leer archivos de imagen)

g = lambda f,b:copyto(f,b,'no',f==[0,255,0])
from numpy import*

import cv2

f = cv2.imread("fg.png")
b = cv2.imread("bg.png")

g(f, b)

cv2.imshow("Output", f)
cv2.imwrite("out.png", f)

Muestra la imagen en la pantalla y la escribe en un archivo de salida.

notjagan
fuente
17
¿Qué pasa con todos los puntos rojos en la imagen resultante?
Yytsi
1
He preguntado acerca de E / S; esto parece cumplir con la redacción actual (es decir, "su biblioteca"). Si es así, ¿el propio cv2 requiere la importación de numpy? Si no pudiera hacerlo en el 54 por no utilizar ninguna función numpy, y no importando numpy: lambda f,b:[x[list(x[0])==[0,255,0]]for x in zip(f,b)]. Si la lista de listas de enteros también es realmente aceptable, entonces podría hacerlo en 48 conlambda f,b:[x[x[0]==[0,255,0]]for x in zip(f,b)]
Jonathan Allan
de hecho, incluso si se requiere numpy para que cv2 realice la conversión, sigo pensando que podría hacer la versión de 54 bytes, ya que no necesitamos importar cv2 para el desafío.
Jonathan Allan
55
Si G == 255, el valor se reemplaza incluso si R y B no son cero, lo que conduce a los puntos rojos. Esto también sucede para las otras bandas incluso duras que son menos visibles. Por lo tanto, realiza las verificaciones lógicas de forma independiente y cambia canales individuales incluso si solo se cumple una de las condiciones. Por ejemplo, si un píxel es, [0 255 37]las bandas rojas y verdes serán reemplazadas.
Leander Moesinger
2
@LeanderMoesinger: Bien visto. También tuve ese error en el mío>. <; ¡IDK por qué pensé que solo verificar verde = 0xFF mientras ignoraba R y B era correcto!
Peter Cordes
9

Procesamiento, 116 99 bytes

PImage f(PImage b,PImage f){int i=0;for(int c:f.pixels){if(c!=#00FF00)b.pixels[i]=c;i++;}return b;}

Desafortunadamente, el procesamiento no admite cosas de Java 8, como lambdas.

Implementación de ejemplo: (guarda la imagen como out.pngy también la dibuja en la pantalla)

PImage bg;
void settings() {
  bg = loadImage("bg.png");
  size(bg.width,bg.height);
}
void setup() {
  image(f(bg, loadImage("fg.png")), 0, 0);
  save("out.png");
}
PImage f(PImage b,PImage f){int i=0;for(int c:f.pixels){if(c!=#00FF00)b.pixels[i]=c;i++;}return b;}
dzaima
fuente
Usted puede deshacerse de los settings()y las setup()funciones y basta con ejecutar el código directamente.
Kevin Workman
@KevinWorkman Tengo configuraciones y configuraciones allí para que muestre la imagen en la pantalla, lo que de otro modo no sería posible
dzaima
¿Es #ff00o 0xff00lo mismo que #00ff00en el procesamiento?
Peter Cordes
@PeterCordes # FF00 da un error de sintaxis, lamentablemente, y # 00FF00 == 0xFF00FF00, por lo que 0xFF00 no funciona, ya que comprueba el valor alfa 0
dzaima
@dzaima: ¿Puedes tomar tus imágenes en formato RGB0, por lo que 0x0000FF00es el patrón de bits que estás buscando?
Peter Cordes
6

Bash + ImageMagick, 45 bytes

convert $1 $2 -transparent lime -composite x:

Toma dos imágenes como argumentos y muestra la salida en la pantalla. Cambie x:a $3para escribir en un tercer argumento de archivo en su lugar. El método es simple: lea la imagen de "fondo"; lea la imagen "en primer plano"; reinterprete el color "lima" (# 00ff00) como transparencia en la segunda imagen; luego componga la segunda imagen en la primera y envíela.

ImageMagick: 28 bytes?

Podría haber enviado esto como una respuesta de ImageMagick, pero no está claro cómo lidiar con los argumentos. Si desea plantear que ImageMagick es un lenguaje basado en la pila (que es algo así como cierto, pero casi ... es extraño), entonces -transparent lime -compositees una función que espera dos imágenes en la pila y deja una imagen fusionada en la pila. tal vez eso es lo suficientemente bueno como para contar?

hobbs
fuente
3

MATL , 40 37 31 bytes

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

Ejemplo ejecutado con el intérprete fuera de línea. Las imágenes son ingresadas por sus URL (también se pueden proporcionar nombres de archivos locales).

ingrese la descripción de la imagen aquí

Explicación

,        % 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
Luis Mendo
fuente
3

Pyth , 27 bytes

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

Se necesita entrada citada. La entrada son las dos rutas de los archivos de imagen. Salida de un archivo o.pngDesafortunadamente, no se puede probar en el intérprete en línea por razones de seguridad ( 'está deshabilitado en él). Necesitarás tener Pyth en tu computadora para probarlo.

Explicación

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
Jim
fuente
La función de combinación de clave de croma por sí sola es de 13 bytes, igual que mi respuesta de código de máquina x86. No me di cuenta antes de que este era un programa completo que también entregaba E / S de imágenes.
Peter Cordes
2

Matlab 2016b y Octave, 62 59 bytes

Entrada: A = matriz de primer plano de la unidad8 MxNx3, B = matriz de fondo de la unidad8 MxNx3.

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

Salida: A = MxNx3 unit8 matrix

Uso de la muestra:

A = imread('foreground.png');
B = imread('backgroundimg.png');

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

imshow(A)
Leander Moesinger
fuente
1

C ++, 339 bytes

Esto usa CImg, y también puede tomar archivos en otros formatos. El resultado se muestra en una ventana.

#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();}}

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

ckjbgames
fuente
1

R, 135 bytes

function(x,y,r=png::readPNG){a=r(x);m=apply(a,1:2,function(x)all(x==0:1));for(i in 1:4)a[,,i][m]=r(y)[,,i][m];png::writePNG(a,"a.png")}

Función anónima, toma 2 rutas de archivos png como argumentos y genera una imagen png llamada a.png.

Ligeramente no golfista, con explicaciones:

function(x,y){
    library(png)
    # readPNG output a 3D array corresponding to RGBA values on a [0,1] scale:
    a = readPNG(x)
    # 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")
}
plannapus
fuente
1

SmileBASIC, 90 bytes, ¿cuál es la clave?

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

Ies el primer plano y la salida, Jes el fondo. Ambos son conjuntos enteros de píxeles, en formato ARGB de 32 bits.

Sin golf

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

Explicación:

ARYOP es una función que aplica una operación simple a cada elemento de una matriz.
Se llama comoARYOP mode, output_array, input_array_1, input_array_2, ...

Primero, para determinar qué píxeles en la imagen son verdes, -16711936(la representación RGBA del color verde) se resta de cada píxel en la imagen en primer plano. Esto proporciona una matriz donde 0representa píxeles verdes, y cualquier otro número representa píxeles no verdes.

Para convertir todos los valores distintos de cero a 1, que se elevan al cuadrado (para eliminar los números negativos), entonces sujeta a entre 0y 1.

Esto da como resultado una matriz con solo 0sy 1s.
0s representan píxeles verdes en la imagen de primer plano y deben reemplazarse con píxeles del fondo.
1s representan píxeles no verdes, y estos deberán ser reemplazados por píxeles del primer plano.

Esto se puede hacer fácilmente usando interpolación lineal.

12Me21
fuente
0

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);

asume archivos PNG de 24 bits; toma nombres de archivos de argumentos de líneas de comando, escribe en stdout.
Corre con -r.

Descompostura

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
Titus
fuente
0

JavaScript (ES6), 290 bytes

a=>b=>(c=document.createElement`canvas`,w=c.width=a.width,h=c.height=a.height,x=c.getContext`2d`,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()))

Toma la entrada como dos Imageobjetos (en sintaxis curry), que se pueden crear con un <image>elemento HTML . Devuelve una promesa que se resuelve en la URL de datos Base64 de la imagen resultante, que se puede aplicar a la srcde un <image>.

La idea aquí era establecer el valor alfa para cada #00FF00píxel 0y luego pintar el primer plano, con su fondo borrado, encima del fondo.

Fragmento de prueba

Incluir el primer plano y el fondo por sus URL de datos era demasiado grande para publicarlo aquí, por lo que se movió a CodePen:

Pruébalo en línea!

Justin Mariner
fuente
0

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;}}

Toma dos entradas. El primero es el primer plano, y el segundo, el fondo.

Scott Milner
fuente