Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Evision.DNN Example - Benchmark using Densenet121

examples/densenet121_benchmark.livemd

Evision.DNN Example - Benchmark using Densenet121

Mix.install([
  {:evision, "~> 0.2"},
  {:kino, "~> 0.7"},
  {:req, "~> 0.5"}
], system_env: [
  # optional, defaults to `true`
  # set `EVISION_PREFER_PRECOMPILED` to `false`
  # if you prefer `:evision` to be compiled from source
  # note that to compile from source, you may need at least 1GB RAM
  {"EVISION_PREFER_PRECOMPILED", true},

  # optional, defaults to `true`
  # set `EVISION_ENABLE_CONTRIB` to `false`
  # if you don't need modules from `opencv_contrib`
  {"EVISION_ENABLE_CONTRIB", true},

  # optional, defaults to `false`
  # set `EVISION_ENABLE_CUDA` to `true`
  # if you wish to use CUDA related functions
  # note that `EVISION_ENABLE_CONTRIB` also has to be `true`
  # because cuda related modules come from the `opencv_contrib` repo
  {"EVISION_ENABLE_CUDA", false},

  # required when
  # - `EVISION_ENABLE_CUDA` is `true`
  # - and `EVISION_PREFER_PRECOMPILED` is `true`
  #
  # set `EVISION_CUDA_VERSION` to the version that matches
  # your local CUDA runtime version
  #
  # current available versions are
  # - 118
  # - 121
  {"EVISION_CUDA_VERSION", "118"},

  # require for Windows users when
  # - `EVISION_ENABLE_CUDA` is `true`
  # set `EVISION_CUDA_RUNTIME_DIR` to the directory that contains
  # CUDA runtime libraries
  {"EVISION_CUDA_RUNTIME_DIR", "C:/PATH/TO/CUDA/RUNTIME"}
])

Define Some Helper Functions

defmodule Helper do
  def download!(url, save_as, overwrite? \\ false) do
    unless File.exists?(save_as) do
      Req.get!(url, http_errors: :raise, into: File.stream!(save_as), cache: not overwrite?)
    end

    :ok
  end
end

Write the Benchmark Module

alias Evision, as: Cv

defmodule DetectionModel do
  def benchmark(times, mat, model, out_names, opts \\ []) do
    mean = opts[:mean] || [0, 0, 0]
    scalefactor = opts[:scalefactor] || 1.0

    for _ <- 1..times, reduce: {[], []} do
      {forward_only, all} ->
        start_time_1 = :os.system_time(:millisecond)
        blob = Cv.DNN.blobFromImage(mat, opts)
        model = Cv.DNN.Net.setInput(model, blob, name: "", scalefactor: scalefactor, mean: mean)

        start_time_2 = :os.system_time(:millisecond)
        _detections = Cv.DNN.Net.forward(model, outBlobNames: out_names)
        end_time = :os.system_time(:millisecond)

        inference_time_1 = end_time - start_time_1
        inference_time_2 = end_time - start_time_2
        IO.puts("Inference time=>#{inference_time_2} ms")
        {[inference_time_2 | forward_only], [inference_time_1 | all]}
    end
  end

  def get_model(filename) do
    net = Cv.DNN.readNetFromONNX(filename)
    out_names = Cv.DNN.Net.getUnconnectedOutLayersNames(net)
    {:ok, net, out_names}
  end
end

Write the DenseNet121 Module

defmodule DenseNet121 do
  defp download_model(opset_version) when opset_version in [3, 6, 7, 8, 9, 12] do
    onnx_filename = "densenet-#{opset_version}.onnx"
    test_filename = "CyprusShorthair.jpg"

    Helper.download!(
      "https://github.com/onnx/models/raw/main/vision/classification/densenet-121/model/densenet-#{opset_version}.onnx",
      onnx_filename
    )

    Helper.download!(
      "https://upload.wikimedia.org/wikipedia/commons/b/b9/CyprusShorthair.jpg",
      "CyprusShorthair.jpg"
    )

    {onnx_filename, test_filename}
  end

  def get_detection_model(opset_version \\ 12) do
    {onnx_filename, test_filename} = download_model(opset_version)
    {:ok, net, out_names} = DetectionModel.get_model(onnx_filename)
    test_mat = Cv.imread(test_filename)
    {net, out_names, test_mat}
  end

  def benchmark(times, opset_version \\ 12) do
    {net, out_names, test_mat} = get_detection_model(opset_version)

    DetectionModel.benchmark(times, test_mat, net, out_names,
      scalefactor: 1,
      swapRB: true,
      mean: {128, 128, 128},
      size: {224, 224}
    )
  end
end

Load and Run

{forward_only, all} = DenseNet121.benchmark(50)
avg_forward_only = Enum.sum(forward_only) / Enum.count(forward_only)
avg_all = Enum.sum(all) / Enum.count(all)
{avg_forward_only, avg_all}