Help us understand the problem. What is going on with this article?

【SNS顔出しNGの方も安心】顔に目隠しを付けてくれるアプリを機械学習APIで作ったよ

作ったもの

SNS顔出しNGの方のために、
顔に目隠しを付けてくれるアプリを作りました。

ウェブアプリなので、PC、スマホの機種は問いません。
こちらのリンクからどうぞ。
https://hardcore-ritchie-cca31e.netlify.app/

使用例

写真をアップロードすると、いい感じに目隠しを付けてくれます。
116153353_3186371734744672_7900174768791699760_o.jpg

複数人でも大丈夫。
(普通の飲み会が、なにやら悪巧みしてそうな会合に変貌)
※コロナ禍より前の飲み会です。

秘密のデートも目隠しを入れれば、誰にもバレません。
これで、プライバシーもバッチリ!
(写真はフリー写真サイト「ぱくたそ」 さんより)

スクリーンショット 2020-08-04 21.42.32.png

これで、みなさんも安心してSNSに投稿できますね。

番外編

なんと、私の3Dモデル(リアルアバター)にも反応しました。
つまり、私のアバターは人間と見分けがつかない?
ちなみに、このアバターは浅草にあるリアルアバターさんで作ってもらいました。
スクリーンショット 2020-08-04 21.49.02.png

猫はダメでした。
「人間じゃないよ」ってアラートを出します。

使用技術

言語:

JavaScript

CSSフレームワーク

Bootstrap
レスポンシブデザインを気持ち程度に使用

ライブラリ

face-api.js について

TensorFlow.jsで学習済みの顔認識の機械学習モデルが使えるAPIです。

顔の検出、表情分析、年齢・性別分析などができます。
サービスアカウント不要、アクセストークン・シークレットキー不要、無料で手軽に利用できます。

導入手順

face-api.jsのREADMEやネット記事を読んで私が実行した手順は以下

  • 以下のリンクからZIPファイルをダウンロード (Cloneでも可)
    https://github.com/justadudewhohacks/face-api.js/
  • distフォルダにある face-api.js を自分のアプリと同じディレクトリに移動する
  • weightsフォルダの名前を modelsに変更し、自分のアプリと同じディレクトリに移動する
index.html
face-api.js
models

他のフォルダ、ファイルは不要です。

本件で使用したモデル

tinyFaceDetector というモデルで顔を検出し、
faceLandmark68Net というモデルで顔のランドマークを検出します。
この写真のように68個の顔のポイント1つ1つの画像上での座標が取得できます。

このような感じで座標を取得できます。(これが全部で68個あります)
スクリーンショット 2020-08-05 22.43.33.png

ポイント36からポイント45へ線を引けばいいのですが、それだと線が短すぎるので、
線の始点: X軸はポイント0、Y軸はポイント36
線の終点: X軸はポイント45、Y軸はポイント16
を採用してバランスをとりました。

本当は、ポイント36からポイント45の線の傾きを算出して、線を伸ばすようなコードを書けばよいのですが、あまり影響がないので、そのままにしました。

参考サイト

以下のサイトに大変お世話になりました。
ありがとうございました。

コード

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- Bootstrap -->
    <!-- CSS only -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous" />
    <!-- JS, Popper.js, and jQuery -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous" ></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous" ></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous" ></script>
    <style>
      .container {
      padding-top: 50px;
      }
    </style>

    <!-- face-api.jsの読み込み -->
    <script src="face-api.js"></script>

    <!-- humaneライブラリの読み込み -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/humane-js/3.2.2/themes/boldlight.css"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/humane-js/3.2.2/humane.min.js"></script>

  </head>
  <body>
    <div class="container">
      写真をアップロードしてください
      <div class="upload"><input type="file" name="file" id="file"></div>
      <div id="result"></div>
      <canvas id="canvas"></canvas>
    </div>
  </body>

  <script>
    const MODEL_URL = 'models/'
    let file = document.getElementById('file')
    let canvas = document.getElementById('canvas')
    let canvasWidth = 350
    let canvasHeight = 350
    let uploadImgSrc

    //ファイルをアップロードしてCanvas上に表示するのは、ほぼこのサイトどおり
    //https://www.tam-tam.co.jp/tipsnote/javascript/post13538.html
    // Canvasの準備
    canvas.width = canvasWidth
    canvas.height = canvasHeight
    var ctx = canvas.getContext('2d')

    function loadLocalImage(e) {
      // ファイル情報を取得
      let fileData = e.target.files[0]
      // 画像ファイル以外は処理を止める
      if(!fileData.type.match('image.*')) {
        humane.log("画像ファイルでお願いします")
      }
      // FileReaderオブジェクトを使ってファイル読み込み
      let reader = new FileReader()
      // ファイル読み込みに成功したときの処理
      reader.onload = function() {
        // Canvas上に表示する
        uploadImgSrc = reader.result;
        canvasDraw(fileData)
      }
      // ファイル読み込みを実行
      reader.readAsDataURL(fileData)
    }
    // ファイルが指定された時にloadLocalImage()を実行
    file.addEventListener('change', loadLocalImage, false)
    // Canvas上に画像を表示する
    function canvasDraw() {
      // canvas内の要素をクリアする
      ctx.clearRect(0, 0, canvasWidth, canvasHeight)
      // Canvas上に画像を表示
      let img = new Image()
      img.src = uploadImgSrc
      img.onload = function() {
      ctx.drawImage(img, 0, 0, canvasWidth, this.height * (canvasWidth / this.width))
      let canvasRate = canvasWidth / this.width

     //顔の情報を取得
      getFaceData(img,canvasRate)  
  }
}

async function getFaceData(img,canvasRate) {
  await faceapi.nets.tinyFaceDetector.load('models/') //モデル読み込み
  await faceapi.nets.faceLandmark68Net.load("models/") //モデル読み込み
  // 顔検出の実行
  const detectionsWithLandmarks = await faceapi.detectAllFaces(img,
        new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks()
  if (detectionsWithLandmarks.length == 0){
    humane.log('人間じゃないよ')
  }else{
    for (let n = 0; n < detectionsWithLandmarks.length ; n++ ) {
      let x0 = detectionsWithLandmarks[n].landmarks._positions[0].x * canvasRate
      let y0 = detectionsWithLandmarks[n].landmarks._positions[36].y * canvasRate
      let x1 = detectionsWithLandmarks[n].landmarks._positions[16].x * canvasRate
      let y1 = detectionsWithLandmarks[n].landmarks._positions[45].y * canvasRate

      let line_thickness =  (x1-x0) * .2 //線の太さを長さの20%とした

      drawLine(x0,y0,x1,y1,line_thickness) //目隠し線を引く関数
    }
  }
}

//目隠し線を引く
function drawLine(x0,y0,x1,y1,line_thickness){ 
  ctx.strokeStyle = '#000000'
  ctx.lineWidth = line_thickness
  ctx.beginPath()
  ctx.moveTo(x0, y0) 
  ctx.lineTo(x1,y1)
  ctx.closePath()
  ctx.stroke()
}

</script>

</html>
tatsuya1970
普通のサラリーマンです。趣味でアプリ開発、電子工作などしています。
http://tatsuya1970.main.jp/
protoout-studio
ProtoOut Studioは日本初のプロトタイピング専門スクールです。プログラミングとプランニング(企画)の両方のスキルを兼ね備えた人材輩出を行います。作って発信して、がんがんプロトアウトしていきましょう。
https://protoout.studio
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした