Хроматический ключ к успеху


23

Значение цвета RGB #00FF00является довольно важным: оно используется для создания фильмов, телевизионных шоу, объявлений о погоде и многого другого. Это знаменитый «зеленый экран» или «зеленый экран».

Соревнование

Ваша задача - написать программу, которая принимает два входных изображения, как в формате PNG (или в типе изображения в вашей библиотеке изображений), так и одинакового размера. Одно изображение может быть любым старым изображением. Другое изображение - это фон с цветом #00FF00. Выходное изображение будет состоять из второго изображения, наложенного поверх первого, без #00FF00присутствия цвета (кроме первого изображения). Ввод и вывод могут выполняться с помощью файлов, графического интерфейса пользователя и т. Д. Вы можете принимать массив значений RGB в качестве входных данных, как показано здесь . Вы можете предположить, что изображение имеет только пиксели с полной непрозрачностью.

В основном...

Создайте программу, которая берет каждый #00FF00пиксель в одном изображении и заменяет его соответствующим пикселем на фоновом изображении.

Тестовые случаи

Предоставлено @dzaima: Справочная информация: Передний план: Вывод:
мое изображение профиля

Деннис

выход


Конечно, стандартные лазейки строго запрещены . Это включает в себя использование интернет-ресурса, чтобы сделать это для вас.
Это , так что может победить самый короткий код и лучший программист ...


2
Можем ли мы взять объект изображения в родном формате языка / библиотеки в качестве входных данных, или мы должны прочитать изображение через имя файла?
notjagan

@notjagan Вы можете использовать объекты изображения в качестве входных данных.
ckjbgames

3
Допустим ли ввод-вывод массивов целых чисел или мы фактически ограничены каким-либо другим набором ввода-вывода изображения?
Джонатан Аллан

1
@PeterCordes Я позволю это.
ckjbgames

1
@PeterCordes хорошо
ckjbgames

Ответы:


14

машинный код x86-64 (и x86-32), 13 15 13 байт

Список изменений:

  1. Исправление: первая версия проверяла только G = 0xff, не требуя, чтобы R и B равнялись 0. Я перешел на модификацию фона на месте, чтобы я мог использовать lodsdна переднем плане, чтобы использовать пиксели fg 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_countэто строки * столбцы. Это не заботится о строках / столбцах; он просто сочетает ChromeKey с любым количеством мечей памяти, которые вы укажете.

RGBA (с A требуется равным 0xFF) потребует использования другой константы, но без изменения размера функции. DWORD переднего плана сравниваются на предмет точного равенства с произвольной 32-битной константой, хранящейся в 4 байтах, поэтому любой цвет в пиксельном порядке или цветность ключа цвета могут быть легко поддержаны.

Тот же машинный код также работает в 32-битном режиме. Для того, чтобы собрать как 32-бит, изменения , rdiчтобы ediв источнике. Все остальные регистры, которые становятся 64-битными, являются неявными (lodsd / stosd и loop), а другие явные регистры остаются 32-битными. Но обратите внимание, что вам понадобится оболочка для вызова из 32-битного C, потому что ни одно из стандартных соглашений о вызовах x86-32 не использует те же regs, что и x86-64 SysV.

Список 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)

непроверенный , но я вполне уверен, что основная идея загрузки / загрузки / cmov / store - это звук, потому что это так просто.

Я мог бы сохранить 3 байта, если бы потребовал, чтобы вызывающая сторона передавала константу хроматического ключа (0x00ff00) в качестве дополнительного аргумента вместо жесткого кодирования константы в функцию. Я не думаю, что обычные правила позволяют писать более общую функцию, в которой вызывающий объект устанавливает для нее константы. Но если это так, третий аргумент (в настоящее время dummy) передается в edxx86-64 SysV ABI. Просто измените cmp eax, 0x0000ff00(5B) на cmp eax, edx(2B).


С SSE4 или AVX вы могли бы сделать это быстрее (но с большим размером кода) с помощью pcmpeqdи blendvpsсделать 32-разрядное изменение размера элемента, управляемое маской сравнения. (С помощью pandвы можете игнорировать старший байт). Для упакованного RGB24 вы можете использовать, pcmpeqbа затем 2x pshufb+, pandчтобы получить ИСТИНА в байтах, где совпадают все 3 компонента этого пикселя pblendvb.

(Я знаю, что это код-гольф, но я решил попробовать MMX, прежде чем переходить к скалярному целому числу.)


Не могли бы вы выслать мне исполняемый файл с этим машинным кодом?
ckjbgames

x86_32, пожалуйста.
ckjbgames

@ckjbgames: я не написал вызывающую программу, которая загружает / сохраняет изображения, только часть "изменение пикселей на месте". Я должен сделать это, прежде чем будет иметь смысл создавать исполняемый файл. Но если бы я сделал, что за исполняемый файл? Windows PE32? Linux ELF32? FreeBSD ??
Питер Кордес

ELF32, если хотите.
ckjbgames

@ckjbgames: если я найду время, я поищу библиотеку загрузки изображений и напишу что-нибудь. Я добавил параграф о том, как превратить листинг обратно в код, который можно собрать nasm -felf32. (Для 32-битных вам также понадобится функция-обертка для вызова из C, потому что она все еще использует те же регистры, что и x86-64 SysV ABI.)
Питер Кордес

13

Mathematica 57 35 байт

обновление: по умолчанию зеленый фон удаляется с помощью RemoveBackground. Первое представление включало ненужный второй параметр, `{" 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массива, с целочисленными триплетами, представляющими пиксели (где #00FF00в шестнадцатеричном коде цвета эквивалентно [0, 255, 0]). Входной массив изменяется на месте, что разрешено для каждой мета .

Примеры изображений

Вход (из вопроса)

Задний план:

Фотография профиля пользователя ckjbgames

Передний план:

Фотография профиля пользователя Dennis

Изображение переднего плана после запуска функции:

Объединенное изображение с заменой # 00FF00 на фоновые пиксели

Реализация ссылок (используется 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
Что со всеми красными точками на полученном изображении?
Юцы

1
Я спрашивал о вводе / выводе - это, кажется, соответствует текущей формулировке (то есть "ваша библиотека"), если да, то требует ли сам cv2 импровизированный импорт? Если вы не можете сделать это в 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)]
Джонатан Аллан

..в факт , даже если NumPy это требуется для CV2 , чтобы выполнить преобразование , я все еще думаю , что вы могли бы сделать версию 54 байт, так как мы не должны импортировать CV2 на вызов.
Джонатан Аллан

5
Если G == 255значение заменяется, даже если R и B не равны нулю, что приводит к появлению красных точек. Это также случается для других групп, даже жестких, которые менее заметны. Таким образом, он выполняет логические проверки независимо друг от друга и заменяет отдельные каналы, даже если выполняется только одно из условий. Например, если пиксель - [0 255 37]красная и зеленая полосы будут заменены.
Леандер Мезингер

2
@LeanderMoesinger: Хорошо заметили. У меня тоже была эта ошибка>. <; ИДК, почему я думал, что только проверка зеленого цвета = 0xFF при игнорировании R и B была правильной!
Питер Кордес

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

Это #ff00или так 0xff00же, как #00ff00в обработке?
Питер Кордес

@PeterCordes # FF00, к сожалению, выдает синтаксическую ошибку и # 00FF00 == 0xFF00FF00, поэтому 0xFF00 не работает, поскольку проверяет альфа-значение 0
dzaima

@dzaima: Можете ли вы взять ваши изображения в формате RGB0, так 0x0000FF00что это битовый шаблон, который вы ищете?
Питер Кордес

6

Bash + ImageMagick, 45 байт

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

Принимает два изображения в качестве аргументов и отображает вывод на экране. Изменение x:к $3записи в третьем аргументе файла вместо этого. Способ прост: прочитайте «фоновое» изображение; читать изображение на переднем плане; повторно интерпретировать цвет «лайм» (# 00ff00) как прозрачность во втором изображении; затем скомпонуйте второе изображение на первое и выведите.

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

Функция смешивания с хроматическим ключом сама по себе составляет 13 байт, так же, как мой ответ с машинным кодом x86. Раньше я не осознавал, что это была полная программа для ввода-вывода изображений.
Питер Кордес

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

Анонимная функция, принимает 2 пути файла png в качестве аргументов и выводит изображение png с именем a.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 байт Что ключ

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.

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

Объяснение:

ARYOP - это функция, которая применяет простую операцию к каждому элементу в массиве.
Это называется какARYOP mode, output_array, input_array_1, input_array_2, ...

Сначала, чтобы определить, какие пиксели в изображении являются зелеными, -16711936(представление RGBA зеленого цвета) вычитается из каждого пикселя на переднем плане изображения. Это дает массив, где 0представляет зеленые пиксели, а любое другое число представляет не зеленые пиксели.

Чтобы преобразовать все ненулевые значения в 1, они возводятся в квадрат (для удаления отрицательных чисел), а затем зажимаются между 0и 1.

Это приводит к массиву только 0с s и 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; берет имена файлов из аргументов командной строки, пишет в стандартный вывод.
Беги с -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()))

Принимает ввод как два Imageобъекта (в синтаксисе карри), которые могут быть созданы с помощью <image>элемента HTML . Возвращает Promise, который разрешает URL-адрес данных Base64 конечного изображения, который может быть применен к 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;}}

Принимает два входа. Первый - это передний план, а второй - фон.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.