seam carving
This commit is contained in:
263
Basic Julia syntax.jl
Normal file
263
Basic Julia syntax.jl
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
### A Pluto.jl notebook ###
|
||||||
|
# v0.11.12
|
||||||
|
|
||||||
|
using Markdown
|
||||||
|
using InteractiveUtils
|
||||||
|
|
||||||
|
# ╔═╡ 0d3aec92-edeb-11ea-3adb-cd0dc17cbdab
|
||||||
|
md"# A basic Julia syntax cheatsheet
|
||||||
|
|
||||||
|
This notebook briefly summarizes some of the basic Julia syntax that we will need for the problem sets.
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 3b038ee0-edeb-11ea-0977-97cc30d1c6ff
|
||||||
|
md"## Variables
|
||||||
|
|
||||||
|
We can define a variable using `=` (assignment). Then we can use its value in other expressions:
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 3e8e0ea0-edeb-11ea-22e0-c58f7c2168ce
|
||||||
|
x = 3
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 59b66862-edeb-11ea-2d62-71dcc79dbfab
|
||||||
|
y = 2x
|
||||||
|
|
||||||
|
# ╔═╡ 5e062a24-edeb-11ea-256a-d938f77d7815
|
||||||
|
md"By default Julia displays the output of the last operation. (You can suppress the output by adding `;` (a semicolon) at the end.)
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 7e46f0e8-edeb-11ea-1092-4b5e8acd9ee0
|
||||||
|
md"We can ask what type a variable has using `typeof`:"
|
||||||
|
|
||||||
|
# ╔═╡ 8a695b86-edeb-11ea-08cc-17263bec09df
|
||||||
|
typeof(y)
|
||||||
|
|
||||||
|
# ╔═╡ 8e2dd3be-edeb-11ea-0703-354fb31c12f5
|
||||||
|
md"## Functions"
|
||||||
|
|
||||||
|
# ╔═╡ 96b5a28c-edeb-11ea-11c0-597615962f54
|
||||||
|
md"We can use a short-form, one-line function definition for simple functions:"
|
||||||
|
|
||||||
|
# ╔═╡ a7453572-edeb-11ea-1e27-9f710fd856a6
|
||||||
|
f(x) = 2 + x
|
||||||
|
|
||||||
|
# ╔═╡ b341db4e-edeb-11ea-078b-b71ac00089d7
|
||||||
|
md"Typing the function's name gives information about the function. To call it we must use parentheses:"
|
||||||
|
|
||||||
|
# ╔═╡ 23f9afd4-eded-11ea-202a-9f0f1f91e5ad
|
||||||
|
f
|
||||||
|
|
||||||
|
# ╔═╡ cc1f6872-edeb-11ea-33e9-6976fd9b107a
|
||||||
|
f(10)
|
||||||
|
|
||||||
|
# ╔═╡ ce9667c2-edeb-11ea-2665-d789032abd11
|
||||||
|
md"For longer functions we use the following syntax with the `function` keyword and `end`:"
|
||||||
|
|
||||||
|
# ╔═╡ d73d3400-edeb-11ea-2dea-95e8c4a6563b
|
||||||
|
function g(x, y)
|
||||||
|
z = x + y
|
||||||
|
return z^2
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ e04ccf10-edeb-11ea-36d1-d11969e4b2f2
|
||||||
|
g(1, 2)
|
||||||
|
|
||||||
|
# ╔═╡ e297c5cc-edeb-11ea-3bdd-090f415685ab
|
||||||
|
md"## For loops"
|
||||||
|
|
||||||
|
# ╔═╡ ec751446-edeb-11ea-31ba-2372e7c71b42
|
||||||
|
md"Use `for` to loop through a pre-determined set of values:"
|
||||||
|
|
||||||
|
# ╔═╡ fe3fa290-edeb-11ea-121e-7114e5c573c1
|
||||||
|
let s = 0
|
||||||
|
|
||||||
|
for i in 1:10
|
||||||
|
s += i # Equivalent to s = s + i
|
||||||
|
end
|
||||||
|
|
||||||
|
s
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 394b0ec8-eded-11ea-31fb-27392068ef8f
|
||||||
|
md"Here, `1:10` is a **range** representing the numbers from 1 to 10:"
|
||||||
|
|
||||||
|
# ╔═╡ 4dc00908-eded-11ea-25c5-0f7b2b7e18f9
|
||||||
|
typeof(1:10)
|
||||||
|
|
||||||
|
# ╔═╡ 6c44abb4-edec-11ea-16bd-557800b5f9d2
|
||||||
|
md"Above we used a `let` block to define a new local variable `s`.
|
||||||
|
But blocks of code like this are usually better inside functions, so that they can be reused. For example, we could rewrite the above as follows:
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 683af3e2-eded-11ea-25a5-0d90bf099d98
|
||||||
|
function mysum(n)
|
||||||
|
s = 0
|
||||||
|
|
||||||
|
for i in 1:n
|
||||||
|
s += i
|
||||||
|
end
|
||||||
|
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 76764ea2-eded-11ea-1aa6-296f3421de1c
|
||||||
|
mysum(100)
|
||||||
|
|
||||||
|
# ╔═╡ 93a231f4-edec-11ea-3b39-299b3be2da78
|
||||||
|
md"## Conditionals: `if`"
|
||||||
|
|
||||||
|
# ╔═╡ 82e63a24-eded-11ea-3887-15d6bfabea4b
|
||||||
|
md"We can evaluate whether a condition is true or not by simply writing the condition:"
|
||||||
|
|
||||||
|
# ╔═╡ 9b339b2a-eded-11ea-10d7-8fc9a907c892
|
||||||
|
a = 3
|
||||||
|
|
||||||
|
# ╔═╡ 9535eb40-eded-11ea-1651-e33c9c23dbfb
|
||||||
|
a < 5
|
||||||
|
|
||||||
|
# ╔═╡ a16299a2-eded-11ea-2b56-93eb7a1010a7
|
||||||
|
md"We see that conditions have a Boolean (`true` or `false`) value.
|
||||||
|
|
||||||
|
We can then use `if` to control what we do based on that value:"
|
||||||
|
|
||||||
|
# ╔═╡ bc6b124e-eded-11ea-0290-b3760cb81024
|
||||||
|
if a < 5
|
||||||
|
"small"
|
||||||
|
|
||||||
|
else
|
||||||
|
"big"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ cfb21014-eded-11ea-1261-3bc30952a88e
|
||||||
|
md"""Note that the `if` also returns the last value that was evaluated, in this case the string `"small"` or `"big"`, Since Pluto is reactive, changing the definition of `a` above will automatically cause this to be reevaluated!"""
|
||||||
|
|
||||||
|
# ╔═╡ ffee7d80-eded-11ea-26b1-1331df204c67
|
||||||
|
md"## Arrays"
|
||||||
|
|
||||||
|
# ╔═╡ cae4137e-edee-11ea-14af-59a32227de1b
|
||||||
|
md"### 1D arrays (`Vector`s)"
|
||||||
|
|
||||||
|
# ╔═╡ 714f4fca-edee-11ea-3410-c9ab8825d836
|
||||||
|
md"We can make a `Vector` (1-dimensional, or 1D array) using square brackets:"
|
||||||
|
|
||||||
|
# ╔═╡ 82cc2a0e-edee-11ea-11b7-fbaa5ad7b556
|
||||||
|
v = [1, 2, 3]
|
||||||
|
|
||||||
|
# ╔═╡ 85916c18-edee-11ea-0738-5f5d78875b86
|
||||||
|
typeof(v)
|
||||||
|
|
||||||
|
# ╔═╡ 881b7d0c-edee-11ea-0b4a-4bd7d5be2c77
|
||||||
|
md"The `1` in the type shows that this is a 1D array.
|
||||||
|
|
||||||
|
We access elements also using square brackets:"
|
||||||
|
|
||||||
|
# ╔═╡ a298e8ae-edee-11ea-3613-0dd4bae70c26
|
||||||
|
v[2]
|
||||||
|
|
||||||
|
# ╔═╡ a5ebddd6-edee-11ea-2234-55453ea59c5a
|
||||||
|
v[2] = 10
|
||||||
|
|
||||||
|
# ╔═╡ a9b48e54-edee-11ea-1333-a96181de0185
|
||||||
|
md"Note that Pluto does not automatically update cells when you modify elements of an array, but the value does change."
|
||||||
|
|
||||||
|
# ╔═╡ 68c4ead2-edef-11ea-124a-03c2d7dd6a1b
|
||||||
|
md"A nice way to create `Vector`s following a certain pattern is to use an **array comprehension**:"
|
||||||
|
|
||||||
|
# ╔═╡ 84129294-edef-11ea-0c77-ffa2b9592a26
|
||||||
|
v2 = [i^2 for i in 1:10]
|
||||||
|
|
||||||
|
# ╔═╡ d364fa16-edee-11ea-2050-0f6cb70e1bcf
|
||||||
|
md"## 2D arrays (matrices)"
|
||||||
|
|
||||||
|
# ╔═╡ db99ae9a-edee-11ea-393e-9de420a545a1
|
||||||
|
md"We can make small matrices (2D arrays) with square brackets too:"
|
||||||
|
|
||||||
|
# ╔═╡ 04f175f2-edef-11ea-0882-712548ebb7a3
|
||||||
|
M = [1 2
|
||||||
|
3 4]
|
||||||
|
|
||||||
|
# ╔═╡ 0a8ac112-edef-11ea-1e99-cf7c7808c4f5
|
||||||
|
typeof(M)
|
||||||
|
|
||||||
|
# ╔═╡ 1295f48a-edef-11ea-22a5-61e8a2e1d005
|
||||||
|
md"The `2` in the type confirms that this is a 2D array."
|
||||||
|
|
||||||
|
# ╔═╡ 3e1fdaa8-edef-11ea-2f03-eb41b2b9ea0f
|
||||||
|
md"This won't work for larger matrices, though. For that we can use e.g."
|
||||||
|
|
||||||
|
# ╔═╡ 48f3deca-edef-11ea-2c18-e7419c9030a0
|
||||||
|
zeros(5, 5)
|
||||||
|
|
||||||
|
# ╔═╡ a8f26af8-edef-11ea-2fc7-2b776f515aea
|
||||||
|
md"Note that `zeros` gives `Float64`s by default. We can also specify a type for the elements:"
|
||||||
|
|
||||||
|
# ╔═╡ b595373e-edef-11ea-03e2-6599ef14af20
|
||||||
|
zeros(Int, 4, 5)
|
||||||
|
|
||||||
|
# ╔═╡ 4cb33c04-edef-11ea-2b35-1139c246c331
|
||||||
|
md"We can then fill in the values we want by manipulating the elements, e.g. with a `for` loop."
|
||||||
|
|
||||||
|
# ╔═╡ 54e47e9e-edef-11ea-2d75-b5f550902528
|
||||||
|
md"A nice alternative syntax to create matrices following a certain pattern is an array comprehension with a *double* `for` loop:"
|
||||||
|
|
||||||
|
# ╔═╡ 6348edce-edef-11ea-1ab4-019514eb414f
|
||||||
|
[i + j for i in 1:5, j in 1:6]
|
||||||
|
|
||||||
|
# ╔═╡ Cell order:
|
||||||
|
# ╟─0d3aec92-edeb-11ea-3adb-cd0dc17cbdab
|
||||||
|
# ╟─3b038ee0-edeb-11ea-0977-97cc30d1c6ff
|
||||||
|
# ╠═3e8e0ea0-edeb-11ea-22e0-c58f7c2168ce
|
||||||
|
# ╠═59b66862-edeb-11ea-2d62-71dcc79dbfab
|
||||||
|
# ╟─5e062a24-edeb-11ea-256a-d938f77d7815
|
||||||
|
# ╟─7e46f0e8-edeb-11ea-1092-4b5e8acd9ee0
|
||||||
|
# ╠═8a695b86-edeb-11ea-08cc-17263bec09df
|
||||||
|
# ╟─8e2dd3be-edeb-11ea-0703-354fb31c12f5
|
||||||
|
# ╟─96b5a28c-edeb-11ea-11c0-597615962f54
|
||||||
|
# ╠═a7453572-edeb-11ea-1e27-9f710fd856a6
|
||||||
|
# ╟─b341db4e-edeb-11ea-078b-b71ac00089d7
|
||||||
|
# ╠═23f9afd4-eded-11ea-202a-9f0f1f91e5ad
|
||||||
|
# ╠═cc1f6872-edeb-11ea-33e9-6976fd9b107a
|
||||||
|
# ╟─ce9667c2-edeb-11ea-2665-d789032abd11
|
||||||
|
# ╠═d73d3400-edeb-11ea-2dea-95e8c4a6563b
|
||||||
|
# ╠═e04ccf10-edeb-11ea-36d1-d11969e4b2f2
|
||||||
|
# ╟─e297c5cc-edeb-11ea-3bdd-090f415685ab
|
||||||
|
# ╟─ec751446-edeb-11ea-31ba-2372e7c71b42
|
||||||
|
# ╠═fe3fa290-edeb-11ea-121e-7114e5c573c1
|
||||||
|
# ╟─394b0ec8-eded-11ea-31fb-27392068ef8f
|
||||||
|
# ╠═4dc00908-eded-11ea-25c5-0f7b2b7e18f9
|
||||||
|
# ╟─6c44abb4-edec-11ea-16bd-557800b5f9d2
|
||||||
|
# ╠═683af3e2-eded-11ea-25a5-0d90bf099d98
|
||||||
|
# ╠═76764ea2-eded-11ea-1aa6-296f3421de1c
|
||||||
|
# ╟─93a231f4-edec-11ea-3b39-299b3be2da78
|
||||||
|
# ╟─82e63a24-eded-11ea-3887-15d6bfabea4b
|
||||||
|
# ╠═9b339b2a-eded-11ea-10d7-8fc9a907c892
|
||||||
|
# ╠═9535eb40-eded-11ea-1651-e33c9c23dbfb
|
||||||
|
# ╟─a16299a2-eded-11ea-2b56-93eb7a1010a7
|
||||||
|
# ╠═bc6b124e-eded-11ea-0290-b3760cb81024
|
||||||
|
# ╟─cfb21014-eded-11ea-1261-3bc30952a88e
|
||||||
|
# ╟─ffee7d80-eded-11ea-26b1-1331df204c67
|
||||||
|
# ╟─cae4137e-edee-11ea-14af-59a32227de1b
|
||||||
|
# ╟─714f4fca-edee-11ea-3410-c9ab8825d836
|
||||||
|
# ╠═82cc2a0e-edee-11ea-11b7-fbaa5ad7b556
|
||||||
|
# ╠═85916c18-edee-11ea-0738-5f5d78875b86
|
||||||
|
# ╟─881b7d0c-edee-11ea-0b4a-4bd7d5be2c77
|
||||||
|
# ╠═a298e8ae-edee-11ea-3613-0dd4bae70c26
|
||||||
|
# ╠═a5ebddd6-edee-11ea-2234-55453ea59c5a
|
||||||
|
# ╟─a9b48e54-edee-11ea-1333-a96181de0185
|
||||||
|
# ╟─68c4ead2-edef-11ea-124a-03c2d7dd6a1b
|
||||||
|
# ╠═84129294-edef-11ea-0c77-ffa2b9592a26
|
||||||
|
# ╟─d364fa16-edee-11ea-2050-0f6cb70e1bcf
|
||||||
|
# ╟─db99ae9a-edee-11ea-393e-9de420a545a1
|
||||||
|
# ╠═04f175f2-edef-11ea-0882-712548ebb7a3
|
||||||
|
# ╠═0a8ac112-edef-11ea-1e99-cf7c7808c4f5
|
||||||
|
# ╟─1295f48a-edef-11ea-22a5-61e8a2e1d005
|
||||||
|
# ╟─3e1fdaa8-edef-11ea-2f03-eb41b2b9ea0f
|
||||||
|
# ╠═48f3deca-edef-11ea-2c18-e7419c9030a0
|
||||||
|
# ╟─a8f26af8-edef-11ea-2fc7-2b776f515aea
|
||||||
|
# ╠═b595373e-edef-11ea-03e2-6599ef14af20
|
||||||
|
# ╟─4cb33c04-edef-11ea-2b35-1139c246c331
|
||||||
|
# ╟─54e47e9e-edef-11ea-2d75-b5f550902528
|
||||||
|
# ╠═6348edce-edef-11ea-1ab4-019514eb414f
|
||||||
807
Lecture 1 - Images.jl
Normal file
807
Lecture 1 - Images.jl
Normal file
@@ -0,0 +1,807 @@
|
|||||||
|
### A Pluto.jl notebook ###
|
||||||
|
# v0.11.12
|
||||||
|
|
||||||
|
using Markdown
|
||||||
|
using InteractiveUtils
|
||||||
|
|
||||||
|
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
|
||||||
|
macro bind(def, element)
|
||||||
|
quote
|
||||||
|
local el = $(esc(element))
|
||||||
|
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
|
||||||
|
el
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 5e688928-e939-11ea-0e16-fbc80af390ab
|
||||||
|
using LinearAlgebra
|
||||||
|
|
||||||
|
# ╔═╡ a50b5f48-e8d5-11ea-1f05-a3741b5d15ba
|
||||||
|
html"<button onclick=present()>Present</button>"
|
||||||
|
|
||||||
|
# ╔═╡ 8a6fed4c-e94b-11ea-1113-d56f56fb293b
|
||||||
|
br = HTML("<br>")
|
||||||
|
|
||||||
|
# ╔═╡ dc53f316-e8c8-11ea-150f-1374dbce114a
|
||||||
|
md"""# Welcome to 18.S191 -- Fall 2020!
|
||||||
|
|
||||||
|
### Introduction to Computational Thinking for Real-World Problems"""
|
||||||
|
|
||||||
|
# ╔═╡ c3f43d66-e94b-11ea-02bd-23cfeb878ff1
|
||||||
|
br
|
||||||
|
|
||||||
|
# ╔═╡ c6c77738-e94b-11ea-22f5-1dce3dbcc3ca
|
||||||
|
md"### <https://github.com/mitmath/18S191>"
|
||||||
|
|
||||||
|
# ╔═╡ cf80793a-e94b-11ea-0120-f7913ae06f22
|
||||||
|
br
|
||||||
|
|
||||||
|
# ╔═╡ d1638d96-e94b-11ea-2ff4-910e399f864d
|
||||||
|
md"##### Alan Edelman, David P. Sanders, Grant Sanderson, James Schloss"
|
||||||
|
|
||||||
|
# ╔═╡ 0117246a-e94c-11ea-1a76-c981ce8e725d
|
||||||
|
md"##### & Philip the Corgi"
|
||||||
|
|
||||||
|
# ╔═╡ 27060098-e8c9-11ea-2fe0-03b39b1ddc32
|
||||||
|
md"""# Class outline
|
||||||
|
|
||||||
|
### Data and computation
|
||||||
|
|
||||||
|
- Module 1: Analyzing images
|
||||||
|
|
||||||
|
- Module 2: Particles and ray tracing
|
||||||
|
|
||||||
|
- Module 3: Epidemic spread
|
||||||
|
|
||||||
|
- Module 4: Climate change
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 4fc58814-e94b-11ea-339b-cb714a63f9b6
|
||||||
|
md"## Tools
|
||||||
|
|
||||||
|
- Julia programming language: <http://www.julialang.org/learning>
|
||||||
|
|
||||||
|
- Pluto notebook environment
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ f067d3b8-e8c8-11ea-20cb-474709ffa99a
|
||||||
|
md"""# Module 1: Images"""
|
||||||
|
|
||||||
|
# ╔═╡ 37c1d012-ebc9-11ea-2dfe-8b86bb78f283
|
||||||
|
4 + 4
|
||||||
|
|
||||||
|
# ╔═╡ a0a97214-e8d2-11ea-0f46-0bfaf016ab6d
|
||||||
|
md"""## Data takes many forms
|
||||||
|
|
||||||
|
- Time series:
|
||||||
|
- Number of infections per day
|
||||||
|
- Stock price each minute
|
||||||
|
- A piece for violin broadcast over the radio
|
||||||
|
$(HTML("<br>"))
|
||||||
|
|
||||||
|
- Video:
|
||||||
|
- The view from a window of a self-driving car
|
||||||
|
- A hurricane monitoring station
|
||||||
|
$(HTML("<br>"))
|
||||||
|
|
||||||
|
- Images:
|
||||||
|
- Diseased versus healthy tissue in a scan
|
||||||
|
- Deep space via the Hubble telescope
|
||||||
|
- Can your social media account recognise your friends?
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 1697a756-e93d-11ea-0b6e-c9c78d527993
|
||||||
|
md"## Capture your own image!"
|
||||||
|
|
||||||
|
# ╔═╡ af28faca-ebb7-11ea-130d-0f94bf9bd836
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ ee1d1596-e94a-11ea-0fb4-cd05f62471d3
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ 8ab9a978-e8c9-11ea-2476-f1ef4ba1b619
|
||||||
|
md"""## What is an image?"""
|
||||||
|
|
||||||
|
# ╔═╡ 38c54bfc-e8cb-11ea-3d52-0f02452f8ba1
|
||||||
|
md"Albrecht Dürer:"
|
||||||
|
|
||||||
|
# ╔═╡ 983f8270-e8c9-11ea-29d2-adeccb5a7ffc
|
||||||
|
# md"# begin
|
||||||
|
# using Images
|
||||||
|
|
||||||
|
# download("https://i.stack.imgur.com/QQL8X.jpg", "durer.jpg")
|
||||||
|
|
||||||
|
# load("durer.jpg")
|
||||||
|
# end
|
||||||
|
|
||||||
|
md""
|
||||||
|
|
||||||
|
# ╔═╡ 2fcaef88-e8ca-11ea-23f7-29c48580f43c
|
||||||
|
md"""##
|
||||||
|
|
||||||
|
An image is:
|
||||||
|
|
||||||
|
- A 2D representation of a 3D world
|
||||||
|
|
||||||
|
- An approximation
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 7636c4b0-e8d1-11ea-2051-757a850a9d30
|
||||||
|
begin
|
||||||
|
image_text =
|
||||||
|
md"""
|
||||||
|
## What *is* an image, though?
|
||||||
|
|
||||||
|
- A grid of coloured squares called **pixels**
|
||||||
|
|
||||||
|
- A colour for each pair $(i, j)$ of indices
|
||||||
|
|
||||||
|
- A **discretization**
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
image_text
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ bca22176-e8ca-11ea-2004-ebeb103116b5
|
||||||
|
md"""
|
||||||
|
## How can we store an image in the computer?
|
||||||
|
|
||||||
|
- Is it a 1D array (`Vector`)?
|
||||||
|
|
||||||
|
- A 2D array (`Matrix`)?
|
||||||
|
|
||||||
|
- A 3D array (`tensor`)?
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 0ad91f1e-e8d2-11ea-2c18-93f66c906a8b
|
||||||
|
md"""## If in doubt: Ask Julia!
|
||||||
|
|
||||||
|
- Let's use the `Images.jl` package to load an image and see what we get
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ de373816-ec79-11ea-2772-ebdca52246ac
|
||||||
|
begin
|
||||||
|
import Pkg
|
||||||
|
Pkg.activate(mktempdir())
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 552129ae-ebca-11ea-1fa1-3f9fa00a2601
|
||||||
|
begin
|
||||||
|
Pkg.add(["Images", "ImageIO", "ImageMagick"])
|
||||||
|
using Images
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ fbe11200-e938-11ea-12e9-6125c1b56b25
|
||||||
|
begin
|
||||||
|
Pkg.add("PlutoUI")
|
||||||
|
using PlutoUI
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 54c1ba3c-e8d2-11ea-3564-bdaca8563738
|
||||||
|
# defines a variable called `url`
|
||||||
|
# whose value is a string (written inside `"`):
|
||||||
|
|
||||||
|
url = "https://i.imgur.com/VGPeJ6s.jpg"
|
||||||
|
|
||||||
|
# ╔═╡ 6e0fefb6-e8d4-11ea-1f9b-e7a3db40df39
|
||||||
|
philip_file = download(url, "philip.jpg") # download to a local file
|
||||||
|
|
||||||
|
# ╔═╡ 9c359212-ec79-11ea-2d7e-0124dad5f127
|
||||||
|
philip = load(philip_file)
|
||||||
|
|
||||||
|
# ╔═╡ 7703b032-ebca-11ea-3074-0b80a077078e
|
||||||
|
philip
|
||||||
|
|
||||||
|
# ╔═╡ 7eff3522-ebca-11ea-1a65-59e66a4e72ab
|
||||||
|
typeof(philip)
|
||||||
|
|
||||||
|
# ╔═╡ c9cd6c04-ebca-11ea-0990-5fa19ff7ed97
|
||||||
|
RGBX(0.9, 0.1, 0.1)
|
||||||
|
|
||||||
|
# ╔═╡ 0d873d9c-e93b-11ea-2425-1bd79677fb97
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ 6b09354a-ebb9-11ea-2d5a-3b75c5ae7aa9
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 2d6c434e-e93b-11ea-2678-3b9db4975089
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ 2b14e93e-e93b-11ea-25f1-5f565f80e778
|
||||||
|
typeof(philip)
|
||||||
|
|
||||||
|
# ╔═╡ 0bdc6058-e8d5-11ea-1889-3f706cea7a1f
|
||||||
|
md"""##
|
||||||
|
|
||||||
|
- According to Julia / Pluto, the variable `philip` *is* an image
|
||||||
|
|
||||||
|
- Julia always returns output
|
||||||
|
|
||||||
|
- The output can be displayed in a "rich" way
|
||||||
|
|
||||||
|
$(HTML("<br>"))
|
||||||
|
|
||||||
|
- Arthur C. Clarke:
|
||||||
|
|
||||||
|
> Any sufficiently advanced technology is indistinguishable from magic.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ e61db924-ebca-11ea-2f79-f9f1c121b7f5
|
||||||
|
size(philip)
|
||||||
|
|
||||||
|
# ╔═╡ ef60fcc4-ebca-11ea-3f69-155afffe8ea8
|
||||||
|
philip
|
||||||
|
|
||||||
|
# ╔═╡ fac550ec-ebca-11ea-337a-dbc16848c617
|
||||||
|
philip[1:1000, 1:400]
|
||||||
|
|
||||||
|
# ╔═╡ 42aa8cfe-e8d5-11ea-3cb9-c365b98e7a8c
|
||||||
|
md"
|
||||||
|
## How big is Philip?
|
||||||
|
|
||||||
|
- He's pretty big:
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 4eea5710-e8d5-11ea-3978-af66ee2a137e
|
||||||
|
size(philip)
|
||||||
|
|
||||||
|
# ╔═╡ 57b3a0c2-e8d5-11ea-15aa-8da4549f849b
|
||||||
|
md"- Which number is which?"
|
||||||
|
|
||||||
|
# ╔═╡ 03a7c0fc-ebba-11ea-1c71-79d750c97b16
|
||||||
|
philip
|
||||||
|
|
||||||
|
# ╔═╡ e6fd68fa-e8d8-11ea-3dc4-274caceda222
|
||||||
|
md"# So, what *is* an image?"
|
||||||
|
|
||||||
|
# ╔═╡ 63a1d282-e8d5-11ea-0bba-b9cdd32a218b
|
||||||
|
typeof(philip)
|
||||||
|
|
||||||
|
# ╔═╡ fc5e1af0-e8d8-11ea-1077-07216ff96d29
|
||||||
|
md"""
|
||||||
|
- It's an `Array`
|
||||||
|
|
||||||
|
- The `2` means that it has **2 dimensions** (a **matrix**)
|
||||||
|
|
||||||
|
$(HTML("<br>"))
|
||||||
|
|
||||||
|
- `RGBX{Normed{UInt8,8}}` is the type of object stored in the array
|
||||||
|
|
||||||
|
- A Julia object representing a colour
|
||||||
|
|
||||||
|
- RGB = Red, Green, Blue
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ c79dd836-e8e8-11ea-029d-57be9899979a
|
||||||
|
md"## Getting pieces of an image"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ ae260168-e932-11ea-38fd-4f2c6f43e21c
|
||||||
|
begin
|
||||||
|
(h, w) = size(philip)
|
||||||
|
head = philip[(h ÷ 2):h, (w ÷ 10): (9w ÷ 10)]
|
||||||
|
# `÷` is typed as \div <TAB> -- integer division
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 47d1bc04-ebcb-11ea-3643-d1ba8dea57c8
|
||||||
|
size(head)
|
||||||
|
|
||||||
|
# ╔═╡ 72400458-ebcb-11ea-26b6-678ae1de8e23
|
||||||
|
size(philip)
|
||||||
|
|
||||||
|
# ╔═╡ f57ea7c2-e932-11ea-0d52-4112187bcb38
|
||||||
|
md"## Manipulating matrices
|
||||||
|
|
||||||
|
- An image is just a matrix, so we can manipulate *matrices* to manipulate the *image*
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 740ed2e2-e933-11ea-236c-f3c3f09d0f8b
|
||||||
|
[head head]
|
||||||
|
|
||||||
|
# ╔═╡ 6128a5ba-e93b-11ea-03f5-f170c7b90b25
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ 78eafe4e-e933-11ea-3539-c13feb894ef6
|
||||||
|
[
|
||||||
|
head reverse(head, dims=2)
|
||||||
|
reverse(head, dims=1) reverse(reverse(head, dims=1), dims=2)
|
||||||
|
]
|
||||||
|
|
||||||
|
# ╔═╡ bf3f9050-e933-11ea-0df7-e5dcff6bb3ee
|
||||||
|
md"## Manipulating an image
|
||||||
|
|
||||||
|
- How can we get inside the image and change it?
|
||||||
|
|
||||||
|
- There are two possibilities:
|
||||||
|
|
||||||
|
- **Modify** (**mutate**) numbers inside the array -- useful to change a small piece
|
||||||
|
|
||||||
|
- Create a new **copy** of the array -- useful to alter everything together
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 212e1f12-e934-11ea-2f35-51c7a6c8dff1
|
||||||
|
md"## Painting a piece of an image
|
||||||
|
|
||||||
|
- Let's paint a corner red
|
||||||
|
|
||||||
|
- We'll copy the image first so we don't destroy the original
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 117a98c0-e936-11ea-3aac-8f66337cea68
|
||||||
|
new_phil = copy(head)
|
||||||
|
|
||||||
|
# ╔═╡ 8004d076-e93b-11ea-29cc-a1bfcc75e87f
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ 3ac63296-e936-11ea-2144-f94bdbd60eaf
|
||||||
|
red = RGB(1, 0, 0)
|
||||||
|
|
||||||
|
# ╔═╡ 3e3f841a-e936-11ea-0a81-1b95fe0faa83
|
||||||
|
for i in 1:100
|
||||||
|
for j in 1:300
|
||||||
|
new_phil[i, j] = red
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 5978db50-e936-11ea-3145-059a51be2281
|
||||||
|
md"Note that `for` loops *do not return anything* (or, rather, they return `nothing`)"
|
||||||
|
|
||||||
|
# ╔═╡ 21638b14-ebcc-11ea-1761-bbd2f4306a96
|
||||||
|
new_phil
|
||||||
|
|
||||||
|
# ╔═╡ 70cb0e36-e936-11ea-3ade-49fde77cb696
|
||||||
|
md"""## Element-wise operations: "Broadcasting"
|
||||||
|
|
||||||
|
- Julia provides powerful technology for operating element by element: **broadcasting**
|
||||||
|
|
||||||
|
- Adding "`.`" applies an operation element by element
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ b3ea975e-e936-11ea-067d-81339575a3cb
|
||||||
|
begin
|
||||||
|
new_phil2 = copy(new_phil)
|
||||||
|
new_phil2[100:200, 1:100] .= RGB(0, 1, 0)
|
||||||
|
new_phil2
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 918a0762-e93b-11ea-1115-71dbfdb03f27
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ daabe66c-e937-11ea-3bc3-d77f2bce406c
|
||||||
|
new_phil2
|
||||||
|
|
||||||
|
# ╔═╡ 095ced62-e938-11ea-1169-939dc7136fd0
|
||||||
|
md"## Modifying the whole image at once
|
||||||
|
|
||||||
|
- We can use the same trick to modify the whole image at once
|
||||||
|
|
||||||
|
- Let's **redify** the image
|
||||||
|
|
||||||
|
- We define a **function** that turns a colour into just its red component
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 31f3605a-e938-11ea-3a6d-29a185bbee31
|
||||||
|
function redify(c)
|
||||||
|
return RGB(c.r, 0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 2744a556-e94f-11ea-2434-d53c24e59285
|
||||||
|
begin
|
||||||
|
color = RGB(0.9, 0.7, 0.2)
|
||||||
|
|
||||||
|
[color, redify(color)]
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 98412a36-e93b-11ea-1954-f1c105c6ed4a
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ 3c32efde-e938-11ea-1ae4-5d88290f5311
|
||||||
|
redify.(philip)
|
||||||
|
|
||||||
|
# ╔═╡ 4b26e4e6-e938-11ea-2635-6d4fc15e13b7
|
||||||
|
md"## Transforming an image
|
||||||
|
|
||||||
|
- The main goal of this week will be to transfrom images in more interesting ways
|
||||||
|
|
||||||
|
- First let's **decimate** poor Phil
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ c12e0928-e93b-11ea-0922-2b590a99ee89
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ ff5dc538-e938-11ea-058f-693d6b016640
|
||||||
|
md"## Experiments come alive with interaction
|
||||||
|
|
||||||
|
- We start to get a feel for things when we can **experiment**!
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ fa24f4a8-e93b-11ea-06bd-25c9672166d6
|
||||||
|
md"##"
|
||||||
|
|
||||||
|
# ╔═╡ 15ce202e-e939-11ea-2387-93be0ec4cf1f
|
||||||
|
@bind repeat_count Slider(1:10, show_value=true)
|
||||||
|
|
||||||
|
# ╔═╡ bf2167a4-e93d-11ea-03b2-cdd24b459ba9
|
||||||
|
md"## Summary
|
||||||
|
|
||||||
|
- Images are readily-accessible data about the world
|
||||||
|
|
||||||
|
- We want to process them to extract information
|
||||||
|
|
||||||
|
- Relatively simple mathematical operations can transform images in useful ways
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 58184d88-e939-11ea-2fc8-73b3476ebe92
|
||||||
|
expand(image, ratio=5) = kron(image, ones(ratio, ratio))
|
||||||
|
|
||||||
|
# ╔═╡ 2dd09f16-e93a-11ea-2cdc-13f558e3391d
|
||||||
|
extract_red(c) = c.r
|
||||||
|
|
||||||
|
# ╔═╡ df1b7996-e93b-11ea-1a3a-81b4ec520679
|
||||||
|
decimate(image, ratio=5) = image[1:ratio:end, 1:ratio:end]
|
||||||
|
|
||||||
|
# ╔═╡ 41fa85c0-e939-11ea-1ad8-79805a2083bb
|
||||||
|
poor_phil = decimate(head, 5)
|
||||||
|
|
||||||
|
# ╔═╡ cd5721d0-ede6-11ea-0918-1992c69bccc6
|
||||||
|
repeat(poor_phil, repeat_count, repeat_count)
|
||||||
|
|
||||||
|
# ╔═╡ b8daeea0-ec79-11ea-34b5-3f13e8a56a42
|
||||||
|
md"# Appendix"
|
||||||
|
|
||||||
|
# ╔═╡ bf1bb2c8-ec79-11ea-0671-3ffb34828f3c
|
||||||
|
md"## Package environment"
|
||||||
|
|
||||||
|
# ╔═╡ 69e3aa82-e93c-11ea-23fe-c1103d989cba
|
||||||
|
md"## Camera input"
|
||||||
|
|
||||||
|
# ╔═╡ 739c3bb6-e93c-11ea-127b-efb6a8ab9379
|
||||||
|
function camera_input(;max_size=200, default_url="https://i.imgur.com/SUmi94P.png")
|
||||||
|
"""
|
||||||
|
<span class="pl-image waiting-for-permission">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.pl-image.popped-out {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-image #video-container {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-image video {
|
||||||
|
border-radius: 1rem 1rem 0 0;
|
||||||
|
}
|
||||||
|
.pl-image.waiting-for-permission #video-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.pl-image #prompt {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.pl-image.waiting-for-permission #prompt {
|
||||||
|
width: 250px;
|
||||||
|
height: 200px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
font-family: monospace;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 5px dashed rgba(0,0,0,.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-image video {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.pl-image .bar {
|
||||||
|
width: inherit;
|
||||||
|
display: flex;
|
||||||
|
z-index: 6;
|
||||||
|
}
|
||||||
|
.pl-image .bar#top {
|
||||||
|
position: absolute;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-image .bar#bottom {
|
||||||
|
background: black;
|
||||||
|
border-radius: 0 0 1rem 1rem;
|
||||||
|
}
|
||||||
|
.pl-image .bar button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
background: rgba(255,255,255,.8);
|
||||||
|
border: none;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
border-radius: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 7;
|
||||||
|
}
|
||||||
|
.pl-image .bar button#shutter {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
margin: -1.5rem auto .2rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-image video.takepicture {
|
||||||
|
animation: pictureflash 200ms linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pictureflash {
|
||||||
|
0% {
|
||||||
|
filter: grayscale(1.0) contrast(2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
filter: grayscale(0.0) contrast(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="video-container">
|
||||||
|
<div id="top" class="bar">
|
||||||
|
<button id="stop" title="Stop video">✖</button>
|
||||||
|
<button id="pop-out" title="Pop out/pop in">⏏</button>
|
||||||
|
</div>
|
||||||
|
<video playsinline autoplay></video>
|
||||||
|
<div id="bottom" class="bar">
|
||||||
|
<button id="shutter" title="Click to take a picture">📷</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="prompt">
|
||||||
|
<span>
|
||||||
|
Enable webcam
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// based on https://github.com/fonsp/printi-static (by the same author)
|
||||||
|
|
||||||
|
const span = this.currentScript.parentElement
|
||||||
|
const video = span.querySelector("video")
|
||||||
|
const popout = span.querySelector("button#pop-out")
|
||||||
|
const stop = span.querySelector("button#stop")
|
||||||
|
const shutter = span.querySelector("button#shutter")
|
||||||
|
const prompt = span.querySelector(".pl-image #prompt")
|
||||||
|
|
||||||
|
const maxsize = $(max_size)
|
||||||
|
|
||||||
|
const send_source = (source, src_width, src_height) => {
|
||||||
|
const scale = Math.min(1.0, maxsize / src_width, maxsize / src_height)
|
||||||
|
|
||||||
|
const width = Math.floor(src_width * scale)
|
||||||
|
const height = Math.floor(src_height * scale)
|
||||||
|
|
||||||
|
const canvas = html`<canvas width=\${width} height=\${height}>`
|
||||||
|
const ctx = canvas.getContext("2d")
|
||||||
|
ctx.drawImage(source, 0, 0, width, height)
|
||||||
|
|
||||||
|
span.value = {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
data: ctx.getImageData(0, 0, width, height).data,
|
||||||
|
}
|
||||||
|
span.dispatchEvent(new CustomEvent("input"))
|
||||||
|
}
|
||||||
|
|
||||||
|
const clear_camera = () => {
|
||||||
|
window.stream.getTracks().forEach(s => s.stop());
|
||||||
|
video.srcObject = null;
|
||||||
|
|
||||||
|
span.classList.add("waiting-for-permission");
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt.onclick = () => {
|
||||||
|
navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: false,
|
||||||
|
video: {
|
||||||
|
facingMode: "environment",
|
||||||
|
},
|
||||||
|
}).then(function(stream) {
|
||||||
|
|
||||||
|
stream.onend = console.log
|
||||||
|
|
||||||
|
window.stream = stream
|
||||||
|
video.srcObject = stream
|
||||||
|
window.cameraConnected = true
|
||||||
|
video.controls = false
|
||||||
|
video.play()
|
||||||
|
video.controls = false
|
||||||
|
|
||||||
|
span.classList.remove("waiting-for-permission");
|
||||||
|
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.log(error)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
stop.onclick = () => {
|
||||||
|
clear_camera()
|
||||||
|
}
|
||||||
|
popout.onclick = () => {
|
||||||
|
span.classList.toggle("popped-out")
|
||||||
|
}
|
||||||
|
|
||||||
|
shutter.onclick = () => {
|
||||||
|
const cl = video.classList
|
||||||
|
cl.remove("takepicture")
|
||||||
|
void video.offsetHeight
|
||||||
|
cl.add("takepicture")
|
||||||
|
video.play()
|
||||||
|
video.controls = false
|
||||||
|
console.log(video)
|
||||||
|
send_source(video, video.videoWidth, video.videoHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.visibilityState != "visible") {
|
||||||
|
clear_camera()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// Set a default image
|
||||||
|
|
||||||
|
const img = html`<img crossOrigin="anonymous">`
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
console.log("helloo")
|
||||||
|
send_source(img, img.width, img.height)
|
||||||
|
}
|
||||||
|
img.src = "$(default_url)"
|
||||||
|
console.log(img)
|
||||||
|
</script>
|
||||||
|
</span>
|
||||||
|
""" |> HTML
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 9529bc40-e93c-11ea-2587-3186e0978476
|
||||||
|
@bind raw_camera_data camera_input(;max_size=2000)
|
||||||
|
|
||||||
|
# ╔═╡ 832ebd1a-e93c-11ea-1d18-d784f3184ebe
|
||||||
|
|
||||||
|
function process_raw_camera_data(raw_camera_data)
|
||||||
|
# the raw image data is a long byte array, we need to transform it into something
|
||||||
|
# more "Julian" - something with more _structure_.
|
||||||
|
|
||||||
|
# The encoding of the raw byte stream is:
|
||||||
|
# every 4 bytes is a single pixel
|
||||||
|
# every pixel has 4 values: Red, Green, Blue, Alpha
|
||||||
|
# (we ignore alpha for this notebook)
|
||||||
|
|
||||||
|
# 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])
|
||||||
|
|
||||||
|
# but these are still 1-dimensional arrays, nicknamed 'flat' arrays
|
||||||
|
# We will 'reshape' this into 2D arrays:
|
||||||
|
|
||||||
|
width = raw_camera_data["width"]
|
||||||
|
height = raw_camera_data["height"]
|
||||||
|
|
||||||
|
# shuffle and flip to get it in the right shape
|
||||||
|
reds = reshape(reds_flat, (width, height))' / 255.0
|
||||||
|
greens = reshape(greens_flat, (width, height))' / 255.0
|
||||||
|
blues = reshape(blues_flat, (width, height))' / 255.0
|
||||||
|
|
||||||
|
# we have our 2D array for each color
|
||||||
|
# Let's create a single 2D array, where each value contains the R, G and B value of
|
||||||
|
# that pixel
|
||||||
|
|
||||||
|
RGB.(reds, greens, blues)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 9a843af8-e93c-11ea-311b-1bc6d5b58492
|
||||||
|
grant = decimate(process_raw_camera_data(raw_camera_data), 2)
|
||||||
|
|
||||||
|
# ╔═╡ 6aa73286-ede7-11ea-232b-63e052222ecd
|
||||||
|
[
|
||||||
|
grant grant[:,end:-1:1]
|
||||||
|
grant[end:-1:1,:] grant[end:-1:1,end:-1:1]
|
||||||
|
]
|
||||||
|
|
||||||
|
# ╔═╡ Cell order:
|
||||||
|
# ╟─a50b5f48-e8d5-11ea-1f05-a3741b5d15ba
|
||||||
|
# ╟─8a6fed4c-e94b-11ea-1113-d56f56fb293b
|
||||||
|
# ╟─dc53f316-e8c8-11ea-150f-1374dbce114a
|
||||||
|
# ╟─c3f43d66-e94b-11ea-02bd-23cfeb878ff1
|
||||||
|
# ╟─c6c77738-e94b-11ea-22f5-1dce3dbcc3ca
|
||||||
|
# ╟─cf80793a-e94b-11ea-0120-f7913ae06f22
|
||||||
|
# ╟─d1638d96-e94b-11ea-2ff4-910e399f864d
|
||||||
|
# ╟─0117246a-e94c-11ea-1a76-c981ce8e725d
|
||||||
|
# ╟─27060098-e8c9-11ea-2fe0-03b39b1ddc32
|
||||||
|
# ╟─4fc58814-e94b-11ea-339b-cb714a63f9b6
|
||||||
|
# ╟─f067d3b8-e8c8-11ea-20cb-474709ffa99a
|
||||||
|
# ╠═37c1d012-ebc9-11ea-2dfe-8b86bb78f283
|
||||||
|
# ╟─a0a97214-e8d2-11ea-0f46-0bfaf016ab6d
|
||||||
|
# ╟─1697a756-e93d-11ea-0b6e-c9c78d527993
|
||||||
|
# ╟─af28faca-ebb7-11ea-130d-0f94bf9bd836
|
||||||
|
# ╠═9529bc40-e93c-11ea-2587-3186e0978476
|
||||||
|
# ╟─ee1d1596-e94a-11ea-0fb4-cd05f62471d3
|
||||||
|
# ╠═6aa73286-ede7-11ea-232b-63e052222ecd
|
||||||
|
# ╠═9a843af8-e93c-11ea-311b-1bc6d5b58492
|
||||||
|
# ╟─8ab9a978-e8c9-11ea-2476-f1ef4ba1b619
|
||||||
|
# ╟─38c54bfc-e8cb-11ea-3d52-0f02452f8ba1
|
||||||
|
# ╟─983f8270-e8c9-11ea-29d2-adeccb5a7ffc
|
||||||
|
# ╟─2fcaef88-e8ca-11ea-23f7-29c48580f43c
|
||||||
|
# ╟─7636c4b0-e8d1-11ea-2051-757a850a9d30
|
||||||
|
# ╟─bca22176-e8ca-11ea-2004-ebeb103116b5
|
||||||
|
# ╟─0ad91f1e-e8d2-11ea-2c18-93f66c906a8b
|
||||||
|
# ╠═de373816-ec79-11ea-2772-ebdca52246ac
|
||||||
|
# ╠═552129ae-ebca-11ea-1fa1-3f9fa00a2601
|
||||||
|
# ╠═54c1ba3c-e8d2-11ea-3564-bdaca8563738
|
||||||
|
# ╠═6e0fefb6-e8d4-11ea-1f9b-e7a3db40df39
|
||||||
|
# ╠═9c359212-ec79-11ea-2d7e-0124dad5f127
|
||||||
|
# ╠═7703b032-ebca-11ea-3074-0b80a077078e
|
||||||
|
# ╠═7eff3522-ebca-11ea-1a65-59e66a4e72ab
|
||||||
|
# ╠═c9cd6c04-ebca-11ea-0990-5fa19ff7ed97
|
||||||
|
# ╟─0d873d9c-e93b-11ea-2425-1bd79677fb97
|
||||||
|
# ╠═6b09354a-ebb9-11ea-2d5a-3b75c5ae7aa9
|
||||||
|
# ╟─2d6c434e-e93b-11ea-2678-3b9db4975089
|
||||||
|
# ╠═2b14e93e-e93b-11ea-25f1-5f565f80e778
|
||||||
|
# ╟─0bdc6058-e8d5-11ea-1889-3f706cea7a1f
|
||||||
|
# ╠═e61db924-ebca-11ea-2f79-f9f1c121b7f5
|
||||||
|
# ╠═ef60fcc4-ebca-11ea-3f69-155afffe8ea8
|
||||||
|
# ╠═fac550ec-ebca-11ea-337a-dbc16848c617
|
||||||
|
# ╟─42aa8cfe-e8d5-11ea-3cb9-c365b98e7a8c
|
||||||
|
# ╠═4eea5710-e8d5-11ea-3978-af66ee2a137e
|
||||||
|
# ╟─57b3a0c2-e8d5-11ea-15aa-8da4549f849b
|
||||||
|
# ╠═03a7c0fc-ebba-11ea-1c71-79d750c97b16
|
||||||
|
# ╟─e6fd68fa-e8d8-11ea-3dc4-274caceda222
|
||||||
|
# ╠═63a1d282-e8d5-11ea-0bba-b9cdd32a218b
|
||||||
|
# ╟─fc5e1af0-e8d8-11ea-1077-07216ff96d29
|
||||||
|
# ╟─c79dd836-e8e8-11ea-029d-57be9899979a
|
||||||
|
# ╠═ae260168-e932-11ea-38fd-4f2c6f43e21c
|
||||||
|
# ╠═47d1bc04-ebcb-11ea-3643-d1ba8dea57c8
|
||||||
|
# ╠═72400458-ebcb-11ea-26b6-678ae1de8e23
|
||||||
|
# ╟─f57ea7c2-e932-11ea-0d52-4112187bcb38
|
||||||
|
# ╠═740ed2e2-e933-11ea-236c-f3c3f09d0f8b
|
||||||
|
# ╟─6128a5ba-e93b-11ea-03f5-f170c7b90b25
|
||||||
|
# ╠═78eafe4e-e933-11ea-3539-c13feb894ef6
|
||||||
|
# ╟─bf3f9050-e933-11ea-0df7-e5dcff6bb3ee
|
||||||
|
# ╟─212e1f12-e934-11ea-2f35-51c7a6c8dff1
|
||||||
|
# ╠═117a98c0-e936-11ea-3aac-8f66337cea68
|
||||||
|
# ╟─8004d076-e93b-11ea-29cc-a1bfcc75e87f
|
||||||
|
# ╠═3ac63296-e936-11ea-2144-f94bdbd60eaf
|
||||||
|
# ╠═3e3f841a-e936-11ea-0a81-1b95fe0faa83
|
||||||
|
# ╟─5978db50-e936-11ea-3145-059a51be2281
|
||||||
|
# ╠═21638b14-ebcc-11ea-1761-bbd2f4306a96
|
||||||
|
# ╟─70cb0e36-e936-11ea-3ade-49fde77cb696
|
||||||
|
# ╠═b3ea975e-e936-11ea-067d-81339575a3cb
|
||||||
|
# ╟─918a0762-e93b-11ea-1115-71dbfdb03f27
|
||||||
|
# ╠═daabe66c-e937-11ea-3bc3-d77f2bce406c
|
||||||
|
# ╟─095ced62-e938-11ea-1169-939dc7136fd0
|
||||||
|
# ╠═31f3605a-e938-11ea-3a6d-29a185bbee31
|
||||||
|
# ╠═2744a556-e94f-11ea-2434-d53c24e59285
|
||||||
|
# ╟─98412a36-e93b-11ea-1954-f1c105c6ed4a
|
||||||
|
# ╠═3c32efde-e938-11ea-1ae4-5d88290f5311
|
||||||
|
# ╟─4b26e4e6-e938-11ea-2635-6d4fc15e13b7
|
||||||
|
# ╠═41fa85c0-e939-11ea-1ad8-79805a2083bb
|
||||||
|
# ╟─c12e0928-e93b-11ea-0922-2b590a99ee89
|
||||||
|
# ╟─ff5dc538-e938-11ea-058f-693d6b016640
|
||||||
|
# ╠═fbe11200-e938-11ea-12e9-6125c1b56b25
|
||||||
|
# ╟─fa24f4a8-e93b-11ea-06bd-25c9672166d6
|
||||||
|
# ╠═15ce202e-e939-11ea-2387-93be0ec4cf1f
|
||||||
|
# ╠═cd5721d0-ede6-11ea-0918-1992c69bccc6
|
||||||
|
# ╟─bf2167a4-e93d-11ea-03b2-cdd24b459ba9
|
||||||
|
# ╟─5e688928-e939-11ea-0e16-fbc80af390ab
|
||||||
|
# ╟─58184d88-e939-11ea-2fc8-73b3476ebe92
|
||||||
|
# ╟─2dd09f16-e93a-11ea-2cdc-13f558e3391d
|
||||||
|
# ╟─df1b7996-e93b-11ea-1a3a-81b4ec520679
|
||||||
|
# ╟─b8daeea0-ec79-11ea-34b5-3f13e8a56a42
|
||||||
|
# ╟─bf1bb2c8-ec79-11ea-0671-3ffb34828f3c
|
||||||
|
# ╟─69e3aa82-e93c-11ea-23fe-c1103d989cba
|
||||||
|
# ╟─739c3bb6-e93c-11ea-127b-efb6a8ab9379
|
||||||
|
# ╟─832ebd1a-e93c-11ea-1d18-d784f3184ebe
|
||||||
256
Wonderful invention.jl
Normal file
256
Wonderful invention.jl
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
### A Pluto.jl notebook ###
|
||||||
|
# v0.12.11
|
||||||
|
|
||||||
|
using Markdown
|
||||||
|
using InteractiveUtils
|
||||||
|
|
||||||
|
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
|
||||||
|
macro bind(def, element)
|
||||||
|
quote
|
||||||
|
local el = $(esc(element))
|
||||||
|
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
|
||||||
|
el
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 15a4ba3e-f0d1-11ea-2ef1-5ff1dee8795f
|
||||||
|
using Pkg
|
||||||
|
|
||||||
|
# ╔═╡ 21e744b8-f0d1-11ea-2e09-7ffbcdf43c37
|
||||||
|
begin
|
||||||
|
Pkg.add("Gadfly")
|
||||||
|
Pkg.add("Compose")
|
||||||
|
Pkg.add("Statistics")
|
||||||
|
Pkg.add("Hyperscript")
|
||||||
|
Pkg.add("Colors")
|
||||||
|
Pkg.add("Images")
|
||||||
|
Pkg.add("ImageMagick")
|
||||||
|
Pkg.add("ImageFiltering")
|
||||||
|
|
||||||
|
using Gadfly
|
||||||
|
using Images
|
||||||
|
using Compose
|
||||||
|
using Hyperscript
|
||||||
|
using Colors
|
||||||
|
using Statistics
|
||||||
|
using PlutoUI
|
||||||
|
using ImageMagick
|
||||||
|
using ImageFiltering
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 1ab1c808-f0d1-11ea-03a7-e9854427d45f
|
||||||
|
Pkg.activate(mktempdir())
|
||||||
|
|
||||||
|
# ╔═╡ 10f850fc-f0d1-11ea-2a58-2326a9ea1e2a
|
||||||
|
set_default_plot_size(12cm, 12cm)
|
||||||
|
|
||||||
|
# ╔═╡ 7b4d5270-f0d3-11ea-0b48-79005f20602c
|
||||||
|
function convolve(M, kernel)
|
||||||
|
height, width = size(kernel)
|
||||||
|
|
||||||
|
half_height = height ÷ 2
|
||||||
|
half_width = width ÷ 2
|
||||||
|
|
||||||
|
new_image = similar(M)
|
||||||
|
|
||||||
|
# (i, j) loop over the original image
|
||||||
|
m, n = size(M)
|
||||||
|
@inbounds for i in 1:m
|
||||||
|
for j in 1:n
|
||||||
|
# (k, l) loop over the neighbouring pixels
|
||||||
|
accumulator = 0 * M[1, 1]
|
||||||
|
for k in -half_height:-half_height + height - 1
|
||||||
|
for l in -half_width:-half_width + width - 1
|
||||||
|
Mi = i - k
|
||||||
|
Mj = j - l
|
||||||
|
# First index into M
|
||||||
|
if Mi < 1
|
||||||
|
Mi = 1
|
||||||
|
elseif Mi > m
|
||||||
|
Mi = m
|
||||||
|
end
|
||||||
|
# Second index into M
|
||||||
|
if Mj < 1
|
||||||
|
Mj = 1
|
||||||
|
elseif Mj > n
|
||||||
|
Mj = n
|
||||||
|
end
|
||||||
|
|
||||||
|
accumulator += kernel[k, l] * M[Mi, Mj]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
new_image[i, j] = accumulator
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return new_image
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 6fd3b7a4-f0d3-11ea-1f26-fb9740cd16e0
|
||||||
|
function disc(n, r1=0.8, r2=0.8)
|
||||||
|
white = RGB{Float64}(1,1,1)
|
||||||
|
blue = RGB{Float64}(colorant"#4EC0E3")
|
||||||
|
convolve(
|
||||||
|
[(i-n/2)^2 + (j-n/2)^2 <= (n/2-5)^2 ? white : blue for i=1:n, j=1:n],
|
||||||
|
Kernel.gaussian((1,1))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ fe3559e0-f13b-11ea-06c8-a314e44c20d6
|
||||||
|
brightness(c) = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b
|
||||||
|
|
||||||
|
# ╔═╡ 0ccf76e4-f0d9-11ea-07c9-0159e3d4d733
|
||||||
|
@bind img_select Radio(["disc", "mario"], default="disc")
|
||||||
|
|
||||||
|
# ╔═╡ 236dab08-f13d-11ea-1922-a3b82cfc7f51
|
||||||
|
begin
|
||||||
|
url = "http://files.softicons.com/download/game-icons/super-mario-icons-by-sandro-pereira/png/32/Retro%20Mario.png"
|
||||||
|
img = Dict(
|
||||||
|
"disc" => disc(25),
|
||||||
|
"mario" => load(download(url))
|
||||||
|
)[img_select]
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 03434682-f13b-11ea-2b6e-11ad781e9a51
|
||||||
|
md"""Show $G_x$ $(@bind Gx CheckBox())
|
||||||
|
|
||||||
|
Show $G_y$ $(@bind Gy CheckBox())"""
|
||||||
|
|
||||||
|
# ╔═╡ ca13597a-f168-11ea-1a2c-ff7b98b7b2c7
|
||||||
|
function partial_derivatives(img)
|
||||||
|
Sy,Sx = Kernel.sobel()
|
||||||
|
∇x, ∇y = zeros(size(img)), zeros(size(img))
|
||||||
|
|
||||||
|
if Gx
|
||||||
|
∇x = convolve(brightness.(img), Sx)
|
||||||
|
end
|
||||||
|
if Gy
|
||||||
|
∇y = convolve(brightness.(img), Sy)
|
||||||
|
end
|
||||||
|
return ∇x, ∇y
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ b369584c-f183-11ea-260a-35dc797e63ad
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ b2cbe058-f183-11ea-39dc-23d4a5b92796
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 9d9cccb2-f118-11ea-1638-c76682e636b2
|
||||||
|
function arrowhead(θ)
|
||||||
|
eq_triangle = [(0, 1/sqrt(3)),
|
||||||
|
(-1/3, -2/(2 * sqrt(3))),
|
||||||
|
(1/3, -2/(2 * sqrt(3)))]
|
||||||
|
|
||||||
|
compose(context(units=UnitBox(-1,-1,2,2), rotation=Rotation(θ, 0, 0)),
|
||||||
|
polygon(eq_triangle))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ b7ea8a28-f0d7-11ea-3e98-7b19a1f58304
|
||||||
|
function quiver(points, vecs)
|
||||||
|
xmin = minimum(first.(points))
|
||||||
|
ymin = minimum(last.(points))
|
||||||
|
xmax = maximum(first.(points))
|
||||||
|
ymax = maximum(last.(points))
|
||||||
|
hs = map(x->hypot(x...), vecs)
|
||||||
|
hs = hs / maximum(hs)
|
||||||
|
|
||||||
|
vector(p, v, h) = all(iszero, v) ? context() :
|
||||||
|
(context(),
|
||||||
|
(context((p.+v.*6 .- .2)..., .4,.4),
|
||||||
|
arrowhead(atan(v[2], v[1]) - pi/2)),
|
||||||
|
stroke(RGBA(90/255,39/255,41/255,h)),
|
||||||
|
fill(RGBA(90/255,39/255,41/255,h)),
|
||||||
|
line([p, p.+v.*8]))
|
||||||
|
|
||||||
|
compose(context(units=UnitBox(xmin,ymin,xmax,ymax)),
|
||||||
|
vector.(points, vecs, hs)...)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ c821b906-f0d8-11ea-2df0-8f2d06964aa2
|
||||||
|
function sobel_quiver(img, ∇x, ∇y)
|
||||||
|
quiver([(j-1,i-1) for i=1:size(img,1), j=1:size(img,2)],
|
||||||
|
[(∇x[i,j], ∇y[i,j]) for i=1:size(img,1), j=1:size(img,2)])
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 6da3fdfe-f0dd-11ea-2407-7b85217b35cc
|
||||||
|
# render an Image using squares in Compose
|
||||||
|
function compimg(img)
|
||||||
|
xmax, ymax = size(img)
|
||||||
|
xmin, ymin = 0, 0
|
||||||
|
arr = [(j-1, i-1) for i=1:ymax, j=1:xmax]
|
||||||
|
|
||||||
|
compose(context(units=UnitBox(xmin, ymin, xmax, ymax)),
|
||||||
|
fill(vec(img)),
|
||||||
|
rectangle(
|
||||||
|
first.(arr),
|
||||||
|
last.(arr),
|
||||||
|
fill(1.0, length(arr)),
|
||||||
|
fill(1.0, length(arr))))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ f22aa34e-f0df-11ea-3053-3dcdc070ec2f
|
||||||
|
let
|
||||||
|
∇x, ∇y = partial_derivatives(img)
|
||||||
|
|
||||||
|
compose(context(),
|
||||||
|
sobel_quiver(img, ∇x, ∇y),
|
||||||
|
compimg(img))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 885ec336-f146-11ea-00c4-c1d1ab4c0001
|
||||||
|
function show_colored_array(array)
|
||||||
|
pos_color = RGB(0.36, 0.82, 0.8)
|
||||||
|
neg_color = RGB(0.99, 0.18, 0.13)
|
||||||
|
to_rgb(x) = max(x, 0) * pos_color + max(-x, 0) * neg_color
|
||||||
|
to_rgb.(array) / maximum(abs.(array))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 9232dcc8-f188-11ea-08fe-b787ea93c598
|
||||||
|
begin
|
||||||
|
Sy, Sx = Kernel.sobel()
|
||||||
|
show_colored_array(Sx)
|
||||||
|
Sx
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 7864bd00-f146-11ea-0020-7fccb3913d8b
|
||||||
|
let
|
||||||
|
∇x, ∇y = partial_derivatives(img)
|
||||||
|
|
||||||
|
to_show = (x -> RGB(0, 0, 0)).(zeros(size(img)))
|
||||||
|
if Gx && Gy
|
||||||
|
edged = sqrt.(∇x.^2 + ∇y.^2)
|
||||||
|
to_show = Gray.(edged) / maximum(edged)
|
||||||
|
elseif Gx
|
||||||
|
to_show = show_colored_array(∇x)
|
||||||
|
elseif Gy
|
||||||
|
to_show = show_colored_array(∇y)
|
||||||
|
end
|
||||||
|
compose(
|
||||||
|
context(),
|
||||||
|
compimg(to_show)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ Cell order:
|
||||||
|
# ╠═15a4ba3e-f0d1-11ea-2ef1-5ff1dee8795f
|
||||||
|
# ╠═1ab1c808-f0d1-11ea-03a7-e9854427d45f
|
||||||
|
# ╠═21e744b8-f0d1-11ea-2e09-7ffbcdf43c37
|
||||||
|
# ╠═10f850fc-f0d1-11ea-2a58-2326a9ea1e2a
|
||||||
|
# ╟─7b4d5270-f0d3-11ea-0b48-79005f20602c
|
||||||
|
# ╠═6fd3b7a4-f0d3-11ea-1f26-fb9740cd16e0
|
||||||
|
# ╠═fe3559e0-f13b-11ea-06c8-a314e44c20d6
|
||||||
|
# ╟─b7ea8a28-f0d7-11ea-3e98-7b19a1f58304
|
||||||
|
# ╟─0ccf76e4-f0d9-11ea-07c9-0159e3d4d733
|
||||||
|
# ╟─236dab08-f13d-11ea-1922-a3b82cfc7f51
|
||||||
|
# ╟─03434682-f13b-11ea-2b6e-11ad781e9a51
|
||||||
|
# ╠═ca13597a-f168-11ea-1a2c-ff7b98b7b2c7
|
||||||
|
# ╟─f22aa34e-f0df-11ea-3053-3dcdc070ec2f
|
||||||
|
# ╟─9232dcc8-f188-11ea-08fe-b787ea93c598
|
||||||
|
# ╠═7864bd00-f146-11ea-0020-7fccb3913d8b
|
||||||
|
# ╠═b369584c-f183-11ea-260a-35dc797e63ad
|
||||||
|
# ╠═b2cbe058-f183-11ea-39dc-23d4a5b92796
|
||||||
|
# ╟─9d9cccb2-f118-11ea-1638-c76682e636b2
|
||||||
|
# ╟─c821b906-f0d8-11ea-2df0-8f2d06964aa2
|
||||||
|
# ╟─6da3fdfe-f0dd-11ea-2407-7b85217b35cc
|
||||||
|
# ╠═885ec336-f146-11ea-00c4-c1d1ab4c0001
|
||||||
256
gradient.jl
Normal file
256
gradient.jl
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
### A Pluto.jl notebook ###
|
||||||
|
# v0.11.10
|
||||||
|
|
||||||
|
using Markdown
|
||||||
|
using InteractiveUtils
|
||||||
|
|
||||||
|
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
|
||||||
|
macro bind(def, element)
|
||||||
|
quote
|
||||||
|
local el = $(esc(element))
|
||||||
|
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
|
||||||
|
el
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 15a4ba3e-f0d1-11ea-2ef1-5ff1dee8795f
|
||||||
|
using Pkg
|
||||||
|
|
||||||
|
# ╔═╡ 21e744b8-f0d1-11ea-2e09-7ffbcdf43c37
|
||||||
|
begin
|
||||||
|
Pkg.add("Gadfly")
|
||||||
|
Pkg.add("Compose")
|
||||||
|
Pkg.add("Statistics")
|
||||||
|
Pkg.add("Hyperscript")
|
||||||
|
Pkg.add("Colors")
|
||||||
|
Pkg.add("Images")
|
||||||
|
Pkg.add("ImageMagick")
|
||||||
|
Pkg.add("ImageFiltering")
|
||||||
|
|
||||||
|
using Gadfly
|
||||||
|
using Images
|
||||||
|
using Compose
|
||||||
|
using Hyperscript
|
||||||
|
using Colors
|
||||||
|
using Statistics
|
||||||
|
using PlutoUI
|
||||||
|
using ImageMagick
|
||||||
|
using ImageFiltering
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 1ab1c808-f0d1-11ea-03a7-e9854427d45f
|
||||||
|
Pkg.activate(mktempdir())
|
||||||
|
|
||||||
|
# ╔═╡ 10f850fc-f0d1-11ea-2a58-2326a9ea1e2a
|
||||||
|
set_default_plot_size(12cm, 12cm)
|
||||||
|
|
||||||
|
# ╔═╡ 7b4d5270-f0d3-11ea-0b48-79005f20602c
|
||||||
|
function convolve(M, kernel)
|
||||||
|
height, width = size(kernel)
|
||||||
|
|
||||||
|
half_height = height ÷ 2
|
||||||
|
half_width = width ÷ 2
|
||||||
|
|
||||||
|
new_image = similar(M)
|
||||||
|
|
||||||
|
# (i, j) loop over the original image
|
||||||
|
m, n = size(M)
|
||||||
|
@inbounds for i in 1:m
|
||||||
|
for j in 1:n
|
||||||
|
# (k, l) loop over the neighbouring pixels
|
||||||
|
accumulator = 0 * M[1, 1]
|
||||||
|
for k in -half_height:-half_height + height - 1
|
||||||
|
for l in -half_width:-half_width + width - 1
|
||||||
|
Mi = i - k
|
||||||
|
Mj = j - l
|
||||||
|
# First index into M
|
||||||
|
if Mi < 1
|
||||||
|
Mi = 1
|
||||||
|
elseif Mi > m
|
||||||
|
Mi = m
|
||||||
|
end
|
||||||
|
# Second index into M
|
||||||
|
if Mj < 1
|
||||||
|
Mj = 1
|
||||||
|
elseif Mj > n
|
||||||
|
Mj = n
|
||||||
|
end
|
||||||
|
|
||||||
|
accumulator += kernel[k, l] * M[Mi, Mj]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
new_image[i, j] = accumulator
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return new_image
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 6fd3b7a4-f0d3-11ea-1f26-fb9740cd16e0
|
||||||
|
function disc(n, r1=0.8, r2=0.8)
|
||||||
|
white = RGB{Float64}(1,1,1)
|
||||||
|
blue = RGB{Float64}(colorant"#4EC0E3")
|
||||||
|
convolve(
|
||||||
|
[(i-n/2)^2 + (j-n/2)^2 <= (n/2-5)^2 ? white : blue for i=1:n, j=1:n],
|
||||||
|
Kernel.gaussian((1,1))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ fe3559e0-f13b-11ea-06c8-a314e44c20d6
|
||||||
|
brightness(c) = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b
|
||||||
|
|
||||||
|
# ╔═╡ 0ccf76e4-f0d9-11ea-07c9-0159e3d4d733
|
||||||
|
@bind img_select Radio(["disc", "mario"], default="disc")
|
||||||
|
|
||||||
|
# ╔═╡ 236dab08-f13d-11ea-1922-a3b82cfc7f51
|
||||||
|
begin
|
||||||
|
url = "http://files.softicons.com/download/game-icons/super-mario-icons-by-sandro-pereira/png/32/Retro%20Mario.png"
|
||||||
|
img = Dict(
|
||||||
|
"disc" => disc(25),
|
||||||
|
"mario" => load(download(url))
|
||||||
|
)[img_select]
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 03434682-f13b-11ea-2b6e-11ad781e9a51
|
||||||
|
md"""Show $G_x$ $(@bind Gx CheckBox())
|
||||||
|
|
||||||
|
Show $G_y$ $(@bind Gy CheckBox())"""
|
||||||
|
|
||||||
|
# ╔═╡ ca13597a-f168-11ea-1a2c-ff7b98b7b2c7
|
||||||
|
function partial_derivatives(img)
|
||||||
|
Sy,Sx = Kernel.sobel()
|
||||||
|
∇x, ∇y = zeros(size(img)), zeros(size(img))
|
||||||
|
|
||||||
|
if Gx
|
||||||
|
∇x = convolve(brightness.(img), Sx)
|
||||||
|
end
|
||||||
|
if Gy
|
||||||
|
∇y = convolve(brightness.(img), Sy)
|
||||||
|
end
|
||||||
|
return ∇x, ∇y
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ b369584c-f183-11ea-260a-35dc797e63ad
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ b2cbe058-f183-11ea-39dc-23d4a5b92796
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 9d9cccb2-f118-11ea-1638-c76682e636b2
|
||||||
|
function arrowhead(θ)
|
||||||
|
eq_triangle = [(0, 1/sqrt(3)),
|
||||||
|
(-1/3, -2/(2 * sqrt(3))),
|
||||||
|
(1/3, -2/(2 * sqrt(3)))]
|
||||||
|
|
||||||
|
compose(context(units=UnitBox(-1,-1,2,2), rotation=Rotation(θ, 0, 0)),
|
||||||
|
polygon(eq_triangle))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ b7ea8a28-f0d7-11ea-3e98-7b19a1f58304
|
||||||
|
function quiver(points, vecs)
|
||||||
|
xmin = minimum(first.(points))
|
||||||
|
ymin = minimum(last.(points))
|
||||||
|
xmax = maximum(first.(points))
|
||||||
|
ymax = maximum(last.(points))
|
||||||
|
hs = map(x->hypot(x...), vecs)
|
||||||
|
hs = hs / maximum(hs)
|
||||||
|
|
||||||
|
vector(p, v, h) = all(iszero, v) ? context() :
|
||||||
|
(context(),
|
||||||
|
(context((p.+v.*6 .- .2)..., .4,.4),
|
||||||
|
arrowhead(atan(v[2], v[1]) - pi/2)),
|
||||||
|
stroke(RGBA(90/255,39/255,41/255,h)),
|
||||||
|
fill(RGBA(90/255,39/255,41/255,h)),
|
||||||
|
line([p, p.+v.*8]))
|
||||||
|
|
||||||
|
compose(context(units=UnitBox(xmin,ymin,xmax,ymax)),
|
||||||
|
vector.(points, vecs, hs)...)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ c821b906-f0d8-11ea-2df0-8f2d06964aa2
|
||||||
|
function sobel_quiver(img, ∇x, ∇y)
|
||||||
|
quiver([(j-1,i-1) for i=1:size(img,1), j=1:size(img,2)],
|
||||||
|
[(∇x[i,j], ∇y[i,j]) for i=1:size(img,1), j=1:size(img,2)])
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 6da3fdfe-f0dd-11ea-2407-7b85217b35cc
|
||||||
|
# render an Image using squares in Compose
|
||||||
|
function compimg(img)
|
||||||
|
xmax, ymax = size(img)
|
||||||
|
xmin, ymin = 0, 0
|
||||||
|
arr = [(j-1, i-1) for i=1:ymax, j=1:xmax]
|
||||||
|
|
||||||
|
compose(context(units=UnitBox(xmin, ymin, xmax, ymax)),
|
||||||
|
fill(vec(img)),
|
||||||
|
rectangle(
|
||||||
|
first.(arr),
|
||||||
|
last.(arr),
|
||||||
|
fill(1.0, length(arr)),
|
||||||
|
fill(1.0, length(arr))))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ f22aa34e-f0df-11ea-3053-3dcdc070ec2f
|
||||||
|
let
|
||||||
|
∇x, ∇y = partial_derivatives(img)
|
||||||
|
|
||||||
|
compose(context(),
|
||||||
|
sobel_quiver(img, ∇x, ∇y),
|
||||||
|
compimg(img))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 885ec336-f146-11ea-00c4-c1d1ab4c0001
|
||||||
|
function show_colored_array(array)
|
||||||
|
pos_color = RGB(0.36, 0.82, 0.8)
|
||||||
|
neg_color = RGB(0.99, 0.18, 0.13)
|
||||||
|
to_rgb(x) = max(x, 0) * pos_color + max(-x, 0) * neg_color
|
||||||
|
to_rgb.(array) / maximum(abs.(array))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 9232dcc8-f188-11ea-08fe-b787ea93c598
|
||||||
|
begin
|
||||||
|
Sy, Sx = Kernel.sobel()
|
||||||
|
show_colored_array(Sx)
|
||||||
|
Sx
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 7864bd00-f146-11ea-0020-7fccb3913d8b
|
||||||
|
let
|
||||||
|
∇x, ∇y = partial_derivatives(img)
|
||||||
|
|
||||||
|
to_show = (x -> RGB(0, 0, 0)).(zeros(size(img)))
|
||||||
|
if Gx && Gy
|
||||||
|
edged = sqrt.(∇x.^2 + ∇y.^2)
|
||||||
|
to_show = Gray.(edged) / maximum(edged)
|
||||||
|
elseif Gx
|
||||||
|
to_show = show_colored_array(∇x)
|
||||||
|
elseif Gy
|
||||||
|
to_show = show_colored_array(∇y)
|
||||||
|
end
|
||||||
|
compose(
|
||||||
|
context(),
|
||||||
|
compimg(to_show)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ Cell order:
|
||||||
|
# ╠═15a4ba3e-f0d1-11ea-2ef1-5ff1dee8795f
|
||||||
|
# ╠═1ab1c808-f0d1-11ea-03a7-e9854427d45f
|
||||||
|
# ╟─21e744b8-f0d1-11ea-2e09-7ffbcdf43c37
|
||||||
|
# ╠═10f850fc-f0d1-11ea-2a58-2326a9ea1e2a
|
||||||
|
# ╟─7b4d5270-f0d3-11ea-0b48-79005f20602c
|
||||||
|
# ╠═6fd3b7a4-f0d3-11ea-1f26-fb9740cd16e0
|
||||||
|
# ╟─fe3559e0-f13b-11ea-06c8-a314e44c20d6
|
||||||
|
# ╟─b7ea8a28-f0d7-11ea-3e98-7b19a1f58304
|
||||||
|
# ╟─0ccf76e4-f0d9-11ea-07c9-0159e3d4d733
|
||||||
|
# ╟─236dab08-f13d-11ea-1922-a3b82cfc7f51
|
||||||
|
# ╟─03434682-f13b-11ea-2b6e-11ad781e9a51
|
||||||
|
# ╟─ca13597a-f168-11ea-1a2c-ff7b98b7b2c7
|
||||||
|
# ╟─f22aa34e-f0df-11ea-3053-3dcdc070ec2f
|
||||||
|
# ╟─9232dcc8-f188-11ea-08fe-b787ea93c598
|
||||||
|
# ╠═7864bd00-f146-11ea-0020-7fccb3913d8b
|
||||||
|
# ╠═b369584c-f183-11ea-260a-35dc797e63ad
|
||||||
|
# ╠═b2cbe058-f183-11ea-39dc-23d4a5b92796
|
||||||
|
# ╟─9d9cccb2-f118-11ea-1638-c76682e636b2
|
||||||
|
# ╟─c821b906-f0d8-11ea-2df0-8f2d06964aa2
|
||||||
|
# ╟─6da3fdfe-f0dd-11ea-2407-7b85217b35cc
|
||||||
|
# ╠═885ec336-f146-11ea-00c4-c1d1ab4c0001
|
||||||
359
hw0.jl
Normal file
359
hw0.jl
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
### A Pluto.jl notebook ###
|
||||||
|
# v0.12.11
|
||||||
|
|
||||||
|
using Markdown
|
||||||
|
using InteractiveUtils
|
||||||
|
|
||||||
|
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
|
||||||
|
macro bind(def, element)
|
||||||
|
quote
|
||||||
|
local el = $(esc(element))
|
||||||
|
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
|
||||||
|
el
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ fafae38e-e852-11ea-1208-732b4744e4c2
|
||||||
|
md"_homework 0, version 2_"
|
||||||
|
|
||||||
|
# ╔═╡ 7308bc54-e6cd-11ea-0eab-83f7535edf25
|
||||||
|
# edit the code below to set your name and kerberos ID (i.e. email without @mit.edu)
|
||||||
|
|
||||||
|
student = (name = "Jazzy Doe", kerberos_id = "jazz")
|
||||||
|
|
||||||
|
# press the ▶ button in the bottom right of this cell to run your edits
|
||||||
|
# or use Shift+Enter
|
||||||
|
|
||||||
|
# you might need to wait until all other cells in this notebook have completed running.
|
||||||
|
# scroll down the page to see what's up
|
||||||
|
|
||||||
|
# ╔═╡ cdff6730-e785-11ea-2546-4969521b33a7
|
||||||
|
md"""
|
||||||
|
|
||||||
|
Submission by: **_$(student.name)_** ($(student.kerberos_id)@mit.edu)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ a2181260-e6cd-11ea-2a69-8d9d31d1ef0e
|
||||||
|
md"""
|
||||||
|
# Homework 0: Getting up and running
|
||||||
|
|
||||||
|
First of all, **_welcome to the course!_** We are excited to teach you about real world applications of scientific computing, using the same tools that we work with ourselves.
|
||||||
|
|
||||||
|
Before we start next week, we'd like everyone to **submit this zeroth homework assignment**. It will not affect your grade, but it will help us get everything running smoothly when the course starts. If you're stuck or don't have much time, just fill in your name and ID and submit 🙂
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 094e39c8-e6ce-11ea-131b-07c4a1199edf
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 31a8fbf8-e6ce-11ea-2c66-4b4d02b41995
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 339c2d5c-e6ce-11ea-32f9-714b3628909c
|
||||||
|
md"## Exercise 1 - _Square root by Newton's method_
|
||||||
|
|
||||||
|
Computing the square of a number is easy -- you just multiply it with itself.
|
||||||
|
|
||||||
|
But how does one compute the square root of a number?
|
||||||
|
|
||||||
|
##### Algorithm:
|
||||||
|
|
||||||
|
Given: $x$
|
||||||
|
|
||||||
|
Output: $\sqrt{x}$
|
||||||
|
|
||||||
|
1. Take a guess `a`
|
||||||
|
1. Divide `x` by `a`
|
||||||
|
1. Set a = the average of `x/a` and `a`. (The square root must be between these two numbers. Why?)
|
||||||
|
1. Repeat until `x/a` is roughly equal to `a`. Return `a` as the square root.
|
||||||
|
|
||||||
|
In general, you will never get to the point where `x/a` is _exactly_ equal to `a`. So if our algorithm keeps going until `x/a == a`, then it will get stuck.
|
||||||
|
|
||||||
|
So instead, the algorithm takes a parameter `error_margin`, which is used to decide when `x/a` and `a` are close enough to halt.
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 56866718-e6ce-11ea-0804-d108af4e5653
|
||||||
|
md"### Exercise 1.1
|
||||||
|
|
||||||
|
Step 3 in the algorithm sets the new guess to be the average of `x/a` and the old guess `a`.
|
||||||
|
|
||||||
|
This is because the square root must be between the numbers `x/a` and `a`. Why?
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ bccf0e88-e754-11ea-3ab8-0170c2d44628
|
||||||
|
ex_1_1 = md"""
|
||||||
|
your answer here
|
||||||
|
"""
|
||||||
|
|
||||||
|
# you might need to wait until all other cells in this notebook have completed running.
|
||||||
|
# scroll down the page to see what's up
|
||||||
|
|
||||||
|
# ╔═╡ e7abd366-e7a6-11ea-30d7-1b6194614d0a
|
||||||
|
if !(@isdefined ex_1_1)
|
||||||
|
md"""Do not change the name of the variable - write you answer as `ex_1_1 = "..."`"""
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ d62f223c-e754-11ea-2470-e72a605a9d7e
|
||||||
|
md"### Exercise 1.2
|
||||||
|
|
||||||
|
Write a function newton_sqrt(x) which implements the above algorithm."
|
||||||
|
|
||||||
|
# ╔═╡ 4896bf0c-e754-11ea-19dc-1380bb356ab6
|
||||||
|
function newton_sqrt(x, error_margin=0.01, a=x / 2) # a=x/2 is the default value of `a`
|
||||||
|
while true
|
||||||
|
xa = x / a
|
||||||
|
if (abs(a - xa) < error_margin)
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
a = (xa + a) / 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 7a01a508-e78a-11ea-11da-999d38785348
|
||||||
|
newton_sqrt(2)
|
||||||
|
|
||||||
|
# ╔═╡ 682db9f8-e7b1-11ea-3949-6b683ca8b47b
|
||||||
|
let
|
||||||
|
result = newton_sqrt(2, 0.01)
|
||||||
|
if !(result isa Number)
|
||||||
|
md"""
|
||||||
|
!!! warning "Not a number"
|
||||||
|
`newton_sqrt` did not return a number. Did you forget to write `return`?
|
||||||
|
"""
|
||||||
|
elseif abs(result - sqrt(2)) < 0.01
|
||||||
|
md"""
|
||||||
|
!!! correct
|
||||||
|
Well done!
|
||||||
|
"""
|
||||||
|
else
|
||||||
|
md"""
|
||||||
|
!!! warning "Incorrect"
|
||||||
|
Keep working on it!
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 088cc652-e7a8-11ea-0ca7-f744f6f3afdd
|
||||||
|
md"""
|
||||||
|
!!! hint
|
||||||
|
`abs(r - s)` is the distance between `r` and `s`
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ c18dce7a-e7a7-11ea-0a1a-f944d46754e5
|
||||||
|
md"""
|
||||||
|
!!! hint
|
||||||
|
If you're stuck, feel free to cheat, this is homework 0 after all 🙃
|
||||||
|
|
||||||
|
Julia has a function called `sqrt`
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 5e24d95c-e6ce-11ea-24be-bb19e1e14657
|
||||||
|
md"## Exercise 2 - _Sierpinksi's triangle_
|
||||||
|
|
||||||
|
Sierpinski's triangle is defined _recursively_:
|
||||||
|
|
||||||
|
- Sierpinski's triangle of complexity N is a figure in the form of a triangle which is made of 3 triangular figures which are themselves Sierpinski's triangles of complexity N-1.
|
||||||
|
|
||||||
|
- A Sierpinski's triangle of complexity 0 is a simple solid equilateral triangle
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 6b8883f6-e7b3-11ea-155e-6f62117e123b
|
||||||
|
md"To draw Sierpinski's triangle, we are going to use an external package, [_Compose.jl_](https://giovineitalia.github.io/Compose.jl/latest/tutorial). Let's set up a package environment and add the package.
|
||||||
|
|
||||||
|
A package contains a coherent set of functionality that you can often use a black box according to its specification. There are [lots of Julia packages](https://juliahub.com/ui/Home).
|
||||||
|
"
|
||||||
|
|
||||||
|
# ╔═╡ 851c03a4-e7a4-11ea-1652-d59b7a6599f0
|
||||||
|
# setting up an empty package environment
|
||||||
|
begin
|
||||||
|
import Pkg
|
||||||
|
Pkg.activate(mktempdir())
|
||||||
|
Pkg.Registry.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ d6ee91ea-e750-11ea-1260-31ebf3ec6a9b
|
||||||
|
# add (ie install) a package to our environment
|
||||||
|
begin
|
||||||
|
Pkg.add("Compose")
|
||||||
|
# call `using` so that we can use it in our code
|
||||||
|
using Compose
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 5acd58e0-e856-11ea-2d3d-8329889fe16f
|
||||||
|
begin
|
||||||
|
Pkg.add("PlutoUI")
|
||||||
|
using PlutoUI
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ dbc4da6a-e7b4-11ea-3b70-6f2abfcab992
|
||||||
|
md"Just like the definition above, our `sierpinksi` function is _recursive_: it calls itself."
|
||||||
|
|
||||||
|
# ╔═╡ 02b9c9d6-e752-11ea-0f32-91b7b6481684
|
||||||
|
complexity = 5
|
||||||
|
|
||||||
|
# ╔═╡ 1eb79812-e7b5-11ea-1c10-63b24803dd8a
|
||||||
|
if complexity == 3
|
||||||
|
md"""
|
||||||
|
Try changing the value of **`complexity` to `5`** in the cell above.
|
||||||
|
|
||||||
|
Hit `Shift+Enter` to affect the change.
|
||||||
|
"""
|
||||||
|
else
|
||||||
|
md"""
|
||||||
|
**Great!** As you can see, all the cells in this notebook are linked together by the variables they define and use. Just like a spreadsheet!
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ d7e8202c-e7b5-11ea-30d3-adcd6867d5f5
|
||||||
|
md"### Exercise 2.1
|
||||||
|
|
||||||
|
As you can see, the total area covered by triangles is lower when the complexity is higher."
|
||||||
|
|
||||||
|
# ╔═╡ f22222b4-e7b5-11ea-0ea0-8fa368d2a014
|
||||||
|
md"""
|
||||||
|
Can you write a function that computes the _area of `sierpinski(n)`_, as a fraction of the area of `sierpinski(0)`?
|
||||||
|
|
||||||
|
So:
|
||||||
|
```
|
||||||
|
area_sierpinski(0) = 1.0
|
||||||
|
area_sierpinski(1) = 0.??
|
||||||
|
...
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ ca8d2f72-e7b6-11ea-1893-f1e6d0a20dc7
|
||||||
|
function area_sierpinski(n)
|
||||||
|
if (n == 0)
|
||||||
|
return 1.0
|
||||||
|
end
|
||||||
|
return 0.75 * area_sierpinski(n-1)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 71c78614-e7bc-11ea-0959-c7a91a10d481
|
||||||
|
if area_sierpinski(0) == 1.0 && area_sierpinski(1) == 3 / 4
|
||||||
|
md"""
|
||||||
|
!!! correct
|
||||||
|
Well done!
|
||||||
|
"""
|
||||||
|
else
|
||||||
|
md"""
|
||||||
|
!!! warning "Incorrect"
|
||||||
|
Keep working on it!
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ c21096c0-e856-11ea-3dc5-a5b0cbf29335
|
||||||
|
md"**Let's try it out below:**"
|
||||||
|
|
||||||
|
# ╔═╡ 52533e00-e856-11ea-08a7-25e556fb1127
|
||||||
|
md"Complexity = $(@bind n Slider(0:6, show_value=true))"
|
||||||
|
|
||||||
|
# ╔═╡ c1ecad86-e7bc-11ea-1201-23ee380181a1
|
||||||
|
md"""
|
||||||
|
!!! hint
|
||||||
|
Can you write `area_sierpinksi(n)` as a function of `area_sierpinski(n-1)`?
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ c9bf4288-e6ce-11ea-0e13-a36b5e685998
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ a60a492a-e7bc-11ea-0f0b-75d81ce46a01
|
||||||
|
md"That's it for now, see you next week!"
|
||||||
|
|
||||||
|
# ╔═╡ b3c7a050-e855-11ea-3a22-3f514da746a4
|
||||||
|
if student.kerberos_id === "jazz"
|
||||||
|
md"""
|
||||||
|
!!! danger "Oops!"
|
||||||
|
**Before you submit**, remember to fill in your name and kerberos ID at the top of this notebook!
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ d3625d20-e6ce-11ea-394a-53208540d626
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ dfdeab34-e751-11ea-0f90-2fa9bbdccb1e
|
||||||
|
triangle() = compose(context(), polygon([(1, 1), (0, 1), (1 / 2, 0)]))
|
||||||
|
|
||||||
|
# ╔═╡ b923d394-e750-11ea-1971-595e09ab35b5
|
||||||
|
# It does not matter which order you define the building blocks (functions) of the
|
||||||
|
# program in. The best way to organize code is the one that promotes understanding.
|
||||||
|
|
||||||
|
function place_in_3_corners(t)
|
||||||
|
# Uses the Compose library to place 3 copies of t
|
||||||
|
# in the 3 corners of a triangle.
|
||||||
|
# treat this function as a black box,
|
||||||
|
# or learn how it works from the Compose documentation here https://giovineitalia.github.io/Compose.jl/latest/tutorial/#Compose-is-declarative-1
|
||||||
|
compose(context(),
|
||||||
|
(context(1 / 4, 0, 1 / 2, 1 / 2), t),
|
||||||
|
(context(0, 1 / 2, 1 / 2, 1 / 2), t),
|
||||||
|
(context(1 / 2, 1 / 2, 1 / 2, 1 / 2), t))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ e2848b9a-e703-11ea-24f9-b9131434a84b
|
||||||
|
function sierpinski(n)
|
||||||
|
if n == 0
|
||||||
|
triangle()
|
||||||
|
else
|
||||||
|
t = sierpinski(n - 1) # recursively construct a smaller sierpinski's triangle
|
||||||
|
place_in_3_corners(t) # place it in the 3 corners of a triangle
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 9664ac52-e750-11ea-171c-e7d57741a68c
|
||||||
|
sierpinski(complexity)
|
||||||
|
|
||||||
|
# ╔═╡ df0a4068-e7b2-11ea-2475-81b237d492b3
|
||||||
|
sierpinski.(0:6)
|
||||||
|
|
||||||
|
# ╔═╡ 147ed7b0-e856-11ea-0d0e-7ff0d527e352
|
||||||
|
md"""
|
||||||
|
|
||||||
|
Sierpinski's triangle of complexity $(n)
|
||||||
|
|
||||||
|
$(sierpinski(n))
|
||||||
|
|
||||||
|
has area **$(area_sierpinski(n))**
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ Cell order:
|
||||||
|
# ╟─fafae38e-e852-11ea-1208-732b4744e4c2
|
||||||
|
# ╟─cdff6730-e785-11ea-2546-4969521b33a7
|
||||||
|
# ╠═7308bc54-e6cd-11ea-0eab-83f7535edf25
|
||||||
|
# ╟─a2181260-e6cd-11ea-2a69-8d9d31d1ef0e
|
||||||
|
# ╟─094e39c8-e6ce-11ea-131b-07c4a1199edf
|
||||||
|
# ╟─31a8fbf8-e6ce-11ea-2c66-4b4d02b41995
|
||||||
|
# ╟─339c2d5c-e6ce-11ea-32f9-714b3628909c
|
||||||
|
# ╟─56866718-e6ce-11ea-0804-d108af4e5653
|
||||||
|
# ╠═bccf0e88-e754-11ea-3ab8-0170c2d44628
|
||||||
|
# ╟─e7abd366-e7a6-11ea-30d7-1b6194614d0a
|
||||||
|
# ╟─d62f223c-e754-11ea-2470-e72a605a9d7e
|
||||||
|
# ╠═4896bf0c-e754-11ea-19dc-1380bb356ab6
|
||||||
|
# ╠═7a01a508-e78a-11ea-11da-999d38785348
|
||||||
|
# ╟─682db9f8-e7b1-11ea-3949-6b683ca8b47b
|
||||||
|
# ╟─088cc652-e7a8-11ea-0ca7-f744f6f3afdd
|
||||||
|
# ╟─c18dce7a-e7a7-11ea-0a1a-f944d46754e5
|
||||||
|
# ╟─5e24d95c-e6ce-11ea-24be-bb19e1e14657
|
||||||
|
# ╟─6b8883f6-e7b3-11ea-155e-6f62117e123b
|
||||||
|
# ╠═851c03a4-e7a4-11ea-1652-d59b7a6599f0
|
||||||
|
# ╠═d6ee91ea-e750-11ea-1260-31ebf3ec6a9b
|
||||||
|
# ╠═5acd58e0-e856-11ea-2d3d-8329889fe16f
|
||||||
|
# ╟─dbc4da6a-e7b4-11ea-3b70-6f2abfcab992
|
||||||
|
# ╠═e2848b9a-e703-11ea-24f9-b9131434a84b
|
||||||
|
# ╠═9664ac52-e750-11ea-171c-e7d57741a68c
|
||||||
|
# ╠═02b9c9d6-e752-11ea-0f32-91b7b6481684
|
||||||
|
# ╟─1eb79812-e7b5-11ea-1c10-63b24803dd8a
|
||||||
|
# ╟─d7e8202c-e7b5-11ea-30d3-adcd6867d5f5
|
||||||
|
# ╠═df0a4068-e7b2-11ea-2475-81b237d492b3
|
||||||
|
# ╟─f22222b4-e7b5-11ea-0ea0-8fa368d2a014
|
||||||
|
# ╠═ca8d2f72-e7b6-11ea-1893-f1e6d0a20dc7
|
||||||
|
# ╠═71c78614-e7bc-11ea-0959-c7a91a10d481
|
||||||
|
# ╟─c21096c0-e856-11ea-3dc5-a5b0cbf29335
|
||||||
|
# ╟─52533e00-e856-11ea-08a7-25e556fb1127
|
||||||
|
# ╟─147ed7b0-e856-11ea-0d0e-7ff0d527e352
|
||||||
|
# ╟─c1ecad86-e7bc-11ea-1201-23ee380181a1
|
||||||
|
# ╟─c9bf4288-e6ce-11ea-0e13-a36b5e685998
|
||||||
|
# ╟─a60a492a-e7bc-11ea-0f0b-75d81ce46a01
|
||||||
|
# ╟─b3c7a050-e855-11ea-3a22-3f514da746a4
|
||||||
|
# ╟─d3625d20-e6ce-11ea-394a-53208540d626
|
||||||
|
# ╠═dfdeab34-e751-11ea-0f90-2fa9bbdccb1e
|
||||||
|
# ╠═b923d394-e750-11ea-1971-595e09ab35b5
|
||||||
553
seam_carving.jl
Normal file
553
seam_carving.jl
Normal file
@@ -0,0 +1,553 @@
|
|||||||
|
### A Pluto.jl notebook ###
|
||||||
|
# v0.12.15
|
||||||
|
|
||||||
|
using Markdown
|
||||||
|
using InteractiveUtils
|
||||||
|
|
||||||
|
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
|
||||||
|
macro bind(def, element)
|
||||||
|
quote
|
||||||
|
local el = $(esc(element))
|
||||||
|
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
|
||||||
|
el
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 877df834-f078-11ea-303b-e98273ef98a4
|
||||||
|
begin
|
||||||
|
using Pkg
|
||||||
|
Pkg.activate(tempname())
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 0316b94c-eef6-11ea-19bc-dbc959901bb5
|
||||||
|
begin
|
||||||
|
using Images
|
||||||
|
using ImageMagick
|
||||||
|
using Statistics
|
||||||
|
using LinearAlgebra
|
||||||
|
using ImageFiltering
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ fe19ad0a-ef04-11ea-1e5f-1bfcbbb51302
|
||||||
|
using PlutoUI
|
||||||
|
|
||||||
|
# ╔═╡ e196fa66-eef5-11ea-2afe-c9fcb6c48937
|
||||||
|
# Poor man's Project.toml
|
||||||
|
|
||||||
|
Pkg.add(["Images",
|
||||||
|
"ImageMagick",
|
||||||
|
"PlutoUI",
|
||||||
|
"Hyperscript",
|
||||||
|
"ImageFiltering"])
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ cb335074-eef7-11ea-24e8-c39a325166a1
|
||||||
|
md"""
|
||||||
|
# Seam Carving
|
||||||
|
|
||||||
|
1. We use convolution with Sobel filters for "edge detection".
|
||||||
|
2. We use that to write an algorithm that removes "uninteresting"
|
||||||
|
bits of an image in order to shrink it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ d2ae6dd2-eef9-11ea-02df-255ec3b46a36
|
||||||
|
begin
|
||||||
|
example_urls = [
|
||||||
|
"https://cdn.shortpixel.ai/spai/w_1086+q_lossy+ret_img+to_webp/https://wisetoast.com/wp-content/uploads/2015/10/The-Persistence-of-Memory-salvador-deli-painting.jpg",
|
||||||
|
|
||||||
|
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg/1014px-Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg",
|
||||||
|
|
||||||
|
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg/1014px-Gustave_Caillebotte_-_Paris_Street%3B_Rainy_Day_-_Google_Art_Project.jpg",
|
||||||
|
|
||||||
|
"https://upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Grant_Wood_-_American_Gothic_-_Google_Art_Project.jpg/480px-Grant_Wood_-_American_Gothic_-_Google_Art_Project.jpg",
|
||||||
|
"https://cdn.shortpixel.ai/spai/w_1086+q_lossy+ret_img+to_webp/https://wisetoast.com/wp-content/uploads/2015/10/The-Persistence-of-Memory-salvador-deli-painting.jpg",
|
||||||
|
|
||||||
|
"https://upload.wikimedia.org/wikipedia/commons/thumb/7/7d/A_Sunday_on_La_Grande_Jatte%2C_Georges_Seurat%2C_1884.jpg/640px-A_Sunday_on_La_Grande_Jatte%2C_Georges_Seurat%2C_1884.jpg",
|
||||||
|
|
||||||
|
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/758px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg",
|
||||||
|
"https://web.mit.edu/facilities/photos/construction/Projects/stata/1_large.jpg",
|
||||||
|
]
|
||||||
|
img = load(download(example_urls[2]))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 8340cf20-f079-11ea-1665-f5864bc49cb9
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 0b6010a8-eef6-11ea-3ad6-c1f10e30a413
|
||||||
|
# arbitrarily choose the brightness of a pixel as mean of rgb
|
||||||
|
# brightness(c::AbstractRGB) = mean((c.r, c.g, c.b))
|
||||||
|
|
||||||
|
# Use a weighted sum of rgb giving more weight to colors we perceive as 'brighter'
|
||||||
|
# Based on https://www.tutorialspoint.com/dip/grayscale_to_rgb_conversion.htm
|
||||||
|
brightness(c::AbstractRGB) = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b
|
||||||
|
|
||||||
|
# ╔═╡ fc1c43cc-eef6-11ea-0fc4-a90ac4336964
|
||||||
|
Gray.(brightness.(img))
|
||||||
|
|
||||||
|
# ╔═╡ 82c0d0c8-efec-11ea-1bb9-83134ecb877e
|
||||||
|
md"""
|
||||||
|
# Edge detection filter
|
||||||
|
|
||||||
|
(Spoiler alert!) Here, we use the Sobel edge detection filter we created in Homework 1.
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{align}
|
||||||
|
|
||||||
|
G_x &= \begin{bmatrix}
|
||||||
|
1 & 0 & -1 \\
|
||||||
|
2 & 0 & -2 \\
|
||||||
|
1 & 0 & -1 \\
|
||||||
|
\end{bmatrix}*A\\
|
||||||
|
G_y &= \begin{bmatrix}
|
||||||
|
1 & 2 & 1 \\
|
||||||
|
0 & 0 & 0 \\
|
||||||
|
-1 & -2 & -1 \\
|
||||||
|
\end{bmatrix}*A
|
||||||
|
\end{align}
|
||||||
|
```
|
||||||
|
Here $A$ is the array corresponding to your image.
|
||||||
|
We can think of these as derivatives in the $x$ and $y$ directions.
|
||||||
|
|
||||||
|
Then we combine them by finding the magnitude of the **gradient** (in the sense of multivariate calculus) by defining
|
||||||
|
|
||||||
|
$$G_\text{total} = \sqrt{G_x^2 + G_y^2}.$$
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ da726954-eff0-11ea-21d4-a7f4ae4a6b09
|
||||||
|
Sy, Sx = Kernel.sobel()
|
||||||
|
|
||||||
|
# ╔═╡ abf6944e-f066-11ea-18e2-0b92606dab85
|
||||||
|
(collect(Int.(8 .* Sy)), collect(Int.(8 .* Sx)))
|
||||||
|
|
||||||
|
# ╔═╡ ac8d6902-f069-11ea-0f1d-9b0fa706d769
|
||||||
|
md"""
|
||||||
|
- blue shows positive values
|
||||||
|
- red shows negative values
|
||||||
|
$G_x \hspace{180pt} G_y$
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 172c7612-efee-11ea-077a-5d5c6e2505a4
|
||||||
|
function shrink_image(image, ratio=5)
|
||||||
|
(height, width) = size(image)
|
||||||
|
new_height = height ÷ ratio - 1
|
||||||
|
new_width = width ÷ ratio - 1
|
||||||
|
list = [
|
||||||
|
mean(image[
|
||||||
|
ratio * i:ratio * (i + 1),
|
||||||
|
ratio * j:ratio * (j + 1),
|
||||||
|
])
|
||||||
|
for j in 1:new_width
|
||||||
|
for i in 1:new_height
|
||||||
|
]
|
||||||
|
reshape(list, new_height, new_width)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ fcf46120-efec-11ea-06b9-45f470899cb2
|
||||||
|
function convolve(M, kernel)
|
||||||
|
height, width = size(kernel)
|
||||||
|
|
||||||
|
half_height = height ÷ 2
|
||||||
|
half_width = width ÷ 2
|
||||||
|
|
||||||
|
new_image = similar(M)
|
||||||
|
|
||||||
|
# (i, j) loop over the original image
|
||||||
|
m, n = size(M)
|
||||||
|
@inbounds for i in 1:m
|
||||||
|
for j in 1:n
|
||||||
|
# (k, l) loop over the neighbouring pixels
|
||||||
|
accumulator = 0 * M[1, 1]
|
||||||
|
for k in -half_height:-half_height + height - 1
|
||||||
|
for l in -half_width:-half_width + width - 1
|
||||||
|
Mi = i - k
|
||||||
|
Mj = j - l
|
||||||
|
# First index into M
|
||||||
|
if Mi < 1
|
||||||
|
Mi = 1
|
||||||
|
elseif Mi > m
|
||||||
|
Mi = m
|
||||||
|
end
|
||||||
|
# Second index into M
|
||||||
|
if Mj < 1
|
||||||
|
Mj = 1
|
||||||
|
elseif Mj > n
|
||||||
|
Mj = n
|
||||||
|
end
|
||||||
|
|
||||||
|
accumulator += kernel[k, l] * M[Mi, Mj]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
new_image[i, j] = accumulator
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return new_image
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 6f7bd064-eff4-11ea-0260-f71aa7f4f0e5
|
||||||
|
function edgeness(img)
|
||||||
|
Sy, Sx = Kernel.sobel()
|
||||||
|
b = brightness.(img)
|
||||||
|
|
||||||
|
∇y = convolve(b, Sy)
|
||||||
|
∇x = convolve(b, Sx)
|
||||||
|
|
||||||
|
sqrt.(∇x.^2 + ∇y.^2)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ dec62538-efee-11ea-1e03-0b801e61e91c
|
||||||
|
function show_colored_array(array)
|
||||||
|
pos_color = RGB(0.36, 0.82, 0.8)
|
||||||
|
neg_color = RGB(0.99, 0.18, 0.13)
|
||||||
|
to_rgb(x) = max(x, 0) * pos_color + max(-x, 0) * neg_color
|
||||||
|
to_rgb.(array) / maximum(abs.(array))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ da39c824-eff0-11ea-375b-1b6c6e186182
|
||||||
|
# Sx
|
||||||
|
# collect(Int.(8 .* Sx))
|
||||||
|
show_colored_array(Sx)
|
||||||
|
|
||||||
|
# ╔═╡ 074a58be-f146-11ea-382c-b7ae6c44bf75
|
||||||
|
# Sy
|
||||||
|
# collect(Int.(8 .* Sy))
|
||||||
|
show_colored_array(Sy)
|
||||||
|
|
||||||
|
# ╔═╡ f8283a0e-eff4-11ea-23d3-9f1ced1bafb4
|
||||||
|
md"""
|
||||||
|
|
||||||
|
## Seam carving idea
|
||||||
|
|
||||||
|
The idea of seam carving is to find a path from the top of the image to the bottom of the image where the path minimizes the edgness.
|
||||||
|
|
||||||
|
In other words, this path **minimizes the number of edges it crosses**
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 025e2c94-eefb-11ea-12cb-f56f34886334
|
||||||
|
md"""
|
||||||
|
|
||||||
|
At every step in going down, the path is allowed to go south west, south or south east. We want to find a seam with the minimum possible sum of energies.
|
||||||
|
|
||||||
|
We start by writing a `least_edgy` function which given a matrix of energies, returns
|
||||||
|
a matrix of minimum possible energy starting from that pixel going up to a pixel in the bottom most row.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ e5a7e426-2d0c-11eb-07fd-cd587148bd38
|
||||||
|
small_img = shrink_image(img, 10)
|
||||||
|
|
||||||
|
# ╔═╡ eff57fca-2d06-11eb-0ef2-f94561e916eb
|
||||||
|
function get_seam_map(im)
|
||||||
|
m, n = size(im)
|
||||||
|
sm = Array{Float64}(undef, size(im))
|
||||||
|
sd = Array{Int}(undef, size(im))
|
||||||
|
sm[end, :] .= im[end, :]
|
||||||
|
for i in m-1:-1:1
|
||||||
|
for j in 1:n
|
||||||
|
val = im[i,j]
|
||||||
|
min_below = 1000.0
|
||||||
|
path = 0
|
||||||
|
for k in max(1,j-1):min(n,j+1)
|
||||||
|
candidate = sm[i+1,k]
|
||||||
|
if candidate < min_below
|
||||||
|
min_below = candidate
|
||||||
|
path = k-j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sm[i,j] = min_below + val
|
||||||
|
sd[i,j] = path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return sm, sd
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 1c01264c-2d08-11eb-0549-076c31bb8fa6
|
||||||
|
sm, sd = get_seam_map(edgeness(img))
|
||||||
|
|
||||||
|
# ╔═╡ acc1ee8c-eef9-11ea-01ac-9b9e9c4167b3
|
||||||
|
# e[x,y]
|
||||||
|
# ↙ ↓ ↘ <--pick the next path which gives the least overall energy
|
||||||
|
# e[x-1,y+1] e[x,y+1] e[x+1,y+1]
|
||||||
|
#
|
||||||
|
# Basic Comp: e[x,y] += min( e[x-1,y+1],e[x,y],e[x+1,y])
|
||||||
|
# dirs records which one from (-1==SW,0==S,1==SE)
|
||||||
|
|
||||||
|
function least_edgy(E)
|
||||||
|
least_E = zeros(size(E))
|
||||||
|
dirs = zeros(Int, size(E))
|
||||||
|
least_E[end, :] .= E[end, :] # the minimum energy on the last row is the energy
|
||||||
|
# itself
|
||||||
|
|
||||||
|
m, n = size(E)
|
||||||
|
# Go from the last row up, finding the minimum energy
|
||||||
|
for i in m-1:-1:1
|
||||||
|
for j in 1:n
|
||||||
|
j1, j2 = max(1, j-1), min(j+1, n)
|
||||||
|
e, dir = findmin(least_E[i+1, j1:j2])
|
||||||
|
least_E[i,j] += e
|
||||||
|
least_E[i,j] += E[i,j]
|
||||||
|
dirs[i, j] = (-1,0,1)[dir + (j==1)]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
least_E, dirs
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 8b204a2a-eff6-11ea-25b0-13f230037ee1
|
||||||
|
# The bright areas are screaming "AVOID ME!!!"
|
||||||
|
least_e, dirs = least_edgy(edgeness(img))
|
||||||
|
|
||||||
|
# ╔═╡ 84d3afe4-eefe-11ea-1e31-bf3b2af4aecd
|
||||||
|
show_colored_array(least_e)
|
||||||
|
|
||||||
|
# ╔═╡ 50a49b56-2d09-11eb-2d33-8d4cd5896e38
|
||||||
|
show_colored_array(sm)
|
||||||
|
|
||||||
|
# ╔═╡ b507480a-ef01-11ea-21c4-63d19fac19ab
|
||||||
|
# direction the path should take at every pixel.
|
||||||
|
reduce((x,y)->x*y*"\n",
|
||||||
|
reduce(*, getindex.(([" ", "↙", "↓", "↘"],), dirs[1:25, 1:76].+3), dims=2, init=""), init="") |> Text
|
||||||
|
|
||||||
|
# ╔═╡ 696cf94e-2d09-11eb-136a-8f360d3a7507
|
||||||
|
reduce((x,y)->x*y*"\n",
|
||||||
|
reduce(*, getindex.(([" ", "↙", "↓", "↘"],), sd[1:25, 1:76].+3), dims=2, init=""), init="") |> Text
|
||||||
|
|
||||||
|
# ╔═╡ 7d8b20a2-ef03-11ea-1c9e-fdf49a397619
|
||||||
|
md"## Remove seams"
|
||||||
|
|
||||||
|
# ╔═╡ f690b06a-ef31-11ea-003b-4f2b2f82a9c3
|
||||||
|
md"""
|
||||||
|
Compressing an image horizontally involves a number of seams of lowest energy successively.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ╔═╡ 0adf2e18-2d10-11eb-2a27-6de83b5aad3a
|
||||||
|
function get_seam_at2(sd, j)
|
||||||
|
h = size(sd, 1)
|
||||||
|
js = Array{Int}(undef, h)
|
||||||
|
js[1] = j
|
||||||
|
for i=2:h
|
||||||
|
js[i] = js[i-1] + sd[i-1,js[i-1]]
|
||||||
|
end
|
||||||
|
tuple.(1:h, js)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 977b6b98-ef03-11ea-0176-551fc29729ab
|
||||||
|
function get_seam_at(dirs, j)
|
||||||
|
m = size(dirs, 1)
|
||||||
|
js = fill(0, m)
|
||||||
|
js[1] = j
|
||||||
|
for i=2:m
|
||||||
|
js[i] = js[i-1] + dirs[i-1, js[i-1]]
|
||||||
|
end
|
||||||
|
tuple.(1:m, js)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 9abbb158-ef03-11ea-39df-a3e8aa792c50
|
||||||
|
get_seam_at(dirs, 2)
|
||||||
|
|
||||||
|
# ╔═╡ fe709508-2d10-11eb-02a9-29080b78d44a
|
||||||
|
get_seam_at2(dirs, 2)
|
||||||
|
|
||||||
|
# ╔═╡ 14f72976-ef05-11ea-2ad5-9f0914f9cf58
|
||||||
|
function mark_path(img, path)
|
||||||
|
img′ = copy(img)
|
||||||
|
m = size(img, 2)
|
||||||
|
for (i, j) in path
|
||||||
|
# To make it easier to see, we'll color not just
|
||||||
|
# the pixels of the seam, but also those adjacent to it
|
||||||
|
for j′ in j-1:j+1
|
||||||
|
img′[i, clamp(j′, 1, m)] = RGB(1,0,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
img′
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ cf9a9124-ef04-11ea-14a4-abf930edc7cc
|
||||||
|
@bind start_column Slider(1:size(img, 2))
|
||||||
|
|
||||||
|
# ╔═╡ 772a4d68-ef04-11ea-366a-f7ae9e1634f6
|
||||||
|
path = get_seam_at2(dirs, start_column)
|
||||||
|
|
||||||
|
# ╔═╡ 081a98cc-f06e-11ea-3664-7ba51d4fd153
|
||||||
|
function pencil(X)
|
||||||
|
f(x) = RGB(1-x,1-x,1-x)
|
||||||
|
map(f, X ./ maximum(X))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 237647e8-f06d-11ea-3c7e-2da57e08bebc
|
||||||
|
e = edgeness(img);
|
||||||
|
|
||||||
|
# ╔═╡ c7a7386e-2d11-11eb-3885-056f6d7fb840
|
||||||
|
|
||||||
|
|
||||||
|
# ╔═╡ 4f23bc54-ef0f-11ea-06a9-35ca3ece421e
|
||||||
|
function rm_path(img, path)
|
||||||
|
img′ = img[:, 1:end-1] # one less column
|
||||||
|
for (i, j) in path
|
||||||
|
img′[i, 1:j-1] .= img[i, 1:j-1]
|
||||||
|
img′[i, j:end] .= img[i, j+1:end]
|
||||||
|
end
|
||||||
|
img′
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ b401f398-ef0f-11ea-38fe-012b7bc8a4fa
|
||||||
|
function shrink_n(img, n)
|
||||||
|
imgs = []
|
||||||
|
marked_imgs = []
|
||||||
|
|
||||||
|
e = edgeness(img)
|
||||||
|
for i=1:n
|
||||||
|
least_E, dirs = get_seam_map(e)
|
||||||
|
_, min_j = findmin(@view least_E[1, :])
|
||||||
|
seam = get_seam_at2(dirs, min_j)
|
||||||
|
img = rm_path(img, seam)
|
||||||
|
# Recompute the energy for the new image
|
||||||
|
# Note, this currently involves rerunning the convolution
|
||||||
|
# on the whole image, but in principle the only values that
|
||||||
|
# need recomputation are those adjacent to the seam, so there
|
||||||
|
# is room for a meanintful speedup here.
|
||||||
|
e = edgeness(img)
|
||||||
|
e = rm_path(e, seam)
|
||||||
|
|
||||||
|
push!(imgs, img)
|
||||||
|
push!(marked_imgs, mark_path(img, seam))
|
||||||
|
end
|
||||||
|
imgs, marked_imgs
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ b1b6b7fc-f153-11ea-224a-2578e8298775
|
||||||
|
n_examples = min(200, size(img, 2))
|
||||||
|
|
||||||
|
# ╔═╡ 2eb459d4-ef36-11ea-1f74-b53ffec7a1ed
|
||||||
|
# returns two vectors of n successively smaller images
|
||||||
|
# The second images have markings where the seam is cut out
|
||||||
|
carved, marked_carved = shrink_n(img, n_examples);
|
||||||
|
|
||||||
|
# ╔═╡ 7038abe4-ef36-11ea-11a5-75e57ab51032
|
||||||
|
@bind n Slider(1:length(carved))
|
||||||
|
|
||||||
|
# ╔═╡ 2d6c6820-ef2d-11ea-1704-49bb5188cfcc
|
||||||
|
md"shrunk by $n:"
|
||||||
|
|
||||||
|
# ╔═╡ 1fd26a60-f089-11ea-1f56-bb6eba7d9651
|
||||||
|
function hbox(x, y, gap=16; sy=size(y), sx=size(x))
|
||||||
|
w,h = (max(sx[1], sy[1]),
|
||||||
|
gap + sx[2] + sy[2])
|
||||||
|
|
||||||
|
slate = fill(RGB(1,1,1), w,h)
|
||||||
|
slate[1:size(x,1), 1:size(x,2)] .= RGB.(x)
|
||||||
|
slate[1:size(y,1), size(x,2) + gap .+ (1:size(y,2))] .= RGB.(y)
|
||||||
|
slate
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 44192a40-eff2-11ea-0ec7-05cdadb0c29a
|
||||||
|
begin
|
||||||
|
img_brightness = brightness.(img)
|
||||||
|
∇x = convolve(img_brightness, Sx)
|
||||||
|
∇y = convolve(img_brightness, Sy)
|
||||||
|
hbox(show_colored_array(∇x), show_colored_array(∇y))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ d6a268c0-eff4-11ea-2c9e-bfef19c7f540
|
||||||
|
begin
|
||||||
|
edged = edgeness(img)
|
||||||
|
# hbox(img, pencil(edged))
|
||||||
|
hbox(img, Gray.(edgeness(img)) / maximum(abs.(edged)))
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 552fb92e-ef05-11ea-0a79-dd7a6760089a
|
||||||
|
hbox(mark_path(img, path), mark_path(show_colored_array(least_e), path))
|
||||||
|
|
||||||
|
# ╔═╡ dfd03c4e-f06c-11ea-1e2a-89233a675138
|
||||||
|
let
|
||||||
|
hbox(mark_path(img, path), mark_path(pencil(e), path));
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ ca4a87e8-eff8-11ea-3d57-01dfa34ff723
|
||||||
|
let
|
||||||
|
# least energy path of them all:
|
||||||
|
_, k = findmin(least_e[1, :])
|
||||||
|
path = get_seam_at(dirs, k)
|
||||||
|
hbox(
|
||||||
|
mark_path(img, path),
|
||||||
|
mark_path(show_colored_array(least_e), path)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ fa6a2152-ef0f-11ea-0e67-0d1a6599e779
|
||||||
|
hbox(img, marked_carved[n], sy=size(img))
|
||||||
|
|
||||||
|
# ╔═╡ 71b16dbe-f08b-11ea-2343-5f1583074029
|
||||||
|
vbox(x,y, gap=16) = hbox(x', y')'
|
||||||
|
|
||||||
|
# ╔═╡ ddac52ea-f148-11ea-2860-21cff4c867e6
|
||||||
|
let
|
||||||
|
∇y = convolve(brightness.(img), Sy)
|
||||||
|
∇x = convolve(brightness.(img), Sx)
|
||||||
|
# zoom in on the clock
|
||||||
|
vbox(
|
||||||
|
hbox(img[300:end, 1:300], img[300:end, 1:300]),
|
||||||
|
hbox(show_colored_array.((∇x[300:end, 1:300], ∇y[300:end, 1:300]))...)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ╔═╡ 15d1e5dc-ef2f-11ea-093a-417108bcd495
|
||||||
|
[size(img) size(carved[n])]
|
||||||
|
|
||||||
|
# ╔═╡ Cell order:
|
||||||
|
# ╠═877df834-f078-11ea-303b-e98273ef98a4
|
||||||
|
# ╠═e196fa66-eef5-11ea-2afe-c9fcb6c48937
|
||||||
|
# ╠═0316b94c-eef6-11ea-19bc-dbc959901bb5
|
||||||
|
# ╟─cb335074-eef7-11ea-24e8-c39a325166a1
|
||||||
|
# ╠═d2ae6dd2-eef9-11ea-02df-255ec3b46a36
|
||||||
|
# ╠═8340cf20-f079-11ea-1665-f5864bc49cb9
|
||||||
|
# ╠═0b6010a8-eef6-11ea-3ad6-c1f10e30a413
|
||||||
|
# ╠═fc1c43cc-eef6-11ea-0fc4-a90ac4336964
|
||||||
|
# ╟─82c0d0c8-efec-11ea-1bb9-83134ecb877e
|
||||||
|
# ╠═da726954-eff0-11ea-21d4-a7f4ae4a6b09
|
||||||
|
# ╠═da39c824-eff0-11ea-375b-1b6c6e186182
|
||||||
|
# ╠═074a58be-f146-11ea-382c-b7ae6c44bf75
|
||||||
|
# ╠═abf6944e-f066-11ea-18e2-0b92606dab85
|
||||||
|
# ╠═44192a40-eff2-11ea-0ec7-05cdadb0c29a
|
||||||
|
# ╟─ac8d6902-f069-11ea-0f1d-9b0fa706d769
|
||||||
|
# ╠═ddac52ea-f148-11ea-2860-21cff4c867e6
|
||||||
|
# ╠═6f7bd064-eff4-11ea-0260-f71aa7f4f0e5
|
||||||
|
# ╟─d6a268c0-eff4-11ea-2c9e-bfef19c7f540
|
||||||
|
# ╠═172c7612-efee-11ea-077a-5d5c6e2505a4
|
||||||
|
# ╟─fcf46120-efec-11ea-06b9-45f470899cb2
|
||||||
|
# ╟─dec62538-efee-11ea-1e03-0b801e61e91c
|
||||||
|
# ╟─f8283a0e-eff4-11ea-23d3-9f1ced1bafb4
|
||||||
|
# ╟─025e2c94-eefb-11ea-12cb-f56f34886334
|
||||||
|
# ╠═e5a7e426-2d0c-11eb-07fd-cd587148bd38
|
||||||
|
# ╠═eff57fca-2d06-11eb-0ef2-f94561e916eb
|
||||||
|
# ╠═1c01264c-2d08-11eb-0549-076c31bb8fa6
|
||||||
|
# ╠═acc1ee8c-eef9-11ea-01ac-9b9e9c4167b3
|
||||||
|
# ╠═8b204a2a-eff6-11ea-25b0-13f230037ee1
|
||||||
|
# ╠═84d3afe4-eefe-11ea-1e31-bf3b2af4aecd
|
||||||
|
# ╠═50a49b56-2d09-11eb-2d33-8d4cd5896e38
|
||||||
|
# ╠═b507480a-ef01-11ea-21c4-63d19fac19ab
|
||||||
|
# ╠═696cf94e-2d09-11eb-136a-8f360d3a7507
|
||||||
|
# ╟─7d8b20a2-ef03-11ea-1c9e-fdf49a397619
|
||||||
|
# ╠═f690b06a-ef31-11ea-003b-4f2b2f82a9c3
|
||||||
|
# ╠═fe19ad0a-ef04-11ea-1e5f-1bfcbbb51302
|
||||||
|
# ╠═0adf2e18-2d10-11eb-2a27-6de83b5aad3a
|
||||||
|
# ╠═977b6b98-ef03-11ea-0176-551fc29729ab
|
||||||
|
# ╠═9abbb158-ef03-11ea-39df-a3e8aa792c50
|
||||||
|
# ╠═fe709508-2d10-11eb-02a9-29080b78d44a
|
||||||
|
# ╠═772a4d68-ef04-11ea-366a-f7ae9e1634f6
|
||||||
|
# ╟─14f72976-ef05-11ea-2ad5-9f0914f9cf58
|
||||||
|
# ╠═cf9a9124-ef04-11ea-14a4-abf930edc7cc
|
||||||
|
# ╠═552fb92e-ef05-11ea-0a79-dd7a6760089a
|
||||||
|
# ╠═081a98cc-f06e-11ea-3664-7ba51d4fd153
|
||||||
|
# ╠═237647e8-f06d-11ea-3c7e-2da57e08bebc
|
||||||
|
# ╠═dfd03c4e-f06c-11ea-1e2a-89233a675138
|
||||||
|
# ╠═ca4a87e8-eff8-11ea-3d57-01dfa34ff723
|
||||||
|
# ╠═c7a7386e-2d11-11eb-3885-056f6d7fb840
|
||||||
|
# ╠═4f23bc54-ef0f-11ea-06a9-35ca3ece421e
|
||||||
|
# ╠═b401f398-ef0f-11ea-38fe-012b7bc8a4fa
|
||||||
|
# ╠═b1b6b7fc-f153-11ea-224a-2578e8298775
|
||||||
|
# ╠═2eb459d4-ef36-11ea-1f74-b53ffec7a1ed
|
||||||
|
# ╠═7038abe4-ef36-11ea-11a5-75e57ab51032
|
||||||
|
# ╟─2d6c6820-ef2d-11ea-1704-49bb5188cfcc
|
||||||
|
# ╠═fa6a2152-ef0f-11ea-0e67-0d1a6599e779
|
||||||
|
# ╟─71b16dbe-f08b-11ea-2343-5f1583074029
|
||||||
|
# ╟─1fd26a60-f089-11ea-1f56-bb6eba7d9651
|
||||||
|
# ╟─15d1e5dc-ef2f-11ea-093a-417108bcd495
|
||||||
Reference in New Issue
Block a user