Web会議の相手を美女に変えるブックマークレット

＜追記＞
appearのドメイン変更に伴い、使えなくなりました:sob:
恐らく新しいドメインでも対応しようと思えばできますが、美女の裏におじさんが見え隠れすると悲しくなることに気づいたので、やめておきます。

－－－－－－－－－－－－－－－－－－－－－－－－

**エンジニアって、全体的に男が多いじゃないですか。**まぁそれはそれで気楽でいいんですが、毎日男だらけの職場で男たちを見ながら打合せやってると、なんかこう、**たまには綺麗なものを見たいなー**なんて思うわけですよ。

特に**Web会議で1対1で話してるときは、否応なく2人で向かい合ってしまう**わけで。別に仕事にストレスがなくても、この相手が綺麗なお姉さんだったらなぁ…なんて思ったり思わなかったりなわけですよ。

じゃあ、**Web会議の相手を美女に変えてしまえばいいのでは？**ということで今回は、こんなものを作りました。

## 作ったもの

**Web会議の相手を美少女キャラに変えるブックマークレット**（誰でもすぐ使えるやつ）

<サンプルイメージ＞
![c.gif](https://qiita-image-store.s3.amazonaws.com/0/280076/61c3e32f-b408-0fdd-ec47-2fdf69a272c4.gif)
（カフェで1人でキョロキョロしてたので怪しい奴ですね。。）

## 使い方
### ①ブックマークの登録
以下のResultにある「変身」リンクを、PCのブラウザのブックマークに登録する。
（ブックマークバーにドラッグ＆ドロップでOK）

<p data-height="265" data-theme-id="light" data-slug-hash="ebGGwB" data-default-tab="html,result" data-user="yossymura" data-pen-title="ebGGwB" class="codepen">See the Pen <a href="https://codepen.io/yossymura/pen/ebGGwB/">ebGGwB</a> by yossymura (<a href="https://codepen.io/yossymura">@yossymura</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### ②Web会議の開始
PCで[appear.in](http://appear.in)という無料Web会議サービスで会議を始める。
相手が必要なので、端末2台で同じ会議URLにアクセスする。（相手側はスマホでも可）

※appearは自分の仕事でも使用しており、アカウントなしで使えて資料共有もできて割と安定しているサービスです。
※会議用の部屋（会議用URL）を新しく作るにはアカウントが必要ですが、既存の部屋を使う場合は不要です。（例えば僕が作成した https://appear.in/vtubers に入るだけなら無料）

### ③ ブックマークをクリック
①で登録したブックマークをクリックする。（相手の顔が映っている状態で）

以上。これで相手の顔の動きをトレースして動くキャラが表示されます。**ブックマークレットなので、適用されるのは自分のブラウザだけです。相手にはバレません。**

##作り方のポイント

全てJavaScriptです。ポイントは3点。

###１．フェイストラッキング（clmtrackr）
顔の動きをトラッキングするため、**[clmtrackr](https://github.com/auduno/clmtrackr)**というライブラリを利用することに。よくカメラ映像や動画に対して使われているようですが、今回はWeb会議で受信する映像に対して適用してみました。

```js
//clmtrackrのインスタンス
var ctrack = new clm.tracker();

//トラッキング対象要素を取得（ここでは2つ目のvideoタグ）
//自分のカメラ映像を対象にする場合は("video")[0]となる。
var video = document.getElementsByTagName("video")[1];

//　･･･（初期化処理など）

//トラッキング開始
ctrack.start(video);
```

**トラッキング対象の指定**さえすれば、その他の箇所はサンプルがネット上に沢山転がっているのでコピペでなんとかなります。ここでは、**appear.inで相手の映像が映る要素である「2つ目のvideoタグ」を指定しました。**

また、こんな画像が公式ページで紹介されているように、顔のラインはcanvasで描くこともできますし、**顔のパーツごとの座標も関数1つで取得できます。**
![face.jpg](https://qiita-image-store.s3.amazonaws.com/0/280076/e962fb97-7e34-2928-b66b-d46444a445b5.jpeg)

```js
//全顔パーツの座標取得（X座標＆Y座標のペアの配列）
pos = ctrack.getCurrentPosition();
```

###２．キャラクター描画（Live2D）
ブラウザで描画するため軽めのものを使いたく、初めて**[Live2D](https://www.live2d.com/ja/)**というものを使ってみました。Web向けのものを使えば、JavaScriptで**簡単なパラメータを指定するだけで、2D画像のキャラクターを3Dっぽく動かすことができるようです。**

```js

//画面の右を向くならこんな感じ（-30～30度で指定）
live2DModel.setParamFloat("PARAM_ANGLE_X", 30);
```


既に**①で顔のパーツの座標を手に入れているので、それをLive2D用に加工し、パラメータとして渡してあげる**と、キャラクターが映像の顔の動きと連動して動きます。

```js

//事前計算
var faceL = pos[62][0] - pos[2][0];
var faceR = pos[12][0] - pos[62][0];
var vecL = [pos[2][0] - pos[7][0], pos[2][1] - pos[7][1]];
var vecR = [pos[12][0] - pos[7][0], pos[12][1] - pos[7][1]];
var lipH = pos[53][1] - pos[57][1];
var eyeHL = pos[26][1] - pos[24][1];
var eyeHR = pos[31][1] - pos[29][1];

//顔の向き
params["PARAM_ANGLE_X"] = 90 * (faceL - faceR) / (faceL + faceR);
params["PARAM_ANGLE_Y"] = -90 * (vecL[0] * vecR[0] + vecL[1] *
    vecR[1]) / Math.sqrt(vecL[0] * vecL[0] + vecL[1] * vecL[1]) /
    Math.sqrt(vecR[0] * vecR[0] + vecR[1] * vecR[1]);
params["PARAM_ANGLE_Z"] = -90 * (pos[33][0] - pos[62][0]) /
    (pos[33][1] - pos[62][1]);

//顔に合わせて体の向きも
params["PARAM_BODY_ANGLE_X"] = params["PARAM_ANGLE_X"] / 3;
params["PARAM_BODY_ANGLE_Y"] = params["PARAM_ANGLE_Y"] / 3;
params["PARAM_BODY_ANGLE_Z"] = params["PARAM_ANGLE_Z"] / 3;

//口の開閉・形
params["PARAM_MOUTH_OPEN_Y"] = (pos[57][1] - pos[60][1]) / lipH - 0.5;
params["PARAM_MOUTH_FORM"] = 2 * (pos[50][0] - pos[44][0]) /
    (pos[30][0] - pos[25][0]) - 1;

//眼球の動き
params["PARAM_EYE_BALL_X"] = (pos[27][0] - pos[23][0]) / (pos[25][0] - pos[23][0]) - 0.5;
params["PARAM_EYE_BALL_Y"] = (pos[27][1] - pos[24][1]) / eyeHL - 0.5;

//目の開閉
params["PARAM_EYE_L_OPEN"] = 0.7 * eyeHL / lipH;
params["PARAM_EYE_R_OPEN"] = 0.7 * eyeHR / lipH;

//眉の上下
params["PARAM_BROW_L_Y"] = 2 * (pos[24][1] - pos[21][1]) / lipH - 4;
params["PARAM_BROW_R_Y"] = 2 * (pos[29][1] - pos[17][1]) / lipH - 4;

//Live2Dモデルに連携
for (var paramName in params) {
    live2DModel.setParamFloat(paramName, params[paramName]);
}
```

ちなみに今回はLive2Dのサンプルキャラをそのまま使いましたが、ちゃんとやるなら動物キャラとかにした方がしっくり来るんでしょうね。女性キャラと男性の声はギャップがありすぎるので。

###３．クロスオリジン（CORS）
１と２をJavaScriptファイルのみで実現できれば３は不要なのですが、**Live2Dでは、キャラクターのモデルとなるファイルが必要になります。**「appear.in」ドメインには当然そんなファイルは存在しないため、JavaScript内で外部から読み込まなければいけません。

しかし、異なるオリジン（ドメイン、プロトコル、ポート番号）間でのデータのやりとりになるため、何も考えずに作成するとセキュリティ上の制約にひっかかります。その回避のため、**クライアント（JS）側とファイルを配置するサーバ側にそれぞれクロスオリジン対応が必要になります。**

###クライアント側（例）
JavaScriptで画像にアクセスする際、「crossOrigin」属性を追加。

```js
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var img = new Image();

// クロスオリジン対応
// 認証情報を使用しない場合は "Anonymous"
img.crossOrigin = "use-credentials";

img.src = "https://XXXXX/XXXXX.png";
img.onload = function () {
    ・・・//contextへのimg描画処理など
}
```


###サーバ側（例）
.htaccessファイルに以下設定を追加。

```apache
# https://appear.in からのクロスオリジンを許可（全て許可する場合は "*" を設定）
Header set Access-Control-Allow-Origin "https://appear.in"

# リクエストに対するレスポンス開示を許可
Header set Access-Control-Allow-Credentials true
```

これで、クロスオリジンの際にサーバ側が認めたアクセス元（今回はappear.in）からだけデータ取得を許可されます。セキュリティ的にはもっとしっかり作り込んだ方が良いのかもしれませんが、お遊びネタなのでこれぐらいに。

## あとがき
VTuber関連の技術は、Unityや3Dモデリングあたりを深く学ぶ必要があるイメージでしたが、以前に[FaceVTuber](https://facevtuber.com/)というサービスを見て、JSでここまでのモノが作れるんだと感動しました。[こちらの記事](https://qiita.com/kotauchisunsun/items/7ddb82c3f34a1ca029e9)ではその過程が見れて面白かったです。

今回は、そんな流行りのVTuberと、自分の仕事で実際に使っているWeb会議サービスを絡めて手軽に何かできるかなという思い付きから、コピペをベースに作ってみました。**技術的にはシンプルですが、誰か1人にでも「組み合わせればこんなことできるんだ」という感覚を持ってもらえると嬉しいです。**会議にappearを使ってる人は多くないかもしれませんが、ブラウザでカメラ映像を映すものであれば他にも簡単に転用できるはず。

今はGoogleやApple配下の「アプリ」として楽しまれているコンテンツが今後Webに移るとしたら、Web上で様々な面白いものがどんどん展開されていくことになります。そんなときに備えて、いち早く面白いものを作れるようにしておきたいものです。

## 参考リンク
作るにあたりいくつか記事を拝見したので、載せておきます。

**＜Live2d周り＞**
- [Live2DのWebGL版は今週公開](https://qiita.com/naotaro0123/items/d8bb0439dc3e7aa4083c)
- [Live2D WebGLでドラッグした方を見る](https://qiita.com/naotaro0123/items/26ff4e1f4c4393c1774d)
- [Web で顔認識して Live2D のモデルを動かす](https://qiita.com/kimamula/items/9338a83cf0dd7775b154)

**＜CORS周り＞**
- [クロスオリジン リソース シェアリング（CORS）](https://cloud.google.com/storage/docs/cross-origin?hl=ja)
- [オリジン間リソース共有 (CORS)](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)
- [CORSまとめ](https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34)
- [CORS紹介](https://qiita.com/alingogo/items/8cd84994a5458aaa8377)
- [HTML5 における CORS について](https://qiita.com/nanocloudx/items/f49600b705be9d53a9b5)
- [CORSリクエストでクレデンシャル(≒クッキー)を必要とする場合の注意点](https://qiita.com/kawaz/items/1e51c374b7a13c21b7e2)