Skip to content

Commit

Permalink
more imshow examples and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
pnezis committed Jan 2, 2024
1 parent 8cd024f commit dee8426
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 17 deletions.
101 changes: 92 additions & 9 deletions lib/tucan.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3114,18 +3114,37 @@ defmodule Tucan do
convention (the default) `:upper` is typically used for matrices and images.
Note that the vertical axis points upward for `:lower` but downward for `:upper`.
"""
```tucan
data = Nx.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]], type: {:f, 32})
Tucan.hconcat([
Tucan.imshow(data, width: 200, height: 200, origin: :upper, show_scale: true, tooltip: true)
|> Tucan.set_title(":upper origin (default)"),
Tucan.imshow(data, width: 200, height: 200, origin: :lower, show_scale: true, tooltip: true)
|> Tucan.set_title(":lower origin")
])
```
""",
default: :upper
],
show_scale: [
type: :boolean,
doc: "If set the color scale is displayed.",
default: false,
section: :style
]
]

@imshow_opts Tucan.Options.take!(
[
:width,
:height,
:tooltip
],
imshow_opts
)
[
:width,
:height,
:title,
:tooltip
],
imshow_opts
)
@imshow_schema Tucan.Options.to_nimble_schema!(@imshow_opts)

@doc """
Expand Down Expand Up @@ -3167,10 +3186,74 @@ defmodule Tucan do
Tucan.concat(images, columns: 10)
```
You can use any of the supported color schemes. Below we draw the same image with 10
different color schemes with and without `:reverse` set. (Notice how we use `VegaLite.resolve/3`
to ensure that each sub-plot will have an independent color scale.)
```tucan
mnist_path = Path.expand("../../assets/mnist_sample.bin", __DIR__)
images = File.read!(mnist_path) |> Nx.deserialize()
image = images[[images: 2]]
schemes = [:viridis, :inferno, :plasma, :turbo, :greenblue, :darkgold, :rainbow, :redblue, :greens, :greys]
non_reversed =
for scheme <- schemes do
Tucan.imshow(image, color_scheme: scheme, width: 60, height: 60, title: Atom.to_string(scheme))
end
reversed =
for scheme <- schemes do
Tucan.imshow(image, color_scheme: scheme, reverse: true, width: 60, height: 60)
end
Tucan.vconcat([
Tucan.hconcat(non_reversed)
|> VegaLite.resolve(:scale, color: :independent),
Tucan.hconcat(reversed)
|> VegaLite.resolve(:scale, color: :independent)
])
```
> #### 3d plots visualization {: .tip}
>
> You can also use `imshow/2` to "visualize" 3d plots. Below we create a meshgrid
> and calculate the value of a function across the grid. We then use `imshow/2` to
> visualize `z` using the color:
>
> ```tucan
> nx = 121
> ny = 121
>
> x = Nx.linspace(-20, 20, n: nx)
> y = Nx.linspace(-20, 20, n: ny)
>
> defmodule Foo do
> import Nx.Defn
>
> defn f(x, y) do
> Nx.sqrt(Nx.pow(x - 5, 2) + Nx.pow(y - 5, 2))
> end
>
> def meshgrid(x, y, nx, ny) do
> xx = Nx.broadcast(x, {ny, nx}, axes: [1])
> yy = Nx.broadcast(y, {ny, nx}, axes: [0])
>
> {xx, yy}
> end
> end
>
> {xx, yy} = Foo.meshgrid(x, y, nx, ny)
>
> zz = Foo.f(xx, yy)
>
> Tucan.imshow(zz, width: 300, height: 300, origin: :lower, show_scale: true)
> ```
"""
@doc section: :images
@spec imshow(data :: Nx.Tensor.t(), opts :: keyword()) :: VegaLite.t()
def imshow(data, opts) do
def imshow(data, opts \\ []) do
opts = NimbleOptions.validate!(opts, @imshow_schema)

Tucan.Image.show(data, opts)
Expand Down
22 changes: 15 additions & 7 deletions lib/tucan/image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,32 @@ defmodule Tucan.Image do
|> Nx.broadcast({height, width}, axes: [1])
|> Nx.to_flat_list()

{start_index, end_index} =
case opts[:origin] do
:upper -> {0, height - 1}
:lower -> {height - 1, 0}
end

y =
Nx.tensor(Enum.to_list(0..(height - 1)))
Nx.tensor(Enum.to_list(start_index..end_index))
|> Nx.broadcast({height, width}, axes: [0])
|> Nx.to_flat_list()

v = Nx.to_flat_list(tensor)

Vl.new(Keyword.take(opts, [:width, :height]))
spec_opts = Keyword.take(opts, [:width, :height, :title])
mark_opts = Keyword.take(opts, [:tooltip])

Vl.new(spec_opts)
|> Vl.data_from_values(x: x, y: y, v: v)
|> Vl.mark(:rect)
|> Vl.mark(:rect, mark_opts)
|> Vl.encode_field(:x, "x", type: :ordinal)
|> Vl.encode_field(:y, "y", type: :ordinal)
|> Vl.encode_field(:color, "v", type: :quantitative)
|> Tucan.Axes.set_enabled(false)
|> Tucan.Scale.set_color_scheme(opts[:color_scheme] || :greys,
reverse: Keyword.get(opts, :reverse, true)
)
|> Tucan.Legend.set_enabled(:color, false)
|> Tucan.Scale.set_color_scheme(opts[:color_scheme], reverse: opts[:reverse])
|> Tucan.Legend.set_title(:color, nil)
|> Tucan.Legend.set_enabled(:color, opts[:show_scale])
end

defp assert_nx! do
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule Tucan.MixProject do
source_url: @scm_url,
description: "A plotting library on top of VegaLite",
test_coverage: [
summary: [threshold: 97]
summary: [threshold: 95]
]
]
end
Expand Down

0 comments on commit dee8426

Please sign in to comment.