Хромальний ключ до успіху


23

Значення кольору RGB #00FF00є досить важливим: воно використовується для створення фільмів, телевізійних шоу, погодних оголошень тощо. Це відомий колір "зелений телевізор" або "зелений екран".

Змагання

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

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

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

Випробування

Щедро надає @dzaima: Передумови: передній план: вихід:
моє зображення профілю

dennis

вихід


Звичайно, стандартні лазівки категорично заборонені . Це включає використання Інтернет-ресурсу, щоб зробити це за вас.
Це , тож може виграти найкоротший код і процвітати найкращий програміст ...


2
Чи можемо ми взяти об’єкт зображення у рідному форматі мови / бібліотеки в якості вхідного чи ми маємо читати зображення через ім'я файлу?
notjagan

@notjagan Ви можете взяти об’єкти зображення як вхідні дані.
ckjbgames

3
Чи прийнятний введення / виведення масивів масивів цілих чисел чи насправді обмежений деяким іншим набором вводу-виводу зображень?
Джонатан Аллан

1
@PeterCordes Я дозволю це.
ckjbgames

1
@PeterCordes ok
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-бітного С, оскільки жодна зі стандартних конвенцій викликів x86-32 не використовує ті ж регістри, що і 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).

неперевірений , але я досить впевнений, що основна ідея load / load / cmov / store є здоровою, адже це так просто.

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


За допомогою SSE4 або AVX ви можете зробити це швидше (але більший розмір коду) за допомогою pcmpeqdта blendvpsзробити 32-бітний розмір елементів із змінною сумішшю, керованою маскою порівняння. (З pand, ви можете ігнорувати високий байт). Для упакованого RGB24 ви можете використовувати, pcmpeqbа потім 2x pshufb+, pandщоб отримати TRUE в байтах, де відповідають всі 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. Перше подання включало непотрібний другий параметр `{" Фон ", Зелений}".


#~ImageCompose~RemoveBackground@#2&

Вилучає тло зображення 2 і компонує результат із зображенням 1.


Приклад

i1

Далі, в префіксах, а не в формі інфіксу, більш чітко показано, як працює код.

i2


4
Чи може це працювати для зображень, де зелений не "фон"? (Здається, у вашому виході залишився невеликий патьок зеленого кольору)
DBS

Якби на зображенні був зелений "острів", потрібен був би додатковий параметр "{" Фон ", Зелений}", який би підняв загальну кількість до 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
Що з усіма червоними крапками на отриманому зображенні?
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)]
Джонатан Аллан

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

5
Якщо G == 255значення замінюється, навіть якщо R і B не дорівнюють нулю, що призводить до червоних крапок. Це трапляється і для інших гуртів, навіть жорстких, які менш помітні. Таким чином, він здійснює перевірку логіки незалежно один від одного і замінює окремі канали, навіть якщо виконується лише одна з умов. Наприклад, якщо піксель є [0 255 37]червоним і зеленим смугами будуть замінені.
Leander Moesinger

2
@LeanderMoesinger: Добре помічений. У мене теж була ця помилка>. <; IDK, чому я вважав, що лише перевірка зеленого кольору = 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

Піт , 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 як аргументи та виводить зображення, що називається 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.

Безумовно

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.

Це призводить до масиву з лише 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()))

Вводить дані як два Imageоб'єкти (у синтаксисі currying), які можна створити за допомогою <image>елемента HTML . Повертає обіцянку , що вирішує до 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;}}

Бере два входи. Перший - передній план, а другий - фон.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.