カッってしたい。

# はじめに

Netflixでペルソナのアニメを見た。
すごかった。
メガネかけると"カッ"ってなる。
これ、カメラアプリで実装したらペルソナ使いになれるんじゃなかろうか。

![kaltu01.png](https://qiita-image-store.s3.amazonaws.com/0/42445/73611279-ff25-3d02-9b44-92814a5e1c40.png)

こんな奴

# 出来上がったもの

ハンドスピナーをカッってしてみた。

[![Persona](https://img.youtube.com/vi/rqmAPKnKaMM/0.jpg)](https://youtu.be/rqmAPKnKaMM)

誰かしらの参考になれば嬉しいので、githubにも上げた
[Kaltu](https://github.com/miyatama/Kaltu)

# アプリのざっくり動作

 + カッ画像の作成
 + カメラ表示中にカッってできる

方針

 + 見栄えにはこだわらない
 + コードにもこだわらない
 + 妥協できる所は積極的に妥協

## カッ画像の作成

まずは自分の画像を撮って加工する所。
目の部分だけ取れればOKとする。

### ざっくり説明

camera2のサンプルや各種サイトを参考にカメラプレビューを表示。
顔を撮るためフロントカメラを指定。

画像の保存はこんな感じ。

``` java:CreateKaltuActivity
Log.d(TAG, "onImageAvailable");
Matrix matrix = new Matrix();
matrix.preScale(0.3f, -0.3f);
final Image image = imageReader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] buff = new byte[buffer.remaining()];
buffer.get(buff);
Bitmap bitmap = BitmapFactory.decodeByteArray(buff, 0, buff.length);
image.close();

mCorrectRotateBitmap = Bitmap.createBitmap(bitmap, 0,0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);

Frame frame = new Frame.Builder()
        .setBitmap(mCorrectRotateBitmap)
        .build();
SparseArray detectedFaces = mFaceDetector.detect(frame);
if (detectedFaces != null && detectedFaces.size() > 0){
    float maxSize = 0;
    int idx = 0;
    for (int i = 0 ; i < detectedFaces.size(); i++){
        Face face = (Face)detectedFaces.valueAt(i);
        if (maxSize < (face.getWidth() * face.getHeight())){
            maxSize = (face.getWidth() * face.getHeight());
            idx = i;
        }
    }
    Face face = (Face)detectedFaces.valueAt(idx);
    // 目の位置が満足に取れないので顔の高さからざっくり計算する。
    int width = mCorrectRotateBitmap.getWidth();
    int height = (int)Math.floor(face.getHeight() / 4.0f);
    int left = 0;
    int top = (int)Math.floor(face.getPosition().y) + (int)Math.floor(height * 1.7f);
    if ((height + top ) > mCorrectRotateBitmap.getHeight()){
        height = mCorrectRotateBitmap.getHeight() - top;
    }
    Bitmap kaltuBitmap = Bitmap.createBitmap(mCorrectRotateBitmap, left, top, width, height);
    try{
        File file = new File(activity.getFilesDir(), "Kaltu.png");
        FileOutputStream outStream = new FileOutputStream(file);
        kaltuBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
    } catch (IOException e ){
        Toast.makeText(activity, "Save bitmap failure", Toast.LENGTH_SHORT).show();
    }
    kaltuBitmap.recycle();
    kaltuBitmap = null;

    Message msgShowImage = Message.obtain();
    msgShowImage.what = HDL_SHOW_KALTU;
    mHandler.sendMessage(msgShowImage);

    Message msgHideImage = Message.obtain();
    msgHideImage.what = HDL_HIDE_KALTU;
    mHandler.sendMessageDelayed(msgHideImage, 5000);
} else {
    Toast.makeText(activity, "face not exists", Toast.LENGTH_SHORT).show();
}

bitmap.recycle();
bitmap = null;
```

コメントにある通り目の位置算出がなかなかうまくいかなかったので、
顔認識の大きさから無理やり算出してある。
なので、目の位置がうまく合わない人は上目使いとか下目使いとか工夫して合わせる必要がある。

### 妥協点

 + 目の位置が取れたり取れなかったりなので、顔の大きさからざっくり計算

### 参考

 + [FaceDetector](https://developers.google.com/android/reference/com/google/android/gms/vision/face/FaceDetector)
 + [AndroidのCamera2 APIのサンプル](http://blog.kotemaru.org/2015/05/23/android-camera2-sample.html)
 + [Saving Files](https://developer.android.com/training/data-storage/files.html)

## カメラ表示中にカッってできる

画面タップでカッとする。
カッの特徴

 + 割とでかい
 + 横線の薄い白がちょこちょこ入っている→気が乗ったらやる
 + 出てくる時のエフェクト→気が乗ったらやる。
 + 出てくる時間は2秒間
 + 文字でカッって出る

### ざっくり説明

ほぼカッ画像作成から画像作成を差し引いた処理になる。
日本語としておかしいけど何となくわかってほしい。
カメラはリアを指定。

書くことないのもあれなので、画像読み込みだけでも転記。

``` java:EnjoyKaltuActivity
try{
    File file = new File(activity.getFilesDir(), "Kaltu.png");
    FileInputStream fis = new FileInputStream(file);
    mKaltuBitmap = BitmapFactory.decodeStream(fis);
} catch(FileNotFoundException e){
    Toast.makeText(activity, "Kaltu Not Found", Toast.LENGTH_SHORT).show();
    this.finish();
}
```

# 振り返り

遊んでみてわかったのが、猫とかティッシュとか映しながらカッってすると、
とてつもなくくだらなくて楽しい。