View Source Evision.ML.SVM Example - Support Vector Machine

Mix.install([
  {:evision, "~> 0.2"},
  {:kino, "~> 0.7"},
  {:req, "~> 0.3"}
], 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"}
])
:ok

Set Up Training Data

This example is based on the Introduction to Support Vector Machines from Cv.

alias Evision, as: Cv

labels = [1, -1, -1, -1]
training_data = [[501, 10], [255, 10], [501, 255], [10, 501]]

labels_mat = Cv.Mat.literal(labels, :s32)
training_data_mat = Cv.Mat.literal(training_data, :f32)
%Evision.Mat{
  channels: 1,
  dims: 2,
  type: {:f, 32},
  raw_type: 5,
  shape: {4, 2},
  ref: #Reference<0.1481828880.2785148948.120970>
}

Create an SVM and Train It With the Data

svm = Cv.ML.SVM.create()
svm = Cv.ML.SVM.setType(svm, Cv.Constant.cv_C_SVC())
svm = Cv.ML.SVM.setKernel(svm, Cv.Constant.cv_LINEAR())
svm = Cv.ML.SVM.setTermCriteria(svm, {Cv.Constant.cv_MAX_ITER(), 100, 0.000001})
true = Cv.ML.SVM.train(svm, training_data_mat, Cv.Constant.cv_ROW_SAMPLE(), labels_mat)
true = Cv.ML.SVM.isTrained(svm)
true

Get Support Vectors

%Evision.Mat{shape: {rows, cols}} = sv = Cv.ML.SVM.getUncompressedSupportVectors(svm)
sv_binary = Cv.Mat.to_binary(sv)
float_bytes = 4

support_vector =
  for i <- (rows - 1)..0, reduce: [] do
    support_vector ->
      current_vector =
        for j <- (cols - 1)..0, reduce: [] do
          vec ->
            <<float_data::float-size(32)-little>> =
              :binary.part(sv_binary, (i * cols + j) * float_bytes, 4)

            [trunc(float_data) | vec]
        end

      [current_vector | support_vector]
  end

[[501, 10], [255, 10], [501, 255]] = support_vector
support_vector
[[501, 10], [255, 10], [501, 255]]

Visualise the Training Result in A Response Map

green = [0, 255, 0]
blue = [255, 0, 0]
width = 512
height = 512

response_data =
  for x <- (width - 1)..0, y <- (height - 1)..0, reduce: [] do
    acc ->
      sample =
        Cv.Mat.from_binary(
          <<y::float-size(32)-little, x::float-size(32)-little>>,
          {:f, 32},
          1,
          2,
          1
        )

      {_, %Cv.Mat{shape: {1, 1}} = response_mat} = Cv.ML.SVM.predict(svm, sample)
      <<response::float-size(32)-little>> = Cv.Mat.to_binary(response_mat)
      response = trunc(response)

      case response do
        1 ->
          [green | acc]

        -1 ->
          [blue | acc]
      end
  end

response_data = response_data |> List.flatten() |> IO.iodata_to_binary()
response_map = Cv.Mat.from_binary(response_data, {:u, 8}, height, width, 3)

# show the training data
thickness = 1

response_map =
  Cv.circle(response_map, List.to_tuple(Enum.at(training_data, 0)), 5, {0, 0, 0},
    thickness: thickness
  )

response_map =
  Cv.circle(response_map, List.to_tuple(Enum.at(training_data, 1)), 5, {255, 255, 255},
    thickness: thickness
  )

response_map =
  Cv.circle(response_map, List.to_tuple(Enum.at(training_data, 2)), 5, {255, 255, 255},
    thickness: thickness
  )

response_map =
  Cv.circle(response_map, List.to_tuple(Enum.at(training_data, 3)), 5, {255, 255, 255},
    thickness: thickness
  )

# show support vectors
response_map =
  Cv.circle(response_map, List.to_tuple(Enum.at(support_vector, 0)), 6, {128, 128, 128},
    thickness: thickness
  )

response_map =
  Cv.circle(response_map, List.to_tuple(Enum.at(support_vector, 1)), 6, {128, 128, 128},
    thickness: thickness
  )

response_map =
  Cv.circle(response_map, List.to_tuple(Enum.at(support_vector, 2)), 6, {128, 128, 128},
    thickness: thickness
  )

Cv.imencode(".png", response_map)
|> Kino.Image.new(:png)