Jdi na navigaci předmětu

07: Vizualizace a manipulace s grafikou

Tento notebook je výukovým materiálem v předmětu BI-JUL.21 vyučovaném v zimním semestru akademického roku 2024/2025 Tomášem Kalvodou. Tvorba těchto materiálů byla podpořena NVS FIT.

Hlavní stránkou předmětu, kde jsou i další notebooky a zajímavé informace, je jeho Course Pages stránka.

versioninfo()
Julia Version 1.12.0
Commit b907bd0600f (2025-10-07 15:42 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
  WORD_SIZE: 64
  LLVM: libLLVM-18.1.7 (ORCJIT, skylake)
  GC: Built with stock GC
Threads: 1 default, 1 interactive, 1 GC (on 8 virtual cores)

1. Vizualizace

Nejen v Julia existuje celá řada nástrojů/knihoven pro vytváření různých grafů, diagramů a vizualizací. V Python ekosystému je asi nejpoúžívanější Matplotlib, který je k dispozici i v Julia (viz PyPlot.jl níže).

Všechny tyto nástroje se snaží dosáhnout podobných cílů a nabízejí různě kvalitní implementace. Dost často bývá otázkou vkusu, který z nástrojů použít. V tomto notebooku ukážeme tři možnosti:

Paleta možností je ale širší. Zmiňme alespoň následující (pro zájemce k prozkoumání):

  • Plots: nadstavba nad několika grafickými balíčky.
  • Gadfly: atraktivní a interaktivní výstup.
  • Winston: malý a rychlý, terminologií vychází s MATLABu.
  • UnicodePlots: zajímavý nástroj vytvářející grafy pomocí unicode symbolů a fungující i v terminálu.

Možností je opravdu mnoho a tento notebook je spíše experimentální.

Při používání tohoto notebooku doporučuji vždy mezi sekcemi (různými balíčky) restartovat jádro. Moduly mají podobné zaměření a mohlo by dojít ke kolizím pojmenování metod.


1.1 GR.jl

Balíček GR.jl poskytuje Julia rozhranní k GR frameworku. GR byl vyvinut skupinou Scientific IT-Systems z Peter Grünberg Institutu na Forschungszentrum Jülich.

  • Mezi jeho přednosti patří rychlost.
  • Mezi nevýhody bych zařadil nekvalitní dokumentaci a nekonzistenci API. (Chyba může být ale na straně Julia balíčku...)

Tento balíček pravděpodobně nemáte nainstalovaný, takže nejprve tento musíme nainstalovat (případně viz pokyny k instalaci systémových závislostí):

(@v1.11) pkg> add GR

Zde v notebooku projdeme jen ty nejužitečnější části balíčku. Také tento balíček použijeme jako jakéhosi průvodci po možnostech, u ostatních se totiž dost opakují. Zvídavého čtenáře odkazujeme na dokumentaci s ukázkami dalších typů grafů.

using GR

Tvorbu jednotlivých grafů můžeme oddělit zavoláním figure, případně subplot. K dispozici také je savefig.


plot a oplot

Pomocí plot nepřekvapivě vykreslíme 2D graf funkce. Poziční argumenty mohou mít následující význam:

  • stejně dlouhé vektory x a y reprezentující souřadnice bodů,
  • vektor x a funkce pro výpočet yy-nové hodnoty,
  • pouze vektor y, nezávisle proměnná pak odpovídá indexům.

Dále plot přijímá několik keyword argumentů, nejzajímavější asi jsou:

  • title: titulek grafu (alternativně metoda title),
  • xlabel: titulek grafu (alternativně metoda xlabel),
  • ylabel: titulek grafu (alternativně metoda ylabel),
  • xlim, ylim: rozsah jednotlivých os (alternativně metody xlim a ylim),

a metoda

  • legend: popisky jednotlivých grafů.

Následuje několik jednoduchých ukázek:

xs = LinRange(-10, 10, 100)
ys = sin.(xs)

figure()
plot(xs, ys, title="\$y=\\sin(x)\$", xlabel="\$x\$", ylabel="\$y\$", ylim=(-1.2, 1.2))

Poznámka: Všimněte si, že vykreslení prvního grafu je (nejen zde, takřka vždy) výrazně pomalejší, než těch následujících. To je způsobeno probíhající JIT kompilací. Tento efekt je v Julia světě označován zkratkou TTFP (Time To First Plot). Tomuto efektu se lze v některých situacích částečně vyhnout pomocí PackageCompiler.jl.


Pokud chceme vykreslit více dat do jednoho obrázku, použijeme k tomu oplot, nastavení popisků atp. se bohužel bere z posledního volání oplus (lze to obejít nastavením těchto parametrů pomocí metod, ne pomocí keyword hodnot).

legend("\$\\sin\$", "\$\\cos\$")

figure()
plot(xs, ys, title="\$y=\\sin(x)\$", xlabel="\$x\$", ylabel="\$y\$", ylim=(-1.2, 1.2))
oplot(xs, cos, title="Trigonometrické funkce", xlabel="\$x\$", ylabel="\$y\$", ylim=(-1.2, 1.2))

Styl čáry lze kontrolovat pomocí klíčů:

  • setlinetype: druh čáry (viz GR.LINETYPE_...),
  • setlinewidth: šířka čáry,
  • setlinecolorind: barva čáry (???).
figure()

setlinetype(GR.LINETYPE_TRIPLE_DOT)
plot(LinRange(-1, 1, 50), exp, linewidth=5)
figure()

setlinetype(GR.LINETYPE_TRIPLE_DOT)
plot(LinRange(-1, 1, 3), exp, linewidth=5)

Zde jsem se vzdal a proto:


Cvičení: ???

  1. Pokuste* se změnit barvu čáry například na červenou.

* tj. úspěch není nutný.

figure()

@which plot(LinRange(-1, 1, 50), exp)
plot(args::Union{AbstractString, Function, AbstractMatrix, AbstractVector}...; kv...) in GR.jlgr at /home/kalvin/.julia/packages/GR/TPhYU/src/jlgr.jl:1894

scatter

Scatter plot vykresluje pouze jednotlivé body a neinterpoluje (lineárně) mezi nimi:

figure()

title("\$a_n = (-1)^n\$")
legend("\$a_n\$")
xlabel("\$n\$")
ylabel("\$a_n\$")

plot([(-1)^n for n=0:9])
?legend
search: legend seekend Signed begin prepend! signed setenv Regex end

Set the legend of the plot.

The plot legend is drawn using the extended text function GR.textext. You can use a subset of LaTeX math syntax, but will need to escape certain characters, e.g. parentheses. For more information see the documentation of GR.textext.

:param args: The legend strings

Usage examples:

.. code-block:: julia

julia> # Set the legends to "a" and "b"
julia> legend("a", "b")
figure()

title("\$a_n = (-1)^n\$")
legend("\$a_n\$")
xlabel("\$n\$")
ylabel("\$a_n\$")

x = Float64[j for j=0:9]
y = Float64[(-1)^n for n=0:9]

scatter(x, y, xlim=(-0.2, 9.2), ylim=(-1.2, 1.2))
figure()

title("\$a_n = (-1)^n\$")
legend("\$a_n\$")
xlabel("\$n\$")
ylabel("\$a_n\$")

x = Float64[j for j=0:9]
y = Float64[(-1)^n for n=0:9]
s = 2*ones(10)
c = rand(0:255, 10)

scatter(x, y, s, c, xlim=(-0.2, 9.2), ylim=(-1.2, 1.2))
x
10-element Vector{Float64}:
 0.0
 1.0
 2.0
 3.0
 4.0
 5.0
 6.0
 7.0
 8.0
 9.0
y
10-element Vector{Float64}:
  1.0
 -1.0
  1.0
 -1.0
  1.0
 -1.0
  1.0
 -1.0
  1.0
 -1.0
s
10-element Vector{Float64}:
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
c
10-element Vector{Int64}:
 110
 119
  18
 234
  21
   4
 196
 246
 222
 134
figure()
x = [z/10 for z=0:10]
y = LinRange(0, 1, 11)
s = LinRange(1, 3, 11)
c = LinRange(0, 255, 11)
scatter(x, y, s, c)
x
11-element Vector{Float64}:
 0.0
 0.1
 0.2
 0.3
 0.4
 0.5
 0.6
 0.7
 0.8
 0.9
 1.0
y
11-element LinRange{Float64, Int64}:
 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0
s
11-element LinRange{Float64, Int64}:
 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
c
11-element LinRange{Float64, Int64}:
 0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0

Můžeme snadno i změnit barevnou škálu.

figure()

ts = LinRange(0, 4pi, 40)
x  = [t * sin(t) for t in ts]
y  = [t * cos(t) for t in ts]
s  = ts
c  = LinRange(0, 255, 40)
scatter(x, y, s, c, colormap=GR.COLORMAP_SPRING)

plot3

plot3 je 3D analog čárového grafu. Přijímá tedy tři vektory x, y a z, jejichž složky popořadě tvoří jednotlivé body křivky.

ts = LinRange(0, 20, 400)
xs = [ sin(t) for t in ts ]
ys = [ cos(t) for t in ts ]
zs = ts

figure()
plot3(xs, ys, zs, xlabel="x", ylabel="y", zlabel="z", title="Šroubovice")

histogram a hexbin, Cvičení

Histogram bere vektor x hodnot a jako keyword argument nbins udávající počet přihrádek. Budeme ho ilustrovat na náhodném generátoru bodů na kružnici (vzpomeňte dřívější diskuzi).

n = 10_000
xs = rand(n) .- 0.5
ys = rand(n) .- 0.5;
figure()
scatter(xs, ys, xlim=(-1.2,1.2), ylim=(-1.2,1.2))
for j=1:length(xs)
    r = sqrt(xs[j]^2 + ys[j]^2)
    xs[j] /= r
    ys[j] /= r
end
figure()
scatter(xs, ys)
φs = [ atan(ys[j], xs[j]) for j=1:length(xs) ];
figure()
histogram(φs, nbins=50, xlabel="\$\\varphi\$", ylabel="hits")
savefig("test.pdf")
savefig("test.png")
savefig("test.svg")

Takovýto generátor evidentně preferuje směry odpovídající osám kvadrantů.

Dvourozměrný ekvivalent histogramu (tj. pro dvousložková data) je metoda hexbin, která funguje analogicky, jen rovinu rozděluje na šestiúhelníky.

xs = rand(10^6)
ys = randn(10^6)

figure()
hexbin(xs, ys)

contour a contourf

Contour plot je vizualizace funkce dvou proměnných, která v jejím definičním oboru znázorňuje křivky, kde je daná funkce konstantní (tzv. kontury). Varianta s f v názvu vybarvuje plochy mezi konturami (fill).

Metody očekávají buď trojici vektorů x, y, z, kde x a y definují souřadnice mřížky a z obsahuje funkční hodnoty na této mřížce (tj. z je matice). Místo z můžeme předat funkci dvou proměnných.

figure()
contour(LinRange(-1, 1, 50), LinRange(-2, 2, 50), (x, y) -> 3x^2 + y^2,
    colormap=GR.COLORMAP_AUTUMN, xlabel="\$x\$", ylabel="\$y\$")

Pozor, jak se GR chová k pořadí os v buňce níže!

figure()

xs = LinRange(-10, 10, 150)
ys = LinRange(-15, 15, 100)
zs = [ sin(x)*cos(y) for y in ys, x in xs ]

contourf(xs, ys, zs)
zs
100×150 Matrix{Float64}:
 -0.413286   -0.324264   -0.229408   …   0.229408    0.324264    0.413286
 -0.288886   -0.226659   -0.160355       0.160355    0.226659    0.288886
 -0.13816    -0.1084     -0.0766901      0.0766901   0.1084      0.13816
  0.0251563   0.0197376   0.0139638     -0.0139638  -0.0197376  -0.0251563
  0.18618     0.146077    0.103345      -0.103345   -0.146077   -0.18618
  0.330237    0.259104    0.183309   …  -0.183309   -0.259104   -0.330237
  0.444201    0.34852     0.246569      -0.246569   -0.34852    -0.444201
  0.517687    0.406177    0.287359      -0.287359   -0.406177   -0.517687
  0.543997    0.42682     0.301964      -0.301964   -0.42682    -0.543997
  0.520735    0.408568    0.289051      -0.289051   -0.408568   -0.520735
  0.450019    0.353085    0.249798   …  -0.249798   -0.353085   -0.450019
  0.338295    0.265426    0.187782      -0.187782   -0.265426   -0.338295
  0.195743    0.15358     0.108654      -0.108654   -0.15358    -0.195743
  ⋮                                  ⋱                          
  0.338295    0.265426    0.187782      -0.187782   -0.265426   -0.338295
  0.450019    0.353085    0.249798      -0.249798   -0.353085   -0.450019
  0.520735    0.408568    0.289051   …  -0.289051   -0.408568   -0.520735
  0.543997    0.42682     0.301964      -0.301964   -0.42682    -0.543997
  0.517687    0.406177    0.287359      -0.287359   -0.406177   -0.517687
  0.444201    0.34852     0.246569      -0.246569   -0.34852    -0.444201
  0.330237    0.259104    0.183309      -0.183309   -0.259104   -0.330237
  0.18618     0.146077    0.103345   …  -0.103345   -0.146077   -0.18618
  0.0251563   0.0197376   0.0139638     -0.0139638  -0.0197376  -0.0251563
 -0.13816    -0.1084     -0.0766901      0.0766901   0.1084      0.13816
 -0.288886   -0.226659   -0.160355       0.160355    0.226659    0.288886
 -0.413286   -0.324264   -0.229408       0.229408    0.324264    0.413286

heatmap (od B231 i B241 funguje divně!)

heatmap funguje v podstatě stejně jako contour, jen se nesnaží nalézt křivky konstantní hodnoty funkce (což je netriviální! Odpovídající algoritmus marching cubes algorithm (zde squares -- 2D varianta) byl ještě do relativně nedávná doby pod patentem.)

figure()

xs = LinRange(-10, 10, 200)
ys = LinRange(-10, 10, 200)
zs = [ sin(x)*cos(y/3) for x in xs, y in ys ]

heatmap(zs, colormap=GR.COLORMAP_FLAME)
figure()

xs = LinRange(-10, 10, 200)
ys = LinRange(-10, 10, 200)
zs = [ sin(x)*cos(y/3) for x in xs, y in ys ]

heatmap(xs, ys, zs, colormap=GR.COLORMAP_FLAME)
figure()

# Create example data
x = LinRange(-2, 2, 40)
y = LinRange(0, pi, 20)
z = sin.(x') .+ cos.(y)
# Draw the heatmap
heatmap(z)

Z neznámého důvodu od jisté doby heatmap nefunguje v GR... :-/

?heatmap

imshow

Tato metoda je velmi užitečná. Umožňuje nám graficky znázornit matici.

h = [ i + j - 1 for i=1:10, j=1:10 ]

figure()
imshow(h, colormap=GR.COLORMAP_INFERNO)
figure()

xs = LinRange(-10, 10, 200)
ys = LinRange(-10, 10, 200)
zs = [ sin(x)*cos(y/3) for x in xs, y in ys ]

imshow(zs, colormap=GR.COLORMAP_WINTER)
figure()

xs = LinRange(-10, 10, 200)
ys = LinRange(-10, 10, 200)
zs = [ sin(x)*cos(y) for x in xs, y in ys ]

imshow(zs, colormap=GR.COLORMAP_FLAME)

Cvičení: Julia v Julia

Uvažme zobrazení f:CCf: \mathbb{C} \to \mathbb{C} zadané předpisem fc(z)=z2+cf_c(z) = z^2 + c, pro pevně zadanou konstantu cCc \in \mathbb{C}. Zkoumáme pak množinu bodů zCz\in\mathbb{C}, pro které je posloupnost (fcn(z))n=1(f^n_c(z))_{n=1}^\infty omezená (mocnina zde nyní označuje opakované skládání zobrazení).

Přibližně se lze k této množině blížit takto: zvolme dostatečně velké R>0R > 0 a maximální počet iterací MNM \in \mathbb{N} a pro zz z nějaké zvolené množiny (typicky mřížka) naměřme, kolik iterací musíme provést, než se dostaneme dále než RR od počátku.

Proveďte tento výpočet pro c=0.8+0.156ic = -0.8 + 0.156i a c=(φ2)+(φ1)ic = (\varphi - 2) + (\varphi - 1)i.

function julia(c::Complex{Float64}, R::Float64, reals, imags; imax::Int64=1_000)
    data = fill(imax, length(reals), length(imags))
    f(u) = u^2 + c
    
    for j = axes(reals, 1), k = axes(imags, 1)
        z = reals[j] + 1im * imags[k]
        
        for n = 1:imax
            z = f(z)
            if abs(z) > R
                data[j, k] = n
                break
            end
        end
    end
    
    return reverse(transpose(data), dims=1)
end
julia (generic function with 1 method)
res = LinRange(-2, 2, 10)
ims = LinRange(-1, 1, 10)
data1 = julia(-0.8 + 0.156im, 4.0, res, ims)
10×10 Matrix{Int64}:
 1  2   2    3    3    3    2   2  2  1
 2  2   3    3    5    4    3   2  2  1
 2  2   4    5  116   11    4   3  2  2
 2  3   9    8   65  108    7   4  3  2
 2  4  60   19   21  131  125   7  3  2
 2  3   7  125  131   21   19  60  4  2
 2  3   4    7  108   65    8   9  3  2
 2  2   3    4   11  116    5   4  2  2
 1  2   2    3    4    5    3   3  2  2
 1  2   2    2    3    3    3   2  2  1
res = LinRange(-2, 2, 20)
ims = LinRange(-1, 1, 10)
data1 = julia(-0.8 + 0.156im, 4.0, res, ims)
10×20 Matrix{Int64}:
 1  2  2   2   2    2    3    3   3  …   3    3    2    2   2   2  2  2  1
 2  2  2   2   3    3    3    4   4      4    3    3    3   2   2  2  2  1
 2  2  2   3   4    4    5    6  12      6    4    4    3   3   2  2  2  2
 2  2  3   4   7  179    9  187  42     32    6    7   12   4   3  3  2  2
 2  2  4  23  52   42   18   52  20     14   87  414   92   6   5  3  2  2
 2  2  3   5   6   92  414   87  14  …  20   52   18   42  52  23  4  2  2
 2  2  3   3   4   12    7    6  32     42  187    9  179   7   4  3  2  2
 2  2  2   2   3    3    4    4   6     12    6    5    4   4   3  2  2  2
 1  2  2   2   2    3    3    3   4      4    4    3    3   3   2  2  2  2
 1  2  2   2   2    2    2    3   3      3    3    3    2   2   2  2  2  1
imshow(data1, colormap=GR.COLORMAP_BLUESCALE)
UndefVarError: `GR` not defined in `Main`
Suggestion: check for spelling errors or missing imports.

Stacktrace:
 [1] top-level scope
   @ In[10]:1
 [2] eval(m::Module, e::Any)
   @ Core ./boot.jl:489
imshow([0 1 0; 0 0 1])
PyObject <matplotlib.image.AxesImage object at 0x7f8b799756d0>
res = LinRange(-2, 2, 600)
ims = LinRange(-1, 1, 400)
data2 = julia(-0.8 + 0.156im, 4.0, res, ims, imax=1000);
imshow(data2, colormap=GR.COLORMAP_BLUESCALE)
UndefVarError: `GR` not defined in `Main`
Suggestion: check for spelling errors or missing imports.

Stacktrace:
 [1] top-level scope
   @ In[13]:1
 [2] eval(m::Module, e::Any)
   @ Core ./boot.jl:489
res = LinRange(-0.3, -0.1, 800)
ims = LinRange(-0.35, -0.15, 800)
data2 = julia(-0.8 + 0.156im, 4.0, res, ims);
imshow(data2, colormap=GR.COLORMAP_INFERNO)

Nyní obrázky generované pomocí PyPlot (viz níže).

using PyPlot
res = LinRange(-2, 2, 600)
ims = LinRange(-1, 1, 400)
data2 = julia(-0.8 + 0.156im, 4.0, res, ims);

fig, ax = plt.subplots()
ax.imshow(data2, extent=[res[1], res[end], ims[1], ims[end]])
PyObject <matplotlib.image.AxesImage object at 0x7f8b799c3610>
res = LinRange(0, 2, 600)
ims = LinRange(0, 1, 400)
data2 = julia(-0.8 + 0.156im, 4.0, res, ims);

fig, ax = plt.subplots()
ax.imshow(data2, extent=[res[1], res[end], ims[1], ims[end]])
PyObject <matplotlib.image.AxesImage object at 0x7f8b7985e350>
res = LinRange(-2, 2, 1000)
ims = LinRange(-1, 1, 1000)
c = (MathConstants.golden - 2) + (MathConstants.golden - 1)*1im
data3 = julia(c, 5.0, res, ims);
fig, ax = plt.subplots()
ax.imshow(data3, extent=[res[1], res[end], ims[1], ims[end]], cmap="Reds")
PyObject <matplotlib.image.AxesImage object at 0x7f8b798af890>
res = LinRange(0.25, 0.75, 1000)
ims = LinRange(-0.75, -0.25, 1000)
c = (MathConstants.golden - 2) + (MathConstants.golden - 1)*1im
data4 = julia(c, 5.0, res, ims);
fig, ax = plt.subplots()
ax.imshow(data4, extent=[res[1], res[end], ims[1], ims[end]], cmap="Reds")
PyObject <matplotlib.image.AxesImage object at 0x7f8b7805e710>

1.2 PyPlot.jl

PyPlot.jl je v podstatě Julia rozhraní k Matplotlib Pyhon knihovně, přesněji k funkcím v matplotlib.pyplot Python modulu.

Pokud znáte tuto knihovnu, pak by pro vás tato varianta měla být asi nejpříjemnější na používání. Pro ostatní mohou být užitečné tyto taháky.

  • Výhody: lepší dokumentace, obrovská základna uživatelů, ozkoušený a robustní software.
  • Nevýhody: pomalejší, závislost na Pythonu.

Budete potřebovat mít nainstalovánu tuto knihovnu ($ pip install matplotlib) a poté nainstalovat Julia balíček PyPlot.jl:

(@v1.11) pkg> add PyPlot

Pro porovnání ukažme jen základní typ grafu a pak zkusme vytvořit komplikovanější diagram v cvičení.

# Doporučuji restartovat jádro!
using PyPlot

plot

Logika této metody je velmi podobná jako analogické metody v GR.jl výše.

xs = LinRange(0, 10, 200)
ys = [ sin(x) - sin(2x)/2 + sin(3x)/3 - sin(4x)/4 for x in xs ]

f = figure()
plot(xs, ys, color="red", linewidth=4.0, linestyle="--")
grid()
title("Můj úžasný graf")
xlabel("\$x\$")
ylabel("\$y\$");

Výsledné obrázky lze opět snadno uložit.

f.savefig("matplotlib_fig.png")
f.savefig("matplotlib_fig.pdf")

K dispozici také je několik přednastavených stylů, například:

plt.style.available
29-element Vector{String}:
 "Solarize_Light2"
 "_classic_test_patch"
 "_mpl-gallery"
 "_mpl-gallery-nogrid"
 "bmh"
 "classic"
 "dark_background"
 "fast"
 "fivethirtyeight"
 "ggplot"
 "grayscale"
 "petroff10"
 "seaborn-v0_8"
 ⋮
 "seaborn-v0_8-darkgrid"
 "seaborn-v0_8-deep"
 "seaborn-v0_8-muted"
 "seaborn-v0_8-notebook"
 "seaborn-v0_8-paper"
 "seaborn-v0_8-pastel"
 "seaborn-v0_8-poster"
 "seaborn-v0_8-talk"
 "seaborn-v0_8-ticks"
 "seaborn-v0_8-white"
 "seaborn-v0_8-whitegrid"
 "tableau-colorblind10"
f = figure()
plt.style.use("seaborn-v0_8")
plot(xs, ys, color="red", linewidth=4.0, linestyle="--")
title("Můj úžasný graf")
xlabel(L"$x$")
ylabel(L"$y$");

Cvičení: Řešení ODE

V předchozí lekci jsme si ukázali, jak vyřešit obyčejnou diferenciální rovnici y=Ayy' = \mathbb{A} y, y(0)=y0Cny(0) = y_0 \in \mathbb{C}^n, pomocí maticové exponenciály. Řešením byla vektorová funkce y(t)=exp(tA)y0y(t) = \exp(t\mathbb{A}) y_0.

Konkrétně jsme zvolili

A=(0110).\mathbb{A} = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix}.

Pokuste se vizualizovat tato řešení (v rovině) pro pár počátečních podmínek.

Poznámka: Viz předchozí notebook.

A = Float64[0 -1; 1 0]

function solution(matrix::Matrix{Float64}, initial_condition::Vector{Float64}, timestamps)
    data = zeros(length(timestamps),2)
    y    = initial_condition

    for j in axes(data, 1)
        data[j, :] = y
        y = exp(ts[j] * matrix) * initial_condition
    end

    return data
end
solution (generic function with 3 methods)
ts = LinRange(0, 3, 100)
s1 = solution(A, [1.0, 0.0], ts)
s2 = solution(A, [-0.5, 0.0], ts)
s3 = solution(A, [1.0, 1.0], ts);
f = figure()

scatter(s1[:,1], s1[:,2], color="red")
scatter(s2[:,1], s2[:,2], color="green")
scatter(s3[:,1], s3[:,2], color="blue")
grid()
title("Trajektorie")
xlabel("\$y_1\$")
ylabel("\$y_2\$");
ts = LinRange(0, 7, 350)
s1 = solution(A, [1.0, 0.0], ts)
s2 = solution(A, [-0.5, 0.0], ts)
s3 = solution(A, [1.0, 1.0], ts);
fig, ax = plt.subplots()
ax.set_aspect("equal")

scatter(s1[:,1], s1[:,2], color="red")
scatter(s2[:,1], s2[:,2], color="green")
scatter(s3[:,1], s3[:,2], color="blue")
grid()
title("Trajektorie")
xlabel("\$y_1\$")
ylabel("\$y_2\$");

1.3 PGFPlotsX.jl

Tento balíček využívá pro generování grafů LaTeX balíček PGFPlots.

  • Výhody: zřejmě nejkvalitnější výstup, vhodný i do publikací.
  • Nevýhody: ideálně vyžaduje familiaritu s PGFPlots, náročnější instalace.

Musíte mít lokálně nainstalovaný LaTeX s balíčkem PGFPlots. Poté jen přidáme Julia balíček

(@v1.11) pkg> add PGFPlotsX
using PGFPlotsX
[ Info: Precompiling StructUtilsTablesExt [06a1ca1f-7bc7-53d2-8cfe-f45bf2a4bdd3] (cache misses: wrong dep version loaded (2))
┌ Warning: Module StructUtils with build ID fafbfcfd-3f39-4f5b-fc0f-f447682647b8 is missing from the cache.
│ This may mean StructUtils [ec057cc2-7a8d-4b58-b3b3-92acb9f63b42] does not support precompilation but is imported by a module that does.
└ @ Base loading.jl:2613

SYSTEM: caught exception of type :MethodError while trying to print a failed Task notice; giving up
┌ Info: Skipping precompilation due to precompilable error. Importing StructUtilsTablesExt [06a1ca1f-7bc7-53d2-8cfe-f45bf2a4bdd3].
└   exception = Error when precompiling module, potentially caused by a __precompile__(false) declaration in the module.

Takřka nutností pro používání tohoto balíčku je prostudování si PGFPlots manuálu, který je velmi podrobný a názorný.

xs = LinRange(0, 20, 100)
ys = [ cos(x)/sqrt(x+1) for x in xs ];

Makro @pgf nám umožňuje parsovat výrazy, které by nebyly validní Julia kód, například parametry grafu:

options = @pgf {
    title => "Tlumená oscilace",
    xlabel => "\$x\$",
    ylabel => "\$y\$",
    grid => "major"
}
[title={Tlumená oscilace}, xlabel={$x$}, ylabel={$y$}, grid={major}] 

Grafické objekty pak vytvoříme pomocí konstruktorů, jejichž jména kopírují názvy LaTeX maker a prostředí.

g = Axis(options, Plot(Table(xs, ys)))

Můžeme si i prohlédnout LaTeX kód, který se vygeneroval:

print_tex(g)
\begin{axis}[title={Tlumená oscilace}, xlabel={$x$}, ylabel={$y$}, grid={major}]
    \addplot
        table[row sep={\\}]
        {
            \\
            0.0  1.0  \\
            0.20202020202020204  0.893554245654044  \\
            0.4040404040404041  0.775983556965637  \\
            0.6060606060606061  0.6485406037552491  \\
            0.8080808080808082  0.5138048905565358  \\
            1.0101010101010102  0.3750758675276676  \\
            1.2121212121212122  0.23601775972659012  \\
            1.4141414141414141  0.10041183197927928  \\
            1.6161616161616164  -0.028037702725686314  \\
            1.8181818181818183  -0.14586492596796072  \\
            2.0202020202020203  -0.24997819449287356  \\
            2.2222222222222223  -0.3377729061720941  \\
            2.4242424242424243  -0.4072208430616047  \\
            2.6262626262626263  -0.4569351605487472  \\
            2.8282828282828283  -0.4862101377254743  \\
            3.0303030303030303  -0.4950352538653798  \\
            3.2323232323232327  -0.484083816106636  \\
            3.4343434343434343  -0.45467712002295513  \\
            3.6363636363636367  -0.4087259023159157  \\
            3.8383838383838382  -0.348651588805861  \\
            4.040404040404041  -0.2772905093754423  \\
            4.242424242424242  -0.19778481180957286  \\
            4.444444444444445  -0.11346423350720694  \\
            4.646464646464646  -0.027723165846137755  \\
            4.848484848484849  0.056102440473789546  \\
            5.050505050505051  0.13485383368998885  \\
            5.252525252525253  0.2056596042718416  \\
            5.454545454545454  0.2660333065780028  \\
            5.656565656565657  0.31395516431249626  \\
            5.858585858585858  0.3479350974447313  \\
            6.0606060606060606  0.3670550360768516  \\
            6.262626262626263  0.37098928216349664  \\
            6.464646464646465  0.3600025126700308  \\
            6.666666666666666  0.334925856167946  \\
            6.8686868686868685  0.2971122875082646  \\
            7.070707070707071  0.2483733418065222  \\
            7.272727272727273  0.19089982160318078  \\
            7.474747474747475  0.1271697352276353  \\
            7.6767676767676765  0.05984713990471977  \\
            7.878787878787879  -0.00832414513650889  \\
            8.080808080808081  -0.07462785190881611  \\
            8.282828282828284  -0.13647951821882634  \\
            8.484848484848484  -0.19152377168661003  \\
            8.686868686868687  -0.23772105099838264  \\
            8.88888888888889  -0.27342062443246246  \\
            9.09090909090909  -0.29741733199031645  \\
            9.292929292929292  -0.30899013494359545  \\
            9.494949494949495  -0.30792127917779194  \\
            9.696969696969697  -0.2944956380370906  \\
            9.8989898989899  -0.2694805669827386  \\
            10.101010101010102  -0.2340873468053269  \\
            10.303030303030303  -0.1899159860547257  \\
            10.505050505050505  -0.13888577066265204  \\
            10.707070707070708  -0.08315446652729264  \\
            10.909090909090908  -0.02502948025897692  \\
            11.11111111111111  0.033125449750166375  \\
            11.313131313131313  0.0889843341231118  \\
            11.515151515151516  0.14035010633054038  \\
            11.717171717171716  0.18523937201735224  \\
            11.919191919191919  0.22195751208390194  \\
            12.121212121212121  0.24916133517051656  \\
            12.323232323232322  0.265906996654124  \\
            12.525252525252526  0.27168150542183467  \\
            12.727272727272727  0.26641680216167013  \\
            12.92929292929293  0.25048608730278416  \\
            13.131313131313131  0.22468277566227216  \\
            13.333333333333332  0.1901831310210957  \\
            13.535353535353536  0.1484942611195437  \\
            13.737373737373737  0.10138970795629311  \\
            13.939393939393941  0.050835328894516835  \\
            14.141414141414142  -0.0010914861119250925  \\
            14.343434343434343  -0.05228598940028595  \\
            14.545454545454547  -0.10070027530470042  \\
            14.747474747474747  -0.14442417523563253  \\
            14.94949494949495  -0.1817596870160649  \\
            15.151515151515152  -0.21128606825729157  \\
            15.353535353535353  -0.23191312435899492  \\
            15.555555555555555  -0.24292071556420858  \\
            15.757575757575758  -0.24398307403676198  \\
            15.95959595959596  -0.23517713813765204  \\
            16.161616161616163  -0.2169747521993337  \\
            16.363636363636363  -0.19021922062382077  \\
            16.565656565656568  -0.1560873198104007  \\
            16.767676767676768  -0.11603843624934314  \\
            16.96969696969697  -0.07175299227959078  \\
            17.17171717171717  -0.02506272370473082  \\
            17.373737373737374  0.022124329366016028  \\
            17.575757575757574  0.06790107619516698  \\
            17.77777777777778  0.11043777281991562  \\
            17.97979797979798  0.14805452738856317  \\
            18.18181818181818  0.1792870624657542  \\
            18.383838383838384  0.20294317957898042  \\
            18.585858585858585  0.21814776617432916  \\
            18.78787878787879  0.2243746621565776  \\
            18.98989898989899  0.22146424265126738  \\
            19.19191919191919  0.20962615358154776  \\
            19.393939393939394  0.1894272337188925  \\
            19.595959595959595  0.1617652471816049  \\
            19.7979797979798  0.12782961052335254  \\
            20.0  0.08905080657207223  \\
        }
        ;
\end{axis}

PGFPlots nabízí bohaté možnosti změny stylu a prezentace:

@pgf Axis(options, Plot({"red", "line width" => "2pt" }, Table(xs, ys)))

PGFPlots zvládá i 3D grafy (v LaTeXu!!!).

@pgf Axis({"colorbar"},
    Plot3({"surf", "samples" => "15", "domain" => "0:1", "y domain" => "-1:1"}, Expression("x^2 - y^2"))
)

1.4 Makie.jl

Do čtvrtice ještě jedna poměrně zajímavá možnost. Pro podrobnější prozkoumání doporučuji vaší pozornosti galerii.

  • Výhody: poměrně velmi dobrá dokumentace se spoustou příkladů.
  • Nevýhody: vleče se při prvním startu.

Balíček nabízí několik backendů: CairoMakie.jl (2D), GLMakie.jl a WGLMakie.jl. Instalace je opět standardní a nebudu ji zde opakovat.

using CairoMakie
xs = LinRange(0, 20, 200)
ys = sin.(xs) .* xs;
fig, ax, plt = lines(xs, ys)
lines!(xs, cos.(xs))

ax.xlabel = "x"
ax.ylabel = "y"
ax.title = "Pokusný graf"

current_figure()

Cvičení: Mandelbrotův fraktál

function mandelbrot(R::Float64, reals, imags; imax::Int64=50)
    data = fill(imax, length(reals), length(imags))
    f(u, c) = u^2 + c 
    
    for j = axes(reals, 1), k = axes(imags, 1)
        c = reals[j] + 1im * imags[k]
        z = zero(c)
        
        for n = 1:imax
            z = f(z, c)
            if abs(z) > R
                data[j, k] = n
                break
            end
        end
    end
    
    return data
end
mandelbrot (generic function with 1 method)
res = LinRange(-2, 1, 400)
ims = LinRange(-1, 1, 200)
data1 = mandelbrot(10.0, res, ims);
heatmap(res, ims, data1, colormap = :deep)
res = LinRange(-1.51, -1.49, 600)
ims = LinRange(-0.01, 0.01, 400)
data1 = mandelbrot(15.0, res, ims, imax=50);
heatmap(res, ims, data1, colormap = :inferno)
res = LinRange(-1.505, -1.500, 600)
ims = LinRange(-0.002, 0.002, 400)
data1 = mandelbrot(50.0, res, ims, imax=200);
heatmap(res, ims, data1, colormap = :viridis)

3D grafy

Makie poskytuje dva backendy schopné vykreslovat 3D grafiku, GLMakie.jl a WGLMakie.jl.

using GLMakie

xs = LinRange(0, 10, 100)
ys = LinRange(0, 15, 100)
zs = [cos(x) * sin(2*y) + x + y for x in xs, y in ys]

surface(xs, ys, zs, axis=(type=Axis3,))

Cvičení: Vizualizace PRNG založeného na logistickém zobrazení

function my_prng(n, seed)
    data = zeros(n)
    data[1] = seed
    
    for j = 2:n
        data[j] = 4data[j-1]*(1 - data[j-1])
    end
    
    return data
end
data = my_prng(10^6, 0.7500000001);

Jak tato posloupnost vypadá?

using CairoMakie
fig, ax, plt = scatter(1:1000, data[1:1000])

ax.xlabel = "n"

current_figure()
hist(data, bins=50, normalization = :pdf)
f(x) = acos(1-2x)/pi
udata = f.(data);
hist(udata, bins=50, normalization = :pdf)

1.5 Interact.jl

Tento balíček umožňuje vytvářet interaktivní vizualizace. Pokyny k instalaci jsou tentokrát malinko komplikovanější, viz Getting Started.

using Interact, CairoMakie
xs = LinRange(-5, 20, 200);
@manipulate for a=0:0.01:1, ω=0:0.01:2
    ys  = [ a * sin(ω * x) for x in xs  ]
    vbox(lines(xs, ys))
end

1.6 UnicodePlots.jl

Samostatnou kapitolou a disciplínou je vytváření grafů pomocí unicode znaků (lze i v terminálu, vyzkoušejte!).

using UnicodePlots
lineplot(LinRange(0, 10, 100), sin.(LinRange(0, 10, 100)), title="Sinus")
      ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Sinus⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 
      ┌────────────────────────────────────────┐ 
    1 │⠀⠀⠀⠀⡰⠉⠉⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠉⠑⡄⠀⠀⠀⠀⠀⠀│ 
      │⠀⠀⠀⡰⠁⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀│ 
      │⠀⠀⢠⠃⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠸⡀⠀⠀⠀⠀│ 
      │⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀⠀⠀│ 
      │⠀⡸⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠁⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀│ 
      │⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⠀⠀⠀│ 
      │⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀│ 
      │⠧⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⢵⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⢤⠧⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⢵⠤⠤│ 
      │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀│ 
      │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀│ 
      │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇│ 
      │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡀⠀⠀⠀⠀⠀⠀⢰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘│ 
      │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
      │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
   -1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⣀⣀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
      └────────────────────────────────────────┘ 
      ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀10⠀ 
heatmap(randn(100, 100))
       ┌──────────────────────────────┐  4  
   100 │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ ┌──┐
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
     1 │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ └──┘
       └──────────────────────────────┘ -3  
        1                          100      
using Images, FileIO

img_Gauss = load("../homeworks/B211/files/gauss_portret.jpg")
img_Gauss = map(x -> Float64(x.r), img_Gauss)

heatmap(reverse(img_Gauss, dims=1))
       ┌──────────────────────────┐  1  
   480 │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ ┌──┐
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
       │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ │▄▄│
     1 │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ └──┘
       └──────────────────────────┘  0  
        1                      422      

2. Images.jl, Colors.jl, ImageMagick.jl, ImageMetadata.jl...

Tyto balíčky se specializují přímo na práci s obrázky, jejich vytváření a jejich editaci. Nespadají tedy přímo do kategorie předchozích balíčků. Využijete je třeba při rychlém generování obrázků z matic nebo při strojovém před/zpracování obrázků (viz ImageFeatures.jl, ImageTransformation.jl nebo ImageSegmentation.jl, ImageDraw.jl).

Instalace je opět standardní (]add Images, ]add Colors).

using Images, Colors

2.1 Vytvoření obrázku

img_data = rand(400, 600)
img_data[1:50, 1:50] .= 1
img_data
400×600 Matrix{Float64}:
 1.0        1.0       1.0        …  0.121643   0.113807    0.0668084
 1.0        1.0       1.0           0.948274   0.619606    0.502734
 1.0        1.0       1.0           0.503101   0.670143    0.151584
 1.0        1.0       1.0           0.0239172  0.277538    0.604295
 1.0        1.0       1.0           0.490541   0.393127    0.667793
 1.0        1.0       1.0        …  0.472548   0.812393    0.655114
 1.0        1.0       1.0           0.956671   0.398792    0.233052
 1.0        1.0       1.0           0.83457    0.0990415   0.444197
 1.0        1.0       1.0           0.112091   0.00794115  0.844425
 1.0        1.0       1.0           0.829232   0.0768055   0.427351
 1.0        1.0       1.0        …  0.750726   0.658181    0.0316363
 1.0        1.0       1.0           0.38384    0.672689    0.671094
 1.0        1.0       1.0           0.425385   0.320389    0.593037
 ⋮                               ⋱                         
 0.439972   0.219535  0.409426      0.197511   0.862967    0.141982
 0.771146   0.802731  0.700258      0.908076   0.185539    0.1993
 0.636862   0.137429  0.0485984  …  0.436429   0.39579     0.990276
 0.431764   0.903658  0.998625      0.21813    0.0300592   0.672166
 0.884402   0.216983  0.0844578     0.445998   0.89231     0.606835
 0.593039   0.854489  0.16887       0.480693   0.567922    0.14455
 0.961033   0.15405   0.916742      0.956848   0.563512    0.0277343
 0.761781   0.353725  0.502362   …  0.475769   0.84306     0.718264
 0.56985    0.141446  0.330206      0.735418   0.913564    0.123032
 0.594534   0.418833  0.820879      0.900206   0.301198    0.863166
 0.659226   0.714832  0.701179      0.54729    0.915038    0.866396
 0.0252055  0.7622    0.917001      0.157949   0.0172959   0.499759
Gray(0.9)
typeof(Gray(0.9))
Gray{Float64}
img = Gray.(img_data)
display(MIME("text/plain"), img)
400×600 Matrix{Gray{Float64}}:
 1.0        1.0       1.0        …  0.121643   0.113807    0.0668084
 1.0        1.0       1.0           0.948274   0.619606    0.502734
 1.0        1.0       1.0           0.503101   0.670143    0.151584
 1.0        1.0       1.0           0.0239172  0.277538    0.604295
 1.0        1.0       1.0           0.490541   0.393127    0.667793
 1.0        1.0       1.0        …  0.472548   0.812393    0.655114
 1.0        1.0       1.0           0.956671   0.398792    0.233052
 1.0        1.0       1.0           0.83457    0.0990415   0.444197
 1.0        1.0       1.0           0.112091   0.00794115  0.844425
 1.0        1.0       1.0           0.829232   0.0768055   0.427351
 1.0        1.0       1.0        …  0.750726   0.658181    0.0316363
 1.0        1.0       1.0           0.38384    0.672689    0.671094
 1.0        1.0       1.0           0.425385   0.320389    0.593037
 ⋮                               ⋱                         
 0.439972   0.219535  0.409426      0.197511   0.862967    0.141982
 0.771146   0.802731  0.700258      0.908076   0.185539    0.1993
 0.636862   0.137429  0.0485984  …  0.436429   0.39579     0.990276
 0.431764   0.903658  0.998625      0.21813    0.0300592   0.672166
 0.884402   0.216983  0.0844578     0.445998   0.89231     0.606835
 0.593039   0.854489  0.16887       0.480693   0.567922    0.14455
 0.961033   0.15405   0.916742      0.956848   0.563512    0.0277343
 0.761781   0.353725  0.502362   …  0.475769   0.84306     0.718264
 0.56985    0.141446  0.330206      0.735418   0.913564    0.123032
 0.594534   0.418833  0.820879      0.900206   0.301198    0.863166
 0.659226   0.714832  0.701179      0.54729    0.915038    0.866396
 0.0252055  0.7622    0.917001      0.157949   0.0172959   0.499759
dump(img)
Array{Gray{Float64}}((400, 600))
  1: Gray{Float64}
    val: Float64 1.0
  2: Gray{Float64}
    val: Float64 1.0
  3: Gray{Float64}
    val: Float64 1.0
  4: Gray{Float64}
    val: Float64 1.0
  5: Gray{Float64}
    val: Float64 1.0
  ...
  239996: Gray{Float64}
    val: Float64 0.7182644089563988
  239997: Gray{Float64}
    val: Float64 0.12303167272208015
  239998: Gray{Float64}
    val: Float64 0.8631657055536427
  239999: Gray{Float64}
    val: Float64 0.8663963088439961
  240000: Gray{Float64}
    val: Float64 0.4997585018792784

Obrázky můžeme i snadno ukládat v různých formátech.

using FileIO
ArgumentError: Package FileIO not found in current path.
- Run `import Pkg; Pkg.add("FileIO")` to install the FileIO package.

Stacktrace:
 [1] macro expansion
   @ ./loading.jl:2375 [inlined]
 [2] macro expansion
   @ ./lock.jl:376 [inlined]
 [3] __require(into::Module, mod::Symbol)
   @ Base ./loading.jl:2358
 [4] require(into::Module, mod::Symbol)
   @ Base ./loading.jl:2334
 [5] eval(m::Module, e::Any)
   @ Core ./boot.jl:489
save("test.jpg", img)
200464
RGB(0.1, 0.2, 0.52)
HSV(180,0.5,1)
map(x -> HSV(360 * x, 1, 1), img_data)

Nahrávání obrázků

using FileIO, ImageMagick, ImageIO
img_Gauss = load("../homeworks/B211/files/gauss_portret.jpg")
typeof(img_Gauss)
Matrix{RGB{N0f8}} (alias for Array{RGB{Normed{UInt8, 8}}, 2})
dump(img_Gauss)
Array{RGB{N0f8}}((480, 422))
  1: RGB{N0f8}
    r: N0f8
      i: UInt8 0xe3
    g: N0f8
      i: UInt8 0xeb
    b: N0f8
      i: UInt8 0xed
  2: RGB{N0f8}
    r: N0f8
      i: UInt8 0x99
    g: N0f8
      i: UInt8 0xa1
    b: N0f8
      i: UInt8 0xa3
  3: RGB{N0f8}
    r: N0f8
      i: UInt8 0x37
    g: N0f8
      i: UInt8 0x3f
    b: N0f8
      i: UInt8 0x41
  4: RGB{N0f8}
    r: N0f8
      i: UInt8 0x57
    g: N0f8
      i: UInt8 0x5f
    b: N0f8
      i: UInt8 0x61
  5: RGB{N0f8}
    r: N0f8
      i: UInt8 0xa1
    g: N0f8
      i: UInt8 0xa9
    b: N0f8
      i: UInt8 0xab
  ...
  202556: RGB{N0f8}
    r: N0f8
      i: UInt8 0x3f
    g: N0f8
      i: UInt8 0x4a
    b: N0f8
      i: UInt8 0x4c
  202557: RGB{N0f8}
    r: N0f8
      i: UInt8 0x3b
    g: N0f8
      i: UInt8 0x46
    b: N0f8
      i: UInt8 0x48
  202558: RGB{N0f8}
    r: N0f8
      i: UInt8 0x35
    g: N0f8
      i: UInt8 0x40
    b: N0f8
      i: UInt8 0x42
  202559: RGB{N0f8}
    r: N0f8
      i: UInt8 0x42
    g: N0f8
      i: UInt8 0x4d
    b: N0f8
      i: UInt8 0x4f
  202560: RGB{N0f8}
    r: N0f8
      i: UInt8 0x2e
    g: N0f8
      i: UInt8 0x39
    b: N0f8
      i: UInt8 0x3b

Všimněte si speciálního datového typu:

?N0f8
search: N0f8 n0f8 N8f8 N0f64 N4f28 N30f2 N6f58 N10f6 N24f8 N0f32 N60f4 N56f8

Normed{T <: Unsigned, f} <: FixedPoint{T, f}

Normed{T,f} maps Unsigned integers from 0 to 2^f-1 to the range [0.0, 1.0]. For example, Normed{UInt8,8} maps 0x00 to 0.0 and 0xff to 1.0.

There are the typealiases for Normed in the NXfY notation, where Y is the number of fractional bits (i.e. f), and X+Y equals the number of underlying bits used. For example, N0f8 is aliased to Normed{UInt8,8} and N4f12 is aliased to Normed{UInt16,12}.

Jeden pixel je barevně reprezentován trojicí čísel (RGB).

p = img_Gauss[1, 1]
typeof(p)
RGB{N0f8}
p.r
0.89N0f8
p.g
0.922N0f8
p.b
0.929N0f8
img_Gauss[10, 10]

Například můžeme extrahovat červený kanál a převést ho do datového typu, v kterém se lépe počítá:

imgR = map(x -> Float64(x.r), img_Gauss)
480×422 Matrix{Float64}:
 0.890196  0.501961   0.682353  0.854902  …  0.635294  0.647059  0.541176
 0.6       0.478431   0.639216  0.584314     0.462745  0.827451  0.788235
 0.215686  0.705882   0.615686  0.431373     0.454902  0.65098   0.513725
 0.341176  0.482353   0.435294  0.576471     0.356863  0.564706  0.4
 0.631373  0.545098   0.596078  0.403922     0.411765  0.603922  0.356863
 0.215686  0.462745   0.937255  0.65098   …  0.611765  0.584314  0.411765
 0.337255  0.109804   0.627451  0.843137     0.541176  0.615686  0.576471
 0.705882  0.427451   0.431373  0.67451      0.294118  0.701961  0.65098
 0.337255  0.439216   0.423529  0.407843     0.368627  0.639216  0.627451
 0.282353  0.278431   0.501961  0.313725     0.721569  0.478431  0.701961
 0.6       0.533333   0.513725  0.447059  …  0.768627  0.611765  0.639216
 0.682353  0.752941   0.619608  0.619608     0.721569  0.615686  0.470588
 0.427451  0.568627   0.713725  0.419608     0.556863  0.678431  0.509804
 ⋮                                        ⋱            ⋮         
 0.305882  0.0588235  0.270588  0.537255     0.239216  0.34902   0.423529
 0.356863  0.207843   0.184314  0.337255     0.380392  0.415686  0.423529
 0.27451   0.254902   0.294118  0.235294  …  0.407843  0.360784  0.403922
 0.368627  0.12549    0.356863  0.427451     0.45098   0.341176  0.4
 0.52549   0.396078   0.309804  0.258824     0.576471  0.266667  0.34902
 0.376471  0.337255   0.454902  0.372549     0.4       0.372549  0.368627
 0.129412  0.235294   0.580392  0.509804     0.443137  0.45098   0.352941
 0.329412  0.101961   0.239216  0.337255  …  0.341176  0.254902  0.247059
 0.505882  0.14902    0.192157  0.372549     0.556863  0.313725  0.231373
 0.376471  0.203922   0.219608  0.337255     0.462745  0.14902   0.207843
 0.219608  0.509804   0.623529  0.415686     0.239216  0.113725  0.258824
 0.211765  0.568627   0.607843  0.25098      0.235294  0.211765  0.180392
Gray.(imgR)
Gray.([imgR[1:180, :]; imgR[225:end, :]])

3. Cairo.jl a Luxor.jl

Cairo.jl představuje rozhranní k 2D grafické knihovně Cairo. S pomocí této knihovny jsme schopni deklarativním způsobem vytvářet zcela vlastní obrázky, ovšem operuje na poměrně nízké úrovni. Uživatelsky příjemnější je balíček Luxor.jl. Tento balíček nám umožňuje vytvářet rasterovou (PNG) i vektorovou (SVG) grafiku. Jeho výhodou je i poměrně podrobná dokumentace s tutoriály.

using Luxor
@svg begin
    text("Hello world")
end
@svg begin
    text("Toto je můj první obrázek!", Point(0, 0), angle = pi/4, halign = :center)
    rect(-50, -50, 100, 100, :stroke)
    circle(Point(0, 0), 50, :stroke)
end
@svg begin
    circle(Point(-50, 0), 50, action=:fill)
    circle(Point(50, 0), 60, action=:fill)
    rulers()
end

Pozor, souřadný systém je orientován v grafice obvyklým způsobem "hlavou dolů".

Vedle výše použitých primitiv rect a circ máme k dispozici spoustu dalších nástrojů.

@svg begin
    background("blue")
    sethue("red")
    move(Point(100, 0))
    arc(Point(0, 0), 100, 0, pi/4)
    arc(Point(0, 0), 200, pi/4, pi/2)
    strokepath()
end
@svg begin
    background("black")
    sethue("white")
    for j = 0:15
        line(100 * (-1)^j, -200 + j * 20)
    end
    strokepath()
end

move a line pracují v souřadnicích relativních k počátku v bodě (0,0)(0,0). K dispozici máme i rmove a rline, které pracují relativně k aktuální poloze.

@svg begin
    background("black")
    sethue("white")

    circle(Point(-100, 0), 5, action = :fill)
    circle(Point(100, 0), 5, action = :fill)
    
    move(Point(-100, 0))
    for j = 0:8
        rline(Point(50*cos(j*2pi/8), 50*sin(j*2pi/8)))
    end
    closepath()
    
    move(Point(100, 0))
    for j = 0:8
        rline(Point(50*cos(j*2pi/8), 50*sin(j*2pi/8)))
    end
    closepath()
    
    strokepath()
end

Makra se hodí k rychlému prozkoumávání a experimentování v Notebooku nebo editoru (VS Code umí obrázky správně zobrazovat).

Co když chceme výsledný obrázek exportovat?

function on_circle(j::Integer, n::Integer, R::Real)
    return Point(R*cos((j-1)*2pi/n), R*sin((j-1)*2pi/n))
end

function my_colorful_ngon(n::Integer, filename::AbstractString; R::Real = 200)
    n < 3 && error("At least 3 vertices are needed!")
    R <= 0 && error("R has to be positive!")

    Drawing(500, 500, filename)
    origin()

    for j = 1:n
        setcolor(rand(3)...)
        poly([ Point(0,0), on_circle(j-1, n, R), on_circle(j, n, R) ], :fill)
    end

    finish()
    preview()
end
my_colorful_ngon (generic function with 1 method)
my_colorful_ngon(3, "3.svg")
my_colorful_ngon(8, "8.svg")

Řešení některých příkladů

K Julia:

function julia(c::Complex{Float64}, R::Float64, reals, imags; imax::Int64=1000)
    img   = zeros(length(reals), length(imags))
    fc(z) = z^2 + c
    
    for j in axes(reals, 1), k in axes(imags, 1)
        n = 0
        z = reals[j] + 1im*imags[k]
        
        while n <= imax && abs(z) < R
            z = fc(z)
            n += 1
        end
        
        img[j, k] = n
    end
    
    return img
end