View Source Find and Draw Contours in an Image

# 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
# System.put_env("EVISION_PREFER_PRECOMPILED", "false")

Mix.install([
  {:evision, "~> 0.1.21"},
  {:kino, "~> 0.7"},
  {:req, "~> 0.3"}
])
:ok

helper-function

Helper Function

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

    :ok
  end
end
{:module, Helper, <<70, 79, 82, 49, 0, 0, 10, ...>>, {:download!, 3}}
Helper.download!("https://docs.opencv.org/4.x/pca_test1.jpg", "pca_test.jpg")
:ok

load-image-as-gray-scale

Load Image As Gray Scale

gray = Evision.imread("pca_test.jpg", flags: Evision.cv_IMREAD_GRAYSCALE())
%Evision.Mat{
  channels: 1,
  dims: 2,
  type: {:u, 8},
  raw_type: 0,
  shape: {600, 800},
  ref: #Reference<0.2172638371.2268463131.71223>
}

get-a-binary-image

Get A Binary Image

# import Bitwise so that we can use `|||` (bitwise or)
import Bitwise

# binarization
{_, bw} =
  Evision.threshold(gray, 50, 255, Evision.cv_THRESH_BINARY() ||| Evision.cv_THRESH_OTSU())

bw
%Evision.Mat{
  channels: 1,
  dims: 2,
  type: {:u, 8},
  raw_type: 0,
  shape: {600, 800},
  ref: #Reference<0.2172638371.2268463122.70922>
}

find-all-contours-in-the-binary-image

Find All Contours in the Binary Image

# Find all the contours in the thresholded image
{contours, _} = Evision.findContours(bw, Evision.cv_RETR_LIST(), Evision.cv_CHAIN_APPROX_NONE())

IO.puts("Find #{Enum.count(contours)} contour(s)")
Find 7 contour(s)
:ok

ignore-contours-that-are-too-small-or-too-large

Ignore Contours That Are Too Small or Too Large

minimal_area = 100
maximal_area = 100_000

contours =
  Enum.reject(contours, fn c ->
    # Calculate the area of each contour
    area = Evision.contourArea(c)
    # Ignore contours that are too small or too large
    # (return true to reject)
    area < minimal_area or area > maximal_area
  end)

IO.puts("#{Enum.count(contours)} contour(s) remains")
6 contour(s) remains
:ok

draw-all-contours

Draw All Contours

# color in {Blue, Green, Red}, range from 0-255
edge_color = {0, 0, 255}

# draw all contours by setting `index` to `-1`
index = -1

# Load image in color
src = Evision.imread("pca_test.jpg")

# draw all contours on the color image
Evision.drawContours(src, contours, index, edge_color, thickness: 2)
%Evision.Mat{
  channels: 3,
  dims: 2,
  type: {:u, 8},
  raw_type: 16,
  shape: {600, 800, 3},
  ref: #Reference<0.2172638371.2268463122.70944>
}