Gabor Kernel

Hi,
I’m trying to create and visualize a Gabor kernel.
First I used Kernel.gabor from ImageFiltering.jl and it worked, I think (I still have to learn how to use it to convolve with an image, but okay).

gabor = Kernel.gabor(10, 10, 2, 22.5, 2, 1, 0)
colorview(RGB, zeroarray, gabor[1], zeroarray)

But since I’ll be working with image processing, I need/want to get a better grasp of it.
So I’m following this guide for python (http://vision.psych.umn.edu/users/kersten/kersten-lab/courses/Psy5036W2017/Lectures/17_PythonForVision/Demos/html/2b.Gabor.html), and I made this far:

function gen_gabor(siz, ω, θ)
    # siz(e) = width x height; A amplitude; ω frequency = 2πf; ρ phase
    radius = [floor(Int, siz[1]/2.), floor(Int, siz[2]/2.)]
    iter = CartesianIndices((-radius[1]:radius[1]+1, -radius[2]:radius[2]+1))
    x1 = [Tuple(iter[i])[1] for i = 1:length(iter)]
    y1 = [Tuple(iter[i])[2] for i = 1:length(iter)]
    x = x1.*cos(θ) + y1.*sin(θ)
    y = -x1.*sin(θ) + y1.*cos(θ)

    gauss = (((ω^2)/(4pi^3))*exp((-ω^2)/(8pi^2))).*(4(x.^2 + y.^2))
    sinusoid = cos.(ω.*x).*exp((pi^2)/2)
    gabor = gauss.*sinusoid

    ga = reshape(gauss, size(iter))
    si = reshape(sinusoid, size(iter))
    gb = reshape(gabor, size(iter))
    return ga, si, gb
end

siz = (256, 256)
ω = .3
θ = pi/4
(gauss, sinusoid, gabor) = gen_gabor(siz, ω, θ)

Gray.(gabor)

gabor

My question then is, why it doesn’t work?
If you have another approach to it, please I’d love to know.
Thanks,

The parentheses in your gaussian were misplaced. The x y terms are in the exponential, which is not what you wrote. In figuring this out, I also went ahead and wrote this in a more “julian” style, instead of the more pythonic (vectorized) approach.

function gen_gabor(sz, ω, θ)
    r = (sz .÷ 2)
    ga = zeros(sz)
    si = zeros(sz)
    gb = zeros(sz)

    f(x, y) = ω^2 / (4pi^3) * exp(-ω^2/(8pi^2) * (4*x^2 + y^2))
 
    for I in CartesianIndices(sz)
        x̃, ỹ = Tuple(I) .- r
        s, c = sincos(θ)
        x, y = [c s; -s c] * [x̃, ỹ]

        ga[I] = f(x, y)
        si[I] = cos(ω*x) * exp((pi^2)/2)
        gb[I] = ga[I]*si[I]
    end
    return ga, si, gb
end

Note that if not for the purposes of the exercise, ga and si would not be necessary outputs of the function, and in that case should not be allocated at all.

1 Like

Hi, I love what you did there. Made everything look easier and cleaner. Thanks a lot.

For visualization, how’d you do it?

I guess I should scale those values, I have to learn how; Gray.(gabor) isn’t doing what I hoped.

Thanks again, I learnt a lot!

I used ImageView to visualize, using Gray.(gb) just like you did.

using ImageView

imshow(Gray.(gen_gabor((256,256), 0.5, π/5)[3]))

If you’re seeing a very dark image using a different method, that’s because the values in the gb matrix are poorly normalized (some are negative, and even the positive ones are quite small). The ImageView viewer is doing some normalization/contrast adjustment automatically to show a better image (the same thing that the python demo was doing, by the way, as their matrix is identical to ours). I can get the following result (very similar to ImageView’s output) by rescaling all the values in the matrix to be between 0 and 0.8:

gbimage

using Plots
# rescale formula [a, b] ↦ [y, z]
rescale(x, a, b, y, z) = (x - a) * (z - y)/ (b - a) + y

gb = gen_gabor((256,256), 0.3, π/4)[3]
plot(Gray.(rescale.(gb, minimum(gb), maximum(gb), 0.0, 0.8))

Thank you so much!

To be honest I still couldn’t see any of the outputs. But it must be something broken on my end. imshow outputs a white image, and plot a grey background only.

I’ll accept your answers as solutions, obviously. =]

To be honest I still couldn’t see any of the outputs. But it must be something broken on my end. imshow outputs a white image, and plot a grey background only.

Interesting that you can’t see it any which way… Did you check the values in the matrix prior to calling Gray to see if they’re reasonable? If you rescale them, do they successfully rescale so their minimum and maximum are 0 and 0.8 respectively?

You can add the TestImages package to see if ImageView can view things other than that plot (that you know should work). Heck, you can even try to visualize rand() and try to debug it that way…

Ok, I think ImageView is working; I could view something there.

Now for the matrices, something is wrong indeed. The original gabor has really big numbers, negative and positive, right?
Then Gray keeps them without scaling. Is that expected? I thought it would rescale them to the interval [0, 1].
And when I use rescale it does compress the values between .0 and .8 but the mean value is almost .8 (.799 something).

I must be doing something really stupid… sorry for that.

1 Like

I’m not quite seeing the same statistics, so maybe there is yet another difference between our gen_gabor functions? Here is mine again (this time with extraneous stuff removed). Do you mind trying again with this one? If it works you can compare source code to find the discrepancy.

function gen_gabor(sz, ω, θ)
    r = (sz .÷ 2)
    gb = zeros(sz)

    f(x, y) = ω^2 / (4pi^3) * exp(-ω^2/(8pi^2) * (4*x^2 + y^2))

    for I in CartesianIndices(sz)
        x̃, ỹ = Tuple(I) .- r
        s, c = sincos(θ)
        x, y = [c s; -s c] * [x̃, ỹ]

        gb[I] = f(x, y) * cos(ω*x) * exp((pi^2)/2)
    end
    return gb
end

The statistics I see are:

julia> gb = gen_gabor((256,256), 0.3, π/4);

julia> minimum(gb), maximum(gb), mean(gb)
(-0.06360674975739578, 0.1008997906173088, 1.525878906249998e-5)

There are values in gb like -9.84825e-16, which may be throwing you off, but note that the exponent there is negative, so they are small numbers.

After rescaling, the relationships should remain the same:

julia> gbrsc = rescale.(gb, extrema(gb)..., 0.0, 0.8);

julia> minimum(gbrsc), maximum(gbrsc), mean(gbrsc)
(0.0, 0.8, 0.3093956430013947)

Yes. Gray converts each number independently (it is broadcast with .) so it has no sense of the overall image. I think also it would generally be undesirable for it to try to rescale or normalize an input matrix into a “pleasing” image. That sort of processing should happen elsewhere (in our case with rescale).

1 Like

Aff, I have it now!! Feels so good =]

You’re the best! Thank you!

1 Like