アルファ値を含むブレンドモードの画像合成の計算式

アルファ値を考慮した場合の「乗算」合成や「加算」合成等の数式が分からなかったので、調べてまとめました。

本記事では、アルファ値 $ \alpha $ と色 $ C $ はどちらも $ [0, 1] $ の範囲で正規化されていると仮定します。
(`max` や `min` の中の定数 `1` を `255` に読み替える等、注意。)

範囲外の値になる場合は 0 から 1 の範囲内に切り詰めます。

# 0. まとめ

画像の各情報を以下の変数で表す。

|変数|説明|
|---|---|
|$\alpha$|合成後のアルファ値|
|$C$|合成後の色|
|$\alpha_b$, $\alpha_f$|後ろと手前の画像のアルファ値|
|$C_b$, $C_f$|後ろと手前の画像の色|
|$B(C_b, C_f)$|ブレンド関数 (アルファ値を考慮しない)<br><br>(例えば、加算合成なら $B(C_b, C_f) = min(1, C_b + C_f)$)|
|$F_b$, $F_f$|Porter Duff 演算の定数 (※詳細は後述)|
|$C_f'$|ブレンド後コンポジット前の色 (※詳細は後述)|

※説明を分かりやすくするように変数名をおいたため、一般的な画像合成の理論で使用される変数名と異なる場合があります。
※状況に応じて変数名を読み替えて下さい。

画像合成の理論式は

```math
\begin{align}
\alpha &= \alpha_b F_b + \alpha_f F_f \tag{10} \\
C_f' &= \alpha_b B(C_b, C_f) + (1 - \alpha_b) C_f \tag{7} \\
C &= \frac{\alpha_b F_b C_b + \alpha_f F_f C_f'}{\alpha} \tag{11}
\end{align}
```

で表される。

Porter Duff 演算の定数 $F_b$, $F_f$ は以下の表の通り。

|名前|式|$F_b$|$F_f$|説明|
|---|---|---|---|---|
|Clear|$clear$|$0$|$0$|画面クリア、全削除|
|Copy|$F$|$0$|$1$|F の画像で上書き|
|Destination|$B$|$1$|$0$|B の画像のまま何もしない|
|Source Over|$F\ over\ B$|$1 - \alpha_f$|$1$|**一般的な、通常の B の上に F を合成する演算**|
|Destination Over|$B\ over\ F$|$1$|$1 - \alpha_b$|-|
|Source In|$F\ in\ B$|$0$|$\alpha_b$|-|
|Destination In|$B\ in\ F$|$\alpha_f$|$0$|F のアルファ値を用いた**マスク**|
|Source Out|$F\ out\ B$|$0$|$1 - \alpha_b$|-|
|Destination Out|$B\ out\ F$|$1 - \alpha_f$|$0$|F のアルファ値を用いた**ステンシルマスク**|
|Source Atop|$F\ atop\ B$|$1 - \alpha_f$|$\alpha_b$|B のアルファ値を用いた**クリッピングマスク**|
|Destination Atop|$B\ atop\ F$|$\alpha_f$|$1 - \alpha_b$|-|
|XOR|$F\ xor\ B$|$1 - \alpha_f$|$1 - \alpha_b$|F と B のアルファ値を用いた排他的論理和<br><br>※ここでは「アルファ値」の演算であって、「色」の演算ではない<br>※計算は引き算で行われ、ビット演算の排他的論理和ではない|
|Plus / Lighter<br><br>(※ Add ではない)<br>(※ Lighten ではない)|$F\ plus\ B$|$1$|$1$|加算 (発光) 合成のような演算 (※詳細は後述)|

※マスク処理はいずれもアルファ値を参照し、一般的な画像編集ソフトウェアのような「色を参照するマスク処理」を直接行えるわけではないことに注意。

画像処理関係のドキュメントで使用されている名称の読み替え (例) は、以下の表の通り。

||本記事|一般|W3C のドキュメント|
|:---:|---|---|---|
|**手前の画像**|F|Source, src|A, Source|
|**後ろの画像**|B|Destination, dest|B, Backdrop ※|

※厳密には、Backdrop は後ろの画像単体のことではなく、画像合成を行うグループ内の下の画像全てを透明黒背景に合成したものを指します。
※ "Source" が「元」で "Destination" が「先」なので勘違いしやすいですが、「**Source が手前で Destination が後ろ**」の画像を指します。

ただし、**画像処理を行うソフトウェアによって、理論式と異なる式で合成している場合があります**。

# 1. 通過する色の割合による説明

## 1.1. 通常合成のアルファブレンド

不透明度 $ \alpha $ は

- $ \alpha $ の割合だけ色を書き換える
- $ (1 - \alpha) $ の割合は背景の色を通す

という意味になります。

2 枚のアルファ値を持つ (半透明な) 画像を通過する色の割合は、以下の図のように考えることができます。

![通過する色の割合1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232067/941fa201-1019-51a4-b5a2-044ba4ef0f41.png)

画像 B と F を 1 枚の画像としてみると、

- $ (1 - \alpha_b) (1 - \alpha_f) $ の割合は後ろの色を通過
- それ以外の色は画像 B と F に由来する色のみ出力

となるので、B と F を合成した不透明度は

```math
\begin{align}
\alpha &= 1 - (1 - \alpha_b) (1 - \alpha_f) \\
&= \alpha_b \alpha_f + \alpha_b (1 - \alpha_f) + (1 - \alpha_b) \alpha_f \tag{1} \\
&= \alpha_b + \alpha_f - \alpha_b \alpha_f
\end{align}
```

になります。

通過する色は以下の表のようになり、合成後の色を求めることができます。

|色の割合|通過する色|
|---|---|
| $ \alpha\_b \alpha\_f $ |F の色: $ C_f $ |
| $ \alpha\_b (1 - \alpha\_f) $ |B の色: $ C_b $ |
| $ (1 - \alpha\_b) \alpha\_f $ |F の色: $ C_f $ |
| $ (1 - \alpha\_b) (1 - \alpha\_f) $ |背景色: $ C_{tra} $ |

実際に目に見える色は

```math
C'  = \alpha_b (1 - \alpha_f) C_b + \alpha_f C_f + (1 - \alpha_b) (1 - \alpha_f) C_{tra} \tag{2}
```

で表され、B と F の合成画像と背景色の関係から

```math
C' = \alpha C + (1 - \alpha) C_{tra} \tag{3}
```

が成り立つ必要があり、(1), (2), (3) 式から B と F を合成した色は

```math
\begin{align}
C &= \frac{C' - (1 - \alpha) C_{tra}}{\alpha} \\
&= \frac{\alpha_b (1 - \alpha_f) C_b + \alpha_f C_f}{\alpha} \tag{4}
\end{align}
```

になります。

まとめると、アルファ値を含む通常合成は、

```math
\begin{align}
\alpha &= \alpha_b \alpha_f + \alpha_b (1 - \alpha_f) + (1 - \alpha_b) \alpha_f \tag{1} \\
&= \alpha_b + \alpha_f - \alpha_b \alpha_f \\
&= \alpha_b (1 - \alpha_f) + \alpha_f \\
C &= \frac{\alpha_b (1 - \alpha_f) C_b + \alpha_f C_f}{\alpha} \tag{4}
\end{align}
```

の式で計算できます。

ただし、$ \alpha = 0 $ (完全に透明) の場合は $ C $ の値は求められないため、適当な値 ($C = 0$ 等) にします。

## 1.2. 通常合成以外も含むアルファブレンド

通常合成のみなら理想的にアルファブレンドを行うことができますが、通常合成以外も含むと理想的な (結合法則が成り立つ) 合成することができません (※理由は後述) 。

一般的な画像編集ソフトウェアでは、**完全な透明色に対する合成を行った場合はブレンドモードにかかわらず通常合成をする**仕様になっており、この仕様で考えると、

- 背景色の上に B を合成する場合
- 背景色の上に F を合成する場合

がそれぞれブレンドモードによらず通常合成として扱われることになり、通過する色が以下の表のようになります。

|色の割合|通過する色|
|---|---|
| $ \alpha\_b \alpha\_f $ |背景色の上に B を合成し、その上に F を合成した色:<br> $ B(C_b, C_f) $ |
| $ \alpha\_b (1 - \alpha\_f) $ |背景色の上に B を合成した色: $ C_b $ |
| $ (1 - \alpha\_b) \alpha\_f $ |背景色の上に F を合成した色: $ C_f $ |
| $ (1 - \alpha\_b) (1 - \alpha\_f) $ |背景色: $ C_{tra} $ |

![通過する色の割合2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232067/b3e7a87a-fc18-e5a4-cd6a-302b63e18909.png)

目に見える色は

```math
C' = \alpha_b \alpha_f B ( C_b, C_f ) + \alpha_b (1 - \alpha_f) C_b + (1 - \alpha_b) \alpha_f C_f + (1 - \alpha_b) (1 - \alpha_f) C_{tra} \tag{5}
```

で表され、B と F の合成画像と背景色が通常合成されるとすれば

```math
C' = \alpha C + (1 - \alpha) C_{tra} \tag{3}
```


が成り立つことから、(1), (3), (5) 式より合成色は

```math
C = \frac{\alpha_b \alpha_f B ( C_b, C_f ) + \alpha_b (1 - \alpha_f) C_b + (1 - \alpha_b) \alpha_f C_f}{\alpha} \tag{6}
```

になります。

まとめると、アルファ値を含み、通常合成以外を含む合成色は

```math
\begin{align}
\alpha &=\alpha_b \alpha_f + \alpha_b (1 - \alpha_f) + (1 - \alpha_b) \alpha_f \tag{1} \\
C &= \frac{\alpha_b \alpha_f B ( C_b, C_f ) + \alpha_b (1 - \alpha_f) C_b + (1 - \alpha_b) \alpha_f C_f}{\alpha} \tag{6}
\end{align}
```

の式で計算できます。

# 2. 一般化された画像合成の式

実際の画像合成では、「色のブレンド」と「Porter Duff 演算 (コンポジット)」の方式を組み合わせることで、より多くの種類の画像合成を実現しています。

参考「[Compositing and Blending Level 1](https://www.w3.org/TR/compositing-1/)」(※式中の $C$ と $c$ の大文字小文字で意味が異なるので注意。一部大文字と小文字の間違いあり)

## 2.1. 色のブレンドの式

アルファ値を考慮しないブレンド関数 $B(C_b, C_f)$ を用いて

```math
C_f' = \alpha_b B(C_b, C_f) + (1 - \alpha_b) C_f \tag{7} \\
```

の式で表されます。

この段階では $\alpha_b$ のみを考慮し、$\alpha_f$ は考えません。

ブレンド関数は例えば以下の表のようなものがあります。

|日本語名|英語名|ブレンド関数|
|---|---|---|
|通常|Normal|$B(C_b, C_f) = C_f$|
|乗算|Multiply|$B(C_b, C_f) = C_b C_f$|
|加算|Linear Dodge / Add|$B(C_b, C_f) = min(1, C_b + C_f)$|

ブレンド関数に関する記事は他に沢山ありますので、各自で確認してください。

参考「[10. Blending - Compositing and Blending Level 1](https://www.w3.org/TR/compositing-1/#blending)」
参考「[Formulas for Photoshop blending modes - Rogelio Bernal Andreo, Premium Astrophotography - DeepSkyColors.com](https://www.deepskycolors.com/archive/2010/04/21/formulas-for-Photoshop-blending-modes.html)」(※ブレンド関数の参考として載せますが、Photoshop が理論式通りにアルファ値を考慮して計算を行うかは未確認)

## 2.2. Porter Duff 演算 (コンポジット) の式

Porter Duff の等式の一般形は $ \alpha $ と $ C $ を用いて

```math
\alpha C = \alpha_b F_b C_b + \alpha_f F_f C_f \tag{8}
```

と表され、見える色は

```math
\begin{align}
C' &= \alpha C + (1 - \alpha) C_{tra} \\
&= \alpha_b F_b C_b + \alpha_f F_f C_f + (1 - \alpha) C_{tra} \tag{9}
\end{align}
```

となることから、色の割合は以下の表のようになることが分かります。

|色の割合|通過する色|
|---|---|
| $ \alpha\_b F\_b $ |B の色: $ C_b $ |
| $ \alpha\_f F\_f $ |F の色: $ C_f $ |
| $ 1 - (\alpha\_b F\_b + \alpha\_f F\_f) $ |背景色: $ C_{tra} $ |

よって不透明度は

```math
\alpha = \alpha_b F_b + \alpha_f F_f \tag{10}
```

となります。

## 2.3. 「ブレンド」と「コンポジット」を組み合わせた画像合成

「ブレンド」してから「コンポジット」すると、式は

```math
\begin{align}
\alpha &= \alpha_b F_b + \alpha_f F_f \tag{10} \\
C_f' &= \alpha_b B(C_b, C_f) + (1 - \alpha_b) C_f \tag{7} \\
C &= \frac{\alpha_b F_b C_b + \alpha_f F_f C_f'}{\alpha} \tag{11}
\end{align}
```

となります。

# 3. 補足情報

## 3.1. 画像処理ソフトウェアによっては理論式と一致しない

ImageMagick 等では理論式に忠実に画像合成を行いますが、CLIP STUDIO PAINT や FireAlpaca といったソフトウェアでは独自の式で画像合成を実装していて、理論式と一致しないことがあります。

特に背景や前景に透明度を持つ (不透明でない) ときに理論値とズレることが多いです。

CLIP STUDIO PAINT や FireAlpaca に関して「加算合成」と「加算 (発光) 合成」の式の検証し、別記事にしました。

参考「[「加算合成」と「加算 (発光) 合成」の違い - Qiita](https://qiita.com/kerupani129/items/215b1f973672452f2d2f)」

## 3.2. Porter Duff 演算子 `plus` について

`plus` 演算子は「コンポジット」時に加算を行う演算であり、「ブレンド」時に加算を行う「加算合成 (Linear Dodge / Add)」とは意味が異なります。

前述の通り、ソフトウェアにより実装が異なるため一概には言えませんが、「Normal ブレンド & Plus コンポジット」したものが一般的な「加算 (発光) 合成」の挙動に近いです。

一方で、発光でない「加算合成」は「Add ブレンド & Source Over コンポジット」になります。

## 3.3. 通常合成以外の任意の場合で理想的な (結合法則が成り立つ) 合成は不可

![通過する色の割合2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232067/b3e7a87a-fc18-e5a4-cd6a-302b63e18909.png)

任意のブレンドモードに関して素直に考えると、通過する色は以下の表のようになります。

|色の割合|通過する色|
|---|---|
| $ \alpha\_b \alpha\_f $ |背景色の上に B を合成し、その上に F を合成した色:<br> $ B_f \left( B_b(C_{tra}, C_b), C_f \right) $ |
| $ \alpha\_b (1 - \alpha\_f) $ |背景色の上に B を合成した色:<br> $ B_b(C_{tra}, C_b) $ |
| $ (1 - \alpha\_b) \alpha\_f $ |背景色の上に F を合成した色:<br> $ B_f(C_{tra}, C_f) $ |
| $ (1 - \alpha\_b) (1 - \alpha\_f) $ |背景色: $ C_{tra} $ |

目に見える色は

```math
C' = \alpha_b \alpha_f B_f \left( B_b(C_{tra}, C_b), C_f \right) + \alpha_b (1 - \alpha_f) B_b(C_{tra}, C_b) + (1 - \alpha_b) \alpha_f B_f(C_{tra}, C_f) + (1 - \alpha_b) (1 - \alpha_f) C_{tra} \tag{12}
```

で表され、B と F を合成した不透明度 $ \alpha $ 色 $ C $ の画像に関する不透明度の関係から

```math
C' = \alpha B_{bf}(C_{tra}, C) + (1 - \alpha) C_{tra} \tag{13}
```

が成り立つ必要があり、(1), (12), (13) 式から

```math
\begin{align}
B_{bf}(C_{tra}, C) &= \frac{C' - (1 - \alpha) C_{tra}}{\alpha} \\
&= \frac{\alpha_b \alpha_f B_f \left( B_b(C_{tra}, C_b), C_f \right) + \alpha_b (1 - \alpha_f) B_b(C_{tra}, C_b) + (1 - \alpha_b) \alpha_f B_f(C_{tra}, C_f)}{\alpha} \tag{14}
\end{align}
```

になります。

ところが、$B_{bf}(C_{tra}, C)$ を既存のブレンド関数から選び、任意の $B_b(C_{tra}, C_b)$, $B_f(C_{tra}, C_b)$ に関して $C_{tra}$ を打ち消すことはできません。
つまり、背景色が分からない段階で B と F を合成することができません。

よって、式を変更して $C_{tra}$ を含む項を打ち消せるようにする必要があります。
その方法のひとつとして、一般的には「完全な透明色に対する合成を行った場合はブレンドモードにかかわらず通常合成をする」とすることで解決しています。
その場合、 結合法則は成り立たなくなります。