ホーム  >   ブログ  >   Poisson Image Editingでいい感じの画像合成ができるやつを作る on Web

2013-12-08

Poisson Image Editingでいい感じの画像合成ができるやつを作る on Web

Aizu Advent Calendar 2013 8日目の記事です。

デモ

まずは作ったやつ(デモ版)からどうぞ。
Poisson Image Blending - Demo

Step1はいじらなくていいので、Step2で適当にマスク領域を塗ってあげてください。
Screen Shot 2013-12-08 at 12.41.01 AM
こんな感じで塗れたら、HEREボタンをクリック。するとStep3にマスクをかけた領域だけ乗っかります。
Screen Shot 2013-12-08 at 12.43.06 AM
そうしたら矢印ボタンで位置を調整して、
Screen Shot 2013-12-08 at 12.44.40 AM
「ここで合成だー」と思ったところでOKボタンをクリックすれば、
Screen Shot 2013-12-08 at 12.44.51 AM
真顔モナリザの完成です。

このようにいい感じの画像合成ができる手法は、ググればC++やPython、さらにHTML5 Canvasでの実装もすでに存在します。ま、まぁマスク領域自分で塗れるようにしたから新規性あるよね・・・。

アプリ版

デモを利用して、合成したい2枚の画像を自分で選べるものを作りました。アプリ版です。
Poisson Image Blending - App

まずはベース画像(合成先)ソース画像(切り抜いて合成する方)をそれぞれ選択します。画像サイズはいずれも150ピクセル×150ピクセルに限定しています。それより大きい/小さい画像を選択すると縮小/拡大されます。

ちゃんと両方選択できると、Start Appボタンが有効になるのでクリック。
Screen Shot 2013-12-08 at 7.26.53 AM
すると先ほどのデモ版と同様の画面が表示されるので、Step2でマスク領域を塗って、Step3で位置調整、合成という流れで遊んでください。
blend_result
こんな感じになります。アゴ

実際に顔写真でやってみるのが個人的には一番おもしろいと思います。

あ、フッターみたいな変な所に結構重要なボタンがあります。最悪ですね。
Screen Shot 2013-12-08 at 2.23.06 AM

Importing gradients と Mixing gradients

ここで無視していたStep1の話をしましょう。

いい感じの画像合成では画像の勾配(Gradients)が大切です。画像における勾配とは、隣り合っているピクセル同士でRGB値がどれだけ違うかということ。
blend_result
先ほどの合成結果を見ると、色はベース画像に馴染みつつも、どこに線があるかという情報はソース画像のものを受け継いでいます。このバランスがいい感じの合成を実現しているんですね。

この線の情報(=合成結果の勾配)の求め方は2通りあります。それがImporting gradientsMixing gradientsです。

Importing gradientsは先ほども例に挙げたように、ソース画像の勾配をそのまま合成結果に利用します。一方でMixing gradientsは、各ピクセルに対してベース画像とソース画像の勾配を比較して大きい方を採用するというものです。

これを切り替えて試せるのが、デモ版にもアプリ版にもあるStep1のラジオボタンです。さらにデモ版ではFacesHand&Signという2種類のベース画像・ソース画像の組み合わせを切り替え可能にしました。勾配の取り方2種類と画像セット2種類なので計4通りの合成を試すことができ、結果は以下のようになります。

Importing Mixing
Faces faces_importing faces_mixing
Hand&Sign hand_importing hand_mixing

見ての通り、Facesの場合はImportingのほうが期待通りの結果になっています。一方Hand&Signでは、Mixingのほうが手のシワを残しつつ文字を合成していて綺麗な結果になっています。文字上では手のシワよりも文字の勾配のほうが大きく、その他の部分は変化の少ない白い紙の写真なので手のシワの勾配のほうが大きかったというわけです。

ImportingとMixingのどちらが良いのかは一概に言うことはできず、合成する画像の組み合わせによって勾配の取り方も適切な方を使う必要があるんですね。

Poisson Image Editing

さて、そろそろ今回の画像合成手法をちゃんと紹介しましょう。
(トップダウン式な記事で我ながらすばらですね)

今回利用している手法はPoisson Image Editingというタイトルの、「ポアソン方程式を解くことで画像補間をいい感じにできるよ」という内容の論文で提案されているものです。PDFは以下より。

P. Pérez, M. Gangnet, A. Blake. Poisson Image Editing. ACM Transactions on Graphics (SIGGRAPH'03), 22(3):313-318, 2003.

いい感じの画像補完の具体例が今回実装したシームレスな画像合成で、これは論文の2章と3章にあたります。式の導出から書こうと思いましたがイマイチ自身無いのと実装で力尽きたのとでやめました。気になった方は原文読んで下さい。

結論だけ書くと、論文内の式(7)を $f_p=$ の形に変形して解けば終わりです。すなわち、すべての $p \in \Omega$ に対して以下の式が成り立つような連立一次方程式を解くと言う問題。

$$ f_p = \frac{\sum_{q \in N_p \cap \Omega}f_q + \sum_{q \in N_p \cap \partial \Omega}f^*_q + \sum_{q \in N_p}v_{pq}}{|N_p|} $$

この式の意味するところは論文中の図1なんかを参照しながらイメージするしかありません。無理やり言葉で説明するとだいたい以下のような雰囲気です。

ベース画像=合成先ソース画像=切り抜いて合成する方です。

$\Omega$
合成結果の中で、切り抜いたソース画像が合成された領域
$f_p$
ある点 $p \in \Omega$ での $f$ の値で、これを求めることが今回の目標
$f$ は $\Omega$ 内の各ピクセルのRGB値を示す関数
$f^*_p$
ある点 $p$ での $f^{\*}$ の値
$f^{\*}$ はベース画像内の各ピクセルのRGB値を示す関数(既知)
$N_p$
ある点 $p$ の近傍点の集合(今回はその点の上下左右、4近傍)
$\partial \Omega$
$\Omega$ の境界領域
$p \notin \Omega$ を満たす点のうち、近傍点が1つでも領域 $\Omega$ に入っていれば $p \in \partial \Omega$
自分自身は入っていないけど近傍点のどれかは入っているという状態
$g_p$
ソース画像からマスクをかけて切り抜いた部分の中にある点 $p$ でのRGB値
$v_{pq}$
ある点 $p$ とその近傍点のうちの1つ $q$ の間の勾配
Importing $g_p-g_q$
Mixing ベース画像の勾配の方が大きければ $f^*_p - f^*_q$, そうじゃなければ $g_p-g_q$

これに沿って実際に合成を行っている(=$f_p$を求めている)コードは、ざっくりと流れを追うと以下のような感じ。

do {
  // 全ピクセルを見る 今回は簡単のため画像の端のピクセルは考慮しない
  for(var y=1; y<base_size.height-1; y++) {
    for(var x=1; x<base_size.width-1; x++) {
      if(/* もしStep2で塗ったマスク領域の中なら合成後のRGB値を推定 */) {

        // そのピクセルのRGB各色について連立一次方程式を解く
        for(var rgb=0; rgb<3; rgb++) {
          var sum_fq = 0;       // (1)
          var sum_boundary = 0; // (2)
          var sum_vpq = 0;      // (3)

          // 近傍点(4点)それぞれについて
          for(var i=0; i<num_neighbors; i++) {

            if(/* もし近傍点がStep2で塗ったマスク領域の中なら */) {
              // (1)の加算
            } else { // 近傍点は境界領域の中
              // (2)の加算
            }

            if(/* Mixingで、ベース画像の勾配の方が大きければ */) {
              // (3)の加算(ベース画像の勾配)
            } else {
              // (3)の加算(ソース画像の勾配)
            }
          }
          // そのピクセルのRGB推定値の格納
        }
      }
    }
  }
  if(/* 全ピクセルの推定が終わったので収束判定 */) break;
} while(true);

takuti / poisson-image-blending

(1)〜(3)は先に示した $f_p = $ の式の右辺、分子の3つ項に対応します。

(1)

$ \sum_{q \in N_p \cap \Omega}f_q $

(2)

$ \sum_{q \in N_p \cap \partial \Omega}f^*_q $

(3)

$ \sum_{q \in N_p}v_{pq} $

今、すべての $p \in \Omega$ に対して $f_p$ を求めているため、連立一次方程式の未知数(=合成される領域内の点の数)は $|\Omega|$ です。$|\Omega|$ 個の未知の点を順番に推定していくことになりますが、推定途中でも(1)では $f$ の値を利用しています。

このような、連立一次方程式の解の推定途中で推定済みの値とまだ推定されていない(過去の)値の両方を計算に利用する形は、ガウスサイデル法の漸化式そのままです。そこで上記簡易コードではガウスサイデル法による解の推定を行っており、推定値の収束を合成の終了としています。

合成の軸になる処理はこのようなシンプルな数値計算で完結します。しかし実際はCanvasの操作なんかでコードの肥大化が深刻。

まとめ

というわけで、Poisson Image Editingという論文で提案されたシームレスな画像合成をCanvasとJavaScriptで実装してみて、おまけにアプリ版も作ってみたお話でした。

実は以前この手法の画像合成を試したことがあったのですが、なぜかうまくいかず詰んだので放置していました。しかし再挑戦したらなんとかなった。これは今期履修している数値解析のおかげかな!?(申し訳程度の会津大要素)

アプリ版はまた気が向いた頃に実装の見直しや改善をするかもしれません。

とりあえず今はこの記事を書き上げたことでようやくBDFS始められるので僕は消えます。

明日、Aizu Advent Calendar 2013 9日目の担当は95さんです!しゃす!

影で就活Advent Calendarやってる人がいるのでよかったらそちらも見てあげて下さい。

参考

  シェアする

このエントリーをはてなブックマークに追加

  カテゴリ

プログラミング

  あわせて読みたい

2020-01-01
“じぶん”の解像度を上げる
2017-03-18
情報検索・インタラクション系の国際会議 CHIIR2017 に参加した #chiir2017
2013-08-04
HTML5 CanvasでメルトPVに出てくるメル時計をつくった

  もっと見る

最終更新日: 2022-01-18

  書いた人: たくち

Takuya Kitazawaたくち)です。長野県出身、カナダ・バンクーバー在住のソフトウェアエンジニア。これまでB2B/B2Cの各領域で、Web技術・データサイエンス・機械学習のプロダクト化および顧客への導入支援・コンサルティング、そして関連分野の啓蒙活動に携わってきました。現在は主に北米(カナダ)、アジア(日本)、アフリカ(マラウイ)の個人および企業を対象にフリーランスとして活動中。詳しい経歴はレジュメ を参照ください。いろいろなまちを走って、時に自然と戯れながら、その時間その場所の「日常」を生きています。ご意見・ご感想およびお仕事のご相談は [email protected] まで。

  近況   一杯のコーヒーを贈る

  免責事項

  • Amazonのアソシエイトとして、当サイトは amazon.co.jp 上の適格販売により収入を得ています。
  • 当サイトおよび関連するメディア上での発言はすべて私個人の見解であり、所属する(あるいは過去に所属した)組織のいかなる見解を代表するものでもありません。
  • 当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、個人ブログという性質上、誤情報や客観性を欠いた意見が入り込んでいることもございます。いかなる場合でも、当サイトおよびリンク先に掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。
  • その他、記事の内容や掲載画像などに問題がございましたら、直接メールでご連絡ください。確認の後、対応させていただきます。