# Histogram Equalization

So `cdf` is apparently an array, and so is `map`? It’s easy to get confused, because those two are also the names of functions. In particular, I think you should change the name of the `map` array to make things less confusing.

Maybe you can try

``````map[i, j] = cdf[round(UInt8, image[i, j]) + 1]
``````

So here, cdf is not an in-built function. Cdf is actually from my code as follows:

``````cum = cumsum(histogram)
cdf = cum ./ sum(cum)

``````

Oh right! If I change my code like,

``````m, n = size(image)
for i in 1-m
for j in 1-n
out[i,j] = cdf [round(UInt8, image[i,j]) + 1]
end
end

``````

So image[i,j] is Already a type of UInt8. I don’t think it’s okay to use round(UInt8, image[i,j) because it will throw an TypeError.

If `image[i, j]` is already of type `UInt8` then it’s not necessary to round or convert it, but it should work. But why are you trying to change things into `UInt8`, then? Just try

``````map[i, j] = cdf[image[i, j] + 1]
``````

I tried that out. It throws an error, “ArgumentError: invalid index: 1.3019608f0 of type Float32”

So I thought about converting it. So i casted UInt8, multiplying it by 255. Unfortunately, it throws the same error.

But then `image` isn’t a `UInt8` array after all, but an array of `Float32`. Why can’t you use `round` like I suggested, then?

Typeof(image) is Array{Gray{Normed{UInt8,8}}, 2}

This is what I’m confused about.

Type of my histogram and cdf both are, Array{Float64, 1}

So I tried using round.() to my cdf, to convert it into UInt8. The plot is more like step side function, not a cumulative histogram.

So, the thing is, there’s apparently a lot of important information that we don’t have access to. In order to help, we need to know what all the different variables are, and what they mean. Perhaps you can read this Please read: make it easier to help you , before going further? Just the first post, not the whole thread.

well then,

``````julia> using Images, ImageView, Plots, ImageCore

#Histogram function
julia>  function myHis(image)
m, n = size(image)
frequency = zeros(256)

for i in 1:m, j in 1:n
frequency[UInt8(image[i,j] * 255)] += 1
end

return frequency ./ sizeof(image)
end

hist = myHis(image)
cum = cumsum(hist)
cdf = cum ./ sum(cum)

#Histogram Equalization
julia>  m, n = size(image)
for i in 1:m-1
for j in 1:n-1
out[UInt8([i,j] * 255)]= cdf[image[i,j]  + 1]
end
end

Error:
ArgumentError: invalid index: 1.3019608f0 of type Float32

Stacktrace:
 to_index(::Float32) at .\indices.jl:297
 to_index(::Array{Float64,1}, ::Float32) at .\indices.jl:274
 to_indices at .\indices.jl:325 [inlined]
 to_indices at .\indices.jl:322 [inlined]
 getindex(::Array{Float64,1}, ::Float32) at .\abstractarray.jl:980
 top-level scope at .\In:4
``````

Note that this code depends on a file local to your computer (`bookstore_dark.tif`), so it’s not possible for others to run.

That said, the problem still seems to be the one initially conjectured: your `image` object likely contains `Float32` element, so when you do `cdf[image[i, j] + 1]` you are essentially trying to get the 1.3rd element of the `cdf` vector, which is not an operation that makes sense.

This isn’t the only issue in that loop though, as:

``````julia> UInt8([1,1]*255)
ERROR: MethodError: no method matching UInt8(::Array{Int64,1})
Closest candidates are:
UInt8(::Union{Bool, Int32, Int64, UInt32, UInt64, UInt8, Int128, Int16, Int8, UInt128, UInt16}) at boot.jl:709
UInt8(::Float32) at float.jl:686
UInt8(::Float64) at float.jl:686
``````

You can’t create an 8-bit unsigned integer from a vector. You might be trying to convert the vector into `UInt8`s, in which case you want to broadcast `UInt8.([1,1]*255)`. Note however that idexing with `UInt8` doesn’t work:

``````julia> rand(10, 10)[UInt8.([1,1]*255)]
ERROR: BoundsError: attempt to access 10×10 Array{Float64,2} at index [UInt8[0xff, 0xff]]
Stacktrace:
 throw_boundserror(::Array{Float64,2}, ::Tuple{Array{UInt8,1}}) at ./abstractarray.jl:537
 checkbounds at ./abstractarray.jl:502 [inlined]
 _getindex at ./multidimensional.jl:726 [inlined]
 getindex(::Array{Float64,2}, ::Array{UInt8,1}) at ./abstractarray.jl:980
 top-level scope at REPL:1
``````

So given that you’re just using the result of this `UInt8` construction to index your `out` array, it’s probably best to forget about this altogether.

1 Like

Where does `out` come from? It has to be initialized somewhere.

So your problem is indexing. We don’t know what your image is, so that makes it harder, but at least we can point out things that do not make sense:

If `image` is an array of floats, then `UInt8(image[i,j] * 255)` is unlikely to work, since `UInt8` can only convert integer values:

``````julia> UInt8(1.0)  # this works
0x01

julia> UInt8(1.6)  # this doesn't works
ERROR: InexactError: UInt8(1.6)
``````

Therefore I think you should round or truncate instead of convert:

``````julia> round(UInt8, 1.0)  # works
0x01

julia> round(UInt8, 1.6)  # works
0x02

julia> trunc(UInt8, 1.0)  # works
0x01

julia> trunc(UInt8, 1.6)  # works
0x01
``````

In fact, when indexing you should normally just use `Int`s instead of `UInt8`.

So you can try

``````frequency[trunc(UInt8, image[i,j] * 255)] += 1
``````

but, I got the impression that `image[i,j]` isn’t a normal float, but a `RGB` type, and I don’t know how to handle them.

Here you are trying to index into `cdf` using the contents of `image[i,j]`, and since that is not an integer, you cannot use it for indexing.

And then there’s this:

Here you are trying to convert the vector `[i, j]` of `Int`s to `UInt8`. I don’t understand why you are trying to do that, since you can (and should) use ordinary `Int`s for indexing, and also because `UInt8([i,j]*255)` could never work anyway. `[i,j]*255` is a vector, so then you would have to use `UInt8.([i,j]*255)`, but even that wouldn’t work, because `i*255` and `j*255` is bigger than 255 except when `i` and `j` are 0 or 1.

1 Like

I understand now how it works. Thanks!
i just found i missed something in my histogram function.

``````  julia> function myHis(image)
m, n = size(image)
frequency = zeros(256)
for i in 1:m, j in 1:n
frequency[UInt8(image[i,j] * 255)+1 ] += 1
end

return frequency ./ sizeof(image)
end

``````

It’s still not possible for us to know what `image[i,j]` contains, but FWIW note that `1` is a literal that automatically gets parsed as `Int` (`Int64` on most systems):

``````julia> typeof(1)
Int64
``````

I don’t understand why you think you need the `UInt8()` conversion here, but adding `1` to the result of `UInt8()` makes this conversion superfluous anyway due to type promotion that happens when adding a `UInt8` and an `Int`:

``````julia> UInt8(255)
0xff

julia> UInt8(255) + 1
256

julia> typeof(UInt8(255) + 1)
Int64
``````
1 Like

Again, this is not a good idea, since you will get an error unless `image[i,j] * 255` is exactly a whole number. Therefore, you should use `round` or `trunc` instead.

1 Like

I understand now how it works. Thanks!

if you see the code, I’m didn’t add “+1” to

`frequency[UInt8(image[i,j] * 255) ] += 1 `

which doesn’t make sense to get a histogram. so i updated that line to

``````frequency[UInt8(image[i,j] * 255) +1 ] += 1  #this works
``````

The explanation for using `UInt8` and `multiplying by 255` is explained my tutor as follow,

You need to cast like this in order to use the image values for access. The internal representation uses Normed{UInt8, 8}, since this corresponds directly to the underlying data representation (UInt8), but also behaves like a floating point number, so you can do useful calculations on the images without converting between representations.

and the image is `Gray image`

i couldn’t upload the image as it’s in `.tif` file.

But you’re doing it here:

`UInt8(image[i,j] * 255) +1` will not be an `UInt8`, because an `UInt8` plus an `Int64` is an `Int64`, and `1` is an `Int64`, look:

``````julia> typeof(UInt8(5) + 1)
Int64
``````

Since you don’t provide any example image data, it’s difficult to say whether what your tutor is saying is correct, but it looks strange to me. As far as I understand `image[i,j] * 255` will be a `Float32`, and in that case there’s no obvious need to use `UInt8`, but I guess no harm, either. But I am very skeptical of using `UInt8()` instead of `round(UInt8, )` or `trunc(UInt8, )`.

Okay to reduce the confusion I went ahead an installed `TestImages`. Here’s what’s happening (I think):

``````julia> using Images, TestImages

julia> image = testimage("cameraman.tif");

julia> typeof(image)
Array{Gray{Normed{UInt8,8}},2}
``````

So `image` is an array of `Gray{Normed{UInt8,8}}` elements:

``````julia> image[1,1]
Gray{N0f8}(0.612)
``````

If you multiply those by an `Int`, as you do, you end up with:

``````julia> image[1,1]*255
Gray{Float32}(156.0f0)
``````

that doesn’t work for indexing, you need an integer. You converted this back to `UInt8`:

``````julia> UInt8(image[1,1]*255)
0x9c
``````

but then you’re directly adding a `1`, which as we said is just a literal that gets parsed as an `Int64`, and adding that to your `UInt8` just gives you a regular `Int`:

``````julia> UInt8(image[1,1]*255)+1
157
``````

So this is essentially the same as doing:

``````julia> Int(image[1,1]*255)+1
157
``````

Thanks for looking that up.

Question is, is

guaranteed to always be a whole number?

I don’t know enough about how images are represented, but there might be something about the `Gray{N0f8}` that means it holds discrete levels of 255 shades of gray which will always be an integer between 1 and 255 when multiplied by 255?

@DNF I noticed once of your reply saying, where the below code is initialized.

`out[UInt8([i,j] * 255)]`

Dumb me, I didn’t do that. Now I initialize with,

``````out = zeros(Uint8, sizeof(image))
``````

As I already initialized `out` with UInt8, I don’t need to do the conversion again in the code. So now I can change the line to

``````out[i,j] = cdf[image[i,j] + 1]
``````

And, I have attached an `.png` file and the code to show the working of histogram for the line, ` frequency[UInt8(image[i,j] * 255)+1 ] += 1 `

``````julia> function myHis(image)
m, n = size(image)
frequency = zeros(256)
for i in 1:m, j in 1:n
frequency[UInt8(image[i,j] * 255)+1 ] += 1
end

return frequency ./ sizeof(image)
end

`````` between 0 and 255.
0 represents black and 255 represents white