成功へのクロマキー


23

RGBカラー値#00FF00はかなり重要な値です。映画、テレビ番組、天気予報などの作成に使用されます。有名な「TV緑」または「緑スクリーン」の色です。

チャレンジ

あなたの仕事は、PNG形式(または画像ライブラリの画像オブジェクトタイプ)と同じサイズの2つの入力画像を取得するプログラムを書くことです。1つのイメージには、任意の古いイメージを使用できます。もう1つは、色の背景を持つ画像です#00FF00。出力イメージは、最初のイメージの上にオーバーレイされた2番目のイメージで構成され、#00FF00色は存在しません(最初のイメージを除く)。入力や出力は、ファイルやGUIなどで実行できますここに示すように、RGB値の配列を入力として使用できます。画像には完全な不透明度のピクセルしかないと仮定できます。

基本的に...

#00FF001つの画像のすべてのピクセルを取得し、背景画像の対応するピクセルに置き換えるプログラムを作成します。

テストケース

@dzaimaが提供するもの:背景: 前景: 出力:
私のプロフィール画像

デニス

出力


もちろん、標準の抜け穴は固く禁じられています。これには、オンラインリソースを使用して行うことが含まれます。
これはですので、最短のコードが勝ち、最高のプログラマーが繁栄するかもしれません...


2
言語/ライブラリのネイティブ形式の画像オブジェクトを入力として使用できますか、またはファイル名で画像を読み取る必要がありますか?
-notjagan

@notjagan入力として画像オブジェクトを使用できます。
-ckjbgames

3
整数の配列の配列のI / Oは許容されますか、それとも実際には他の画像I / Oのセットに制限されていますか?
ジョナサンアラン

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エンコーディング(5バイト)、代わりにcmp dh,0xff(3バイト)。

  2. 2バイトの保存:bgを適切に変更すると、のメモリオペランドを使用できるようcmovになり、2バイトのmov負荷を節約できます(重要な場合はレジスタを保存できます)。


これは、x86-64 System 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で、緑成分は各ピクセル内の2番目に低いメモリアドレスにあります。フォアグラウンドの背景画像はその場で修正されます。 pixel_countrows * columnsです。行/列は気にしません。指定したメモリの多くのdwordをクロムキーブレンドするだけです。

RGBA(Aは0xFFである必要があります)では、異なる定数を使用する必要がありますが、関数サイズは変更しません。フォアグラウンドDWORDは、4バイトに格納された任意の32ビット定数と正確に等しいかどうか比較されるため、ピクセル順またはクロマキーの色を簡単にサポートできます。

同じマシンコードは32ビットモードでも機能します。32ビットとしてアセンブルするには、次のように変更rdiしますediは、ソースます。64ビットになる他のすべてのレジスタは暗黙的(lodsd / stosd、およびループ)であり、他の明示的なregは32ビットのままです。ただし、32ビットCから呼び出すにはラッパーが必要になることに注意してください。これは、標準のx86-32呼び出し規約ではx86-64 SysVと同じregsを使用しないためです。

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番目の引数(現在dummy)はedxx86-64 SysV ABIで渡されます。cmp eax, 0x0000ff00(5B)をcmp eax, edx(2B)に変更するだけです。


SSE4またはAVXを使用するpcmpeqdblendvps、比較マスクで制御される32ビット要素サイズの可変ブレンドを使用して、これをより高速に(ただし、より大きなコードサイズで)実行できます。(を使用pandすると、上位バイトを無視できます)。パックされたRGB24の場合pcmpeqb、次に2x pshufb+ pandを使用して、そのピクセルの3つのコンポーネントすべてが一致するバイトでTRUEを取得し、次にを使用しpblendvbます。

(これがコードゴルフであることは知っていますが、スカラー整数を使用する前にMMXを試すことを検討しました。)


このマシンコードで作成された実行可能ファイルを送ってもらえますか?
ckjbgames

x86_32、お願いします。
ckjbgames

@ckjbgames:画像の読み込み/保存を行う呼び出し元は書いておらず、その場でピクセルを修正する部分だけを書いています。実行可能ファイルをビルドするのが理にかなってしまう前に、それをしなければなりません。しかし、私がした場合、どのような実行可能ファイルですか?Windows PE32?Linux ELF32?FreeBSD ??
ピーターコーデス

ELF32、そうするなら。
ckjbgames

@ckjbgames:時間を見つけたら、画像読み込みライブラリを探して何かを書きます。リストをアセンブル可能なコードに戻す方法についての段落を追加しましたnasm -felf32。(32ビットでは、x86-64 SysV ABIと同じレジスタを使用しているため、Cから呼び出すラッパー関数も必要になります。)
Peter Cordes

13

Mathematica 57 35バイト

更新:デフォルトでは、を使用して緑の背景が削除されRemoveBackgroundます。最初の送信には、不要な2番目のパラメーター、 `{" Background "、Green}"が含まれていました。


#~ImageCompose~RemoveBackground@#2&

画像2の背景を削除し、結果を画像1で構成します。


i1

次は、中置形式ではなく接頭辞形式で、コードがどのように機能するかをより明確に示しています。

i2


4
これは、緑色の「背景」ではない画像に対して機能しますか?(あなたの出力にグリーン左の小さなパッチがあるようです)
DBS

写真に緑色の「島」がある場合、追加のパラメーター「{"Background"、Green}」が必要になり、合計が57バイトになります。これが最初の提出です。画像の前景で隔離され、そのパラメーターは削除されました
DavidC

11

Python 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インポートを必要としますか そうでない場合は、54でnumpy関数を使用せず、numpy:をインポートしないことで実行できます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)]
ジョナサンアラン

..実際、cv2が変換を実行するためにnumpy 必要な場合でも、チャレンジのためにcv2をインポートする必要がないため、54バイトバージョンを実行できると思います。
ジョナサンアラン

5
の場合 G == 255、RとBがゼロでなくても値が置き換えられ、赤い点が表示されます。これは他のバンドでも起こります。そのため、互いに独立してロジックチェックを実行し、条件の1つだけが満たされていても、単一のチャネルを交換します。たとえば、ピクセルが[0 255 37]赤と緑のバンドである場合は置き換えられます。
リアンダーモージンガー

2
@LeanderMoesinger:よく見分けられます。私も私のバグを抱えていました>。<; IDKで、RとBを無視してgreen = 0xFFだけをチェックするのが正しいと思った理由!
ピーターコーデス

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()機能だけで直接コードを実行します。
ケビンワークマン

@KevinWorkmanそれはそうでないことはできないであろう、画面に画像を表示したいので、私はそこの設定とセットアップを持っている
dzaima

である#ff000xff00のように同じ#00ff00処理で?
ピーターコーデス

@PeterCordes#FF00は悲しいことに構文エラーを出し、#00FF00 == 0xFF00FF00なので、0xFF00はアルファ値0をチェックするため動作しません
-dzaima

@dzaima:RGB0形式で画像を撮影できます0x0000FF00か?
ピーター・コーデス

6

Bash + ImageMagick、45バイト

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

引数として2つの画像を取り、画面に出力を表示します。代わりに3番目のファイル引数に書き込むように変更x:$3ます。この方法は簡単です。「背景」画像を読み取ります。「フォアグラウンド」画像を読む; 2番目の画像の透明度として色「ライム」(#00ff00)を再解釈します。次に、2番目の画像を最初の画像に合成して出力します。

ImageMagick:28バイト?

これをImageMagickの回答として提出することもできましたが、引数の処理方法は明確ではありません。ImageMagickがスタックベースの言語であると仮定したい場合(これはちょっと本当ではありませんが、ほとんど...奇妙です)-transparent lime -composite、スタック上の2つの画像を期待し、スタック上に1つのマージされた画像を残す関数です。多分それは数えるのに十分ですか?


3

MATL40 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'

引用された入力を受け取ります。入力は、画像ファイルの2つのパスです。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

クロマキーブレンド関数自体は13バイトで、x86マシンコードの回答と同じです。これがイメージI / Oを処理する完全なプログラムであることも以前は知りませんでした。
ピーターコーデス

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マトリックス

使用例:

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

匿名関数a.png。引数として2つのpngファイルパスを取り、というpng画像を出力します。

説明付きで、わずかに制限なし:

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バイトwhatsキー

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緑のピクセルを表すその他の数値は緑以外のピクセルを表します。

すべての非ゼロの値を変換するには1、彼らが(負の数を削除する)乗され、その後の間にクランプ01

これにより、0sと1s のみの配列が作成されます。
0sは前景画像の緑のピクセルを表し、背景のピクセルに置き換える必要があります。
1sは緑以外のピクセルを表し、それらは前景のピクセルに置き換える必要があります。

これは、線形補間を使用して簡単に実行できます。


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

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

入力を2つのImageオブジェクト(カリー化構文)として受け取り、HTMLで作成できます<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;}}

2つの入力を取ります。最初は前景で、2番目は背景です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.