Last active
August 27, 2020 10:13
-
-
Save fonsp/dacf389ec9997558834600b7478b7036 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### A Pluto.jl notebook ### | |
# v0.9.11 | |
using Markdown | |
macro bind(def, element) | |
quote | |
local el = $(esc(element)) | |
global $(esc(def)) = Core.applicable(Base.peek, el) ? Base.peek(el) : missing | |
el | |
end | |
end | |
# βββ‘ fcac2038-b7aa-11ea-1320-355e0731afb4 | |
using LinearAlgebra | |
# βββ‘ d7eadad2-b7ad-11ea-22e3-f1e5ded42255 | |
md"# Singular Value Decomposition | |
Lorem ipsum SVD est." | |
# βββ‘ dfd0317e-b85f-11ea-3f2b-fd95c87a0a69 | |
zeros(Float64, 0, 0) | |
# βββ‘ d6f2ee1c-b7ad-11ea-0e1c-fb21a58c5711 | |
md"## Step 1: upload your favorite image:" | |
# βββ‘ 841551d2-b861-11ea-08de-c180ca50b9be | |
struct ImageInput | |
use_camera::Bool | |
default_url::AbstractString | |
maxsize::Integer | |
end | |
# βββ‘ 8c0caad4-b861-11ea-3aab-83e4d3718aa9 | |
img = ImageInput(true, "asdf", 200) | |
# βββ‘ 74cac824-b861-11ea-37e9-e97065879618 | |
ph = """ | |
<span class="pl-image"> | |
<style> | |
.pl-image video { | |
max-width: 250px; | |
} | |
.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" title="Click to take a picture"> | |
<video playsinline autoplay></video> | |
</div> | |
<script> | |
// mostly taken from https://github.com/fonsp/printi-static | |
// (by the same author) | |
const span = this.currentScript.parentElement | |
const video = span.querySelector("video") | |
const img = html`<img crossOrigin="anonymous">` | |
const maxsize = $(img.maxsize) | |
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: Array.from(ctx.getImageData(0, 0, width, height).data), | |
} | |
span.dispatchEvent(new CustomEvent("input")) | |
} | |
navigator.mediaDevices.getUserMedia({ | |
audio: false, | |
video: { | |
facingMode: "environment", | |
}, | |
}).then(function(stream) { | |
window.stream = stream | |
video.srcObject = stream | |
window.cameraConnected = true | |
video.controls = false | |
video.play() | |
video.controls = false | |
}).catch(function(error) { | |
console.log(error) | |
}); | |
var sending= false | |
span.querySelector("#video-container").onclick = function() { | |
sending = !sending | |
} | |
setInterval(() => { | |
if(sending){ | |
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) | |
} | |
}, 200) | |
</script> | |
</span> | |
""" |> HTML; | |
# βββ‘ 212d10d6-b7ae-11ea-1434-fb7d45aaf278 | |
md"The (black-and-white) image data is now available as a Julia 2D array:" | |
# βββ‘ 42578c50-b7ae-11ea-2f7d-6b8b3df44aed | |
md"This notebook defines a type `BWImage` that can be used to turn 2D Float arrays into a picture!" | |
# βββ‘ 90715ff6-b7ae-11ea-3780-1d227049322c | |
md"You can use `img_data` like any other Julia 2D array! For example, here is the top left corner of your image:" | |
# βββ‘ 7c8b8c96-b7ae-11ea-149d-61d29d35847c | |
md"## Step 2: running the SVD | |
The Julia standard library package `LinearAlgebra` contains a method to compute the SVD. " | |
# βββ‘ 07834ab8-b7ba-11ea-0279-77179671d826 | |
md"Let's look at the result." | |
# βββ‘ 9765a090-b7b0-11ea-3c1f-e5a8ac58d7de | |
#[π.U, π.S, π.V] | |
# βββ‘ fe4c70fe-b7b0-11ea-232b-1996facd33a0 | |
md"Let's verify the identity | |
$A = U \Sigma V^{\intercal}$" | |
# βββ‘ 8e7cb972-b7b1-11ea-2c4d-ed583218740a | |
md"Are they equal?" | |
# βββ‘ 4df00b20-b7b1-11ea-0836-41314f8a2d35 | |
md"It looks like they are **not** equal - how come? | |
Since we are using a _computer_, the decomposition and multiplication both introduce some numerical errors. So instead of checking whether the reconstructed matrix is _equal_ to the original, we can check how _close_ they are to each other." | |
# βββ‘ 2199d7b2-b7b2-11ea-2e86-95b23658a538 | |
md"One way to quantify the _distance_ between two matrices is to look at the **point-wise difference**. If the **sum** of all differences is close to 0, the matrices are almost equal." | |
# βββ‘ 489b7c9c-b7b1-11ea-3a54-87bd17341b2c | |
md"There are other ways to compare two matrices, such methods are called _**matrix norms**_." | |
# βββ‘ a6dcc77c-b7b2-11ea-0c7d-d5686603d182 | |
md"### The π-norm" | |
# βββ‘ b15d683c-b7b2-11ea-1090-b392950bedb6 | |
md"Another popular matrix norm is the **π_-norm_**: you turn both matrices into a picture, and use your π to see how close they are:" | |
# βββ‘ 064d5a78-b7b3-11ea-399c-f968bf9c910a | |
md"""**How similar are these images?** $(@bind π_dist html"<input>")""" | |
# βββ‘ 2c7c8cdc-b7b3-11ea-166c-cfd232fd2004 | |
π_dist | |
# βββ‘ 64e51904-b7b3-11ea-0f72-359d63261b21 | |
md"In some applications, like _**image compression**_, this is the _most imporant norm_." | |
# βββ‘ d2de4480-b7b0-11ea-143d-033dc76cf6bc | |
md"## Step 3: compression" | |
# βββ‘ 79a01d50-b7b1-11ea-27ee-4161da276cde | |
md"Blabla" | |
# βββ‘ 54f79f6e-b865-11ea-2f16-ff76fe1f14ed | |
@bind upload_data ph | |
# βββ‘ c8f52288-b7a9-11ea-02b7-698ec1d06357 | |
img_data = let | |
# every 4th byte is the Red pixel value | |
reds = UInt8.(upload_data["data"][1:4:end]) | |
# shuffle and flip to get it in the right shape | |
( reshape(reds, (upload_data["width"], upload_data["height"]))') / 255.0 | |
end; | |
# βββ‘ a404b9f8-b7ab-11ea-0b07-a733a3c4f353 | |
π = svd(img_data); | |
# βββ‘ 1e866730-b7ac-11ea-3df1-9f7f92d504db | |
@bind keep HTML("<input type='range' max='50' value='10'>") | |
# βββ‘ 9b18067e-b7b3-11ea-0372-d351201a0e7d | |
md"Showing the **first $(keep) singular pairs**." | |
# βββ‘ 2fdd76dc-b7ce-11ea-12b1-59aa1c44ebbe | |
md"### Store fewer bytes" | |
# βββ‘ 3bb96bf2-b7cc-11ea-377f-2d5d3f04e96d | |
#compressed_size(keep), uncompressed_size() | |
# βββ‘ 906a267a-b7ca-11ea-1e73-a56b7e7c9115 | |
#BWImage(Float16.(F.U)[:,1:keep] * Diagonal(Float16.(F.S[1:keep])) * Float16.(F.V)'[1:keep,:]) | |
# βββ‘ 444bc786-b7ce-11ea-0016-ff1fbb17d736 | |
md"JPEG works in a similar way" | |
# βββ‘ 1c55bd84-b7c9-11ea-0aa5-b95f58fae242 | |
md"### Individual pairs" | |
# βββ‘ 7485990a-b7af-11ea-10e4-53a3ab5dcea7 | |
md"## Going further | |
More stuff to learn about SVD | |
To keep things simple (and dependency-free), this notebook only works with downscaled black-and-white images that you pick using the button. For **color**, **larger images**, or **images from your disk**, you should look into the [`Images.jl`](https://github.com/JuliaImages/Images.jl) package!" | |
# βββ‘ 34de1bc0-b795-11ea-2cac-bbbc496133ad | |
begin | |
struct BWImage | |
data::Array{UInt8, 2} | |
end | |
function BWImage(data::Array{T, 2}) where T <: AbstractFloat | |
BWImage(floor.(UInt8, clamp.(data * 255, 0, 255))) | |
end | |
import Base: show | |
function show(io::IO, ::MIME"image/bmp", i::BWImage) | |
height, width = size(i.data) | |
datawidth = Integer(ceil(width / 4)) * 4 | |
bmp_header_size = 14 | |
dib_header_size = 40 | |
palette_size = 256 * 4 | |
data_size = datawidth * height * 1 | |
# BMP header | |
write(io, 0x42, 0x4d) | |
write(io, UInt32(bmp_header_size + dib_header_size + palette_size + data_size)) | |
write(io, 0x00, 0x00) | |
write(io, 0x00, 0x00) | |
write(io, UInt32(bmp_header_size + dib_header_size + palette_size)) | |
# DIB header | |
write(io, UInt32(dib_header_size)) | |
write(io, Int32(width)) | |
write(io, Int32(-height)) | |
write(io, UInt16(1)) | |
write(io, UInt16(8)) | |
write(io, UInt32(0)) | |
write(io, UInt32(0)) | |
write(io, 0x12, 0x0b, 0x00, 0x00) | |
write(io, 0x12, 0x0b, 0x00, 0x00) | |
write(io, UInt32(0)) | |
write(io, UInt32(0)) | |
# color palette | |
write(io, [[x, x, x, 0x00] for x in UInt8.(0:255)]...) | |
# data | |
padding = fill(0x00, datawidth - width) | |
for y in 1:height | |
write(io, i.data[y,:], padding) | |
end | |
end | |
end | |
# βββ‘ 04c34a64-b7ac-11ea-0cc0-6709153eaf18 | |
BWImage( | |
π.U[:,1:keep] * | |
Diagonal(π.S[1:keep]) * | |
π.V'[1:keep,:] | |
) | |
# βββ‘ Cell order: | |
# ββd7eadad2-b7ad-11ea-22e3-f1e5ded42255 | |
# β βdfd0317e-b85f-11ea-3f2b-fd95c87a0a69 | |
# ββd6f2ee1c-b7ad-11ea-0e1c-fb21a58c5711 | |
# β β841551d2-b861-11ea-08de-c180ca50b9be | |
# β β8c0caad4-b861-11ea-3aab-83e4d3718aa9 | |
# β β74cac824-b861-11ea-37e9-e97065879618 | |
# ββ212d10d6-b7ae-11ea-1434-fb7d45aaf278 | |
# β βc8f52288-b7a9-11ea-02b7-698ec1d06357 | |
# ββ42578c50-b7ae-11ea-2f7d-6b8b3df44aed | |
# ββ90715ff6-b7ae-11ea-3780-1d227049322c | |
# ββ7c8b8c96-b7ae-11ea-149d-61d29d35847c | |
# β βfcac2038-b7aa-11ea-1320-355e0731afb4 | |
# β βa404b9f8-b7ab-11ea-0b07-a733a3c4f353 | |
# ββ07834ab8-b7ba-11ea-0279-77179671d826 | |
# β β9765a090-b7b0-11ea-3c1f-e5a8ac58d7de | |
# ββfe4c70fe-b7b0-11ea-232b-1996facd33a0 | |
# ββ8e7cb972-b7b1-11ea-2c4d-ed583218740a | |
# ββ4df00b20-b7b1-11ea-0836-41314f8a2d35 | |
# ββ2199d7b2-b7b2-11ea-2e86-95b23658a538 | |
# ββ489b7c9c-b7b1-11ea-3a54-87bd17341b2c | |
# ββa6dcc77c-b7b2-11ea-0c7d-d5686603d182 | |
# ββb15d683c-b7b2-11ea-1090-b392950bedb6 | |
# ββ064d5a78-b7b3-11ea-399c-f968bf9c910a | |
# β β2c7c8cdc-b7b3-11ea-166c-cfd232fd2004 | |
# ββ64e51904-b7b3-11ea-0f72-359d63261b21 | |
# ββd2de4480-b7b0-11ea-143d-033dc76cf6bc | |
# ββ79a01d50-b7b1-11ea-27ee-4161da276cde | |
# β β54f79f6e-b865-11ea-2f16-ff76fe1f14ed | |
# β β1e866730-b7ac-11ea-3df1-9f7f92d504db | |
# ββ9b18067e-b7b3-11ea-0372-d351201a0e7d | |
# β β04c34a64-b7ac-11ea-0cc0-6709153eaf18 | |
# ββ2fdd76dc-b7ce-11ea-12b1-59aa1c44ebbe | |
# β β3bb96bf2-b7cc-11ea-377f-2d5d3f04e96d | |
# β β906a267a-b7ca-11ea-1e73-a56b7e7c9115 | |
# ββ444bc786-b7ce-11ea-0016-ff1fbb17d736 | |
# ββ1c55bd84-b7c9-11ea-0aa5-b95f58fae242 | |
# ββ7485990a-b7af-11ea-10e4-53a3ab5dcea7 | |
# β β34de1bc0-b795-11ea-2cac-bbbc496133ad |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### A Pluto.jl notebook ### | |
# v0.9.11 | |
using Markdown | |
macro bind(def, element) | |
quote | |
local el = $(esc(element)) | |
global $(esc(def)) = Core.applicable(Base.peek, el) ? Base.peek(el) : missing | |
el | |
end | |
end | |
# βββ‘ fcac2038-b7aa-11ea-1320-355e0731afb4 | |
using LinearAlgebra | |
# βββ‘ d7eadad2-b7ad-11ea-22e3-f1e5ded42255 | |
md"# Singular Value Decomposition | |
Lorem ipsum SVD est." | |
# βββ‘ dfd0317e-b85f-11ea-3f2b-fd95c87a0a69 | |
zeros(Float64, 0, 0) | |
# βββ‘ d6f2ee1c-b7ad-11ea-0e1c-fb21a58c5711 | |
md"## Step 1: upload your favorite image:" | |
# βββ‘ 841551d2-b861-11ea-08de-c180ca50b9be | |
struct ImageInput | |
use_camera::Bool | |
default_url::AbstractString | |
maxsize::Integer | |
end | |
# βββ‘ 8c0caad4-b861-11ea-3aab-83e4d3718aa9 | |
img = ImageInput(true, "asdf", 512) | |
# βββ‘ 74cac824-b861-11ea-37e9-e97065879618 | |
ph = """ | |
<span class="pl-image"> | |
<style> | |
.pl-image video { | |
max-width: 250px; | |
} | |
.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" title="Click to take a picture"> | |
<video playsinline autoplay></video> | |
</div> | |
<script> | |
// mostly taken from https://github.com/fonsp/printi-static | |
// (by the same author) | |
const span = this.currentScript.parentElement | |
const video = span.querySelector("video") | |
const img = html`<img crossOrigin="anonymous">` | |
const maxsize = $(img.maxsize) | |
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: Array.from(ctx.getImageData(0, 0, width, height).data), | |
} | |
span.dispatchEvent(new CustomEvent("input")) | |
} | |
navigator.mediaDevices.getUserMedia({ | |
audio: false, | |
video: { | |
facingMode: "environment", | |
}, | |
}).then(function(stream) { | |
window.stream = stream | |
video.srcObject = stream | |
window.cameraConnected = true | |
video.controls = false | |
video.play() | |
video.controls = false | |
}).catch(function(error) { | |
console.log(error) | |
}); | |
span.querySelector("#video-container").onclick = function() { | |
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) | |
}; | |
</script> | |
</span> | |
""" |> HTML; | |
# βββ‘ 711bdf40-b7a5-11ea-2605-97b5d7f7e938 | |
@bind upload_data_file html""" | |
<span> | |
<input type='file' accept="image/*"> | |
<script> | |
const span = this.currentScript.parentElement | |
const input = span.querySelector("input") | |
const img = html`<img crossOrigin="anonymous">` | |
const maxsize = 512 | |
img.onload = () => { | |
const scale = Math.min(1.0, maxsize / img.width, maxsize / img.height) | |
const width = Math.floor(img.width * scale) | |
const height = Math.floor(img.height * scale) | |
const canvas = html`<canvas width=${width} height=${height}>` | |
const ctx = canvas.getContext("2d") | |
ctx.drawImage(img, 0, 0, width, height) | |
span.value = { | |
width: width, | |
height: height, | |
data: Array.from(ctx.getImageData(0, 0, width, height).data), | |
} | |
span.dispatchEvent(new CustomEvent("input")) | |
} | |
input.oninput = (e) => { | |
img.src = URL.createObjectURL(input.files[0]) | |
e.stopPropagation() | |
} | |
// set default URL so that you have something to look at: | |
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/GoldenRetrieverSnow.jpg/320px-GoldenRetrieverSnow.jpg" | |
</script> | |
</span> | |
""" | |
# βββ‘ 54f79f6e-b865-11ea-2f16-ff76fe1f14ed | |
@bind upload_data ph | |
# βββ‘ 212d10d6-b7ae-11ea-1434-fb7d45aaf278 | |
md"The (black-and-white) image data is now available as a Julia 2D array:" | |
# βββ‘ c8f52288-b7a9-11ea-02b7-698ec1d06357 | |
img_data = let | |
# every 4th byte is the Red pixel value | |
reds = UInt8.(upload_data["data"][1:4:end]) | |
# shuffle and flip to get it in the right shape | |
( reshape(reds, (upload_data["width"], upload_data["height"]))') / 255.0 | |
end | |
# βββ‘ 42578c50-b7ae-11ea-2f7d-6b8b3df44aed | |
md"This notebook defines a type `BWImage` that can be used to turn 2D Float arrays into a picture!" | |
# βββ‘ 90715ff6-b7ae-11ea-3780-1d227049322c | |
md"You can use `img_data` like any other Julia 2D array! For example, here is the top left corner of your image:" | |
# βββ‘ 7c8b8c96-b7ae-11ea-149d-61d29d35847c | |
md"## Step 2: running the SVD | |
The Julia standard library package `LinearAlgebra` contains a method to compute the SVD. " | |
# βββ‘ a404b9f8-b7ab-11ea-0b07-a733a3c4f353 | |
π = svd(img_data); | |
# βββ‘ 07834ab8-b7ba-11ea-0279-77179671d826 | |
md"Let's look at the result." | |
# βββ‘ a8039914-b7ce-11ea-25fe-03f554383d31 | |
π | |
# βββ‘ 9765a090-b7b0-11ea-3c1f-e5a8ac58d7de | |
#[π.U, π.S, π.V] | |
# βββ‘ fe4c70fe-b7b0-11ea-232b-1996facd33a0 | |
md"Let's verify the identity | |
$A = U \Sigma V^{\intercal}$" | |
# βββ‘ dbfa7a96-b7b0-11ea-2737-c7bfd486db3c | |
img_data_reconstructed = π.U * Diagonal(π.S) * π.V' | |
# βββ‘ 8e7cb972-b7b1-11ea-2c4d-ed583218740a | |
md"Are they equal?" | |
# βββ‘ 917b7e88-b7b1-11ea-1d05-ef95e73ff181 | |
img_data == img_data_reconstructed | |
# βββ‘ 4df00b20-b7b1-11ea-0836-41314f8a2d35 | |
md"It looks like they are **not** equal - how come? | |
Since we are using a _computer_, the decomposition and multiplication both introduce some numerical errors. So instead of checking whether the reconstructed matrix is _equal_ to the original, we can check how _close_ they are to each other." | |
# βββ‘ 2199d7b2-b7b2-11ea-2e86-95b23658a538 | |
md"One way to quantify the _distance_ between two matrices is to look at the **point-wise difference**. If the **sum** of all differences is close to 0, the matrices are almost equal." | |
# βββ‘ 315955ac-b7b1-11ea-31a1-31a206c2ab72 | |
p1_dist = sum(abs.(img_data_reconstructed - img_data)) | |
# βββ‘ 489b7c9c-b7b1-11ea-3a54-87bd17341b2c | |
md"There are other ways to compare two matrices, such methods are called _**matrix norms**_." | |
# βββ‘ a6dcc77c-b7b2-11ea-0c7d-d5686603d182 | |
md"### The π-norm" | |
# βββ‘ b15d683c-b7b2-11ea-1090-b392950bedb6 | |
md"Another popular matrix norm is the **π_-norm_**: you turn both matrices into a picture, and use your π to see how close they are:" | |
# βββ‘ 064d5a78-b7b3-11ea-399c-f968bf9c910a | |
md"""**How similar are these images?** $(@bind π_dist html"<input>")""" | |
# βββ‘ 2c7c8cdc-b7b3-11ea-166c-cfd232fd2004 | |
π_dist | |
# βββ‘ 64e51904-b7b3-11ea-0f72-359d63261b21 | |
md"In some applications, like _**image compression**_, this is the _most imporant norm_." | |
# βββ‘ d2de4480-b7b0-11ea-143d-033dc76cf6bc | |
md"## Step 3: compression" | |
# βββ‘ 79a01d50-b7b1-11ea-27ee-4161da276cde | |
md"Blabla" | |
# βββ‘ 1e866730-b7ac-11ea-3df1-9f7f92d504db | |
@bind keep HTML("<input type='range' max='$(length(π.S))' value='10'>") | |
# βββ‘ 9b18067e-b7b3-11ea-0372-d351201a0e7d | |
md"Showing the **first $(keep) singular pairs**." | |
# βββ‘ dc229eae-b87a-11ea-33c8-83552d0fc8e3 | |
# βββ‘ 2fdd76dc-b7ce-11ea-12b1-59aa1c44ebbe | |
md"### Store fewer bytes" | |
# βββ‘ 3bb96bf2-b7cc-11ea-377f-2d5d3f04e96d | |
#compressed_size(keep), uncompressed_size() | |
# βββ‘ 075510e6-b7cc-11ea-1abe-8725d3153c12 | |
function uncompressed_size() | |
num_el = length(img_data) | |
return num_el * 8 Γ· 8 | |
end | |
# βββ‘ db5c4ad6-b7cb-11ea-096f-a35ef7bc2c7c | |
function compressed_size(keep) | |
num_el = ( | |
length(π.U[:,1:keep]) + | |
length(π.S[1:keep]) + | |
length(π.V'[1:keep,:]) | |
) | |
return num_el * 16 Γ· 8 | |
end | |
# βββ‘ 906a267a-b7ca-11ea-1e73-a56b7e7c9115 | |
#BWImage(Float16.(F.U)[:,1:keep] * Diagonal(Float16.(F.S[1:keep])) * Float16.(F.V)'[1:keep,:]) | |
# βββ‘ 444bc786-b7ce-11ea-0016-ff1fbb17d736 | |
md"JPEG works in a similar way" | |
# βββ‘ 1c55bd84-b7c9-11ea-0aa5-b95f58fae242 | |
md"### Individual pairs" | |
# βββ‘ 27a61bb6-b7c9-11ea-0122-09850dc4322c | |
@bind pair_index HTML("<input type='range' min='1' max='$(length(π.S))' value='10'>") | |
# βββ‘ 779fc8e6-b7c9-11ea-0d74-4da167b76227 | |
normalize_mat(A, p=2) = A ./ norm(A, p) | |
# βββ‘ 7485990a-b7af-11ea-10e4-53a3ab5dcea7 | |
md"## Going further | |
More stuff to learn about SVD | |
To keep things simple (and dependency-free), this notebook only works with downscaled black-and-white images that you pick using the button. For **color**, **larger images**, or **images from your disk**, you should look into the [`Images.jl`](https://github.com/JuliaImages/Images.jl) package!" | |
# βββ‘ 34de1bc0-b795-11ea-2cac-bbbc496133ad | |
begin | |
struct BWImage | |
data::Array{UInt8, 2} | |
end | |
function BWImage(data::Array{T, 2}) where T <: AbstractFloat | |
BWImage(floor.(UInt8, clamp.(data * 255, 0, 255))) | |
end | |
import Base: show | |
function show(io::IO, ::MIME"image/bmp", i::BWImage) | |
height, width = size(i.data) | |
datawidth = Integer(ceil(width / 4)) * 4 | |
bmp_header_size = 14 | |
dib_header_size = 40 | |
palette_size = 256 * 4 | |
data_size = datawidth * height * 1 | |
# BMP header | |
write(io, 0x42, 0x4d) | |
write(io, UInt32(bmp_header_size + dib_header_size + palette_size + data_size)) | |
write(io, 0x00, 0x00) | |
write(io, 0x00, 0x00) | |
write(io, UInt32(bmp_header_size + dib_header_size + palette_size)) | |
# DIB header | |
write(io, UInt32(dib_header_size)) | |
write(io, Int32(width)) | |
write(io, Int32(-height)) | |
write(io, UInt16(1)) | |
write(io, UInt16(8)) | |
write(io, UInt32(0)) | |
write(io, UInt32(0)) | |
write(io, 0x12, 0x0b, 0x00, 0x00) | |
write(io, 0x12, 0x0b, 0x00, 0x00) | |
write(io, UInt32(0)) | |
write(io, UInt32(0)) | |
# color palette | |
write(io, [[x, x, x, 0x00] for x in UInt8.(0:255)]...) | |
# data | |
padding = fill(0x00, datawidth - width) | |
for y in 1:height | |
write(io, i.data[y,:], padding) | |
end | |
end | |
end | |
# βββ‘ 64ca87b0-b7ae-11ea-384d-0ddb81bdb366 | |
uploaded_image = BWImage(img_data) | |
# βββ‘ b8f3fa2e-b7ae-11ea-1089-3d08cd1e7874 | |
let | |
# the first coordinate is vertical, the second is horizontal (it's a matrix!) | |
half_height = size(img_data)[1] Γ· 2 | |
half_width = size(img_data)[2] Γ· 2 | |
topleft = img_data[1:half_height, 1:half_width] | |
BWImage(topleft) | |
end | |
# βββ‘ f0e75616-b7b2-11ea-1187-714258b32e84 | |
[BWImage(img_data), BWImage(img_data_reconstructed)] | |
# βββ‘ 04c34a64-b7ac-11ea-0cc0-6709153eaf18 | |
BWImage( | |
π.U[:,1:keep] * | |
Diagonal(π.S[1:keep]) * | |
π.V'[1:keep,:] | |
) | |
# βββ‘ 3909738e-b7d1-11ea-0e12-955a86967870 | |
[md"# Hi", md"_there_", [BWImage( | |
π.U[:,1:keep] * | |
Diagonal(π.S[1:keep]) * | |
π.V'[1:keep,:] | |
) for keep in 0:20]...] | |
# βββ‘ 379d608c-b7c9-11ea-1ae2-89a2713a91ff | |
BWImage(normalize_mat(( | |
π.U[:,pair_index:pair_index] * | |
Diagonal(π.S[pair_index:pair_index]) * | |
π.V'[pair_index:pair_index,:] | |
), Inf)) | |
# βββ‘ Cell order: | |
# ββd7eadad2-b7ad-11ea-22e3-f1e5ded42255 | |
# β βdfd0317e-b85f-11ea-3f2b-fd95c87a0a69 | |
# ββd6f2ee1c-b7ad-11ea-0e1c-fb21a58c5711 | |
# β β841551d2-b861-11ea-08de-c180ca50b9be | |
# β β8c0caad4-b861-11ea-3aab-83e4d3718aa9 | |
# β β74cac824-b861-11ea-37e9-e97065879618 | |
# β β711bdf40-b7a5-11ea-2605-97b5d7f7e938 | |
# β β54f79f6e-b865-11ea-2f16-ff76fe1f14ed | |
# ββ212d10d6-b7ae-11ea-1434-fb7d45aaf278 | |
# ββc8f52288-b7a9-11ea-02b7-698ec1d06357 | |
# ββ42578c50-b7ae-11ea-2f7d-6b8b3df44aed | |
# β β64ca87b0-b7ae-11ea-384d-0ddb81bdb366 | |
# ββ90715ff6-b7ae-11ea-3780-1d227049322c | |
# β βb8f3fa2e-b7ae-11ea-1089-3d08cd1e7874 | |
# ββ7c8b8c96-b7ae-11ea-149d-61d29d35847c | |
# β βfcac2038-b7aa-11ea-1320-355e0731afb4 | |
# β βa404b9f8-b7ab-11ea-0b07-a733a3c4f353 | |
# ββ07834ab8-b7ba-11ea-0279-77179671d826 | |
# β βa8039914-b7ce-11ea-25fe-03f554383d31 | |
# β β9765a090-b7b0-11ea-3c1f-e5a8ac58d7de | |
# ββfe4c70fe-b7b0-11ea-232b-1996facd33a0 | |
# β βdbfa7a96-b7b0-11ea-2737-c7bfd486db3c | |
# ββ8e7cb972-b7b1-11ea-2c4d-ed583218740a | |
# β β917b7e88-b7b1-11ea-1d05-ef95e73ff181 | |
# ββ4df00b20-b7b1-11ea-0836-41314f8a2d35 | |
# ββ2199d7b2-b7b2-11ea-2e86-95b23658a538 | |
# β β315955ac-b7b1-11ea-31a1-31a206c2ab72 | |
# ββ489b7c9c-b7b1-11ea-3a54-87bd17341b2c | |
# ββa6dcc77c-b7b2-11ea-0c7d-d5686603d182 | |
# ββb15d683c-b7b2-11ea-1090-b392950bedb6 | |
# ββf0e75616-b7b2-11ea-1187-714258b32e84 | |
# ββ064d5a78-b7b3-11ea-399c-f968bf9c910a | |
# β β2c7c8cdc-b7b3-11ea-166c-cfd232fd2004 | |
# ββ64e51904-b7b3-11ea-0f72-359d63261b21 | |
# ββd2de4480-b7b0-11ea-143d-033dc76cf6bc | |
# ββ79a01d50-b7b1-11ea-27ee-4161da276cde | |
# ββ1e866730-b7ac-11ea-3df1-9f7f92d504db | |
# ββ9b18067e-b7b3-11ea-0372-d351201a0e7d | |
# β β04c34a64-b7ac-11ea-0cc0-6709153eaf18 | |
# β β3909738e-b7d1-11ea-0e12-955a86967870 | |
# β βdc229eae-b87a-11ea-33c8-83552d0fc8e3 | |
# ββ2fdd76dc-b7ce-11ea-12b1-59aa1c44ebbe | |
# β β3bb96bf2-b7cc-11ea-377f-2d5d3f04e96d | |
# β β075510e6-b7cc-11ea-1abe-8725d3153c12 | |
# β βdb5c4ad6-b7cb-11ea-096f-a35ef7bc2c7c | |
# β β906a267a-b7ca-11ea-1e73-a56b7e7c9115 | |
# ββ444bc786-b7ce-11ea-0016-ff1fbb17d736 | |
# ββ1c55bd84-b7c9-11ea-0aa5-b95f58fae242 | |
# ββ27a61bb6-b7c9-11ea-0122-09850dc4322c | |
# β β379d608c-b7c9-11ea-1ae2-89a2713a91ff | |
# β β779fc8e6-b7c9-11ea-0d74-4da167b76227 | |
# ββ7485990a-b7af-11ea-10e4-53a3ab5dcea7 | |
# β β34de1bc0-b795-11ea-2cac-bbbc496133ad |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sure! That one is in the Interactivity sample notebook