Détecteur de bord Sobel

12

Votre tâche consiste à écrire un programme qui prend une image d'entrée et à l'exécuter via la détection des contours pour devenir une image de sortie.

La détection des contours fonctionne comme suit (si elle n'est pas claire, voir Détection des contours sobel ):

  • La valeur d'un pixel est la luminosité totale d'un pixel, donc s'il est en couleur, vous devrez d'abord le convertir en niveaux de gris (pour garder les choses simples et aptes au golf, vous pouvez prendre la valeur moyenne pour R, G et B).
  • Les formules pour G x et G y pour le pixel p (i, j) sont:
    • 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)
  • La valeur de la taille du bord à ce pixel est alors: √ (G x 2 + G y 2 )

L'image de sortie est pour chaque pixel la taille du bord √ (G x 2 + G y 2 ) en échelle de gris.

Bonus:

  • Effectuez un flou gaussien pour lisser l'image avant que la détection des bords ne se déclenche, pour omettre les bords plus petits. Cela donne un bonus de -30% sur le résultat final.
  • Tenez compte de l'angle du bord. Vous donnez au pixel de sortie une certaine couleur, en prenant la même valeur d'échelle de gris et en ajoutant de la couleur à partir d'une roue chromatique en utilisant l'angle obtenu à partir de la formule arctan (G y / G x ). Cela donne un autre bonus de -30% sur le résultat final.

Règles:

  • Vous pouvez omettre la valeur des pixels de contour et les définir sur noir, ou vous pouvez utiliser 0 pour tout pixel en dehors de l'image.
  • Votre image de sortie doit être dans un format d'image pouvant être ouvert sur la plupart des ordinateurs.
  • La sortie doit être écrite sur le disque ou raccordée à un fichier.
  • L'entrée est donnée sous forme d'argument de ligne de commande, sous la forme d'un chemin d'accès relatif à l'image, ou via la ligne de commande.
  • C'est le golf de code, donc le code le plus court en octets gagne!
vrwim
la source
Pouvez-vous spécifier exactement le flou gaussien? Le niveau de gris d'entrée est-il également, sinon, comment appliquer cette détection de bord aux images colorées? Est-il exact que l'image de sortie a exactement la même taille que l'entrée, mais l'entrée n'est effectuée que sur les pixels internes (pas celui que nous avons mis à zéro)?
flawr
Avez-vous vu les vidéos sur la détection des bords de Computerphile ? Je peux sentir une connexion là-bas :)
GiantTree
@flawr Je dois tester quel flou gaussien est bon pour la détection des contours, donc je ne sais pas vraiment ce qu'est une bonne valeur. plus sur le flou gaussien ici . L'image d'entrée est en couleur et vous devrez d'abord la convertir en niveaux de gris si vous souhaitez effectuer la détection des contours. La détection des bords est effectuée soit A: sur les pixels intérieurs, et vous définissez la bordure extérieure 1px de l'image de sortie sur noir, ou B: sur tous les pixels, et vous prenez 0 comme valeur pour tous les pixels en dehors de l'image.
vrwim
@GiantTree nooooooo la vidéo n'est absolument pas liée :)
vrwim
4
Pourquoi ce vote a-t-il été rejeté? Cela semble être une question parfaitement valable.
Addison Crump,

Réponses:

13

J, 166 164 161 154 154 150 144 143 octets.

Pas trop joué au golf; J'ai principalement réduit mon implémentation plus longue (voir ci-dessous), donc il y a probablement beaucoup de place pour l'amélioration. Utilise la bibliothèque BMP. Enregistre le résultat dans le fichier o. J'ai manipulé les pixels de bord en utilisant uniquement des cellules 3x3 complètes, de sorte que l'image finale a une largeur et une hauteur plus petites de 2 pixels.

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

Usage:

echo 'image.bmp' | jconsole golf.ijs

Étendu:

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

Exemple d'entrée et de sortie:

Original Détection des contours

Adrian17
la source
Ceci est un bel exemple de l' ;._3opérateur de sous - tableau. J'ai remarqué que vous avez défini un verbe pde rang 2 pour opérer sur les sous-réseaux après les avoir créés. Vous pouvez plutôt opérer sur chaque sous-réseau lorsque vous coupez. Mon essai de l'implémenter en fonction de votre travail est 256#.3#"0<.255<.3 3((|:S)&*+&.*:&(+/)&,S&*);._3%&3(3#256)+/@#:. Cela devrait le ramener à 126 octets au total.
miles
Je l'ai réduit à 119 octets en 'o'writebmp~256#.3#"0<.255<.3 3(*+&.*:&(+/)&,(*|:))&((-,.0,.])1 2 1);._3%&3(3#256)+/@#:readbmp]stdin''supposant que seul le nom de fichier est entré sur stdin. Vous pouvez effectuer cette opération à l'aide de echo -nsorte qu'une nouvelle ligne supplémentaire ne soit pas incluse dans stdin. Sur mon ordinateur, le script se ferme automatiquement lors de l'utilisation d'une entrée redirigée vers un script, ce qui signifie que je n'ai pas à inclure le exit''et peut enregistrer 6 octets supplémentaires, mais je ne suis pas sûr que ce soit vrai pour tous.
miles
1

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

Avec le bonus Flou gaussien.

Comme vous n'avez pas explicitement interdit les méthodes intégrées, voici 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))

Sans bonus, 136 octets

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: Remplacé les constans nommés par leurs valeurs.
  • Edit2: échantillons téléchargés

original filtré

Karl Napf
la source
Pourriez-vous éventuellement donner un exemple d'image d'entrée et de sortie?
R. Kap
@ R.Kap mieux vaut tard que jamais.
Karl Napf
0

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

Utilisation de la boîte à outils du filtre et de l'espace colorimétrique 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')

ou non golfé

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')
Jonas
la source
0

Love2D Lua, 466 octets

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

Prend l'entrée de ligne de commande, les sorties dans un fichier appelé "o" sous votre dossier appsdata Love2D. Love2D ne vous permettra pas d'enregistrer des fichiers ailleurs.

À peu près aussi golfé que possible, je pourrais probablement le faire plus loin.

Expliqué

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

Tester

Contribution Production

Et...

Bien que cela n'améliore pas réellement mon score (le rend pire en fait), voici la version avec la roue chromatique implémentée.

900 - 270 = 630 octets

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

entrez la description de l'image ici

ATaco
la source