Submission by: Jazzy Doe (jazz@mit.edu)
Homework 1 - convolutions
18.S191
, Spring 2021
This notebook contains built-in, live answer checks! In some exercises you will see a coloured box, which runs a test case on your code, and provides feedback based on the result. Simply edit the code, run it, and the check runs again.
For MIT students: there will also be some additional (secret) test cases that will be run as part of the grading process, and we will look at your notebook and write comments.
Feel free to ask questions!
"Jazzy Doe"
"jazz"
Let's create a package environment:
Activating new project at `/tmp/jl_bDiBzx` Updating registry at `~/.julia/registries/General.toml` Resolving package versions... Updating `/tmp/jl_bDiBzx/Project.toml` [ac1192a8] + HypertextLiteral v0.5.2 [6218d12a] + ImageMagick v1.3.0 [916415d5] + Images v0.23.3 [7f904dfe] + PlutoUI v0.7.9 Updating `/tmp/jl_bDiBzx/Manifest.toml` [621f4979] + AbstractFFTs v1.5.0 [79e6a3ab] + Adapt v4.3.0 [13072b0f] + AxisAlgorithms v1.0.1 [39de3d68] + AxisArrays v0.4.7 [aafaddc9] + CatIndices v0.2.2 [d360d2e6] + ChainRulesCore v1.25.1 [9e997f8a] + ChangesOfVariables v0.1.10 [3da002f7] + ColorTypes v0.10.12 [c3611d14] + ColorVectorSpace v0.8.7 [5ae59095] + Colors v0.12.11 [34da2185] + Compat v4.16.0 [ed09eef8] + ComputationalResources v0.3.2 [150eb455] + CoordinateTransformations v0.6.3 [dc8bdbbb] + CustomUnitRanges v1.0.2 [9a962f9c] + DataAPI v1.16.0 [864edb3b] + DataStructures v0.18.22 [b4f34e82] + Distances v0.10.12 [ffbed154] + DocStringExtensions v0.9.5 [4f61f5a4] + FFTViews v0.3.2 [7a1cc6ca] + FFTW v1.9.0 [5789e2e9] + FileIO v1.17.0 [53c48c17] + FixedPointNumbers v0.8.5 [a2bd30eb] + Graphics v1.1.3 [ac1192a8] + HypertextLiteral v0.5.2 [bbac6d45] + IdentityRanges v0.3.1 [2803e5a7] + ImageAxes v0.6.9 [f332f351] + ImageContrastAdjustment v0.3.7 [a09fc81d] + ImageCore v0.8.22 [51556ac3] + ImageDistances v0.2.13 [6a3955dd] + ImageFiltering v0.6.21 [6218d12a] + ImageMagick v1.3.0 [bc367c6b] + ImageMetadata v0.9.5 [787d08f9] + ImageMorphology v0.2.11 [2996bd0c] + ImageQualityIndexes v0.2.2 [4e3cecfd] + ImageShow v0.2.3 [02fcd773] + ImageTransformations v0.8.13 [916415d5] + Images v0.23.3 [9b13fd28] + IndirectArrays v0.5.1 [a98d9a8b] + Interpolations v0.13.6 [8197267c] + IntervalSets v0.7.11 [3587e190] + InverseFunctions v0.1.17 [92d709cd] + IrrationalConstants v0.1.1 [c8e1da08] + IterTools v1.4.0 [692b3bcd] + JLLWrappers v1.7.0 [682c06a0] + JSON v0.21.4 [2ab3a3ac] + LogExpFunctions v0.3.28 [1914dd2f] + MacroTools v0.5.16 [dbb5928d] + MappedArrays v0.4.2 [e1d29d7a] + Missings v1.2.0 [e94cdb99] + MosaicViews v0.3.4 [77ba4419] + NaNMath v1.0.3 [6fe1bfb0] + OffsetArrays v1.17.0 [bac558e1] + OrderedCollections v1.8.1 [5432bcbf] + PaddedViews v0.5.12 [d96e819e] + Parameters v0.12.3 [69de0a69] + Parsers v2.8.3 [7f904dfe] + PlutoUI v0.7.9 [aea7be01] + PrecompileTools v1.2.1 [21216c6a] + Preferences v1.4.3 [94ee1d12] + Quaternions v0.7.6 [b3c3ace0] + RangeArrays v0.3.2 [c84ed2f1] + Ratios v0.4.5 [c1ae055f] + RealDot v0.1.0 [3cdcf5f2] + RecipesBase v1.3.4 [189a3867] + Reexport v1.2.2 [ae029012] + Requires v1.3.1 [6038ab10] + Rotations v1.7.1 [699a6c99] + SimpleTraits v0.9.4 [a2af1166] + SortingAlgorithms v1.2.1 [276daf66] + SpecialFunctions v1.8.8 [cae243ae] + StackViews v0.1.2 [90137ffa] + StaticArrays v1.9.13 [1e83bf80] + StaticArraysCore v1.4.3 [82ae8749] + StatsAPI v1.7.1 [2913bbd2] + StatsBase v0.33.21 [fd094767] + Suppressor v0.2.8 [06e1c1a7] + TiledIteration v0.3.1 [3a884ed6] + UnPack v1.0.2 [efce3f68] + WoodburyMatrices v0.5.6 [f5851436] + FFTW_jll v3.3.11+0 [c73af94c] + ImageMagick_jll v6.9.10-12+3 [1d5cc7b8] + IntelOpenMP_jll v2025.0.4+0 [aacddb02] + JpegTurbo_jll v3.1.1+0 [88015f11] + LERC_jll v3.0.0+1 [89763e89] + Libtiff_jll v4.4.0+0 [856f044c] + MKL_jll v2025.0.1+1 [efe28fd5] + OpenSpecFun_jll v0.5.6+0 [3161d3a3] + Zstd_jll v1.5.7+1 [b53b4c65] + libpng_jll v1.6.49+0 [1317d2d5] + oneTBB_jll v2022.0.0+0 [0dad84c5] + ArgTools [56f22d72] + Artifacts [2a0f44e3] + Base64 [ade2ca70] + Dates [8ba89e20] + Distributed [f43a241f] + Downloads [7b1f6079] + FileWatching [b77e0a4c] + InteractiveUtils [4af54fe1] + LazyArtifacts [b27032c2] + LibCURL [76f85450] + LibGit2 [8f399da3] + Libdl [37e2e46d] + LinearAlgebra [56ddb016] + Logging [d6f4376e] + Markdown [a63ad114] + Mmap [ca575930] + NetworkOptions [44cfe95a] + Pkg [de0858da] + Printf [3fa0cd96] + REPL [9a3f8284] + Random [ea8e919c] + SHA [9e88b42a] + Serialization [1a1011a3] + SharedArrays [6462fe0b] + Sockets [2f01184e] + SparseArrays [10745b16] + Statistics [fa267f1f] + TOML [a4e569a6] + Tar [8dfed614] + Test [cf7118a7] + UUIDs [4ec0a83e] + Unicode [e66e0078] + CompilerSupportLibraries_jll [deac9b47] + LibCURL_jll [29816b5a] + LibSSH2_jll [c8ffd9c3] + MbedTLS_jll [14a3606d] + MozillaCACerts_jll [4536629a] + OpenBLAS_jll [05823500] + OpenLibm_jll [83775a58] + Zlib_jll [8e850b90] + libblastrampoline_jll [8e850ede] + nghttp2_jll [3f19e933] + p7zip_jll
Exercise 1 - Manipulating vectors (1D images)
A Vector
is a 1D array. We can think of that as a 1D image.
0.5
0.4
0.3
0.2
0.1
0.0
0.7
0.0
0.7
0.9
Exerise 1.1
👉 Make a random vector random_vect
of length 10 using the rand
function.
0.119185
0.135122
0.699314
0.348632
0.0965132
0.472572
0.755472
0.290664
0.737792
0.892839
Got it!
Well done! You can run the cell above again to generate a new vector!
Hint
You can find out more about any function (like rand
) by clicking on the Live Docs in the bottom right of this Pluto window, and typing it in the top.
We recommend that you leave the window open while you work on Julia code. It will continually look up documentation for anything you type!
Help, I don't see the Live Docs!
Try the following:
🙋 Are you viewing a static preview? The Live Docs only work if you run the notebook. If you are reading this on our course website, then click the button in the top right to run the notebook.
🙋 Is your screen to small? Try resizing your window or zooming out.
TODO
We should include more pointers to basic Julia tutorials👉 Make a function mean
using a for
loop, which computes the mean/average of a vector of numbers.
mean (generic function with 1 method)
missing
Here we go!
Replace missing
with your answer.
👉 Define m
to be the mean of random_vect
.
missing
Here we go!
Replace missing
with your answer.
👉 Write a function demean
, which takes a vector x
and subtracts the mean from each value in x
.
demean (generic function with 1 method)
Let's check that the mean of the demean(random_vect)
is 0:
Due to floating-point round-off error it may not be exactly 0.
missing
TODO
In the demean
exercise, we should talk about mutation. Can we have them discover the problem with mutation instead of saying it?
Idea
We could have an exercise to write two visual tranformations: flip180
and invert
. If you show those, applied to the same image, next to eachother, the second image will be flipped and inverted.
Exercise 1.2
👉 Generate a vector of 100 elements. Where:
the center 20 elements are set to
1
, andall other elements are
0
.
create_bar (generic function with 1 method)
Here we go!
Replace missing
with your answer.
TODO
Last year these next exercises proved too difficult, but we should still do something to introduce 2D arrays
Exercise 1.3
👉 Write a function that turns a Vector
of Vector
s into a Matrix
.
vecvec_to_matrix (generic function with 1 method)
missing
Here we go!
Replace missing
with your answer.
👉 Write a function that turns a Matrix
into aVector
of Vector
s .
matrix_to_vecvec (generic function with 1 method)
missing
Here we go!
Replace missing
with your answer.
colored_line (generic function with 2 methods)
Exercise 2 - Manipulating images
In this exercise we will get familiar with matrices (2D arrays) in Julia, by manipulating images. Recall that in Julia images are matrices of RGB
color objects.
Let's load a picture of Philip again.
"https://user-images.githubusercontent.com/6933510/107239146-dcc3fd00-6a28-11eb-8c7b-41aaf6618935.png"
"/tmp/jl_44gbyL"
Hi there Philip
TODO
Let's first have them play around with this imageTODO
I don't like that we are returning a tuple here instead of a colorExercise 2.1
👉 Write a function mean_color
that accepts an object called image
. It should calculate the mean (average) amounts of red, green and blue in the image and return a tuple (r, g, b)
of those means.
mean_color (generic function with 1 method)
missing
Here we go!
Replace missing
with your answer.
Exercise 2.2
👉 Look up the documentation on the floor
function. Use it to write a function quantize(x::Number)
that takes in a value
quantize (generic function with 1 method)
missing
missing
Here we go!
Replace missing
with your answer.
Exercise 2.3
👉 Write the second method of the function quantize
, i.e. a new version of the function with the same name. This method will accept a color object called color
, of the type AbstractRGB
.
Here, ::AbstractRGB
is a type annotation. This ensures that this version of the function will be chosen when passing in an object whose type is a subtype of the AbstractRGB
abstract type. For example, both the RGB
and RGBX
types satisfy this.
The method you write should return a new RGB
object, in which each component (
quantize (generic function with 2 methods)
Exercise 2.4
👉 Write a method quantize(image::AbstractMatrix)
that quantizes an image by quantizing each pixel in the image. (You may assume that the matrix is a matrix of color objects.)
quantize (generic function with 3 methods)
Let's apply your method!
missing
Exercise 2.5
👉 Write a function invert
that inverts a color, i.e. sends
invert (generic function with 1 method)
Let's invert some colors:
missing
missing
Can you invert the picture of Philip?
missing
Exercise 2.6
👉 Write a function noisify(x::Number, s)
to add randomness of intensity clamp
function, but you should write your own function myclamp(x)
.)
noisify (generic function with 1 method)
Hint
The rand
function generates (uniform) random floating-point numbers between
👉 Write the second method noisify(c::AbstractRGB, s)
to add random noise of intensity
noisify (generic function with 2 methods)
MethodError: no method matching noisify(::typeof(ColorTypes.red), ::Float64)
Closest candidates are:
noisify(::Number, ::Any) at ~/work/disorganised-mess/disorganised-mess/homework1.jl#==#f38b198d-39cf-456f-a841-1ba08f206010:1
noisify(::ColorTypes.AbstractRGB, ::Any) at ~/work/disorganised-mess/disorganised-mess/homework1.jl#==#db4bad9f-df1c-4640-bb34-dd2fe9bdce18:1
noisify(::AbstractMatrix, ::Any) at ~/work/disorganised-mess/disorganised-mess/homework1.jl#==#21a5885d-00ab-428b-96c3-c28c98c4ca6d:1
Here is what happened, the most recent locations are first:
👉 Write the third method noisify(image::AbstractMatrix, s)
to noisify each pixel of an image.
noisify (generic function with 3 methods)
missing
👉 For which noise intensity does it become unrecognisable?
You may need noise intensities larger than 1. Why?
The image is unrecognisable with intensity ...
Exercise 3 - Convolutions
As we have seen in the videos, we can produce cool effects using the mathematical technique of convolutions. We input one image
Conceptually we think of Matrix
of color objects, and we may need to take that into account. Ideally, however, we should write a generic function that will work for any type of data contained in the matrix.
A convolution works on a small window of an image, i.e. a region centered around a given point
The result of the convolution over a given window, centred at the point
To get started let's restrict ourselves to convolutions in 1D. So a window is just a 1D region from
Let's create a vector v
of random numbers of length n=100
.
100
7.1063e-5
0.350458
0.403612
0.394027
0.612336
0.0949315
0.868886
0.640738
0.530271
0.808074
0.735443
0.101631
0.050326
0.81701
0.380617
0.708762
0.0351414
0.519823
0.932136
0.974237
0.872181
0.136259
0.202741
0.315529
0.996482
0.315551
0.196865
0.977243
0.782316
0.706034
Feel free to experiment with different values!
Exercise 3.1
You've seen some colored lines in this notebook to visualize arrays. Can you make another one?
👉 Try plotting our vector v
using colored_line(v)
.
Try changing n
and v
around. Notice that you can run the cell v = rand(n)
again to regenerate new random values.
Exercise 3.2
We need to decide how to handle the boundary conditions, i.e. what happens if we try to access a position in the vector v
beyond 1:n
. The simplest solution is to assume that
A better solution is to use the closest value that is inside the vector. Effectively we are extending the vector and copying the extreme values into the extended positions. (Indeed, this is one way we could implement this; these extra positions are called ghost cells.)
👉 Write a function extend(v, i)
that checks whether the position 1:n
. If so, return the v
; otherwise, return the nearest end value.
extend (generic function with 1 method)
Some test cases:
missing
missing
missing
Extended with 0:
Extended with your extend
:
missing
Here we go!
Replace missing
with your answer.
Exercise 3.3
👉 Write a function blur_1D(v, l)
that blurs a vector v
with a window of length l
by averaging the elements within a window from
blur_1D (generic function with 1 method)
Exercise 3.4
👉 Apply the box blur to your vector v
. Show the original and the new vector by creating two cells that call colored_line
. Make the parameter l_box
instead of just l
to avoid a variable naming conflict.
Hint
Have a look at Exercise 2 to see an example of adding interactivity with a slider. You can read the Interactivity and the PlutoUI sample notebooks (right click -> Open in new tab) to learn more.
Exercise 3.5
The box blur is a simple example of a convolution, i.e. a linear function of a window around each point, given by
where
Again, we need to take care about what happens if
👉 Write a function convolve_vector(v, k)
that performs this convolution. You need to think of the vector
convolve_vector (generic function with 1 method)
Hint
l = (length(k) - 1) ÷ 2
missing
Edit the cell above, or create a new cell with your own test cases!
Here we go!
Replace missing
with your answer.
Exercise 3.6
👉 Write a function gaussian_kernel
.
The definition of a Gaussian in 1D is
We need to sample (i.e. evaluate) this at each pixel in a region of size
For simplicity you can take
gaussian_kernel (generic function with 1 method)
Let's test your kernel function!
3
Exercise 4 - Convolutions of images
Now let's move to 2D images. The convolution is then given by a kernel matrix
where the sum is over the possible values of
A common notation for this operation is
Exercise 4.1
👉 Write a function extend
that takes a matrix M
and indices i
and j
, and returns the closest element of the matrix.
extend (generic function with 2 methods)
Hint
num_rows, num_columns = size(M)
Let's test it!
Extended with 0
:
Extended with your extend
:
9×9 Matrix{Missing}:
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
Oopsie!
Make sure that you define a variable called extend_mat
283×223 Matrix{Missing}:
missing missing missing missing missing … missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing … missing missing missing missing
missing missing missing missing missing missing missing missing missing
⋮ ⋱ ⋮
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing … missing missing missing missing
missing missing missing missing missing missing missing missing missing
missing missing missing missing missing missing missing missing missing
Exercise 4.2
👉 Implement a function convolve_image(M, K)
.
convolve_image (generic function with 1 method)
Hint
num_rows, num_columns = size(K)
Let's test it out! 🎃
3×3 Matrix{Float64}:
0.0 0.0 0.0
0.5 0.0 0.5
0.0 0.0 0.0
missing
Edit K_test
to create your own test case!
missing
You can create all sorts of effects by choosing the kernel in a smart way. Today, we will implement two special kernels, to produce a Gaussian blur and a Sobel edge detect filter.
Make sure that you have watched the lecture about convolutions!
Exercise 4.3
👉 Apply a Gaussian blur to an image.
Here, the 2D Gaussian kernel will be defined as
with_gaussian_blur (generic function with 1 method)
Let's make it interactive. 💫
Another cell defining gauss_camera_image contains errors.
MethodError: no method matching getindex(::Missing, ::String)
Here is what happened, the most recent locations are first:
- process_raw_camera_data
(raw_camera_data::Missing) from Other cell: line 13# So to get the red values for each pixel, we take every 4th value, starting at
# the 1st:
reds_flat = UInt8.(raw_camera_data["data"][1:4:end])
greens_flat = UInt8.(raw_camera_data["data"][2:4:end])
blues_flat = UInt8.(raw_camera_data["data"][3:4:end])
- Show more...
Exercise 4.4
👉 Create a Sobel edge detection filter.
Here, we will need to create two separate filters that separately detect edges in the horizontal and vertical directions:
Here
Then we combine them by finding the magnitude of the gradient (in the sense of multivariate calculus) by defining
For simplicity you can choose one of the "channels" (colours) in the image to apply this to.
with_sobel_edge_detect (generic function with 1 method)
Another cell defining sobel_camera_image contains errors.
MethodError: no method matching getindex(::Missing, ::String)
Here is what happened, the most recent locations are first:
- process_raw_camera_data
(raw_camera_data::Missing) from Other cell: line 13# So to get the red values for each pixel, we take every 4th value, starting at
# the 1st:
reds_flat = UInt8.(raw_camera_data["data"][1:4:end])
greens_flat = UInt8.(raw_camera_data["data"][2:4:end])
blues_flat = UInt8.(raw_camera_data["data"][3:4:end])
- Show more...
Exercise 5 - Lecture transcript
(MIT students only)
Please see the Canvas post for transcript document for week 1 here.
We need each of you to correct about 100 lines (see instructions in the beginning of the document.)
👉 Please mention the name of the video and the line ranges you edited:
Convolution, lines 100-0 (for example)
Oops!
Before you submit, remember to fill in your name and kerberos ID at the top of this notebook!
Function library
Just some helper functions used in the notebook.
hint (generic function with 1 method)
almost (generic function with 1 method)
still_missing (generic function with 2 methods)
keep_working (generic function with 2 methods)
Fantastic!
Splendid!
Great!
Yay ❤
Great! 🎉
Well done!
Keep it up!
Good job!
Awesome!
You got the right answer!
Let's move on to the next section.
correct (generic function with 2 methods)
not_defined (generic function with 1 method)
todo (generic function with 1 method)
camera_input (generic function with 1 method)
process_raw_camera_data (generic function with 1 method)
homework 1, version 5