Evision.ML.SVM Example - Support Vector Machine
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"}
])
: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 ->
<> =
: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(
<>,
{:f, 32},
1,
2,
1
)
{_, %Cv.Mat{shape: {1, 1}} = response_mat} = Cv.ML.SVM.predict(svm, sample)
<> = 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)