54
49

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-08-04

##作ったもの
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>
54
49
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
49

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?