20191110:成果報告

今回はレンダリング結果の軽量化を図ります。
ここで書いたか忘れましたが、3DディスプレイドングルではWindowsで実行したUnityで12枚の画像を合成した結果(レンダリング結果)をUDP通信でraspberrypiに送信する想定でいます。
この送る際に、画像をPNG形式の圧縮を掛けています。
レンダリング結果の確認のために圧縮したものをファイル出力したりするのですが、これが実に5MBあるのです。
秒間30枚(30fps)送ることになるので、秒間150MBを送らなければなりません。

しかし、raspberrypiの有線LANの通信速度はどうにも秒間39MB程度の様子。
https://raspida.com/new-pi-3b-plus?amp=1
全然足りません。
そこで、秒間150MBをなんとか1/5程度にする方法を検討します。

アプローチは以下の2つ。
①30fpsの動画としての軽量化
②秒間30枚送る画像1枚1枚の軽量化

①を検討します。
世の中ニコニコ生放送、Youtubelive、instaliveとリアルタイムに動画をストリーミングする技術は沢山あります。
この既存技術の適用によって軽量化は実現できるはずです。
しかし、調べてみたものの門外漢の私にはやはりなかなか難しい内容。
これはどっしりと腰を据えて取り組まねばなりません。
その中で断片的に私が理解できた範囲では、どうやらキーフレームという1枚の画像をフルサイズ送るタイミングと、そのキーフレームからどれだけ変化したかという差分情報だけを送るタイミングに分けることで軽量化しているようです。
これだけを実装するなら泥臭くコーディングしてもなんとかなりそうですが、世の中APIというものが普及していますから、すんなりと実装できる手段が転がっているかもしれません。これは引き続き調査をしていくことにします。

手段はどうであれ上記の理解から1つわかるのは、画像フルサイズは送らなければならないということです。当たり前といえば当たり前ですが。
この画像フルサイズが5MBあるのですから、どう考えてもフレームレートは低下してしまいます。画像は軽いに越したことはないのです。
そこで②の検討です。
画像を軽くするにあたって以下の3つの観点で検討する必要があります。
①PNGでいいのか
②画像を変えることなく軽量化する方法
③画像を変えて軽量化する方法

まず①です。
なぜPNGか。
これはもうひとえにUNITYがサポートしているからに他なりません。
ただしJPEGではダメな理由はあります。
レンダリング編でも説明していますが、サブピクセルが正確に表示されなければいけません。
不可逆圧縮であるJPEGではサブピクセル配置まで保証されないのです。
可逆圧縮である必要があります。
多分。
画像の可逆圧縮で最もポピュラーなのがPNGではあります。
しかし、もっと圧縮効率のいい可逆圧縮があるかもしれません。
いまいちこれぞという情報が出てこないのでこれも引き続き調査です。

次に②です。
画像を変えることなく軽量化する方法。
どういうことかというと、圧縮パラメータの選び方とかでもっと軽い方式があるんじゃないの?ということです。
「PNG 軽量化」で調べるとオンラインでPNG画像のファイルサイズだけを小さくしてくれるサービスが大量に見つかります。
どれもドラッグアンドドロップで変換ボタンを押すだけでファイルサイズを1/3程度にしてくれるものばかり。
しかし、中でなにをやっているかを教えてくれるものはありません。
ウンウンしていたら、テクニックみたいなものが見つかりました。
1.余計な情報を削除する
→PNG形式のファイルフォーマットには画像データ以外にも自由に使える領域が広く取られています。この自由領域を削除することで画像自体は変えずにファイルサイズを小さくできるとのこと。
2.色数を減らす
→PNGフォーマットでは、1ピクセルあたりを何bitで保存するかを選ぶことができます。
最大は赤緑青それぞれに16bit、1ピクセルに48bitを使い、281兆色の表現を可能にするモード。仮に200×200ピクセルの画像をこのモードで保存すると最大で234kB程度になります。
特に意識しない限りは、赤緑青にそれぞれ8bit、1ピクセルに24bit使い、1677万色の表現を可能にするモードが使われています。上記のサイズの画像をこのモードで保存すると、最大117kB程度と半分になります。
更にその下、代表的な256色のみでピクセルを表現することで1ピクセル8bitにするモード。上記のサイズの画像をこのモードで保存すると39kB程度と更に1/3程度になります。
ここまでの説明でお分かりかと思いますが、ファイルサイズを落とせば落とすほど表現できる色数が少なくなります。
ただし、仮に青が1/65536変化したところで余程のことがない限りわかりませんし、なんなら1/256変化しても多分大してわかりません。
全体で256色まで少なくなってしまうとさすがにわかるかもしれませんが、これが以外にも見劣りしないものになるのです。
 
上記を踏まえて、軽いPNGを作ります。
ここはフレームレートを優先して1ピクセル8bitのモードにしたいと思います。
と、ここで問題が。
UnityでPNG圧縮をサポートしているのですが、モードは選べないのです。問答無用で24bitモードで保存されます。自由領域にどんな情報が保存されているかもわかりません。なんてこった。
これではPNG圧縮を1から自力でコーディングしなければなりません。
いやできるんだろうけど。
今までの検討は一体なんだったのか。

ではもう③です。
画像を変えて軽量化する方法。
どんな画像ならPNGは軽くなるのか。
これはPNG圧縮の仕様からすぐにわかります。
PNG圧縮は、ピクセルごとの情報を隣のピクセルとの差分情報に置き換えることで軽くしています。こうして見ると動画でやっていることとほぼ同じですね。
青256、青256と横に同じピクセルが並んでいるとき、無圧縮であれば8bit+8bitで16bitかかることになりますが、差分情報であれば青256、差分0なので、8bit+1bitで7bitになります。2ピクセルだけでも半分以下。これが40000ピクセルとかになったらそりゃ軽くなります。
つまり、同じ色で塗りつぶされているような画像だと軽くなることになります。
同じ色で塗りつぶされているような画像を作るためにどうするか。
変化のない背景をなくします。
ブルーバックとかグリーンバックとか呼ばれる、背景が1色に塗りつぶされている状態はかなり軽くなる可能性が高いです。
そしてPNGは透明をサポートしています。
背景を透明にして、表示する時に背景画像に被せるようにすれば、1回あたりに送る画像サイズは小さくなります。
Unityで実現する方法は、背景をカメラのレンダリング対象から外すだけ、なんて簡単。
意気揚々と設定を変更して、いざレンダリング。
出力される真っ白なPNG画像。
????
ゲーム画面には背景1色の結果が表示されています。
PNG圧縮すると真っ白になります。というか多分全部透明。

なぜなのか。

PNG圧縮する対象のテクスチャをRGB24モードにすると、透明にはなりません。全部。透明になって欲しいところも含めて。
ARGB32モードにすると、透明になります。全部。透明になって欲しくないところも含めて。

いや、なぜなのか。

そういったところで時間切れでした。
解決せず。
引き続き対応を調査します。
アドバイスいただけると助かります。








コメント

このブログの人気の投稿

3Dディスプレイの仕組み(発展編)

20190804:成果報告

3Dディスプレイの仕組み(基本編)