La clé chroma du succès

23

La valeur de couleur RVB #00FF00est assez importante: elle est utilisée pour faire des films, des émissions de télévision, des annonces météo, etc. C'est la fameuse couleur "vert TV" ou "écran vert".

Le défi

Votre tâche consiste à écrire un programme qui prend deux images d'entrée, à la fois au format PNG (ou dans le type d'objet image de votre bibliothèque d'images) et de mêmes dimensions. Une image peut être n'importe quelle image ancienne. L'autre est l'image qui aura un fond de couleur #00FF00. L'image de sortie sera constituée de la deuxième image superposée sur la première, sans #00FF00couleur présente (sauf dans la première image). L'entrée et la sortie peuvent être effectuées avec des fichiers, une interface graphique, etc. Vous êtes autorisé à prendre un tableau de valeurs RVB en entrée, comme on le voit ici . Vous pouvez supposer qu'une image n'a que des pixels de pleine opacité.

Fondamentalement...

Créez un programme qui prend chaque #00FF00pixel d'une image et remplacez-le par le pixel correspondant dans l'image d'arrière-plan.

Cas de test

Généreusement fourni par @dzaima: Contexte: Premier plan: Sortie:
mon image de profil

Dennis

sortie


Bien sûr, les failles standard sont strictement interdites . Cela comprend l'utilisation d'une ressource en ligne pour le faire pour vous.
C'est du , alors que le code le plus court gagne et que le meilleur programmeur prospère ...

ckjbgames
la source
2
Pouvons-nous prendre un objet image au format natif de la langue / bibliothèque comme entrée, ou devons-nous lire l'image via le nom de fichier?
notjagan
@notjagan Vous pouvez prendre des objets image en entrée.
ckjbgames
3
Les E / S de tableaux de tableaux d'entiers sont-elles acceptables ou sommes-nous réellement limités à un autre ensemble d'E / S d'image?
Jonathan Allan
1
@PeterCordes Je le permettrai.
ckjbgames
1
@PeterCordes ok
ckjbgames

Réponses:

14

code machine x86-64 (et x86-32), 13 15 13 octets

journal des modifications:

  1. Bugfix: la première version vérifiait seulement G = 0xff, ne nécessitant pas que R et B soient 0. J'ai changé pour modifier l'arrière-plan en place afin que je puisse utiliser lodsdau premier plan pour avoir des pixels fg eaxpour l' cmp eax, imm32encodage court (5 octets) ), au lieu de cmp dh,0xff(3 octets).

  2. Enregistrer 2 octets: a remarqué que la modification du bg en place permettait d'utiliser un opérande de mémoire pour cmov, l'enregistrement d'une movcharge de 2 octets (et l'enregistrement d'un registre, au cas où cela importait).


Il s'agit d'une fonction suivant la convention d'appel System V x86-64, appelable directement à partir de C ou C ++ (sur les systèmes x86-64 non Windows) avec cette signature:

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

Le format d'image est RGB0 32bpp, avec le composant vert à la 2ème adresse mémoire la plus basse dans chaque pixel. L' image d'arrière- plan de premier plan est modifiée sur place. pixel_countest lignes * colonnes. Il ne se soucie pas des lignes / colonnes; il suffit que chromekey mélange les nombreux mots de mémoire que vous spécifiez.

RGBA (avec A requis pour être 0xFF) nécessiterait l'utilisation d'une constante différente, mais aucun changement dans la taille de la fonction. Les DWORD de premier plan sont comparés pour une égalité exacte avec une constante arbitraire de 32 bits stockée sur 4 octets, de sorte que toute couleur d'ordre des pixels ou de chrominance peut être facilement prise en charge.

Le même code machine fonctionne également en mode 32 bits. Pour assembler en 32 bits, passez rdià edidans la source. Tous les autres registres qui deviennent 64 bits sont implicites (lodsd / stosd et boucle), et les autres regs explicites restent 32 bits. Mais notez que vous aurez besoin d'un wrapper pour appeler à partir de C 32 bits, car aucune des conventions d'appel x86-32 standard n'utilise les mêmes paramètres que x86-64 SysV.

Liste NASM (code machine + source), commentée pour les débutants asm avec des descriptions de ce que font les instructions les plus complexes. (La duplication du manuel de référence des instructions est un mauvais style en utilisation normale.)

 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

Pour obtenir la source NASM d'origine de cette liste, supprimez les 26 premiers caractères de chaque ligne avec <chromakey.lst cut -b 26- > chromakey.asm. J'ai généré cela avec des
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))- listes NASM laissant plus de colonnes vides que je ne le souhaite entre le code machine et la source. Pour créer un fichier objet que vous pouvez lier avec C ou C ++, utilisez nasm -felf64 chromakey.asm. (Ou yasm -felf64 chromakey.asm).

non testé , mais je suis assez confiant que l'idée de base de load / load / cmov / store est bonne, car c'est si simple.

Je pourrais économiser 3 octets si je pouvais exiger que l'appelant passe la constante de chrominance (0x00ff00) comme argument supplémentaire, au lieu de coder en dur la constante dans la fonction. Je ne pense pas que les règles habituelles permettent d'écrire une fonction plus générique pour laquelle l'appelant a configuré des constantes. Mais si c'est le cas, le 3ème argument (actuellement dummy) est passé edxdans l'ABI SysV x86-64. Changez simplement cmp eax, 0x0000ff00(5B) en cmp eax, edx(2B).


Avec SSE4 ou AVX, vous pouvez le faire plus rapidement (mais avec une taille de code plus grande) avec pcmpeqdet blendvpspour effectuer un mélange variable de taille d'élément 32 bits contrôlé par le masque de comparaison. (Avec pand, vous pouvez ignorer l'octet de poids fort). Pour RGB24 compressé, vous pouvez utiliser pcmpeqbpuis 2x pshufb+ pandpour obtenir VRAI en octets où les 3 composants de ce pixel correspondent, alors pblendvb.

(Je sais que c'est du code-golf, mais j'ai envisagé d'essayer MMX avant de choisir un entier scalaire.)

Peter Cordes
la source
Pourriez-vous m'envoyer un exécutable réalisé avec ce code machine?
ckjbgames
x86_32, s'il vous plaît.
ckjbgames
@ckjbgames: Je n'ai pas écrit d'appelant qui charge / enregistre les images, juste la partie modify-pixels-in-place. Je devrais le faire avant qu'il soit logique de construire un exécutable. Mais si je le faisais, quel genre d'exécutable? Windows PE32? Linux ELF32? FreeBSD ??
Peter Cordes
ELF32, si vous voulez.
ckjbgames
@ckjbgames: Si je trouve du temps, je chercherai une bibliothèque de chargement d'images et j'écrirai quelque chose. J'ai ajouté un paragraphe sur la façon de transformer la liste en code avec lequel vous pouvez assembler nasm -felf32. (Pour 32 bits, vous aurez également besoin d'une fonction wrapper pour appeler à partir de C, car elle utilise toujours les mêmes registres que l'ABI SysV x86-64.)
Peter Cordes
13

Mathematica 57 35 octets

mise à jour: par défaut, un fond vert est supprimé à l'aide de RemoveBackground. La première communication comprenait le deuxième paramètre inutile, `{" Background ", Green}".


#~ImageCompose~RemoveBackground@#2&

Supprime l'arrière-plan de l'image 2 et compose le résultat avec l'image 1.


Exemple

i1

Ce qui suit, sous forme de préfixe plutôt que d'infixe, montre plus clairement comment fonctionne le code.

i2

DavidC
la source
4
Est-ce que cela fonctionnerait pour des images où ce n'est pas le "fond" qui est vert? (Il semble y avoir un petit morceau de vert dans votre sortie)
DBS
S'il y avait une "île" verte dans l'image, le paramètre supplémentaire, "{" Contexte ", Vert}", serait requis, ce qui porterait le total à 57 octets. C'était ma première soumission. Parce que je ne vois pas de vert isolé au premier plan de l'image, ce paramètre a été supprimé.
DavidC
11

Python 3 + numpy , 59 octets

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

Essayez-le en ligne!

L'entrée est donnée sous la forme d'un numpytableau, avec des triplets entiers représentant des pixels (où #00FF00en code couleur hex équivaut à [0, 255, 0]). Le tableau d'entrée est modifié sur place, ce qui est autorisé par méta .

Exemples d'images

Entrée (de la question)

Contexte:

Photo de profil de ckjbgames

Premier plan:

Photo de profil de Dennis

Image de premier plan après l'exécution de la fonction:

Image fusionnée avec # 00FF00 remplacée par des pixels d'arrière-plan

Implémentation de référence (permet opencvde lire les fichiers image)

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)

Affiche l'image à l'écran et l'écrit dans un fichier de sortie.

notjagan
la source
17
Qu'y a-t-il avec tous les points rouges sur l'image résultante?
Yytsi
1
J'ai posé des questions sur les E / S - cela semble conforme au libellé actuel (c'est-à-dire "votre bibliothèque"), si oui, cv2 lui-même nécessite-t-il l'importation numpy? Sinon , vous pouvez le faire en 54 par ne pas utiliser les fonctions numpy, et non l' importation numpy: lambda f,b:[x[list(x[0])==[0,255,0]]for x in zip(f,b)]. Si la liste des listes d'entiers est également acceptable, vous pouvez le faire en 48 aveclambda f,b:[x[x[0]==[0,255,0]]for x in zip(f,b)]
Jonathan Allan
..en fait, même si numpy est requis pour que cv2 effectue la conversion, je pense toujours que vous pourriez faire la version 54 octets, car nous n'avons pas besoin d'importer cv2 pour le challenge.
Jonathan Allan
5
Si G == 255, la valeur est remplacée même si R et B ne sont pas nuls, ce qui conduit aux points rouges. Cela se produit également pour les autres groupes, même les plus difficiles qui sont moins visibles. Il effectue donc les vérifications logiques indépendamment les unes des autres et échange des canaux uniques même si une seule des conditions est remplie. Par exemple, si un pixel est [0 255 37]les bandes rouges et vertes seront remplacées.
Leander Moesinger
2
@LeanderMoesinger: Bien repéré. J'ai aussi eu ce bug dans le mien>. <; IDK pourquoi je pensais que seule la vérification de green = 0xFF en ignorant R et B était correcte!
Peter Cordes
9

Traitement, 116 99 octets

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

Malheureusement, le traitement ne prend pas en charge les éléments Java 8, comme les lambdas.

Exemple d'implémentation: (enregistre l'image sous out.pnget la dessine également à l'écran)

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
la source
Vous pouvez vous débarrasser des settings()et setup()fonctions et il suffit d' exécuter le code directement.
Kevin Workman
@KevinWorkman J'ai des paramètres et une configuration là-bas pour afficher l'image à l'écran, ce qui autrement ne serait pas possible
dzaima
Est #ff00-ce que c'est 0xff00la même chose que #00ff00dans Traitement?
Peter Cordes
@PeterCordes # FF00 donne malheureusement une erreur de syntaxe et # 00FF00 == 0xFF00FF00, donc 0xFF00 ne fonctionne pas car il vérifie la valeur alpha 0
dzaima
@dzaima: Pouvez-vous prendre vos images au format RGB0, ainsi 0x0000FF00est le motif binaire que vous recherchez?
Peter Cordes
6

Bash + ImageMagick, 45 octets

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

Prend deux images comme arguments et affiche la sortie à l'écran. Remplacez x:par $3pour écrire dans un troisième argument de fichier à la place. La méthode est simple: lire l'image "d'arrière-plan"; lire l'image "au premier plan"; réinterpréter la couleur "chaux" (# 00ff00) comme transparence dans la deuxième image; puis composez la seconde image sur la première et sortez.

ImageMagick: 28 octets?

J'aurais pu soumettre cela comme une réponse ImageMagick mais il n'est pas clair comment gérer les arguments. Si vous voulez affirmer qu'ImageMagick est un langage basé sur la pile (ce qui n'est pas vraiment vrai, mais presque ... c'est bizarre), -transparent lime -compositec'est une fonction qui attend deux images sur la pile et laisse une image fusionnée sur la pile .. Peut-être que c'est assez bon pour compter?

Hobbs
la source
3

MATL , 40 37 31 octets

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

Exemple exécuté avec l'interpréteur hors ligne. Les images sont saisies par leur URL (des noms de fichiers locaux peuvent également être fournis).

entrez la description de l'image ici

Explication

,        % 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
la source
3

Pyth , 27 octets

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

Il faut une entrée entre guillemets. L'entrée sont les deux chemins des fichiers image. Sortie d'un fichier o.pngMalheureusement qui ne peut pas être testé sur l'interpréteur en ligne pour des raisons de sécurité ( 'est désactivé dessus). Vous devrez télécharger Pyth sur votre ordinateur pour le tester.

Explication

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
la source
La fonction de mélange chroma-key à elle seule est de 13 octets, identique à ma réponse de code machine x86. Je ne m'étais pas rendu compte plus tôt qu'il s'agissait également d'un programme complet de gestion des E / S d'images.
Peter Cordes
2

Matlab 2016b et Octave, 62 59 octets

Entrée: A = matrice de premier plan MxNx3 unit8, B = matrice de fond MxNx3 unit8.

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

Sortie: A = matrice MxNx3 unit8

Exemple d'utilisation:

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
la source
1

C ++, 339 octets

Cela utilise CImg et peut également prendre des fichiers dans d'autres formats. Le résultat est affiché dans une fenêtre.

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

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

ckjbgames
la source
1

R, 135 octets

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

Fonction anonyme, prend 2 chemins de fichier png comme arguments et génère une image png appelée a.png.

Légèrement non golfé, avec explications:

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
la source
1

SmileBASIC, 90 octets quelle est la clé

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

Iest le premier plan et la sortie, Jest l'arrière-plan. Les deux sont des tableaux entiers de pixels, au format ARGB 32 bits.

Non 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

Explication:

ARYOP est une fonction qui applique une opération simple à chaque élément d'un tableau.
Il s'appelle commeARYOP mode, output_array, input_array_1, input_array_2, ...

Premièrement, pour déterminer quels pixels de l'image sont verts, -16711936(la représentation RGBA de la couleur verte) est soustraite de chaque pixel de l'image de premier plan. Cela donne un tableau où 0représente les pixels verts, et tout autre nombre représente les pixels non verts.

Pour convertir toutes les valeurs non nulles en 1, elles sont au carré (pour supprimer les nombres négatifs), puis fixées entre 0et 1.

Il en résulte un tableau avec uniquement 0s et 1s.
0s représentent des pixels verts dans l'image de premier plan et doivent être remplacés par des pixels de l'arrière-plan.
1s représentent des pixels non verts, et ceux-ci devront être remplacés par des pixels du premier plan.

Cela peut facilement être fait en utilisant une interpolation linéaire.

12Me21
la source
0

PHP, 187 octets

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

suppose des fichiers PNG 24 bits; prend les noms de fichiers des arguments des lignes de commande, écrit dans stdout.
Courez avec -r.

panne

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
la source
0

JavaScript (ES6), 290 octets

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

Prend l'entrée comme deux Imageobjets (dans la syntaxe de curry), qui peuvent être créés avec un <image>élément HTML . Renvoie une promesse qui se résout à l'URL de données Base64 de l'image résultante, qui peut être appliquée à celle srcd'un <image>.

L'idée ici était de définir la valeur alpha pour chaque #00FF00pixel 0, puis de peindre le premier plan, avec son arrière-plan masqué, au-dessus de l'arrière-plan.

Extrait de test

Inclure le premier plan et l'arrière-plan par leurs URL de données était trop volumineux pour être publié ici, il a donc été déplacé vers CodePen:

Essayez-le en ligne!

Justin Mariner
la source
0

OSL , 83 octets

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

Prend deux entrées. Le premier est le premier plan et le second l'arrière-plan.

Scott Milner
la source