# Array Comprehension Problem

So completely new to coding and I’m currently going through the MIT courseware intro to computational thinking in Julia and I’m pretty confused how this piece of code works:

Write the third method `noisify(image::AbstractMatrix, s)` to noisify each pixel of an image. This function should be a single line!

function noisify(image::AbstractMatrix, s)
return [noisify(x, s) for x in image]
end

in which a slider is used to adjust noise and the final function, where philip_head is the image being used.

@bind philip_noise Slider(0:0.01:1, show_value=true)

So to be honest I used chatGPT to write the code for me because I was at a loss for what to write. I’m having trouble understanding how [noisify(x ,s) for x in image] works. I added noise to the other function, which called for a random integer between -s and s to be added:

function noisify(x::Number, s)
s = rand()*rand((1,-1))
noise = x+s
return noise
end

which makes sense to me, kinda. But [noisify(x,s) for x in image] is making absolutely no sense to me right now lmao. Is it adding s (Adding 0-1 per the scale to the RGB values respectively) to every value of the matrix?

I doubt using ChatGPT will ever help you learn. In this instance it has written a nonsense function, and the fact that you don’t know how to identify that it’s nonsense is exactly why you shouldn’t use it.

About array comprehension, the natural language version of `[f(x) for x in y]` is “for every element `x` in `y`, put `f(x)` in an array”. Hope that helps.

1 Like

The array comprehension is just a `for` loop. For each pixel `x` in the image, we will run some function.

``````julia> const corgi_url = "https://user-images.githubusercontent.com/6933510/107239146-dcc3fd00-6a28-11eb-8c7b-41aaf6618935.png"
"https://user-images.githubusercontent.com/6933510/107239146-dcc3fd00-6a28-11eb-8c7b-41aaf6618935.png"

julia> [println(x) for x in corgi];
RGB{N0f8}(0.157,0.141,0.106)
RGB{N0f8}(0.157,0.141,0.106)
RGB{N0f8}(0.157,0.141,0.106)
RGB{N0f8}(0.157,0.141,0.106)
RGB{N0f8}(0.145,0.129,0.094)
RGB{N0f8}(0.145,0.129,0.094)
RGB{N0f8}(0.145,0.129,0.094)
...
``````

The above array comprehension is similar to the following loop.

``````values = []
for x in corgi
val = println(x)
push!(values, val)
end
return values
``````

The function you (or ChatGPT) proposed has some issues.

``````function noisify(x::Number, s)
s = rand()*rand((1,-1))
noise = x+s
return noise
end
``````

The above function ignores the argument `s`. It also does not generate a random value between `-s` and `s`.

In Julia, you can generate a random integer between `s` and `-s` as follows:

``````julia> let s = 5
rand(-s:s)
end
-3

julia> let s = 5
rand(-s:s)
end
-4

julia> let s = 5
rand(-s:s)
end
-1

julia> let s = 5
rand(-s:s)
end
-4

julia> let s = 5
rand(-s:s)
end
5
``````

I’m not sure what `image` is your in space, but it appears to be a simple matrix of numbers rather than a matrix of RGB values. A matrix of numbers can be interpreted as a gray scale image going from 0 (black) to 1 (white).

The slider above suggests that `s` is a a floating point value (Float64) between 0 and 1. Thus it seems we do not want an integer between -s and s but a floating point value in that range.

`rand()` by itself will return a pseudo-random floating point value between `0` and `1`. Let’s manipulate this to be a value between `-1` and `1`. First, let’s multiply this `rand()` by `2`. Now we have a psuedo-random number between `0` and `2`.

``````julia> [rand()*2 for x in 1:5]
5-element Vector{Float64}:
0.29105644396254204
0.4805402794429039
0.7185490585199521
1.0051175366207994
1.8548283774237921
``````

Second, let’s subtract one so that the range of the psuedo-random number is between `-1` and `1`.

``````julia> [rand()*2-1 for x in 1:5]
5-element Vector{Float64}:
-0.7154356748800863
0.5931495061220962
-0.5112898446445013
0.5877524288483504
-0.39967985881552304
``````

Third, let’s scale this so that the random values are between `-s` and `s`. To do so, we just multiply the above result by `s`:

``````julia> let s = 0.5
[s*(rand()*2-1) for x in 1:5]
end
5-element Vector{Float64}:
0.010240801151369938
0.3113220164047772
0.42303223201915907
-0.22238035973003045
-0.322278836649768
``````

The homework assignment suggests `clamp`.

``````function noisify(x::Number, s)
noise = s*(rand()*2-1)
return clamp(x + noise, 0, 1)
end
``````

We can apply that function direct to values to generate “nosified” values.

``````julia> [noisify(x,0.1) for x in 0:0.1:1]
11-element Vector{Float64}:
0.04185649966271443
0.059601413880822074
0.17569873187297833
0.23385349040162423
0.37913057544096834
0.5253420496055848
0.5290349279141047
0.6933767312224085
0.8389770893749297
0.9442019869910827
0.9293120465194884
``````

To `noisify` the image, however, you need to write a version of noisify for RGB values. I’m not going to describe exactly how to do this part since it’s the homework assignment, but I’m to describe how to work with RGB values.

Below I extract the first pixel of the image and find it’s red, green, and blue values.

``````julia> px = image # get the first pixel
RGB{N0f8}(0.157,0.141,0.106)

julia> px.r
0.157N0f8

julia> px.g
0.141N0f8

julia> px.b
0.106N0f8
``````

Notice that I can apply the `noisify` function above.

``````julia> noisify(px.r, 0.1)
0.10024330653215385

julia> noisify(px.g, 0.1)
0.2120751894938211

julia> noisify(px.b, 0.1)
0.0671814168720399
``````

I can then create a new RGB value in the following way.

``````julia> noisified_px = RGB{N0f8}(
noisify(px.r, 0.1),
noisify(px.g, 0.1),
noisify(px.b, 0.1)
)
RGB{N0f8}(0.188,0.149,0.122)
``````

You’ll notice that you cannot apply `noisify` direct to the pixel:

``````julia> noisify(px, 0.1)
ERROR: MethodError: no method matching noisify(::RGB{N0f8}, ::Float64)

Closest candidates are:
noisify(::Number, ::Any)
@ Main REPL:1
``````

To do so, you need to define `noisify` as follows:

``````function noisify(px::RGB, s)
# create a noisified RGB value
# return the value
end
``````

When complete, then you can do the following:

``````julia> noisify(px, 0.1)
RGB{N0f8}(0.255,0.09,0.078)

julia> noisify(px, 0.1)
RGB{N0f8}(0.063,0.11,0.161)
``````

Finally, then you can use the array comprehension.

``````julia> [noisify(x,0.1) for x in image]
864×700 Array{RGB{N0f8},2} with eltype RGB{N0f8}:
RGB{N0f8}(0.239,0.055,0.137)  …  RGB{N0f8}(0.78,0.655,0.706)
RGB{N0f8}(0.078,0.075,0.188)     RGB{N0f8}(0.604,0.729,0.765)
...
``````
2 Likes

Much love !!!
Thanks for not solving the homework but giving me the knowledge instead

1 Like