Детектор крайового звуку


12

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

Виявлення краю працює наступним чином (якщо незрозуміло, див. Розпізнавання крайових міток ):

  • Значення пікселя - це загальна яскравість пікселя, тому, якщо він кольоровий, вам потрібно спочатку перетворити його в масштаб сірого (щоб все було просто та в гольф, ви можете взяти середнє значення для R, G та Б).
  • Формули для G x і G y для пікселя p (i, j) такі:
    • G x = -1 * p (i-1, j-1) - 2 * p (i-1, j) - 1 * p (i-1, j + 1) + 1 * p (i + 1, j -1) + 2 * p (i + 1, j) + 1 * p (i + 1, j + 1)
    • G y = -1 * p (i-1, j-1) - 2 * p (i, j-1) - 1 * p (i + 1, j-1) + 1 * p (i-1, j +1) + 2 * p (i, j + 1) + 1 * p (i + 1, j + 1)
  • Значення для розміру краю в цьому пікселі дорівнює: √ (G x 2 + G y 2 )

Вихідне зображення має для кожного пікселя розмір краю √ (G x 2 + G y 2 ) у вигляді відтінків сірого.

Бонуси:

  • Виконайте гаусову розмитість, щоб згладити зображення, перш ніж починати виявлення ребер, щоб опустити будь-які менші краї. Це дає бонус -30% на кінцевий результат.
  • Враховуйте кут ребра. Ви надаєте вихідному пікселю деякий колір, беручи те саме значення сірого масштабу і додаючи колір з кольорового колеса, використовуючи кут, отриманий з формули арктан (G y / G x ). Це дає ще один бонус -30% на кінцевий результат.

Правила:

  • Ви можете опустити значення крайових пікселів і встановити їх на чорний колір, або ви можете використовувати 0 для будь-якого пікселя поза зображенням.
  • Ваше зображення має бути у форматі зображення, який можна відкрити на більшості комп'ютерів.
  • Вихідні дані повинні бути записані на диск або передаватися у файл.
  • Введення задається у вигляді аргументу командного рядка у вигляді відносного шляху до зображення або введено з командного рядка.
  • Це код гольфу, тому найкоротший код у байтах виграє!

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

Ви бачили відео про виявлення краю від Computerphile ? Я відчуваю запах зв'язку :)
GiantTree

@flawr Мені потрібно перевірити, яка гауссова розмитість корисна для виявлення ребер, тому я не знаю, що таке хороша цінність. більше про розмиття Гаусса тут . Вхідне зображення кольорове, і вам потрібно спочатку перетворити його в масштаб сірого, якщо ви хочете виявити край. Виявлення ребер виконується або A: на внутрішніх пікселях, і ви встановлюєте зовнішню межу 1px вихідного зображення чорною, або B: на всіх пікселях, і ви приймаєте 0 як значення для будь-яких пікселів поза зображенням.
vrwim

@GiantTree nooooooo відео абсолютно не пов'язане :)
vrwim

4
Чому за це було проголосовано? Здається, це цілком справедливе питання.
Аддісон Кримп

Відповіді:


13

J, 166 164 161 154 150 144 143 байт.

Не надто багато гольфу; Я в основному згорнув свою тривалішу реалізацію (див. Нижче), тому, ймовірно, є багато можливостей для вдосконалення. Використовує бібліотеку BMP. Зберігає результат у файлі o. Я обробляв крайові пікселі, використовуючи лише повні 3х3 комірки, тому кінцеве зображення має ширину та висоту менше на 2 пікселі.

load'bmp'
S=:s,.0,.-s=:1 2 1
p=:([:*:[:+/[:,*)"2
'o'writebmp~256#.3#"0<.255<.%:(S&p+(|:S)&p)3 3,.;._3(3%~])+/"1(3#256)#:readbmp}:stdin''
exit''

Використання:

echo 'image.bmp' | jconsole golf.ijs

Розширено:

load 'bmp'

sobel1 =: 3 3 $ 1 0 _1 2 0 _2 1 0 _1
NB. transposed
sobel2 =: |: sobel1
NB. read image
image =: readbmp }: stdin''
NB. convert default representation to R,G,B arrays
rgbimage =: (3 # 256) #: image
NB. convert to grayscale
greyimage =: 3 %~ (+/"1) rgbimage
NB. 3x3 cells around each pixel
cells =: 3 3 ,.;._3 greyimage
NB. multiply 3x3 cell by 3x3 sobel, then sum all values in it
partial =: 4 : '+/"1 +/"1 x *"2 y'
NB. square partial (vertical and horizontal) results, sum and root
combine =: [: %: *:@[ + *:@]
NB. limit RGB values to 255
limit =: 255 <. ]
newimage =: limit (sobel1&partial combine sobel2&partial) cells
NB. convert back to J-friendly representation
to_save =: 256 #. 3 #"0 <. newimage
to_save writebmp 'out.bmp'
NB. jconsole stays open by default
exit''

Зразок введення та виведення:

Оригінал Виявлення країв


Це хороший приклад ;._3оператора subarray. Я помітив, що ви визначили дієслово pз ранг 2, щоб діяти над підрядами після їх створення. Ви можете замість цього оперувати кожним підрядним масивом під час скорочення. Моя спроба втілити його на основі вашої роботи 256#.3#"0<.255<.3 3((|:S)&*+&.*:&(+/)&,S&*);._3%&3(3#256)+/@#:. Це повинно зменшити його до 126 байт.
милі

Я отримав його до 119 байт з 'o'writebmp~256#.3#"0<.255<.3 3(*+&.*:&(+/)&,(*|:))&((-,.0,.])1 2 1);._3%&3(3#256)+/@#:readbmp]stdin''припущенням, що на stdin вводиться лише ім'я файлу. Ви можете виконати це за допомогою echo -nтого, щоб додатковий новий рядок не був включений у stdin. На моєму комп’ютері сценарій автоматично вимикається при використанні конвеєрного вводу до сценарію, що означає, що я не повинен включати exit''і можу зберегти зайві 6 байт, але я не впевнений, чи це справедливо для всіх.
милі

1

Пітон, 161 * 0,7 = 112,7 байт

З бонусом за розмиття Гаусса.

Оскільки ви прямо не заборонили вбудовані методи, ось OpenCV:

from cv2 import*
from numpy import*
g=GaussianBlur(cvtColor(imread(raw_input()),6),(3,3),sigmaX=1)
x,y=Sobel(g,5,1,0),Sobel(g,5,0,1)
imwrite('s.png',sqrt(x*x+y*y))

Без бонусу, 136 байт

from cv2 import*
from numpy import*
g=cvtColor(imread(raw_input()),6)
x,y=Sobel(g,5,1,0),Sobel(g,5,0,1)
imwrite('s.png',sqrt(x*x+y*y))
  • Edit1: Замінено названі константи на їх значення.
  • Edit2: завантажені зразки

оригінальний фільтрують


Чи могли б ви надати зразок вхідного та вихідного зображення?
Р. Кап

@ R.Kap краще пізно, ніж ніколи.
Карл Напф

0

MATLAB, 212 * 0,4 = 84,8 байт

Використання панелі інструментів фільтра та простору кольорів HSV

function f(x);f=@(i,x)imfilter(i,x);s=@(x)fspecial(x);S=s('sobel');A=f(double(rgb2gray(imread(x)))/255,s('gaussian'));X=f(A,S);Y=f(A,S');imwrite(hsv2rgb(cat(3,atan2(Y,X)/pi/2+0.5,0*A+1,sqrt(X.^2+Y.^2))),'t.png')

або невольф

function f(x)
f=@(i,x)imfilter(i,x);
s=@(x)fspecial(x);
S=s('sobel');
A=f(double(rgb2gray(imread(x)))/255,s('gaussian'));
X=f(A,S);
Y=f(A,S');
imwrite(hsv2rgb(cat(3,atan2(Y,X)/pi/2+0.5,0*A+1,sqrt(X.^2+Y.^2))),'t.png')

0

Love2D Lua, 466 байт

A=arg[2]i=love.image.newImageData q=math t=i(A)g=i(t:getWidth()-2,t:getHeight()-2)m={{-1,-2,-1},{0,0,0},{1,2,1}}M={{-1,0,1},{-2,0,2},{-1,0,1}}t:mapPixel(function(_,_,r,g,b)a=(r+g+b)/3 return a,a,a end)g:mapPixel(function(x,y)v=0 for Y=0,2 do for X=0,2 do v=v+(t:getPixel(x+X,y+Y)*m[Y+1][X+1])end end V=0 for Y=0,2 do for X=0,2 do V=V+(t:getPixel(x+X,y+Y)*M[Y+1][X+1])end end v=q.max(q.min(q.sqrt(V^2+v^2),255),0)return v,v,v end)g:encode('png',"o")love.event.quit()

Здійснює введення командного рядка та виводить файл, який називається "o", у папці з поданими додатків Love2D. Love2D Не дозволяю вам зберігати файли в будь-якому іншому місці.

Настільки ж гольф, як я міг його отримати, ймовірно, можна було б пограти в гольф далі.

Пояснив

-- Assign the Input to A
A=arg[2]


-- Assign some macros to save FUTURE BYTES™
i=love.image.newImageData
q=math

-- t is the original image, g is the new output image. g is two pixels smaller, which is easier and better looking than a border.
t = i(A)
g = i(t:getWidth()-2,t:getHeight()-2)

-- m and M are our two sobel kernals. Fairly self explanitary.
m = {{-1,-2,-1}
    ,{0,0,0}
    ,{1,2,1}}

M = {{-1,0,1}
    ,{-2,0,2}
    ,{-1,0,1}}

-- Convert t to grayscale, to save doing this math later.
t:mapPixel(function(_,_,r,g,b)a=(r+g+b)/3 return a,a,a end)

-- Execute our kernals
g:mapPixel(function(x,y)
    -- v refers to the VERTICAL output of the Kernel m.
    v=0
    for Y=0,2 do
        for X=0,2 do
            v=v+(t:getPixel(x+X,y+Y)*m[Y+1][X+1])
        end
    end

    -- V is the HORIZONTAL of M
    V=0
    for Y=0,2 do
        for X=0,2 do
            V=V+(t:getPixel(x+X,y+Y)*M[Y+1][X+1])
        end
    end

    -- Clamp the values and sum them.
    v = q.max(q.min(q.sqrt(V^2 + v^2),255),0)
    -- Return the grayscale.
    return v,v,v
end)

-- Save, renaming the file. The golfed version just outputs as 'o'
g:encode('png',"S_".. A:gsub("(.*)%....","%1.png"))

-- Quit. Not needed, but I'm a sucker for self contained LOVE2D
love.event.quit()

Тест

Вхідні дані Вихідні дані

І ...

Хоча насправді це не покращує міркування (робить його гіршим), ось версія з кольоровим колесом.

900 - 270 = 630 байт

A=arg[2]i=love.image.newImageData q=math t=i(A)g=i(t:getWidth()-2,t:getHeight()-2)m={{-1,-2,-1},{0,0,0},{1,2,1}}M={{-1,0,1},{-2,0,2},{-1,0,1}}function T(h,s,v)if s <=0 then return v,v,v end h,s,v=h*6,s,v/255 local c=v*s local x=(1-q.abs((h%2)-1))*c local m,r,g,b=(v-c),0,0,0 if h < 1 then r,g,b=c,x,0 elseif h < 2 then r,g,b=x,c,0 elseif h < 3 then r,g,b=0,c,x elseif h < 4 then r,g,b=0,x,c elseif h < 5 then r,g,b=x,0,c else r,g,b=c,0,x end return(r+m)*255,(g+m)*255,(b+m)*255 end t:mapPixel(function(_,_,r,g,b)a=(r+g+b)/3 return a,a,a end)g:mapPixel(function(x,y)v=0 for Y=0,2 do for X=0,2 do v=v+(t:getPixel(x+X,y+Y)*m[Y+1][X+1])end end V=0 for Y=0,2 do for X=0,2 do V=V+(t:getPixel(x+X,y+Y)*M[Y+1][X+1])end end h=v H=V v=q.max(q.min(q.sqrt(V^2+v^2),255),0)h=q.atan2(H,h)/q.pi*2 return T(h,1,v,255)end)g:encode('png',"S_".. A:gsub("(.*)%....","%1.png"))G=love.graphics.newImage(g)love.event.quit()

введіть тут опис зображення

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