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

AWS Rekognition

livebooks/aws/rekognition.livemd

AWS Rekognition

Mix.install([
  {:ex_aws, "~> 2.5"},
  {:ex_aws_rekognition, "~> 0.6"},
  {:poison, "~> 5.0"},
  {:hackney, "~> 1.20"},
  {:sweet_xml, "~> 0.7"},
  {:explorer, "~> 0.9"},
  {:evision, "~> 0.2"},
  {:req, "~> 0.5"},
  {:kino, "~> 0.14"}
])

準備

alias ExAws.Rekognition
alias Explorer.DataFrame
alias Explorer.Series
require Explorer.DataFrame

認証

access_key_id_input = Kino.Input.password("ACCESS_KEY_ID")
secret_access_key_input = Kino.Input.password("SECRET_ACCESS_KEY")
region_input = Kino.Input.text("REGION")

[
  access_key_id_input,
  secret_access_key_input,
  region_input
]
|> Kino.Layout.grid(columns: 3)
auth_config = [
  access_key_id: Kino.Input.read(access_key_id_input),
  secret_access_key: Kino.Input.read(secret_access_key_input),
  region: Kino.Input.read(region_input)
]

Kino.nothing()

物体検出

lenna_mat =
  "https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png"
  |> Req.get!()
  |> Map.get(:body)
  |> Evision.imdecode(Evision.Constant.cv_IMREAD_COLOR())
rek_res =
  Evision.imencode(".png", lenna_mat)
  |> Rekognition.detect_labels()
  |> ExAws.request!(auth_config)
detections_df =
  rek_res["Labels"]
  # 位置情報がないものは除外する
  |> Enum.filter(&(Enum.count(&1["Instances"]) > 0))
  |> Enum.flat_map(fn detection ->
    detection["Instances"]
    |> Enum.map(fn instance ->
      [
        label: detection["Name"],
        left: instance["BoundingBox"]["Left"],
        top: instance["BoundingBox"]["Top"],
        width: instance["BoundingBox"]["Width"],
        height: instance["BoundingBox"]["Height"],
        score: instance["Confidence"]
      ]
    end)
  end)
  |> DataFrame.new()

Kino.DataTable.new(detections_df)
detections_df =
  DataFrame.distinct(detections_df, ["left", "top", "width", "height"], keep_all: true)

Kino.DataTable.new(detections_df)
{img_height, img_width, _} = Evision.Mat.shape(lenna_mat)
detections_df
|> DataFrame.to_rows()
|> Enum.reduce(lenna_mat, fn detection, drawed ->
  label = detection["label"]
  left = (img_width * detection["left"]) |> trunc()
  top = (img_height * detection["top"]) |> trunc()
  right = (left + img_width * detection["width"]) |> trunc()
  bottom = (top + img_height * detection["height"]) |> trunc()

  drawed
  |> Evision.rectangle(
    {left, top},
    {right, bottom},
    {255, 0, 0},
    thickness: 4
  )
  |> Evision.putText(
    label,
    {left + 6, top + 26},
    Evision.Constant.cv_FONT_HERSHEY_SIMPLEX(),
    0.8,
    {0, 0, 255},
    thickness: 2
  )
end)
show_objects = fn input_mat ->
  Evision.imencode(".jpg", input_mat)
  |> Rekognition.detect_labels()
  |> ExAws.request!(auth_config)
  |> Map.get("Labels")
  |> Enum.filter(&(Enum.count(&1["Instances"]) > 0))
  |> Enum.flat_map(fn detection ->
    detection["Instances"]
    |> Enum.map(fn instance ->
      [
        label: detection["Name"],
        left: instance["BoundingBox"]["Left"],
        top: instance["BoundingBox"]["Top"],
        width: instance["BoundingBox"]["Width"],
        height: instance["BoundingBox"]["Height"],
        score: instance["Confidence"]
      ]
    end)
  end)
  |> DataFrame.new()
  |> DataFrame.distinct(["left", "top", "width", "height"], keep_all: true)
  |> DataFrame.to_rows()
  |> Enum.reduce(input_mat, fn detection, drawed ->
    {img_height, img_width, _} = Evision.Mat.shape(input_mat)

    label = detection["label"]
    left = (img_width * detection["left"]) |> trunc()
    top = (img_height * detection["top"]) |> trunc()
    right = (left + img_width * detection["width"]) |> trunc()
    bottom = (top + img_height * detection["height"]) |> trunc()

    drawed
    |> Evision.rectangle(
      {left, top},
      {right, bottom},
      {255, 0, 0},
      thickness: 4
    )
    |> Evision.putText(
      label,
      {left + 6, top + 26},
      Evision.Constant.cv_FONT_HERSHEY_SIMPLEX(),
      0.8,
      {0, 0, 255},
      thickness: 2
    )
  end)
end
dog_mat =
  "https://raw.githubusercontent.com/pjreddie/darknet/master/data/dog.jpg"
  |> Req.get!()
  |> Map.get(:body)
  |> Evision.imdecode(Evision.Constant.cv_IMREAD_COLOR())
show_objects.(dog_mat)

顔検出

rek_res =
  Evision.imencode(".png", lenna_mat)
  |> Rekognition.detect_faces(attributes: ["ALL"])
  |> ExAws.request!(auth_config)
lenna_face =
  rek_res["FaceDetails"]
  |> Enum.at(0)

顔の位置

{img_height, img_width, _} = Evision.Mat.shape(lenna_mat)

box = lenna_face["BoundingBox"]

left = (img_width * box["Left"]) |> trunc()
top = (img_height * box["Top"]) |> trunc()
right = (left + img_width * box["Width"]) |> trunc()
bottom = (top + img_height * box["Height"]) |> trunc()

lenna_mat
|> Evision.rectangle(
  {left, top},
  {right, bottom},
  {255, 0, 0},
  thickness: 4
)

顔のパーツ

{img_height, img_width, _} = Evision.Mat.shape(lenna_mat)

lenna_face["Landmarks"]
|> Enum.reduce(lenna_mat, fn detection, drawed ->
  x = (img_width * detection["X"]) |> trunc()
  y = (img_height * detection["Y"]) |> trunc()

  drawed
  |> Evision.circle(
    {x, y},
    4,
    {255, 0, 0},
    thickness: -1
  )
end)

顔の向き

lenna_face["Pose"]
|> then(
  &%{
    "どれくらい首を傾げているか" => Float.round(&1["Roll"]),
    "どれくらい見上げたり見下ろしたりしているか" => Float.round(&1["Pitch"]),
    "どれくらい左右に向いているか" => Float.round(&1["Yaw"])
  }
)

写りの良さ

lenna_face["Quality"]["Brightness"]
|> Float.round()
|> then(&"明るさ #{&1}%")
lenna_face["Quality"]["Sharpness"]
|> Float.round()
|> then(&"鮮明さ #{&1}%")

感情

lenna_face["Emotions"]
|> Enum.map(fn emotion ->
  %{
    "confidence" => Float.round(emotion["Confidence"]),
    "emotion" => emotion["Type"]
  }
end)
|> DataFrame.new()
|> DataFrame.select(["emotion", "confidence"])
|> Kino.DataTable.new()

年齢

lenna_face["AgeRange"]
|> then(&"#{&1["Low"]}歳 〜 #{&1["High"]}歳")

性別

lenna_face["Gender"]
|> then(fn attr ->
  "#{Float.round(attr["Confidence"])}% " <>
    case attr["Value"] do
      "Female" ->
        "女性"

      "Male" ->
        "男性"
    end
end)

その他の情報

to_jp = fn bool ->
  case bool do
    true ->
      "る"

    false ->
      "ない"
  end
end
attributes = [
  {"Smile", "微笑んで"},
  {"EyesOpen", "目を開けて"},
  {"MouthOpen", "口を開けて"},
  {"Eyeglasses", "メガネをかけて"},
  {"Sunglasses", "サングラスをかけて"},
  {"Mustache", "髭が生えて"},
  {"Beard", "ハゲて"}
]
attributes
|> Enum.map(fn {en, jp} ->
  lenna_face[en]
  |> then(fn attr ->
    "#{Float.round(attr["Confidence"])}% #{jp}#{to_jp.(attr["Value"])}"
  end)
end)