2021年03月07日

JPEG画像のノイズ除去と超解像処理

以前から構造だけは考えていたので、重い腰を上げて実装してみました。まぁ基本は既存モデルの流用なので実装というほどでもないのですが、最新技術を簡単に利用できるのは素晴らしいですね。

JPEGの圧縮ノイズ除去を行う手法も色々ありますが、JPEGのQualityをラベルとして与えてあげれば少し改善されるのではないかというのがアイデアです。未知の画像はQualityが分かりませんので、それを推定する必要があります。全体構造としては下記の図のようになります。

jpeg_sr_001.png

1. JPEG画像をEfficientNet-b7[1]に入力してJPEGのQualityを推定(10クラス分類)
2. JPEG画像をパッチサイズ(128x128x3)に分割
3. JPEGのQualityをnn.Embeddingを使ってベクトル化→128x128x1にreshape
4. 画像とラベルをtorch.catで結合して128x128x4の入力テンソルに変形
5. 4ch入力に変更したS-Net[2]に入力してJPEGノイズを除去
6. SPSR[3]に入力して画像を4倍に拡大
7. 拡大したパッチを結合して画像として保存

といった流れになります。

ポイント1
EfficientNet-b7への入力は600x600でしたので、入力画像から切り出す必要がありますが、torchivisionにtransforms.FiveCropという4隅+中央を指定したサイズで切り出すという処理がありましたので、画像から5か所切り出しでEfficientNet-b7に入力し、結果として得られた5個の結果の中央値を出力クラスとしました。かなり精度が高い印象でした。

ポイント2
nn.Embeddingはラベルをベクトルに変換してくれますが、毎回同じベクトルにするために乱数シードの固定が必要です。また、ベクトルの次元が変わると生成される値も当然変わってきますので、推論時は画像を丸々S-Netに入力するのではなく、パッチサイズに分割してサイズを固定してあげる必要があります。

ポイント3
分割してからノイズ除去→拡大を実行すると、かなり切れ目が目立ちました。下記のようにオフセットとして畳み込みで影響しそうな領域を大きめに切り取ってノイズ除去→拡大したのち、余分な部分をカットしました。
jpeg_sr_002.png

ポイント4
パッチ分割する利点ですが、画像が大きすぎるとSPSRで拡大するときにメモリが足りなくなるので、その点でも有効だったと思います。

実験条件

EfficientNet-b7は出力層を10層に変更[4]して、DIV2Kを使ってファインチューニングしました。LossはCrossEntropy誤差を使用しました。
S-NetはEncoder部分の入力を4chに変更して、DIV2Kを使ってLossをSSIMLossで学習させました。元々原画像とJPEG圧縮したものをDatasetとして使うようになっていましたので、圧縮時のQuality÷10をラベル情報として追加で出力するように変更しました。
SPSRは全く変更しておらず、学習済みモデルをそのまま使用させていただきました。

実行結果

拡大しないと分かりにくいかもしれませんが結構きれいにできていると思います。

jpeg_sr_003.jpg
入力画像:Set14の画像をQuality=40で保存したJPEG画像

jpeg_sr_004.png
(左)Bicubic補間で入力画像を4倍したもの(右)1.~7.を実行したもの(一部切り出して拡大)

[1] https://github.com/lukemelas/EfficientNet-PyTorch
[2] "S-Net: A Scalable Convolutional Neural Network for JPEG Compression Artifact Reduction"
[3] Structure-Preserving Super Resolution with Gradient Guidance (CVPR 2020)
[4] EfficientNetの事前学習モデルをPytorchで使う
posted by シンドラー at 15:37| Comment(0) | Machine Learning | 更新情報をチェックする

2021年02月04日

PyTorch3Dを使ったボリュームレンダリング

もう一つのチュートリアル(Fit A Simple Neural Radiance Field)も面白そうですが、今回はとりあえずボリュームレンダリングのテストです。データは[1]のBonsai1をV^3 Library[2]のpvm2rawを使ってrawデータに変換して、それをnpzに変換したものを使用しました。この辺の話もブログに書いた気がしたんですが見当たらないので気のせいでしょうか。

ソース
volume_rendering_test.html

前回のチュートリアルの一部を残して、学習してボリュームデータを最適化する代わりにnpzファイルを読み込んでレンダリングしています。

実行結果

result.png

結局伝達関数をどうするのかがネックな気もしますが、この辺も学習で何とかなるんですかね。教師データとなる画像があるのならできそうですが…。

[1] The Volume Library
[2] VVV - Real-Time Volume Renderer
[3] https://github.com/facebookresearch/pytorch3d
posted by シンドラー at 23:14| Comment(0) | Machine Learning | 更新情報をチェックする

2021年01月31日

PyTorch3Dを使ったボリュームデータのフィッティング

PyTorch3D[1]も気が付くとバージョンアップしていたりチュートリアルが増えていたりしていいことですね。今回はボリュームデータに関するチュートリアルが増えていたので試してみることにしました。

手法としては、ポリゴンモデルを複数の視点からレンダリングして、その視点座標+レンダリング結果(カラー画像とシルエット画像)の組を教師データとして、128x128x128のボリュームデータをレンダリング→カラー画像とシルエット画像との誤差を計算→ボリュームデータの色や不透明度を修正というもののようです。

これを使えばポリゴンモデルのボリュームデータ化なんかが簡単にできそうな気がしますね。

実行結果
pytorch3d_fit_volume_result.png

左上がボクセルをレンダリングしたカラー画像、右上が教師カラー画像、左下がボクセルをレンダリングしたシルエット画像、右下が教師シルエット画像です。ボリュームデータが最適化されているのがわかるかと思います。

適当に訳したコード+npz保存
fit_textured_volume_jp.html

最適化されたデータをnpzで保存して、voxデータに変換してMagicaVoxelで表示してみました。

pytorch3d_fit_volume_result_002.png

データは[0-1]なので255倍して、densityが10未満のデータについては空域としました。ボリュームレンダリングすれば見やすいのでしょうが、無理やりボクセル化すると周辺の黒い部分が残ってしまいます。閾値を大きくすると中身も削れてしまったので、あくまでもボリュームデータ作成に使える、といった感じのようです。

手元にあまりいいボリュームレンダラがないので、次回はボリュームレンダリングにPyTorch3Dを使ってみようかなと思います。

[1] https://github.com/facebookresearch/pytorch3d
posted by シンドラー at 20:54| Comment(0) | Machine Learning | 更新情報をチェックする

2021年01月22日

SSIMのテスト

前回の続きです。

PyTorchでAutoencoderの[1]ですが、loss関数にRMSE(Root Mean Square Error)が使われています。

loss = torch.sqrt((y_hat - y).pow(2).mean())

これをSSIMに変更して学習させてみます。PythonでSSIMを計算する場合、piqa[2]というパッケージが簡単に使用できるようです。SSIMの他にもたくさん指標が実装されているようですので、簡単に比較なども出来そうです。

使い方は[3]にありましたのでその通りにしました。

実行結果

ae_ssim_001.png
ae_ssim_002.png
ae_ssim_003.png

どちらも120エポック学習したものです。8エポック改善されなかったら学習率を下げるようになっているのですが、結構効果があるみたいですね。過学習かもしれませんが。

最終的にはSSIMが0.971まで上がりましたので、数値的には結構近づいてきていますが、画像を見ると少し色合いが変わっている気がしますね。RMSEの最終的な値は記録するのを忘れました。基本的に機械学習もRGBでやっていますが、HSVに変換したりするとまた少しは変わるんでしょうか。

[1] https://github.com/foamliu/Autoencoder
[2] https://github.com/francois-rozet/piqa
[3] https://stackoverflow.com/questions/53956932/use-pytorch-ssim-loss-function-in-my-model
posted by シンドラー at 01:29| Comment(0) | Machine Learning | 更新情報をチェックする

2021年01月20日

画像の情報量と評価指標

年末年始に更新しようと思っていたのにいつの間にか1月も下旬ですね…。

深層学習に使うデータセットを準備しているとき、解像度の高い画像からランダムクロップでデータ数を増やしたりするんですが、たまたま真っ黒な背景の部分だけ切り取ってきたりした場合は、あまり学習データとして良くない気がします(実は必要なのかもしれませんが…)。

また、ノイズ除去とかで、劣化画像と原画像のペアを用意する場合、平坦な部分などほとんど違いが感じられない場合もあるので、2枚の画像を比べてどれくらい違っているかの指標も必要かと思います。

前者は画素値の分散を計算して閾値以下のものをはじくといったのでも良いかと思いますが、画像の複雑さとしてはエントロピーを計算するのもいいかなと思いますので、その辺を計算してみます。

後者はMAE(Mean Absolute Error)、MSE(Mean Squared Error)、PSNR(Peak Signal-to-Noise Ratio)などが有名かと思いますが、SSIM(Structural Similarity)というものが最近?はよく使われているようですので、それも試してみることにしました。

1. エントロピー[1]~[4]
画像のエントロピーというと、その画素値がどれくらいの頻度で発生しているかを確率としてみて、単色なら0、全色均等に使われている場合は最大値になるような指標です。ヒストグラムを計算して、合計1になるように正規化して後はlogとって掛け算するだけですね。RGBだけではなく、例えばグレースケール画像は除きたい場合などもあると思うので、HSVでも計算してみました。

2. SSIM[5]~[7]
ガウスでぼかして近傍との分散を計算しないといけないみたいで、計算量が多そうです。OpenCVで実装サンプル[8]がそのままありましたので、使わせていただきました。こちらもRGBとHSVで計算してみました。

実行結果

pix2pix用にDIV2K dataset[9]からランダム切り出しして原画像(左)/JPEG圧縮画像(右)のペアを作っています。JPEGで劣化させた場合はブロックノイズのせいでエントロピーは下がるかな、と思いましたが、画像によるみたいですね。

__000840.jpg
ssim_fig_1.png
論文等でよく見る蝶々の一部ですね。拡大しないとあまり違いが分かりませんが、類似度は高くないようです。

__000480.jpg
ssim_fig_2.png
空の一部?です。割と違っているように見えますが、PSNRもSSIMも高いですね。こちらはJPEG圧縮で減色状態になったのか分散はほぼ一緒ですがエントロピーが結構下がっています。類似度が高くてもエントロピーに差があるので学習に使う、というのもありかもしれません。

深層学習ではロスの計算にL1LossやMSEが良く使われていますが、SSIM使うとどうなるのか試してみようかなと思います。

[1] 平均情報量 Entropy
[2] 平均情報量の性質(3章)(pdf)
[3] 画像のエントロピーをpythonで求めてみた
[4] 医用画像工学実験 -画像の情報量 編- (pdf)
[5] SSIMとは何か?その3
[6] 画質評価アラカルト
[7] Googleが薦める「Jpegの品質は85」を検証する
[8] OpenCV Tutorial Video Input PSNR SSIM
[9] DIV2K dataset
posted by シンドラー at 02:59| Comment(0) | Image Processing | 更新情報をチェックする

2020年11月29日

lightweight GANのテスト

もう11月も終わりなので、今回もテストしただけの更新です。GANは面白いのですが、なかなかうまくいかないことも多いです。メモリがたくさん必要とか学習に時間がかかるという問題もあるので、その辺を改良したのがlightweight GAN[1]です。githubにコードもありますし、pipでインストールできるので使う分には画像をディレクトリにおいて学習するだけなので簡単です。

SDNET2018[2]の画像で異常データだけで学習させた結果は下記のようになっています。SDNET2018は3種類のデータがあるのですが、1種類だけで学習させて最後でちょっとだけ他の2種類も混ぜて学習させました。なのでまだ学習中のようなものですが、それなりにひびが入った画像が生成できているように見えます。

データセットにはひび入っているかな?と素人が見ても分かりにくい画像も混ざっていますので、ひびがはっきり見える画像だけ選別して学習させるともっと良さそうな気もします。

単純にGANで増やしても異常のバリエーションが増えるわけではないみたいですが、データ増強の方法の一つとして考えれば別に問題ない気もしますがどうなのでしょうか。

実行結果
202.jpg

基本的なGANでSDNET2018の画像生成をしたときはあまりうまくいかなかったので、画像を用意して動かすだけでこれだけ生成できるのは凄いと思います。

[1] https://github.com/lucidrains/lightweight-gan
[2] https://digitalcommons.usu.edu/all_datasets/48/
posted by シンドラー at 16:29| Comment(0) | Machine Learning | 更新情報をチェックする

2020年10月15日

EDARのテスト

2カ月に1回更新になってしまっている今日この頃です。最近GANの勉強していますが面白いですね。今回はGANではなく通常?のネットワークです。

動画を見ていると結構ノイズが入っているので、動画のノイズ除去を試してみることにしましたが、非力なPCでは動画を扱うのは厳しいので、静止画像のノイズ除去を行うネットワークを試してみることにします。mp4のエンコードアルゴリズムは調べていないですが、画像を見る限りjpegと同じようなノイズが載っているような気がしますので、EDAR[1]を使ってみることにしました。

ConvとResidual Blockを重ねただけの分かりやすい構成ですが、結構きれいにノイズが取れる気がします。

1. データセットの準備
高画質な動画を探してきて、ffmpegを使って元の動画像と同じビットレートの動画に変換したもの(教師画像)と、そのビットレート×0.3に下げた動画像(入力画像)を用意して、1動画から2000枚のペア画像を切り出して、さらに320x240の画像に分割して枚数を増やしました。EDARは48x48のパッチサイズで切り出しますので、320x240からランダムクロップ+ランダム回転(90度毎)で実際には学習します。元が4K動画とかなので400万枚ぐらいの画像になっていますね…。

2. EDARの改造
元のプログラムは教師画像のみを与えて、10~40%ぐらいのクオリティでjpeg圧縮した画像を入力画像にして学習するようになっていたので、改造して入力画像と教師画像をディレクトリから読み出すようにしました。また、テスト(動画のノイズ除去)は動画を渡したいので、OpenCV、ffmpeg、pydubを使って、入力動画→動画と音声に分割→動画から画像を切り出し→ノイズ除去→動画に結合→音声を結合→出力動画ができるようなプログラムを作成しました。

実行結果

サンプルとして、あまり良くないかもしれませんが[2]の灯台画像を使用させていただきました。

jpegexample_06.png
圧縮画像(圧縮50%, 20%, 10%のはずですがサイトの画像なので違うかもしれません)

edar_org_450.png
[1]で用意されていた450エポック学習済みの重みを用いた場合

edar_20.png
自前で用意した画像で20エポック学習したものを用いた場合

[2]の無圧縮原画像と比べると芝生の辺りの高周波成分はちょっと潰れてしまっていますが、10%まで圧縮してブロックノイズが結構載っていても復元できているような気がします。

次は同じようなFusion-Netを試すか、CycleGANでノイズ除去とかも試してみたいところです。

[1] https://github.com/developer0hye/EDAR
[2] 深層学習を使用した JPEG イメージのデブロック
posted by シンドラー at 22:23| Comment(0) | Machine Learning | 更新情報をチェックする

2020年08月23日

PyTorchのC++での利用

油断すると月1更新をさぼってしまいますね。今回はWindows上でC++使ってPyTorchを利用するテストです。以前試したTensorflowをWindows上のC++で動かすのは結構大変(今は簡単になっているかもしれませんが)だったのですが、PyTorchは簡単ですね。

1. ダウンロード・解凍
[1]からダウンロードします。図の通り、PyTorch 1.6.0、C++/Java、CUDA 10.2版を使用しました。CUDA10.02とcuDNN(必要?)は事前にインストールしています。

pytorch_cpp_002.png

解凍するとincludeなどライブラリとして必要なものが入っています。

2. サンプルコードのビルド
基本的には[2]の通りにやっていけば問題ないかと思います。DCGANのサンプルを動かしてみます。今回はCMakeとVisual Studio 2019を使用します。まずはtestフォルダにdcganというフォルダを作成し、dcgan.cppとCMakefile.txtを作成します。

CMake-GUIを起動して上記のフォルダを指定すると、Torch_DIRの指定が必要になります。libtorch/share/cmake/Torchを指定するといいようです。

pytorch_cpp_001.png

後はVisual Studioを起動してビルドすれば実行できるはずです。

3. 別プロジェクトでのテスト

どちらかというと学習済みデータを使った推論をC++側でやりたいことが多いと思いますので、今回はOpenGLのプログラムのテクスチャ貼替をやってみました。libtorchを動かすために必要なincludeファイルやlibファイルは、上記のCMakeで生成されたプロジェクトのものを参考にします。

学習済みモデルの使い方としては、モデルの構築とパラメータ読込み、forwardを使って推論して、結果を取り出すということです。

モデルの構築(略)とパラメータ読込み(最初に1回)
pytorch_cpp_003.png

画像生成
pytorch_cpp_004.png

実行結果


16枚の28x28の手書き文字画像を生成してテクスチャとして貼り付けています。これぐらいのサイズなら余裕でリアルタイムで動きますね。

[1] https://pytorch.org/
[2] https://pytorch.org/tutorials/advanced/cpp_frontend.html
posted by シンドラー at 14:06| Comment(0) | Machine Learning | 更新情報をチェックする

2020年06月17日

PIFuHDのテスト

Twitterを見ていたら、PIFuHD[1]というものが流れてきましたので、試してみることにしました。Google Colaboratory上で動かせるようになっていてありがたいですね。サンプルそのまま動かして生成された.objの中身を見てみると、テクスチャ座標は出力されていませんでした。せっかくなので前回の手法でテクスチャを付けられないか試してみることにします。

1. UV展開
bff[2]を使用させていただきました。出力されたモデルをそのまま使うと警告が出てずっと終わらなかったので、MeshLab[3]のFilter→Cleaning and Repairingで適当にクリーニングして出力したものをbffにかけると、今度はすんなりUV展開できました。今回コマンドラインから実行したのですが、--normalizeUVsというオプションを付けないと1枚に収まらなかったような気がします。

PIFuHD_001.png

確認として[4]のUVマップ確認画像を使用させていただきました。

2. Mitsuba2[5]用のXML出力
こんなこともあろうかと?前回作成したXML出力のプログラムでは、目標画像を半透明表示する機能を追加して位置合わせができるようにしていました。カメラの位置も学習させるといいかと思いましたが、Mitsuba2だとPerspectiveCameraのパラメータに*が付いていなかったので、学習できないかもしれない(未確認)ので今回は手動です。

PIFuHD_002.png

一応レンダリングすると下記のような感じです。

PIFuHD_003.png

3. Mitsuba2で環境マップとテクスチャの学習
後はほぼ前回のhead.htmlと同様の処理ですが、違う点としてはテクスチャの学習だけではなく、環境マップも同時に学習しています。

# 微分したいパラメータを除いて、すべてのパラメータを破棄する
params.keep(['material_000.reflectance.data','my_envmap.data'])

実行結果

1000回学習した結果は下記のような感じです。まだ誤差は下がっているようなのでもうちょっと回しても良かったかもしれません。自前のGPU1枚しかない環境なのでSPP数を上げることができず、あまりきれいには学習できていません。後、視野角が小さい方が画像とモデルの一致率が高かったので、結構小さくしています。そのせいで環境マップの利用領域がものすごく狭くなっているので、こちらは殆ど学習できていません。
PIFuHD_005.jpg

学習後のテクスチャです。ジーパンっぽいところは分かりますが、他はほぼ白ですね。
PIFuHD_006.jpg

MeshLabで確認すると、手の部分も色がおかしいので、なかなか難しいです。環境マップも同時に学習しているので、肌色はそちらが学習してしまったのかもしれません。
PIFuHD_004.png

というわけで、前後2枚の写真があれば解像度は微妙ですがテクスチャ付きのモデルも作れそうかなといった感じでした。

以下Mitsuba2のコード
mitsuba2_pifuhd.html

[1] AIで1枚の人物写真から高精細3Dモデル作成 Facebookなど開発 - ITmedia
[2] Boundary First Flattening
[3] MeshLab
[4] UVマップ確認用テクスチャーの制作 - リカのきままなブログ
[5] Mitsuba 2
posted by シンドラー at 21:35| Comment(0) | Machine Learning | 更新情報をチェックする

2020年06月08日

Zoom配信の前処理について

新型コロナウイルスの影響でオンライン会議などが増えましたね。Zoomを使った会議などにそのままカメラを使うのも面白くないので、OpenCVで前処理をしてから配信を試してみることにしました。今回はFaceRig→OpenCV(C++)→OBS Studio→Zoomという流れです。 OBS StudioはPythonのScriptが使えますので、多分OpenCV(C++)は必要ないのですが、使い慣れた環境でやりたかったので間に挟みました。

1. FaceRigの使用
Steamで購入しました。Webカメラでもいいです。FaceRigの配信を他アプリから使用するためには、FaceRigのVirtual Camera Driverをインストールする必要があります[1]。FaceRigを起動してブロードキャストすればいいです。

2. OpenCV(C++)
前処理は何でも良かったのですが、今回はOpenCV 4.3.0 + CUDA 10.2 + CuDNN 7.6.5でDNNを使用して顔検出して笑い男を貼り付けるよくあるやつを試してみました。DNNを使った顔検出[2]とマスク処理[3]と笑い男画像[4]を使用/参考にさせていただきました。FaceRigのバーチャルカメラは普通のWebカメラのように認識できますので、VideoCaptureでOpenすれば画像を取得できます。

3. OBS Studio
最初はDirectShowを使ったりGStreamerを使ったりしてOpenCVからZoomに配信できないかなと調べてみたのですが、なかなか難しそうだったのでOBS Studio[5]を使うことにしました。使い方は[6]を参考にさせていただきましたが、こちらもVirtual Cameraのプラグインをインストールする必要があります。cv::imshowで表示したウィンドウをOBS Studioでキャプチャして、VirtualCamのOBS-Cameraに配信します。

4. Zoom
最終的にOBS-Cameraから画像が流れてきますので、Zoomのビデオにはこれを選択します。

実行結果


一応頭の上に確信度を表示していますが、一般外国人男性のアバターだとかなりの確信度で顔検出できていますね。思い付きで試しただけなどで顔隠し以外の前処理が思いつかないですね…。

[1] ZoomでFaceRig使ってバーチャル参加する方法
[2] OpenCV 4.1 DNNによる顔検出
[3] OpenCV 2.0 でマスク処理してみた。
[4] 笑い男マーク
[5] OBS Studio
[6] OBSをバーチャルカメラとして出力してZoomやTeamsで映像ソースとして認識させる方法
posted by シンドラー at 01:24| Comment(0) | Image Processing | 更新情報をチェックする