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

Unsplash #7 #16

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .bumblebee/README.md

This file was deleted.

16 changes: 2 additions & 14 deletions lib/app/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,6 @@ defmodule App.Application do
end

def serving do

# BLIP -----
# {:ok, model_info} = Bumblebee.load_model({:hf, "Salesforce/blip-image-captioning-base"})
# {:ok, featurizer} = Bumblebee.load_featurizer({:hf, "Salesforce/blip-image-captioning-base"})
# {:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "Salesforce/blip-image-captioning-base"})
# {:ok, generation_config} = Bumblebee.load_generation_config({:hf, "Salesforce/blip-image-captioning-base"})
#
# Bumblebee.Vision.image_to_text(model_info, featurizer, tokenizer, generation_config,
# compile: [batch_size: 10],
# defn_options: [compiler: EXLA]
# )

# ResNet-50 -----
{:ok, model_info} = Bumblebee.load_model({:hf, "microsoft/resnet-50"})
{:ok, featurizer} = Bumblebee.load_featurizer({:hf, "microsoft/resnet-50"})
Expand All @@ -49,9 +37,9 @@ defmodule App.Application do
top_k: 1,
compile: [batch_size: 10],
defn_options: [compiler: EXLA],
preallocate_params: true # needed to run on `Fly.io`
# needed to run on `Fly.io`
preallocate_params: true
)

end

# Tell Phoenix to update the endpoint configuration
Expand Down
99 changes: 75 additions & 24 deletions lib/app_web/live/page_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,25 @@
use AppWeb, :live_view
alias Vix.Vips.Image, as: Vimage

@unsplashes [
"https://source.unsplash.com/_CFv3bntQlQ",
"https://source.unsplash.com/r1SwcagHVG0"
]

@impl true
def mount(_params, _session, socket) do
Process.send_after(self(), :example_list, 3_000)

{:ok,
socket
|> assign(label: nil, running: false, task_ref: nil, image_preview_base64: nil)
|> assign(
label: nil,
running: false,
task_ref: nil,
image_preview_base64: nil,
display_list?: false,
displayed_list: []
)
|> allow_upload(:image_list,
accept: ~w(image/*),
auto_upload: true,
Expand All @@ -24,26 +38,31 @@

def handle_progress(:image_list, entry, socket) do
if entry.done? do

# Consume the entry and get the tensor to feed to classifier
%{tensor: tensor, file_binary: file_binary} = consume_uploaded_entry(socket, entry, fn %{} = meta ->
file_binary = File.read!(meta.path)
%{tensor: tensor, file_binary: file_binary} =
consume_uploaded_entry(socket, entry, fn %{} = meta ->
file_binary = File.read!(meta.path)

# Get image and resize
# This is dependant on the resolution of the model's dataset.
# In our case, we want the width to be closer to 640, whilst maintaining aspect ratio.
width = 640
{:ok, thumbnail_vimage} = Vix.Vips.Operation.thumbnail(meta.path, width, size: :VIPS_SIZE_DOWN)
# Get image and resize
# This is dependant on the resolution of the model's dataset.
# In our case, we want the width to be closer to 640, whilst maintaining aspect ratio.
width = 640

# Pre-process it
{:ok, tensor} = pre_process_image(thumbnail_vimage)
{:ok, thumbnail_vimage} =
Vix.Vips.Operation.thumbnail(meta.path, width, size: :VIPS_SIZE_DOWN)

# Return it
{:ok, %{tensor: tensor, file_binary: file_binary}}
end)
# Pre-process it
{:ok, tensor} = pre_process_image(thumbnail_vimage)

# Return it
{:ok, %{tensor: tensor, file_binary: file_binary}}
end)

# Create an async task to classify the image
task = Task.Supervisor.async(App.TaskSupervisor, fn -> Nx.Serving.batched_run(ImageClassifier, tensor) end)
task =
Task.Supervisor.async(App.TaskSupervisor, fn ->
Nx.Serving.batched_run(ImageClassifier, tensor)
end)

# Encode the image to base64
base64 = "data:image/png;base64, " <> Base.encode64(file_binary)
Expand All @@ -56,29 +75,61 @@
end

@impl true
def handle_info({ref, result}, %{assigns: %{task_ref: ref}} = socket) do
def handle_info({ref, result}, %{assigns: assigns} = socket) do
# This is called everytime an Async Task is created.
# We flush it here.
Process.demonitor(ref, [:flush])

# And then destructure the result from the classifier.
# %{results: [%{text: label}]} = result # BLIP
%{predictions: [%{label: label}]} = result # ResNet-50
# ResNet-50
%{predictions: [%{label: label}]} = result

cond do
Map.get(assigns, :task_ref) == ref ->
{:noreply, assign(socket, label: label, running: false)}

img = Map.get(assigns, :example_list_tasks) |> Enum.find(&(&1.ref == ref)) ->

Check warning on line 92 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L92

Added line #L92 was not covered by tests
{:noreply,
assign(socket,
displayed_list: [%{url: img.url, label: label} | assigns.displayed_list],

Check warning on line 95 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L95

Added line #L95 was not covered by tests
running: false,
display_list?: true
)}

true ->

Check warning on line 100 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L100

Added line #L100 was not covered by tests
{:noreply, socket}
end
end

def handle_info(:example_list, socket) do
tasks = @unsplashes |> Enum.map(&handle_image/1)

Check warning on line 106 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L105-L106

Added lines #L105 - L106 were not covered by tests

# Update the socket assigns with result and stopping spinner.
{:noreply, assign(socket, label: label, running: false)}
{:noreply, assign(socket, example_list_tasks: tasks)}
end

def handle_image(url) do
{:ok, img} =
Req.get!(url).body

Check warning on line 113 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L112-L113

Added lines #L112 - L113 were not covered by tests
|> Vix.Vips.Image.new_from_buffer()

{:ok, t_img} = pre_process_image(img)

Check warning on line 116 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L116

Added line #L116 was not covered by tests

Task.Supervisor.async(App.TaskSupervisor, fn ->
Nx.Serving.batched_run(ImageClassifier, t_img)

Check warning on line 119 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L119

Added line #L119 was not covered by tests
end)
|> Map.merge(%{url: url})

Check warning on line 121 in lib/app_web/live/page_live.ex

View check run for this annotation

Codecov / codecov/patch

lib/app_web/live/page_live.ex#L121

Added line #L121 was not covered by tests
end

def error_to_string(:too_large), do: "Image too large. Upload a smaller image up to 10MB."

defp pre_process_image(%Vimage{} = image) do

# If the image has an alpha channel, flatten it:
{:ok, flattened_image} = case Vix.Vips.Image.has_alpha?(image) do
true -> Vix.Vips.Operation.flatten(image)
false -> {:ok, image}
end
{:ok, flattened_image} =
case Vix.Vips.Image.has_alpha?(image) do
true -> Vix.Vips.Operation.flatten(image)
false -> {:ok, image}
end

# Convert the image to sRGB colourspace ----------------
{:ok, srgb_image} = Vix.Vips.Operation.colourspace(flattened_image, :VIPS_INTERPRETATION_sRGB)
Expand Down
Loading
Loading