La chiave cromatica per il successo


23

Il valore del colore RGB #00FF00è piuttosto importante: viene utilizzato per realizzare film, programmi TV, annunci meteorologici e altro ancora. È il famoso colore "TV verde" o "schermo verde".

La sfida

Il tuo compito è scrivere un programma che acquisisca due immagini di input, sia nel formato PNG (o nel tipo di oggetto immagine della libreria di immagini) che delle stesse dimensioni. Un'immagine può essere qualsiasi vecchia immagine. L'altra è l'immagine che avrà uno sfondo del colore #00FF00. L'immagine in uscita sarà costituita dalla seconda immagine sovrapposta alla prima, senza #00FF00colore presente (tranne nella prima immagine). L'input e l'output possono essere eseguiti con file, una GUI, ecc. È consentito prendere una matrice di valori RGB come input, come mostrato qui . Si può presumere che un'immagine abbia solo pixel di opacità completa.

Fondamentalmente...

Crea un programma che prende tutti i #00FF00pixel in un'immagine e sostituiscilo con il pixel corrispondente nell'immagine di sfondo.

Casi test

Generosamente fornito da @dzaima: Background: Foreground: Output:
la mia immagine del profilo

dennis

produzione


Naturalmente, le scappatoie standard sono severamente vietate . Ciò include l'uso di una risorsa online per farlo per te.
Questo è , quindi può vincere il codice più breve e prosperare il miglior programmatore ...


2
Possiamo prendere un oggetto immagine nel formato nativo della lingua / libreria come input, o dobbiamo leggere l'immagine tramite il nome file?
notjagan,

@notjagan È possibile prendere oggetti immagine come input.
ckjbgames,

3
L'I / O di array di array di numeri interi è accettabile o siamo effettivamente limitati a qualche altro set di I / O di immagine?
Jonathan Allan,

1
@PeterCordes Lo permetterò.
ckjbgames,

1
@PeterCordes ok
ckjbgames

Risposte:


14

codice macchina x86-64 (e x86-32), 13 15 13 byte

changelog:

  1. Bugfix: la prima versione stava solo controllando G = 0xff, non richiedendo che R e B fossero 0. Ho cambiato modificando lo sfondo in posizione in modo da poter usare lodsdin primo piano per avere pixel fg eaxper la cmp eax, imm32codifica in formato breve (5 byte ), anziché cmp dh,0xff(3 byte).

  2. Salva 2 byte: notato che la modifica del bg in atto ha consentito l'uso di un operando di memoria per cmov, il salvataggio di un movcarico di 2 byte (e il salvataggio di un registro, nel caso in cui sia importante).


Questa è una funzione che segue la convenzione di chiamata System V x86-64, richiamabile direttamente da C o C ++ (su sistemi x86-64 non Windows) con questa firma:

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

Il formato dell'immagine è RGB0 32 bpp, con il componente verde al secondo indirizzo di memoria più basso all'interno di ciascun pixel. L' immagine di sfondo in primo piano viene modificata sul posto. pixel_countè righe * colonne. Non importa di righe / colonne; semplicemente Chromekey unisce comunque molte parole di memoria che specifichi.

RGBA (con A richiesto essere 0xFF) richiederebbe l'utilizzo di una costante diversa, ma nessuna modifica nella dimensione della funzione. I DWORD in primo piano vengono confrontati per l'uguaglianza esatta con una costante arbitraria a 32 bit memorizzata in 4 byte, in modo che qualsiasi ordine di pixel o cromaticità possa essere facilmente supportato.

Lo stesso codice macchina funziona anche in modalità 32 bit. Per assemblare come 32 bit, passare rdia edinella sorgente. Tutti gli altri registri che diventano 64 bit sono impliciti (lodsd / stosd e loop) e gli altri registri espliciti rimangono a 32 bit. Ma tieni presente che avrai bisogno di un wrapper per chiamare da 32-bit C, perché nessuna delle convenzioni di chiamata standard x86-32 utilizza gli stessi registri di SysV x86-64.

Elenco della NASM (codice macchina + sorgente), commentato per i principianti con descrizioni di ciò che fanno le istruzioni più complesse. (La duplicazione del manuale di riferimento delle istruzioni è un cattivo stile durante il normale utilizzo.)

 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

Per ottenere l'origine NASM originale da questo elenco, rimuovere i 26 caratteri principali di ogni riga con <chromakey.lst cut -b 26- > chromakey.asm. Ho generato questo con gli
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))- elenchi NASM che lasciano più colonne vuote di quelle che voglio tra il codice macchina e l'origine. Per creare un file oggetto che è possibile collegare con C o C ++, utilizzare nasm -felf64 chromakey.asm. (O yasm -felf64 chromakey.asm).

non testato , ma sono abbastanza sicuro che l'idea di base di load / load / cmov / store sia valida, perché è così semplice.

Potrei salvare 3 byte se potessi richiedere al chiamante di passare la costante chroma-key (0x00ff00) come argomento extra, invece di codificare la costante nella funzione. Non penso che le solite regole consentano di scrivere una funzione più generica che ha il chiamante impostato costanti per esso. Ma se lo facesse, il 3o argomento (attualmente dummy) viene passato edxnell'ABI SysV x86-64. Cambia cmp eax, 0x0000ff00(5B) in cmp eax, edx(2B).


Con SSE4 o AVX, è possibile farlo più velocemente (ma con una dimensione del codice maggiore) con pcmpeqde blendvpsper eseguire una fusione variabile di dimensioni degli elementi a 32 bit controllata dalla maschera di confronto. (Con pand, potresti ignorare il byte alto). Per RGB24 compresso, è possibile utilizzare pcmpeqbe quindi 2x pshufb+ pandper ottenere TRUE in byte in cui corrispondono tutti e 3 i componenti di quel pixel pblendvb.

(So ​​che questo è code-golf, ma ho pensato di provare MMX prima di andare con un intero scalare.)


Potresti inviarmi un eseguibile realizzato con questo codice macchina?
ckjbgames,

x86_32, per favore.
ckjbgames,

@ckjbgames: non ho scritto un chiamante che carica / salva le immagini, solo la parte di modifica-pixel sul posto. Dovrei farlo prima che avrebbe senso costruire un eseguibile. Ma se lo facessi, che tipo di eseguibile? Windows PE32? Linux ELF32? FreeBSD ??
Peter Cordes,

ELF32, se vuoi.
ckjbgames,

@ckjbgames: se trovo il tempo, cercherò una libreria di caricamento delle immagini e scriverò qualcosa. Ho aggiunto un paragrafo su come trasformare la lista in codice che puoi assemblare nasm -felf32. (Per 32 bit, avrai anche bisogno di una funzione wrapper per chiamare da C, perché sta ancora usando gli stessi registri dell'ABI SysV x86-64.)
Peter Cordes,

13

Mathematica 57 35 byte

aggiornamento: per impostazione predefinita, uno sfondo verde viene rimosso utilizzando RemoveBackground. Il primo invio includeva il secondo parametro non necessario, `{" Sfondo ", Verde}".


#~ImageCompose~RemoveBackground@#2&

Rimuove lo sfondo dell'immagine 2 e compone il risultato con l'immagine 1.


Esempio

i1

Quanto segue, in forma di prefisso anziché di infisso, mostra più chiaramente come funziona il codice.

i2


4
Funzionerebbe per le immagini in cui non è lo "sfondo" che è verde? (Sembra che ci sia una piccola patch di verde a sinistra nella tua uscita)
DBS

Se nella foto fosse presente una "isola" verde, sarebbe necessario il parametro aggiuntivo, "{" Sfondo ", verde}", che aumenterebbe il totale a 57 byte. Quello era il mio primo invio. Perché non vedo verde isolato in primo piano nell'immagine, quel parametro è stato abbandonato.
DavidC

11

Python 3 + numpy , 59 byte

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

Provalo online!

L'input viene dato nel formato di un numpyarray, con terzine intere che rappresentano i pixel (dove #00FF00in codice di colore esadecimale è equivalente [0, 255, 0]). L'array di input viene modificato in posizione, il che è consentito per meta .

Immagini di esempio

Input (dalla domanda)

Sfondo:

ckjbgames' profile picture

Primo piano:

Dennis' profile picture

Immagine in primo piano dopo aver eseguito la funzione:

Merged image with #00FF00 replaced with background pixels

Implementazione di riferimento (utilizza opencvper leggere i file di immagine)

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)

Visualizza l'immagine sullo schermo e la scrive in un file di output.


17
Cosa c'è con tutti i punti rossi sull'immagine risultante?
Yytsi,

1
Ho chiesto informazioni sull'I / O - questo sembra essere conforme all'attuale formulazione (cioè "la tua libreria"), in tal caso, cv2 stesso richiede l'importazione numpy? Se non si poteva fare in 54, non utilizzando alcuna funzione NumPy, e non l'importazione NumPy: lambda f,b:[x[list(x[0])==[0,255,0]]for x in zip(f,b)]. Se anche l'elenco degli elenchi di numeri interi è effettivamente accettabile, puoi farlo in 48 conlambda f,b:[x[x[0]==[0,255,0]]for x in zip(f,b)]
Jonathan Allan,

..in effetti, anche se numpy è richiesto per cv2 per eseguire la conversione, penso comunque che potresti fare la versione a 54 byte, dato che non è necessario importare cv2 per la sfida.
Jonathan Allan,

5
Se G == 255il valore viene sostituito anche se R e B non sono zero, il che porta ai punti rossi. Questo succede anche per le altre band anche difficili che sono meno visibili. Quindi esegue i controlli logici indipendentemente l'uno dall'altro e scambia i singoli canali anche se viene soddisfatta solo una delle condizioni. Ad esempio se un pixel è [0 255 37]le bande rosse e verdi verranno sostituite.
Leander Moesinger,

2
@LeanderMoesinger: ben individuato. Ho avuto anche quel bug nel mio>. <; IDK perché ho pensato che solo il controllo di green = 0xFF ignorando R e B fosse corretto!
Peter Cordes,

9

Elaborazione, 116 99 byte

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

Sfortunatamente, l'elaborazione non supporta roba java 8, come lambdas.

Esempio di implementazione: (salva l'immagine come out.pnge la disegna anche sullo schermo)

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

Si può sbarazzarsi delle settings()e setup()funzioni e basta eseguire il codice direttamente.
Kevin Workman,

@KevinWorkman Ho delle impostazioni e delle impostazioni lì in modo da visualizzare l'immagine sullo schermo, che altrimenti non sarebbe possibile
dzaima,

È #ff00o 0xff00uguale a #00ff00in elaborazione?
Peter Cordes,

@PeterCordes # FF00 fornisce l'errore di sintassi, purtroppo, e # 00FF00 == 0xFF00FF00, quindi 0xFF00 non funziona poiché controlla il valore alfa 0
dzaima,

@dzaima: puoi prendere le tue immagini in formato RGB0, quindi 0x0000FF00lo schema di bit che stai cercando?
Peter Cordes,

6

Bash + ImageMagick, 45 byte

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

Prende due immagini come argomenti e visualizza l'output sullo schermo. Passa invece x:a $3scrivere in un terzo argomento del file. Il metodo è semplice: leggi l'immagine di "sfondo"; leggi l'immagine "in primo piano"; reinterpretare il colore "lime" (# 00ff00) come trasparenza nella seconda immagine; quindi componi la seconda immagine sulla prima e la stampa.

ImageMagick: 28 byte?

Avrei potuto inviarlo come una risposta ImageMagick ma non è chiaro come trattare gli argomenti. Se vuoi affermare che ImageMagick è un linguaggio basato -transparent lime -compositesullo stack (che è un po 'non proprio vero ma quasi ... è strano) allora è una funzione che prevede due immagini nello stack e lascia un'immagine unita nello stack. forse è abbastanza buono da contare?


3

MATL , 40 37 31 byte

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

Esempio eseguito con l'interprete offline. Le immagini sono inserite dai loro URL (potrebbero essere forniti anche nomi di file locali).

inserisci qui la descrizione dell'immagine

Spiegazione

,        % 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

3

Pyth , 27 byte

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

Richiede input tra virgolette. L'input sono i due percorsi dei file di immagine. Output di un file o.pngPurtroppo non può essere testato sull'interprete online per motivi di sicurezza ( 'è disabilitato su di esso). Dovrai ottenere Pyth sul tuo computer per testarlo.

Spiegazione

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

La funzione di fusione del chroma-key da sola è di 13 byte, come la mia risposta del codice macchina x86. In precedenza non mi ero reso conto che si trattava di un programma completo che gestiva anche l'I / O dell'immagine.
Peter Cordes,

2

Matlab 2016b e Octave, 62 59 byte

Input: A = matrice in primo piano MxNx3 unità8, B = matrice in background MxNx3 unità8.

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

Uscita: A = matrice MxNx3 unit8

Esempio di utilizzo:

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)

1

C ++, 339 byte

Questo utilizza CImg e può anche prendere file in altri formati. Il risultato viene visualizzato in una finestra.

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

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


1

R, 135 byte

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")}

Funzione anonima, accetta 2 percorsi di file png come argomenti e genera un'immagine png chiamata a.png.

Leggermente non golfato, con spiegazioni:

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")
}

1

SmileBASIC, 90 byte qual è la chiave

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è il primo piano e l'output, Jè lo sfondo. Entrambi sono array interi di pixel, in formato ARGB a 32 bit.

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

Spiegazione:

ARYOP è una funzione che applica una semplice operazione a tutti gli elementi di un array.
Si chiama comeARYOP mode, output_array, input_array_1, input_array_2, ...

Innanzitutto, per determinare quali pixel nell'immagine sono verdi, -16711936(la rappresentazione RGBA del colore verde) viene sottratta da ciascun pixel nell'immagine in primo piano. Ciò fornisce una matrice in cui 0rappresentano pixel verdi e qualsiasi altro numero rappresenta pixel non verdi.

Per convertire tutti i valori diversi da zero 1, vengono quadrati (per rimuovere i numeri negativi), quindi bloccati tra 0e 1.

Ciò si traduce in un array con solo 0s e 1s.
0s rappresentano pixel verdi nell'immagine in primo piano e devono essere sostituiti con pixel dello sfondo.
1s rappresentano pixel non verdi e questi dovranno essere sostituiti con pixel di primo piano.

Questo può essere fatto facilmente usando l'interpolazione lineare.


0

PHP, 187 byte

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

assume file PNG a 24 bit; prende i nomi dei file dagli argomenti della riga di comando, scrive su stdout.
Corri con -r.

abbattersi

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

0

JavaScript (ES6), 290 byte

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

Accetta input come due Imageoggetti (nella sintassi del curry), che possono essere creati con un <image>elemento HTML . Restituisce una Promessa che si risolve nell'URL dei dati Base64 dell'immagine risultante, che può essere applicata a srcdi un <image>.

L'idea qui era di impostare il valore alfa per ciascun #00FF00pixel su0 e quindi dipingere il primo piano, con lo sfondo digitato, sopra lo sfondo.

Test dello snippet

Includere il primo piano e lo sfondo dai loro URL di dati era troppo grande per essere pubblicato qui, quindi è stato spostato in CodePen:

Provalo online!


0

OSL , 83 byte

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

Accetta due input. Il primo è il primo piano e il secondo lo sfondo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.