自分のカードを作る体験
先日、「カメラで撮影した画像と背景デザイン画像を合成して、自分のカードを作成する」というシステムを作りました。決して複雑な構成ではないのですが、中々ハマる部分もあったので、詰まった箇所や考えていたことをまとめてみます。
全体構成は以下のような感じです。
◎ システム構成
・Windows 10
・Unity 2018.4.8f
・Canon CX-系カードプリンター
・Canon EOS コンデジ
メインプログラムはUnityで実装しました。実行PCにプリンターとカメラを接続し、カメラ制御にはSDKなどは使わず、キャプチャボードを利用して一般的なWebカメラとして認識させています。撮影の際、UnityでWebカメラに映る画をスクリーンショットしているイメージです。
◎ グリーンバックでの人物切り抜き
背景にカードデザインを合成するために、人物だけが切り取られた画像を取得する必要があります。そのために今回はグリーンバック(クロマキー)を用いました。映像制作でよく用いられる手法です。
Unityでこれを実現するには、既存のAssetを利用するのが手取り早いと思います。またAssetの他にも、凹みさんが提供しているように、Github等にオープンに公開されているものもあります。実装にあたりいくつか試してみましたが、どれも微妙に使い方や精度が異なる印象です。
特に異なるのは抜け方の調整部分です。感度(Sensitivity)のみで調整するものや、抜く色味(Hue)を調整できるものなど様々です。また、色味の調整具合もモノで変わってくるので、試してみて、使いやすいものを選ぶと良いと思います。
今回は以下のGitからShaderを拝借し、緑色の変化にロバスト(堅牢)になるよう重みのパラメータを一部変更して利用しました。
◎ 余談:iPhone Portrait Matteで人物切り抜きできないのか?
グリーンバックは枯れた技術ですが、Deep Learningが使われた最新の領域分割技術を用いれば、同様の効果を得ることもできます。セマンティックセグメンテーションと呼ばれるものです。
iPhoneのPortrait Matteもその一つです。ポートレートモードが利用できるiPhone端末で撮影をし、処理すると、人物(と思われる)の領域を抽出してくれます。画像のこの辺りが人だろう、とDeep Learningで判断してくれるのです。
これを使えば、クロマキー用の背景を用意することなく、iPhoneさえあればカメラも必要ありません。ありがたすぎる技術です。
しかし使いづらさもあります。それは「人物だけしか」切り取れないという点です。
例えば、ギター演奏者を背景から切り取りたいとします。そういうとき、本当に切り取りたいのは「人物+ギター」の領域です。フルート奏者であれば「フルートを吹いている人」を切り取りたい。そういう場面もあるかと思います。
でも、上記のセグメンテーションを使うと、ギターやフルートの一部が欠けてしまうことがあります。「人が写っている領域」だけコンピュータが学習しているので、人物だけを残そうとしてしまうのです。
同じような理由から、今回は用件に合わず、Portrait Matteの導入を見送りました。技術は最新のものが必ずしも使いやすいとは限らず、一長一短があります。要件や起こりうる体験からそれを見分け、選択することが重要です。
余談が長くなってしまいました。
◎ 背景やエフェクト画像との合成
人物画像が取得できたので、次に背景デザインと合成します。合成というと、画素(ピクセル)を一つずつ重ね合わせるいわゆる「画像処理」をイメージするかもしれませんが、今回はいったんUnity上で「重ねて」います。
Unityで仮想キャンバスを用意し、その上に人物画像と背景画像を重ねていくやり方です。こうすることで、Unityのプレビュー画面で確認しながら進めることができます。
またここで、人物画像のみにコントラスト調整などエフェクトをかけています。OpenCVやDlibといった高性能な画像処理ライブラリを使うまでもないので、Shaderで処理を完結させています。
(触っていて思ったのは、Unityでの画像処理は結構ややこしいということです。後述するCameraとGame画面の関係など、一癖あります。単純なエフェクトかけるのにも少し手間がかかります。TouchDesignerの方が直感的なので、場合によってはそちらを利用すべきです。)
◎ 合成画像のpng化
Unity上で重ねましたが、このままでは画像データになっておらず印刷ができません。そのためpngとして保存します。今回ここが一番厄介な部分でした。
Unityにはゲーム画面をスクショし画像化する便利なメソッドが用意されています。ゲーム画面に映るものをpng化したい場合、これを呼ぶだけで完了です。
ただし、これはゲーム画面のスクショです。仮想キャンバスに重ね合わせた画像はゲーム画面には表示されないため、このメソッドは使用できませんでした。
そのため仮想キャンバスの目の前に仮想カメラを置き、そこに映るものを取得し、pngとして保存することにしました。
Unityのcameraコンポーネントは、そこに映るものをRenderTextureという形式で取得できます。このRenderTextureを画像として保存するようにしています。
cameraの挙動が少々ややこしく、また処理が重くてGPUに任せたりと、一筋縄では行かなかったのですが、細かいので詳細はQiitaに書くこととします。
◎ カードの印刷
最後に保存したpngを印刷します。
Unityから印刷する場合、macOSではlprコマンドというUnixコマンドを使うことが一般的なようです。LPR = Line Printer Deamon Protocol の略で、TCP/IPで印刷するプロトコルです。
しかしWindowsでlprを使うのは若干面倒との情報があったため、.NET系の.dllを利用することにしました。System.Drawing.dllをプロジェクトに読み込むことで、印刷系のメソッドを使えるようになります。
System.Drawing.dllの注意点として、印刷はできますが、完了やエラー通知をとるのには向いていないという点があります。また、詳細な印刷設定にもアクセスしづらいです。
そのため、利用するプリンターは「通常使う」設定にし、用紙など設定はあらかじめプリンタのダイアログで済ませておくと、指定せず印刷ができるのでおすすめです。
◎ まとめ
Unityでのクロマキー、画像合成、印刷の実装方法をまとめました。Unityなんでもできて便利だなーと思う反面、3D表現をあまり使わないのであれば、他の選択肢の方が良いのかもしれません。TouchDesignerやmacOSアプリなど。
断片的なTIPSはQiitaやブログ上に多く上がっていますが、一つの目的に向かって情報がまとまっていることは少ないと感じるので、こういう記事をこれからも書いていけたらと思います。