この手のツールは既にフリーソフトが沢山ありそうな気もしますが…
どうも、じゅんじゅんです。
社員証や、グループウェアのアイコンなど、顔写真って何かと利用しますよね。
弊社では Microsoft 365 (Office 365) の各人のアイコンに社員証の顔写真を登録しているのですが、365 導入当時に行った各人への顔写真登録作業が結構しんどかった記憶があります(以下)
- 人事部から全社員(約500人)の顔写真をもらう(サイズ、比率が一部不揃い)
- 1枚ずつ切り抜き&サイズ変更(市販のソフトで顔の位置検出はできたが複数一括処理はできなかった)
- PowerShell で 365 へ一括登録
社員が多いと、2が絶望ですよね。もうやりたくない。
でも、次の社員証の更新が近づいている。
やるならせめて、自動化したい。
そんなとき、ふと Microsoft MVP の のりじさんの記事を読んでいて、「これを応用すれば自動化できるんじゃね?」と思い、作ってみました。
ascii.jp
作ったもの
OneDrive の特定フォルダに人物写真を保存すると、人物を検出して証明写真っぽく切り抜いた写真を別フォルダに格納するツールを、Azure Logic Apps で作ってみました。
のりじさんの記事を読んでいて、人物写真を証明写真っぽく切り出せるのでは?と思い、試しに作ってみました😌
— Jun’ichi Kodama (@KodamaJn) August 2, 2020
1枚目:before
2枚目:after ※顔周辺のスペースが不十分だと縦横比が崩れます(4つ目の顔写真)
3枚目:仕様
4枚目:処理の全体像
自分で作ると、なんか楽しい。#Azure #LogicApps https://t.co/otbGU1JllN pic.twitter.com/Um0aN3j3qS
仕組み
人物写真の顔の位置を検出し、その周りを任意のサイズ&位置で切り出します。
最終的に指定サイズになるよう拡大縮小も行います。
以下の寸法で切り出したい場合を例に説明します。
縦:600px
横:450px
顔の幅:240px
写真上部から顎までの距離:420px
まず、人物写真から顔の位置を検出します。
顔の位置の四隅の座標が取れると、顔のサイズが分かります。
この顔のサイズは最終的に240pxになるようにします。
続いて、顔の座標をもとに切り出したい座標とサイズを計算します。
この例だと、人物写真の顔のサイズが360pxで、最終的に切り出したい顔のサイズの1.5倍となっているため、ここでは各々のサイズや距離を1.5倍して、最終的に切り出したい写真と同じ比率になるよう切り出します。
最後に、切り出したいサイズになるよう拡縮して終了です。
この例だと、1.5倍大きいので全体のサイズを2/3に縮小します。
詳細
では、Logic Apps を見ていきたいと思います。
ここでは、OneDrive の特定フォルダに人物写真を保存すると、人物を検出して証明写真っぽく切り抜いた写真を別フォルダに格納する処理を作成します。
まずは全体像から。
人物の検出や切り出し、拡縮はのりじさんの記事と同様に Cloudmersive を利用します。
Cloudmersive の利用登録や初回設定については、のりじさんの記事をご参照ください。
https://ascii.jp/elem/000/004/020/4020587/
1. トリガーの作成
トリガーは OneDrive for Business コネクタの「ファイルが作成されたとき」トリガーとします。
お好きなフォルダーを指定してください。
2. 変数の初期化(4つのパラメータ)
切り出しに使用する4つのパラメータ:
縦:ResizedHeight
横:ResizedWidth
顔の幅:ResizedFaceWidth
写真上部から顎までの距離:ResizedLengthFromTopToChin
を、後に変更しやすいよう変数に格納します。
「変数を初期化する」アクションを使用します。
3. 変数の初期化(切り出した写真の左端から顔の左端までの距離を計算)
- で設定した値を用いて、切り出した写真の左端から顔の左端までの距離を計算しておきます。
div(sub(variables('ResizedWidth'), variables('ResizedFaceWidth')), 2)
4. 顔を検出する
Cloudmersive コネクタを利用して、顔を検出し位置を取得します。
「Detect and find faces in an image」アクションを利用します。
Image file には、トリガーから取得できる OneDrive のファイル コンテンツを指定します。
検出したら、「作成」アクションを用いて顔の座標を取得するための準備をします。
この操作の詳細はのりじさんの記事をご参照ください。
first(body('Detect_and_find_faces_in_an_image')?['Faces'])
5. 検出した顔の幅を計算
後に、切り出したい写真の顔の幅と検出した顔の幅の比率を計算で使用するため、検出した顔の幅を変数に格納しておきます。
sub(outputs('作成')?['RightX'],outputs('作成')?['LeftX'])
6. 人物写真を切り出す(拡縮はまだ)
Cloudmersive コネクタの「Crop an image to a rectangular area」アクションを利用して、任意の座標位置から任意のサイズで画像を切り出します。 Image file には、トリガーから取得できる OneDrive のファイル コンテンツを指定します。
切り出す始点となる座標とサイズはそれぞれ以下のように算出します。
「5.の検出した顔の幅 / 1.の切り出したい顔の幅」が、仕組みの②で述べた倍率を表しています。
言葉で書くと↓
X座標 (The left edge of the rectangular crop area in) = 検出した顔の左端のX - (3.で計算した距離 * 5.の検出した顔の幅 / 1.の切り出したい顔の幅) Y座標 (The top edge of the rectangular crop area in) = 検出した顔の下側のY - (1.の写真上部から顎までの距離 * 5.の検出した顔の幅 / 1.の切り出したい顔の幅) 横幅 (The width of the rectangular crop area in) = 1.の縦サイズ * 5.の検出した顔の幅 / 1.の切り出したい顔の幅 縦幅 (The height of the rectangular crop area in) = 1.の横サイズ * 5.の検出した顔の幅 / 1.の切り出したい顔の幅
式で書くと、上から↓
sub(outputs('作成')?['LeftX'], div(mul(variables('ResizedWidthFromLeftToFaceleft'), variables('DetectedFaceWidth')), variables('ResizedFaceWidth'))) sub(outputs('作成')?['BottomY'], div(mul(variables('ResizedLengthFromTopToChin'), variables('DetectedFaceWidth')), variables('ResizedFaceWidth'))) div(mul(variables('ResizedWidth'), variables('DetectedFaceWidth')), variables('ResizedFaceWidth')) div(mul(variables('ResizedHeight'), variables('DetectedFaceWidth')), variables('ResizedFaceWidth'))
※四則演算すべてに関数が必要なのが煩わしい。。
7. 拡大縮小する
Cloudmersive コネクタの「Resize an image」アクションを利用して、指定のサイズにリサイズします。 Width と Height に各々1.で設定したサイズの変数を設定するだけです。 Image file は先程切り出したものを使用するので、6.の本文を指定してください。
8. OneDrive に保存する
最後に、リサイズした画像を OneDrive の任意のフォルダに作成して終了です。 ファイルコンテンツは7.の本文を指定してください。
注意点
人物写真の顔が大きい場合など、顔の周辺に十分なサイズがないと6.の切り出しが指定したサイズで切り出せず、7.の拡大縮小によって縦/横に伸びた画像が生成されてしまいます。
例えば正常に処理を終えたファイルは元のフォルダから削除し、指定したサイズで切り出せないファイルを残すなどして、どのファイルが正常に処理できなかったかが分かるよう処理を加えてみるのも良いかもしれません。
おわりに
この手のツールは既にフリーソフトが沢山ありそうな気もしますが、いざ探してみると商用利用は有償だったり、余計な機能が沢山搭載されていたりして、ピッタリくるツールってなかなかないんですよね。
(Logic Apps が従量課金であることはさておきw)
しっくりくるツールがないなと思ったら、身の回りのもので作成できないかをちょっと考えてみると、思わぬヒントがあるかもしれません。
欲しいツールは欲しい人が作る時代へ。
何かヒントになれば幸いです。
参考文献
今回は以下の記事にお世話になりました。 のりじさん、Hiro さん、ありがとうございました!