Rilevatore di bordi Sobel


12

Il tuo compito è scrivere un programma che acquisisca un'immagine di input ed esegua il rilevamento dei bordi per diventare un'immagine di output.

Il rilevamento dei bordi funziona come segue (se non chiaro, vedere rilevamento dei bordi sobel ):

  • Il valore per un pixel è la luminosità totale di un pixel, quindi se è a colori, dovrai prima convertirlo in scala di grigi (per mantenere le cose semplici e in grado di giocare a golf, puoi prendere il valore medio per R, G e B).
  • Le formule per G xe G y per pixel p (i, j) sono:
    • 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)
  • Il valore per la dimensione del bordo in quel pixel è quindi: √ (G x 2 + G y 2 )

L'immagine di output è per ogni pixel la dimensione del bordo √ (G x 2 + G y 2 ) come scala di grigi.

bonus:

  • Eseguire una sfocatura gaussiana per appianare l'immagine prima che inizi il rilevamento dei bordi, per omettere eventuali bordi più piccoli. Questo dà un bonus del -30% sul risultato finale.
  • Prendi in considerazione l'angolo del bordo. Dai un po 'di colore al pixel di output, prendendo lo stesso valore di scala di grigi e aggiungendo il colore da una ruota dei colori usando l'angolo ottenuto dalla formula arctan (G y / G x ). Questo dà un altro bonus del -30% sul risultato finale.

Regole:

  • È possibile omettere il valore per i pixel di bordo e impostarli su nero oppure utilizzare 0 per qualsiasi pixel esterno all'immagine.
  • L'immagine di uscita deve essere in un formato immagine che può essere aperto sulla maggior parte dei computer.
  • L'output deve essere scritto su disco o può essere trasferito in un file.
  • L'input viene fornito come argomento della riga di comando, sotto forma di un percorso relativo all'immagine, oppure reindirizzato dalla riga di comando.
  • Questo è il codice golf, quindi vince il codice più breve in byte!

Puoi specificare esattamente la sfocatura gaussiana? Anche la scala di grigi di input, se no, come dovremmo applicare questo rilevamento dei bordi alle immagini colorate? È corretto che l'immagine di output abbia le stesse dimensioni dell'input, ma l'input viene eseguito solo sui pixel interni (non su quello che abbiamo impostato a zero)?
Flawr

Hai visto i video sul rilevamento dei bordi da Computerphile ?
Sento l'

@flawr Devo testare quale sfocatura gaussiana è buona per il rilevamento dei bordi, quindi non so davvero quale sia un buon valore. di più sulla sfocatura gaussiana qui . L'immagine di input è a colori e dovrai prima convertirla in scala di grigi se vuoi eseguire il rilevamento dei bordi. Il rilevamento dei bordi viene eseguito su A: sui pixel interni e si imposta il bordo esterno 1px dell'immagine di output su nero, oppure B: su tutti i pixel e si prende 0 come valore per tutti i pixel esterni all'immagine.
vrwim,

@GiantTree nooooooo il video non è assolutamente correlato :)
vrwim

4
Perché è stato votato in giù? Sembra essere una domanda perfettamente valida.
Addison Crump,

Risposte:


13

J, 166 164 161 154 150 144 143 byte.

Non giocare troppo a golf; Per lo più ho compresso l'implementazione più lunga (vedi sotto), quindi probabilmente c'è molto margine di miglioramento. Utilizza la libreria BMP. Salva il risultato nel file o. Ho gestito i pixel perimetrali utilizzando solo celle 3x3 complete, quindi l'immagine finale ha larghezza e altezza inferiori di 2 pixel.

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

Uso:

echo 'image.bmp' | jconsole golf.ijs

Allargato:

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

Input e output di esempio:

Originale Rilevazione dei bordi


Questo è un bell'esempio dell'operatore del ;._3subarray. Ho notato che hai definito un verbo pcon rango 2 per operare sui subarrays dopo averli creati. Potresti invece operare su ogni subarray quando tagli. Il mio tentativo di implementarlo in base al tuo lavoro è 256#.3#"0<.255<.3 3((|:S)&*+&.*:&(+/)&,S&*);._3%&3(3#256)+/@#:. Ciò dovrebbe ridurlo a 126 byte in totale.
miglia,

Sono arrivato a 119 byte 'o'writebmp~256#.3#"0<.255<.3 3(*+&.*:&(+/)&,(*|:))&((-,.0,.])1 2 1);._3%&3(3#256)+/@#:readbmp]stdin''supponendo che su stdin sia inserito solo il nome file. È possibile eseguire questa operazione in echo -nmodo che una nuova riga aggiuntiva non sia inclusa in stdin. Sul mio computer, lo script si chiude automaticamente quando si utilizza un input convogliato a uno script, il che significa che non devo includere il exit''e posso salvare altri 6 byte, ma non sono sicuro che questo sia vero per tutti.
miglia,

1

Python, 161 * 0,7 = 112,7 byte

Con il bonus Sfocatura gaussiana.

Poiché non hai esplicitamente vietato i metodi integrati, ecco 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))

Senza bonus, 136 byte

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: ha sostituito le costanti nominate con i loro valori.
  • Modifica2: campioni caricati

originale filtrato


Potresti forse fornire un'immagine di input e output di esempio?
R. Kap

@ R.Kap meglio tardi che mai.
Karl Napf,

0

MATLAB, 212 * 0,4 = 84,8 byte

Utilizzo della casella degli strumenti filtro e dello spazio colore 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')

o non golfato

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 byte

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

Accetta l'input della riga di comando, l'output in un file chiamato "o" nella cartella Appsdata di Love2D. Love2D Non ti consente di salvare file altrove.

Quasi quanto potessi ottenere, probabilmente potrebbe essere ulteriormente golfato.

spiegato

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

Test

Ingresso Produzione

E...

Anche se in realtà non migliora il mio punteggio (peggiora la situazione), ecco la versione con la ruota dei colori implementata.

900 - 270 = 630 byte

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

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.