Sobel边缘检测器


12

您的任务是编写一个获取输入图像的程序,并通过边缘检测将其运行以成为输出图像。

边缘检测的工作原理如下(如果不清楚,请参阅sobel边缘检测):

  • 像素的值是像素的总亮度,因此,如果它是彩色的,则需要先将其转换为灰度(为使操作简单且易于打高尔夫球,可以取R,G和B)。
  • 像素p (i,j)的 G x和G y的公式为:
    • 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%的奖金。
  • 考虑到边缘的角度。通过使用相同的灰度值并使用从公式arctan(G y / G x)获得的角度从色轮添加颜色,可以为输出像素提供某种颜色。这将为最终结果带来-30%的额外奖金。

规则:

  • 您可以省略边缘像素的值,并将其设置为黑色,或者可以将0用于图像外部的任何像素。
  • 输出图像必须采用可在大多数计算机上打开的图像格式。
  • 输出必须写入磁盘或可通过管道传输到文件。
  • 输入是作为命令行参数提供的,形式为图像的相对路径,或者从命令行通过管道输入。
  • 这是代码高尔夫,所以最短的代码以字节为单位!

您能准确指定高斯模糊吗?输入灰度是否也是如此?如果没有,我们应该如何将此边缘检测应用于彩色图像?输出图像的大小与输入的大小完全相同,但是输入仅在内部像素上执行(不是我们设置为零的像素)是否正确?
瑕疵的

您是否看过Computerphile有关边缘检测的视频?我能闻到那里的连接

@flawr我必须测试哪种高斯模糊对边缘检测有好处,所以我真的不知道什么是好的值。更多关于高斯模糊的信息。输入图像为彩色,如果要执行边缘检测,则需要先将其转换为灰度。边缘检测要么在内部像素上执行A :,然后将输出图像的外部1px边框设置为黑色,要么在所有像素上执行B :,并且将0用作图像外部任何像素的值。
vrwim 2015年

@GiantTree nooooooo视频是完全不相关的:)
vrwim

4
为什么这被否决?这似乎是一个完全正确的问题。
Addison Crump 2015年

Answers:


13

Ĵ,166个164 161 154 150 144 143字节。

不要打太多球;我大部分时间都不再使用更长的实现(请参见下文),因此可能还有很多改进的余地。使用BMP库。将结果保存在file中o。我仅使用完整的3x3单元来处理edgepixels,因此最终图像的宽度和高度减小了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子数组运算符的一个很好的例子。我注意到您p在创建子数组后定义了第2级动词以对其进行操作。您可以在切割时对每个子阵列进行操作。我根据您的工作来实施它的尝试是256#.3#"0<.255<.3 3((|:S)&*+&.*:&(+/)&,S&*);._3%&3(3#256)+/@#:。那应该减少到总共126个字节。
英里

'o'writebmp~256#.3#"0<.255<.3 3(*+&.*:&(+/)&,(*|:))&((-,.0,.])1 2 1);._3%&3(3#256)+/@#:readbmp]stdin''假设仅在stdin上输入文件名,我将其减少到119个字节。您可以使用来执行此操作,echo -n以便在stdin中不包含多余的换行符。在我的计算机上,当使用管道输入到脚本时,脚本会自动退出,这意味着我不必包含,exit''并且可以节省额外的6个字节,但是我不确定这是否对所有情况都是正确的。
英里

1

Python,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))
  • 编辑1:用其值替换命名的常量。
  • 编辑2:上传的样本

原版的 已过滤


您能否提供示例输入和输出图像?
R. Kap

@ R.Kap迟到总比没有好。
Karl Napf '16

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

接受命令行输入,输出到Love2D appsdata文件夹下的一个名为“ o”的文件。Love2D Wont可以让您将文件保存在其他任何地方。

大概我会打高尔夫球,可能还会打高尔夫球。

解释

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

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.