A chave do croma para o sucesso

23

O valor da cor RGB #00FF00é bastante importante: é usado para fazer filmes, programas de TV, anúncios sobre o clima e muito mais. É a famosa cor "verde da TV" ou "tela verde".

O desafio

Sua tarefa é gravar um programa que receba duas imagens de entrada, no formato PNG (ou no tipo de objeto de imagem da sua biblioteca de imagens) e com as mesmas dimensões. Uma imagem pode ser qualquer imagem antiga. A outra é a imagem que terá um fundo da cor #00FF00. A imagem de saída consistirá na segunda imagem sobreposta à primeira, sem #00FF00cor presente (exceto na primeira imagem). A entrada e a saída podem ser feitas com arquivos, uma GUI, etc. Você pode receber uma matriz de valores RGB como entrada, como visto aqui . Você pode supor que uma imagem tenha apenas pixels de total opacidade.

Basicamente...

Crie um programa que pegue cada #00FF00pixel em uma imagem e substitua-o pelo pixel correspondente na imagem de fundo.

Casos de teste

Fornecido generosamente por @dzaima: Histórico: Primeiro plano: Saída:
minha imagem de perfil

dennis

saída


Obviamente, as brechas padrão são estritamente proibidas . Isso inclui o uso de um recurso online para fazer isso por você.
Este é o , para que o código mais curto ganhe e o melhor programador prospere ...

ckjbgames
fonte
2
Podemos pegar um objeto de imagem no formato nativo da linguagem / biblioteca como entrada ou precisamos ler a imagem por nome de arquivo?
notjagan
@notjagan Você pode receber objetos de imagem como entrada.
Ckjbgames
3
A E / S de matrizes de matrizes de números inteiros é aceitável ou na verdade estamos restritos a algum outro conjunto de E / S de imagem?
Jonathan Allan
1
@PeterCordes Eu vou permitir isso.
Ckjbgames 17/07
1
@PeterCordes ok
ckjbgames

Respostas:

14

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

changelog:

  1. Correção de bug: a primeira versão estava apenas checando G = 0xff, não exigindo que R e B fossem 0. Alterei para modificar o plano de fundo no local para que eu pudesse usar lodsdem primeiro plano para ter pixels fg na codificação de eaxformato curto cmp eax, imm32(5 bytes ), em vez de cmp dh,0xff(3 bytes).

  2. Economize 2 bytes: observe que modificar o bg no local permite o uso de um operando de memória cmov, economizando uma movcarga de 2 bytes (e salvando um registro, caso isso importe).


Esta é uma função que segue a convenção de chamada do System V x86-64, que pode ser chamada diretamente de C ou C ++ (em sistemas x86-64 não Windows) com esta assinatura:

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

O formato da imagem é RGB0 32bpp, com o componente verde no segundo endereço de memória mais baixo em cada pixel. A imagem de fundo do primeiro plano é modificada no local. pixel_counté linhas * colunas. Não se preocupa com linhas / colunas; apenas o chromekey combina no entanto muitas dwords de memória que você especificar.

RGBA (com A necessário para 0xFF) exigiria o uso de uma constante diferente, mas nenhuma alteração no tamanho da função. Os DWORDs em primeiro plano são comparados quanto à igualdade exata em relação a uma constante arbitrária de 32 bits armazenada em 4 bytes, para que qualquer cor de ordem de pixel ou chave de croma possa ser facilmente suportada.

O mesmo código de máquina também funciona no modo de 32 bits. Para montar como 32 bits, altere rdipara edina fonte. Todos os outros registradores que se tornam 64 bits são implícitos (lodsd / stosd e loop), e os outros registros explícitos permanecem em 32 bits. Mas observe que você precisará de um wrapper para chamar de C de 32 bits, porque nenhuma das convenções de chamada x86-32 padrão usa os mesmos registros do x86-64 SysV.

Listagem NASM (código de máquina + fonte), comentada para iniciantes em asm com descrições do que as instruções mais complexas fazem. (Duplicar o manual de referência das instruções é um estilo ruim no 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 obter a fonte NASM original dessa lista, retire os 26 caracteres iniciais de cada linha <chromakey.lst cut -b 26- > chromakey.asm. Eu
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))- criei isso com listagens NASM, deixando mais colunas em branco do que eu quero entre o código da máquina e a fonte. Para criar um arquivo de objeto que você pode vincular com C ou C ++, use nasm -felf64 chromakey.asm. (Ou yasm -felf64 chromakey.asm)

não testado , mas estou bastante confiante de que a idéia básica de load / load / cmov / store é sólida, porque é muito simples.

Eu poderia salvar 3 bytes se exigisse que o chamador passasse a constante de chave de croma (0x00ff00) como um argumento extra, em vez de codificar a constante na função. Eu não acho que as regras usuais permitam escrever uma função mais genérica que tenha o chamador configurado constantes para ele. Mas, se o fez, o terceiro argumento (atualmente dummy) é passado na edxABI x86-64 SysV. Apenas mude cmp eax, 0x0000ff00(5B) para cmp eax, edx(2B).


Com o SSE4 ou AVX, você pode fazer isso mais rapidamente (mas com um tamanho de código maior) pcmpeqde blendvpsfazer uma mistura variável de tamanho de elemento de 32 bits controlada pela máscara de comparação. (Com pand, você pode ignorar o byte alto). Para RGB24 compactado, você pode usar pcmpeqbe, em seguida, 2x pshufb+ pandpara obter VERDADEIRO em bytes, onde todos os 3 componentes desse pixel correspondem pblendvb.

(Eu sei que isso é código-golfe, mas considerei tentar o MMX antes de usar o número inteiro escalar.)

Peter Cordes
fonte
Você poderia me enviar um executável feito com este código de máquina?
Ckjbgames 17/07
x86_32, por favor.
Ckjbgames 17/07
@ckjbgames: Eu não escrevi um chamador que carrega / salva imagens, apenas a parte de modificar pixels no local. Eu teria que fazer isso antes que fizesse algum sentido criar um executável. Mas se eu fiz, que tipo de executável? Windows PE32? Linux ELF32? FreeBSD ??
Peter Cordes
ELF32, se você quiser.
Ckjbgames 17/07
@ckjbgames: Se eu encontrar tempo, vou procurar uma biblioteca de carregamento de imagens e escrever algo. Adicionei um parágrafo sobre como transformar a listagem novamente em código que você pode montar nasm -felf32. (Para 32-bit, você também vai precisar de uma função wrapper para chamar de C, porque ele ainda está usando os mesmos registros como o x86-64 SysV ABI.)
Peter Cordes
13

Mathematica 57 35 bytes

update: por padrão, um fundo verde é removido usando RemoveBackground. A primeira submissão incluiu o segundo parâmetro desnecessário, `{" Background ", Green}".


#~ImageCompose~RemoveBackground@#2&

Remove o fundo da imagem 2 e compõe o resultado com a imagem 1.


Exemplo

i1

A seguir, no prefixo, e não no formato infix, mostra mais claramente como o código funciona.

i2

DavidC
fonte
4
Isso funcionaria para imagens onde não é o "fundo" verde? (Parece haver um pequeno pedaço de esquerda verde em sua saída)
DBS
Se houvesse uma "ilha" verde na imagem, seria necessário o parâmetro adicional `{" Background ", Green}", que aumentaria o total para 57 bytes. Essa foi minha primeira submissão. Porque não vejo verde isolado em primeiro plano da imagem, esse parâmetro foi descartado #
1860
11

Python 3 + numpy , 59 bytes

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

Experimente online!

A entrada é fornecida no formato de uma numpymatriz, com trigêmeos inteiros representando pixels (onde #00FF00no código de cor hexadecimal é equivalente a [0, 255, 0]). A matriz de entrada é modificada no local, o que é permitido por meta .

Imagens de exemplo

Entrada (da pergunta)

Fundo:

Foto de perfil de ckjbgames

Primeiro plano:

Foto do perfil de Dennis

Imagem em primeiro plano após executar a função:

Imagem mesclada com # 00FF00 substituída por pixels de fundo

Implementação de referência (usada opencvpara ler arquivos de imagem)

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)

Exibe a imagem na tela e a grava em um arquivo de saída.

notjagan
fonte
17
O que há com todos os pontos vermelhos na imagem resultante?
Yytsi 16/07/19
1
Eu perguntei sobre E / S - isso parece estar de acordo com a redação atual (ou seja, "sua biblioteca"); se assim for, o próprio cv2 exige a importação numpy? Se não você poderia fazê-lo em 54 por não usar quaisquer funções numpy, e não importando numpy: lambda f,b:[x[list(x[0])==[0,255,0]]for x in zip(f,b)]. Se a lista de listas de números inteiros também é realmente aceitável, você pode fazê-lo em 48 comlambda f,b:[x[x[0]==[0,255,0]]for x in zip(f,b)]
Jonathan Allan
.. de fato, mesmo que seja necessário um numpy para o cv2 realizar a conversão, ainda acho que você poderia fazer a versão de 54 bytes, pois não precisamos importar o cv2 para o desafio.
Jonathan Allan
5
Se G == 255, o valor é substituído mesmo que R e B não sejam zero, o que leva aos pontos vermelhos. Isso também acontece para as outras bandas, mesmo difíceis, que são menos visíveis. Portanto, ele executa as verificações lógicas independentemente uma da outra e troca os canais únicos, mesmo que apenas uma das condições seja atendida. Por exemplo, se um pixel é [0 255 37]o vermelho e as faixas verdes serão substituídas.
Leander Moesinger
2
@LeanderMoesinger: Bem localizado. Eu também tinha esse bug no meu>> <; IDK por que eu pensei que apenas verificando verde = 0xFF enquanto ignorava R e B estava correto!
27516 Peter Cordes
9

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

Infelizmente, o processamento não suporta coisas java 8, como lambdas.

Exemplo de implementação: (salva a imagem como out.pngtambém a desenha na tela)

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
fonte
Você pode se livrar dos settings()e setup()funções e simplesmente executar o código diretamente.
21417 Kevin Workman
@KevinWorkman tenho configurações e configuração lá para que ele iria exibir a imagem na tela, o que de outra forma não seria possível
dzaima
É #ff00ou é 0xff00o mesmo que #00ff00em Processamento?
Peter Cordes
@PeterCordes # FF00 dá erro de sintaxe, infelizmente, e # 00FF00 == 0xFF00FF00, então 0xFF00 não funciona como ele verifica alfa valor 0
dzaima
@dzaima: Você pode tirar suas imagens no formato RGB0, assim 0x0000FF00como o padrão de bits que está procurando?
Peter Cordes
6

Bash + ImageMagick, 45 bytes

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

Tira duas imagens como argumentos e exibe a saída na tela. Mude x:para $3para gravar em um terceiro argumento de arquivo. O método é simples: leia a imagem "background"; leia o "primeiro plano" imagek; reinterprete a cor "limão" (# 00ff00) como transparência na segunda imagem; depois componha a segunda imagem na primeira e saia.

ImageMagick: 28 bytes?

Eu poderia ter enviado isso como uma resposta do ImageMagick, mas não está claro como lidar com os argumentos. Se você deseja afirmar que o ImageMagick é uma linguagem baseada em pilha (que meio que não é realmente verdadeira, mas quase ... é estranha), -transparent lime -compositeé uma função que espera duas imagens na pilha e deixa uma imagem mesclada na pilha. Talvez seja bom o suficiente para contar?

hobbs
fonte
3

MATL , 40 37 31 bytes

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

Exemplo executado com o intérprete offline. As imagens são inseridas por seus URLs (nomes de arquivos locais também podem ser fornecidos).

insira a descrição da imagem aqui

Explicação

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

Pitão , 27 bytes

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

É preciso entrada citada. A entrada são os dois caminhos dos arquivos de imagem. Saída de um arquivo o.pngInfelizmente, isso não pode ser testado no intérprete on-line por motivos de segurança ( 'está desativado). Você precisará obter o Pyth no seu computador para testá-lo.

Explicação

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
fonte
A função de combinação de chave de croma por si só é de 13 bytes, igual à minha resposta de código de máquina x86. Eu não percebi anteriormente que este era um programa completo para entregar E / S de imagem.
Peter Cordes
2

Matlab 2016b e oitava, 62 59 bytes

Entrada: A = matriz em primeiro plano MxNx3 unit8, B = matriz em segundo plano MxNx3 unit8.

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

Saída: A = matriz MxNx3 unit8

Uso da amostra:

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
fonte
1

C ++, 339 bytes

Isso usa o CImg e pode levar arquivos em outros formatos também. O resultado é exibido em uma janela.

#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 com g++ chromakey.cpp -g -L/usr/lib/i386-linux-gnu -lX11 -o chromakey -pthread.

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

Função anônima, usa 2 caminhos de arquivo png como argumentos e gera uma imagem png chamada a.png.

Ligeiramente não-destruído, com explicações:

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
fonte
1

SmileBASIC, 90 bytes, qual é a chave

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é o primeiro plano e a saída, Jé o segundo plano. Ambos são matrizes inteiras de pixels, no formato ARGB de 32 bits.

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

Explicação:

ARYOP é uma função que aplica uma operação simples a todos os elementos de uma matriz.
É chamado comoARYOP mode, output_array, input_array_1, input_array_2, ...

Primeiro, para determinar quais pixels na imagem são verdes, -16711936(a representação RGBA da cor verde) é subtraída de cada pixel na imagem em primeiro plano. Isso fornece uma matriz em que 0representa pixels verdes e qualquer outro número representa pixels não verdes.

Para converter todos os valores diferentes de zero em 1, eles são ao quadrado (para remover números negativos) e, em seguida, fixados entre 0e 1.

Isso resulta em uma matriz com apenas 0s e 1s.
0s representam pixels verdes na imagem em primeiro plano e devem ser substituídos por pixels em segundo plano.
1s representam pixels não verdes, e esses precisarão ser substituídos por pixels do primeiro plano.

Isso pode ser feito facilmente usando interpolação linear.

12Me21
fonte
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);

assume arquivos PNG de 24 bits; pega nomes de arquivos nos argumentos das linhas de comando e grava no stdout.
Corra com -r.

demolir

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

Recebe entrada como dois Imageobjetos (na sintaxe de currying), que podem ser criados com um <image>elemento HTML . Retorna uma promessa que resolve para o URL de dados Base64 da imagem resultante, que pode ser aplicada ao srcde um <image>.

A idéia aqui era definir o valor alfa de cada #00FF00pixel 0e pintar o primeiro plano, com o plano de fundo excluído, na parte superior do plano de fundo.

Snippet de teste

A inclusão do primeiro plano e do plano de fundo pelos URLs de dados era muito grande para serem postados aqui; portanto, foi movida para o CodePen:

Experimente online!

Justin Mariner
fonte
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 duas entradas. O primeiro é o primeiro plano e o segundo, o segundo plano.

Scott Milner
fonte