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

OpenCV basics

opencv_basics.livemd

OpenCV basics

Mix.install([
  {:evision, "0.1.30"},
  {:kino, "~> 0.7.0"},
  {:exla, "~> 0.4.0"},
  {:req, "~> 0.3.0"}
])

Resources

Evision

  • OpenCVのElixirラッパー
  • Nxのバックエンドとして使用でき、行列演算の高速化(CPU,GPU)ができる
  • 画像処理の関数を使用できる
alias Evision, as: Cv

Download data files

downloads_dir = System.tmp_dir!()

download = fn url ->
  save_as = Path.join(downloads_dir, URI.encode_www_form(url))
  unless File.exists?(save_as), do: Req.get!(url, output: save_as)
  save_as
end

data_files =
  [
    # an image that is to be used as input
    input_image: "https://github.com/livebook-dev/livebook/raw/main/static/images/logo.png"
  ]
  |> Enum.map(fn {key, url} -> {key, download.(url)} end)
  |> Map.new()
Nx.tensor([255, 0, 0], type: :u8)
|> Nx.broadcast({800, 400, 3})
|> Cv.Mat.from_nx_2d()

Mat.zerosで黒色画像の作成

  • 第1引数:行列サイズ
  • 第2引数:型
color = {255, 255, 255}
font = Cv.Constant.cv_FONT_HERSHEY_SIMPLEX()

# 第一引数で指定したサイズで0で埋めた行列を作成
Cv.Mat.zeros({200, 200}, :u8)
|> Cv.putText("hello", {16, 48}, font, 1, color, thickness: 2)
|> Cv.circle({50, 40}, 30, color)
# RGBの値で作ってMatで表示
Cv.Mat.zeros({100, 100, 3}, :u8)
|> Cv.Mat.to_nx()
|> Cv.Mat.from_nx_2d()
|> dbg()

:ok

Mat.fullでグレー画像の作成

  • 第1引数:行列サイズ
  • 第2引数:値
  • 第3引数:型
Cv.Mat.full({100, 100}, 200, :u8)

Mat.from_nx_2dでNxから色画像の作成

shape = {250, 250, 3}

red =
  Nx.tensor([0, 0, 255], type: :u8)
  |> Nx.broadcast(shape)
  |> Cv.Mat.from_nx_2d()

green =
  Nx.tensor([0, 255, 0], type: :u8)
  |> Nx.broadcast(shape)
  |> Cv.Mat.from_nx_2d()

blue =
  Nx.tensor([255, 0, 0], type: :u8)
  |> Nx.broadcast(shape)
  |> Cv.Mat.from_nx_2d()

mat_to_img = &Kino.Image.new(Cv.imencode(".png", &1), :png)

[red, green, blue]
|> Enum.map(mat_to_img)
|> Kino.Layout.grid(columns: 3)

imreadで画像の読み込み

img = Cv.imread(data_files.input_image)

imwriteでファイル書き込み

# write the image as PNG
png_path = Path.join(System.tmp_dir!(), "opencv_basics_img" <> ".png")
Cv.imwrite(png_path, img)
png_path
# write the image as JPG
jpg_path = Path.join(System.tmp_dir!(), "opencv_basics_img" <> ".jpg")
Cv.imwrite(jpg_path, img)
jpg_path
# error without on ext
{:error, _} = Cv.imwrite("images/logo", img)

resize

{w, h} = {300, 300}
img = Cv.resize(img, {w, h})

Crop and resize

img[[{10, 90}, {40, 120}]]
|> Cv.resize({200, 200})

rotate

Cv.rotate(img, Cv.Constant.cv_ROTATE_90_CLOCKWISE())
Cv.rotate(img, Cv.Constant.cv_ROTATE_90_COUNTERCLOCKWISE())
Cv.rotate(img, Cv.Constant.cv_ROTATE_180())

flip

# 縦方向に反転
Cv.flip(img, 0)
# 横方向に反転
Cv.flip(img, 1)

hconcatで横連結

Cv.hconcat([img, img])

vconcatで縦連結

Cv.vconcat([img, img])

rectangle

{x, y, w, h} = {40, 5, 80, 80}
start_point = {x, y}
end_point = {x + w, y + h}
color = {255, 255, 255}
options = [thickness: 5]

Cv.rectangle(img, start_point, end_point, color, options)
{x, y, w, h} = {40, 5, 80, 80}
start_point = {x, y}
end_point = {x + w, y + h}
color = {255, 255, 255}
options = [thickness: -1]

Cv.rectangle(img, start_point, end_point, color, options)

circle

center = {100, 100}
diameter = 40
color = {255, 255, 255}
options = [thickness: 3]

Cv.circle(img, center, diameter, color, options)
{ox, oy} = {100, 100}
{h, w} = {50, 100}
rotation = -30
start_point = 0
end_point = 360
color = {255, 255, 255}
options = [thickness: 1]

Cv.ellipse(img, {ox, oy}, {w, h}, rotation, start_point, end_point, color, options)
{ox, oy} = {100, 100}
{h, w} = {50, 50}
rotation = 90
start_point = 0
end_point = 180
color = {255, 255, 255}
options = [thickness: -1]

Cv.ellipse(img, {ox, oy}, {w, h}, rotation, start_point, end_point, color, options)[
  [0..40, 0..20]
]

putText

text = "LiveViewJP"
position = {20, 120}
options = [thickness: 2]

Cv.putText(img, text, position, Cv.Constant.cv_FONT_ITALIC(), 1.0, color, options)

Mat.roiで切り取り

{x, y, w, h} = {40, 10, 50, 80}
Cv.Mat.roi(img, {x, y, x + w, y + h})

warpAffineでアフィン変換

並行移動

{w, h} = {200, 200}
{x, y} = {50, -10}

m_shift = Nx.tensor([[1, 0, x], [0, 1, y]], type: :f32) |> Cv.Mat.from_nx()
sheer_img = Cv.warpAffine(img, m_shift, {w, h})

回転

{w, h} = {200, 200}
center = {w / 2, h / 2}
angle = 45
scale = 1.0

m_rotate = Cv.getRotationMatrix2D(center, angle, scale)
rotation_img = Cv.warpAffine(img, m_rotate, {w, h})

スキュー(平行四辺形に変形する処理)

###

{w, h} = {200, 200}
{a, b} = {0.2, 0.0}

m_shear = Nx.tensor([[1, a, 0], [b, 1, 0]], type: :f32) |> Cv.Mat.from_nx()
shear_img = Cv.warpAffine(img, m_shear, {w, h})