5.18 [C#]写真の透かしを除去したい


Home -> 雑用 -> 雑用メモ -> [5.18 [C#]写真の透かしを除去したい]

2016/02/17 公開
2016/02/26 追記
書きっぱなしの文章なので大変読み難い代物となっている。それでも良ければどうぞ。

概要

写真の透かしを除去するためにC#で試行錯誤した記録。

見出し一覧

基本的に時系列順なので副題が前後しているが容赦していただきたい。

  1. 序: 透かしとは
  2. 前準備: 透かしの理論
  3. 透かしの除去方法 その1
  4. C#による透かし除去の実装 その1
  5. コントラストが僅かに落ちる原因
  6. 透かしの除去方法 その2
  7. C#による透かし除去の実装 その2
  8. 透かし画像の生成
  9. 圧縮を主原因とする赤色劣化問題
  10. 赤色劣化の軽減
  11. 遺伝的アルゴリズムによる解の最適化
  12. まとめ
  13. 付録1: ギャラリー
  14. 付録2: 画像の切り抜き練習
  15. 最後に
  16. 追記(2016/02/26): delogo について

序: 透かしとは

C#の話の前に前書きを。

透かしとは以下のように画像の上に半透明に被さった文字あるいは記号を指す。ウォーターマークとも呼ばれる。
[画像00]の例では「美瑛」などの文字が透かしとして入っている。これをどうにかして消したい。

透かし入り画像の例 画像00: 透かし入り画像の例(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460349.jpgより)

厳密には、この種の透かしは知覚可能型電子透かしというらしい。

画像に乗せられた透かしを除去しようとする先人たちの取り組みを検索してみたが、大したものは出てこなかった。
唯一何かやってるなー、と感じたのは以下の参考ページ。

明るめの同じ透かしが同じ位置に入った標本が大量にある場合は、それらを加算なり乗算なりで重ねまくると
透かし部分は必ず透過度に応じたある明るさ以上になるので透かしが浮き出てくる。それを加工して透かしを復元する。
透かしを復元できればそれを元に透かしが入る前の画像も復元することができる、という調子らしい。

でも結局やっていることは「ペイントで根性」と大差ない。結構面倒臭そうというかセンスが要求されそうという印象。
また、この方法で復元した透かし画像は透過度を調整する必要があり、周縁部も不完全という欠点がある。
さらに、透かしが単色ではなく白と黒の2色が使われている今回の場合、この方法ではとてつもなく面倒な作業を行わねばならない。

ところが幸いなことに[画像00]には透かしが入っていない縮小版(サムネイル)が存在する。

透かし入り画像の例 画像01: [画像00]のサムネイル

これをうまく使えば、プログラムにより半自動で透かしの復元および透かし除去ができそうだ。

前準備: 透かしの理論

プログラムで画像を扱うためには、その性質を定性的かつ定量的に議論する必要がある。
まず、画像に透かしを被せると画素の色がどう変化するのかを"何となく"理解していきたい。

簡単のために透かし画像は無彩色であると仮定する
このとき、直感的にもそうであるように、透かしが不透明でない限り理想的には透かしを重ねる前後で色相は変わらない。
(ただし実際には後述のように量子化誤差や圧縮による擾乱が存在し、状況によってはこれがかなり効いてくる。)

有彩色に無彩色を重ねたときの色の変化 画像02: 有彩色に無彩色を重ねたときの色の変化

つまりこの仮定の下では、透かしが入った画像の特定画素の色から元の色を復元する際、透かし除去後の色相は既に明らかであるため
彩度と明度のみが未知数であり、これから議論するのは1つの画素につき自由度2の決定系ということになる。

では、具体的に式を追うことにより、どのような計算を行えばよいのか確認していこう。
画像src上の座標r=(x,y)における画素値をsrc(r)=RGB(R,G,B)とし、これにfilter上の同座標の画素値を
指定された透過情報により重ねてできる新たな色をdst上の同じ座標にマッピングする。

いま、ある座標r_iに着目してsrc(r_i)であり、これにfilter(r_i)
不透明度P_i(P_i)で加算した結果をdst(r_i)にマッピングするとき、以下の関係が成り立つ。
eq1.1――(1.1)

上式における未知数はP_iおよびC_iの2つのみなので、src(r_i)が有彩色であれば
上式は2変数に対して方程式が3つ存在する過剰決定系であり、その最適解は最小二乗解である。

画像srcdst上の座標r_iにおける任意の原色成分をそれぞれX_iY_iと表すことにすると、Y_i
P_iおよびC_iによって一意に定まるX_iの関数(すなわちモデル関数)f(X_i)と誤差e_iを用いて
eq1.2――(1.2)
と表すことができる。また(1.1)より(1.3)のように定めることができる。
eq1.3――(1.3)
ここで(1.4)の変数変換を考える。
eq1.4――(1.4)
すると、(1.3)は(1.5)に示す通り一次関数で表すことができる。
eq1.5――(1.5)
すなわち、P_iおよびC_iを求めることは(1.5)に示すモデル関数について係数の最適解を求めることと等価であり、
(1.5)についてはごく一般的な1次関数による最小二乗近似を用いることができるので、以下によって値を定められる。
eq1.6――(1.6)

ある画像のある画素について着目したとき、原色成分の値のペアは3組できるので、標本画像数の3倍のデータが得られる。
これを元にして(1.6)により係数を計算すれば、フィルタの明度および不透明度が計算できる。

透かしの除去方法

前準備において、フィルタが掛かる前の画像が手元にある時はフィルタが復元できることが示された。
では、今回のように透かしが入る前の画像が存在せず、透かしを除去した状態の画像が欲しい場合はどうしたらよいのか。

今回は、先に紹介した通り透かしの入っていない縮小版があるため、これを用いる。
とはいえ解像度が元画像に比べてかなり低いので色々と不具合が生じる。そこで、以下の仮定を置くことにより、
縮小版を用いて得られた結果の正確さがある程度保証されるものとして議論を進める。

注意しなければならないのは、この仮定は正しいとは限らないということである。実際にこの仮定が妥当かどうかは後に検証を行う。
この仮定により、誤差の大きさに対して標本数が十分に多い場合はsrc_p(r_i)をそれなりに正確に求められる。
実際には、まず(1.6)により係数を計算し、以下の関数によりsrc_p(r_i)を求めればよい。
eq1.7――(1.7)
(1.7)における不定の扱いだが、(1.4)からわかるようにこのときフィルタの不透明度100%であり元の色を特定できない。

C#による透かし除去の実装 その1

実装自体は大したことのないごく普通の画像処理である。しかし何点か注意しなければならないことがある。

ここまでは、十分な数の標本画像が手元に存在するという前提で議論を進めてきた。実際にはそれらを予め用意しておくという手順を
追加する必要がある。ひとまず下記の条件で揃えた画像を標本とする。これだけの枚数があればそこそこの精度は出るだろう。
(当然ながらこの時点では統計学的に十分な精度が保証されるかどうかはわからない。)

標本の枚数が多いので処理する画素数も多くなる。既に有名なようにBitmap.GetPixel()Bitmap.SetPixel()は非常に遅いので
別の方法で処理するべきである。

また、縮小版はそのまま拡大すると透かし入りの画像と微妙にずれるので、これも補正して処理する必要がある。

縮小版を拡大したものと透かし入りの画像のずれ 画像03: 縮小版を拡大したものと透かし入りの画像のずれ

縮小版を拡大するときに適切に補正を施したもの 画像04: 縮小版を拡大するときに適切に補正を施したもの

以上に注意しつつ色々と作っていく。また、src_p(r_i)に対するsrc(r_i)の誤差が正規分布に従うかどうか、仮に正規分布に従わない場合、
最小二乗法が適切な解を与えるのかどうかを確かめるために、ある座標に着目してその誤差の分布を記録する。
まずは美瑛の「美」の中央上寄りあたりについて検証する。この点は[画像05]において水色の○で示した。
透かしが入っていないことが明らかな領域は計算対象から除外する。領域の境界は[画像05]において桃色の四角形で示した。

透かしの範囲選択と座標の指定 画像05: 透かしの範囲選択と座標の指定

これまでに述べた計算を全ての画素について行うと、フィルタとして[画像06]のようなものが得られる。計算時間は30秒弱。
高速化の余地も残っているが所詮は.NETなのでここを改良してもそれほど意味はない。
[画像06]は実際には計算上生じたいくつかの不具合を誤魔化しているので、正確な透かし画像ではない。これはあくまでもイメージである。

得られた透かし 画像06: 得られた透かし

実際の透かし除去は、最小二乗法で得られた係数をそのまま用いてモデル関数の逆関数による計算によって行う。
この時点で[画像07]のような除去結果が得られる。

透かしの除去結果 画像07: 透かしの除去結果

一見してかなり良好な結果にみえるだろう。実際に、べた塗りが少ない画像ならばこれでも全く問題ない。
しかし、よく見ると透かし除去処理を行った部分は色のコントラストがごく僅かに落ちてしまっている。
空のように明度の高い領域に被る部分では明度の変化が明らかである。
安いディスプレイでも下から覗き込んだりすれば、先ほど桃色の枠で囲った領域の跡が確認できるだろう。

透かしの除去結果を補正したもの 画像08: 透かしの除去結果を補正したもの

コントラストが僅かに落ちる原因

ここからは、上で述べたような現象が生じる原因を考えて対策していく。

まず、先の理論によるフィッティングは正しく機能しているのか。座標(150,75)について横軸X_i、縦軸Y_iでプロットすると下図のようになる。
この座標はちょうど美瑛の「美」の字の真上に位置し、フィルタにより白みがかるため以下のような相関が得られる。
これだけを見ると、割と正しく動作しているように見受けられる。

座標(150,75)におけるフィルタ前後の画素値の相関 画像09: 座標(150,75)におけるフィルタ前後の画素値の相関

では、期待値は実際の画素値に対してどのような分布を取っているのか。同座標について横軸を残差、縦軸を確率としてプロットすると
[画像10]のようになる。橙色の線は、統計的に求めた期待値と分散を元に描いた正規分布であり、縦軸は確率密度を表す。
見ての通り、残差の分布は正規分布ではフィッティングできない。正規分布に比べて裾が広いため、
計算して出てくる分散の値は見た目よりも大きくなってしまう。また平均値はほぼ0だが最頻値は-1となっている
これが割と重要で、フィルタ画像から元画像を推定すると、この座標では多くの場合においておよそ1だけ明度が落ちることを意味する。

座標(150,75)における残差と確率 画像10: 座標(150,75)における残差と確率

文字上以外の点ではどうなるだろうか。美瑛の「美」の上あたりに相当する座標(150,60)についても統計を取ったところ
[画像11]のようになった。この結果を見るときに注意しなければならないのは、このグラフの概形が意味するところは
「元画像にフィルタを重ねてフィルタ済みの出力を得るときに、入力の明度によって出力の明度がどう変化するか」、すなわち
「入力からフィルタ済みの出力を得るために設定されたトーンカーブ」であるということ。

透かしの除去結果を補正したもの 画像11: 座標(150,60)におけるフィルタ前後の画素値の相関

上の結果をもう少し噛み砕くと、今のままだとフィルタ後にはコントラストが上がるということであり、
逆に言えば透かし画像から除去結果を得るとコントラストが下がってしまうということである。
これは上のグラフが理想的には(0,0)と(255,255)を結ぶ直線にならなければならないことからもわかる。

このようになってしまう原因として以下の3点が考えられる。

では、どのようにこれを補正するのか。まずは正確なずれを知る必要があるのでこれを求める。
ひとまず、透かしに近く本来は不透明度0%となる領域として、[画像12]中の緑の枠で囲った領域を選んで検証する。

透かしに近く不透明度0%となるべき領域 画像12: 透かしに近く不透明度0%となるべき領域

フィルタ前後の画素値の相関は[画像13]の通り。理想的な直線とは(119.6, 119.6)を中心としてズレを生じている。
このことは、縮小・拡大を経ると平均的に見てRGB(119.6, 119.6, 119.6)あたりに近づくことを意味する。

フィルタ前後の画素値の理想的な関係と実際の相関 画像13: フィルタ前後の画素値の理想的な関係と実際の相関

透かしの除去方法 その2

ここまでに述べた方法で得られる値をX_i_p、補正後の値をX_i_ppとすると、[画像13]のグラフは縦軸がX_i_pp、横軸がX_i_pであり、
傾きおよびX_i_pp切片をそれぞれA_i_pB_i_pとすると、(1.8)の関係が成り立つ。
eq1.8――(1.8)
また、X_i_p_eqなので(1.9)の関係が成り立つ。
eq1.9――(1.9)

今まで(1.7)を用いていた代わりに(1.9)を用いれば、尤もらしい出力が期待できる。
一番最初に縮小版を拡大する時にコントラストを弄ってしまうという方法もあるが、これだと補正係数を先に求めなければ
ならないため実装が面倒になってしまう上に、コントラストを弄った時点でRGBの各値が整数に丸められてしまい誤差の原因になる、
といったことが予想されるため保留。

また一応述べておくが(1.9)自体はフィルタ除去前と除去後の画素値のマッピングを各座標について与えるだけであり、
元画像に乗せられた透かし画像がどのようなものだったのかについては言及していない

C#による透かし除去の実装 その2

実装といっても、必要な定数を追加で解析時に求めるようにして、画像生成時に(1.9)を用いるようにするだけ。
これで[画像14]のように良好な結果が得られるようになった。

(1.9)による透かしの除去結果 画像14: (1.9)による透かしの除去結果

[画像08]と同様に補正を掛けても[画像08]とは異なり不具合は見られなくなった。

[画像08]と同様に透かしの除去結果を補正したもの 画像15: [画像08]と同様に透かしの除去結果を補正したもの

という訳で目標としていた透かしの除去が概ね達成された。

透かし画像の生成

式(1.9)は先に述べたように単体では透かし自体の情報は持っていない。しかし実際に画像処理ソフトウェアでの使用を想定すると
フィルタ前後の値のマッピングを与えるよりも透過情報を持った透かし画像を与える方が現実的である。
実際にどんなフィルタが掛けられていたのか確認したいというだけでも動機としては十分である。

補正係数が必要となる原理より明らかにA_i_p_1なので、(1.9)を変形すると(1.10)が得られる。
eq1.10――(1.10)
(1.3)より、(1.4)と同様の関係として(1.11)が考えられる。
eq1.11――(1.11)
これを整理すると(1.12)が得られる。
eq1.12――(1.12)
よって、これを用いることで補正結果を適用した透かし画像を生成することができる。

生成結果は[画像16]の通りである。これと[画像06]とは似て非なるものである。

補正係数を適用して生成した透かし画像 画像16: 補正係数を適用して生成した透かし画像

また、これができるからといって何か嬉しいことがある訳ではないが、[画像17]のように適当な画像に同じ透かしを追加することもできる。

適当な画像に透かしを適用した結果 画像17: 適当な画像に透かしを適用した結果

圧縮を主原因とする赤色劣化問題

ここまでに述べた内容だけを見ると既に目的を完全に達成したかのように見えるが、実際にはまだ問題が残っている。
[画像18]の例では、美瑛の「瑛」の字の右側部分の輪郭が浮き出てしまっている。

透かしが除去しきれない例 画像18: 透かしが除去しきれない例

[画像18]だけだと遠目には分からないかもしれないが、1次微分(Gradient)を取ることにより輪郭を抽出すると[画像19]が得られる。
やはり容易に抽出可能な程度で跡が残ってしまっているので、これでは透かしを完全に除去できたとは言えない。

除去しきれなかった透かしを顕在化させたもの 画像19: 除去しきれなかった透かしを顕在化させたもの

この問題が生じる原因は主に以下の通りである。

JPEGの圧縮原理については以下のページに分かりやすく書いてある。

という訳で、たまたまモデルになってもらった3269番さんがたまたま真っ赤な服を着ていたために無視できない問題となってしまった。

問題を整理すると、純粋な赤に近い色は縮小版においては透かし入り画像に比べて暗めになってしまうため、これを元にして
透かし除去の仕組みを構成すると、透かし入り画像に比べて透かし除去済み画像は純粋な赤に近い色は元よりも明るくなってしまう、
ということである。これは透かし入り画像と縮小版とで比べた時に、赤色に近い色であることが原因で及ぼされる色の劣化の程度の差が
大きい座標、すなわち透かし文字上(特に文字輪郭部分)において顕在化するため、このように文字の輪郭が浮き出てしまった。

赤色劣化の軽減

では、どのように対策を施せばよいのか。
想定する補正関数は以下の要件を満たす必要がある。

そこで、付け焼刃ではあるが(1.13)のような関数を考える。
eq1.13――(1.13)
(1.13)は4つの要素からそれぞれ独立して決定される数の積である。
ゲインおよび閾値は定数であり、適切に設定をする必要がある。余弦類似度およびシグモイド関数は(1.14)により定義する。
eq1.14――(1.14)
とりあえずは画素値のうち赤色成分のみを対象とし、新たな画素値を(1.15)により決定する。
eq1.15――(1.15)

勾配は適当なアルゴリズムを選択して計算する。

4つ目の成分は不透明度により決定され、[画像20]のように閾値より大きい場合は正、小さい場合は負となる。
これにより輝度を大きくするか小さくするかを分ける。
ただし[画像20]ではゲインおよび閾値を適当に設定している。

不透明度により決定される成分の特性 画像20: 不透明度により決定される成分の特性

2つ目・3つ目の成分はそれぞれ勾配・余弦類似度により決定され、両方の値が大きい時のみ補正関数が有効に働くような特性を持つ。
不透明度が閾値から十分に離れているとき、補正の特性は殆どこの2成分で決まる。

勾配および余弦類似度により決定される成分の特性 画像21: 勾配および余弦類似度により決定される成分の特性

このような補正関数を考えるとき、ゲインや閾値といった定数をどのように決定するかが問題となる。
理想的には何らかの効用関数が最大となるように最適な大きさを決定するべきだが、まずは手作業により調整を行った。

赤色劣化を軽減した結果 画像22: 赤色劣化を軽減した結果

定数を適当に調整して補正を行った結果、透かしの跡が補正前に比べて目立たなくなった。

補正関数による処理前後の出力比較 画像23: 補正関数による処理前後の出力比較

勾配を取り輪郭を抽出すると、他の部分に殆ど変更を加えることなく透かしの跡を目立たなくすることができたことがわかる。
すなわち先に定義した補正関数はある程度有効に働いたということになる。

補正関数による処理前後の出力を1次微分したものの比較 画像24: 補正関数による処理前後の出力を1次微分したものの比較

では、よりよい結果を得るために各定数をどのように決定するか。
一番簡単な方法は、色々な値を代入してみて勾配が最小となる組を探す、という感じだろう。
しかし7変数もあるのであらゆる組み合わせを総当たりで検証しようとするとメモリを大量に食ってしまいVisualStudioに怒られてしまう。

遺伝的アルゴリズムによる解の最適化

真面目に解くと複雑な問題に関して最適解を得たいとき、遺伝的アルゴリズム(GA)がよく用いられる。
これは進化的アルゴリズムの1つで、評価関数を用いて適応度を計算することが可能な問題であれば
だいたいどんな問題でも最適解かそれに近い何かが得られるという便利なアルゴリズムである。

遺伝的アルゴリズム、とだけ聞くと何か先進的なものであるかのように感じてしまうかもしれないが、中身は実に単純である
初期値を適当に設定し、それらを交叉させたり突然変異させたりして次世代を作り、評価の高いものが多く残るように淘汰する。
あとは交叉・突然変異・淘汰を繰り返すだけである。エリート(評価の高い個体)を暫定的な解とみなすと、
これは世代交代を繰り返す度に最適解に漸近する。ただし、個体数・交叉率・突然変異率が適切でなければならない。
中身といっても実のところこれは表向きであり、裏には色々と理論があるようだか今回は触れないことにする。

ここで問題となるのが、評価関数・個体数・交叉率・突然変異率である。まず、評価関数は(1.16)により定義する。
eq1.16――(1.16)
この値が大きくなるようなパラメータは適応度が高い組み合わせとなる。alphaはユーザが決める定数で、
勾配は少ないが人が見て不自然な結果となるような組み合わせを淘汰するためのものである。これを大きくし過ぎると
閾値が大きくなりすぎたりゲインが小さくなりすぎたりするため、大きさは0.2程度に調整する。

この評価関数を用いて、個体数100、交叉率90%、突然変異率1%で世代交代を50回行ったところ[画像25]のような結果が得られた。

遺伝的アルゴリズムにより決定された定数を用いて補正したものと補正前との比較 画像25: 遺伝的アルゴリズムにより決定された定数を用いて補正したものと補正前との比較

補正前よりは透かしが目立たなくなったが、予想していたほどの効果は見られなかった。
これは補正関数あるいは評価関数の設計が不適当であることが原因と考えられる。
何らかのスケーリングを施すことである程度の改善が期待できるが、手動で決定したときの結果を超えられる見込みはないので
そこまではやらないことにする。
一応赤色劣化の補正はある程度までなら自動で行うことが可能であることを示すことができたので、この章はこれでよしということでご勘弁願いたい。

まとめ

以上より、縮小版でもいいので透かしが入っていない画像と組になるような標本を十分な量だけ用意することができれば、
画像から透かしを除去することは技術的には十分に可能であることがわかった。
実際には標本が用意できないような場合も多いので、このときは別の方法を検討しなければならない。
また標本については透かしが同じ位置に入っていることが前提となっているという点も注意が必要。

行った操作の概要 画像99: 行った操作の概要

恐らくここまでの説明はかなり分かり辛いものだっただろうと考え、概略を[画像99]の通り作成してみた。
実際には複数の標本を用いて推定を行っているためここまで単純ではないが、要旨は理解できるだろう。

また標本数が統計学的に十分かどうかについては一切の検討を行わなかったが、出力にノイズが殆ど乗っていないので十分だろう。
もう少し標本数は少なくてもいいかもしれないが、計算結果は使い回せるので1回の計算に多少時間が掛かったとしても問題にはならない。

付録1: ギャラリー

同様の画像から透かしを除去したもの。これらの画像は検証の結果を示すために掲載したものであり、他意はない。
一部の画像はGIMPを用いて後処理を施しているが、素人が数分間弄っただけなので基本的にはC#側で透かしをほぼ100%除去できたと考えてよい。
透かしがどれほど綺麗に除去されうるかを以下から確認して頂きたい。

透かし除去結果 元画像
hm0040406x hm0040406
▲hm0040406: 2011年 ゴール地点 11:00~11:30
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm004/images/hm0040406.jpgより)
この画像のみ2011年のものであり、透かし文字及び縦横比が異なる。
縦長画像は透かし文字が横長画像に比べて若干小さいため、横長画像との透かし除去情報の共有はできない。
hm0460345x hm0460345
▲hm0460345: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460345.jpgより)
hm0460347x hm0460347
▲hm0460347: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460347.jpgより)
hm0460348x hm0460348
▲hm0460348: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460348.jpgより)
hm0460349x hm0460349
▲hm0460349: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460349.jpgより)
hm0460350x hm0460350
▲hm0460350: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460350.jpgより)
hm0460396x hm0460396
▲hm0460396: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460396.jpgより)
hm0460397x hm0460397
▲hm0460397: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm046/images/hm0460397.jpgより)
hm0320054x hm0320054
▲hm0320054: 2012年ハーフ、クオーター、ワンエイツマラソン ゴール地点 11:00~11:15
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm032/images/hm0320054.jpgより)
hm0320218x hm0320218
▲hm0320218: 2012年ハーフ、クオーター、ワンエイツマラソン ゴール地点 11:00~11:15
(https://saltworks.jp/~photo-sports.saltworks.jp/marathon/photo/hm032/images/hm0320218.jpgより)

付録2: 画像の切り抜き練習

ついでにGIMPの練習をした。電脳はさみが思ったよりも無能だったのでほぼ手動で切り抜くことになった。
背景はスタイルシートで適当な色になっているが画像自体は透過PNGになっている。
PCならマウスオーバで背景色が切り替わるので、これにより切り抜きの精度がどの程度か容易に確認することができる。

切り抜き透過PNG 最小枠透過PNG
hm0040406y hm0040406z
▲hm0040406: 2011年 ゴール地点 11:00~11:30
hm0460345y hm0460345z
▲hm0460345: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
hm0460347y hm0460347z
▲hm0460347: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
hm0460348y hm0460348z
▲hm0460348: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
hm0460349y hm0460349z
▲hm0460349: 2012年クォーターマラソン ペンション星ヶ丘 10:30~10:45
hm0320054y hm0320054z
▲hm0320054: 2012年ハーフ、クオーター、ワンエイツマラソン ゴール地点 11:00~11:15
hm0320218y hm0320218z
▲hm0320218: 2012年ハーフ、クオーター、ワンエイツマラソン ゴール地点 11:00~11:15

最後に

今回の検証と同じようなことは既に世界のどこかで何回も行われていることだろう(未確認)。
しかし少なくとも国内の記事を探してみた範囲では見つからなかったので、画像に透かしを入れようと考えている方は
それが復元可能な状態であるかどうかに留意する必要があることを今後は念頭に置いておいてほしい。

また逆に透かしを除去してみたいという方はこの手法を試してみるのもよいだろう。
一日あれば書けるし、メモリが許せばPHPでも画像処理ができる時代なので、好きな言語で書いてみてほしい。
今は先人たちが作成した画像処理用の便利なライブラリも沢山転がっているので、そういうものを漁ってみると
もしかしたらもっと先進的な手法が見つかるかもしれない。

この記事を書いたのは[画像00]の透かしを除去しようと思ったのがきっかけである。
びえいヘルシーマラソン2012 PHOTO WEB 様からは検証用に多くの素材をお借りした。
事後報告になってしまい申し訳ないが、この場で感謝の意を表したい。
ついでに、びえいヘルシーマラソンの画像に陽を当ててくださった各氏との出会いに感謝。

これらの検証(コーディング)には Visual Studio Community 2015 を用いた。
このような優れた開発環境を無償で利用できるからこその検証である。という訳でMicrosoft社に感謝。
付録1の画像の一部、および付録2の画像においては加工にあたり画像編集ソフトウェア GIMP を用いた。The GIMP Team の皆様に感謝。

(2016/02/17)

追記(2016/02/26): delogo について

読んでいて気付いた方もいるとは思うが、ウォーターマークの除去という点で一番需要があるのはテレビ番組の局ロゴ除去である。
録画したアニメからロゴを除去し、再エンコードしてBDなり何なりに保存しているという方も多いだろう。
地デジ化してからロゴが綺麗に除去できるようになったので、この操作も割と一般化しているように思われる。
少なくともDTV関連の方々には、ロゴデータ作成・除去といった操作はそれほど珍しいものでもないはず。

この局ロゴ除去操作はdelogoとかDeLogoなどと呼ばれ、AviUtlやらAviSynthやらに向けて様々なツールが割と昔から存在する。
特に有名なのは「AviUtl用ロゴ解析プラグイン」で、これはAviUtlで読み込んだ動画から局ロゴのプロファイルを作成するものである。
これで作成したプロファイルを用いて局ロゴを除去するためのプラグインが複数存在する。

では、今回わざわざC#で実装した透かし除去アルゴリズムと局ロゴ除去(delogo)は何が違うのか。
基本的な事項・差異を以下にまとめた。

局ロゴ除去(delogo) 透かし除去(C#)
プロファイルの作成 動画の全フレームを走査し、ロゴデータの抽出に使えそうなフレームのみを選んで局ロゴデータを作成する。 理想的には透かし除去前と除去後の画像のペアを1つの標本とし、これを複数集めて尤もらしい透かし画像を推定する。
使用するデータ ロゴの背景がベタ塗りになっているフレーム。局ロゴデータの作成にはベタ塗りが多く有効サンプルも多いアニメ番組が頻繁に用いられる。 透かしが入った画像と、サムネイルを拡大した画像のペアを複数集めたもの。本当は透かし除去後の画像を用いたいところだが実際には存在しない(入手不可能である)ため、サムネイルを拡大したものを代替とする。
データの精度 基本的に高いが中程度のものも標本とする。背景がベタ塗りでさえあれば高い精度が得られる。テレビ番組はロゴ除去後の画素値が基本的に不明であるため、ロゴ除去後の画素値が精度よく推定可能なフレームのみを用いる。ふつうフレーム数は十分に多いため標本に適さないフレームを除外したとしても十分な数の標本を得ることができる。 基本的に低い。サムネイルを代替に用いることによって生じる不確実性を打ち消すために十分な数の標本を用意する必要がある。またサムネイルの縮小・拡大で生じるトーンの変化を補正する必要もある。しかし動画とは異なり枚数に限度があるため質の低い標本も上手く使う必要がある。
推定の精度 十分なフレーム数だけ標本を用意できれば綺麗にロゴを除去できる。テレビ番組を新たに録画すれば標本数を無限に増やせるため精度も際限なく向上させることが可能。 十分な数だけ標本を用意できればそれなりに綺麗にロゴを除去できる。ただし性質上、赤・青のベタ塗り部分に被ったロゴは除去が難しく、これは標本数を多くしても回避することができない。また写真の枚数は限られているため精度の向上に限度がある。
透かしの大きさ 放送局によるが基本的に小さい。大きくても 150x100 には収まっていることが多い。 画像によるが基本的に大きい。今回の事例では 360x232 の透かしが乗っていた。このため標本数を多くし過ぎるとメモリ不足に陥る。回避するには計算方法を工夫する必要がある。
放送局によるが無彩色に限らず有彩色を含むこともある。地上デジタルであれば大抵は無彩色だがBSやCSでは有彩色を含むロゴを採用している局も多い。 無彩色に限定。有彩色を含むロゴの除去も原理的には可能だが、今回はロゴが無彩色であることが初めから明らかであったため、精度を上げるために透かしは無彩色であるものとした。
透かしの除去方法 局ロゴデータを用いて除去する。詳しい仕様は各プラグインに依存する。 透かし画像データを直接使用せず、最小二乗法で求めた係数により画素値を計算する。
その他 フレームが標本として有効かどうか判定する機構が別途必要となる。放送局や放送時期によってはロゴにアニメーションが付いていることがあり、この場合はプロファイルの作成が困難。 画像の収集を別途行う必要がある。また縦長画像と横長画像は別々に解析する必要がある。
まとめ ロゴ抽出時に、ロゴ除去後の画素値を推定するためにロゴ背景がベタ塗りになっているフレームを使用し、ロゴ周囲の画素値からロゴ除去後の画素値を精度よく推定する。 透かし推定時に、透かし除去後の画素値を推定するためにサムネイルを拡大したものを用い、その画像上で透かしの入った画像と同座標の色をロゴ除去後の推定画素値とする。用いる画像は写真でありベタ塗りがほぼ存在しないため局ロゴ抽出と同じ方法は使えず、代わりにこのような方法を用いなければならない。推定画素値は精度が低いため工夫が必要。

ゴチャゴチャと書き連ねてはみたが、やはり分かり難いと思うので下の画像を見比べてほしい。
画素情報は黒の太矢印、その他の情報は青の細矢印で表している。

局ロゴ除去処理の流れ 画像26: 局ロゴ除去処理の流れ

写真からの透かし除去処理の流れ 画像27: 写真からの透かし除去処理の流れ

これは記事公開時から掲載している[画像99]をより詳細にしたものである。
局ロゴ除去処理の流れに関しては幾らかの誤りが含まれている恐れもあるが大体はこんな感じ。
つまり標本は違う形であるということが示したかった。以上。

(2016/02/26)


管理人Twitter: @su_te_ak/◆mmft4k9vgtL6
要望等はTwitterへ

Home -> 雑用 -> 雑用メモ -> [5.18 [C#]写真の透かしを除去したい]

ここ以降は鯖が勝手に付加するやつです