"""
Malvar (2004) Bayer CFA Demosaicing
===================================
*Bayer* CFA (Colour Filter Array) *Malvar (2004)* demosaicing.
References
----------
- :cite:`Malvar2004a` : Malvar, H. S., He, L.-W., Cutler, R., & Way, O. M.
(2004). High-Quality Linear Interpolation for Demosaicing of
Bayer-Patterned Color Images. International Conference of Acoustic, Speech
and Signal Processing, 5-8.
http://research.microsoft.com/apps/pubs/default.aspx?id=102068
"""
from __future__ import annotations
import numpy as np
from colour.hints import ArrayLike, Literal, NDArrayFloat
from colour.utilities import as_float_array, ones, tstack
from scipy.ndimage.filters import convolve
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_Malvar2004",
]
[docs]
def demosaicing_CFA_Bayer_Malvar2004(
CFA: ArrayLike,
pattern: Literal["RGGB", "BGGR", "GRBG", "GBRG"] | str = "RGGB",
) -> NDArrayFloat:
"""
Return the demosaiced *RGB* colourspace array from given *Bayer* CFA using
*Malvar (2004)* demosaicing algorithm.
Parameters
----------
CFA
*Bayer* CFA.
pattern
Arrangement of the colour filters on the pixel array.
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:`Malvar2004a`
Examples
--------
>>> CFA = np.array(
... [
... [0.30980393, 0.36078432, 0.30588236, 0.3764706],
... [0.35686275, 0.39607844, 0.36078432, 0.40000001],
... ]
... )
>>> demosaicing_CFA_Bayer_Malvar2004(CFA)
array([[[ 0.30980393, 0.31666668, 0.32941177],
[ 0.33039216, 0.36078432, 0.38112746],
[ 0.30588236, 0.32794118, 0.34877452],
[ 0.36274511, 0.3764706 , 0.38480393]],
<BLANKLINE>
[[ 0.34828432, 0.35686275, 0.36568628],
[ 0.35318628, 0.38186275, 0.39607844],
[ 0.3379902 , 0.36078432, 0.3754902 ],
[ 0.37769609, 0.39558825, 0.40000001]]])
>>> CFA = np.array(
... [
... [0.3764706, 0.360784320, 0.40784314, 0.3764706],
... [0.35686275, 0.30980393, 0.36078432, 0.29803923],
... ]
... )
>>> demosaicing_CFA_Bayer_Malvar2004(CFA, "BGGR")
array([[[ 0.35539217, 0.37058825, 0.3764706 ],
[ 0.34264707, 0.36078432, 0.37450981],
[ 0.36568628, 0.39607844, 0.40784314],
[ 0.36568629, 0.3764706 , 0.3882353 ]],
<BLANKLINE>
[[ 0.34411765, 0.35686275, 0.36200981],
[ 0.30980393, 0.32990197, 0.34975491],
[ 0.33039216, 0.36078432, 0.38063726],
[ 0.29803923, 0.30441178, 0.31740197]]])
"""
CFA = np.squeeze(as_float_array(CFA))
R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern)
GR_GB = (
as_float_array(
[
[0.0, 0.0, -1.0, 0.0, 0.0],
[0.0, 0.0, 2.0, 0.0, 0.0],
[-1.0, 2.0, 4.0, 2.0, -1.0],
[0.0, 0.0, 2.0, 0.0, 0.0],
[0.0, 0.0, -1.0, 0.0, 0.0],
]
)
/ 8
)
Rg_RB_Bg_BR = (
as_float_array(
[
[0.0, 0.0, 0.5, 0.0, 0.0],
[0.0, -1.0, 0.0, -1.0, 0.0],
[-1.0, 4.0, 5.0, 4.0, -1.0],
[0.0, -1.0, 0.0, -1.0, 0.0],
[0.0, 0.0, 0.5, 0.0, 0.0],
]
)
/ 8
)
Rg_BR_Bg_RB = np.transpose(Rg_RB_Bg_BR)
Rb_BB_Br_RR = (
as_float_array(
[
[0.0, 0.0, -1.5, 0.0, 0.0],
[0.0, 2.0, 0.0, 2.0, 0.0],
[-1.5, 0.0, 6.0, 0.0, -1.5],
[0.0, 2.0, 0.0, 2.0, 0.0],
[0.0, 0.0, -1.5, 0.0, 0.0],
]
)
/ 8
)
R = CFA * R_m
G = CFA * G_m
B = CFA * B_m
del G_m
G = np.where(np.logical_or(R_m == 1, B_m == 1), convolve(CFA, GR_GB), G)
RBg_RBBR = convolve(CFA, Rg_RB_Bg_BR)
RBg_BRRB = convolve(CFA, Rg_BR_Bg_RB)
RBgr_BBRR = convolve(CFA, Rb_BB_Br_RR)
del GR_GB, Rg_RB_Bg_BR, Rg_BR_Bg_RB, Rb_BB_Br_RR
# 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)
del R_m, B_m
R = np.where(np.logical_and(R_r == 1, B_c == 1), RBg_RBBR, R)
R = np.where(np.logical_and(B_r == 1, R_c == 1), RBg_BRRB, R)
B = np.where(np.logical_and(B_r == 1, R_c == 1), RBg_RBBR, B)
B = np.where(np.logical_and(R_r == 1, B_c == 1), RBg_BRRB, B)
R = np.where(np.logical_and(B_r == 1, B_c == 1), RBgr_BBRR, R)
B = np.where(np.logical_and(R_r == 1, R_c == 1), RBgr_BBRR, B)
del RBg_RBBR, RBg_BRRB, RBgr_BBRR, R_r, R_c, B_r, B_c
return tstack([R, G, B])