PowerAppsで撮影した画像の上にペン入力でお絵かきして保存する

# 概要
- 例えば現場報告アプリで、撮影した画像に○をつけて問題のポイントが分かるようにしたいときがあります。
- Power Appsでは写真を撮影できるのですが、その上からペンで描画できる機能はなく、ネイティブアプリで撮影・描画したものをアップロードするしかありません。
- Power Appsにはペン入力コントロールがあり、これと写真を合成して画像へのお絵かきを実現できないかと考えました。

:::note warn
こちらの記事の方法はおすすめしません。
外部APIを利用して画像を合成しますが、本来の用途ではありません。

以下の記事の参照をおすすめします。
[①PowerAppsで撮影した写真にお絵描きする(外部API無し版)](https://qiita.com/Rambosan/items/5e093e5d111f8c07bb12)
[②AzureFunctionsとPowerAppsでぬり絵アプリを作ってみた](https://qiita.com/Rambosan/items/29ae971e1e7463559b4f)

:::




# 方法の検討
最初はPower AutomateからAzure Functionsを呼び出して合成しようかと考えていましたが、以下の記事を拝見させて頂き、remove.bgと呼ばれるサービスのAPIを使って手軽に実現できそう！と思ったので、自分も記事を参考に試してみました。
[Power AppsとPower Automateで火星に行ってみよう！](https://qiita.com/MiyakeMito/items/1b110cfcf266c0c6af18)

※Power Appsで頑張ればこういうこともできますよ、という検証的な記事ですのでご了承願います。
AzureFunctionsで自前でAPIを作る場合は[こちら](https://qiita.com/Rambosan/items/29ae971e1e7463559b4f)

# 完成品
### ①カメラコントロールの画像とペンを合成
写真を撮影→画像表示→ペン入力で線を書く→合成です。
![20210228_202232.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293667/80992bc9-5328-0996-8c71-c7eacebfe39f.gif)
製造現場で製品の不具合箇所に印をつけるのに活用できそです。

### ②リソース画像とペンを合成
事前にリソースとして用意した画像と、ペン入力画像を合成します。
寿司画像に好き、嫌いを書いて送信。
![MarkingSushi.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293667/c3f0fdc6-039d-cd7e-9cfa-8092213b7b74.gif)
製造現場などで用意された紙に不具合箇所に印を付けるような作業は、事前に用意した画像にマーカーを入れることでデジタル化できるかな？

# 解説
上記の記事を参考にPower Appsとフローを作成します。
ただし今回はurlからではなく、Power Appsで撮影した画像とペン入力画像を送信して合成します。
異なる部分だけ解説してみます。
また、今回は写真とペン入力ですが、同じフローを使えばPowerAppsで撮影した写真(切り抜き)と写真の合成も可能です。

## Power Appsの設定
撮影した画像の上に描画しているように「見せかけるため」コントロールを配置し、画像の合成フローに画像データを渡して実行します。

### ①画像作成画面

1. ペン入力を配置し、width:640、height:480に設定します。(カメラコントロールの標準解像度)
2. 背景(Fill)は透明にします(既定)。
3. その背面に画像コントロールを配置し、Imageには後で設定するCameraDataUri変数を設定し撮影した画像が入るように設定。
4. 次に、画像のX, Y, Width, Heightはペン入力と合わせます(width:640、height:480)
5. これで画像の上に描画しているように見せかけます。
6. 詳細は割愛しますが、別画面でカメラコントロールを配置し、OnSelectに```Set(CameraDataUri,Camera1.Photo);```を設定し撮影したときに変数に画像データが入るようにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293667/3b6d5b5e-9806-7c9f-c8cf-f46e22892923.png)

### ②合成(フロー実行)ボタン

- 合成ボタンには以下のようなコードを設定します。
- フロー名.Run(ペン入力の画像データ(前景), カメラの画像データ(背景))でフローにデータを渡します。
そして、戻り値をMergedImage変数にセット。<br>
※これはフローを作成してから設定します。
- ペン入力のImageはJSON関数とSubstituteでDataUriに変換する必要があります。

```javascript:
Set(
    MergedImage,
    '画像とペン入力を合成'.Run(
        Substitute(JSON(PenInput1_1.Image,IncludeBinaryData),"""",""),
        CameraDataUri
    )
);
```

### ③合成後の画像取得表示
画像コントロールを配置し、imageには以下を設定します。
```Concatenate("data:image/png;base64,", MergedImage.imagedata)```

<br>
## PowerAutomateの設定
フローではremovebgのAPIを使ってペン入力の画像背景を切り抜き、カメラ画像と合成します。


### 背景画像の合成フロー
全体のフローは上記の記事と同じです。
@MiyakeMitoさんの記事に沿ってフローを作成し、その後に以下の改造を行います。
以下のようなフローの完成を目指します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293667/49d5be07-941d-c2db-abdf-8f156c624570.png)


ポイント

- 背景画像はDataUriをバイナリに変換して送信する必要がある。
- HTTPリクエストのContent-Typeはmultipart/form-dataを使用。



### ①フローの2つ目、背景画像をバイナリ化するアクションを挿入
・データ操作→作成アクションを追加し以下の式を設定します。
・これは、APIの仕様上背景画像をバイナリで指定する必要があるためです。
```dataUriToBinary(triggerBody()['HTTP_本文_1'])```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293667/b000ca97-50ba-9c1b-83b9-634734273898.png)

#### ②HTTPアクション、本文は以下のように設定
出力は①の出力です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293667/f5b7e785-406f-4e46-a93b-cafa1a07d969.png)

- 少し難しかったポイントはbg_image_fileの指定です。
- 切り抜く画像である「image_file_b64」パラメータの設定はPowerAppsから受け取ったデータをそのまま指定するだけなので簡単でしたが、背景画像である「bg_image_file」はbase64でなくバイナリ形式で指定する必要がありました。
- 最初はContent-Typeをjsonで指定したのですが、jsonだとバイナリ形式が送信できません。
- APIのマニュアルを見ると、ファイルをバイナリで指定するサンプルコードはHTMLフォームで使用される「multipart/form-data」で送信されていました。

<br>
#### ③Power Automateでmultipart/form-dataを送信するにはどうすればいいのか。

以下の記事を参考にさせて頂きました。
ヘッダーのContent-Typeは空にして本文にJSONで記述します。
このあたりは、Power AutomateではなくLogicAppsのマニュアルには書かれているようです。

https://www.kwbtblog.com/entry/2019/08/29/004341
> HTTPアクションで「multipart/form-data」するには、ヘッダーでは「content-type」を設定せず、本文の中に、送信するデータと合わせてJSONで記述する必要があります。

以下コピー用、filenameは何でもいいと思います。

```json
{
  "$content-type": "multipart/form-data",
  "$multipart": [
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"image_file_b64\""
      },
      "body": @{triggerBody()['HTTP_本文']}
    },
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"size\""
      },
      "body": "auto"
    },
    {
      "headers": {
        "Content-Disposition": "form-data; name=bg_image_file;filename=\"bgimage.png\""
      },
      "body": @{outputs('Convert_to_binary_image')}
    }
  ]
}
```

### 画像の保存フロー
これでPower Appsで合成をクリックするとフローが実行され、合成後の画像が表示されます。
後はこの画像データをPower Automateで任意のドキュメントライブラリに保存します。
以下のテンプレートを使うと簡単です。
https://japan.flow.microsoft.com/ja-jp/galleries/public/templates/fe971b57c1994482b565ccfce936900d/upload-a-photo-to-sharepoint-from-power-apps/

# 画像の大きさについて
2つの画像を合成する際は、2つのサイズを同じ又は縦横比が同じにすればズレなく合成可能でした。
ペン入力はコントロールのサイズが画像サイズとなるため、このサイズを調整して縦横比を合わせます。
縦横比が異なると描画した線の位置がずれるので注意が必要です。
画像サイズは以下のように設定しました。

■カメラコントロール画像の場合
・カメラコントロールの出力画像は640×480なので、ペン入力とカメラ画像を表示する画像コントロールは両方640×480のサイズに設定します。
・画面からはみ出す場合は縦横比を維持したまま縮小しても大丈夫です。
・ペン入力の下から60pxは描画ツールバーとなっており、描画できるエリアは赤枠の部分になります。ただし、出力される画像は描画ツールバーの部分を含んだ「コントロールのサイズ」になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293667/f389d693-e54e-74bf-e017-76c78f3720d9.png)

■リソース画像の場合
リソース画像の場合は、ペン入力と画像コントロールのサイズを元画像のサイズか縦横比を維持したサイズにします。
因みにjpegとpng形式のみ対応です。
画像の追加コントロールではフォーマットが異なりAPIに渡せませんでした？

## その他

- ペン入力の背景は既定で白なので、当然ですが白色で描画するとエラーとなり切り抜きできません。
- remove.bgはディープラーニング等の高度なAI技術を使用しており背景が複雑な画像でも高精度に切り抜けるようです。　ペン入力のような背景白の単純な切り抜きには性能の無駄遣いかも知れませんｗ<br>

<font color="Red">※→すみません、後で調べたらペン入力から出力された画像の背景は既に透過になっており、切り抜きサービスは全く必要ありませんでした。
今回の場合だと単純に画像を合成するサービスがあれば十分です。自分で作る場合もかなり単純なコードで実現できますね・・</font>


# あとがき
コミュニティプラン/プレミアムプランが必要なことに加え、removegbのAPIは無料枠だと50回/月までしか使えません。
セキュリティ等も考えると業務への適用は難しいかも知れません。Azure Functionsでコードを書いたら公開してみたいと思います。
→https://qiita.com/Rambosan/items/29ae971e1e7463559b4f

画像に描画というのは現場系のアプリでは必要だと思います。標準機能として実装してほしいですね。
ideasで有望なのはこのあたりでしょうか。　
https://powerusers.microsoft.com/t5/Power-Apps-Ideas/Draw-on-an-uploaded-photo/idi-p/82039#
https://powerusers.microsoft.com/t5/Power-Apps-Ideas/Capture-the-whole-screen/idi-p/93376