From 8a4bd35a826a97a7833befa8462002e75be4a177 Mon Sep 17 00:00:00 2001 From: Arijit Kar Date: Wed, 7 Mar 2018 17:13:05 +0530 Subject: [PATCH] Added 2D Gabor filter (#57) * Added function to construct 2D Gabor kernel. * Modified gabor function to include imaginary part of the filter. * Added tests for gabor filter function. * Modified gabor function and placed validate_gabor outside of gabor. * Added warnings for cases when size_x or size_y is not positive. * Added test cases for errors/exceptions. --- src/ImageFiltering.jl | 2 +- src/kernel.jl | 64 ++++++++++++++++++++++++++++++++++++++++++- test/gabor.jl | 52 +++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 4 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 test/gabor.jl diff --git a/src/ImageFiltering.jl b/src/ImageFiltering.jl index f9a6b4d..dcec3aa 100644 --- a/src/ImageFiltering.jl +++ b/src/ImageFiltering.jl @@ -53,7 +53,7 @@ ArrayLike{T} = Union{ArrayType{T}, AnyIIR{T}} include("kernel.jl") using .Kernel -using .Kernel: Laplacian, reflect, ando3, ando4, ando5, scharr, bickley, prewitt, sobel +using .Kernel: Laplacian, reflect, ando3, ando4, ando5, scharr, bickley, prewitt, sobel, gabor NDimKernel{N,K} = Union{AbstractArray{K,N},ReshapedOneD{K,N},Laplacian{N}} diff --git a/src/kernel.jl b/src/kernel.jl index f19f574..a461ff7 100644 --- a/src/kernel.jl +++ b/src/kernel.jl @@ -11,6 +11,7 @@ dimensionality. The following kernels are supported: - `DoG` (Difference-of-Gaussian) - `LoG` (Laplacian-of-Gaussian) - `Laplacian` + - `gabor` See also: [`KernelFactors`](@ref). """ @@ -295,7 +296,7 @@ end """ Laplacian((true,true,false,...)) Laplacian(dims, N) - Lacplacian() + Laplacian() Laplacian kernel in `N` dimensions, taking derivatives along the directions marked as `true` in the supplied tuple. Alternatively, one @@ -328,6 +329,67 @@ function Base.convert(::Type{AbstractArray}, L::Laplacian{N}) where N end _reshape(L::Laplacian{N}, ::Type{Val{N}}) where {N} = L + +""" + gabor(size_x,size_y,σ,θ,λ,γ,ψ) -> (k_real,k_complex) + +Returns a 2 Dimensional Complex Gabor kernel contained in a tuple where + + - `size_x`, `size_y` denote the size of the kernel + - `σ` denotes the standard deviation of the Gaussian envelope + - `θ` represents the orientation of the normal to the parallel stripes of a Gabor function + - `λ` represents the wavelength of the sinusoidal factor + - `γ` is the spatial aspect ratio, and specifies the ellipticity of the support of the Gabor function + - `ψ` is the phase offset + +#Citation +N. Petkov and P. Kruizinga, “Computational models of visual neurons specialised in the detection of periodic and aperiodic oriented visual stimuli: bar and grating cells,” Biological Cybernetics, vol. 76, no. 2, pp. 83–96, Feb. 1997. doi.org/10.1007/s004220050323 +""" +function gabor(size_x::Integer, size_y::Integer, σ::Real, θ::Real, λ::Real, γ::Real, ψ::Real) + + σx = σ + σy = σ/γ + nstds = 3 + c = cos(θ) + s = sin(θ) + + validate_gabor(σ,λ,γ) + + if(size_x > 0) + xmax = floor(Int64,size_x/2) + else + warn("The input parameter size_x should be positive. Using size_x = 6 * σx + 1 (Default value)") + xmax = round(Int64,max(abs(nstds*σx*c),abs(nstds*σy*s),1)) + end + + if(size_y > 0) + ymax = floor(Int64,size_y/2) + else + warn("The input parameter size_y should be positive. Using size_y = 6 * σy + 1 (Default value)") + ymax = round(Int64,max(abs(nstds*σx*s),abs(nstds*σy*c),1)) + end + + xmin = -xmax + ymin = -ymax + + x = [j for i in xmin:xmax,j in ymin:ymax] + y = [i for i in xmin:xmax,j in ymin:ymax] + xr = x*c + y*s + yr = -x*s + y*c + + kernel_real = (exp.(-0.5*(((xr.*xr)/σx^2) + ((yr.*yr)/σy^2))).*cos.(2*(π/λ)*xr + ψ)) + kernel_imag = (exp.(-0.5*(((xr.*xr)/σx^2) + ((yr.*yr)/σy^2))).*sin.(2*(π/λ)*xr + ψ)) + + kernel = (kernel_real,kernel_imag) + return kernel +end + +function validate_gabor(σ::Real,λ::Real,γ::Real) + if !(σ>0 && λ>0 && γ>0) + throw(ArgumentError("The parameters σ, λ and γ must be positive numbers.")) + end +end + """ reflect(kernel) --> reflectedkernel diff --git a/test/gabor.jl b/test/gabor.jl new file mode 100644 index 0000000..9863aad --- /dev/null +++ b/test/gabor.jl @@ -0,0 +1,52 @@ +using ImageFiltering, Base.Test + +@testset "gabor" begin + σx = 8 + σy = 12 + size_x = 6*σx+1 + size_y = 6*σy+1 + γ = σx/σy + kernel = Kernel.gabor(0,0,σx,0,5,γ,0) + @test isequal(size(kernel[1]),(size_x,size_y)) + kernel = Kernel.gabor(0,0,σx,π,5,γ,0) + @test isequal(size(kernel[1]),(size_x,size_y)) + + for x in 0:4, y in 0:4, z in 0:4, t in 0:4 + σx = 2*x+1 + σy = 2*y+1 + λ = 2*z+1 + γ = σx/σy + θ = 2*t+1 + kernel1 = Kernel.gabor(9,9,σx,θ,λ,γ,0) + kernel2 = Kernel.gabor(9,9,σx,θ+π,λ,γ,0) + @test abs(sum(kernel1[1] - kernel2[1])) < 1e-2 + @test abs(sum(kernel1[2] - kernel2[2])) < 1e-2 + end + + x = [j for i in 0:49,j in 0:49] + wavelengths = (3, 10) + images = [sin.(2*π*x/λ) for λ in wavelengths] + σx = 4 + σy = 5 + function match_score(image, λ) + gabor_real = imfilter(image,centered(Kernel.gabor(6*σx+1,6*σy+1,σx,0,λ,σx/σy,0)[1]),[border="replicate"]) + gabor_imag = imfilter(image,centered(Kernel.gabor(6*σx+1,6*σy+1,σx,0,λ,σx/σy,0)[2]),[border="replicate"]) + gabor_result = sqrt.((gabor_real.*gabor_real) + (gabor_imag.*gabor_imag)) + return mean(gabor_result) + end + gabor_output = rand(Float64,2,2) + for i = 1:2 + for j = 1:2 + gabor_output[i,j] = match_score(images[i],wavelengths[j]) + end + end + @test gabor_output[1,1] > gabor_output[1,2] + @test gabor_output[2,2] > gabor_output[1,2] + @test gabor_output[1,1] > gabor_output[2,1] + @test gabor_output[2,2] > gabor_output[2,1] + + @test_throws ArgumentError Kernel.gabor(9,9,-2,0,5,0.1,0) + @test_throws ArgumentError Kernel.gabor(9,9,2,0,-5,0.1,0) + @test_throws ArgumentError Kernel.gabor(9,9,2,0,5,0,0) + +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index c8b124d..c43170d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,5 +14,6 @@ include("specialty.jl") include("gradient.jl") include("mapwindow.jl") include("basic.jl") +include("gabor.jl") nothing