소벨 에지 검출기


12

당신의 임무는 입력 이미지를 가지고 가장자리 감지를 통해 출력 이미지가되도록 프로그램을 작성하는 것입니다.

가장자리 감지는 다음과 같이 작동합니다 (확실하지 않은 경우 sobel 가장자리 감지 참조 ).

  • 픽셀의 값은 픽셀의 총 밝기이므로, 컬러 인 경우 먼저 그레이 스케일로 변환해야합니다 (단순하고 골프를 치기 위해 R, G 및 비).
  • 픽셀 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을 사용할 수 있습니다.
  • 출력 이미지는 대부분의 컴퓨터에서 열 수있는 이미지 형식이어야합니다.
  • 출력은 디스크에 쓰거나 파일로 파이프 할 수 있어야합니다.
  • 입력은 이미지에 대한 상대 경로 형식으로 명령 줄 인수로 제공되거나 명령 줄에서 파이프됩니다.
  • 이것은 코드 골프이므로 바이트 단위의 가장 짧은 코드가 이깁니다!

가우시안 블러를 정확하게 지정할 수 있습니까? 입력 그레이 스케일이 아닌 경우,이 가장자리 감지를 컬러 이미지에 어떻게 적용해야합니까? 출력 이미지의 크기가 입력과 정확히 동일하지만 입력이 내부 픽셀 (0으로 설정 한 것이 아닌)에서만 수행되는 것이 맞습니까?
flawr

Computerphile의 에지 감지에 대한 비디오를 보셨습니까 ? 나는 연결을 냄새 맡을 수 있습니다 :)
GiantTree

@flawr 나는 가우시안 블러가 가장자리 감지에 적합한 지 테스트해야하므로 좋은 값이 무엇인지 실제로 모른다. 가우시안 블러에 대한 자세한 내용은 여기를 참조하십시오 . 입력 이미지는 컬러로되어 있으며 가장자리 감지를 수행하려면 먼저 이미지를 그레이 스케일로 변환해야합니다. 가장자리 감지는 내부 픽셀에서 A :를 수행하고 출력 이미지의 외부 1px 테두리를 검은 색으로 설정하거나 모든 픽셀에서 B :를 설정하고 이미지 외부의 모든 픽셀 값으로 0을 사용합니다.
vrwim

@GiantTree은 비디오가됩니다 nooooooo 완전히 :) 관련이없는
vrwim

4
이것이 왜 표결되지 않았습니까? 완벽하게 유효한 질문 인 것 같습니다.
애디슨 크럼

답변:


13

J, 166164161154150144143 바이트.

너무 많은 골프를하지 않습니다; 나는 대부분 더 긴 구현을 무너 뜨렸으므로 (아래 참조) 아마도 개선의 여지가 많이있을 것입니다. BMP 라이브러리를 사용합니다. 결과를 file에 저장합니다 o. 전체 3x3 셀만 사용하여 가장자리 픽셀을 처리 했으므로 최종 이미지의 너비와 높이가 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

파이썬, 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 : 명명 된 constans를 값으로 대체했습니다.
  • Edit2 : 업로드 된 샘플

기발한 거르는


샘플 입력 및 출력 이미지를 줄 수 있습니까?
R. Kap

@ R.Kap은 결코 늦지 않는 것보다 늦습니다.
Karl Napf

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

또는 ungolfed

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 루아, 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 다른 곳에 파일을 저장할 수 없습니다.

내가 얻을 수있는만큼의 골프는 아마 더 골프를 쳤다.

설명

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

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.