성공의 크로마 키


23

RGB 색상 값 #00FF00은 영화, TV 쇼, 날씨 알림 등을 만드는 데 사용됩니다. 유명한 "TV green"또는 "green screen"색상입니다.

도전

당신의 임무는 PNG 형식 (또는 이미지 라이브러리의 이미지 객체 유형)과 동일한 크기의 두 가지 입력 이미지를 취하는 프로그램을 작성하는 것입니다. 하나의 이미지는 오래된 이미지 일 수 있습니다. 다른 하나는 색상의 배경을 가진 이미지입니다 #00FF00. 출력 이미지는 #00FF00색상이 없는 첫 번째 이미지 위에 겹쳐진 두 번째 이미지로 구성됩니다 (첫 번째 이미지 제외). 입력 및 출력은 파일, GUI 등을 사용하여 수행 할 수 있습니다 . 여기에 표시된대로 RGB 값 배열을 입력으로 사용할 수 있습니다 . 이미지에 전체 불투명도의 픽셀 만 있다고 가정 할 수 있습니다.

원래...

#00FF00한 이미지의 모든 픽셀 을 가져 와서 배경 이미지의 해당 픽셀로 대체 하는 프로그램을 만듭니다 .

테스트 사례

@dzaima에서 제공 : 배경 : 전경 : 출력 :
내 프로필 이미지

데니스

산출


물론 표준 허점은 엄격히 금지되어 있습니다. 여기에는 온라인 리소스를 사용하는 것이 포함됩니다.
이것은 이므로 가장 짧은 코드가 승리하고 최고의 프로그래머가 번영 할 수 있습니다 ...


2
언어 / 라이브러리의 기본 형식으로 이미지 객체를 입력으로 가져 오거나 파일 이름을 통해 이미지를 읽어야합니까?
notjagan

@notjagan 이미지 객체를 입력으로 취할 수 있습니다.
ckjbgames

3
정수 배열 배열의 I / O가 수용 가능합니까 아니면 실제로 다른 이미지 I / O 세트로 제한됩니까?
Jonathan Allan

1
@PeterCordes 허용합니다.
ckjbgames

1
@PeterCordes ok
ckjbgames

답변:


14

x86-64 (및 x86-32) 머신 코드, 13 15 13 바이트

변경 로그:

  1. 버그 픽스 : 첫 번째 버전은 G = 0xff 만 확인하고 R과 B는 0이 아니 었습니다. 배경을 수정 lodsd하여 포 그라운드 eax에서 짧은 형식 cmp eax, imm32인코딩 을 위해 fg 픽셀을 사용할 수 있도록 변경했습니다 (5 바이트) ) 대신 cmp dh,0xff(3 바이트)입니다.

  2. 2 바이트 저장 : bg를 수정하면 메모리 피연산자를 사용하여 cmov2 바이트 mov로드를 절약 할 수 있습니다 (필요한 경우 레지스터 저장).


이것은 x86-64 시스템 V 호출 규칙에 따라이 서명을 사용하여 C 또는 C ++ (x86-64 비 Windows 시스템의 경우)에서 직접 호출 할 수있는 기능입니다.

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

이미지 형식은 RGB0 32bpp이며 녹색 구성 요소는 각 픽셀에서 두 번째로 낮은 메모리 주소에 있습니다. 전경 배경 이미지에 장소 수정됩니다. pixel_count행 * 열입니다. 행 / 열에 대해서는 신경 쓰지 않습니다. 크롬 키는 사용자가 지정한 많은 양의 메모리를 혼합합니다.

RGBA (A가 0xFF 여야 함)는 다른 상수를 사용해야하지만 기능 크기에는 변화가 없습니다. Foreground DWORD는 4 바이트로 저장된 임의의 32 비트 상수에 대해 정확한 동등성을 비교하므로 모든 픽셀 순서 또는 크로마 키 색상을 쉽게 지원할 수 있습니다.

동일한 머신 코드도 32 비트 모드에서 작동합니다. 32 비트, 변경 등의 조립 rdiedi소스이다. 64 비트가되는 다른 모든 레지스터는 암시 적 (lodsd / stosd 및 루프)이며 다른 명시 적 레지스터는 32 비트를 유지합니다. 그러나 표준 x86-32 호출 규칙은 x86-64 SysV와 동일한 reg를 사용하지 않으므로 32 비트 C에서 호출하려면 랩퍼가 필요합니다.

NASM 목록 (머신 코드 + 소스)은 asm 초보자에게 더 복잡한 지침이 무엇인지 설명합니다. (참고 설명서를 복제하는 것은 일반적인 사용법에서 잘못된 스타일입니다.)

 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

이리스트에서 원래 NASM 소스를 얻으려면, 각 행의 선두 (26 개) 문자를 제거 <chromakey.lst cut -b 26- > chromakey.asm.
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))- NASM 목록을 사용 하여 이것을 생성 하면 시스템 코드와 소스 사이에 원하는 것보다 많은 빈 열이 남습니다. C 또는 C ++와 연결할 수있는 객체 파일을 만들려면을 사용하십시오 nasm -felf64 chromakey.asm. (또는 yasm -felf64 chromakey.asm).

테스트되지 않았지만 load / load / cmov / store의 기본 아이디어는 매우 간단하기 때문에 소리가 확실합니다.

호출자가 상수를 함수에 하드 코딩하는 대신 크로마 키 상수 (0x00ff00)를 추가 인수로 전달해야 할 경우 3 바이트를 절약 할 수 있습니다. 나는 일반적인 규칙이 호출자가 상수를 설정하는보다 일반적인 함수를 작성할 수 있다고 생각하지 않습니다. 그러나 만약 그렇다면, 3 번째 arg (현재 dummy)는 edxx86-64 SysV ABI로 전달됩니다 . 다만 변화 cmp eax, 0x0000ff00로 (5B) cmp eax, edx(도 2b).


SSE4 또는 AVX, 당신은와이 빠른 (그러나 큰 코드 크기를) 할 수 pcmpeqdblendvps비교] 마스크에 의해 제어되는 32 비트 요소 크기의 가변 혼합 할 수 있습니다. (을 사용 pand하면 높은 바이트를 무시할 수 있습니다). 압축 RGB24의 경우 pcmpeqb2x pshufb+ pand를 사용 하여 해당 픽셀의 3 가지 구성 요소가 모두 일치하는 바이트 단위로 TRUE를 얻을 수 pblendvb있습니다.

(이것은 코드 골프라는 것을 알고 있지만 스칼라 정수로 가기 전에 MMX 시도를 고려했습니다.)


이 기계 코드로 만든 실행 파일을 보내 주시겠습니까?
ckjbgames

x86_32 부탁합니다.
ckjbgames

@ckjbgames : 이미지를로드 / 저장하는 발신자를 작성하지 않았으며, 픽셀을 수정합니다. 실행 파일을 빌드하는 것이 이치에 맞기 전에 그렇게해야합니다. 그러나 내가 한 경우 어떤 종류의 실행 파일입니까? Windows PE32? 리눅스 ELF32? FreeBSD ??
Peter Cordes

ELF32입니다.
ckjbgames

@ckjbgames : 시간이 있으면 이미지 로딩 라이브러리를 찾아 무언가를 쓸 것입니다. 리스팅을 다시 조합 할 수있는 코드로 되 돌리는 방법에 대한 단락을 추가했습니다 nasm -felf32. (32 비트의 경우 x86-64 SysV ABI와 동일한 레지스터를 계속 사용하기 때문에 C에서 호출 할 래퍼 함수도 필요합니다.)
Peter Cordes

13

Mathematica 57 35 바이트

업데이트 : 기본적으로 녹색 배경은을 사용하여 제거됩니다 RemoveBackground. 첫 번째 제출에는 불필요한 두 번째 매개 변수`{ "Background", Green} "이 포함되었습니다.


#~ImageCompose~RemoveBackground@#2&

이미지 2의 배경을 제거하고 이미지 1로 결과를 구성합니다.


i1

다음은 접두사 형식이 아닌 접두사로 코드의 작동 방식을보다 명확하게 보여줍니다.

i2


4
녹색 "배경"이 아닌 이미지에서 작동합니까? (출력에 작은 녹색 패치가 남은 것 같습니다)
DBS

그림에 녹색 "섬"이있는 경우 추가 매개 변수 인 "{"Background ", Green}"이 필요하며 총 57 바이트가 증가합니다. 이는 처음 제출 한 것입니다. 그림의 전경에서 분리 된 파라미터는 삭제되었습니다
DavidC

11

파이썬 3 + numpy , 59 바이트

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

온라인으로 사용해보십시오!

입력은 numpy픽셀을 나타내는 정수 삼중 항과 함께 배열 형식으로 제공됩니다 (여기서 #00FF0016 진 색상 코드는와 동일 함 [0, 255, 0]). 입력 배열이 수정되어 meta마다 허용 됩니다 .

이미지 예

입력 (질문에서)

배경:

ckjbgames' profile picture

전경:

Dennis' profile picture

기능을 실행 한 후의 전경 이미지 :

Merged image with #00FF00 replaced with background pixels

참조 구현 ( opencv이미지 파일을 읽는 데 사용 )

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)

이미지를 화면에 표시하고 출력 파일에 씁니다.


17
결과 이미지의 모든 빨간 점은 무엇입니까?
Yytsi

1
나는 I / O에 대해 물었다-이것은 현재의 말 (즉, "귀하의 라이브러리")을 준수하는 것 같다. 그렇다면 cv2 자체는 numpy import를 필요로 하는가? 그렇지 않은 경우 numpy 함수를 사용하지 않고 numpy를 가져 오지 않으면 54에서 수행 할 수 lambda f,b:[x[list(x[0])==[0,255,0]]for x in zip(f,b)]있습니다. 정수 목록의 목록도 실제로 수용 가능하다면 48을 사용하여 수행 할 수 있습니다.lambda f,b:[x[x[0]==[0,255,0]]for x in zip(f,b)]
Jonathan Allan

.. 실제로 cv2 변환을 수행하는 데 numpy 필요한 경우에도 cv2 를 가져올 필요가 없으므로 여전히 54 바이트 버전을 수행 할 수 있다고 생각합니다.
Jonathan Allan

5
인 경우 G == 255R과 B가 0이 아니더라도 값이 바뀌어 빨간 점이 생깁니다. 다른 밴드에서도 잘 보이지 않는 경우에도 마찬가지입니다. 따라서 서로 독립적으로 논리 검사를 수행하고 조건 중 하나만 충족 되더라도 단일 채널을 교체합니다. 예를 들어 픽셀이 [0 255 37]빨간색과 초록색 밴드 이면 교체됩니다.
Leander Moesinger

2
@LeanderMoesinger : 잘 발견되었습니다. 나도 그 버그를 가졌다>. <; IDK는 R과 B를 무시하고 녹색 = xFF 만 검사하는 것이 정확하다고 생각했습니다.
Peter Cordes

9

처리, 116 99 바이트

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

불행히도 처리는 람다와 같은 Java 8을 지원하지 않습니다.

구현 예 : (이미지를 다른 이름으로 저장 out.png하고 화면에 그립니다)

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

settings()setup()함수를 제거 하고 코드를 직접 실행할 수 있습니다.
Kevin Workman

@KevinWorkman 여기에 설정과 설정이있어서 화면에 이미지를 표시합니다. 그렇지 않으면 불가능했을 것입니다
dzaima

인가 #ff00또는 0xff00같은 #00ff00처리에?
Peter Cordes

@PeterCordes # FF00은 슬프게도 구문 오류를 제공하며 # 00FF00 == 0xFF00FF00이므로 0xFF00은 알파 값 0을 검사 할 때 작동하지 않습니다
dzaima

@ dzaima : RGB0 형식으로 이미지를 가져올 수 있습니까 0x0000FF00? 찾고있는 비트 패턴입니까?
Peter Cordes

6

Bash + ImageMagick, 45 바이트

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

두 개의 이미지를 인수로 사용하여 화면에 출력을 표시합니다. 변경 x:하는 $3대신 세 번째 파일 인수에 쓸. 방법은 간단하다 : "배경"이미지를 읽는다; "전경"이미지를 읽으십시오; 컬러 "라임"(# 00ff00)을 제 2 이미지에서 투명도로 재 해석하고; 그런 다음 두 번째 이미지를 첫 번째 이미지와 합성하여 출력합니다.

ImageMagick : 28 바이트?

나는 이것을 ImageMagick 답변 으로 제출 할 수 있었지만 인수를 다루는 방법이 명확하지 않습니다. ImageMagick이 스택 기반 언어 (정말 사실은 아니지만 거의 ... 이상한 것입니다)라는 스택 기반 언어 -transparent lime -composite라는 것을 배치하려면 스택에 두 개의 이미지를 기대하고 스택에 하나의 병합 된 이미지를 남기는 함수입니다. 아마 계산하기에 충분할까요?


3

MATL , 40 37 31 바이트

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

오프라인 인터프리터로 실행하는 예제입니다. 이미지는 URL로 입력됩니다 (로컬 파일 이름도 제공 할 수 있음).

여기에 이미지 설명을 입력하십시오

설명

,        % 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 바이트

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

인용 된 입력이 필요합니다. 입력은 이미지 파일의 두 경로입니다. o.png안타깝게도 안전상의 이유로 온라인 통역사에서 테스트 할 수없는 파일을 출력하십시오 ( '해제되어 있지 않음). 당신은 얻을해야합니다 Pyth을 테스트하기 위해 컴퓨터에.

설명

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

크로 마키 블렌드 기능은 x86 머신 코드 응답과 같은 13 바이트입니다. 나는 이것이 완전한 프로그램 I / O를 전달하는 프로그램이라는 것을 이전에 몰랐다.
Peter Cordes

2

Matlab 2016b 및 Octave, 62 59 바이트

입력 : A = MxNx3 unit8 전경 매트릭스, B = MxNx3 unit8 배경 매트릭스.

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

출력 : A = MxNx3 unit8 matrix

샘플 사용 :

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 바이트

CImg을 사용하며 다른 형식의 파일도 사용할 수 있습니다. 결과가 창에 표시됩니다.

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

로 컴파일하십시오 g++ chromakey.cpp -g -L/usr/lib/i386-linux-gnu -lX11 -o chromakey -pthread.


1

R, 135 바이트

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

익명 함수, 2 개의 png 파일 경로를 인수로 사용하여라는 PNG 그림을 출력합니다 a.png.

설명과 함께 약간 ungolfed :

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 바이트

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전경이고 출력 J은 배경입니다. 둘 다 32 비트 ARGB 형식의 픽셀의 정수 배열입니다.

언 골프

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

설명:

ARYOP는 배열의 모든 요소에 간단한 연산을 적용하는 함수입니다.
그것은처럼 불린다ARYOP mode, output_array, input_array_1, input_array_2, ...

먼저 이미지의 어떤 픽셀이 녹색인지 확인하기 위해 -16711936전경 이미지의 각 픽셀에서 녹색의 RGBA 표현을 뺍니다. 이것은 배열을 제공합니다.0 녹색 픽셀을 나타내는 다른 숫자는 녹색이 아닌 픽셀을 나타냅니다.

0이 아닌 모든 값을로 변환하기 위해 1제곱 (음수를 제거하기 위해) 된 후 0와 사이에 고정됩니다 1.

결과적으로 0s와 1s 만있는 배열이됩니다 .
0s는 전경 이미지에서 녹색 픽셀을 나타내며 배경의 픽셀로 대체되어야합니다.
1는 녹색이 아닌 픽셀을 나타내며 포 그라운드에서 픽셀로 교체해야합니다.

이는 선형 보간을 사용하여 쉽게 수행 할 수 있습니다.


0

PHP, 187 바이트

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

24 비트 PNG 파일을 가정합니다. 명령 행 인수에서 파일 이름을 가져 와서 stdout에 씁니다.
로 실행하십시오 -r.

고장

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

자바 스크립트 (ES6), 290 바이트

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

ImageHTML <image>요소를 사용하여 만들 수있는 두 개의 객체 (커리 구문) 로 입력을 받습니다. 결과 이미지의 Base64 데이터 URL로 확인되는의 약속을 반환 src합니다 <image>.

여기서 아이디어는 각 #00FF00픽셀 의 알파 값 을0 한 다음 배경 위에 키가있는 전경을 배경 위에 페인트하는 것입니다.

테스트 스 니펫

데이터 URL로 포 그라운드 및 백그라운드를 포함하면 여기에 게시하기에 너무 커서 CodePen으로 이동되었습니다.

온라인으로 사용해보십시오!


0

OSL , 83 바이트

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

두 개의 입력을받습니다. 첫 번째는 전경이고 두 번째는 배경입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.