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

Face Alignment: 2D106det

demo_2d106det/FaceAlign.livemd

Face Alignment: 2D106det

File.cd!(__DIR__)
# for windows JP
System.shell("chcp 65001")

Mix.install([
  {:onnx_interp, path: ".."},
  {:cimg, "~> 0.1.17"},
  {:nx, "~> 0.4.1"},
  {:kino, "~> 0.8.0"}
])

0.Original work

1.Implementation with OnnxInterp in Elixir

defmodule FaceAlign do
  @width 192
  @height 192

  alias OnnxInterp, as: NNInterp

  use NNInterp,
    model: "model/2d106det.onnx",
    inputs: [f32: {1, 3, @height, @width}],
    outputs: [f32: {1, 212}]

  def apply(img) do
    # preprocess
    input0 =
      img
      |> CImg.resize({@width, @height})
      |> CImg.to_binary([{:range, {0.0, 255.0}}, :nchw])

    # prediction
    output0 =
      session()
      |> NNInterp.set_input_tensor(0, input0)
      |> NNInterp.invoke()
      |> NNInterp.get_output_tensor(0)
      |> Nx.from_binary(:f32)
      |> Nx.reshape({:auto, 2})

    # postprocess
    landmark =
      output0
      |> Nx.add(Nx.tensor([1.0, 1.0]))
      |> Nx.divide(2.0)
      |> Nx.to_flat_list()
      |> Enum.chunk_every(2)

    {:ok, landmark}
  end
end

Launch FaceAlign.

FaceAlign.start_link([])

2.Let’s try it

defmodule DemoFaceAlign do
  def run(path) do
    img = CImg.load(path)

    [
      img,
      with {:ok, res} = FaceAlign.apply(img) do
        draw_item(res, CImg.builder(img), :red)
      end
    ]
  end

  defp draw_item(landmark, canvas, color) do
    Enum.reduce(landmark, canvas, fn [x, y], canvas ->
      CImg.draw_marker(canvas, x, y, color, size: 2)
    end)
  end
end
DemoFaceAlign.run("face.jpg")
|> Enum.map(&CImg.display_kino(&1, :jpeg))
|> Kino.Layout.grid(columns: 2)

3.TIL ;-)

Appendix