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

OpenCV bounding box

opencv_bounding_box.livemd

OpenCV bounding box

Mix.install([
  {:nx, "~> 0.5.0"},
  {:evision, "0.1.30"},
  {:kino, "~> 0.7.0"}
])

alias Evision, as: Cv

drawMarker

points = [
  [341.7188653945923, 43.22674134373665],
  [347.2751133441925, 38.838579922914505],
  [337.92927277088165, 38.542279705405235],
  [353.93250143527985, 44.37031477689743],
  [332.8073402643204, 43.509576231241226],
  [358.5170496702194, 66.75018179416656],
  [326.14933770895004, 69.04044127464294],
  [346.01376926898956, 90.88503938913345],
  [303.05064821243286, 94.19108891487122],
  [313.96379578113556, 87.70558965206146],
  [302.41149455308914, 88.45037072896957],
  [348.3349735736847, 134.9865512251854],
  [326.03289169073105, 134.1936908364296],
  [351.33337795734406, 197.84249007701874],
  [319.55440336465836, 196.71163403987885],
  [355.83895242214203, 247.53425693511963],
  [340.4989422559738, 243.13054251670837]
]

marker_opts = [markerSize: 5, markerType: Cv.Constant.cv_MARKER_STAR()]
marker_color = {255, 255, 255}
## horizontal rectangle

{img_height, img_width} = {338, 600}
horizontal_pts = points

# background
horizontal_rect_mat =
  Nx.tensor([0, 0, 0], type: :u8)
  |> Nx.broadcast({img_height, img_width, 3})
  |> Cv.Mat.from_nx_2d()

# draw markers
horizontal_rect_mat =
  horizontal_pts
  |> Enum.reduce(horizontal_rect_mat, fn [x, y], mat ->
    Cv.drawMarker(mat, {trunc(x), trunc(y)}, marker_color, marker_opts)
  end)
## vertical rectangle

{img_height, img_width} = {600, 338}
vertical_pts = for [x, y] <- points, do: [y, x]

# background
vertical_rect_mat =
  Nx.tensor([0, 0, 0], type: :u8)
  |> Nx.broadcast({img_height, img_width, 3})
  |> Cv.Mat.from_nx_2d()

# draw markers
vertical_rect_mat =
  vertical_pts
  |> Enum.reduce(vertical_rect_mat, fn [x, y], mat ->
    Cv.drawMarker(mat, {trunc(x), trunc(y)}, marker_color, marker_opts)
  end)

minEnclosingCircle

{{center_x, center_y}, radius} =
  horizontal_pts
  |> Nx.tensor()
  |> Cv.minEnclosingCircle()

horizontal_rect_mat
|> Cv.circle({trunc(center_x), trunc(center_y)}, ceil(radius), {0, 255, 0}, thickness: 2)

Calculate crop region with minEnclosingCircle

calc_crop_region = fn {img_height, img_width}, %Nx.Tensor{} = points ->
  {{center_x, center_y}, _radius} = Cv.minEnclosingCircle(points)

  crop_size = Enum.min([img_height, img_width])
  crop_size_half = div(crop_size, 2)

  {{x, y}, {w, h}} =
    if img_width > img_height do
      {
        {center_x - crop_size_half, 0},
        {center_x + crop_size_half, crop_size}
      }
    else
      {
        {0, center_y - crop_size_half},
        {crop_size, center_y + crop_size_half}
      }
    end

  {{trunc(x), trunc(y)}, {trunc(w), trunc(h)}}
end
## when the image is horizontal

{img_height, img_width, _} = Cv.Mat.shape(horizontal_rect_mat)

# calculate crop region
{crop_start_point, crop_end_point} =
  calc_crop_region.({img_height, img_width}, Nx.tensor(horizontal_pts))

# draw crop region
horizontal_rect_mat
|> Cv.rectangle(crop_start_point, crop_end_point, {0, 255, 0}, thickness: 3)
## when the image is vertical

{img_height, img_width, _} = Cv.Mat.shape(vertical_rect_mat)

# calculate crop region
{crop_start_point, crop_end_point} =
  calc_crop_region.({img_height, img_width}, Nx.tensor(vertical_pts))

# draw crop region
vertical_rect_mat
|> Cv.rectangle(crop_start_point, crop_end_point, {0, 255, 0}, thickness: 3)

boundingRect

## when the image is horizontal

# calculate bounding box
{x, y, w, h} =
  horizontal_pts
  |> Nx.tensor()
  |> Cv.boundingRect()

# draw bounding box
horizontal_rect_mat
# |> Cv.rectangle({x, y}, {w, h}, {0, 255, 0}, thickness: 3)
|> Cv.rectangle({x, y, w, h}, {0, 255, 0})
## when the image is vertical

# calculate bounding box
{x, y, w, h} =
  vertical_pts
  |> Nx.tensor()
  |> Cv.boundingRect()

# draw bounding box
vertical_rect_mat
# |> Cv.rectangle({x, y}, {w, h}, {0, 255, 0}, thickness: 3)
|> Cv.rectangle({x, y, w, h}, {0, 255, 0})