Tech side of "Face Parts Randomizer"

f:id:peroon:20181118034449g:plain

play.google.com

  • このアプリの技術サイドを説明します

使っているもの

  • Unity
  • アセット
    • OpenCVForUnity(有料)
    • FaceTracker(無料)
    • Native Gallery for Android & iOS (無料)
      • Jpeg Exifの回転情報取得
    • STAN'S ASSETS Ultimate Mobile Pro
      • 昔買った
      • Android Galleryのアクセスに使用

処理

f:id:peroon:20181121050554p:plain

  • Texture2DFaceTrackerExampleシーンを書き換える
    • Texture2DFaceTrackerExample.cs
    • FaceTracker.cs
    • これらに書き足す
  • 顔情報が取れるようになっているので、デバッグ用に各パーツを可視化する

perogram.hateblo.jp

f:id:peroon:20181121044640p:plain

  • 可視化すると、目だけIndexがIncrementalじゃないことが分かる
  • 何度も使うので関数化する
        private int[] GetIndexArrayByPartsName(string name)
        {
            switch (name)
            {
                case "eyebrowR":
                    return GetIndexArray(15, 20);
                    break;
                case "eyebrowL":
                    return GetIndexArray(21, 26);
                    break;
                case "nose":
                    return GetIndexArray(37, 45);
                    break;
                case "mouse":
                    return GetIndexArray(48, 59);
                    break;
                case "eyeR":
                    int[] arr = { 32, 72, 33, 73, 34, 74, 35, 75 };
                    return arr;
                    break;
                case "eyeL":
                    int[] arr2 = { 27, 68, 28, 69, 29, 70, 30, 71 };
                    return arr2;
                    break;
                default:
                    Debug.Log("something wrong. naybe name wrong");
                    int[] arr3 = { -1 };
                    return arr3;
                    break;
            }
        }
  • パーツごとに切り抜いて別画像にする
    • 顔画像と同じサイズ(2048x2048)のマスク画像を作り、Index配列 > List > MatOfPointと変換してfillConvexPolyで切り抜く
            Mat mask = new Mat(2048, 2048, CvType.CV_8UC1);
            var partPointList = GetOnePartPointList(indexArray);
            MatOfPoint mop = new MatOfPoint(partPointList.ToArray());
            int lineType = 4; // 4, 8, 16
            Imgproc.fillConvexPoly(mask, mop, white, lineType); // draw mask to white
  • パーツ部分以外を切り捨てるためにsubmatする
result.submat(yStart, yEnd, xStart, xEnd).copyTo(sub);
  • 各パーツ画像のMatができた
  • パーツを切り抜いた、のっぺらぼうの顔部分はInpaintでそれっぽく塗る
        private Mat GetInpaintMask()
        {
            var white = new Scalar(255, 255, 255, 255);
            Mat mask = new Mat(2048, 2048, CvType.CV_8UC1);
            
            foreach(var name in GetPartsNames())
            {
                var points = GetOnePartPointList(GetIndexArrayByPartsName(name));
                MatOfPoint mop = new MatOfPoint(points.ToArray());
                Imgproc.fillConvexPoly(mask, mop, white);
            }

            return mask;
        }

        private void FaceInpaint(Mat im)
        {
            var inpaintMask = GetInpaintMask();
            Photo.inpaint(im, inpaintMask, im, 5, Photo.INPAINT_NS);
        }
  • 各パーツ画像はMatからUnity Texture2Dに変換して各Planeに貼る
    • 適切な位置、スケールでのっぺらぼうPlaneに重ねる
  • あとはUnity上の処理。OnClickで位置やスケールをRandomize
  • 以上

Android Gallery (アルバム)

  • WebからDLした芸能人画像
  • タブレットのカメラで撮った自分の画像
  • などを顔画像として処理してみると、Jpeg Exifの回転情報により90度回転して表示されることがあった
  • Exifも見るようにして、回転を補正した

f:id:peroon:20181118034449g:plain