Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for previewing image in Livebook with Kino #25

Merged
merged 12 commits into from
Nov 8, 2022
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ endif
.DEFAULT_GLOBAL := build

build: $(STB_IMAGE_NIF_SO)
@ echo > /dev/null

$(STB_IMAGE_NIF_SO):
@ mkdir -p $(PRIV_DIR)
Expand Down
98 changes: 98 additions & 0 deletions lib/stb_image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,104 @@ defmodule StbImage do
"unsupported tensor shape: #{inspect(shape)} (expected height-width-channel)"
)

if Code.ensure_loaded?(Kino.Render) do
defimpl Kino.Render do
defp within_maximum_size(image) do
max_size = Application.fetch_env!(:stb_image, :kino_render_max_size)

case max_size do
{max_height, max_width} when is_integer(max_height) and is_integer(max_width) ->
case image.shape do
{h, w} ->
h <= max_height and w <= max_width
cocoa-xu marked this conversation as resolved.
Show resolved Hide resolved

{h, w, _c} ->
h <= max_height and w <= max_width

_ ->
false
end
cocoa-xu marked this conversation as resolved.
Show resolved Hide resolved

_ ->
raise """
invalid :kino_render_max_size configuration. Expected a 2-tuple, {height, width},
where height and width are both integers. Got: #{inspect(max_size)}
"""
end
end

@spec to_livebook(StbImage.t()) :: Kino.Output.t()
def to_livebook(image) when is_struct(image, StbImage) do
render_types = Application.fetch_env!(:stb_image, :kino_render_tab_order)

Enum.map(render_types, fn
:raw ->
{"Raw", Kino.Inspect.new(image)}

:numerical ->
if Code.ensure_loaded?(Nx) do
{"Numerical", Kino.Inspect.new(StbImage.to_nx(image))}
else
{"Numerical",
Kino.Markdown.new("""
The `Numerical` tab requires application `:nx`, please add `{:nx, "~> 0.4"}` to the dependency list.
""")}
end

:image ->
render_encoding = Application.fetch_env!(:stb_image, :kino_render_encoding)

{stb_format, kino_format} =
case render_encoding do
:jpg ->
{:jpg, :jpeg}

:jpeg ->
{:jpg, :jpeg}

:png ->
{:png, :png}

_ ->
raise "invalid :kino_render_encoding configuration. Expected one of :png, :jpg, or :jpeg. Got: #{inspect(render_encoding)}"
end

with true <- within_maximum_size(image),
encoded <- StbImage.to_binary(image, stb_format),
true <- is_binary(encoded) do
{"Image", Kino.Image.new(encoded, kino_format)}
else
_ ->
nil
end

type ->
raise """
invalid :kino_render_tab_order configuration. The set of supported types are [:image, :raw, :numerical].
Got: #{inspect(type)}
"""
end)
|> Enum.reject(&is_nil/1)
|> to_livebook_tabs(render_types, image)
end

defp to_livebook_tabs([], [:image], image) do
Kino.Layout.tabs([{"Raw", Kino.Inspect.new(image)}])
|> Kino.Render.to_livebook()
end

defp to_livebook_tabs(_tabs, [], image) do
Kino.Inspect.new(image)
|> Kino.Render.to_livebook()
end

defp to_livebook_tabs(tabs, _types, _mat) do
Kino.Layout.tabs(tabs)
|> Kino.Render.to_livebook()
end
end
end

@doc """
Reads image from file at `path`.

Expand Down
6 changes: 6 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ defmodule StbImage.MixProject do

def application do
[
env: [
kino_render_encoding: :png,
kino_render_max_size: {8192, 8192},
kino_render_tab_order: [:image, :raw]
],
extra_applications: [:logger]
]
end
Expand All @@ -30,6 +35,7 @@ defmodule StbImage.MixProject do
[
{:elixir_make, "~> 0.6"},
{:nx, "~> 0.1", optional: true},
{:kino, "~> 0.7", optional: true},
{:ex_doc, "~> 0.23", only: :docs, runtime: false}
]
end
Expand Down
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"},
"kino": {:hex, :kino, "0.7.0", "98d2623bebf8d1c4d761c5ed9741c6aae2f1909706baec501f2fdad347cb9d92", [:mix], [{:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "3dd1ee761c241caa30364333e3102a954d68188414a46e867f95c858d745f3fd"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"nx": {:hex, :nx, "0.2.0", "ae550cd72bca2e56f90fc63d59f2f11ac17ed3c4a9cd0968397f4ef9b795b3d6", [:mix], [{:complex, "~> 0.4.0", [hex: :complex, repo: "hexpm", optional: false]}], "hexpm", "31d4936d2cf78a0b147830f0e6fca45bd3ed4b1e592e2bcd61a85f81e892617c"},
"table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"},
}