【De-Rendering】画像内のテキストを再編集する研究の紹介

[2021年のディープラーニング論文を1人で読むAdvent Calendar](https://qiita.com/advent-calendar/2021/solo-dl-papers)21日目の記事です。今日読むのは昨日に続き日本人の論文で、「**ディープラーニングを使った画像内のテキストの再編集**」です。通常、画像内に埋め込まれたテキストはラスターデータなので、内容の変更が容易ではありません。そこで、「テキストの色やフォントといった」ラスタライズ前のパラメーターを推定し、画像内のテキストをDe-renderingしようという挑戦的な試みをしています。**GANは使っていません**。

サイバーエージェントと九州大学のチームによるもので、ICCV2021に採択されています。論文は読解が難しい部分があるので、サンプルやデモアプリで遊ぶ程度にしておいたほうが楽しめると思います。

* タイトル：**De-Rendering Stylized Texts**
* URL：https://openaccess.thecvf.com/content/ICCV2021/html/Shimoda_De-Rendering_Stylized_Texts_ICCV_2021_paper.html
* 出典：Wataru Shimoda, Daichi Haraguchi, Seiichi Uchida, Kota Yamaguchi; Proceedings of the IEEE/CVF International Conference on Computer Vision (ICCV), 2021, pp. 1076-1085
* コード：https://github.com/CyberAgentAILab/derendering-text
* プロジェクトページ：https://cyberagentailab.github.io/derendering-text/

## 画像内のテキストを再編集したい
![21_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/a684e595-a472-de91-51eb-802b4bae55b0.png)

入力画像に「I WANT YOU FOR U.S. ARMY」とありますよね。「WANTだけ赤文字にしたい」「ARMYをNAVYに変えたい」、これをテキストの再レンダリングを通じて行おうというのがこの論文の試みです。

この論文は**テキストの描画はニューラルネットワークではやっていません**。「NAVY」と入力するときは、通常の画像処理でフォントをレンダリングしてテキストを載せています。あくまでニューラルネットワークでやっているのは、**フォントの種類・サイズの特定、位置の特定、スタイルの特定、文字がない場合の背景の修復**など、再レンダリングのためのパラメーターの推定です。全てをEnd-to-Endにやる生成モデルと大きく異なるのはこの点です。

### 3つの問題を同時に解かないといけない
これは「**de-rendering**」問題と呼ばれますが、実は3つの問題を同時に解いていることと考えられます。

1. OCR
2. 背景のInpainting
3. スタイルの属性の認識

OCRは文字通り「画像→テキスト」への変換です。例えば「WANT」の部分を赤色に変えたい場合、元の文字がわからないと「赤色のWANT」をレンダリングできないからです。

背景のInpaintingは、これまでの[アドベントカレンダーでもInpaintingの論文は紹介](https://qiita.com/koshian2/items/7184cfa790e1713842f5)してきましたが、文字の置き換えをするときに、テキストが描かれていた部分をそれっぽく復元する必要があります。例えば「ARMYをNAVYに置き換えする」場合、「ARMY」と描かれていた部分に対し、背景の復元をする必要があります。

スタイルの属性の認識は、例はフォントサイズや種類、太さなどでしょう。「NAVY」と描く場合でも、元のフォントがわからなければそれっぽく描けません。

## アウトライン
最初に、ネットワークの概略図、詳細なレイヤー構成、損失関数を見ていきます。この論文とにかくマルチタスクなので、**モジュールが大量**にあります。一個一個突っ込んでみていくときりがないので、アウトラインだけ追います。

### ネットワーク構成
![21_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/7b3662f3-aca0-8e0f-50b4-5863899fc7f2.png)

![21_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/129bae66-6aa8-31af-e356-d3f0332f2658.png)

### 損失関数
```math
\mathcal{L}=\mathcal{L}_{\rm{OCR}}+\mathcal{L}_{\rm{inpaint}}+\lambda_{\rm{font}}\mathcal{L}_{\rm{font}}+ \sum_k\Bigl[\lambda_{\rm{alpha}}\mathcal{L}_{\rm{alpha}}^k+\mathcal{L}_{\rm{visibility}}^k+\mathcal{L}_{\rm{param}}^k\Bigr]
```

性能を上げるために複数のロス（L1 Loss, Adversarial Loss, Perceptual Loss）を加算することはよくありますが、どちらかというとこの**損失関数の各項はタスク別のロス**です。$k$は文字に対するエフェクト（塗りつぶす、境界線を描く、影を落とす）の種類のパラメーターです。

## 最初はOCRのモデルから
本論文は複雑なパイプラインのモデルですが、最初は**OCRのモデル**から始まります。OCRのモデルは[CharNet](https://arxiv.org/abs/1910.07954)を使っています。CharNetの論文からの図です。

![21_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/55e17614-64b3-e18b-efd3-c3f5740c0cf7.png)

CharNetではセグメンテーションの要領で、「文字のあるエリア」と「文字（A-Z）」を推定しています。Boxと文字が検出できれば特にモデルの縛りはないそうです。

CharNetではHourglassNet-likeな構成をしているため、[コード](https://github.com/CyberAgentAILab/derendering-text/blob/e129bc46ff0163eee308becf85863afc57fb13cd/src/models/vectorization.py#L32)のOCRのバックボーンのクラス名は「HourGlassNet」となっています。

## 文字と単語の検出を行う
CharNetの図では文字単位での検出を行っていますが、本研究では**単語と文字のBounding Box**を検出します。ここは物体検出のHeadが複数あるイメージです。[コードでは、単語の検出がWordDetector、文字の検出がCharDetector](https://github.com/CyberAgentAILab/derendering-text/blob/e129bc46ff0163eee308becf85863afc57fb13cd/src/models/layers/inner_ocr.py#L8)となっています。単語のBounding Boxを$b^t$、文字のBounding Boxを$b^c$としたとき、Bounding Box全体を

```math
\mathcal{B}(x)=\{b^t, b^c\}
```

と表現します。ネットワーク構成の図の「Character」と「Location」の部分が終わりました。ここだけ見ると物体検出ですね。

![21_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/08c439a5-59c2-fa9e-c8d2-5807242f4fd4.png)

## Bounding Box内のフォントの種類、エフェクトの分類
次にBounding Boxの中について見ます。OCRの部分で単語と文字のBounding Box$b^t, b^c$が検出されました。そのBounding Box内の特徴量を$e_b^t, r_b^c$と表します。

レイヤー構成を見ると、**Bounding Box$b^t$に対する特徴量$e_b^c$は空間情報を失っている**のがわかります。実際にInputが$e_b^t$であるモデル「Font」「Effects visibility」「Effects params」を見ると、入力サイズが「256×1×1」となり、Global Average Poolingを適用したようなshapeになっています。

![21_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/a33b764e-13c6-a705-afa0-91cba9b70bbb.png)

ネットワーク全体図の$e^t$から出ている3分岐が予測しているのがこれです。全体図の$e^t$と、レイヤー構成のInput$e_b^t$は同じものを指していると考えられます。

Bounding Box内の各種パラメーターの推定についてのモデルは、コードでは[こちら](https://github.com/CyberAgentAILab/derendering-text/blob/dbfa9af5034098a24c3829f5b95c6fd5b82e84dc/src/models/layers/estimator.py)になります。

### フォントの分類（Font）
わかりやすい「Font」の部分から見てみましょう。これはただの**分類問題**で、100個の予め与えたフォントの中から、どれにマッチするのか推定するレイヤーです。この論文では無限のフォントの中から探索をすることはせず、**Google Fontの中から予めランダムにダウンロードした100のフォントに対する推定**をしています。これは後述のテキストをプリレンダリングすることと関連しています。

### エフェクトの視認性（Effects visibility）
エフェクトとは文字に対する影や境界線のことです。そもそも文字のエフェクトとは何でしょうか。

![21_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/dfa96eeb-fe56-ba2b-e4f8-ca756ea42473.png)

この論文では**塗りつぶし**（Fill）、**影**（Shadow）、**境界線**（BorderあるいはStrokeと表記）の3つのエフェクトを検討しています。塗りつぶしについては特にパラメーターは推定しません[^1]。影と境界線に対しては推定を行います。

[^1]: 文字色はどうしてるんでしょうかね

「Effects visibility」のモデルでは、これらの**2つのエフェクトに対して、使うか使わないか**を$v_k\in\{0, 1\}$で推定しています。出力層の次元の2が何を指すのか理解しづらかったのですが、「影も境界線もつける」というのを許容したモデル設計となっています。ここは[ロスの計算のコード](https://github.com/CyberAgentAILab/derendering-text/blob/dbfa9af5034098a24c3829f5b95c6fd5b82e84dc/src/modules/trainer/loss.py#L137)を見たら理解できました。

### エフェクトのパラメーター（Effects params）
「影」や「境界線」に付随するパラメーターです。「Shadow offset」は影をx, yそれぞれどのぐらいずらして表示するか、「Shadow blur」は影に対しどのぐらいぼかしをかけるのかを表すパラメーターだと考えられます。

Border weights（Stroke parameter）の次元5が何を表しているかよくわからなかったのですが、[ロスを見ているとこの部分はクロスエントロピーで評価](https://github.com/CyberAgentAILab/derendering-text/blob/dbfa9af5034098a24c3829f5b95c6fd5b82e84dc/src/modules/trainer/loss.py#L212)しているので、何らかの確率を表すのではないでしょうか。

### Bounding Box内のまとめ
Bounding Box内では、フォントの種類、エフェクトの視認性、エフェクトのパラメーターを推定していました。これら1個1個を紐解くと、**分類と回帰のマルチタスク**であることがわかります。

例えば、フォントの種類は単純な分類問題ですが、これにフォントサイズの推定がもし加わったら（流し見していた限りですが、フォントサイズに対するコードもありました）回帰になります。回帰か分類かは、出力層の活性化関数の選択によって決まるので、ここらへんの柔軟性は高いです。

![21_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/fb2d8793-4573-dc92-ef99-240ee9d251c2.png)

## 背景のInpainting
テキストの置き換えには、テキスト挿入前の背景画像を推定する必要があります。これはまさに**Inpaintingの問題**です。Inpaintingの問題は、概念的には次のような擬似コードで表されるモデルです。

```python
def inpainting_model(image_with_text, text_mask):
    predict_background = inpainting_model([image_with_text, text_mask])
    return predict_background
```

ここで```image_with_text```は入力画像をそのまま、```text_mask```はOCRのモデルで求めたセグメンテーションマップを使います。「CharNet」の図では、文字のあるエリアのセグメンテーションマップを推定していることを思い出してください。


## Colorはテキストレンダリングからの色の選択
ややこしいのは「Color」の部分です。最初読んだときは理解できなく、読み込んでも「ピクセル単位のアルファブレンドかな」と誤解してしまいました。前提条件として、**フォントの選択やテキストの描画は微分不可能**です。ここを微分可能な形でプリレンダリングしておくことから始まります。

### テキストのプリレンダリング
フォントの選択やテキストの描画は微分不可能です。そこで**各フォントの各文字に対する、テキストのラスター画像を事前に作っておく**というということをしています。具体的にはこのような画像です。

![21_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/431735d6-77de-199c-72ea-95ad2d8b6de8.png)

縦軸にフォントの種類、横軸に文字をおきます。今フォントと文字の推定が終わっているので、グリッド状に同時確率を計算します。図で表すと次の通りです。

![21_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/40045c49-5434-d6f5-b723-1d6efb8b2ef8.png)

つまり、$i$番目のフォントに対する$j$番目の文字の確率が、$P(\Theta')A_{\Theta'}(i, j)$です。論文ではこれもアルファと読んでいますが、意味合い的にはAttention Mapに近いと思います。

### 色の選択
ネットワーク的には、まずエフェクト$k$に対するアルファ値（これはアルファブレンディングの意味でのアルファです）を先に出力します。ここの**部分の読解が非常に難しく、論文でAttention Mapの意味でのアルファと、アルファブレンディングのアルファをほぼ同じ文章内で書いてあるので処理が全く見えてこない**のです（これも最初は同一のものだと勘違いしてました）[^2]。2,3時間ぐらい考えましたが、半分わかったようで結局わからなかったので、式だけコピペしてスルーします。

[^2]: 個人的にはこういう複雑な処理は擬似コードで図示したほうが助かります。

```math
\begin{align}\hat{\alpha}_k&=D_{\alpha,k}(e(x)) \\ \mathbf{y}_{i,j,k}&=\frac{\mathbf{c}(i,j)-(1-\hat{\alpha}_k(i, j))\mathbf{c}_{\rm{bg}}(i,j)}{\hat{\alpha}_k(i,j)} \\ \hat{\mathbf{c}}_k&=\arg\max_{\mathbf{y}_{i,j,k}}P(\mathbf{y}_{i,j,k}) \end{align}
```

「$i, j, k$の周りで総当りして、アルファブレンドの逆変換でテキストの色を計算して、確率を最大化するような計算をする」的な感じだと思いますが、処理が全く見えてきませんでした。ここが論文のコア手法の一つだと思われるだけに残念です。

![21_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/f954af7d-27d0-bd73-f616-66cbdd144f24.png)

個々の損失関数は細かい実装まで見ていくとあまりにしんどすぎるので、省略します。気になった方はコードを参照してください。

## Feedback Refinerはなにをやっているのか
この後Feedback Refinerを通していますが、これはテキストの**再レンダリング**です。これは訓練時、テキストの内容を編集したい以外も再レンダリングします。精度を上げるためのモジュールです。

直感的にはCycle GANのCycle Consistency Lossと似ています。Feedforward Parserで「フォントの種類やエフェクトの詳細」を推定できましたが、**推定できたパラメーターでテキストを再レンダリングした画像と、入力画像の差を減らしたい**のです。これによってパラメーターの推定の精度を上げることができます。

今Feedfoward Parserによってパラメーター$\mathcal{\Theta}$を推定できたとしましょう。入力画像を$x$、パラメーター$\mathcal{\Theta}$のもとでFeedback Refinerによって再レンダリングされた画像を$\mathcal{R}(\mathcal{\Theta})$としたとき、

$$\min_{\mathcal{\Theta}}|\mathcal{R}(\mathcal{\Theta})-x|$$

という最適化を別に行います。

![21_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/7eb1935d-2c68-3662-1149-8e738a68014c.png)

実際にこのRefineの役割が、どの程度効いているかというと結構効いています。上の表の「Feedfoward」が「w/o refinementです」。ブックカバーではPSNRが0.8、広告では0.6上昇しています。下の表はRefinementの中身の要因分析で、色の復元を外すと結構落ちるみたいですね。逆に出現頻度がそこまで高くなさそうな「Shadow」や「Border」は寄与度が低めになっています。

## テキストのスタイル変換もできる
テキストの色やフォントを推定しているため、パラメーターをいじれば文字内のスタイル変換は容易です。応用例は広そうです。

![21_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/cc841b86-1624-d32e-0a2e-71965bc39d49.png)

## まとめと感想
この論文は「**フォントの種類や色、文字のスタイル**」という**ラスタライズ前のパラメーターを推定**することで、ラスター画像を「De-rendering」するという挑戦的な内容でした。結果が有用で、そのうちデモアプリが実装されるらしいので、それで遊んでみたいと思います。

論文自体はとにかく読むのが疲れる内容でした。他の記事と比べて紹介が雑になったのは「Color」の部分がわからなくて力尽きてしまったからです。「Color」以外のモジュールはなんとか読解できたのですが、本文読んでいるだけだとアウトラインをつかみにくく、複雑なパイプラインの全体像が把握しづらかったです。「OCR→Bounding Box内」と切り分けて読むことでようやくアウトラインは見えました。Colorの部分が把握したかったのですが、あれでわからないということはおそらく自分の読解力不足でしょう。

コードもモジュールをたくさん組み合わせた複雑なプロジェクトらしく、「これを管理するのはなかなか大変だな」と思ってしまいました。頑張って論文読んだり実装したりするより、デモアプリやサンプルコードで遊ぶ程度にしたほうが楽しめる内容だと思います。

## 告知
このアドベントカレンダーが本になりました！
**https://koshian2.booth.pm/items/3595424**
Amazonでも扱いあります詳しくは👉　https://shikoan.com