ImageFiltering.jl
この記事はImageFilteringのページを参考に試した内容です。Factored kernel(詳細は次回)の部分は勉強になりました。
ImageFiltering(Imagesパッケージの一つ)は配列に対する線形および非線形フィルタリングをサポートします。imfilter
という関数と、共通カーネル(フィルタ)を提供するKernel
モジュールとKernelFactors
モジュールから構成されています。
デモ
using Images, ImageFiltering
using TestImages
img = testimage("cameraman")
imgg = imfilter(img, Kernel.gaussian(3))
imgl = imfilter(img, Kernel.Laplacian())
imgl_ = (imgl .+ 1.0) ./ 2.0
images = hcat(img, imgg, imgl_)
save("demo.png", images)
線形フィルタリング
畳み込みではなく、相関
(原文だと「Correlation, not convolution」なのですが、どう訳すのがいいんでしょう。)
ImageFilteringでは、入力画像A
をカーネルK
でフィルタリングした画像F
は次の式で計算されます。
F[I] = \sum_J A[I+J]K[J]
畳み込みを計算したければ、カーネルに対してreflect
を作用させればよいとのこと。
カーネルのインデックス
カーネルのインデックスにはOffsetArray
が使われています。
julia> kernel = Kernel.gaussian(1)
OffsetArray(::Array{Float64,2}, -2:2, -2:2) with eltype Float64 with indices -2:2×-2:2:
0.00296902 0.0133062 0.0219382 0.0133062 0.00296902
0.0133062 0.0596343 0.0983203 0.0596343 0.0133062
0.0219382 0.0983203 0.162103 0.0983203 0.0219382
0.0133062 0.0596343 0.0983203 0.0596343 0.0133062
0.00296902 0.0133062 0.0219382 0.0133062 0.00296902
上記の場合、インデックスは各軸に対して-2:2
となっており、[0, 0]
がカーネルの中心に来るようになっています。このおかげで、フィルタリングによって画像がシフトしなくなります。
自分で作成したカーネルには、centered
関数を通せばOffsetArray
になります。
julia> centered([1 0 1; 0 1 0; 1 0 1])
OffsetArrays.OffsetArray{Int64,2,Array{Int64,2}} with indices -1:1×-1:1:
1 0 1
0 1 0
1 0 1
Factored kernels
ガウシアンカーネルなどのよく使われるカーネルはseparableという性質を持ちます。separableなカーネルとは、$K[j_1, j_2, ...]$が$K_1[j_1]K_2[j_2]$と書けるカーネルのことです。このとき、相関の式
F[i_1, i_2] = \sum_{j_1, j_2}A[i_1+j_1, i_2+j_2] K[j_1, j_2]
は
F[i_1, i_2] = \sum_{j_2} \left( \sum_{j_1}A[i_1+j_1, i_2+j_2] K_1[j_1]
\right) K_2[j_2]
と書けます。例えば、カーネルサイズがmxn
の場合、元の式だと各点でmn
回の演算が必要なのに対して、変形した式だとm+n
回の演算で済みます。
この性質を利用してseparableなカーネルで効率的にフィルタリングするための補助関数として、Kernelfactor.kernelfactors
が用意されています。
julia> kern1 = centered([1/3, 1/3, 1/3])
OffsetArrays.OffsetArray{Float64,1,Array{Float64,1}} with indices -1:1:
0.333333
0.333333
0.333333
julia> kernf = kernelfactors((kern1, kern1))
(ImageFiltering.KernelFactors.ReshapedOneD{Float64,2,0,OffsetArrays.OffsetArray{Float64,1,Array{Float64,1}}}([0.333333,0.333333,0.333333]),ImageFiltering.KernelFactors.ReshapedOneD{Float64,2,1,OffsetArrays.OffsetArray{Float64,1,Array{Float64,1}}}([0.333333,0.333333,0.333333]))
ここから2次元カーネルに変形するにはbroadcast(*, kernf...)
を用います。
julia> kernp = broadcast(*, kernf...)
OffsetArrays.OffsetArray{Float64,2,Array{Float64,2}} with indices -1:1×-1:1:
0.111111 0.111111 0.111111
0.111111 0.111111 0.111111
0.111111 0.111111 0.111111
julia> imfilter(img, kernf) ≈ imfilter(img, kernp)
true
今回はここまで。
次回は、Factored kernelsで簡単な比較実験をしてみます。