【webアプリ】画像の背景透過処理を行うwebサービスを作ってみた

# ➊ はじめに
2021年12月4日、今年も残すところ、あと少しになりましたね🥺ぴえん

NTTテクノクロスの中の人『[PoodleMaster](https://qiita.com/PoodleMaster)』です。今年も[アドベントカレンダー](https://qiita.com/advent-calendar/2021/ntt-tx)にチャレンジ(コラボ)させて戴きました。

今年は、個人的に『[✂AIを利用した画像切り抜き](https://qiita.com/PoodleMaster/items/12003900d6b362f4fe58)』や『[☁Herokuデプロイ方法](https://qiita.com/PoodleMaster/items/32ab70b14940ada5d8a9)』などを学びました。~~面倒くさい~~ せっかくなので、これらを利用して「**webアプリとしてサービス化**」したいと思います。

# ➋ やること
出来上がりは、こんな感じのwebアプリができるイメージです。
> お試しはこちら 👉 https://app-bge-web.herokuapp.com/
> ※無料枠なので、初回立ち上げまでに時間(40秒くらい)がかかります。お茶でも飲んでお待ちください🍵サーバ側で画像の保存はしていません。安心してお楽しみください。

<img width="600" src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/eb5714a4-21b7-bf18-ab75-ce061b08016c.gif'>

# ➌ 全体概要
全体のイメージはこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/645288ef-e5ac-6ebf-3445-7b091949ea2e.png)

# ➍ 画像切り抜き
画像切り抜きは、**[Rembg](https://github.com/danielgatis/rembg)**というOSSを使用します。こちらは、**Alpha Matting**という手法で実行します。**Alpha Matting**は、「ソース写真」と「**Trimap**」と呼ばれる中間データを用いて切り抜き処理を行います。「**Trimap**」のイメージは以下になります。詳しくは👉 [こちら](https://qiita.com/PoodleMaster/items/12003900d6b362f4fe58#alpha-matting%E3%81%A8%E3%81%AF) へ。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/f471f484-6554-a441-1991-f96a43728849.png">
「`T`, `R`, `I`」のモデルは、ボクです😅

# ➎ ソースコード概説
作成したソースコードの説明を簡単にしておきます。

```text
background-erase-web
        ├─ Dockerfile ・・・ Dockerイメージ作成用設定ファイル
        ├─ server.py  ・・・ バックエンド側(rembgコール)
        ├─ Exec.js    ・・・ 画像データをバックエンドへ送信。バックエンドからの画像表示。
        └─ index.html ・・・ HPの骨格。
```

## (1) index.html
Ajax通信をJQueryを使用して簡単に実装するため、CDN使用の宣言をします。

```html:index.html&nbsp;(snippet)
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
```

## (2) Exec.js
Ajax通信を行うため、jQueryの$.ajaxメソッドを使用します。通信が成功しデータが返却されるとコールバック関数が呼ばれるので、そこで返却データの解析、DOMの処理を行います。

```javascript:Exec.js&nbsp;(snippet)
  // JQueryによるPOST処理
  // javascript→pythonへ画像データ転送
  var textData = JSON.stringify({ b64_pic: send_pic });

  $.ajax({
    type: "POST",
    url: "/output",
    data: textData,
    contentType: "application/json",

    // 非同期通信が成功したら実行される
    success: function (data) {
      // 返却jsonデータからparseしてデータ取り出し
      response = JSON.parse(data.ResultSet);
      ・・・
    }
  });
```

## (3) server.py
サーバはFlaskを利用します(Flaskについての説明は割愛します)。POSTされたデータを受信します。受信データはBase64でエンコードされているので、デコードします。デコードしたデータをrembgへInputしバックグラウンドを削除します。その結果を、Base64でエンコードして返却します。

```python:server.py&nbsp;(snippet)
@app.route('/output', methods=['POST'])
def output():
    # json形式でPOSTされたデータを受け取る
    b64_data = request.json['b64_pic']

    # base64デコード
    tmpdata = b64_data.split(',')
    bindata = base64.b64decode(tmpdata[1])  

    # rembgでバックグランドを削除
    contents = remove(bindata,
                      alpha_matting=True,
                      alpha_matting_foreground_threshold=240,
                      alpha_matting_background_threshold=10,
                      alpha_matting_erode_structure_size=10,
                      alpha_matting_base_size=1000)

    # base64エンコード
    tmpdata = str(base64.b64encode(contents))
    tmpdata = tmpdata[2:-1]

    # 返却データ設定
    data1 = "data:image/png;base64," + tmpdata
    data2 = "OK"

    return_data = {"result_pic": data1,
                   "result_result": data2,
                   }

    return jsonify(ResultSet=json.dumps(return_data))
```

# ➏ 実行結果
こんな感じで、切り抜きができます。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/0c41189d-7f1a-2f16-0725-8897293e2dc7.png">

# ➐ その他工夫点
**rembg**を起動させる場合、以下の2点において初回のみ時間がかかる箇所があるので、画像処理プロセスに入る前までに、予めダウンロードやコンパイルをしておきます。**Heroku**で起動させる場合、特に下準備をやっておかないと、リクエストに対して応答をモタモタしていると、応答なしと判断され勝手に再起動してしまいます。

> 以下、2点に注意してDockerイメージを作成すれば、初回リクエスト時のみ処理に時間がかかるということはなくなります。

## (1) u2netモデルの先行DL
rembgがremoveメソッドをコールする際に(初回リクエスト時)に[**u2netモデル**](https://github.com/xuebinqin/U-2-Net)を使用するためダウンロードをしにいきます。u2netモデルは、168MBとサイズが大きいためダウンロードの待ち時間が発生してしまいます。このような待ち時間を発生させないため、予めモデルをダウンロードしておけば再ダウンロードはしないので、時間短縮が望めます。格納場所はLinuxだと「/home/username/.u2net」、win10だと「C:\\Users\\username\\.u2net」です。

https://drive.google.com/file/d/1IG3HdpcRiDoWNookbncQjeaPN28t90yW/view?usp=sharing

## (2) pymattingの先行upgrade
[pymatting](https://github.com/pymatting/pymatting)をソースからインストール(git clone)した場合だと、環境によりリクエスト時にモジュールを再コンパイルする場合がありました。その場合、画像処理プロセス中にコンパイルの時間がかかってしまいます。そうならないため、以下コマンドを予め実行しておきます。

```bash:pymattingの先行upgrade
pip install --upgrade pymatting
python -c "import pymatting"
```

# ➑ おまけ
## (1) 公開ソースコード
今回実施したソースコードは**GitHub**にUPしていますので、詳細はこちらを御覧ください。多少構成が違っているのはご了承ください。MITライセンスで公開していますので、ご自由にお使いください。

https://github.com/PoodleMaster/background-erase-web

## (2) 使い方
例えば、毛がフサフサなワンコであっても、キレイにオブジェクトを切り抜きができれば、Tシャツや携帯電話ケースに、こんな感じにデザインすることができます！
某)オオタニサンＴシャツも、某)変なおじさんモモヒキも、簡単にデザインできます😁
<img width="300" src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/318e2c30-abca-5425-c6a4-4b95e7842ec2.png'> <img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/55d6b99f-7654-0310-d32d-78efc36f18f0.png">

# ➒ 以上
あぁ………気づけば、今年も残りあと僅かですね………
今年は『[グチャグチャになった「Ubuntu on WSL2」のやり直し方](https://qiita.com/PoodleMaster/items/b54db3608c4d343d27c4)』が小バズりまして、みなさまのお役に立てたようで、とても嬉しかったです🙌マジョリティでベーシックものはバズりやすいのかもしれませんが、これからもマイノリティ、マジョリティ関係なくチャレンジしていきたいと思いますので、引き続きよろしくお願い致します🙇‍♂

お疲れ様でした😊

# 参考
記事内で使用した主なライブラリやサービスなどは、以下のサイトを参考にしております。

|使用した主なライブラリやサービスなど|Licence|
|:-:|:-:|
|[rembg](https://github.com/danielgatis/rembg)|[MIT Licence](https://github.com/danielgatis/rembg/blob/master/LICENSE.txt)|
|[U2net](https://github.com/xuebinqin/U-2-Net)|[Apache License Version 2.0](https://github.com/xuebinqin/U-2-Net/blob/master/LICENSE)|
|[pymatting](https://github.com/pymatting/pymatting)|[MIT Licence](https://github.com/pymatting/pymatting/blob/master/LICENSE.md)|
|[JQuery](https://openjsf.org/)|[MIT Licence](https://github.com/jquery/jquery/blob/main/LICENSE.txt)|
|[Heroku](https://jp.heroku.com/)|[Free(無料枠)](https://jp.heroku.com/pricing)|
|[GitHub](https://github.com/)|[Free(無料枠)](https://github.com/pricing)|