Help us understand the problem. What is going on with this article?

JuliaImagesの備忘録その3

More than 1 year has passed since last update.

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で簡単な比較実験をしてみます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away