├── test ├── test_helper.exs ├── live_onnx_web │ ├── views │ │ ├── page_view_test.exs │ │ ├── layout_view_test.exs │ │ └── error_view_test.exs │ └── controllers │ │ └── page_controller_test.exs └── support │ ├── channel_case.ex │ └── conn_case.ex ├── priv ├── static │ ├── favicon.ico │ ├── images │ │ └── phoenix.png │ └── robots.txt └── gettext │ ├── en │ └── LC_MESSAGES │ │ └── errors.po │ └── errors.pot ├── lib ├── live_onnx │ ├── mailer.ex │ └── application.ex ├── live_onnx_web │ ├── views │ │ ├── page_view.ex │ │ ├── layout_view.ex │ │ ├── error_view.ex │ │ └── error_helpers.ex │ ├── controllers │ │ └── page_controller.ex │ ├── templates │ │ ├── layout │ │ │ ├── app.html.heex │ │ │ ├── live.html.heex │ │ │ └── root.html.heex │ │ └── page │ │ │ └── index.html.heex │ ├── gettext.ex │ ├── telemetry.ex │ ├── endpoint.ex │ ├── router.ex │ └── live │ │ ├── page_live.html.heex │ │ └── page_live.ex ├── live_onnx.ex └── live_onnx_web.ex ├── .formatter.exs ├── config ├── test.exs ├── config.exs ├── prod.exs ├── dev.exs └── runtime.exs ├── model ├── resnet18 │ └── README.md ├── convnext │ └── README.md ├── squeezenet │ └── README.md ├── vgg16 │ └── README.md ├── alexnet │ └── README.md └── classlist.json ├── .gitignore ├── README.md ├── assets ├── js │ └── app.js ├── css │ ├── app.css │ └── phoenix.css └── vendor │ └── topbar.js ├── mix.exs └── mix.lock /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /priv/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thehaigo/live_onnx/HEAD/priv/static/favicon.ico -------------------------------------------------------------------------------- /lib/live_onnx/mailer.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnx.Mailer do 2 | use Swoosh.Mailer, otp_app: :live_onnx 3 | end 4 | -------------------------------------------------------------------------------- /lib/live_onnx_web/views/page_view.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.PageView do 2 | use LiveOnnxWeb, :view 3 | end 4 | -------------------------------------------------------------------------------- /priv/static/images/phoenix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thehaigo/live_onnx/HEAD/priv/static/images/phoenix.png -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | import_deps: [:phoenix], 3 | inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /test/live_onnx_web/views/page_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.PageViewTest do 2 | use LiveOnnxWeb.ConnCase, async: true 3 | end 4 | -------------------------------------------------------------------------------- /lib/live_onnx_web/controllers/page_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.PageController do 2 | use LiveOnnxWeb, :controller 3 | 4 | def index(conn, _params) do 5 | render(conn, "index.html") 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/static/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /lib/live_onnx_web/templates/layout/app.html.heex: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | <%= @inner_content %> 5 |
6 | -------------------------------------------------------------------------------- /test/live_onnx_web/controllers/page_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.PageControllerTest do 2 | use LiveOnnxWeb.ConnCase 3 | 4 | test "GET /", %{conn: conn} do 5 | conn = get(conn, "/") 6 | assert html_response(conn, 200) =~ "Welcome to Phoenix!" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/live_onnx.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnx do 2 | @moduledoc """ 3 | LiveOnnx keeps the contexts that define your domain 4 | and business logic. 5 | 6 | Contexts are also responsible for managing your data, regardless 7 | if it comes from the database, an external API or others. 8 | """ 9 | end 10 | -------------------------------------------------------------------------------- /lib/live_onnx_web/views/layout_view.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.LayoutView do 2 | use LiveOnnxWeb, :view 3 | 4 | # Phoenix LiveDashboard is available only in development by default, 5 | # so we instruct Elixir to not warn if the dashboard route is missing. 6 | @compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}} 7 | end 8 | -------------------------------------------------------------------------------- /test/live_onnx_web/views/layout_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.LayoutViewTest do 2 | use LiveOnnxWeb.ConnCase, async: true 3 | 4 | # When testing helpers, you may want to import Phoenix.HTML and 5 | # use functions such as safe_to_string() to convert the helper 6 | # result into an HTML string. 7 | # import Phoenix.HTML 8 | end 9 | -------------------------------------------------------------------------------- /priv/gettext/en/LC_MESSAGES/errors.po: -------------------------------------------------------------------------------- 1 | ## `msgid`s in this file come from POT (.pot) files. 2 | ## 3 | ## Do not add, change, or remove `msgid`s manually here as 4 | ## they're tied to the ones in the corresponding POT file 5 | ## (with the same domain). 6 | ## 7 | ## Use `mix gettext.extract --merge` or `mix gettext.merge` 8 | ## to merge POT files into PO files. 9 | msgid "" 10 | msgstr "" 11 | "Language: en\n" 12 | -------------------------------------------------------------------------------- /lib/live_onnx_web/templates/layout/live.html.heex: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 | 9 | 10 | <%= @inner_content %> 11 |
12 | -------------------------------------------------------------------------------- /priv/gettext/errors.pot: -------------------------------------------------------------------------------- 1 | ## This is a PO Template file. 2 | ## 3 | ## `msgid`s here are often extracted from source code. 4 | ## Add new translations manually only if they're dynamic 5 | ## translations that can't be statically extracted. 6 | ## 7 | ## Run `mix gettext.extract` to bring this file up to 8 | ## date. Leave `msgstr`s empty as changing them here has no 9 | ## effect: edit them in PO (`.po`) files instead. 10 | 11 | -------------------------------------------------------------------------------- /test/live_onnx_web/views/error_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.ErrorViewTest do 2 | use LiveOnnxWeb.ConnCase, async: true 3 | 4 | # Bring render/3 and render_to_string/3 for testing custom views 5 | import Phoenix.View 6 | 7 | test "renders 404.html" do 8 | assert render_to_string(LiveOnnxWeb.ErrorView, "404.html", []) == "Not Found" 9 | end 10 | 11 | test "renders 500.html" do 12 | assert render_to_string(LiveOnnxWeb.ErrorView, "500.html", []) == "Internal Server Error" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/live_onnx_web/views/error_view.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.ErrorView do 2 | use LiveOnnxWeb, :view 3 | 4 | # If you want to customize a particular status code 5 | # for a certain format, you may uncomment below. 6 | # def render("500.html", _assigns) do 7 | # "Internal Server Error" 8 | # end 9 | 10 | # By default, Phoenix returns the status message from 11 | # the template name. For example, "404.html" becomes 12 | # "Not Found". 13 | def template_not_found(template, _assigns) do 14 | Phoenix.Controller.status_message_from_template(template) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /config/test.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # We don't run a server during test. If one is required, 4 | # you can enable the server option below. 5 | config :live_onnx, LiveOnnxWeb.Endpoint, 6 | http: [ip: {127, 0, 0, 1}, port: 4002], 7 | secret_key_base: "HZ230zFY0xebxs1J/GwhFcfU4+ceHJSkqbHKtVz0hBpSkrpH71M+ewlhRBTbJUEq", 8 | server: false 9 | 10 | # In test we don't send emails. 11 | config :live_onnx, LiveOnnx.Mailer, 12 | adapter: Swoosh.Adapters.Test 13 | 14 | # Print only warnings and errors during test 15 | config :logger, level: :warn 16 | 17 | # Initialize plugs at runtime for faster test compilation 18 | config :phoenix, :plug_init_mode, :runtime 19 | -------------------------------------------------------------------------------- /model/resnet18/README.md: -------------------------------------------------------------------------------- 1 | ## resnet18.onnx export 2 | export from torchvision resnet18 3 | 4 | ``` 5 | pip install torch torchvision 6 | ``` 7 | 8 | ```python 9 | import torchvision 10 | import torch 11 | 12 | net = torchvision.models.resnet18(pretrained = True) 13 | 14 | model_onnx_path = "model/resnet18/model.onnx" 15 | input_names = [ "input" ] 16 | output_names = [ "output" ] 17 | 18 | input_shape = (3, 224, 224) 19 | batch_size = 1 20 | dummy_input = torch.randn(batch_size, *input_shape) 21 | 22 | 23 | output = torch.onnx.export(net, dummy_input, model_onnx_path, \ 24 | verbose=False, input_names=input_names, output_names=output_names) 25 | 26 | ``` -------------------------------------------------------------------------------- /model/convnext/README.md: -------------------------------------------------------------------------------- 1 | ## convnext.onnx export 2 | export from torchvision convnext_base 3 | 4 | ``` 5 | pip install torch torchvision 6 | ``` 7 | 8 | ```python 9 | import torchvision 10 | import torch 11 | 12 | net = torchvision.models.convnext_base(pretrained = True) 13 | 14 | model_onnx_path = "model/convnext/model.onnx" 15 | input_names = [ "input" ] 16 | output_names = [ "output" ] 17 | 18 | input_shape = (3, 224, 224) 19 | batch_size = 1 20 | dummy_input = torch.randn(batch_size, *input_shape) 21 | 22 | 23 | output = torch.onnx.export(net, dummy_input, model_onnx_path, \ 24 | verbose=False, input_names=input_names, output_names=output_names) 25 | 26 | ``` -------------------------------------------------------------------------------- /model/squeezenet/README.md: -------------------------------------------------------------------------------- 1 | ## squeeze.onnx export 2 | export from torchvision squeezenet 3 | 4 | ``` 5 | pip install torch torchvision 6 | ``` 7 | 8 | ```python 9 | import torchvision 10 | import torch 11 | 12 | net = torchvision.models.squeezenet1_0(pretrained = True) 13 | 14 | model_onnx_path = "model/squeezenet/model.onnx" 15 | input_names = [ "input" ] 16 | output_names = [ "output" ] 17 | 18 | input_shape = (3, 224, 224) 19 | batch_size = 1 20 | dummy_input = torch.randn(batch_size, *input_shape) 21 | 22 | 23 | output = torch.onnx.export(net, dummy_input, model_onnx_path, \ 24 | verbose=False, input_names=input_names, output_names=output_names) 25 | 26 | ``` -------------------------------------------------------------------------------- /lib/live_onnx_web/gettext.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.Gettext do 2 | @moduledoc """ 3 | A module providing Internationalization with a gettext-based API. 4 | 5 | By using [Gettext](https://hexdocs.pm/gettext), 6 | your module gains a set of macros for translations, for example: 7 | 8 | import LiveOnnxWeb.Gettext 9 | 10 | # Simple translation 11 | gettext("Here is the string to translate") 12 | 13 | # Plural translation 14 | ngettext("Here is the string to translate", 15 | "Here are the strings to translate", 16 | 3) 17 | 18 | # Domain-based translation 19 | dgettext("errors", "Here is the error message to translate") 20 | 21 | See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. 22 | """ 23 | use Gettext, otp_app: :live_onnx 24 | end 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | live_onnx-*.tar 24 | 25 | # Ignore assets that are produced by build tools. 26 | /priv/static/assets/ 27 | 28 | # Ignore digested assets cache. 29 | /priv/static/cache_manifest.json 30 | 31 | # In case you use Node.js/npm, you want to ignore these. 32 | npm-debug.log 33 | /assets/node_modules/ 34 | 35 | .DS_Store 36 | .cache 37 | model/*/model.* 38 | model/*/model_fail.onnx -------------------------------------------------------------------------------- /model/vgg16/README.md: -------------------------------------------------------------------------------- 1 | ## vgg16.onnx export 2 | export from torchvision vgg16 3 | 4 | ``` 5 | pip install torch torchvision 6 | ``` 7 | 8 | ```python 9 | import torchvision 10 | import torch 11 | 12 | net = torchvision.models.vgg16(pretrained = True) 13 | 14 | model_onnx_path = "model/vgg16/model.onnx" 15 | input_names = [ "input" ] 16 | output_names = [ "output" ] 17 | 18 | input_shape = (3, 224, 224) 19 | batch_size = 1 20 | dummy_input = torch.randn(batch_size, *input_shape) 21 | 22 | 23 | output = torch.onnx.export(net, dummy_input, model_onnx_path, \ 24 | verbose=False, input_names=input_names, output_names=output_names) 25 | 26 | ``` 27 | 28 | ## model.dets create 29 | AxonOnnx import too slow 30 | becasue load from dets 31 | 32 | ```elixir 33 | {model, params} = AxonOnnx.import("model/vgg16/model.onnx") 34 | 35 | # save to dets 36 | :dets.open_file("vgg16", type: :bag, file: 'model/vgg16/model.dets') 37 | :dets.insert("vgg16",{1,{model,params}}) 38 | :dets.sync("vgg16") 39 | :dets.stop 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /model/alexnet/README.md: -------------------------------------------------------------------------------- 1 | ## alexnet.onnx export 2 | export from torchvision alexnet 3 | 4 | ``` 5 | pip install torch torchvision 6 | ``` 7 | 8 | ```python 9 | import torchvision 10 | import torch 11 | 12 | net = torchvision.models.alexnet(pretrained = True) 13 | 14 | model_onnx_path = "model/alexnet/model.onnx" 15 | input_names = [ "input" ] 16 | output_names = [ "output" ] 17 | 18 | input_shape = (3, 224, 224) 19 | batch_size = 1 20 | dummy_input = torch.randn(batch_size, *input_shape) 21 | 22 | 23 | output = torch.onnx.export(net, dummy_input, model_onnx_path, \ 24 | verbose=False, input_names=input_names, output_names=output_names) 25 | 26 | ``` 27 | 28 | ## model.dets create 29 | AxonOnnx import too slow 30 | becasue load from dets 31 | 32 | ```elixir 33 | {model, params} = AxonOnnx.import("model/alexnet/model.onnx") 34 | 35 | # save to dets 36 | :dets.open_file("alexnet", type: :bag, file: 'model/alexnet/model.dets') 37 | :dets.insert("alexnet",{1,{model,params}}) 38 | :dets.sync("alexnet") 39 | :dets.stop 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LiveOnnx 2 | 3 | Requirements: 4 | 5 | * Elixir installed 6 | 7 | To start your Phoenix server: 8 | 9 | * Install dependencies with `mix deps.get` 10 | * get supported onnx model 11 | * Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server` 12 | 13 | Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. 14 | 15 | Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html). 16 | 17 | ## demo 18 | 19 | https://user-images.githubusercontent.com/91950/172017672-ad98402d-93a7-4d7a-9e94-b69eebc1cea2.mp4 20 | 21 | ## supported model 22 | 23 | * ResNet18 24 | * AlexNet 25 | * SqueezeNet 26 | * Vgg16 27 | * ConvNext 28 | 29 | ## unsupported model 30 | 31 | * densenet -> unsupported "Pad" 2022/06/05 32 | * inception -> unsupported "Pad" 2022/06/05 33 | * shufflenet -> unable to build model from ONNX graph, expected value onnx::Conv_378 to be a graph input, but it was not present in built graphs 2022/06/05 34 | 35 | ## export onnx proccess 36 | see model/model_name directory 37 | -------------------------------------------------------------------------------- /test/support/channel_case.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.ChannelCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | channel tests. 5 | 6 | Such tests rely on `Phoenix.ChannelTest` and also 7 | import other functionality to make it easier 8 | to build common data structures and query the data layer. 9 | 10 | Finally, if the test case interacts with the database, 11 | we enable the SQL sandbox, so changes done to the database 12 | are reverted at the end of every test. If you are using 13 | PostgreSQL, you can even run database tests asynchronously 14 | by setting `use LiveOnnxWeb.ChannelCase, async: true`, although 15 | this option is not recommended for other databases. 16 | """ 17 | 18 | use ExUnit.CaseTemplate 19 | 20 | using do 21 | quote do 22 | # Import conveniences for testing with channels 23 | import Phoenix.ChannelTest 24 | import LiveOnnxWeb.ChannelCase 25 | 26 | # The default endpoint for testing 27 | @endpoint LiveOnnxWeb.Endpoint 28 | end 29 | end 30 | 31 | setup _tags do 32 | :ok 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/live_onnx/application.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnx.Application do 2 | # See https://hexdocs.pm/elixir/Application.html 3 | # for more information on OTP Applications 4 | @moduledoc false 5 | 6 | use Application 7 | 8 | @impl true 9 | def start(_type, _args) do 10 | children = [ 11 | # Start the Telemetry supervisor 12 | LiveOnnxWeb.Telemetry, 13 | # Start the PubSub system 14 | {Phoenix.PubSub, name: LiveOnnx.PubSub}, 15 | # Start the Endpoint (http/https) 16 | LiveOnnxWeb.Endpoint 17 | # Start a worker by calling: LiveOnnx.Worker.start_link(arg) 18 | # {LiveOnnx.Worker, arg} 19 | ] 20 | 21 | # See https://hexdocs.pm/elixir/Supervisor.html 22 | # for other strategies and supported options 23 | opts = [strategy: :one_for_one, name: LiveOnnx.Supervisor] 24 | Supervisor.start_link(children, opts) 25 | end 26 | 27 | # Tell Phoenix to update the endpoint configuration 28 | # whenever the application is updated. 29 | @impl true 30 | def config_change(changed, _new, removed) do 31 | LiveOnnxWeb.Endpoint.config_change(changed, removed) 32 | :ok 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /test/support/conn_case.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.ConnCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | tests that require setting up a connection. 5 | 6 | Such tests rely on `Phoenix.ConnTest` and also 7 | import other functionality to make it easier 8 | to build common data structures and query the data layer. 9 | 10 | Finally, if the test case interacts with the database, 11 | we enable the SQL sandbox, so changes done to the database 12 | are reverted at the end of every test. If you are using 13 | PostgreSQL, you can even run database tests asynchronously 14 | by setting `use LiveOnnxWeb.ConnCase, async: true`, although 15 | this option is not recommended for other databases. 16 | """ 17 | 18 | use ExUnit.CaseTemplate 19 | 20 | using do 21 | quote do 22 | # Import conveniences for testing with connections 23 | import Plug.Conn 24 | import Phoenix.ConnTest 25 | import LiveOnnxWeb.ConnCase 26 | 27 | alias LiveOnnxWeb.Router.Helpers, as: Routes 28 | 29 | # The default endpoint for testing 30 | @endpoint LiveOnnxWeb.Endpoint 31 | end 32 | end 33 | 34 | setup _tags do 35 | {:ok, conn: Phoenix.ConnTest.build_conn()} 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/live_onnx_web/templates/layout/root.html.heex: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= csrf_meta_tag() %> 8 | <%= live_title_tag assigns[:page_title] || "LiveOnnx", suffix: " · Phoenix Framework" %> 9 | 10 | 11 | 12 | 13 |
14 |
15 | 23 | 26 |
27 |
28 | <%= @inner_content %> 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/live_onnx_web/templates/page/index.html.heex: -------------------------------------------------------------------------------- 1 |
2 |

<%= gettext "Welcome to %{name}!", name: "Phoenix" %>

3 |

Peace of mind from prototype to production

4 |
5 | 6 |
7 |
8 |

Resources

9 | 20 |
21 |
22 |

Help

23 | 40 |
41 |
42 | -------------------------------------------------------------------------------- /lib/live_onnx_web/telemetry.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.Telemetry do 2 | use Supervisor 3 | import Telemetry.Metrics 4 | 5 | def start_link(arg) do 6 | Supervisor.start_link(__MODULE__, arg, name: __MODULE__) 7 | end 8 | 9 | @impl true 10 | def init(_arg) do 11 | children = [ 12 | # Telemetry poller will execute the given period measurements 13 | # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics 14 | {:telemetry_poller, measurements: periodic_measurements(), period: 10_000} 15 | # Add reporters as children of your supervision tree. 16 | # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()} 17 | ] 18 | 19 | Supervisor.init(children, strategy: :one_for_one) 20 | end 21 | 22 | def metrics do 23 | [ 24 | # Phoenix Metrics 25 | summary("phoenix.endpoint.stop.duration", 26 | unit: {:native, :millisecond} 27 | ), 28 | summary("phoenix.router_dispatch.stop.duration", 29 | tags: [:route], 30 | unit: {:native, :millisecond} 31 | ), 32 | 33 | # VM Metrics 34 | summary("vm.memory.total", unit: {:byte, :kilobyte}), 35 | summary("vm.total_run_queue_lengths.total"), 36 | summary("vm.total_run_queue_lengths.cpu"), 37 | summary("vm.total_run_queue_lengths.io") 38 | ] 39 | end 40 | 41 | defp periodic_measurements do 42 | [ 43 | # A module, function and arguments to be invoked periodically. 44 | # This function must call :telemetry.execute/3 and a metric must be added above. 45 | # {LiveOnnxWeb, :count_users, []} 46 | ] 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/live_onnx_web/endpoint.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.Endpoint do 2 | use Phoenix.Endpoint, otp_app: :live_onnx 3 | 4 | # The session will be stored in the cookie and signed, 5 | # this means its contents can be read but not tampered with. 6 | # Set :encryption_salt if you would also like to encrypt it. 7 | @session_options [ 8 | store: :cookie, 9 | key: "_live_onnx_key", 10 | signing_salt: "pMR1BscD" 11 | ] 12 | 13 | socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] 14 | 15 | # Serve at "/" the static files from "priv/static" directory. 16 | # 17 | # You should set gzip to true if you are running phx.digest 18 | # when deploying your static files in production. 19 | plug Plug.Static, 20 | at: "/", 21 | from: :live_onnx, 22 | gzip: false, 23 | only: ~w(assets fonts images favicon.ico robots.txt) 24 | 25 | # Code reloading can be explicitly enabled under the 26 | # :code_reloader configuration of your endpoint. 27 | if code_reloading? do 28 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket 29 | plug Phoenix.LiveReloader 30 | plug Phoenix.CodeReloader 31 | end 32 | 33 | plug Phoenix.LiveDashboard.RequestLogger, 34 | param_key: "request_logger", 35 | cookie_key: "request_logger" 36 | 37 | plug Plug.RequestId 38 | plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] 39 | 40 | plug Plug.Parsers, 41 | parsers: [:urlencoded, :multipart, :json], 42 | pass: ["*/*"], 43 | json_decoder: Phoenix.json_library() 44 | 45 | plug Plug.MethodOverride 46 | plug Plug.Head 47 | plug Plug.Session, @session_options 48 | plug LiveOnnxWeb.Router 49 | end 50 | -------------------------------------------------------------------------------- /lib/live_onnx_web/router.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.Router do 2 | use LiveOnnxWeb, :router 3 | 4 | pipeline :browser do 5 | plug :accepts, ["html"] 6 | plug :fetch_session 7 | plug :fetch_live_flash 8 | plug :put_root_layout, {LiveOnnxWeb.LayoutView, :root} 9 | plug :protect_from_forgery 10 | plug :put_secure_browser_headers 11 | end 12 | 13 | pipeline :api do 14 | plug :accepts, ["json"] 15 | end 16 | 17 | scope "/", LiveOnnxWeb do 18 | pipe_through :browser 19 | 20 | live "/", PageLive 21 | live "/:id", PageLive 22 | end 23 | 24 | # Other scopes may use custom stacks. 25 | # scope "/api", LiveOnnxWeb do 26 | # pipe_through :api 27 | # end 28 | 29 | # Enables LiveDashboard only for development 30 | # 31 | # If you want to use the LiveDashboard in production, you should put 32 | # it behind authentication and allow only admins to access it. 33 | # If your application does not have an admins-only section yet, 34 | # you can use Plug.BasicAuth to set up some basic authentication 35 | # as long as you are also using SSL (which you should anyway). 36 | if Mix.env() in [:dev, :test] do 37 | import Phoenix.LiveDashboard.Router 38 | 39 | scope "/" do 40 | pipe_through :browser 41 | 42 | live_dashboard "/dashboard", metrics: LiveOnnxWeb.Telemetry 43 | end 44 | end 45 | 46 | # Enables the Swoosh mailbox preview in development. 47 | # 48 | # Note that preview only shows emails that were sent by the same 49 | # node running the Phoenix server. 50 | if Mix.env() == :dev do 51 | scope "/dev" do 52 | pipe_through :browser 53 | 54 | forward "/mailbox", Plug.Swoosh.MailboxPreview 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/live_onnx_web/views/error_helpers.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.ErrorHelpers do 2 | @moduledoc """ 3 | Conveniences for translating and building error messages. 4 | """ 5 | 6 | use Phoenix.HTML 7 | 8 | @doc """ 9 | Generates tag for inlined form input errors. 10 | """ 11 | def error_tag(form, field) do 12 | Enum.map(Keyword.get_values(form.errors, field), fn error -> 13 | content_tag(:span, translate_error(error), 14 | class: "invalid-feedback", 15 | phx_feedback_for: input_name(form, field) 16 | ) 17 | end) 18 | end 19 | 20 | @doc """ 21 | Translates an error message using gettext. 22 | """ 23 | def translate_error({msg, opts}) do 24 | # When using gettext, we typically pass the strings we want 25 | # to translate as a static argument: 26 | # 27 | # # Translate "is invalid" in the "errors" domain 28 | # dgettext("errors", "is invalid") 29 | # 30 | # # Translate the number of files with plural rules 31 | # dngettext("errors", "1 file", "%{count} files", count) 32 | # 33 | # Because the error messages we show in our forms and APIs 34 | # are defined inside Ecto, we need to translate them dynamically. 35 | # This requires us to call the Gettext module passing our gettext 36 | # backend as first argument. 37 | # 38 | # Note we use the "errors" domain, which means translations 39 | # should be written to the errors.po file. The :count option is 40 | # set by Ecto and indicates we should also apply plural rules. 41 | if count = opts[:count] do 42 | Gettext.dngettext(LiveOnnxWeb.Gettext, "errors", msg, msg, count, opts) 43 | else 44 | Gettext.dgettext(LiveOnnxWeb.Gettext, "errors", msg, opts) 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/live_onnx_web/live/page_live.html.heex: -------------------------------------------------------------------------------- 1 |
2 | 20 | 21 |
22 |
23 |
24 |
25 |
26 | 35 |
36 |
37 |
38 | <%= if @upload_file do %> 39 |
40 | <% end %> 41 |
42 |
43 |
44 | 45 | -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | // We import the CSS which is extracted to its own file by esbuild. 2 | // Remove this line if you add a your own CSS build pipeline (e.g postcss). 3 | import "../css/app.css" 4 | 5 | // If you want to use Phoenix channels, run `mix help phx.gen.channel` 6 | // to get started and then uncomment the line below. 7 | // import "./user_socket.js" 8 | 9 | // You can include dependencies in two ways. 10 | // 11 | // The simplest option is to put them in assets/vendor and 12 | // import them using relative paths: 13 | // 14 | // import "../vendor/some-package.js" 15 | // 16 | // Alternatively, you can `npm install some-package --prefix assets` and import 17 | // them using a path starting with the package name: 18 | // 19 | // import "some-package" 20 | // 21 | 22 | // Include phoenix_html to handle method=PUT/DELETE in forms and buttons. 23 | import "phoenix_html" 24 | // Establish Phoenix Socket and LiveView configuration. 25 | import {Socket} from "phoenix" 26 | import {LiveSocket} from "phoenix_live_view" 27 | import topbar from "../vendor/topbar" 28 | 29 | let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") 30 | let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}}) 31 | 32 | // Show progress bar on live navigation and form submits 33 | topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) 34 | window.addEventListener("phx:page-loading-start", info => topbar.show()) 35 | window.addEventListener("phx:page-loading-stop", info => topbar.hide()) 36 | 37 | // connect if there are any LiveViews on the page 38 | liveSocket.connect() 39 | 40 | // expose liveSocket on window for web console debug logs and latency simulation: 41 | // >> liveSocket.enableDebug() 42 | // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session 43 | // >> liveSocket.disableLatencySim() 44 | window.liveSocket = liveSocket 45 | 46 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Config module. 3 | # 4 | # This configuration file is loaded before any dependency and 5 | # is restricted to this project. 6 | 7 | # General application configuration 8 | import Config 9 | 10 | config :nx, :default_backend, EXLA.Backend 11 | config :nx, :default_defn_options, compiler: EXLA 12 | 13 | # Configures the endpoint 14 | config :live_onnx, LiveOnnxWeb.Endpoint, 15 | url: [host: "localhost"], 16 | render_errors: [view: LiveOnnxWeb.ErrorView, accepts: ~w(html json), layout: false], 17 | pubsub_server: LiveOnnx.PubSub, 18 | live_view: [signing_salt: "+34hIyL0"] 19 | 20 | # Configures the mailer 21 | # 22 | # By default it uses the "Local" adapter which stores the emails 23 | # locally. You can see the emails in your browser, at "/dev/mailbox". 24 | # 25 | # For production it's recommended to configure a different adapter 26 | # at the `config/runtime.exs`. 27 | config :live_onnx, LiveOnnx.Mailer, adapter: Swoosh.Adapters.Local 28 | 29 | # Swoosh API client is needed for adapters other than SMTP. 30 | config :swoosh, :api_client, false 31 | 32 | # Configure esbuild (the version is required) 33 | config :esbuild, 34 | version: "0.14.0", 35 | default: [ 36 | args: 37 | ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), 38 | cd: Path.expand("../assets", __DIR__), 39 | env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} 40 | ] 41 | 42 | # Configures Elixir's Logger 43 | config :logger, :console, 44 | format: "$time $metadata[$level] $message\n", 45 | metadata: [:request_id] 46 | 47 | # Use Jason for JSON parsing in Phoenix 48 | config :phoenix, :json_library, Jason 49 | 50 | # Import environment specific config. This must remain at the bottom 51 | # of this file so it overrides the configuration defined above. 52 | import_config "#{config_env()}.exs" 53 | -------------------------------------------------------------------------------- /config/prod.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # For production, don't forget to configure the url host 4 | # to something meaningful, Phoenix uses this information 5 | # when generating URLs. 6 | # 7 | # Note we also include the path to a cache manifest 8 | # containing the digested version of static files. This 9 | # manifest is generated by the `mix phx.digest` task, 10 | # which you should run after static files are built and 11 | # before starting your production server. 12 | config :live_onnx, LiveOnnxWeb.Endpoint, cache_static_manifest: "priv/static/cache_manifest.json" 13 | 14 | # Do not print debug messages in production 15 | config :logger, level: :info 16 | 17 | # ## SSL Support 18 | # 19 | # To get SSL working, you will need to add the `https` key 20 | # to the previous section and set your `:url` port to 443: 21 | # 22 | # config :live_onnx, LiveOnnxWeb.Endpoint, 23 | # ..., 24 | # url: [host: "example.com", port: 443], 25 | # https: [ 26 | # ..., 27 | # port: 443, 28 | # cipher_suite: :strong, 29 | # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), 30 | # certfile: System.get_env("SOME_APP_SSL_CERT_PATH") 31 | # ] 32 | # 33 | # The `cipher_suite` is set to `:strong` to support only the 34 | # latest and more secure SSL ciphers. This means old browsers 35 | # and clients may not be supported. You can set it to 36 | # `:compatible` for wider support. 37 | # 38 | # `:keyfile` and `:certfile` expect an absolute path to the key 39 | # and cert in disk or a relative path inside priv, for example 40 | # "priv/ssl/server.key". For all supported SSL configuration 41 | # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1 42 | # 43 | # We also recommend setting `force_ssl` in your endpoint, ensuring 44 | # no data is ever sent via http, always redirecting to https: 45 | # 46 | # config :live_onnx, LiveOnnxWeb.Endpoint, 47 | # force_ssl: [hsts: true] 48 | # 49 | # Check `Plug.SSL` for all available options in `force_ssl`. 50 | -------------------------------------------------------------------------------- /config/dev.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # For development, we disable any cache and enable 4 | # debugging and code reloading. 5 | # 6 | # The watchers configuration can be used to run external 7 | # watchers to your application. For example, we use it 8 | # with esbuild to bundle .js and .css sources. 9 | config :live_onnx, LiveOnnxWeb.Endpoint, 10 | # Binding to loopback ipv4 address prevents access from other machines. 11 | # Change to `ip: {0, 0, 0, 0}` to allow access from other machines. 12 | http: [ip: {127, 0, 0, 1}, port: 4000], 13 | check_origin: false, 14 | code_reloader: true, 15 | debug_errors: true, 16 | secret_key_base: "DnJBaHZRY5EcwnD7AvW4u0uqWtkZpT3+Btmd1bqwrwYwaB4q8/zoWGsiJFxPTueE", 17 | watchers: [ 18 | # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args) 19 | esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]} 20 | ] 21 | 22 | # ## SSL Support 23 | # 24 | # In order to use HTTPS in development, a self-signed 25 | # certificate can be generated by running the following 26 | # Mix task: 27 | # 28 | # mix phx.gen.cert 29 | # 30 | # Note that this task requires Erlang/OTP 20 or later. 31 | # Run `mix help phx.gen.cert` for more information. 32 | # 33 | # The `http:` config above can be replaced with: 34 | # 35 | # https: [ 36 | # port: 4001, 37 | # cipher_suite: :strong, 38 | # keyfile: "priv/cert/selfsigned_key.pem", 39 | # certfile: "priv/cert/selfsigned.pem" 40 | # ], 41 | # 42 | # If desired, both `http:` and `https:` keys can be 43 | # configured to run both http and https servers on 44 | # different ports. 45 | 46 | # Watch static and templates for browser reloading. 47 | config :live_onnx, LiveOnnxWeb.Endpoint, 48 | live_reload: [ 49 | patterns: [ 50 | ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", 51 | ~r"priv/gettext/.*(po)$", 52 | ~r"lib/live_onnx_web/(live|views)/.*(ex)$", 53 | ~r"lib/live_onnx_web/templates/.*(eex)$" 54 | ] 55 | ] 56 | 57 | # Do not include metadata nor timestamps in development logs 58 | config :logger, :console, format: "[$level] $message\n" 59 | 60 | # Set a higher stacktrace during development. Avoid configuring such 61 | # in production as building large stacktraces may be expensive. 62 | config :phoenix, :stacktrace_depth, 20 63 | 64 | # Initialize plugs at runtime for faster development compilation 65 | config :phoenix, :plug_init_mode, :runtime 66 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnx.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :live_onnx, 7 | version: "0.1.0", 8 | elixir: "~> 1.12", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | aliases: aliases(), 13 | deps: deps() 14 | ] 15 | end 16 | 17 | # Configuration for the OTP application. 18 | # 19 | # Type `mix help compile.app` for more information. 20 | def application do 21 | [ 22 | mod: {LiveOnnx.Application, []}, 23 | extra_applications: [:logger, :runtime_tools] 24 | ] 25 | end 26 | 27 | # Specifies which paths to compile per environment. 28 | defp elixirc_paths(:test), do: ["lib", "test/support"] 29 | defp elixirc_paths(_), do: ["lib"] 30 | 31 | # Specifies your project dependencies. 32 | # 33 | # Type `mix help deps` for examples and options. 34 | defp deps do 35 | [ 36 | {:phoenix, "~> 1.6.6"}, 37 | {:phoenix_ecto, "~> 4.4"}, 38 | {:ecto_sql, "~> 3.6"}, 39 | {:postgrex, ">= 0.0.0"}, 40 | {:phoenix_html, "~> 3.0"}, 41 | {:phoenix_live_reload, "~> 1.2", only: :dev}, 42 | {:phoenix_live_view, "~> 0.17.5"}, 43 | {:floki, ">= 0.30.0", only: :test}, 44 | {:phoenix_live_dashboard, "~> 0.6"}, 45 | {:esbuild, "~> 0.3", runtime: Mix.env() == :dev}, 46 | {:swoosh, "~> 1.3"}, 47 | {:telemetry_metrics, "~> 0.6"}, 48 | {:telemetry_poller, "~> 1.0"}, 49 | {:gettext, "~> 0.18"}, 50 | {:jason, "~> 1.2"}, 51 | {:plug_cowboy, "~> 2.5"}, 52 | {:axon_onnx, github: "elixir-nx/axon_onnx"}, 53 | {:stb_image, "~> 0.4.0"}, 54 | {:exla, "~> 0.3.0-dev", github: "elixir-nx/nx", sparse: "exla"}, 55 | {:nx, "~> 0.3.0-dev", 56 | [env: :prod, git: "https://github.com/elixir-nx/nx.git", sparse: "nx", override: true]} 57 | ] 58 | end 59 | 60 | # Aliases are shortcuts or tasks specific to the current project. 61 | # For example, to install project dependencies and perform other setup tasks, run: 62 | # 63 | # $ mix setup 64 | # 65 | # See the documentation for `Mix` for more info on aliases. 66 | defp aliases do 67 | [ 68 | setup: ["deps.get", "ecto.setup", "cmd --cd assets npm install"], 69 | "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], 70 | "ecto.reset": ["ecto.drop", "ecto.setup"], 71 | test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"], 72 | "assets.deploy": [ 73 | "cmd --cd assets npm run deploy", 74 | "esbuild default --minify", 75 | "phx.digest" 76 | ] 77 | ] 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /assets/css/app.css: -------------------------------------------------------------------------------- 1 | /* This file is for your main application CSS */ 2 | @import "./phoenix.css"; 3 | @import "https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css"; 4 | /* Alerts and form errors used by phx.new */ 5 | .alert { 6 | padding: 15px; 7 | margin-bottom: 20px; 8 | border: 1px solid transparent; 9 | border-radius: 4px; 10 | } 11 | .alert-info { 12 | color: #31708f; 13 | background-color: #d9edf7; 14 | border-color: #bce8f1; 15 | } 16 | .alert-warning { 17 | color: #8a6d3b; 18 | background-color: #fcf8e3; 19 | border-color: #faebcc; 20 | } 21 | .alert-danger { 22 | color: #a94442; 23 | background-color: #f2dede; 24 | border-color: #ebccd1; 25 | } 26 | .alert p { 27 | margin-bottom: 0; 28 | } 29 | .alert:empty { 30 | display: none; 31 | } 32 | .invalid-feedback { 33 | color: #a94442; 34 | display: block; 35 | margin: -1rem 0 2rem; 36 | } 37 | 38 | /* LiveView specific classes for your customization */ 39 | .phx-no-feedback.invalid-feedback, 40 | .phx-no-feedback .invalid-feedback { 41 | display: none; 42 | } 43 | 44 | .phx-click-loading { 45 | opacity: 0.5; 46 | transition: opacity 1s ease-out; 47 | } 48 | 49 | .phx-loading{ 50 | cursor: wait; 51 | } 52 | 53 | .phx-modal { 54 | opacity: 1!important; 55 | position: fixed; 56 | z-index: 1; 57 | left: 0; 58 | top: 0; 59 | width: 100%; 60 | height: 100%; 61 | overflow: auto; 62 | background-color: rgba(0,0,0,0.4); 63 | } 64 | 65 | .phx-modal-content { 66 | background-color: #fefefe; 67 | margin: 15vh auto; 68 | padding: 20px; 69 | border: 1px solid #888; 70 | width: 80%; 71 | } 72 | 73 | .phx-modal-close { 74 | color: #aaa; 75 | float: right; 76 | font-size: 28px; 77 | font-weight: bold; 78 | } 79 | 80 | .phx-modal-close:hover, 81 | .phx-modal-close:focus { 82 | color: black; 83 | text-decoration: none; 84 | cursor: pointer; 85 | } 86 | 87 | .fade-in-scale { 88 | animation: 0.2s ease-in 0s normal forwards 1 fade-in-scale-keys; 89 | } 90 | 91 | .fade-out-scale { 92 | animation: 0.2s ease-out 0s normal forwards 1 fade-out-scale-keys; 93 | } 94 | 95 | .fade-in { 96 | animation: 0.2s ease-out 0s normal forwards 1 fade-in-keys; 97 | } 98 | .fade-out { 99 | animation: 0.2s ease-out 0s normal forwards 1 fade-out-keys; 100 | } 101 | 102 | @keyframes fade-in-scale-keys{ 103 | 0% { scale: 0.95; opacity: 0; } 104 | 100% { scale: 1.0; opacity: 1; } 105 | } 106 | 107 | @keyframes fade-out-scale-keys{ 108 | 0% { scale: 1.0; opacity: 1; } 109 | 100% { scale: 0.95; opacity: 0; } 110 | } 111 | 112 | @keyframes fade-in-keys{ 113 | 0% { opacity: 0; } 114 | 100% { opacity: 1; } 115 | } 116 | 117 | @keyframes fade-out-keys{ 118 | 0% { opacity: 1; } 119 | 100% { opacity: 0; } 120 | } 121 | -------------------------------------------------------------------------------- /lib/live_onnx_web.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb do 2 | @moduledoc """ 3 | The entrypoint for defining your web interface, such 4 | as controllers, views, channels and so on. 5 | 6 | This can be used in your application as: 7 | 8 | use LiveOnnxWeb, :controller 9 | use LiveOnnxWeb, :view 10 | 11 | The definitions below will be executed for every view, 12 | controller, etc, so keep them short and clean, focused 13 | on imports, uses and aliases. 14 | 15 | Do NOT define functions inside the quoted expressions 16 | below. Instead, define any helper function in modules 17 | and import those modules here. 18 | """ 19 | 20 | def controller do 21 | quote do 22 | use Phoenix.Controller, namespace: LiveOnnxWeb 23 | 24 | import Plug.Conn 25 | import LiveOnnxWeb.Gettext 26 | alias LiveOnnxWeb.Router.Helpers, as: Routes 27 | end 28 | end 29 | 30 | def view do 31 | quote do 32 | use Phoenix.View, 33 | root: "lib/live_onnx_web/templates", 34 | namespace: LiveOnnxWeb 35 | 36 | # Import convenience functions from controllers 37 | import Phoenix.Controller, 38 | only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1] 39 | 40 | # Include shared imports and aliases for views 41 | unquote(view_helpers()) 42 | end 43 | end 44 | 45 | def live_view do 46 | quote do 47 | use Phoenix.LiveView, 48 | layout: {LiveOnnxWeb.LayoutView, "live.html"} 49 | 50 | unquote(view_helpers()) 51 | end 52 | end 53 | 54 | def live_component do 55 | quote do 56 | use Phoenix.LiveComponent 57 | 58 | unquote(view_helpers()) 59 | end 60 | end 61 | 62 | def component do 63 | quote do 64 | use Phoenix.Component 65 | 66 | unquote(view_helpers()) 67 | end 68 | end 69 | 70 | def router do 71 | quote do 72 | use Phoenix.Router 73 | 74 | import Plug.Conn 75 | import Phoenix.Controller 76 | import Phoenix.LiveView.Router 77 | end 78 | end 79 | 80 | def channel do 81 | quote do 82 | use Phoenix.Channel 83 | import LiveOnnxWeb.Gettext 84 | end 85 | end 86 | 87 | defp view_helpers do 88 | quote do 89 | # Use all HTML functionality (forms, tags, etc) 90 | use Phoenix.HTML 91 | 92 | # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc) 93 | import Phoenix.LiveView.Helpers 94 | 95 | # Import basic rendering functionality (render, render_layout, etc) 96 | import Phoenix.View 97 | 98 | import LiveOnnxWeb.ErrorHelpers 99 | import LiveOnnxWeb.Gettext 100 | alias LiveOnnxWeb.Router.Helpers, as: Routes 101 | end 102 | end 103 | 104 | @doc """ 105 | When used, dispatch to the appropriate controller/view/etc. 106 | """ 107 | defmacro __using__(which) when is_atom(which) do 108 | apply(__MODULE__, which, []) 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /config/runtime.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # config/runtime.exs is executed for all environments, including 4 | # during releases. It is executed after compilation and before the 5 | # system starts, so it is typically used to load production configuration 6 | # and secrets from environment variables or elsewhere. Do not define 7 | # any compile-time configuration in here, as it won't be applied. 8 | # The block below contains prod specific runtime configuration. 9 | 10 | # Start the phoenix server if environment is set and running in a release 11 | if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do 12 | config :live_onnx, LiveOnnxWeb.Endpoint, server: true 13 | end 14 | 15 | if config_env() == :prod do 16 | # The secret key base is used to sign/encrypt cookies and other secrets. 17 | # A default value is used in config/dev.exs and config/test.exs but you 18 | # want to use a different value for prod and you most likely don't want 19 | # to check this value into version control, so we use an environment 20 | # variable instead. 21 | secret_key_base = 22 | System.get_env("SECRET_KEY_BASE") || 23 | raise """ 24 | environment variable SECRET_KEY_BASE is missing. 25 | You can generate one by calling: mix phx.gen.secret 26 | """ 27 | 28 | host = System.get_env("PHX_HOST") || "example.com" 29 | port = String.to_integer(System.get_env("PORT") || "4000") 30 | 31 | config :live_onnx, LiveOnnxWeb.Endpoint, 32 | url: [host: host, port: 443], 33 | http: [ 34 | # Enable IPv6 and bind on all interfaces. 35 | # Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access. 36 | # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html 37 | # for details about using IPv6 vs IPv4 and loopback vs public addresses. 38 | ip: {0, 0, 0, 0, 0, 0, 0, 0}, 39 | port: port 40 | ], 41 | secret_key_base: secret_key_base 42 | 43 | # ## Using releases 44 | # 45 | # If you are doing OTP releases, you need to instruct Phoenix 46 | # to start each relevant endpoint: 47 | # 48 | # config :live_onnx, LiveOnnxWeb.Endpoint, server: true 49 | # 50 | # Then you can assemble a release by calling `mix release`. 51 | # See `mix help release` for more information. 52 | 53 | # ## Configuring the mailer 54 | # 55 | # In production you need to configure the mailer to use a different adapter. 56 | # Also, you may need to configure the Swoosh API client of your choice if you 57 | # are not using SMTP. Here is an example of the configuration: 58 | # 59 | # config :live_onnx, LiveOnnx.Mailer, 60 | # adapter: Swoosh.Adapters.Mailgun, 61 | # api_key: System.get_env("MAILGUN_API_KEY"), 62 | # domain: System.get_env("MAILGUN_DOMAIN") 63 | # 64 | # For this example you need include a HTTP client required by Swoosh API client. 65 | # Swoosh supports Hackney and Finch out of the box: 66 | # 67 | # config :swoosh, :api_client, Swoosh.ApiClient.Hackney 68 | # 69 | # See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details. 70 | end 71 | -------------------------------------------------------------------------------- /lib/live_onnx_web/live/page_live.ex: -------------------------------------------------------------------------------- 1 | defmodule LiveOnnxWeb.PageLive do 2 | use LiveOnnxWeb, :live_view 3 | require Axon 4 | 5 | @impl true 6 | def mount(params, _session, socket) do 7 | socket = 8 | socket 9 | |> assign_enable_model() 10 | |> assign(:list, File.read!("model/classlist.json") |> Jason.decode!()) 11 | |> assign(:upload_file, nil) 12 | |> assign(:tensor, nil) 13 | |> assign(:ans, []) 14 | |> allow_upload( 15 | :image, 16 | accept: :any, 17 | chunk_size: 6400_000, 18 | progress: &handle_progress/3, 19 | auto_upload: true 20 | ) 21 | 22 | {:ok, socket} 23 | end 24 | 25 | defp assign_enable_model(socket) do 26 | model_names = 27 | File.ls!("model") 28 | |> Enum.filter(fn name -> File.exists?("model/#{name}/model.onnx") end) 29 | 30 | assign(socket, :enable_model, model_names) 31 | end 32 | 33 | def handle_params(%{"id" => "vgg16"}, _action, socket) do 34 | {:ok, dets} = :dets.open_file('model/vgg16/model.dets') 35 | [{1, {model, params}}] = :dets.lookup(dets, 1) 36 | 37 | {:noreply, assign_model(socket, model, params, "vgg16")} 38 | end 39 | 40 | def handle_params(%{"id" => "alexnet"}, _action, socket) do 41 | {:ok, dets} = :dets.open_file('model/alexnet/model.dets') 42 | [{1, {model, params}}] = :dets.lookup(dets, 1) 43 | 44 | {:noreply, assign_model(socket, model, params, "alexnet")} 45 | end 46 | 47 | def handle_params(%{"id" => model_name}, _action, socket) do 48 | {model, params} = AxonOnnx.import("model/#{model_name}/model.onnx") 49 | 50 | {:noreply, assign_model(socket, model, params, model_name)} 51 | end 52 | 53 | def handle_params(%{}, _action, socket) do 54 | {:noreply, assign(socket, model_name: "None")} 55 | end 56 | 57 | defp assign_model(socket, model, params, model_name) do 58 | socket 59 | |> assign(:model, model) 60 | |> assign(:params, params) 61 | |> assign(:model_name, model_name) 62 | |> assign(:ans, []) 63 | end 64 | 65 | def handle_progress(:image, _entry, socket) do 66 | upload_file = 67 | consume_uploaded_entries(socket, :image, fn %{path: path}, _entry -> 68 | File.read(path) 69 | end) 70 | |> List.first() 71 | 72 | tensor = build_tensor(upload_file) 73 | {:noreply, assign(socket, upload_file: upload_file, tensor: tensor)} 74 | end 75 | 76 | defp build_tensor(data) do 77 | {:ok, image} = StbImage.from_binary(data) 78 | {:ok, image} = StbImage.resize(image, 224, 224) 79 | nx_image = StbImage.to_nx(image) 80 | nx_channels = Nx.axis_size(nx_image, 2) 81 | 82 | case nx_channels do 83 | 3 -> nx_image 84 | 4 -> Nx.slice(nx_image, [0, 0, 0], [224, 224, 3]) 85 | end 86 | |> Nx.divide(255) 87 | |> Nx.subtract(Nx.tensor([0.485, 0.456, 0.406])) 88 | |> Nx.divide(Nx.tensor([0.229, 0.224, 0.225])) 89 | |> Nx.transpose() 90 | |> Nx.new_axis(0) 91 | end 92 | 93 | @impl true 94 | def handle_event("validate", _params, socket) do 95 | {:noreply, socket} 96 | end 97 | 98 | @impl true 99 | def handle_event("selected", %{"model" => model}, socket) do 100 | {:noreply, push_patch(socket, to: "/#{model}", replace: true)} 101 | end 102 | 103 | @impl true 104 | def handle_event( 105 | "detect", 106 | _params, 107 | %{assigns: %{tensor: tensor, model: model, params: params}} = socket 108 | ) do 109 | ans = 110 | Axon.predict(model, params, tensor) 111 | |> Nx.flatten() 112 | |> Nx.argsort() 113 | |> Nx.reverse() 114 | |> Nx.slice([0], [5]) 115 | |> Nx.to_flat_list() 116 | 117 | {:noreply, assign(socket, :ans, ans)} 118 | end 119 | 120 | @impl true 121 | def handle_event("clear", _params, socket) do 122 | socket = 123 | socket 124 | |> assign(:upload_file, nil) 125 | |> assign(:tensor, nil) 126 | |> assign(:ans, []) 127 | 128 | {:noreply, socket} 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /assets/vendor/topbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license MIT 3 | * topbar 1.0.0, 2021-01-06 4 | * https://buunguyen.github.io/topbar 5 | * Copyright (c) 2021 Buu Nguyen 6 | */ 7 | (function (window, document) { 8 | "use strict"; 9 | 10 | // https://gist.github.com/paulirish/1579671 11 | (function () { 12 | var lastTime = 0; 13 | var vendors = ["ms", "moz", "webkit", "o"]; 14 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 15 | window.requestAnimationFrame = 16 | window[vendors[x] + "RequestAnimationFrame"]; 17 | window.cancelAnimationFrame = 18 | window[vendors[x] + "CancelAnimationFrame"] || 19 | window[vendors[x] + "CancelRequestAnimationFrame"]; 20 | } 21 | if (!window.requestAnimationFrame) 22 | window.requestAnimationFrame = function (callback, element) { 23 | var currTime = new Date().getTime(); 24 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 25 | var id = window.setTimeout(function () { 26 | callback(currTime + timeToCall); 27 | }, timeToCall); 28 | lastTime = currTime + timeToCall; 29 | return id; 30 | }; 31 | if (!window.cancelAnimationFrame) 32 | window.cancelAnimationFrame = function (id) { 33 | clearTimeout(id); 34 | }; 35 | })(); 36 | 37 | var canvas, 38 | progressTimerId, 39 | fadeTimerId, 40 | currentProgress, 41 | showing, 42 | addEvent = function (elem, type, handler) { 43 | if (elem.addEventListener) elem.addEventListener(type, handler, false); 44 | else if (elem.attachEvent) elem.attachEvent("on" + type, handler); 45 | else elem["on" + type] = handler; 46 | }, 47 | options = { 48 | autoRun: true, 49 | barThickness: 3, 50 | barColors: { 51 | 0: "rgba(26, 188, 156, .9)", 52 | ".25": "rgba(52, 152, 219, .9)", 53 | ".50": "rgba(241, 196, 15, .9)", 54 | ".75": "rgba(230, 126, 34, .9)", 55 | "1.0": "rgba(211, 84, 0, .9)", 56 | }, 57 | shadowBlur: 10, 58 | shadowColor: "rgba(0, 0, 0, .6)", 59 | className: null, 60 | }, 61 | repaint = function () { 62 | canvas.width = window.innerWidth; 63 | canvas.height = options.barThickness * 5; // need space for shadow 64 | 65 | var ctx = canvas.getContext("2d"); 66 | ctx.shadowBlur = options.shadowBlur; 67 | ctx.shadowColor = options.shadowColor; 68 | 69 | var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0); 70 | for (var stop in options.barColors) 71 | lineGradient.addColorStop(stop, options.barColors[stop]); 72 | ctx.lineWidth = options.barThickness; 73 | ctx.beginPath(); 74 | ctx.moveTo(0, options.barThickness / 2); 75 | ctx.lineTo( 76 | Math.ceil(currentProgress * canvas.width), 77 | options.barThickness / 2 78 | ); 79 | ctx.strokeStyle = lineGradient; 80 | ctx.stroke(); 81 | }, 82 | createCanvas = function () { 83 | canvas = document.createElement("canvas"); 84 | var style = canvas.style; 85 | style.position = "fixed"; 86 | style.top = style.left = style.right = style.margin = style.padding = 0; 87 | style.zIndex = 100001; 88 | style.display = "none"; 89 | if (options.className) canvas.classList.add(options.className); 90 | document.body.appendChild(canvas); 91 | addEvent(window, "resize", repaint); 92 | }, 93 | topbar = { 94 | config: function (opts) { 95 | for (var key in opts) 96 | if (options.hasOwnProperty(key)) options[key] = opts[key]; 97 | }, 98 | show: function () { 99 | if (showing) return; 100 | showing = true; 101 | if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId); 102 | if (!canvas) createCanvas(); 103 | canvas.style.opacity = 1; 104 | canvas.style.display = "block"; 105 | topbar.progress(0); 106 | if (options.autoRun) { 107 | (function loop() { 108 | progressTimerId = window.requestAnimationFrame(loop); 109 | topbar.progress( 110 | "+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2) 111 | ); 112 | })(); 113 | } 114 | }, 115 | progress: function (to) { 116 | if (typeof to === "undefined") return currentProgress; 117 | if (typeof to === "string") { 118 | to = 119 | (to.indexOf("+") >= 0 || to.indexOf("-") >= 0 120 | ? currentProgress 121 | : 0) + parseFloat(to); 122 | } 123 | currentProgress = to > 1 ? 1 : to; 124 | repaint(); 125 | return currentProgress; 126 | }, 127 | hide: function () { 128 | if (!showing) return; 129 | showing = false; 130 | if (progressTimerId != null) { 131 | window.cancelAnimationFrame(progressTimerId); 132 | progressTimerId = null; 133 | } 134 | (function loop() { 135 | if (topbar.progress("+.1") >= 1) { 136 | canvas.style.opacity -= 0.05; 137 | if (canvas.style.opacity <= 0.05) { 138 | canvas.style.display = "none"; 139 | fadeTimerId = null; 140 | return; 141 | } 142 | } 143 | fadeTimerId = window.requestAnimationFrame(loop); 144 | })(); 145 | }, 146 | }; 147 | 148 | if (typeof module === "object" && typeof module.exports === "object") { 149 | module.exports = topbar; 150 | } else if (typeof define === "function" && define.amd) { 151 | define(function () { 152 | return topbar; 153 | }); 154 | } else { 155 | this.topbar = topbar; 156 | } 157 | }.call(this, window, document)); 158 | -------------------------------------------------------------------------------- /assets/css/phoenix.css: -------------------------------------------------------------------------------- 1 | /* Includes some default style for the starter application. 2 | * This can be safely deleted to start fresh. 3 | */ 4 | 5 | /* Milligram v1.4.1 https://milligram.github.io 6 | * Copyright (c) 2020 CJ Patoilo Licensed under the MIT license 7 | */ 8 | 9 | *,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#000000;font-family:'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#0069d9;border:0.1rem solid #0069d9;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#0069d9;border-color:#0069d9}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#0069d9}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#0069d9}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#0069d9}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#0069d9}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #0069d9;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='color'],input[type='date'],input[type='datetime'],input[type='datetime-local'],input[type='email'],input[type='month'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],input[type='week'],input:not([type]),textarea,select{-webkit-appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem .7rem;width:100%}input[type='color']:focus,input[type='date']:focus,input[type='datetime']:focus,input[type='datetime-local']:focus,input[type='email']:focus,input[type='month']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,input[type='week']:focus,input:not([type]):focus,textarea:focus,select:focus{border-color:#0069d9;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}select[multiple]{background:none;height:auto}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-40{margin-left:40%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-60{margin-left:60%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#0069d9;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;display:block;overflow-x:auto;text-align:left;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}@media (min-width: 40rem){table{display:table;overflow-x:initial}}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right} 10 | 11 | /* General style */ 12 | h1{font-size: 3.6rem; line-height: 1.25} 13 | h2{font-size: 2.8rem; line-height: 1.3} 14 | h3{font-size: 2.2rem; letter-spacing: -.08rem; line-height: 1.35} 15 | h4{font-size: 1.8rem; letter-spacing: -.05rem; line-height: 1.5} 16 | h5{font-size: 1.6rem; letter-spacing: 0; line-height: 1.4} 17 | h6{font-size: 1.4rem; letter-spacing: 0; line-height: 1.2} 18 | pre{padding: 1em;} 19 | 20 | .container{ 21 | margin: 0 auto; 22 | max-width: 80.0rem; 23 | padding: 0 2.0rem; 24 | position: relative; 25 | width: 100% 26 | } 27 | select { 28 | width: auto; 29 | } 30 | 31 | /* Phoenix promo and logo */ 32 | .phx-hero { 33 | text-align: center; 34 | border-bottom: 1px solid #e3e3e3; 35 | background: #eee; 36 | border-radius: 6px; 37 | padding: 3em 3em 1em; 38 | margin-bottom: 3rem; 39 | font-weight: 200; 40 | font-size: 120%; 41 | } 42 | .phx-hero input { 43 | background: #ffffff; 44 | } 45 | .phx-logo { 46 | min-width: 300px; 47 | margin: 1rem; 48 | display: block; 49 | } 50 | .phx-logo img { 51 | width: auto; 52 | display: block; 53 | } 54 | 55 | /* Headers */ 56 | header { 57 | width: 100%; 58 | background: #fdfdfd; 59 | border-bottom: 1px solid #eaeaea; 60 | margin-bottom: 2rem; 61 | } 62 | header section { 63 | align-items: center; 64 | display: flex; 65 | flex-direction: column; 66 | justify-content: space-between; 67 | } 68 | header section :first-child { 69 | order: 2; 70 | } 71 | header section :last-child { 72 | order: 1; 73 | } 74 | header nav ul, 75 | header nav li { 76 | margin: 0; 77 | padding: 0; 78 | display: block; 79 | text-align: right; 80 | white-space: nowrap; 81 | } 82 | header nav ul { 83 | margin: 1rem; 84 | margin-top: 0; 85 | } 86 | header nav a { 87 | display: block; 88 | } 89 | 90 | @media (min-width: 40.0rem) { /* Small devices (landscape phones, 576px and up) */ 91 | header section { 92 | flex-direction: row; 93 | } 94 | header nav ul { 95 | margin: 1rem; 96 | } 97 | .phx-logo { 98 | flex-basis: 527px; 99 | margin: 2rem 1rem; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "axon": {:git, "https://github.com/elixir-nx/axon.git", "d59221193a80efde0c6268aab8338840bae2f04a", []}, 3 | "axon_onnx": {:git, "https://github.com/elixir-nx/axon_onnx.git", "a1c6bf6dce6ca25520941c62aa6e5ec083fe2848", []}, 4 | "castore": {:hex, :castore, "0.1.17", "ba672681de4e51ed8ec1f74ed624d104c0db72742ea1a5e74edbc770c815182f", [:mix], [], "hexpm", "d9844227ed52d26e7519224525cb6868650c272d4a3d327ce3ca5570c12163f9"}, 5 | "complex": {:hex, :complex, "0.4.1", "5d5a7bdbec13428c05824bac550b7d2b14ec395cd64663eca3cb0d3359aa22c5", [:mix], [], "hexpm", "6a813529dddba9bdfc17dffdd1aeda7b3d6891b76c8d4c1ae902ec74eb496a32"}, 6 | "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, 7 | "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, 8 | "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, 9 | "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, 10 | "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"}, 11 | "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, 12 | "ecto": {:hex, :ecto, "3.8.4", "e06b8b87e62b27fea17fd2ff6041572ddd10339fd16cdf58446e402c6c90a74b", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f9244288b8d42db40515463a008cf3f4e0e564bb9c249fe87bf28a6d79fe82d4"}, 13 | "ecto_sql": {:hex, :ecto_sql, "3.8.3", "a7d22c624202546a39d615ed7a6b784580391e65723f2d24f65941b4dd73d471", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.8.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "348cb17fb9e6daf6f251a87049eafcb57805e2892e5e6a0f5dea0985d367329b"}, 14 | "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, 15 | "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"}, 16 | "exla": {:git, "https://github.com/elixir-nx/nx.git", "8eadd775cf08b871c364794b231924170ff06e3d", [sparse: "exla"]}, 17 | "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, 18 | "floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"}, 19 | "gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"}, 20 | "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, 21 | "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, 22 | "mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"}, 23 | "nx": {:git, "https://github.com/elixir-nx/nx.git", "8eadd775cf08b871c364794b231924170ff06e3d", [sparse: "nx"]}, 24 | "phoenix": {:hex, :phoenix, "1.6.11", "29f3c0fd12fa1fc4d4b05e341578e55bc78d96ea83a022587a7e276884d397e4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1664e34f80c25ea4918fbadd957f491225ef601c0e00b4e644b1a772864bfbc2"}, 25 | "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, 26 | "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, 27 | "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.5", "1495bb014be12c9a9252eca04b9af54246f6b5c1e4cd1f30210cd00ec540cf8e", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.7", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "ef4fa50dd78364409039c99cf6f98ab5209b4c5f8796c17f4db118324f0db852"}, 28 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, 29 | "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.11", "205f6aa5405648c76f2abcd57716f42fc07d8f21dd8ea7b262dd12b324b50c95", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7177791944b7f90ed18f5935a6a5f07f760b36f7b3bdfb9d28c57440a3c43f99"}, 30 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, 31 | "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, 32 | "plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"}, 33 | "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, 34 | "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, 35 | "postgrex": {:hex, :postgrex, "0.16.3", "fac79a81a9a234b11c44235a4494d8565303fa4b9147acf57e48978a074971db", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "aeaae1d2d1322da4e5fe90d241b0a564ce03a3add09d7270fb85362166194590"}, 36 | "protox": {:hex, :protox, "1.6.10", "41d0b0c5b9190e7d5e6a2b1a03a09257ead6f3d95e6a0cf8b81430b526126908", [:mix], [{:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "9769fca26ae7abfc5cc61308a1e8d9e2400ff89a799599cee7930d21132832d9"}, 37 | "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, 38 | "stb_image": {:hex, :stb_image, "0.4.0", "0f5cf6b3a3a91fccdd712be44c4651689edb2b5ec22c418833126e349254b3f2", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}], "hexpm", "0f2abad7f863bf0efcb15da4852471641864ab4cea36733eda0a7da0536ed67a"}, 39 | "swoosh": {:hex, :swoosh, "1.7.3", "febb47c8c3ce76747eb9e3ea25ed694c815f72069127e3bb039b7724082ec670", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76abac313f95b6825baa8ceec269d597e8395950c928742fc6451d3456ca256d"}, 40 | "table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"}, 41 | "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"}, 42 | "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, 43 | "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, 44 | "xla": {:hex, :xla, "0.3.0", "7f40d3d799447bd745e6e61985c922821e355eca37c25754bac299d11f137f2e", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "0de304f5624ea2f2ae1afdcbd72d33855364be36fed2cc5de8d6d2ec8950926e"}, 45 | } 46 | -------------------------------------------------------------------------------- /model/classlist.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "tench, Tinca tinca", 3 | "1": "goldfish, Carassius auratus", 4 | "2": "great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias", 5 | "3": "tiger shark, Galeocerdo cuvieri", 6 | "4": "hammerhead, hammerhead shark", 7 | "5": "electric ray, crampfish, numbfish, torpedo", 8 | "6": "stingray", 9 | "7": "cock", 10 | "8": "hen", 11 | "9": "ostrich, Struthio camelus", 12 | "10": "brambling, Fringilla montifringilla", 13 | "11": "goldfinch, Carduelis carduelis", 14 | "12": "house finch, linnet, Carpodacus mexicanus", 15 | "13": "junco, snowbird", 16 | "14": "indigo bunting, indigo finch, indigo bird, Passerina cyanea", 17 | "15": "robin, American robin, Turdus migratorius", 18 | "16": "bulbul", 19 | "17": "jay", 20 | "18": "magpie", 21 | "19": "chickadee", 22 | "20": "water ouzel, dipper", 23 | "21": "kite", 24 | "22": "bald eagle, American eagle, Haliaeetus leucocephalus", 25 | "23": "vulture", 26 | "24": "great grey owl, great gray owl, Strix nebulosa", 27 | "25": "European fire salamander, Salamandra salamandra", 28 | "26": "common newt, Triturus vulgaris", 29 | "27": "eft", 30 | "28": "spotted salamander, Ambystoma maculatum", 31 | "29": "axolotl, mud puppy, Ambystoma mexicanum", 32 | "30": "bullfrog, Rana catesbeiana", 33 | "31": "tree frog, tree-frog", 34 | "32": "tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui", 35 | "33": "loggerhead, loggerhead turtle, Caretta caretta", 36 | "34": "leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea", 37 | "35": "mud turtle", 38 | "36": "terrapin", 39 | "37": "box turtle, box tortoise", 40 | "38": "banded gecko", 41 | "39": "common iguana, iguana, Iguana iguana", 42 | "40": "American chameleon, anole, Anolis carolinensis", 43 | "41": "whiptail, whiptail lizard", 44 | "42": "agama", 45 | "43": "frilled lizard, Chlamydosaurus kingi", 46 | "44": "alligator lizard", 47 | "45": "Gila monster, Heloderma suspectum", 48 | "46": "green lizard, Lacerta viridis", 49 | "47": "African chameleon, Chamaeleo chamaeleon", 50 | "48": "Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis", 51 | "49": "African crocodile, Nile crocodile, Crocodylus niloticus", 52 | "50": "American alligator, Alligator mississipiensis", 53 | "51": "triceratops", 54 | "52": "thunder snake, worm snake, Carphophis amoenus", 55 | "53": "ringneck snake, ring-necked snake, ring snake", 56 | "54": "hognose snake, puff adder, sand viper", 57 | "55": "green snake, grass snake", 58 | "56": "king snake, kingsnake", 59 | "57": "garter snake, grass snake", 60 | "58": "water snake", 61 | "59": "vine snake", 62 | "60": "night snake, Hypsiglena torquata", 63 | "61": "boa constrictor, Constrictor constrictor", 64 | "62": "rock python, rock snake, Python sebae", 65 | "63": "Indian cobra, Naja naja", 66 | "64": "green mamba", 67 | "65": "sea snake", 68 | "66": "horned viper, cerastes, sand viper, horned asp, Cerastes cornutus", 69 | "67": "diamondback, diamondback rattlesnake, Crotalus adamanteus", 70 | "68": "sidewinder, horned rattlesnake, Crotalus cerastes", 71 | "69": "trilobite", 72 | "70": "harvestman, daddy longlegs, Phalangium opilio", 73 | "71": "scorpion", 74 | "72": "black and gold garden spider, Argiope aurantia", 75 | "73": "barn spider, Araneus cavaticus", 76 | "74": "garden spider, Aranea diademata", 77 | "75": "black widow, Latrodectus mactans", 78 | "76": "tarantula", 79 | "77": "wolf spider, hunting spider", 80 | "78": "tick", 81 | "79": "centipede", 82 | "80": "black grouse", 83 | "81": "ptarmigan", 84 | "82": "ruffed grouse, partridge, Bonasa umbellus", 85 | "83": "prairie chicken, prairie grouse, prairie fowl", 86 | "84": "peacock", 87 | "85": "quail", 88 | "86": "partridge", 89 | "87": "African grey, African gray, Psittacus erithacus", 90 | "88": "macaw", 91 | "89": "sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita", 92 | "90": "lorikeet", 93 | "91": "coucal", 94 | "92": "bee eater", 95 | "93": "hornbill", 96 | "94": "hummingbird", 97 | "95": "jacamar", 98 | "96": "toucan", 99 | "97": "drake", 100 | "98": "red-breasted merganser, Mergus serrator", 101 | "99": "goose", 102 | "100": "black swan, Cygnus atratus", 103 | "101": "tusker", 104 | "102": "echidna, spiny anteater, anteater", 105 | "103": "platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus", 106 | "104": "wallaby, brush kangaroo", 107 | "105": "koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus", 108 | "106": "wombat", 109 | "107": "jellyfish", 110 | "108": "sea anemone, anemone", 111 | "109": "brain coral", 112 | "110": "flatworm, platyhelminth", 113 | "111": "nematode, nematode worm, roundworm", 114 | "112": "conch", 115 | "113": "snail", 116 | "114": "slug", 117 | "115": "sea slug, nudibranch", 118 | "116": "chiton, coat-of-mail shell, sea cradle, polyplacophore", 119 | "117": "chambered nautilus, pearly nautilus, nautilus", 120 | "118": "Dungeness crab, Cancer magister", 121 | "119": "rock crab, Cancer irroratus", 122 | "120": "fiddler crab", 123 | "121": "king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica", 124 | "122": "American lobster, Northern lobster, Maine lobster, Homarus americanus", 125 | "123": "spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish", 126 | "124": "crayfish, crawfish, crawdad, crawdaddy", 127 | "125": "hermit crab", 128 | "126": "isopod", 129 | "127": "white stork, Ciconia ciconia", 130 | "128": "black stork, Ciconia nigra", 131 | "129": "spoonbill", 132 | "130": "flamingo", 133 | "131": "little blue heron, Egretta caerulea", 134 | "132": "American egret, great white heron, Egretta albus", 135 | "133": "bittern", 136 | "134": "crane", 137 | "135": "limpkin, Aramus pictus", 138 | "136": "European gallinule, Porphyrio porphyrio", 139 | "137": "American coot, marsh hen, mud hen, water hen, Fulica americana", 140 | "138": "bustard", 141 | "139": "ruddy turnstone, Arenaria interpres", 142 | "140": "red-backed sandpiper, dunlin, Erolia alpina", 143 | "141": "redshank, Tringa totanus", 144 | "142": "dowitcher", 145 | "143": "oystercatcher, oyster catcher", 146 | "144": "pelican", 147 | "145": "king penguin, Aptenodytes patagonica", 148 | "146": "albatross, mollymawk", 149 | "147": "grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus", 150 | "148": "killer whale, killer, orca, grampus, sea wolf, Orcinus orca", 151 | "149": "dugong, Dugong dugon", 152 | "150": "sea lion", 153 | "151": "Chihuahua", 154 | "152": "Japanese spaniel", 155 | "153": "Maltese dog, Maltese terrier, Maltese", 156 | "154": "Pekinese, Pekingese, Peke", 157 | "155": "Shih-Tzu", 158 | "156": "Blenheim spaniel", 159 | "157": "papillon", 160 | "158": "toy terrier", 161 | "159": "Rhodesian ridgeback", 162 | "160": "Afghan hound, Afghan", 163 | "161": "basset, basset hound", 164 | "162": "beagle", 165 | "163": "bloodhound, sleuthhound", 166 | "164": "bluetick", 167 | "165": "black-and-tan coonhound", 168 | "166": "Walker hound, Walker foxhound", 169 | "167": "English foxhound", 170 | "168": "redbone", 171 | "169": "borzoi, Russian wolfhound", 172 | "170": "Irish wolfhound", 173 | "171": "Italian greyhound", 174 | "172": "whippet", 175 | "173": "Ibizan hound, Ibizan Podenco", 176 | "174": "Norwegian elkhound, elkhound", 177 | "175": "otterhound, otter hound", 178 | "176": "Saluki, gazelle hound", 179 | "177": "Scottish deerhound, deerhound", 180 | "178": "Weimaraner", 181 | "179": "Staffordshire bullterrier, Staffordshire bull terrier", 182 | "180": "American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier", 183 | "181": "Bedlington terrier", 184 | "182": "Border terrier", 185 | "183": "Kerry blue terrier", 186 | "184": "Irish terrier", 187 | "185": "Norfolk terrier", 188 | "186": "Norwich terrier", 189 | "187": "Yorkshire terrier", 190 | "188": "wire-haired fox terrier", 191 | "189": "Lakeland terrier", 192 | "190": "Sealyham terrier, Sealyham", 193 | "191": "Airedale, Airedale terrier", 194 | "192": "cairn, cairn terrier", 195 | "193": "Australian terrier", 196 | "194": "Dandie Dinmont, Dandie Dinmont terrier", 197 | "195": "Boston bull, Boston terrier", 198 | "196": "miniature schnauzer", 199 | "197": "giant schnauzer", 200 | "198": "standard schnauzer", 201 | "199": "Scotch terrier, Scottish terrier, Scottie", 202 | "200": "Tibetan terrier, chrysanthemum dog", 203 | "201": "silky terrier, Sydney silky", 204 | "202": "soft-coated wheaten terrier", 205 | "203": "West Highland white terrier", 206 | "204": "Lhasa, Lhasa apso", 207 | "205": "flat-coated retriever", 208 | "206": "curly-coated retriever", 209 | "207": "golden retriever", 210 | "208": "Labrador retriever", 211 | "209": "Chesapeake Bay retriever", 212 | "210": "German short-haired pointer", 213 | "211": "vizsla, Hungarian pointer", 214 | "212": "English setter", 215 | "213": "Irish setter, red setter", 216 | "214": "Gordon setter", 217 | "215": "Brittany spaniel", 218 | "216": "clumber, clumber spaniel", 219 | "217": "English springer, English springer spaniel", 220 | "218": "Welsh springer spaniel", 221 | "219": "cocker spaniel, English cocker spaniel, cocker", 222 | "220": "Sussex spaniel", 223 | "221": "Irish water spaniel", 224 | "222": "kuvasz", 225 | "223": "schipperke", 226 | "224": "groenendael", 227 | "225": "malinois", 228 | "226": "briard", 229 | "227": "kelpie", 230 | "228": "komondor", 231 | "229": "Old English sheepdog, bobtail", 232 | "230": "Shetland sheepdog, Shetland sheep dog, Shetland", 233 | "231": "collie", 234 | "232": "Border collie", 235 | "233": "Bouvier des Flandres, Bouviers des Flandres", 236 | "234": "Rottweiler", 237 | "235": "German shepherd, German shepherd dog, German police dog, alsatian", 238 | "236": "Doberman, Doberman pinscher", 239 | "237": "miniature pinscher", 240 | "238": "Greater Swiss Mountain dog", 241 | "239": "Bernese mountain dog", 242 | "240": "Appenzeller", 243 | "241": "EntleBucher", 244 | "242": "boxer", 245 | "243": "bull mastiff", 246 | "244": "Tibetan mastiff", 247 | "245": "French bulldog", 248 | "246": "Great Dane", 249 | "247": "Saint Bernard, St Bernard", 250 | "248": "Eskimo dog, husky", 251 | "249": "malamute, malemute, Alaskan malamute", 252 | "250": "Siberian husky", 253 | "251": "dalmatian, coach dog, carriage dog", 254 | "252": "affenpinscher, monkey pinscher, monkey dog", 255 | "253": "basenji", 256 | "254": "pug, pug-dog", 257 | "255": "Leonberg", 258 | "256": "Newfoundland, Newfoundland dog", 259 | "257": "Great Pyrenees", 260 | "258": "Samoyed, Samoyede", 261 | "259": "Pomeranian", 262 | "260": "chow, chow chow", 263 | "261": "keeshond", 264 | "262": "Brabancon griffon", 265 | "263": "Pembroke, Pembroke Welsh corgi", 266 | "264": "Cardigan, Cardigan Welsh corgi", 267 | "265": "toy poodle", 268 | "266": "miniature poodle", 269 | "267": "standard poodle", 270 | "268": "Mexican hairless", 271 | "269": "timber wolf, grey wolf, gray wolf, Canis lupus", 272 | "270": "white wolf, Arctic wolf, Canis lupus tundrarum", 273 | "271": "red wolf, maned wolf, Canis rufus, Canis niger", 274 | "272": "coyote, prairie wolf, brush wolf, Canis latrans", 275 | "273": "dingo, warrigal, warragal, Canis dingo", 276 | "274": "dhole, Cuon alpinus", 277 | "275": "African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus", 278 | "276": "hyena, hyaena", 279 | "277": "red fox, Vulpes vulpes", 280 | "278": "kit fox, Vulpes macrotis", 281 | "279": "Arctic fox, white fox, Alopex lagopus", 282 | "280": "grey fox, gray fox, Urocyon cinereoargenteus", 283 | "281": "tabby, tabby cat", 284 | "282": "tiger cat", 285 | "283": "Persian cat", 286 | "284": "Siamese cat, Siamese", 287 | "285": "Egyptian cat", 288 | "286": "cougar, puma, catamount, mountain lion, painter, panther, Felis concolor", 289 | "287": "lynx, catamount", 290 | "288": "leopard, Panthera pardus", 291 | "289": "snow leopard, ounce, Panthera uncia", 292 | "290": "jaguar, panther, Panthera onca, Felis onca", 293 | "291": "lion, king of beasts, Panthera leo", 294 | "292": "tiger, Panthera tigris", 295 | "293": "cheetah, chetah, Acinonyx jubatus", 296 | "294": "brown bear, bruin, Ursus arctos", 297 | "295": "American black bear, black bear, Ursus americanus, Euarctos americanus", 298 | "296": "ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus", 299 | "297": "sloth bear, Melursus ursinus, Ursus ursinus", 300 | "298": "mongoose", 301 | "299": "meerkat, mierkat", 302 | "300": "tiger beetle", 303 | "301": "ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle", 304 | "302": "ground beetle, carabid beetle", 305 | "303": "long-horned beetle, longicorn, longicorn beetle", 306 | "304": "leaf beetle, chrysomelid", 307 | "305": "dung beetle", 308 | "306": "rhinoceros beetle", 309 | "307": "weevil", 310 | "308": "fly", 311 | "309": "bee", 312 | "310": "ant, emmet, pismire", 313 | "311": "grasshopper, hopper", 314 | "312": "cricket", 315 | "313": "walking stick, walkingstick, stick insect", 316 | "314": "cockroach, roach", 317 | "315": "mantis, mantid", 318 | "316": "cicada, cicala", 319 | "317": "leafhopper", 320 | "318": "lacewing, lacewing fly", 321 | "319": "dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk", 322 | "320": "damselfly", 323 | "321": "admiral", 324 | "322": "ringlet, ringlet butterfly", 325 | "323": "monarch, monarch butterfly, milkweed butterfly, Danaus plexippus", 326 | "324": "cabbage butterfly", 327 | "325": "sulphur butterfly, sulfur butterfly", 328 | "326": "lycaenid, lycaenid butterfly", 329 | "327": "starfish, sea star", 330 | "328": "sea urchin", 331 | "329": "sea cucumber, holothurian", 332 | "330": "wood rabbit, cottontail, cottontail rabbit", 333 | "331": "hare", 334 | "332": "Angora, Angora rabbit", 335 | "333": "hamster", 336 | "334": "porcupine, hedgehog", 337 | "335": "fox squirrel, eastern fox squirrel, Sciurus niger", 338 | "336": "marmot", 339 | "337": "beaver", 340 | "338": "guinea pig, Cavia cobaya", 341 | "339": "sorrel", 342 | "340": "zebra", 343 | "341": "hog, pig, grunter, squealer, Sus scrofa", 344 | "342": "wild boar, boar, Sus scrofa", 345 | "343": "warthog", 346 | "344": "hippopotamus, hippo, river horse, Hippopotamus amphibius", 347 | "345": "ox", 348 | "346": "water buffalo, water ox, Asiatic buffalo, Bubalus bubalis", 349 | "347": "bison", 350 | "348": "ram, tup", 351 | "349": "bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis", 352 | "350": "ibex, Capra ibex", 353 | "351": "hartebeest", 354 | "352": "impala, Aepyceros melampus", 355 | "353": "gazelle", 356 | "354": "Arabian camel, dromedary, Camelus dromedarius", 357 | "355": "llama", 358 | "356": "weasel", 359 | "357": "mink", 360 | "358": "polecat, fitch, foulmart, foumart, Mustela putorius", 361 | "359": "black-footed ferret, ferret, Mustela nigripes", 362 | "360": "otter", 363 | "361": "skunk, polecat, wood pussy", 364 | "362": "badger", 365 | "363": "armadillo", 366 | "364": "three-toed sloth, ai, Bradypus tridactylus", 367 | "365": "orangutan, orang, orangutang, Pongo pygmaeus", 368 | "366": "gorilla, Gorilla gorilla", 369 | "367": "chimpanzee, chimp, Pan troglodytes", 370 | "368": "gibbon, Hylobates lar", 371 | "369": "siamang, Hylobates syndactylus, Symphalangus syndactylus", 372 | "370": "guenon, guenon monkey", 373 | "371": "patas, hussar monkey, Erythrocebus patas", 374 | "372": "baboon", 375 | "373": "macaque", 376 | "374": "langur", 377 | "375": "colobus, colobus monkey", 378 | "376": "proboscis monkey, Nasalis larvatus", 379 | "377": "marmoset", 380 | "378": "capuchin, ringtail, Cebus capucinus", 381 | "379": "howler monkey, howler", 382 | "380": "titi, titi monkey", 383 | "381": "spider monkey, Ateles geoffroyi", 384 | "382": "squirrel monkey, Saimiri sciureus", 385 | "383": "Madagascar cat, ring-tailed lemur, Lemur catta", 386 | "384": "indri, indris, Indri indri, Indri brevicaudatus", 387 | "385": "Indian elephant, Elephas maximus", 388 | "386": "African elephant, Loxodonta africana", 389 | "387": "lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens", 390 | "388": "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca", 391 | "389": "barracouta, snoek", 392 | "390": "eel", 393 | "391": "coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch", 394 | "392": "rock beauty, Holocanthus tricolor", 395 | "393": "anemone fish", 396 | "394": "sturgeon", 397 | "395": "gar, garfish, garpike, billfish, Lepisosteus osseus", 398 | "396": "lionfish", 399 | "397": "puffer, pufferfish, blowfish, globefish", 400 | "398": "abacus", 401 | "399": "abaya", 402 | "400": "academic gown, academic robe, judge's robe", 403 | "401": "accordion, piano accordion, squeeze box", 404 | "402": "acoustic guitar", 405 | "403": "aircraft carrier, carrier, flattop, attack aircraft carrier", 406 | "404": "airliner", 407 | "405": "airship, dirigible", 408 | "406": "altar", 409 | "407": "ambulance", 410 | "408": "amphibian, amphibious vehicle", 411 | "409": "analog clock", 412 | "410": "apiary, bee house", 413 | "411": "apron", 414 | "412": "ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin", 415 | "413": "assault rifle, assault gun", 416 | "414": "backpack, back pack, knapsack, packsack, rucksack, haversack", 417 | "415": "bakery, bakeshop, bakehouse", 418 | "416": "balance beam, beam", 419 | "417": "balloon", 420 | "418": "ballpoint, ballpoint pen, ballpen, Biro", 421 | "419": "Band Aid", 422 | "420": "banjo", 423 | "421": "bannister, banister, balustrade, balusters, handrail", 424 | "422": "barbell", 425 | "423": "barber chair", 426 | "424": "barbershop", 427 | "425": "barn", 428 | "426": "barometer", 429 | "427": "barrel, cask", 430 | "428": "barrow, garden cart, lawn cart, wheelbarrow", 431 | "429": "baseball", 432 | "430": "basketball", 433 | "431": "bassinet", 434 | "432": "bassoon", 435 | "433": "bathing cap, swimming cap", 436 | "434": "bath towel", 437 | "435": "bathtub, bathing tub, bath, tub", 438 | "436": "beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon", 439 | "437": "beacon, lighthouse, beacon light, pharos", 440 | "438": "beaker", 441 | "439": "bearskin, busby, shako", 442 | "440": "beer bottle", 443 | "441": "beer glass", 444 | "442": "bell cote, bell cot", 445 | "443": "bib", 446 | "444": "bicycle-built-for-two, tandem bicycle, tandem", 447 | "445": "bikini, two-piece", 448 | "446": "binder, ring-binder", 449 | "447": "binoculars, field glasses, opera glasses", 450 | "448": "birdhouse", 451 | "449": "boathouse", 452 | "450": "bobsled, bobsleigh, bob", 453 | "451": "bolo tie, bolo, bola tie, bola", 454 | "452": "bonnet, poke bonnet", 455 | "453": "bookcase", 456 | "454": "bookshop, bookstore, bookstall", 457 | "455": "bottlecap", 458 | "456": "bow", 459 | "457": "bow tie, bow-tie, bowtie", 460 | "458": "brass, memorial tablet, plaque", 461 | "459": "brassiere, bra, bandeau", 462 | "460": "breakwater, groin, groyne, mole, bulwark, seawall, jetty", 463 | "461": "breastplate, aegis, egis", 464 | "462": "broom", 465 | "463": "bucket, pail", 466 | "464": "buckle", 467 | "465": "bulletproof vest", 468 | "466": "bullet train, bullet", 469 | "467": "butcher shop, meat market", 470 | "468": "cab, hack, taxi, taxicab", 471 | "469": "caldron, cauldron", 472 | "470": "candle, taper, wax light", 473 | "471": "cannon", 474 | "472": "canoe", 475 | "473": "can opener, tin opener", 476 | "474": "cardigan", 477 | "475": "car mirror", 478 | "476": "carousel, carrousel, merry-go-round, roundabout, whirligig", 479 | "477": "carpenter's kit, tool kit", 480 | "478": "carton", 481 | "479": "car wheel", 482 | "480": "cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM", 483 | "481": "cassette", 484 | "482": "cassette player", 485 | "483": "castle", 486 | "484": "catamaran", 487 | "485": "CD player", 488 | "486": "cello, violoncello", 489 | "487": "cellular telephone, cellular phone, cellphone, cell, mobile phone", 490 | "488": "chain", 491 | "489": "chainlink fence", 492 | "490": "chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour", 493 | "491": "chain saw, chainsaw", 494 | "492": "chest", 495 | "493": "chiffonier, commode", 496 | "494": "chime, bell, gong", 497 | "495": "china cabinet, china closet", 498 | "496": "Christmas stocking", 499 | "497": "church, church building", 500 | "498": "cinema, movie theater, movie theatre, movie house, picture palace", 501 | "499": "cleaver, meat cleaver, chopper", 502 | "500": "cliff dwelling", 503 | "501": "cloak", 504 | "502": "clog, geta, patten, sabot", 505 | "503": "cocktail shaker", 506 | "504": "coffee mug", 507 | "505": "coffeepot", 508 | "506": "coil, spiral, volute, whorl, helix", 509 | "507": "combination lock", 510 | "508": "computer keyboard, keypad", 511 | "509": "confectionery, confectionary, candy store", 512 | "510": "container ship, containership, container vessel", 513 | "511": "convertible", 514 | "512": "corkscrew, bottle screw", 515 | "513": "cornet, horn, trumpet, trump", 516 | "514": "cowboy boot", 517 | "515": "cowboy hat, ten-gallon hat", 518 | "516": "cradle", 519 | "517": "crane", 520 | "518": "crash helmet", 521 | "519": "crate", 522 | "520": "crib, cot", 523 | "521": "Crock Pot", 524 | "522": "croquet ball", 525 | "523": "crutch", 526 | "524": "cuirass", 527 | "525": "dam, dike, dyke", 528 | "526": "desk", 529 | "527": "desktop computer", 530 | "528": "dial telephone, dial phone", 531 | "529": "diaper, nappy, napkin", 532 | "530": "digital clock", 533 | "531": "digital watch", 534 | "532": "dining table, board", 535 | "533": "dishrag, dishcloth", 536 | "534": "dishwasher, dish washer, dishwashing machine", 537 | "535": "disk brake, disc brake", 538 | "536": "dock, dockage, docking facility", 539 | "537": "dogsled, dog sled, dog sleigh", 540 | "538": "dome", 541 | "539": "doormat, welcome mat", 542 | "540": "drilling platform, offshore rig", 543 | "541": "drum, membranophone, tympan", 544 | "542": "drumstick", 545 | "543": "dumbbell", 546 | "544": "Dutch oven", 547 | "545": "electric fan, blower", 548 | "546": "electric guitar", 549 | "547": "electric locomotive", 550 | "548": "entertainment center", 551 | "549": "envelope", 552 | "550": "espresso maker", 553 | "551": "face powder", 554 | "552": "feather boa, boa", 555 | "553": "file, file cabinet, filing cabinet", 556 | "554": "fireboat", 557 | "555": "fire engine, fire truck", 558 | "556": "fire screen, fireguard", 559 | "557": "flagpole, flagstaff", 560 | "558": "flute, transverse flute", 561 | "559": "folding chair", 562 | "560": "football helmet", 563 | "561": "forklift", 564 | "562": "fountain", 565 | "563": "fountain pen", 566 | "564": "four-poster", 567 | "565": "freight car", 568 | "566": "French horn, horn", 569 | "567": "frying pan, frypan, skillet", 570 | "568": "fur coat", 571 | "569": "garbage truck, dustcart", 572 | "570": "gasmask, respirator, gas helmet", 573 | "571": "gas pump, gasoline pump, petrol pump, island dispenser", 574 | "572": "goblet", 575 | "573": "go-kart", 576 | "574": "golf ball", 577 | "575": "golfcart, golf cart", 578 | "576": "gondola", 579 | "577": "gong, tam-tam", 580 | "578": "gown", 581 | "579": "grand piano, grand", 582 | "580": "greenhouse, nursery, glasshouse", 583 | "581": "grille, radiator grille", 584 | "582": "grocery store, grocery, food market, market", 585 | "583": "guillotine", 586 | "584": "hair slide", 587 | "585": "hair spray", 588 | "586": "half track", 589 | "587": "hammer", 590 | "588": "hamper", 591 | "589": "hand blower, blow dryer, blow drier, hair dryer, hair drier", 592 | "590": "hand-held computer, hand-held microcomputer", 593 | "591": "handkerchief, hankie, hanky, hankey", 594 | "592": "hard disc, hard disk, fixed disk", 595 | "593": "harmonica, mouth organ, harp, mouth harp", 596 | "594": "harp", 597 | "595": "harvester, reaper", 598 | "596": "hatchet", 599 | "597": "holster", 600 | "598": "home theater, home theatre", 601 | "599": "honeycomb", 602 | "600": "hook, claw", 603 | "601": "hoopskirt, crinoline", 604 | "602": "horizontal bar, high bar", 605 | "603": "horse cart, horse-cart", 606 | "604": "hourglass", 607 | "605": "iPod", 608 | "606": "iron, smoothing iron", 609 | "607": "jack-o'-lantern", 610 | "608": "jean, blue jean, denim", 611 | "609": "jeep, landrover", 612 | "610": "jersey, T-shirt, tee shirt", 613 | "611": "jigsaw puzzle", 614 | "612": "jinrikisha, ricksha, rickshaw", 615 | "613": "joystick", 616 | "614": "kimono", 617 | "615": "knee pad", 618 | "616": "knot", 619 | "617": "lab coat, laboratory coat", 620 | "618": "ladle", 621 | "619": "lampshade, lamp shade", 622 | "620": "laptop, laptop computer", 623 | "621": "lawn mower, mower", 624 | "622": "lens cap, lens cover", 625 | "623": "letter opener, paper knife, paperknife", 626 | "624": "library", 627 | "625": "lifeboat", 628 | "626": "lighter, light, igniter, ignitor", 629 | "627": "limousine, limo", 630 | "628": "liner, ocean liner", 631 | "629": "lipstick, lip rouge", 632 | "630": "Loafer", 633 | "631": "lotion", 634 | "632": "loudspeaker, speaker, speaker unit, loudspeaker system, speaker system", 635 | "633": "loupe, jeweler's loupe", 636 | "634": "lumbermill, sawmill", 637 | "635": "magnetic compass", 638 | "636": "mailbag, postbag", 639 | "637": "mailbox, letter box", 640 | "638": "maillot", 641 | "639": "maillot, tank suit", 642 | "640": "manhole cover", 643 | "641": "maraca", 644 | "642": "marimba, xylophone", 645 | "643": "mask", 646 | "644": "matchstick", 647 | "645": "maypole", 648 | "646": "maze, labyrinth", 649 | "647": "measuring cup", 650 | "648": "medicine chest, medicine cabinet", 651 | "649": "megalith, megalithic structure", 652 | "650": "microphone, mike", 653 | "651": "microwave, microwave oven", 654 | "652": "military uniform", 655 | "653": "milk can", 656 | "654": "minibus", 657 | "655": "miniskirt, mini", 658 | "656": "minivan", 659 | "657": "missile", 660 | "658": "mitten", 661 | "659": "mixing bowl", 662 | "660": "mobile home, manufactured home", 663 | "661": "Model T", 664 | "662": "modem", 665 | "663": "monastery", 666 | "664": "monitor", 667 | "665": "moped", 668 | "666": "mortar", 669 | "667": "mortarboard", 670 | "668": "mosque", 671 | "669": "mosquito net", 672 | "670": "motor scooter, scooter", 673 | "671": "mountain bike, all-terrain bike, off-roader", 674 | "672": "mountain tent", 675 | "673": "mouse, computer mouse", 676 | "674": "mousetrap", 677 | "675": "moving van", 678 | "676": "muzzle", 679 | "677": "nail", 680 | "678": "neck brace", 681 | "679": "necklace", 682 | "680": "nipple", 683 | "681": "notebook, notebook computer", 684 | "682": "obelisk", 685 | "683": "oboe, hautboy, hautbois", 686 | "684": "ocarina, sweet potato", 687 | "685": "odometer, hodometer, mileometer, milometer", 688 | "686": "oil filter", 689 | "687": "organ, pipe organ", 690 | "688": "oscilloscope, scope, cathode-ray oscilloscope, CRO", 691 | "689": "overskirt", 692 | "690": "oxcart", 693 | "691": "oxygen mask", 694 | "692": "packet", 695 | "693": "paddle, boat paddle", 696 | "694": "paddlewheel, paddle wheel", 697 | "695": "padlock", 698 | "696": "paintbrush", 699 | "697": "pajama, pyjama, pj's, jammies", 700 | "698": "palace", 701 | "699": "panpipe, pandean pipe, syrinx", 702 | "700": "paper towel", 703 | "701": "parachute, chute", 704 | "702": "parallel bars, bars", 705 | "703": "park bench", 706 | "704": "parking meter", 707 | "705": "passenger car, coach, carriage", 708 | "706": "patio, terrace", 709 | "707": "pay-phone, pay-station", 710 | "708": "pedestal, plinth, footstall", 711 | "709": "pencil box, pencil case", 712 | "710": "pencil sharpener", 713 | "711": "perfume, essence", 714 | "712": "Petri dish", 715 | "713": "photocopier", 716 | "714": "pick, plectrum, plectron", 717 | "715": "pickelhaube", 718 | "716": "picket fence, paling", 719 | "717": "pickup, pickup truck", 720 | "718": "pier", 721 | "719": "piggy bank, penny bank", 722 | "720": "pill bottle", 723 | "721": "pillow", 724 | "722": "ping-pong ball", 725 | "723": "pinwheel", 726 | "724": "pirate, pirate ship", 727 | "725": "pitcher, ewer", 728 | "726": "plane, carpenter's plane, woodworking plane", 729 | "727": "planetarium", 730 | "728": "plastic bag", 731 | "729": "plate rack", 732 | "730": "plow, plough", 733 | "731": "plunger, plumber's helper", 734 | "732": "Polaroid camera, Polaroid Land camera", 735 | "733": "pole", 736 | "734": "police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria", 737 | "735": "poncho", 738 | "736": "pool table, billiard table, snooker table", 739 | "737": "pop bottle, soda bottle", 740 | "738": "pot, flowerpot", 741 | "739": "potter's wheel", 742 | "740": "power drill", 743 | "741": "prayer rug, prayer mat", 744 | "742": "printer", 745 | "743": "prison, prison house", 746 | "744": "projectile, missile", 747 | "745": "projector", 748 | "746": "puck, hockey puck", 749 | "747": "punching bag, punch bag, punching ball, punchball", 750 | "748": "purse", 751 | "749": "quill, quill pen", 752 | "750": "quilt, comforter, comfort, puff", 753 | "751": "racer, race car, racing car", 754 | "752": "racket, racquet", 755 | "753": "radiator", 756 | "754": "radio, wireless", 757 | "755": "radio telescope, radio reflector", 758 | "756": "rain barrel", 759 | "757": "recreational vehicle, RV, R.V.", 760 | "758": "reel", 761 | "759": "reflex camera", 762 | "760": "refrigerator, icebox", 763 | "761": "remote control, remote", 764 | "762": "restaurant, eating house, eating place, eatery", 765 | "763": "revolver, six-gun, six-shooter", 766 | "764": "rifle", 767 | "765": "rocking chair, rocker", 768 | "766": "rotisserie", 769 | "767": "rubber eraser, rubber, pencil eraser", 770 | "768": "rugby ball", 771 | "769": "rule, ruler", 772 | "770": "running shoe", 773 | "771": "safe", 774 | "772": "safety pin", 775 | "773": "saltshaker, salt shaker", 776 | "774": "sandal", 777 | "775": "sarong", 778 | "776": "sax, saxophone", 779 | "777": "scabbard", 780 | "778": "scale, weighing machine", 781 | "779": "school bus", 782 | "780": "schooner", 783 | "781": "scoreboard", 784 | "782": "screen, CRT screen", 785 | "783": "screw", 786 | "784": "screwdriver", 787 | "785": "seat belt, seatbelt", 788 | "786": "sewing machine", 789 | "787": "shield, buckler", 790 | "788": "shoe shop, shoe-shop, shoe store", 791 | "789": "shoji", 792 | "790": "shopping basket", 793 | "791": "shopping cart", 794 | "792": "shovel", 795 | "793": "shower cap", 796 | "794": "shower curtain", 797 | "795": "ski", 798 | "796": "ski mask", 799 | "797": "sleeping bag", 800 | "798": "slide rule, slipstick", 801 | "799": "sliding door", 802 | "800": "slot, one-armed bandit", 803 | "801": "snorkel", 804 | "802": "snowmobile", 805 | "803": "snowplow, snowplough", 806 | "804": "soap dispenser", 807 | "805": "soccer ball", 808 | "806": "sock", 809 | "807": "solar dish, solar collector, solar furnace", 810 | "808": "sombrero", 811 | "809": "soup bowl", 812 | "810": "space bar", 813 | "811": "space heater", 814 | "812": "space shuttle", 815 | "813": "spatula", 816 | "814": "speedboat", 817 | "815": "spider web, spider's web", 818 | "816": "spindle", 819 | "817": "sports car, sport car", 820 | "818": "spotlight, spot", 821 | "819": "stage", 822 | "820": "steam locomotive", 823 | "821": "steel arch bridge", 824 | "822": "steel drum", 825 | "823": "stethoscope", 826 | "824": "stole", 827 | "825": "stone wall", 828 | "826": "stopwatch, stop watch", 829 | "827": "stove", 830 | "828": "strainer", 831 | "829": "streetcar, tram, tramcar, trolley, trolley car", 832 | "830": "stretcher", 833 | "831": "studio couch, day bed", 834 | "832": "stupa, tope", 835 | "833": "submarine, pigboat, sub, U-boat", 836 | "834": "suit, suit of clothes", 837 | "835": "sundial", 838 | "836": "sunglass", 839 | "837": "sunglasses, dark glasses, shades", 840 | "838": "sunscreen, sunblock, sun blocker", 841 | "839": "suspension bridge", 842 | "840": "swab, swob, mop", 843 | "841": "sweatshirt", 844 | "842": "swimming trunks, bathing trunks", 845 | "843": "swing", 846 | "844": "switch, electric switch, electrical switch", 847 | "845": "syringe", 848 | "846": "table lamp", 849 | "847": "tank, army tank, armored combat vehicle, armoured combat vehicle", 850 | "848": "tape player", 851 | "849": "teapot", 852 | "850": "teddy, teddy bear", 853 | "851": "television, television system", 854 | "852": "tennis ball", 855 | "853": "thatch, thatched roof", 856 | "854": "theater curtain, theatre curtain", 857 | "855": "thimble", 858 | "856": "thresher, thrasher, threshing machine", 859 | "857": "throne", 860 | "858": "tile roof", 861 | "859": "toaster", 862 | "860": "tobacco shop, tobacconist shop, tobacconist", 863 | "861": "toilet seat", 864 | "862": "torch", 865 | "863": "totem pole", 866 | "864": "tow truck, tow car, wrecker", 867 | "865": "toyshop", 868 | "866": "tractor", 869 | "867": "trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi", 870 | "868": "tray", 871 | "869": "trench coat", 872 | "870": "tricycle, trike, velocipede", 873 | "871": "trimaran", 874 | "872": "tripod", 875 | "873": "triumphal arch", 876 | "874": "trolleybus, trolley coach, trackless trolley", 877 | "875": "trombone", 878 | "876": "tub, vat", 879 | "877": "turnstile", 880 | "878": "typewriter keyboard", 881 | "879": "umbrella", 882 | "880": "unicycle, monocycle", 883 | "881": "upright, upright piano", 884 | "882": "vacuum, vacuum cleaner", 885 | "883": "vase", 886 | "884": "vault", 887 | "885": "velvet", 888 | "886": "vending machine", 889 | "887": "vestment", 890 | "888": "viaduct", 891 | "889": "violin, fiddle", 892 | "890": "volleyball", 893 | "891": "waffle iron", 894 | "892": "wall clock", 895 | "893": "wallet, billfold, notecase, pocketbook", 896 | "894": "wardrobe, closet, press", 897 | "895": "warplane, military plane", 898 | "896": "washbasin, handbasin, washbowl, lavabo, wash-hand basin", 899 | "897": "washer, automatic washer, washing machine", 900 | "898": "water bottle", 901 | "899": "water jug", 902 | "900": "water tower", 903 | "901": "whiskey jug", 904 | "902": "whistle", 905 | "903": "wig", 906 | "904": "window screen", 907 | "905": "window shade", 908 | "906": "Windsor tie", 909 | "907": "wine bottle", 910 | "908": "wing", 911 | "909": "wok", 912 | "910": "wooden spoon", 913 | "911": "wool, woolen, woollen", 914 | "912": "worm fence, snake fence, snake-rail fence, Virginia fence", 915 | "913": "wreck", 916 | "914": "yawl", 917 | "915": "yurt", 918 | "916": "web site, website, internet site, site", 919 | "917": "comic book", 920 | "918": "crossword puzzle, crossword", 921 | "919": "street sign", 922 | "920": "traffic light, traffic signal, stoplight", 923 | "921": "book jacket, dust cover, dust jacket, dust wrapper", 924 | "922": "menu", 925 | "923": "plate", 926 | "924": "guacamole", 927 | "925": "consomme", 928 | "926": "hot pot, hotpot", 929 | "927": "trifle", 930 | "928": "ice cream, icecream", 931 | "929": "ice lolly, lolly, lollipop, popsicle", 932 | "930": "French loaf", 933 | "931": "bagel, beigel", 934 | "932": "pretzel", 935 | "933": "cheeseburger", 936 | "934": "hotdog, hot dog, red hot", 937 | "935": "mashed potato", 938 | "936": "head cabbage", 939 | "937": "broccoli", 940 | "938": "cauliflower", 941 | "939": "zucchini, courgette", 942 | "940": "spaghetti squash", 943 | "941": "acorn squash", 944 | "942": "butternut squash", 945 | "943": "cucumber, cuke", 946 | "944": "artichoke, globe artichoke", 947 | "945": "bell pepper", 948 | "946": "cardoon", 949 | "947": "mushroom", 950 | "948": "Granny Smith", 951 | "949": "strawberry", 952 | "950": "orange", 953 | "951": "lemon", 954 | "952": "fig", 955 | "953": "pineapple, ananas", 956 | "954": "banana", 957 | "955": "jackfruit, jak, jack", 958 | "956": "custard apple", 959 | "957": "pomegranate", 960 | "958": "hay", 961 | "959": "carbonara", 962 | "960": "chocolate sauce, chocolate syrup", 963 | "961": "dough", 964 | "962": "meat loaf, meatloaf", 965 | "963": "pizza, pizza pie", 966 | "964": "potpie", 967 | "965": "burrito", 968 | "966": "red wine", 969 | "967": "espresso", 970 | "968": "cup", 971 | "969": "eggnog", 972 | "970": "alp", 973 | "971": "bubble", 974 | "972": "cliff, drop, drop-off", 975 | "973": "coral reef", 976 | "974": "geyser", 977 | "975": "lakeside, lakeshore", 978 | "976": "promontory, headland, head, foreland", 979 | "977": "sandbar, sand bar", 980 | "978": "seashore, coast, seacoast, sea-coast", 981 | "979": "valley, vale", 982 | "980": "volcano", 983 | "981": "ballplayer, baseball player", 984 | "982": "groom, bridegroom", 985 | "983": "scuba diver", 986 | "984": "rapeseed", 987 | "985": "daisy", 988 | "986": "yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum", 989 | "987": "corn", 990 | "988": "acorn", 991 | "989": "hip, rose hip, rosehip", 992 | "990": "buckeye, horse chestnut, conker", 993 | "991": "coral fungus", 994 | "992": "agaric", 995 | "993": "gyromitra", 996 | "994": "stinkhorn, carrion fungus", 997 | "995": "earthstar", 998 | "996": "hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa", 999 | "997": "bolete", 1000 | "998": "ear, spike, capitulum", 1001 | "999": "toilet tissue, toilet paper, bathroom tissue" 1002 | } --------------------------------------------------------------------------------