ImageProcessing
julia1.0
JuliaImages

JuliaImagesの備忘録その3


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)

demo.png


線形フィルタリング


畳み込みではなく、相関

(原文だと「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で簡単な比較実験をしてみます。