"""
DDFAPD - Menon (2007) Bayer CFA Demosaicing
===========================================
*Bayer* CFA (Colour Filter Array) DDFAPD - *Menon (2007)* demosaicing.
References
----------
- :cite:`Menon2007c` : Menon, D., Andriani, S., & Calvagno, G. (2007).
Demosaicing With Directional Filtering and a posteriori Decision. IEEE
Transactions on Image Processing, 16(1), 132-141.
doi:10.1109/TIP.2006.884928
"""
from __future__ import annotations
import numpy as np
from colour.hints import ArrayLike, Literal, NDArrayFloat
from colour.utilities import as_float_array, ones, tsplit, tstack
from scipy.ndimage.filters import convolve, convolve1d
from colour_demosaicing.bayer import masks_CFA_Bayer
__author__ = "Colour Developers"
__copyright__ = "Copyright 2015 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"
__all__ = [
"demosaicing_CFA_Bayer_Menon2007",
"demosaicing_CFA_Bayer_DDFAPD",
"refining_step_Menon2007",
]
def _cnv_h(x: ArrayLike, y: ArrayLike) -> NDArrayFloat:
"""Perform horizontal convolution."""
return convolve1d(x, y, mode="mirror")
def _cnv_v(x: ArrayLike, y: ArrayLike) -> NDArrayFloat:
"""Perform vertical convolution."""
return convolve1d(x, y, mode="mirror", axis=0)
[docs]
def demosaicing_CFA_Bayer_Menon2007(
CFA: ArrayLike,
pattern: Literal["RGGB", "BGGR", "GRBG", "GBRG"] | str = "RGGB",
refining_step: bool = True,
):
"""
Return the demosaiced *RGB* colourspace array from given *Bayer* CFA using
DDFAPD - *Menon (2007)* demosaicing algorithm.
Parameters
----------
CFA
*Bayer* CFA.
pattern
Arrangement of the colour filters on the pixel array.
refining_step
Perform refining step.
Returns
-------
:class:`numpy.ndarray`
*RGB* colourspace array.
Notes
-----
- The definition output is not clipped in range [0, 1] : this allows for
direct HDRI image generation on *Bayer* CFA data and post
demosaicing of the high dynamic range data as showcased in this
`Jupyter Notebook <https://github.com/colour-science/colour-hdri/\
blob/develop/colour_hdri/examples/\
examples_merge_from_raw_files_with_post_demosaicing.ipynb>`__.
References
----------
:cite:`Menon2007c`
Examples
--------
>>> CFA = np.array(
... [
... [0.30980393, 0.36078432, 0.30588236, 0.3764706],
... [0.35686275, 0.39607844, 0.36078432, 0.40000001],
... ]
... )
>>> demosaicing_CFA_Bayer_Menon2007(CFA)
array([[[ 0.30980393, 0.35686275, 0.39215687],
[ 0.30980393, 0.36078432, 0.39607844],
[ 0.30588236, 0.36078432, 0.39019608],
[ 0.32156864, 0.3764706 , 0.40000001]],
<BLANKLINE>
[[ 0.30980393, 0.35686275, 0.39215687],
[ 0.30980393, 0.36078432, 0.39607844],
[ 0.30588236, 0.36078432, 0.39019609],
[ 0.32156864, 0.3764706 , 0.40000001]]])
>>> CFA = np.array(
... [
... [0.3764706, 0.36078432, 0.40784314, 0.3764706],
... [0.35686275, 0.30980393, 0.36078432, 0.29803923],
... ]
... )
>>> demosaicing_CFA_Bayer_Menon2007(CFA, "BGGR")
array([[[ 0.30588236, 0.35686275, 0.3764706 ],
[ 0.30980393, 0.36078432, 0.39411766],
[ 0.29607844, 0.36078432, 0.40784314],
[ 0.29803923, 0.3764706 , 0.42352942]],
<BLANKLINE>
[[ 0.30588236, 0.35686275, 0.3764706 ],
[ 0.30980393, 0.36078432, 0.39411766],
[ 0.29607844, 0.36078432, 0.40784314],
[ 0.29803923, 0.3764706 , 0.42352942]]])
"""
CFA = np.squeeze(as_float_array(CFA))
R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern)
h_0 = as_float_array([0.0, 0.5, 0.0, 0.5, 0.0])
h_1 = as_float_array([-0.25, 0.0, 0.5, 0.0, -0.25])
R = CFA * R_m
G = CFA * G_m
B = CFA * B_m
G_H = np.where(G_m == 0, _cnv_h(CFA, h_0) + _cnv_h(CFA, h_1), G)
G_V = np.where(G_m == 0, _cnv_v(CFA, h_0) + _cnv_v(CFA, h_1), G)
C_H = np.where(R_m == 1, R - G_H, 0)
C_H = np.where(B_m == 1, B - G_H, C_H)
C_V = np.where(R_m == 1, R - G_V, 0)
C_V = np.where(B_m == 1, B - G_V, C_V)
D_H = np.abs(C_H - np.pad(C_H, ((0, 0), (0, 2)), mode="reflect")[:, 2:])
D_V = np.abs(C_V - np.pad(C_V, ((0, 2), (0, 0)), mode="reflect")[2:, :])
del h_0, h_1, CFA, C_V, C_H
k = as_float_array(
[
[0.0, 0.0, 1.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 3.0, 0.0, 3.0],
[0.0, 0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 1.0, 0.0, 1.0],
]
)
d_H = convolve(D_H, k, mode="constant")
d_V = convolve(D_V, np.transpose(k), mode="constant")
del D_H, D_V
mask = d_V >= d_H
G = np.where(mask, G_H, G_V)
M = np.where(mask, 1, 0)
del d_H, d_V, G_H, G_V
# Red rows.
R_r = np.transpose(np.any(R_m == 1, axis=1)[None]) * ones(R.shape)
# Blue rows.
B_r = np.transpose(np.any(B_m == 1, axis=1)[None]) * ones(B.shape)
k_b = as_float_array([0.5, 0, 0.5])
R = np.where(
np.logical_and(G_m == 1, R_r == 1),
G + _cnv_h(R, k_b) - _cnv_h(G, k_b),
R,
)
R = np.where(
np.logical_and(G_m == 1, B_r == 1) == 1,
G + _cnv_v(R, k_b) - _cnv_v(G, k_b),
R,
)
B = np.where(
np.logical_and(G_m == 1, B_r == 1),
G + _cnv_h(B, k_b) - _cnv_h(G, k_b),
B,
)
B = np.where(
np.logical_and(G_m == 1, R_r == 1) == 1,
G + _cnv_v(B, k_b) - _cnv_v(G, k_b),
B,
)
R = np.where(
np.logical_and(B_r == 1, B_m == 1),
np.where(
M == 1,
B + _cnv_h(R, k_b) - _cnv_h(B, k_b),
B + _cnv_v(R, k_b) - _cnv_v(B, k_b),
),
R,
)
B = np.where(
np.logical_and(R_r == 1, R_m == 1),
np.where(
M == 1,
R + _cnv_h(B, k_b) - _cnv_h(R, k_b),
R + _cnv_v(B, k_b) - _cnv_v(R, k_b),
),
B,
)
RGB = tstack([R, G, B])
del R, G, B, k_b, R_r, B_r
if refining_step:
RGB = refining_step_Menon2007(RGB, tstack([R_m, G_m, B_m]), M)
del M, R_m, G_m, B_m
return RGB
demosaicing_CFA_Bayer_DDFAPD = demosaicing_CFA_Bayer_Menon2007
def refining_step_Menon2007(
RGB: ArrayLike, RGB_m: ArrayLike, M: ArrayLike
) -> NDArrayFloat:
"""
Perform the refining step on given *RGB* colourspace array.
Parameters
----------
RGB
*RGB* colourspace array.
RGB_m
*Bayer* CFA red, green and blue masks.
M
Estimation for the best directional reconstruction.
Returns
-------
:class:`numpy.ndarray`
Refined *RGB* colourspace array.
Examples
--------
>>> RGB = np.array(
... [
... [
... [0.30588236, 0.35686275, 0.3764706],
... [0.30980393, 0.36078432, 0.39411766],
... [0.29607844, 0.36078432, 0.40784314],
... [0.29803923, 0.37647060, 0.42352942],
... ],
... [
... [0.30588236, 0.35686275, 0.3764706],
... [0.30980393, 0.36078432, 0.39411766],
... [0.29607844, 0.36078432, 0.40784314],
... [0.29803923, 0.37647060, 0.42352942],
... ],
... ]
... )
>>> RGB_m = np.array(
... [
... [[0, 0, 1], [0, 1, 0], [0, 0, 1], [0, 1, 0]],
... [[0, 1, 0], [1, 0, 0], [0, 1, 0], [1, 0, 0]],
... ]
... )
>>> M = np.array([[0, 1, 0, 1], [1, 0, 1, 0]])
>>> refining_step_Menon2007(RGB, RGB_m, M)
array([[[ 0.30588236, 0.35686275, 0.3764706 ],
[ 0.30980393, 0.36078432, 0.39411765],
[ 0.29607844, 0.36078432, 0.40784314],
[ 0.29803923, 0.3764706 , 0.42352942]],
<BLANKLINE>
[[ 0.30588236, 0.35686275, 0.3764706 ],
[ 0.30980393, 0.36078432, 0.39411766],
[ 0.29607844, 0.36078432, 0.40784314],
[ 0.29803923, 0.3764706 , 0.42352942]]])
"""
R, G, B = tsplit(RGB)
R_m, G_m, B_m = tsplit(RGB_m)
M = as_float_array(M)
del RGB, RGB_m
# Updating of the green component.
R_G = R - G
B_G = B - G
FIR = ones(3) / 3
B_G_m = np.where(
B_m == 1,
np.where(M == 1, _cnv_h(B_G, FIR), _cnv_v(B_G, FIR)),
0,
)
R_G_m = np.where(
R_m == 1,
np.where(M == 1, _cnv_h(R_G, FIR), _cnv_v(R_G, FIR)),
0,
)
del B_G, R_G
G = np.where(R_m == 1, R - R_G_m, G)
G = np.where(B_m == 1, B - B_G_m, G)
# Updating of the red and blue components in the green locations.
# Red rows.
R_r = np.transpose(np.any(R_m == 1, axis=1)[None]) * ones(R.shape)
# Red columns.
R_c = np.any(R_m == 1, axis=0)[None] * ones(R.shape)
# Blue rows.
B_r = np.transpose(np.any(B_m == 1, axis=1)[None]) * ones(B.shape)
# Blue columns.
B_c = np.any(B_m == 1, axis=0)[None] * ones(B.shape)
R_G = R - G
B_G = B - G
k_b = as_float_array([0.5, 0.0, 0.5])
R_G_m = np.where(
np.logical_and(G_m == 1, B_r == 1),
_cnv_v(R_G, k_b),
R_G_m,
)
R = np.where(np.logical_and(G_m == 1, B_r == 1), G + R_G_m, R)
R_G_m = np.where(
np.logical_and(G_m == 1, B_c == 1),
_cnv_h(R_G, k_b),
R_G_m,
)
R = np.where(np.logical_and(G_m == 1, B_c == 1), G + R_G_m, R)
del B_r, R_G_m, B_c, R_G
B_G_m = np.where(
np.logical_and(G_m == 1, R_r == 1),
_cnv_v(B_G, k_b),
B_G_m,
)
B = np.where(np.logical_and(G_m == 1, R_r == 1), G + B_G_m, B)
B_G_m = np.where(
np.logical_and(G_m == 1, R_c == 1),
_cnv_h(B_G, k_b),
B_G_m,
)
B = np.where(np.logical_and(G_m == 1, R_c == 1), G + B_G_m, B)
del B_G_m, R_r, R_c, G_m, B_G
# Updating of the red (blue) component in the blue (red) locations.
R_B = R - B
R_B_m = np.where(
B_m == 1,
np.where(M == 1, _cnv_h(R_B, FIR), _cnv_v(R_B, FIR)),
0,
)
R = np.where(B_m == 1, B + R_B_m, R)
R_B_m = np.where(
R_m == 1,
np.where(M == 1, _cnv_h(R_B, FIR), _cnv_v(R_B, FIR)),
0,
)
B = np.where(R_m == 1, R - R_B_m, B)
del R_B, R_B_m, R_m
return tstack([R, G, B])