Chroma Key to Success


23

Wartość koloru RGB #00FF00jest raczej ważna: służy do tworzenia filmów, programów telewizyjnych, komunikatów pogodowych i innych. Jest to słynny kolor „zielony telewizor” lub „zielony ekran”.

Wyzwanie

Twoim zadaniem jest napisanie programu, który pobierze dwa obrazy wejściowe, zarówno w formacie PNG (lub w typie obiektu obrazu w bibliotece obrazów), o tych samych wymiarach. Jeden obraz może być dowolnym starym obrazem. Drugi to obraz, który będzie miał tło koloru #00FF00. Obraz wyjściowy będzie się składał z drugiego obrazu nałożonego na pierwszy, bez #00FF00obecności koloru (z wyjątkiem pierwszego obrazu). Dane wejściowe i wyjściowe można wykonywać za pomocą plików, interfejsu GUI itp. Możesz przyjmować tablicę wartości RGB jako danych wejściowych, jak pokazano tutaj . Możesz założyć, że obraz ma tylko piksele o pełnym kryciu.

Gruntownie...

Stwórz program, który pobiera każdy #00FF00piksel na jednym obrazie i zastępuje go odpowiednim pikselem na obrazie tła.

Przypadki testowe

Hojnie dostarczone przez @dzaima: Background: Foreground: Output:
moje zdjęcie profilowe

dennis

wydajność


Oczywiście standardowe luki są surowo zabronione . Obejmuje to korzystanie z zasobu online, aby to zrobić za Ciebie.
To jest , więc może wygrać najkrótszy kod, a najlepszy programista ...


2
Czy możemy wziąć obiekt obrazu w natywnym formacie języka / biblioteki jako dane wejściowe, czy też musimy odczytać obraz za pomocą nazwy pliku?
notjagan

@notjagan Możesz pobierać obiekty obrazu jako dane wejściowe.
ckjbgames

3
Czy I / O tablic tablic liczb całkowitych jest akceptowalne, czy faktycznie jesteśmy ograniczeni do jakiegoś innego zestawu I / O obrazu?
Jonathan Allan,

1
@PeterCordes Pozwolę na to.
ckjbgames

1
@PeterCordes ok
ckjbgames

Odpowiedzi:


14

Kod maszynowy x86-64 (i x86-32), 13 15 13 bajtów

dziennik zmian:

  1. Bugfix: pierwsza wersja sprawdzała tylko G = 0xff, nie wymagając, aby R i B wynosiły 0. Zmieniłem na modyfikację tła w miejscu, dzięki czemu mogłem użyć lodsdna pierwszym planie, aby mieć piksele fg eaxdo cmp eax, imm32kodowania krótkiego (5 bajtów ), zamiast cmp dh,0xff(3 bajty).

  2. Zapisz 2 bajty: zauważyłem, że modyfikacja bg w miejscu pozwoliła na użycie operandu pamięci cmov, zapisanie 2-bajtowego movobciążenia (i zapisanie rejestru, w razie potrzeby ).


Jest to funkcja zgodna z konwencją wywoływania Systemu V x86-64, wywoływaną bezpośrednio z C lub C ++ (w systemach innych niż Windows x86-64) z tym podpisem:

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

Format obrazu to RGB0 32 bpp, z zielonym składnikiem pod drugim najniższym adresem pamięci w każdym pikselu. Obraz tła pierwszego planu jest modyfikowany na miejscu. pixel_countto rzędy * kolumny. Nie obchodzi go wiersze / kolumny; po prostu chromekey łączy dowolną liczbę domen pamięci określoną przez użytkownika.

RGBA (z A musi być 0xFF) wymagałoby użycia innej stałej, ale bez zmiany wielkości funkcji. DWORD pierwszego planu są porównywane w celu uzyskania dokładnej równości z dowolną 32-bitową stałą zapisaną w 4 bajtach, dzięki czemu można łatwo obsługiwać dowolny rząd pikseli lub kolor klucza.

Ten sam kod maszynowy działa również w trybie 32-bitowym. Aby złożyć jako 32-bitowy, zmień rdina edina źródło. Wszystkie inne rejestry, które stają się 64-bitowe, są niejawne (lodsd / stosd i loop), a inne jawne rejestry pozostają 32-bitowe. Pamiętaj jednak, że będziesz potrzebował opakowania, aby zadzwonić z 32-bitowego C, ponieważ żadna ze standardowych konwencji wywoływania x86-32 nie używa tych samych reguł co SysV x86-64.

Lista NASM (kod maszynowy + źródło), komentowała początkujących asmów opisami tego, co robią bardziej złożone instrukcje. (Powielanie instrukcji obsługi jest normalnym użytkiem w złym stylu).

 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

Aby usunąć oryginalne źródło NASM z tej listy, usuń 26 wiodących znaków każdej linii za pomocą <chromakey.lst cut -b 26- > chromakey.asm. Wygenerowałem to z
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))- listami NASM, pozostawiając więcej pustych kolumn między kodem maszynowym a źródłem. Aby zbudować plik obiektowy, który można połączyć za pomocą C lub C ++, użyj nasm -felf64 chromakey.asm. (Lub yasm -felf64 chromakey.asm).

niesprawdzone , ale jestem całkiem pewien, że podstawowa idea load / load / cmov / store jest solidna, ponieważ jest taka prosta.

Mógłbym zapisać 3 bajty, gdybym mógł zażądać od dzwoniącego przekazania stałej klucza kluczowania (0x00ff00) jako dodatkowego argumentu zamiast stałego kodowania stałej w funkcji. Nie sądzę, aby zwykłe reguły pozwalały na pisanie bardziej ogólnej funkcji, w której osoba dzwoniąca skonfigurowała dla niej stałe. Ale jeśli tak, trzeci argument (obecnie dummy) jest przekazywany w edxABI SysV x86-64. Po prostu zmień cmp eax, 0x0000ff00(5B) na cmp eax, edx(2B).


Z SSE4 lub AVX, można zrobić to szybciej (ale większy rozmiar kodu) z pcmpeqdi blendvpszrobić 32-bitowego formatu elementem zmiennym mieszankę kontrolowany przez porównanie maski. (Z pand, możesz zignorować wysoki bajt). W przypadku spakowanego RGB24 możesz użyć, pcmpeqba następnie 2x pshufb+, pandaby uzyskać PRAWDA w bajtach, w których pasują wszystkie 3 elementy tego piksela pblendvb.

(Wiem, że to jest golf golfowy, ale zastanawiałem się nad wypróbowaniem MMX przed przejściem na liczbę całkowitą skalarną).


Czy możesz wysłać mi plik wykonywalny wykonany przy użyciu tego kodu maszynowego?
ckjbgames

x86_32, proszę.
ckjbgames

@ckjbgames: Nie napisałem programu wywołującego, który ładuje / zapisuje obrazy, tylko część modyfikacji pikseli w miejscu. Musiałbym to zrobić, zanim zbudowanie pliku wykonywalnego miałoby sens. Ale jeśli tak, jaki rodzaj pliku wykonywalnego? Windows PE32? Linux ELF32? FreeBSD ??
Peter Cordes,

ELF32, jeśli chcesz.
ckjbgames

@ckjbgames: Jeśli znajdę czas, szukam biblioteki ładującej obrazy i napiszę coś. Dodałem akapit o tym, jak z powrotem zamienić listę w kod, z którym można złożyć nasm -felf32. (W przypadku wersji 32-bitowej będziesz także potrzebować funkcji otoki, aby wywoływać z C, ponieważ nadal używa tych samych rejestrów, co x86-64 SysV ABI.)
Peter Cordes

13

Mathematica 57 35 bajtów

aktualizacja: domyślnie zielone tło jest usuwane za pomocą RemoveBackground. Pierwsze przesłanie zawierało niepotrzebny drugi parametr, „{„ Tło ”, zielony}”.


#~ImageCompose~RemoveBackground@#2&

Usuwa tło obrazu 2 i łączy wynik z obrazem 1.


Przykład

i1

Poniżej, w formie prefiksu, a nie infixu, wyraźniej pokazano, jak działa kod.

i2


4
Czy to zadziała w przypadku obrazów, w których nie jest to „tło” zielone? (Wydaje się, że na twoim wydruku pozostała niewielka plama zieleni)
DBS

Gdyby na zdjęciu była zielona „wyspa”, wymagany byłby dodatkowy parametr „{„ Tło ”, zielony}”, co zwiększyłoby całkowitą liczbę do 57 bajtów. To było moje pierwsze przesłanie. Ponieważ nie widzę zielonego izolowany na pierwszym planie obrazu, parametr ten został odrzucony
DavidC

11

Python 3 + numpy , 59 bajtów

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

Wypróbuj online!

Dane wejściowe są podawane w formacie numpytablicy, z trojaczami całkowitymi reprezentującymi piksele (gdzie #00FF00w kodzie szesnastkowym kod koloru jest równoważny [0, 255, 0]). Tablica wejściowa jest modyfikowana w miejscu, co jest dozwolone na meta .

Przykładowe obrazy

Dane wejściowe (z pytania)

Tło:

ckjbgames' profile picture

Pierwszoplanowy:

Dennis' profile picture

Obraz pierwszego planu po uruchomieniu funkcji:

Merged image with #00FF00 replaced with background pixels

Implementacja referencyjna (służy opencvdo odczytu plików obrazów)

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)

Wyświetla obraz na ekranie i zapisuje go w pliku wyjściowym.


17
Co oznaczają wszystkie czerwone kropki na obrazie wynikowym?
Yytsi

1
Zapytałem o I / O - wydaje się, że jest to zgodne z obecnym brzmieniem (tj. „Twoja biblioteka”), jeśli tak, to czy samo CV2 wymaga importu numpy? Jeśli nie można tego zrobić w 54 nie używając żadnych funkcji NumPy, a nie importowania numpy: lambda f,b:[x[list(x[0])==[0,255,0]]for x in zip(f,b)]. Jeśli lista list liczb całkowitych jest również akceptowalna, możesz to zrobić w 48 zlambda f,b:[x[x[0]==[0,255,0]]for x in zip(f,b)]
Jonathan Allan

..w rzeczywistości nawet jeśli do wykonania konwersji cv2 wymagana jest numpy, nadal uważam, że możesz wykonać wersję 54-bajtową, ponieważ nie musimy importować cv2 do wyzwania.
Jonathan Allan

5
Jeśli G == 255wartość zostanie zastąpiona, nawet jeśli R i B nie są równe zero, co prowadzi do czerwonych kropek. Dzieje się tak również w przypadku innych zespołów, nawet trudnych, co jest mniej widoczne. Wykonuje więc kontrole logiczne niezależnie od siebie i zamienia pojedyncze kanały, nawet jeśli spełniony jest tylko jeden z warunków. Np. Jeśli piksel to [0 255 37]czerwony, a zielone pasy zostaną zastąpione.
Leander Moesinger

2
@LeanderMoesinger: Dobrze zauważony. Miałem też tego robaka>. <; IDK, dlaczego myślałem, że tylko sprawdzanie zielonego = 0xFF podczas ignorowania R i B było poprawne!
Peter Cordes

9

Przetwarzanie, 116 99 bajtów

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

Niestety, przetwarzanie nie obsługuje rzeczy w Javie 8, takich jak lambdas.

Przykładowa implementacja: (zapisuje obraz jako out.pngi rysuje go na ekranie)

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

Można pozbyć się settings()oraz setup()funkcji i wystarczy uruchomić bezpośrednio kod.
Kevin Workman

@KevinWorkman Mam tam ustawienia i ustawienia, aby wyświetlał obraz na ekranie, co inaczej nie byłoby możliwe
dzaima

Jest #ff00albo 0xff00taki sam jak #00ff00w przetwarzaniu?
Peter Cordes,

@PeterCordes # FF00 niestety podaje błąd składniowy i # 00FF00 == 0xFF00FF00, więc 0xFF00 nie działa, ponieważ sprawdza wartość alfa 0
dzaima

@dzaima: Czy możesz robić zdjęcia w formacie RGB0, więc jakiego wzoru 0x0000FF00bitowego szukasz?
Peter Cordes

6

Bash + ImageMagick, 45 bajtów

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

Pobiera dwa obrazy jako argumenty i wyświetla dane wyjściowe na ekranie. Zamiast tego zmień x:na, $3aby zapisać w argumencie trzeciego pliku. Metoda jest prosta: przeczytaj obraz „tła”; przeczytaj obraz „pierwszego planu”; reinterpretuj kolor „limonka” (# 00ff00) jako przezroczystość na drugim obrazie; następnie skomponuj drugi obraz na pierwszym i wyślij.

ImageMagick: 28 bajtów?

Mogłem przesłać to jako odpowiedź ImageMagick, ale nie jest jasne, jak radzić sobie z argumentami. Jeśli chcesz założyć, że ImageMagick jest językiem opartym na stosie (co jest trochę nie do końca prawdą, ale prawie ... to dziwne), to -transparent lime -compositejest funkcja, która oczekuje dwóch obrazów na stosie i pozostawia jeden scalony obraz na stosie. Może to wystarczy, żeby policzyć?


3

MATL , 40 37 31 bajtów

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

Przykład uruchomienia z tłumaczem offline. Obrazy są wprowadzane przez ich adresy URL (można również podać lokalne nazwy plików).

wprowadź opis zdjęcia tutaj

Wyjaśnienie

,        % 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 bajtów

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

Wymaga danych wejściowych. Dane wejściowe to dwie ścieżki plików obrazów. Wyjście pliku o.pngNiestety tego nie można przetestować w tłumaczu online ze względów bezpieczeństwa ( 'jest na nim wyłączone). Musisz zainstalować Pyth na swoim komputerze, aby go przetestować.

Wyjaśnienie

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

Sama funkcja mieszania klucza chrominancji ma 13 bajtów, tak samo jak moja odpowiedź na kod maszynowy x86. Nie zdawałem sobie wcześniej sprawy, że był to kompletny program do przesyłania obrazów I / O.
Peter Cordes

2

Matlab 2016b i Octave, 62 59 bajtów

Dane wejściowe: A = macierz pierwszego planu MxNx3 jednostka 8, B = macierz tła MxNx3 jednostka 8.

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

Wyjście: A = macierz MxNx3 jednostka 8

Przykładowe użycie:

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 bajtów

Korzysta z CImg i może również pobierać pliki w innych formatach. Wynik zostanie wyświetlony w oknie.

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

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


1

R, 135 bajtów

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

Funkcja anonimowa, przyjmuje 2 ścieżki plików png jako argumenty i wyświetla obraz png o nazwie a.png .

Nieco golfa z objaśnieniami:

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 bajtów, co jest kluczem

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 jest pierwszym planem i rezultatem, J jest tłem. Obie są liczbami całkowitymi pikseli, w 32-bitowym formacie ARGB.

Bez golfa

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

Wyjaśnienie:

ARYOP to funkcja, która stosuje prostą operację do każdego elementu w tablicy.
Nazywa się to jakARYOP mode, output_array, input_array_1, input_array_2, ...

Po pierwsze, aby ustalić, które piksele na obrazie są zielone, -16711936(reprezentacja RGBA koloru zielonego) jest odejmowana od każdego piksela na obrazie na pierwszym planie. Daje to tablicę gdzie0 reprezentują zielone piksele, a dowolna inna liczba reprezentuje nie zielone piksele.

Aby przekonwertować wszystkie wartości niezerowe 1, są one podniesione do kwadratu (w celu usunięcia liczb ujemnych), a następnie zaciśnięte pomiędzy 0i 1.

Daje to tablicę zawierającą tylko 0s i 1s.
0s reprezentują zielone piksele na obrazie pierwszego planu i powinny zostać zastąpione pikselami z tła.
1s reprezentują nie zielone piksele, które trzeba będzie zastąpić pikselami z pierwszego planu.

Można to łatwo zrobić za pomocą interpolacji liniowej.


0

PHP, 187 bajtów

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

zakłada 24-bitowe pliki PNG; pobiera nazwy plików z argumentów wiersza poleceń, zapisuje na standardowe wyjście.
Uruchom z -r.

awaria

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 bajtów

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

Pobiera dane wejściowe jako dwa Imageobiekty (w składni curry), które można utworzyć za pomocą <image>elementu HTML . Zwraca obietnicę, która rozwiązuje adres URL danych Base64 wynikowego obrazu, który można zastosować dosrc pliku an<image> .

Chodziło tu o ustawienie wartości alfa dla każdego #00FF00 piksela na0 a następnie pomalować pierwszy plan, z wytłoczonym tłem, na tle.

Test Snippet

Uwzględnienie pierwszego planu i tła według ich adresów URL danych było zbyt duże, aby można było je tutaj opublikować, dlatego zostało przeniesione do CodePen:

Wypróbuj online!


0

OSL , 83 bajty

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

Zajmuje dwa wejścia. Pierwszy to pierwszy plan, a drugi tło.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.