FFmpegで指定できる輝度の伝達特性の一覧


FFmpegの入出力オプション[`color_trc`](https://ffmpeg.org/ffmpeg-all.html#Codec-Options)で指定可能な輝度の伝達特性を表す関数の一覧。
参考に[`zscale`](https://ffmpeg.org/ffmpeg-all.html#zscale-1)フィルタの該当ソースコードを引用する。[^0]ここで、$L$は輝度に線形な電気信号値[^15]、$V$は伝達後の電気信号値である。

| 指定可能な値                 | 伝達特性                                                                | 
| :--------------------------- | :---------------------------------------------------------------------- | 
| bt709                        | [**Rec. ITU-R BT.709-6**](#rec-itu-r-bt709-66)                          | 
| gamma22                      | [**Rec. ITU-R BT.470-6 System M**](#rec-itu-r-bt470-6-system-m11)         | 
| gamma28                      | [**Rec. ITU-R BT.470-6 System B, G**](#rec-itu-r-bt470-6-system-b-g11)    | 
| smpte170m                    | [**SMPTE ST 170 (2004)**](#smpte-st-170-2004)                          | 
| smpte240m                    | [**SMPTE ST 240 (1999)**](#smpte-st-240-199912)         | 
| linear                       | [**Linear transfer characteristics**](#linear-transfer-characteristics) | 
| log<br>log100                | [**Rec. ITU-T H.273 Log**](#rec-itu-t-h273-log13)            | 
| log_sqrt<br>log316           | [**Rec. ITU-T H.273 Log Sqrt**](#rec-itu-t-h273-log-sqrt13) | 
| iec61966_2_4<br>iec61966-2-4 | [**IEC 61966-2-4 xvYCC**](#iec-61966-2-4-xvycc)                         | 
| bt1361<br>bt1361e            | [**Rec. ITU-R BT.1361-0 extended colour gamut system**](#rec-itu-r-bt1361-0-extended-colour-gamut-system)                   | 
| iec61966_2_1<br>iec61966-2-1 | [**IEC 61966-2-1 sRGB**](#iec-61966-2-1-srgb)                           | 
| bt2020_10<br>bt2020_10bit    | [**Rec. ITU-R BT.2020-2 (10-bit system)**](#rec-itu-r-bt2020-2-10-bit-system)         | 
| bt2020_12<br>bt2020_12bit    | [**Rec. ITU-R BT.2020-2 (12-bit system)**](#rec-itu-r-bt2020-2-12-bit-system)         | 
| smpte2084                    | [**SMPTE ST 2084 PQ**](#smpte-st-2084)                                  | 
| smpte428<br>smpte428_1       | [**SMPTE ST 428-1 (2019)**](#smpte-st-428-1-2019)                                               | 
| arib-std-b67                 | [**ARIB STD-B67 HLG**](#arib-std-b67)                                   | 

**特別な伝達特性**
| 指定可能な値           | Rec. ITU-T H.273による説明                                                 | 
| :--------------------- | :------------------------------------------------------------------------- | 
| unknown<br>unspecified | Image characteristics are unknown or<br>are determined by the application. | 
| reserved               | For future use by ITU-T \| ISO/IEC                                         |

**Rec. ITU-T H.273 CICP Code Value**[^9]
| 指定可能な値                 | CV　　　　&nbsp; | 
| :--------------------------- | ------------- | 
| bt709                        | 1             | 
| unknown<br>unspecified       | 2             | 
| reserved                     | 3             |
| gamma22                      | 4             | 
| gamma28                      | 5             | 
| smpte170m                    | 6             | 
| smpte240m                    | 7             | 
| linear                       | 8             | 
| log<br>log100                | 9             | 
| log_sqrt<br>log316           | 10            | 
| iec61966_2_4<br>iec61966-2-4 | 11            | 
| bt1361<br>bt1361e            | 12            | 
| iec61966_2_1<br>iec61966-2-1 | 13            | 
| bt2020_10<br>bt2020_10bit    | 14            | 
| bt2020_12<br>bt2020_12bit    | 15            | 
| smpte2084                    | 16            | 
| smpte428<br>smpte428_1       | 17            | 
| arib-std-b67                 | 18            | 

**ビデオフィルタの対応状況**
変換に対応していない伝達関数には、"×"を付けている。
|  |  colorspace | zscale | libplacebo |
| :--------------------------- | --- | --- |  --- |
| bt709                        |◯  | ◯  |  ◯  | 
| unknown<br>unspecified       |×   | ×   |  ×   | 
| reserved                     |×   | ×   |  ×   | 
| gamma22                      |◯  | ◯  |  ◯  | 
| gamma28                      |◯  | ◯  |  ◯  | 
| smpte170m                    |◯  | ◯  |  ◯  | 
| smpte240m                    |◯  | ◯  |  ◯  | 
| linear                       |◯  | ◯  |  ◯  | 
| log<br>log100                |×   | ◯  |  ×   | 
| log_sqrt<br>log316           |×   | ◯  |  ×   | 
| iec61966_2_4<br>iec61966-2-4 |◯  | ◯  |  ◯  | 
| bt1361<br>bt1361e            | × | × | △<br>[^8]|
| iec61966_2_1<br>iec61966-2-1 |◯  | ◯  |  ◯  | 
| bt2020_10<br>bt2020_10bit    |◯  | ◯  |  ◯  | 
| bt2020_12<br>bt2020_12bit    |◯  | ◯  |  ◯  | 
| smpte2084                    |×   | ◯  |  ◯  | 
| smpte428<br>smpte428_1       |×   | ×   |  ×   | 
| arib-std-b67                 |×   | ◯  |  ◯  | 

各フィルターの特徴は以下の通り。
- colorspaceは、Scene-referred[^2]の変換にのみ対応している。
- zscaleは、colorspaceよりもRec. ITU-T H.273に沿った動作をする。そのため、より多くの計算資源を必要とする。
- libplaceboは、トーンマッピングとHDRアップスケーリングアルゴリズムが最も充実している。動作にはVulkan互換のGPUが必要。

**フィルタオプションエイリアス**
同じ行にある名前は、同じ伝達関数を指す。フィルタ側で指定できない場合は、"×"を付けている。"×"のついた伝達関数は [`color_trc`](https://ffmpeg.org/ffmpeg-all.html#Codec-Options)　[`setparams`](https://ffmpeg.org/ffmpeg-all.html#setparams-1) によって強制的に指定することができるが、フィルター内部ではunknown、unspecifiedとして扱われる。
 | 指定可能な値                 | colorspace            | zscale                 | libplacebo   | 
| :--------------------------- | --------------------- | ---------------------- | ------------ | 
| bt709                        | bt709                 | 709<br>bt709           | bt709        | 
| unknown<br>unspecified       | ×                     | unknown<br>unspecified | unknown      | 
| reserved                     | ×                     | ×                      | ×            | 
| gamma22                      | bt470m<br>gamma22     | bt470m                 | bt470m       | 
| gamma28                      | bt470bg<br>gamma28    | bt470bg                | bt470bg      | 
| smpte170m                    | smpte170m             | 601<br>smpte170m       | smpte170m    | 
| smpte240m                    | smpte240m             | smpte240m              | smpte240m    | 
| linear                       | linear                | linear                 | linear       | 
| log<br>log100                | ×                     | log100                 | ×            | 
| log_sqrt<br>log316           | ×                     | log316                 | ×            | 
| iec61966_2_4<br>iec61966-2-4 | xvycc<br>iec61966-2-4 | iec61966-2-4           | iec61966-2-4 | 
| bt1361<br>bt1361e            | ×                     | ×                      | bt1361e      | 
| iec61966_2_1<br>iec61966-2-1 | srgb<br>iec61966-2-1  | iec61966-2-1           | iec61966-2-1 | 
| bt2020_10<br>bt2020_10bit    | bt2020-10             | 2020_10<br>bt2020-10   | bt2020-10    | 
| bt2020_12<br>bt2020_12bit    | bt2020-12             | 2020_12<br>bt2020-12   | bt2020-12    | 
| smpte2084                    | ×                     | smpte2084              | smpte2084    | 
| smpte428<br>smpte428_1       | ×                     | ×                      | ×            | 
| arib-std-b67                 | ×                     | arib-std-b67           | arib-std-b67 |


OETFとEOTFの関係。
![OFTF-EOTF Workflow!](https://i.imgur.com/4dAy77Y.png)
伝達関数に応じて輝度の分布が変化するイメージ、横軸が<b>入力</b>、縦軸が<b>出力</b>。画像はGIMPのトーンカーブ。
ここでは入力画像の暗い部分が伸張され、明るい部分が圧縮されている。また、入力画像のゼロ付近が強く伸張されている。
![Imitation of BT.709 OETF](https://i.imgur.com/RxxGt87.gif)

## Rec. ITU-R BT.709-6 [^1]
一般的なSDRの転送特性。

**非線形な値へ変換する場合**
もしScene-referred[^2]ならば、Rec. 709 OETFを適用する、もしDisplay-referred[^14]ならばRec. 1886 inverse EOTFを適用する。

**線形な値へ変換する場合**
もしScene-referredならば、Rec. 709 inverse OETFを適用する、もしDisplay-referredならばRec. 1886 EOTFを適用する。

OOTF[^10]は(Rec. 1886 EOTF ∘ Rec. 709 OETF)(L)となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
…
case TransferCharacteristics::REC_709:
	func.to_linear = scene_referred ? rec_709_inverse_oetf : rec_1886_eotf;
	func.to_gamma = scene_referred ? rec_709_oetf : rec_1886_inverse_eotf;
	break;
```
Scene-referredな伝達関数

**Rec. 709 OETF**

```math
\mbox{V=}\begin{cases}
1.09929682680944 L^{0.45} - (1.09929682680944 - 1.0) & 1.0 \ge L \ge 0.018053968510807\\
4.5L & 0.018053968510807 > L \ge 0.0
\end{cases}
```

**Rec. 709 inverse OETF**

```math
\mbox{L=}\begin{cases}
\left ( \dfrac{ V+(1.09929682680944 - 1.0) }{1.09929682680944} \right ) ^{\frac{1.0}{0.45} } & 1.0 \ge V \ge 4.5\cdot0.018053968510807\\
\dfrac{V}{4.5} & 4.5\cdot0.018053968510807 > V \ge 0.0
\end{cases}
```


```cpp
// src/zimg/colorspace/gamma.cppより

constexpr float REC709_ALPHA = 1.09929682680944f;
constexpr float REC709_BETA = 0.018053968510807f;

float rec_709_oetf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < REC709_BETA)
		x = x * 4.5f;
	else
		x = REC709_ALPHA * zimg_x_powf(x, 0.45f) - (REC709_ALPHA - 1.0f);

	return x;
}

float rec_709_inverse_oetf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < 4.5f * REC709_BETA)
		x = x / 4.5f;
	else
		x = zimg_x_powf((x + (REC709_ALPHA - 1.0f)) / REC709_ALPHA, 1.0f / 0.45f);

	return x;
}
```
Display-referredな伝達関数

黒レベルを0.0cd/m^2とすると
**Rec. 1886 inverse EOTF**

$\mbox{V=}{L}^{\frac{1.0}{2.4}}\qquad{L}\ge 0.0$

**Rec. 1886 EOTF**

$\mbox{L=}{V}^{2.4}\qquad{V}\ge 0.0$

```cpp
// src/zimg/colorspace/gamma.cppより

// Ignore the BT.1886 provisions for limited contrast and assume an ideal CRT.
float rec_1886_inverse_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 1.0f / 2.4f);
}

float rec_1886_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 2.4f);
}
```

**OOTF(L)=(Rec. 1886 EOTF ∘ Rec. 709 OETF)(L)**
ディスプレイの輝度[^12]がシーンの輝度[^11]より全体的に暗くなる(輝度1.0以上を除く)。
ガンマ補正$\mbox{V=}L^{1.22}$で近似できるが、比較すると暗い部分がより圧縮されていることがわかる。


```math
\mbox{V=}\begin{cases}
(1.09929682680944 L^{0.45}−(1.09929682680944−1.0) )^{2.4} & L \ge 0.018053968510807\\
(4.5L)^{2.4} & 0.018053968510807 > L \ge 0.0
\end{cases}
```


## Rec. ITU-R BT.470-6 System M[^3]
**非線形な値へ変換する場合**
Rec. 470M OETFを適用する

**線形な値へ変換する場合**
Rec. 470M inverse OETFを適用する

OOTF[^10]は線形となる。

```cpp
// src/zimg/colorspace/gamma.cppより
// OETFとinverse OETF、func.to_linearとfunc.to_gammaが逆になっている

switch (transfer) {
…
case TransferCharacteristics::REC_470_M:
	func.to_linear = rec_470m_oetf;
	func.to_gamma = rec_470m_inverse_oetf;
	break;
```
 
**Rec. 470M OETF**
```math
V={L}^{\frac{1.0}{2.2}}\qquad{L}\ge 0.0
```
**Rec. 470M inverse OETF**
```math
L={V}^{2.2}\qquad{V}\ge 0.0
```

```cpp
// src/zimg/colorspace/gamma.cppより

float rec_470m_oetf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 2.2f);
}

float rec_470m_inverse_oetf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 1.0f / 2.2f);
}
```

## Rec. ITU-R BT.470-6 System B, G[^3]

**非線形な値へ変換する場合**
Rec. 470BG OETFを適用する

**線形な値へ変換する場合**
Rec. 470BG inverse OETFを適用する

OOTF[^10]は線形となる。

```cpp
// src/zimg/colorspace/gamma.cppより
// OETFとinverse OETF、func.to_linearとfunc.to_gammaが逆になっている
switch (transfer) {
…
case TransferCharacteristics::REC_470_BG:
	func.to_linear = rec_470bg_oetf;
	func.to_gamma = rec_470bg_inverse_oetf;
	break;
```

**Rec. 470BG OETF**
```math
V=^{\frac{1.0}{2.8}}\qquad{L}\ge 0.0
```
**Rec. 470BG inverse OETF**
```math
L={V}^{2.8}\qquad{V}\ge 0.0
```

```cpp
// src/zimg/colorspace/gamma.cppより

float rec_470bg_oetf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 2.8f);
}

float rec_470bg_inverse_oetf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 1.0f / 2.8f);
}
```

## SMPTE ST 170 (2004)
機能的に[**Rec. ITU-R BT.709-6**](#rec-itu-r-bt709-66)と同じ。

```cpp
// src/zimg/api/zimg.cppより

zimg::colorspace::TransferCharacteristics translate_transfer(zimg_transfer_characteristics_e transfer)
{
	using zimg::colorspace::TransferCharacteristics;

	static SM_CONSTEXPR_14 const zimg::static_map<zimg_transfer_characteristics_e, TransferCharacteristics, 15> map{
		{ ZIMG_TRANSFER_BT709,         TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_UNSPECIFIED,   TransferCharacteristics::UNSPECIFIED },
		{ ZIMG_TRANSFER_ST240_M,       TransferCharacteristics::SMPTE_240M },
		{ ZIMG_TRANSFER_BT601,         TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_BT470_M,       TransferCharacteristics::REC_470_M },
		{ ZIMG_TRANSFER_BT470_BG,      TransferCharacteristics::REC_470_BG },
		{ ZIMG_TRANSFER_IEC_61966_2_4, TransferCharacteristics::XVYCC },
		{ ZIMG_TRANSFER_IEC_61966_2_1, TransferCharacteristics::SRGB },
		{ ZIMG_TRANSFER_BT2020_10,     TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_BT2020_12,     TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_LINEAR,        TransferCharacteristics::LINEAR },
		{ ZIMG_TRANSFER_LOG_100,       TransferCharacteristics::LOG_100 },
		{ ZIMG_TRANSFER_LOG_316,       TransferCharacteristics::LOG_316 },
		{ ZIMG_TRANSFER_ST2084,        TransferCharacteristics::ST_2084 },
		{ ZIMG_TRANSFER_ARIB_B67,      TransferCharacteristics::ARIB_B67 },
	};
	return search_itu_enum_map(map, transfer, "unrecognized transfer characteristics");
}
```

## SMPTE ST 240 (1999)[^6]
**非線形な値へ変換する場合**
もしScene-referred[^2]ならば、SMPTE 240M OETFを適用する、もしDisplay-referred[^14]ならばRec. 1886 inverse EOTFを適用する。

**線形な値へ変換する場合**
もしScene-referredならば、SMPTE 240M inverse OETFを適用する、もしDisplay-referredならばRec. 1886 EOTFを適用する。

OOTF[^10]は(Rec. 1886 EOTF ∘ SMPTE 240M OETF)(L)となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
…
case TransferCharacteristics::SMPTE_240M:
	func.to_linear = scene_referred ? smpte_240m_inverse_oetf : rec_1886_eotf;
	func.to_gamma = scene_referred ? smpte_240m_oetf : rec_1886_inverse_eotf;
	break;
```
Scene-referredな伝達関数

**SMPTE 240M OETF**

```math  
V=\begin{cases}
1.111572195921731 L^{0.45} - (1.111572195921731 - 1.0) & 1.0 \ge L \ge 0.022821585529445\\
4.0L & 0.022821585529445 > L \ge 0.0
\end{cases}
```


**SMPTE 240M inverse OETF**

```math
L=\begin{cases}
\left ( \dfrac{ V+(1.111572195921731 - 1.0) }{1.111572195921731} \right ) ^{\frac{1.0}{0.45} } & 1.0 \ge　V \ge 4.0\cdot0.022821585529445\\
\dfrac{V}{4.0} & 4.0\cdot0.022821585529445 > V \ge 0.0
\end{cases}
```


```cpp
// src/zimg/colorspace/gamma.cppより
// OETFとinverse OETFが逆になっている

constexpr float SMPTE_240M_ALPHA = 1.111572195921731f;
constexpr float SMPTE_240M_BETA  = 0.022821585529445f;

float smpte_240m_oetf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < 4.0f * SMPTE_240M_BETA)
		x = x / 4.0f;
	else
		x = zimg_x_powf((x + (SMPTE_240M_ALPHA - 1.0f)) / SMPTE_240M_ALPHA, 1.0f / 0.45f);

	return x;
}

float smpte_240m_inverse_oetf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < SMPTE_240M_BETA)
		x = x * 4.0f;
	else
		x = SMPTE_240M_ALPHA * zimg_x_powf(x, 0.45f) - (SMPTE_240M_ALPHA - 1.0f);

	return x;
}
```
Display-referredな伝達関数

黒レベルを0.0cd/m^2とすると
**Rec. 1886 inverse EOTF**
```math
\mbox{V=}{L}^{\frac{1.0}{2.4}}\qquad{L}\ge 0.0
```
**Rec. 1886 EOTF**
```math
\mbox{L=}{V}^{2.4}\qquad{V}\ge 0.0
```

```cpp
// src/zimg/colorspace/gamma.cppより

// Ignore the BT.1886 provisions for limited contrast and assume an ideal CRT.
float rec_1886_inverse_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 1.0f / 2.4f);
}

float rec_1886_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 2.4f);
}
```

**OOTF(L)=(Rec. 1886 EOTF ∘ SMPTE 240M OETF)(L)**
ディスプレイの輝度[^12]がシーンの輝度[^11]より全体的に暗くなる(輝度1.0以上を除く)。
ガンマ補正$\mbox{V=}L^{1.20}$で近似できるが、比較すると暗い部分がより圧縮されていることがわかる。


```math
\mbox{V=}\begin{cases}
(1.111572195921731 L^{0.45}−(1.111572195921731−1.0) )^{2.4} & L \ge 0.022821585529445\\
(4.0L)^{2.4} & 0.022821585529445 > L \ge 0.0
\end{cases}
```


## Linear transfer characteristics
**伝達特性は線形**
値が変化しない、いうまでもなく$V$は輝度に線形。

**Linear EOTF**
```math
\mbox{V=}L
```
**Linear inverse EOTF**
```math
\mbox{L=}V
```
OOTF[^10]は線形となる。

## Rec. ITU-T H.273 Log[^5]
伝達関数が$L \ge 0.01$ と $V \ge 0.0$にclampされているのが特徴。
Rec. ITU-T H.273ではLogarithmic transfer characteristic (100:1 range)と呼ばれている。

**非線形な値へ変換する場合**
Log 100 OETFを適用する。

**線形な値へ変換する場合**
Log 100 inverse OETFを適用する。

OOTF[^10]は線形となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
case TransferCharacteristics::LOG_100:
	func.to_linear = log100_inverse_oetf;
	func.to_gamma = log100_oetf;
	break;
```

$1.0 + \frac{\log_{10}(\color{blue}{0.01})}{2.0}=\color{blue}0$ であり、$\mbox{L=}0.01$の場合は$\mbox{V=}0$となる、よってzimgでは式の区間を調整している。
つまり、
**H.273 Log 100 OETF**

```math
\mbox{V=}\begin{cases}
1.0 + \dfrac{\log_{10}(L)}{2.0} & 1.0 \ge L \color{blue}\ge\color{black} 0.01\\
0.0 & 0.01 \color{blue}>\color{black} L \ge 0.0
\end{cases}
```


<span style="color:#07a;">以上青文字部分</span>を

**zimg Log 100 OETF**

```math
\mbox{V=}\begin{cases}
1.0 + \dfrac{\log_{10}(L)}{2.0} & 1.0 \ge L \color{red}>\color{black} 0.01\\
0.0 & 0.01 \color{red}\ge\color{black} L \ge 0.0
\end{cases}
```


<span style="color:#dd4a68;">以上赤文字部分</span>としている。

**zimg Log 100 inverse OETF**

```math
\mbox{L=}\begin{cases}
10^{2\cdot(V-1.0)} & 1.0 \ge V > 0.0\\
0.01 & 0.0 \ge V \ge -\infty
\end{cases}
```

<br>
<details><summary>別式</summary><div>

**H.273 Log 100 OETF**

```math
\mbox{V=}\begin{cases}
1.0 + \dfrac{\log_{10}(L)}{2.0} & 1.0 \ge L \ge 0.01\\
0.0 & 0.01 > L \ge 0.0
\end{cases}
```

**H.273 Log 100 inverse OETF**

```math
\mbox{L=}\begin{cases}
10^{2\cdot(V-1.0)} & 1.0 \ge V \ge 1.0 + \dfrac{\log_{10}(0.01)}{2.0} = 0.0\\
0.0 & 0.0 = 1.0 + \dfrac{\log_{10}(0.01)}{2.0} > V \ge -\infty
\end{cases}
```

</div></details>

<br>

```cpp
// src/zimg/colorspace/gamma.cppより

float log100_oetf(float x) noexcept
{
	return x <= 0.01f ? 0.0f : 1.0f + zimg_x_log10f(x) / 2.0f;
}

float log100_inverse_oetf(float x) noexcept
{
	return x <= 0.0f ? 0.01f : zimg_x_powf(10.0f, 2 * (x - 1.0f));
}
```

## Rec. ITU-T H.273 Log Sqrt[^5]
伝達関数が$L \ge \frac{\sqrt{10}} {1000}$ と $V \ge 0.0$にclampされているのが特徴。
Rec. ITU-T H.273ではLogarithmic transfer characteristic (100 * Sqrt ( 10 ) : 1 range)と呼ばれている。

**非線形な値へ変換する場合**
Log 316 OETFを適用する。

**線形な値へ変換する場合**
Log 316 inverse OETFを適用する。

OOTF[^10]は線形となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
…
case TransferCharacteristics::LOG_316:
	func.to_linear = log316_inverse_oetf;
	func.to_gamma = log316_oetf;
	break;
```

$1.0 + \frac{\log_{10}(\color{blue}{\frac{\sqrt{10}}{1000}})}{2.5}=\color{blue}0$ であり、$\mbox{L=}\frac{\sqrt{10}}{1000}$の場合は$\mbox{V=}0$となる、よってzimgでは式の区間を調整している。
つまり、
**H.273 Log 316 OETF**


```math
\mbox{V=}\begin{cases}
1.0 + \dfrac{\log_{10}(L)}{2.5} & 1.0 \ge L \color{blue}\ge \dfrac{\sqrt{10}}{1000}\\
0.0 & \dfrac{\sqrt{10}}{1000} \color{blue}> L \ge 0.0
\end{cases}
```


<span style="color:#07a;">以上青文字部分</span>を

**zimg Log 316 OETF**

```math
\mbox{V=}\begin{cases}
1.0 + \dfrac{\log_{10}(L)}{2.5} & 1.0 \ge L \color{red}> \dfrac{\sqrt{10}}{1000}\\
0.0 & \dfrac{\sqrt{10}}{1000} \color{red}\ge L \ge 0.0
\end{cases}
```


<span style="color:#dd4a68;">以上赤文字部分</span>としている。



**zimg Log 316 inverse OETF**


```math
\mbox{L=}\begin{cases}
10^{2.5\cdot(V-1.0)} & 1.0 \ge V > 0.0\\
\dfrac{\sqrt{10}}{1000} & 0.0 \ge V -\infty
\end{cases}
```

<br>

<details><summary>別式</summary><div>

**H.273 Log 316 OETF**

```math
\mbox{V=}\begin{cases}
1.0 + \dfrac{\log_{10}(L)}{2.5} & 1.0 \ge L \ge \dfrac{\sqrt{10}}{1000}\\
0.0 & \dfrac{\sqrt{10}}{1000} > L \ge 0.0
\end{cases}
```

**H.273 Log 316 inverse OETF**

```math
\mbox{L=}\begin{cases}
10^{2.5\cdot(V-1.0)} & 1.0 \ge V \ge 1.0 + \dfrac{\log_{10}(\frac{\sqrt{10}}{1000})}{2.5} = 0.0\\
0.0 & 0.0 = 1.0 + \dfrac{\log_{10}(\frac{\sqrt{10}}{1000})}{2.5} > V \ge -\infty
\end{cases}
```

</div></details>
<br>

```cpp
// src/zimg/colorspace/gamma.cppより

float log316_oetf(float x) noexcept
{
	return x <= 0.00316227766f ? 0.0f : 1.0f + zimg_x_log10f(x) / 2.5f;
}

float log316_inverse_oetf(float x) noexcept
{
	return x <= 0.0f ? 0.00316227766f : zimg_x_powf(10.0f, 2.5f * (x - 1.0f));
}
```

## IEC 61966-2-4 xvYCC
Rec. ITU-R BT.709-5(04/02)をスーパーホワイト、サブブラックまで拡張した伝達特性。
定義域と値域は$\infty \ge L \ge -\infty$、$\infty \ge V \ge -\infty$で、原点に関して対称である。

**非線形な値へ変換する場合**
もしScene-referred[^2]ならば、xvYCC OETFを適用する、もしDisplay-referred[^14]ならばxvYCC inverse EOTFを適用する。

**線形な値へ変換する場合**
もしScene-referredならば、xvYCC inverse OETFを適用する、もしDisplay-referredならばxvYCC EOTFを適用する。

OOTF[^6]は(xvYCC EOTF ∘ xvYCC OETF)(L)となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
…
case TransferCharacteristics::XVYCC:
	func.to_linear = scene_referred ? xvycc_inverse_oetf : xvycc_eotf;
	func.to_gamma = scene_referred ? xvycc_oetf : xvycc_inverse_eotf;
	break;
```
Scene-referredな伝達関数

**xvYCC OETF**

```math
\mbox{V=}\begin{cases}
1.09929682680944 L^{0.45} - (1.09929682680944 - 1.0) & \infty \ge L \ge 0.018053968510807\\
4.5L & 0.018053968510807 > L > -0.018053968510807\\
1.09929682680944 L^{0.45} - (1.09929682680944 - 1.0) & -0.018053968510807 \ge L \ge -\infty
\end{cases}
```


**xvYCC inverse OETF**

```math
\mbox{L=}\begin{cases}
\left ( \dfrac{ V+(1.09929682680944 - 1.0) }{1.09929682680944} \right ) ^{\frac{1.0}{0.45} } & \infty \ge V \ge 4.5\cdot0.018053968510807\\
\dfrac{V}{4.5} & 4.5 \cdot 0.018053968510807 > V > 4.5 \cdot -0.018053968510807\\
\left ( \dfrac{ V+(1.09929682680944 - 1.0) }{1.09929682680944} \right ) ^{\frac{1.0}{0.45} } & 4.5 \cdot -0.018053968510807 \ge V \ge -\infty
\end{cases}
```


---
Display-referredな伝達関数

**zimg xvYCC inverse EOTF (Rec. 1886: 1.0>=L>=0.0)**

```math
\mbox{V=}\begin{cases}
1.09929682680944 L^{0.45} - (1.09929682680944 - 1.0) & \infty \ge L > 1.0\\
L^{\frac{1.0}{2.4}} & 1.0 \ge L \ge 0.0\\
4.5L & 0.0 > L > -0.018053968510807\\
1.09929682680944 L^{0.45} - (1.09929682680944 - 1.0) & -0.018053968510807 \ge L \ge -\infty
\end{cases}
```


**zimg xvYCC EOTF (Rec. 1886: 1.0>=V>=0.0)**


```math
\mbox{L=}\begin{cases}
\left ( \dfrac{ V+(1.09929682680944 - 1.0) }{1.09929682680944} \right ) ^{\frac{1.0}{0.45} } & \infty \ge V >1.0\\
{V}^{2.4} & 1.0 \ge V \ge 0.0\\
\dfrac{V}{4.5} & 0.0 > V > 4.5 \cdot -0.018053968510807\\
\left ( \dfrac{ V+(1.09929682680944 - 1.0) }{1.09929682680944} \right ) ^{\frac{1.0}{0.45} } & 4.5 \cdot -0.018053968510807 \ge V \ge -\infty
\end{cases}
```


```cpp
// src/zimg/colorspace/gamma.cppより

float xvycc_oetf(float x) noexcept
{
	return std::copysign(rec_709_oetf(std::fabs(x)), x);
}

float xvycc_inverse_oetf(float x) noexcept
{
	return std::copysign(rec_709_inverse_oetf(std::fabs(x)), x);
}

…

// Handle values in the range [0.0-1.0] such that they match a legacy CRT.
float xvycc_eotf(float x) noexcept
{
	if (x < 0.0f || x > 1.0f)
		return std::copysign(rec_709_inverse_oetf(std::fabs(x)), x);
	else
		return std::copysign(rec_1886_eotf(std::fabs(x)), x);
}

float xvycc_inverse_eotf(float x) noexcept
{
	if (x < 0.0f || x > 1.0f)
		return std::copysign(rec_709_oetf(std::fabs(x)), x);
	else
		return std::copysign(rec_1886_inverse_eotf(std::fabs(x)), x);
}
```

黒レベルを0.0cd/m^2とすると
**Rec. 1886 inverse EOTF**
```math
\mbox{V=}{L}^{\frac{1.0}{2.4}}\qquad{L}\ge 0.0
```
**Rec. 1886 EOTF**
```math
\mbox{L=}{V}^{2.4}\qquad{V}\ge 0.0
```
```cpp
// src/zimg/colorspace/gamma.cppより

// Ignore the BT.1886 provisions for limited contrast and assume an ideal CRT.
float rec_1886_inverse_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 1.0f / 2.4f);
}

float rec_1886_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 2.4f);
}
```

**OOTF(L)=(xvYCC EOTF ∘ xvYCC OETF)(L)**
ディスプレイの輝度[^12]がシーンの輝度[^11]より全体的に暗くなる(輝度1.0以上を除く)。
ガンマ補正$\mbox{V=}L^{1.22}$で近似できるが、比較すると暗い部分がより圧縮されていることがわかる(輝度0.0未満、また1.0を超える輝度を除く)。


```math
\mbox{V=}\begin{cases}
L & \infty \ge L >1.0\\
(1.09929682680944 L^{0.45}−(1.09929682680944−1.0) )^{2.4} & 1.0 \ge L \ge 0.018053968510807\\
(4.5L)^{2.4} & 0.018053968510807 > L \ge 0.0\\
L & 0.0 > L \ge -\infty
\end{cases}
```


## Rec. ITU-R BT.1361-0 extended colour gamut system
Rec. ITU-R BT.709-3(02/98)をスーパーホワイト、サブブラックまで拡張した伝達特性。
定義域と値域は



$1.33 > L \ge -0.25$
$1.099 \cdot 1.33^{0.45} - (1.099 - 1.0) > V \ge \frac{-(1.099*(-4 \cdot -0.25)^{0.45}-(1.099-1))}{4}$



である。
Rec. 1361 OETFで規定されている$L<1.33$は、$V<1.15\ldots$であり、リミテッドレンジ$[16,235]$の最大値254($\mbox{L=}1.09\ldots$)を超える。
※0と255は同期信号に使用されるため、リミテッドレンジでの映像信号の最大値は254となる。

**線形な値へ変換する場合 (libplaceboの実装に基づく)**
もしDisplay-referred[^14]ならば、Rec. 1886 EOTFを適用する。

OOTF[^10]は(Rec. 1886 EOTF ∘ Rec. 1361 OETF)(L)となる。

[**libplaceboの該当ソースコード**](https://github.com/haasn/libplacebo/blob/adb0ef21848e36c74281075397e979a8fe2a9744/src/include/libplacebo/utils/libav_internal.h#L167-L218)

Scene-referredな伝達関数

**Rec. 1361 OETF**

```math
\mbox{V=}\begin{cases}
1.099 L^{0.45} - (1.099 - 1.0) & 1.33 > L \ge 0.018\\
4.5L & 0.018 > L \ge -0.0045\\
\dfrac{-(1.099\cdot(-4L)^{0.45}-(1.099-1))}{4} & -0.0045 > L \ge -0.25
\end{cases}
```


**Rec. 1361 inverse OETF**


```math
\mbox{L=}\begin{cases}
\left ( \dfrac{ V+(1.099 - 1.0) }{1.099} \right ) ^{\frac{1.0}{0.45} } & 1.099 \cdot 1.33^{0.45} - (1.099 - 1.0) > V \ge 4.5\cdot0.018\\
\dfrac{V}{4.5} & 4.5\cdot0.018 > V \ge 4.500\cdot-0.0045\\
\dfrac{-\left({\frac{(-4V + 0.099)} {1.099}}^{ \frac{1} {0.45} }\right)}{4}
 & \dfrac{-(1.099\cdot(-4L)^{0.45}-(1.099-1))}{4}\cdot -0.0045 > V \ge \dfrac{-(1.099\cdot(-4 \cdot -0.25)^{0.45}-(1.099-1))}{4}
\end{cases}
```


Display-referredな伝達関数


    
**libplacebo Rec. 1886 inverse EOTF**
黒レベルが0.0cd/m^2の場合
```math
\mbox{V=}{L}^{\frac{1.0}{2.4}}\qquad{L}\ge 0.0
```
**libplacebo Rec. 1886 EOTF**
黒レベルが0.0cd/m^2の場合
```math
\mbox{L=}{L}^{2.4}\qquad {V}\ge 0.0
 ```   


[**libplaceboの該当ソースコード**](https://github.com/haasn/libplacebo/blob/9e9b93502d984900cecde793d884c6c79c4cafc1/src/shaders/colorspace.c#L639-L647)

**OOTF(L)=(Rec. 1886 EOTF ∘ Rec. 1361 OETF)(L)**
**Rec. 1361 OETF**
```math
\mbox{V=}\begin{cases}
1.099 L^{0.45} - (1.099 - 1.0) & 1.33 > L \ge 0.018\\
4.5L & 0.018 > L \ge -0.0045\\
\dfrac{-(1.099\cdot(-4L)^{0.45}-(1.099-1))}{4} & -0.0045 > L \ge -0.25
\end{cases}
```


## IEC 61966-2-1 sRGB
**非線形な値へ変換する場合**
sRGB inverse EOTFを適用する。

**線形な値へ変換する場合**
sRGB EOTFを適用する。

OOTF[^10]は線形となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
…
case TransferCharacteristics::SRGB:
	func.to_linear = srgb_eotf;
	func.to_gamma = srgb_inverse_eotf;
	break;
```

**sRGB inverse EOTF**


```math
\mbox{V=}\begin{cases}
1.055010718947587 L^{\frac{1.0}{2.4}} - (1.055010718947587 - 1.0) & L \ge 0.003041282560128\\
12.92L & 0.003041282560128 > L \ge 0.0
\end{cases}
```


**sRGB EOTF**


```math
\mbox{L=}\begin{cases}
\left ( \dfrac{ V+(1.055010718947587 - 1.0) }{1.055010718947587} \right ) ^{2.4} & V \ge 12.92\cdot0.003041282560128\\
\dfrac{V}{12.92} & 12.92\cdot0.003041282560128 > V \ge 0.0
\end{cases}
```


```cpp
// src/zimg/colorspace/gamma.cppより

// Adjusted for continuity of first derivative.
constexpr float SRGB_ALPHA = 1.055010718947587f;
constexpr float SRGB_BETA = 0.003041282560128f;

float srgb_eotf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < 12.92f * SRGB_BETA)
		x = x / 12.92f;
	else
		x = zimg_x_powf((x + (SRGB_ALPHA - 1.0f)) / SRGB_ALPHA, 2.4f);

	return x;
}

float srgb_inverse_eotf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < SRGB_BETA)
		x = x * 12.92f;
	else
		x = SRGB_ALPHA * zimg_x_powf(x, 1.0f / 2.4f) - (SRGB_ALPHA - 1.0f);

	return x;
}
```

## Rec. ITU-R BT.2020-2 (10-bit system)
機能的に[**Rec. ITU-R BT.709-6**](#rec-itu-r-bt709-66)と同じ。

```cpp
// src/zimg/api/zimg.cppより

zimg::colorspace::TransferCharacteristics translate_transfer(zimg_transfer_characteristics_e transfer)
{
	using zimg::colorspace::TransferCharacteristics;

	static SM_CONSTEXPR_14 const zimg::static_map<zimg_transfer_characteristics_e, TransferCharacteristics, 15> map{
		{ ZIMG_TRANSFER_BT709,         TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_UNSPECIFIED,   TransferCharacteristics::UNSPECIFIED },
		{ ZIMG_TRANSFER_ST240_M,       TransferCharacteristics::SMPTE_240M },
		{ ZIMG_TRANSFER_BT601,         TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_BT470_M,       TransferCharacteristics::REC_470_M },
		{ ZIMG_TRANSFER_BT470_BG,      TransferCharacteristics::REC_470_BG },
		{ ZIMG_TRANSFER_IEC_61966_2_4, TransferCharacteristics::XVYCC },
		{ ZIMG_TRANSFER_IEC_61966_2_1, TransferCharacteristics::SRGB },
		{ ZIMG_TRANSFER_BT2020_10,     TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_BT2020_12,     TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_LINEAR,        TransferCharacteristics::LINEAR },
		{ ZIMG_TRANSFER_LOG_100,       TransferCharacteristics::LOG_100 },
		{ ZIMG_TRANSFER_LOG_316,       TransferCharacteristics::LOG_316 },
		{ ZIMG_TRANSFER_ST2084,        TransferCharacteristics::ST_2084 },
		{ ZIMG_TRANSFER_ARIB_B67,      TransferCharacteristics::ARIB_B67 },
	};
	return search_itu_enum_map(map, transfer, "unrecognized transfer characteristics");
}
```

## Rec. ITU-R BT.2020-2 (12-bit system)
機能的に[**Rec. ITU-R BT.709-6**](#rec-itu-r-bt709-66)と同じ。

```cpp
// src/zimg/api/zimg.cppより

zimg::colorspace::TransferCharacteristics translate_transfer(zimg_transfer_characteristics_e transfer)
{
	using zimg::colorspace::TransferCharacteristics;

	static SM_CONSTEXPR_14 const zimg::static_map<zimg_transfer_characteristics_e, TransferCharacteristics, 15> map{
		{ ZIMG_TRANSFER_BT709,         TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_UNSPECIFIED,   TransferCharacteristics::UNSPECIFIED },
		{ ZIMG_TRANSFER_ST240_M,       TransferCharacteristics::SMPTE_240M },
		{ ZIMG_TRANSFER_BT601,         TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_BT470_M,       TransferCharacteristics::REC_470_M },
		{ ZIMG_TRANSFER_BT470_BG,      TransferCharacteristics::REC_470_BG },
		{ ZIMG_TRANSFER_IEC_61966_2_4, TransferCharacteristics::XVYCC },
		{ ZIMG_TRANSFER_IEC_61966_2_1, TransferCharacteristics::SRGB },
		{ ZIMG_TRANSFER_BT2020_10,     TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_BT2020_12,     TransferCharacteristics::REC_709 },
		{ ZIMG_TRANSFER_LINEAR,        TransferCharacteristics::LINEAR },
		{ ZIMG_TRANSFER_LOG_100,       TransferCharacteristics::LOG_100 },
		{ ZIMG_TRANSFER_LOG_316,       TransferCharacteristics::LOG_316 },
		{ ZIMG_TRANSFER_ST2084,        TransferCharacteristics::ST_2084 },
		{ ZIMG_TRANSFER_ARIB_B67,      TransferCharacteristics::ARIB_B67 },
	};
	return search_itu_enum_map(map, transfer, "unrecognized transfer characteristics");
}
```

## SMPTE ST 2084
zimgでは従来のSDRとの相互運用性を考慮し、シーンライト[^11]が100cd/m^2($\mbox{L=}1.0$)に設定されている。
ディスプレイライト[^12]は10000cd/m^2（$\mbox{L=}1.0$）に設定されている。
`zscale`ソースコード中のpeak_luminanceはSDRの公称ピーク輝度を指し、nplオプションで指定できる。

**非線形な値へ変換する場合**
もしScene-referred[^2]ならば、ST 2084 OETFを適用する、もしDisplay-referred[^14]ならばST 2084 inverse EOTFを適用する。


**線形な値へ変換する場合**
もしScene-referredならば、ST 2084 inverse OETFを適用する、もしDisplay-referredならばST 2084 EOTFを適用する。

OOTF[^10]は(ST 2084 EOTF ∘ ST 2084 OETF)(L)となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
…
case TransferCharacteristics::ST_2084:
	func.to_linear = scene_referred ? st_2084_inverse_oetf : st_2084_eotf;
	func.to_gamma = scene_referred ? st_2084_oetf : st_2084_inverse_eotf;
	func.to_linear_scale = static_cast<float>(ST2084_PEAK_LUMINANCE / peak_luminance);
	func.to_gamma_scale = static_cast<float>(peak_luminance / ST2084_PEAK_LUMINANCE);
	break;
```
Scene-referredな伝達関数

**ST 2084 OETF**
```math
\DeclareMathOperator{\EOTF}{EOTF}\\
\DeclareMathOperator{\OETF}{OETF}\\
\mbox{V=}\EOTF^{-1}_{ST 2084}\left(\dfrac{\EOTF_{Rec.1886}\left(\OETF_{Rec.709}\left(nL\right)\right)} {100.0}\right) \qquad 1.0 \ge L \ge 0.0
```

**ST 2084 inverse OETF**

```math
\DeclareMathOperator{\EOTF}{EOTF}\\
\DeclareMathOperator{\OETF}{OETF}\\
\mbox{L=}\dfrac{\OETF^{-1}_{Rec.709}\left(\EOTF^{-1}_{Rec.1886}\left(\EOTF_{ST 2084}(V) \cdot 100.0\right)\right)} {n} \qquad 1.0 \ge V \ge 0.0
```


ここで
- $n=59.49080238715383$[^7]

----
Display-referredな伝達関数

**ST 2084 inverse EOTF**


```math
\mbox{V=}\left( \dfrac{c_1 + c_2 \cdot L^{m_1}}{1 + c_3 \cdot L^{m_1} } \right)^{m_2} \qquad 1.0 \ge L \ge 0.0
```


**ST 2084 EOTF**


```math
\mbox{L=}\left( \dfrac{max[ (V^{1/m_2} - c_1), 0]} {c_2 - c_3 \cdot V ^ {1/m_2} } \right) ^ {1/ m_1} \qquad 1.0 \ge V \ge 0.0
```


ここで



- $m_1 = \dfrac{2610}{16384} = \dfrac{1305}{8192} = 0.1593017578125$
- $m_2 = \dfrac{2523}{4096}\cdot128 = \dfrac{2523}{32} = 78.84375$
- $c_1 = \dfrac{3424}{4096}  = \dfrac{107}{128} = 0.8359375 = c_3 - c_2 + 1$
- $c_2 = \dfrac{2413}{4096}\cdot32 = \dfrac{2413}{128} = 18.8515625$
- $c_3 = \dfrac{2392}{4096}\cdot32 = \dfrac{2392}{128} = 18.6875$


<br>

```cpp
// src/zimg/colorspace/gamma.hより

constexpr float ST2084_PEAK_LUMINANCE = 10000.0f; // Units of cd/m^2.
```

```cpp
// src/zimg/colorspace/gamma.cppより

constexpr float ST2084_M1 = 0.1593017578125f;
constexpr float ST2084_M2 = 78.84375f;
constexpr float ST2084_C1 = 0.8359375f;
constexpr float ST2084_C2 = 18.8515625f;
constexpr float ST2084_C3 = 18.6875f;
…
// Chosen for compatibility with higher precision REC709_ALPHA/REC709_BETA.
// See: ITU-R BT.2390-2 5.3.1
constexpr float ST2084_OOTF_SCALE = 59.49080238715383f;
…
float ootf_st2084(float x) noexcept
{
	return rec_1886_eotf(rec_709_oetf(x * ST2084_OOTF_SCALE)) / 100.0f;
}

float inverse_ootf_st2084(float x) noexcept
{
	return rec_709_inverse_oetf(rec_1886_inverse_eotf(x * 100.0f)) / ST2084_OOTF_SCALE;
}
…
float rec_709_oetf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < REC709_BETA)
		x = x * 4.5f;
	else
		x = REC709_ALPHA * zimg_x_powf(x, 0.45f) - (REC709_ALPHA - 1.0f);

	return x;
}

float rec_709_inverse_oetf(float x) noexcept
{
	x = std::max(x, 0.0f);

	if (x < 4.5f * REC709_BETA)
		x = x / 4.5f;
	else
		x = zimg_x_powf((x + (REC709_ALPHA - 1.0f)) / REC709_ALPHA, 1.0f / 0.45f);

	return x;
}
…
} // namespace
// Ignore the BT.1886 provisions for limited contrast and assume an ideal CRT.
float rec_1886_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 2.4f);
}

float rec_1886_inverse_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 1.0f / 2.4f);
}
…
float st_2084_eotf(float x) noexcept
{
	// Filter negative values to avoid NAN.
	if (x > 0.0f) {
		float xpow = zimg_x_powf(x, 1.0f / ST2084_M2);
		float num = std::max(xpow - ST2084_C1, 0.0f);
		float den = std::max(ST2084_C2 - ST2084_C3 * xpow, FLT_MIN);
		x = zimg_x_powf(num / den, 1.0f / ST2084_M1);
	} else {
		x = 0.0f;
	}

	return x;
}

float st_2084_inverse_eotf(float x) noexcept
{
	// Filter negative values to avoid NAN, and also special-case 0 so that (f(g(0)) == 0).
	if (x > 0.0f) {
		float xpow = zimg_x_powf(x, ST2084_M1);
#if 0
		// Original formulation from SMPTE ST 2084:2014 publication.
		float num = ST2084_C1 + ST2084_C2 * xpow;
		float den = 1.0f + ST2084_C3 * xpow;
		x = zimg_x_powf(num / den, ST2084_M2);
#else
		// More stable arrangement that avoids some cancellation error.
		float num = (ST2084_C1 - 1.0f) + (ST2084_C2 - ST2084_C3) * xpow;
		float den = 1.0f + ST2084_C3 * xpow;
		x = zimg_x_powf(1.0f + num / den, ST2084_M2);
#endif
	} else {
		x = 0.0f;
	}

	return x;
}
…
float st_2084_oetf(float x) noexcept
{
	return st_2084_inverse_eotf(ootf_st2084(x));
}

float st_2084_inverse_oetf(float x) noexcept
{
	return inverse_ootf_st2084(st_2084_eotf(x));
}
```

**OOTF(L)=(ST 2084 EOTF ∘ ST 2084 OETF)(L)**
$\DeclareMathOperator{\EOTF}{EOTF}\\
\DeclareMathOperator{\OETF}{OETF}\\
\mbox{V=}\EOTF_{ST 2084} (\OETF_{ST 2084}(L))$

## SMPTE ST 428-1 (2019)
ディスプレイライト[^12]は48cd/m^2($\mbox{L=}1.0$)に設定されている。
スケール係数$\frac{48} {52.37}$によって、EOTFは、単独でリミテッドレンジをフルレンジに圧縮する働きをする。
例えば、pow(48\*1/219\*239/52.37,1.0/2.6)≒1.00009959139409といったように。

OOTF[^10]は線形となる。

Rec. ITU-T H.273による
**H.273 ST 428-1 EOTF**


```math
\mbox{V=}(\dfrac{48L} {52.37})^{\frac{1.0}{2.6}}\qquad \text{for all values of }L
```


**H.273 ST 428-1 inverse EOTF**


```math
\mbox{L=}\dfrac{52.37V^{2.6}}{48}\qquad \text{for all values of }V
```

## ARIB STD-B67
ディスプレイライト[^12]は1000cd/m^2（$\mbox{L=}1.0$）に設定されている。

**非線形な値へ変換する場合**
もしScene-referred[^2]ならば、ARIB B67 OETFを適用する、もしDisplay-referred[^14]ならばARIB inverse B67 EOTFを適用する。

**線形な値へ変換する場合**
もしScene-referred[^2]ならば、ARIB B67 inverse OETFを適用する、もしDisplay-referredならばARIB B67 EOTFを適用する。

OOTF[^10]は(ARIB B67 EOTF ∘ ARIB B67 OETF)(L)となる。

```cpp
// src/zimg/colorspace/gamma.cppより

switch (transfer) {
…
case TransferCharacteristics::ARIB_B67:
	func.to_linear = scene_referred ? arib_b67_inverse_oetf : arib_b67_eotf;
	func.to_gamma = scene_referred ? arib_b67_oetf : arib_b67_inverse_eotf;
	func.to_linear_scale = scene_referred ? 12.0f : static_cast<float>(1000.0 / peak_luminance);
	func.to_gamma_scale = scene_referred ? 1.0f / 12.0f : static_cast<float>(peak_luminance / 1000.0);
	break;
```
Scene-referredな伝達関数

**ARIB B67 OETF**


```math
\mbox{V=}\begin{cases}
a\cdot\ln(12L - b) + c & 1.0 \ge L > \frac{1}{12}\\
\sqrt{3L} & \frac{1}{12} \ge L \ge 0.0
\end{cases}
```


**ARIB B67 inverse OETF**


```math
\mbox{L=}\begin{cases}
\dfrac{\exp\left(\dfrac{(V - c)} {a}\right) + b} {12.0} & 1.0 \ge V > \sqrt{3\cdot\frac{1} {12}} = 0.5\\
V^2\cdot\dfrac{1} {3}\ & 0.5 = \sqrt{3\cdot\frac{1} {12}} = \ge V \ge 0.0
\end{cases}
```


ここで



- $a=0.17883277$
- $b=0.28466892$
- $c=0.55991073$





----
Display-referredな伝達関数

**ARIB B67 inverse EOTF**


```math
\mbox{V=}\begin{cases}
a\cdot\ln(12L^{\frac{1.0} {1.2}} - b) + c & 1.0 \ge L > \left(\dfrac{1}{12}\right)^{1.2}\\
\sqrt{3L^{\frac{1.0} {1.2}}} & \left(\dfrac{1}{12}\right)^{1.2} \ge L \ge 0.0
\end{cases}
```


**ARIB B67 EOTF**


```math
\mbox{L=}\begin{cases}
\left(\dfrac{\exp\left(\dfrac{(V - c)} {a}\right) + b} {12.0}\right)^{1.2} & 1.0 \ge V > \sqrt{3\cdot\frac{1} {12}} = 0.5\\
\left(V^2\cdot\dfrac{1} {3}\right)^{1.2} & 0.5 = \sqrt{3\cdot\frac{1} {12}} = \ge V \ge 0.0
\end{cases}
```


ここで



- $a=0.17883277$
- $b=0.28466892$
- $c=0.55991073$






```cpp
// src/zimg/colorspace/gamma.cppより

constexpr float ARIB_B67_A = 0.17883277f;
constexpr float ARIB_B67_B = 0.28466892f;
constexpr float ARIB_B67_C = 0.55991073f;
…
float ootf_1_2(float x) noexcept
{
	return x < 0.0f ? x : zimg_x_powf(x, 1.2f);
}

float inverse_ootf_1_2(float x) noexcept
{
	return x < 0.0f ? x : zimg_x_powf(x, 1.0f / 1.2f);
}
…
float arib_b67_oetf(float x) noexcept
{
	// Prevent negative pixels from yielding NAN.
	x = std::max(x, 0.0f);

	if (x <= (1.0f / 12.0f))
		x = zimg_x_sqrtf(3.0f * x);
	else
		x = ARIB_B67_A * zimg_x_logf(12.0f * x - ARIB_B67_B) + ARIB_B67_C;

	return x;
}

float arib_b67_inverse_oetf(float x) noexcept
{
	// Prevent negative pixels expanding into positive values.
	x = std::max(x, 0.0f);

	if (x <= 0.5f)
		x = (x * x) * (1.0f / 3.0f);
	else
		x = (zimg_x_expf((x - ARIB_B67_C) / ARIB_B67_A) + ARIB_B67_B) / 12.0f;

	return x;
}
…
// Applies a per-channel correction instead of the iterative method specified in Rec.2100.
float arib_b67_eotf(float x) noexcept
{
	return ootf_1_2(arib_b67_inverse_oetf(x));
}

float arib_b67_inverse_eotf(float x) noexcept
{
	return arib_b67_oetf(inverse_ootf_1_2(x));
}
```
**OOTF(L)=(ARIB B67 EOTF ∘ ARIB B67 OETF)(L)**
$\mbox{V=}L^{1.2}$

# 参照先
> - FFmpeg release 5.1
>   - H.273 Code Value definitions ：<br>[libavutil/pixfmt.h](https://github.com/FFmpeg/FFmpeg/blob/release/5.1/libavutil/pixfmt.h#L496-L519)
>   - zscale filter ：<br>[libavfilter/vf_zscale.c](https://github.com/FFmpeg/FFmpeg/blob/release/5.1/libavfilter/vf_zscale.c)
>   - colorspace filter ：<br>[ibavfilter/vf_colorspace.c](https://github.com/FFmpeg/FFmpeg/blob/release/5.1/libavfilter/vf_colorspace.c)
>   - libplacebo filter ：<br>[libavfilter/vf_libplacebo.c](https://github.com/FFmpeg/FFmpeg/blob/release/5.1/libavfilter/vf_libplacebo.c)
> - zimg v3.0
>   - API functions ：<br>[api/zimg.h](https://github.com/sekrit-twc/zimg/blob/v3.0/src/zimg/api/zimg.h)<br>[api/zimg.cpp](https://github.com/sekrit-twc/zimg/blob/v3.0/src/zimg/api/zimg.cpp)
>   - Graph ：<br>[colorspace/graph.cpp](https://github.com/sekrit-twc/zimg/blob/v3.0/src/zimg/colorspace/graph.cpp)
>   - Operation ：<br>[colorspace/operation.h](https://github.com/sekrit-twc/zimg/blob/v3.0/src/zimg/colorspace/operation.h)<br>[colorspace/operation.cpp](https://github.com/sekrit-twc/zimg/blob/v3.0/src/zimg/colorspace/operation.cpp)
>   - Transfer functions ：<br>[colorspace/gamma.h](https://github.com/sekrit-twc/zimg/blob/v3.0/src/zimg/colorspace/gamma.h)<br>[colorspace/gamma.cpp](https://github.com/sekrit-twc/zimg/blob/v3.0/src/zimg/colorspace/gamma.cpp)
> - libplacebo v5.229.1
>   - FFmpeg ABI ：<br>[/utils/libav_internal.h](https://github.com/haasn/libplacebo/blob/v5.229.1/src/include/libplacebo/utils/libav_internal.h)
>   - Shader ：<br>[src/shaders/colorspace.c](https://github.com/haasn/libplacebo/blob/v5.229.1/src/shaders/colorspace.c)

# 参考になったサイト
> - toruのブログ
>   - https://trev16.hatenablog.com/
> - genuine HDR colour
>   - https://note.com/animehdr

[^0]:zimgはWTFPLである。

[^1]:Rec. ITU-R BT.709-6 (06/2015)は、Recommendation ITU Radiocommunication BT-series 709 version 6の略称である。Rec.709やBT.709も、同じものを指している。

[^2]:ディスプレイ参照（Scene-referred）とは、光の基準（参照先）がシーンの輝度[^11]であることを示す。基準には2種類ある。もう一つはディスプレイ基準（Display-referred）と呼ばれるもので、こちらはディスプレイで復元された光の輝度が基準である。ITU-R BT.709-6のようにシーンの輝度とディスプレイの輝度が必ずしも線形関係にあるとは限らないため、2つの異なる基準が存在する。

[^3]:Rec. ITU-R BT.470-6 (11/98)は、Recommendation ITU Radiocommunication BT-series 470 version 6の略称である。

[^5]:Rec. ITU-T H.273は、Recommendation ITU Telecommunication H-series 273の略称である。

[^6]:SMPTE ST 240 (1999)は、ST 240:1999 - SMPTE Standard - For Television — 1125-Line High-Definition Production Systems — Signal Parametersの略称である。

[^7]:ITU-R BT.2390-2 (03/2017)およびReport BT.2390-10 (11/2021)では、REC709ALPHAが1.099、REC709BETAが0.018でn=59.5208という値を採用している。しかし、zimgではREC709ALPHA≒1.09929682680944、REC709BETA≒0.018053968510807からn=59.49080238715383としており、これはRec. ITU-T H.273で述べられている定義に沿ったものである。

[^8]:Display Light[^12]への変換のみ対応。EOTFはRec. 1886 EOTF。

[^9]:FFmpegでは、伝達関数をRec. ITU-T H.273 CICP (Coding-independent code points, コーディングに依存しないコードポイント)[^5]で指定することができる。CICPの使用は、MKVコンテナのEBML、MP4コンテナのBOX、HEVCのVUI（Video Usability Information）でも取り入れられている。

[^10]:OOTF(opto-optical transfer function)とは、シーンの輝度$L$[^11]を入力とし、ディスプレイの輝度を出力とする関数$f(L)$のこと。言い換えるとOETF(opto-electronic transfer function)とEOTF(electro-optical transfer function)の結果としてシーンの輝度にかかる伝達特性を表す伝達関数である。つまり$\DeclareMathOperator{\EOTF}{EOTF}\DeclareMathOperator{\OETF}{OETF}\EOTF(\OETF(L))$ また $\DeclareMathOperator{\EOTF}{EOTF}\DeclareMathOperator{\OETF}{OETF}\EOTF \circ \OETF(L)$を指す。OOTFにおける補正の目的の一つは、視聴環境に応じて輝度特性（広義のレンダリングインテント[^13]）を最適化することである。

[^11]:シーンライト(Scene Light)とは、カメラが捉えた現場(シーン)の光であり、そのシーンの輝度、ひいてはシーンの輝度に線形な電気信号値である。Display Light[^12]と対になる概念である。シーンの輝度とシーンの輝度を示す電気信号値には線形関係があることを強調して、リニアシーンライト(Linear Scene Light)とも呼ばれる。[^13]

[^12]:ディスプレイライト(Display Light)とは、ディスプレイが発する光であり、そのディスプレイの輝度(ディスプレイのピーク輝度との混同に注意)、ひいてはディスプレイの輝度に線形な電気信号値である。Scene Light[^11]と対になる概念である。

[^13]:Report BT.2390-10 (11/2021)による。

[^14]:シーン参照（Display-referred）とは、光の基準（参照先）がディスプレイの輝度[^12]であることを示す。基準には2種類ある。もう一つはシーン基準（Scene-referred）と呼ばれるもので、こちらはシーンの光の輝度が基準である。

[^15]:相対輝度（Relative luminance）のこと。最大値を1、最小値を0とし、最大値が白色点（White point）の絶対輝度に等しくなるように正規化したデータ。なお、シーンライト[^11]とディスプレイライト[^12]では白色点の絶対輝度が異なることが多いので、混同しないように注意が必要である。例えば、「SDRの白色点の絶対輝度は100cd/m^2」という説明は、ディスプレイライトの白色点を指している。ちなみに、勧告などで指定された白色点の絶対輝度を基準白（Reference white）という。