状況
まず、何らかの透過画像が存在するとします。
元画像の例
実際にはこの画像は持っていないとします。ただし、これを白背景に貼りつけた画像と、黒背景に貼りつけた画像は得られたとします。
白背景の例
黒背景の例
これらから、元画像を(ある程度の精度で)復元することを考えます。
前提知識
透過画像の合成の計算方法について触れます。Alpha compositing (Wikipedia) にある A over B の合成方法を用います。たぶんメジャーなので。
各画素・RGB の各値に対して、それぞれ独立に行います。
画素 A=(xA,αA) を画素 B=(xB,αB) の上に合成したものを、画素 C=(xC,αC) とします。x には r, g, b がそれぞれ入ります。なお、x や α は [0,255] の整数値ではなく [0,1] の実数値を取るとします。
このとき、(xC,αC) は次のように計算されます。
{αC=αA+αB⋅(1−αA);xC=αC1(xA⋅αA+xB⋅αB⋅(1−αA)).
考察
白背景と黒背景の画像に関して、それぞれ w と b を添字にして表すとします。
白背景の画素 Bw は (1,1)、黒背景の画素 Bb は (0,1) であり、αCw=αCb=1 です。
よって、以下が得られます。
⎩⎨⎧xCb=xA⋅αA+0⋅1⋅(1−αA)=xA⋅αA;xCw=xA⋅αA+1⋅1⋅(1−αA)=xA⋅αA+(1−αA).
よって、xCw=xCb+(1−αA) なので、αA=1+xCb−xCw を得ます。ここから、xA=xCb/αA もわかります。
ここで、αA=0 のケースには注意が必要です。「R の値は 0 だが G の値は正」といった状況で、R の値に基づいて α を計算しようとするとうまくいきません。とりあえず、RGB のうち α を最大にするものを採用します。それでも α=0 の場合は、(xA,αA)=(0,0) とします。
これに基づいて復元が可能です。
復元画像の例
合成の際に(整数に丸めるため)誤差が生じますが、人間の目にわかる程度の差はなさげです。背景は実際には白と黒ではなくても可能そうです(が、誤差が大きくなりそうな気がします)。
ビット数が 8 程度なら、全通り作っておいて表引きしてもよさそうな気もします。16 ビットある場合は表引きでは大変そうです。
Rust で書くとこんな感じ。
おわり
あまり役に立つ機会はなさそう。
ポエム
実は、プログラミング始めたての頃(たぶん 7–8 年くらい前)に書いたものがこれでした。久々に思い出したのでまとめました。あなたは始めたての頃に書いたものを覚えていますか?
最近は(実務を除いては)競プロくらいしかしていなくて、当時の方がいろいろやっていたのではないかという気もしています。
Discussion