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

YOLO with EMLX

livebooks/ortex/yolo_with_emlx.livemd

YOLO with EMLX

Mix.install(
  [
    {:yolo, ">= 0.0.0"},
    {:yolo_fast_nms, "~> 0.1"},
    {:emlx, github: "elixir-nx/emlx"},
    {:evision, "~> 0.2.0"},
    {:kino, "~> 0.14.2"}
  ],
  config: [
    nx: [default_backend: EMLX.Backend]
  ]
)

Load YOLOv8 model

onnx_input = Kino.Input.file("ONNX")
classes_json_input = Kino.Input.file("CLASSES JSON")

Kino.Layout.grid([onnx_input, classes_json_input], columns: 2)
model = YOLO.load([
  model_path: onnx_input |> Kino.Input.read() |> Map.get(:file_ref) |> Kino.Input.file_path(), 
  classes_path: classes_json_input |> Kino.Input.read() |> Map.get(:file_ref) |> Kino.Input.file_path()
])
image_input = Kino.Input.image("IMAGE", format: :png)
image =
  image_input
  |> Kino.Input.read()
  |> Map.get(:file_ref)
  |> Kino.Input.file_path()
  |> File.read!()
mat = Evision.imdecode(image, Evision.Constant.cv_IMREAD_COLOR())

Detect objects

objects =
  model
  |> YOLO.detect(mat, nms_fun: &YoloFastNMS.run/3)
  |> YOLO.to_detected_objects(model.classes)
draw_objects = fn mat, objects ->
  objects
  |> Enum.reduce(mat, fn %{class: class, prob: prob, bbox: bbox, class_idx: class_idx}, drawed_mat ->
    %{w: w, h: h, cx: cx, cy: cy} = bbox
    left = cx - div(w, 2)
    top = cy - div(h, 2)
    right = left + w
    bottom = top + h
  
    score = round(prob * 100) |> Integer.to_string()
  
    color = {
      case rem(class_idx, 3) do
        0 -> 0
        1 -> 128
        2 -> 255
      end,
      case rem(80 - class_idx, 4) do
        0 -> 0
        1 -> 30
        2 -> 60
        3 -> 90
      end,
      case rem(40 + class_idx, 5) do
        0 -> 255
        1 -> 196
        2 -> 128
        3 -> 64
        4 -> 0
      end
    }
  
    text = class <> ":" <> score
    font = Evision.Constant.cv_FONT_HERSHEY_SIMPLEX()
    font_scale = 1
    font_thickness = 2
    {{tw, th}, _} = Evision.getTextSize(text, font, font_scale, font_thickness)
  
    drawed_mat
    |> Evision.rectangle(
      {left, top},
      {right, bottom},
      color,
      thickness: 10
    )
    |> Evision.rectangle(
      {left - 5, top - th - 10},
      {left + tw + 5, top},
      color,
      thickness: -1
    )
    |> Evision.putText(
      text,
      {left, top - 5},
      font,
      font_scale,
      {255, 255, 255},
      thickness: font_thickness
    )
  end)
end
draw_objects.(mat, objects)

Use YOLOv8x

v8x_onnx_input = Kino.Input.file("YOLOv8x ONNX")
v8x_classes_json_input = Kino.Input.file("YOLOv8x CLASSES JSON")

Kino.Layout.grid([v8x_onnx_input, v8x_classes_json_input], columns: 2)
v8x_model = YOLO.load([
  model_path: v8x_onnx_input |> Kino.Input.read() |> Map.get(:file_ref) |> Kino.Input.file_path(), 
  classes_path: v8x_classes_json_input |> Kino.Input.read() |> Map.get(:file_ref) |> Kino.Input.file_path()
])
objects =
  v8x_model
  |> YOLO.detect(mat, nms_fun: &amp;YoloFastNMS.run/3)
  |> YOLO.to_detected_objects(v8x_model.classes)
draw_objects.(mat, objects)