SNS風アクリルカードを使った撮影が上手くできないので画像合成技術で解決してみた件について
みなさん。あけましておめでとうございます。
本年もどうぞよろしくお願いいたします。
エンジニアのせしょうです。
今年もブログ記事を何件か書いていくのでよろしくお願いします!
ということで、私の新年最初の記事は タイトルにもあるように「SNS風アクリルカードを使った撮影が上手くできないので画像合成技術で解決してみた件について」という最近のライトノベルまさりの長いタイトルから始めたいと思います。
最近、様々なグッズがある中で アクリルカード、 キーホルダーなど その手のグッズをよく見かけるようになりました。
私も持っているのですが、どうも納得いく写真が撮れないんですよね。。。。
なぜ納得いく写真が撮れないのか?(3つの問題)
せっかくのグッズなのに、なぜ納得いく写真が撮れないのか。分析してみると、大きく3つの物理的な問題がありました。
問題1:背景ボケのジレンマ
最新のスマホカメラは性能が良く、自動で背景を綺麗にぼかしてくれます。 しかし、これが今回の敵です。「アクリルカード」にピントを合わせると「背景(絶景)」がボケてしまい、逆に「背景」にピントを合わせると「手元のカード」がボケてしまうのです。両方をくっきり写すのが意外と難しい…。

(背景ボケの図 ※geminiで生成した画像です。)
問題2:風によるブレと傾き
屋外での撮影は「風」との戦いです。 カードの先端や端っこをつまんで持っているため、少しでも風が吹くとカードが煽られて傾いたり、ブレたりしてしまいます。ベストショットを狙って何分も粘るのは辛いものです。

(風に煽られてブレる図 ※geminiで生成した画像です。)
問題3:手が映り込む問題
当たり前ですが、カードを持っている自分の「手」や「指」がどうしても映り込みます。 純粋に「推し(キャラ)」と「風景」だけの写真が撮りたいのに、(個人的には)自分の手はノイズでしかありません…。

(手が映り込んでいる図 ※geminiで生成した画像です。)
これら3つの問題を手軽に画像合成の技術で解決していこうと思います。
※ SNS風アクリルカード、 キーホルダーは、今後アクリルカードに統一して記載します。
使用するライブラリ
今回の実装には、Pythonの以下のライブラリを使用します。 ターミナル(コマンドプロンプト)で下記を実行してインストールしてください。
pip install opencv-python numpy pillow
- opencv-python: 画像処理を行うメインのライブラリ
- numpy: 行列計算(画像の数値データ処理)に使用
- pillow: 画像の読み込みや加工の補助に使用
必要なもの
- 持っているアクリルカード (著作権の関係上 実際には実在しないカードを例として出します。)
- Gluegent Flowのアクリルカードアクリルカード

- Gluegent Flowのアクリルカードアクリルカード
- グリーンバック (単色のものならなんでもOK)

やり方
ステップ1: アクリルカード を用意したグリーンバックの上に置いて、撮影をする
下記画像のように、グリーンバックの上に置いて撮影をする

撮影する画像は、正面からで被写体を大きめに撮る

ステップ2: 背景画像を用意する
背景画像は、正直なんでもOKです。 過去に旅行に行った際の絶景写真や、ゲームのスクリーンショットなど、合成したいお好みの画像を用意してください。
画像合成の仕組み
- 色の検出: アクリルカード...の「緑色」の部分だけを特定します。(HSV色空間を使用)
- マスク作成: 緑色の部分を白、それ以外を黒にした「マスク」を作ります。
- 合成: マスクを使って、アクリルカード...と背景を1枚の画像に重ね合わせます。
クロマキー(グリーン検出)
- 「RGB」ではなく、「HSV」で色の検出を行う
- マスク(型紙)の作成
- 自然に見せる「ソフトエッジ(ガウスぼかし)」の魔法(重要!)
1. 「RGB」ではなく、「HSV」で色の検出を行う
普通、画像はRGB(「赤・緑・青」)表記の組み合わせで、 表現されますが、これは画像処理をする上で不向きなことがあります。
今回のように、画像合成を行った際光の当たり方でRGB表記の数値がバラバラになってしまうためです。
そこで、今回の画像合成では、HSVという色空間に変換して処理を行っています。
- H(Hue / 色相): 色の種類(赤、黄、緑など)。今回のグリーンバックのように「緑」の範囲だけを抽出しやすくなります。
- S(Saturation / 彩度): 色の鮮やかさ。白っぽい緑から濃い緑まで抽出できます。
- V(Value / 明度): 明るさ。影になっている緑もこれで抽出できます。
# 画像の読み込み
foreground_img = cv2.imread(foreground_img_path)
# BGRからHSVへ変換
hsv = cv2.cvtColor(foreground_img, cv2.COLOR_BGR2HSV)
2. マスク(型紙)の作成
マスクとは、白と黒だけで描かれた「切り抜き型紙」のようなものです。
今回の場合以下の条件で、白と黒で描かれるマスクを作成します。
- 緑色だと判定された場所 → 「白(1)」にする
- それ以外の場所 → 「黒(0)」にする
この「0 or 1」というone hot ベクトルのような状態では、境界線がギザギザ(ジャキー)になり、合成した時に「以下にも切り貼りしました。」という違和感が出てしまいます。

3. 自然に見せる「ソフトエッジ(ガウスぼかし)」の魔法(重要!)
2.で書いたマスクの方法だと、「0 or 1」という状態だと、緑とそうでない部分の境界線が急激すぎますが、ここに「ソフトエッジ(ガウスぼかし)」という処理を加えることで、境界線が「1 → 0.8 → 0.5 → 0.2 → 0」といった具合に、なだらかなグラデーションで表現することができます。

# グリーンの範囲でマスクを作成 (self.h, self.s, self.v : HSVのパラメータ)
lower_green = np.array([self.h_min.get(), self.s_min.get(), self.v_min.get()])
upper_green = np.array([self.h_max.get(), self.s_max.get(), self.v_max.get()])
mask = cv2.inRange(hsv, lower_green, upper_green)
# ソフトエッジの作成(ガウスぼかし)
mask_blurred = cv2.GaussianBlur(mask, (5,5), 0)
これがアルファ値(透明度)として働きます。
0,1 と ガウスぼかし の比較
確かに、0,1 の方がギザギザしており、ガウスぼかしは うっすらぼやけているのが分かります。

合成の計算式(アルファブレンディング)
実際の合成では、以下のような計算をピクセルごとに行っています。
- αが0(アクリルカード...)の時: 前景画像がそのまま表示される
- αが1(背景)の時: 背景画像がそのまま表示される
- αが0.5(境界)の時: 前景と背景が半分ずつ混ざり合う
この境界値がある「半分ずつ前景と背景が混ざる」ことで、アクリルカードの角部分や全体のラインが背景にふわっと馴染み、自然な合成写真ができます。
# アルファブレンディング
alpha = mask_blurred.astype(float) / 255
# 計算用にチャンネル数を合わせる
alpha = np.stack([alpha, alpha, alpha], axis=2)
# 合成計算: (1 - α) * 前景 + α * 背景
# α=1(緑部分)なら背景が表示され、α=0(カード部分)なら前景が表示される
result = (1 - alpha) * foreground + alpha * background_canvas
さぁ、実践
ということで、分かりやすいように軽い UIのアプリを作成して実行してみました。
1. アプリを立ち上げる。

2. グリーンバックを背景にした アクリルカードを読み込む。

3. 背景としたい画像を読み込む。 (画像の大きさを変更したり、アクリルカードの位置を変更したり)

このようなアプリを作って、画像合成を行いました。
結果
1. 富士山との写真を合成

2. 人が写っている写真を合成

まとめ
ということで、私の新年1本目の私欲にまみれた記事でした。 みなさんいかがでしたか?
「推しと写真を撮りたいけど、上手く撮れないよー」という方、あるいは「毎回グッズを持ち歩くのは破損が怖くて…」という方の、解決のヒントになれば嬉しいです。
画像合成はこれ以外にも色々と使い道があるので、ぜひ色々と使ってみてください。
また実は、最初の例で挙げたので気づいた方もいるかもしれませんが、実はgemini等の生成AIの画像生成でも十分だったりします笑 (とほほ...( ノД`))
ただ生成AIは指示が曖昧だと、このようにイメージと違った画像にもなるので指示が適切でないと難しい部分もありますね。

このように、指示が上手くいかないよという方、細かい位置など調整したかったり、画質が荒くなるのが気になる方などはぜひ、pythonを使用した画像合成技術を参考にしてみてください。
最後まで読んでいただきありがとうございました。また、次の記事でお会いしましょう。
(せしょう)
