ソーベルエッジ検出器


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%のボーナスが追加されます。

ルール:

  • edgepixelsの値を省略して黒に設定するか、画像の外側のピクセルに0を使用します。
  • 出力イメージは、ほとんどのコンピューターで開くことができるイメージ形式である必要があります。
  • 出力はディスクに書き込むか、ファイルにパイプできる必要があります。
  • 入力は、イメージへの相対パスの形式でコマンドライン引数として与えられるか、コマンドラインからパイプで渡されます。
  • これはコードゴルフなので、バイト単位の最短コードが勝ちです!

ガウスぼかしを正確に指定できますか?入力グレースケールも、そうでない場合、このエッジ検出をカラー画像にどのように適用する必要がありますか?出力画像は入力とまったく同じサイズですが、入力は内側のピクセルでのみ実行されます(ゼロに設定したピクセルではありません)。
flawr

Computerphileのエッジ検出に関するビデオを見ましたか?私はそこで接続の匂いを嗅ぐことができます:)
ジャイアントツリー'11

@flawrどのガウスぼかしがエッジ検出に適しているかをテストする必要があるので、何が良い値なのか本当にわかりません。ガウスぼかしの詳細はこちら。入力画像はカラーです。エッジ検出を実行する場合は、まずグレースケールに変換する必要があります。エッジ検出は、A:出力ピクセルの外側の1px境界線を黒に設定するか、B:すべてのピクセルで設定し、画像の外側のピクセルの値として0を使用します。
vrwim

@GiantTree noooooooビデオはまったく関係ありません:)
vrwim

4
なぜこれが反対票を投じられたのですか?それは完全に有効な質問のようです。
アディソンクランプ

回答:


13

J、166 164 161 154 150 144 143バイト。

ゴルフをしすぎない。私はもっ​​と長い実装をほとんど折りたたんでいました(以下を参照)ので、おそらく改善の余地がたくさんあります。BMPライブラリを使用します。結果をファイルに保存します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

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))
  • Edit1:名前付きのコンスタンを値に置き換えました。
  • Edit2:アップロードされたサンプル

元の フィルター済み


サンプルの入力画像と出力画像を提供してもらえますか?
R.ガプス

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

コマンドライン入力を受け取り、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()

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.