Coverage for colour/models/rgb/hanbury2003.py: 100%

39 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-15 19:01 +1300

1""" 

2IHLS Colour Encoding 

3==================== 

4 

5Define the :math:`IHLS` (Improved HLS) colourspace encoding and decoding 

6transformations. The :math:`IHLS` colourspace provides a 3D-polar coordinate 

7colour representation that improves upon the standard HLS model for image 

8analysis applications. 

9 

10- :func:`colour.RGB_to_IHLS` 

11- :func:`colour.IHLS_to_RGB` 

12 

13References 

14---------- 

15- :cite:`Hanbury2003` : Hanbury, A. (2003). A 3D-Polar Coordinate Colour 

16 Representation Well Adapted to Image Analysis. In J. Bigun & T. Gustavsson 

17 (Eds.), Image Analysis (pp. 804-811). Springer Berlin Heidelberg. 

18 ISBN:978-3-540-45103-7 

19""" 

20 

21from __future__ import annotations 

22 

23import numpy as np 

24 

25from colour.algebra import sdiv, sdiv_mode, vecmul 

26from colour.hints import ( # noqa: TC001 

27 Domain1, 

28 NDArrayFloat, 

29 Range1, 

30) 

31from colour.utilities import from_range_1, to_domain_1, tsplit, tstack, zeros 

32 

33__author__ = "Colour Developers" 

34__copyright__ = "Copyright 2013 Colour Developers" 

35__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

36__maintainer__ = "Colour Developers" 

37__email__ = "colour-developers@colour-science.org" 

38__status__ = "Production" 

39 

40__all__ = [ 

41 "RGB_to_IHLS", 

42 "IHLS_to_RGB", 

43] 

44 

45MATRIX_RGB_TO_YC_1_C_2: NDArrayFloat = np.array( 

46 [ 

47 [0.2126, 0.7152, 0.0722], 

48 [1, -0.5, -0.5], 

49 [0, -np.sqrt(3) / 2, np.sqrt(3) / 2], 

50 ] 

51) 

52"""*RGB* colourspace to *YC_1C_2* colourspace matrix.""" 

53 

54MATRIX_YC_1_C_2_TO_RGB: NDArrayFloat = np.linalg.inv(MATRIX_RGB_TO_YC_1_C_2) 

55"""*YC_1C_2* colourspace to *RGB* colourspace matrix.""" 

56 

57 

58def RGB_to_IHLS(RGB: Domain1) -> Range1: 

59 """ 

60 Convert from *RGB* colourspace to *IHLS* (Improved HLS) colourspace. 

61 

62 Parameters 

63 ---------- 

64 RGB 

65 *RGB* colourspace array. 

66 

67 Returns 

68 ------- 

69 :class:`numpy.ndarray` 

70 *HYS* colourspace array. 

71 

72 Notes 

73 ----- 

74 +------------+-----------------------+---------------+ 

75 | **Domain** | **Scale - Reference** | **Scale - 1** | 

76 +============+=======================+===============+ 

77 | ``RGB`` | 1 | 1 | 

78 +------------+-----------------------+---------------+ 

79 

80 +------------+-----------------------+---------------+ 

81 | **Range** | **Scale - Reference** | **Scale - 1** | 

82 +============+=======================+===============+ 

83 | ``HYS`` | 1 | 1 | 

84 +------------+-----------------------+---------------+ 

85 

86 References 

87 ---------- 

88 :cite:`Hanbury2003` 

89 

90 Examples 

91 -------- 

92 >>> RGB = np.array([0.45595571, 0.03039702, 0.04087245]) 

93 >>> RGB_to_IHLS(RGB) # doctest: +ELLIPSIS 

94 array([ 6.2616051..., 0.1216271..., 0.4255586...]) 

95 """ 

96 

97 RGB = to_domain_1(RGB) 

98 R, G, B = tsplit(RGB) 

99 

100 Y, C_1, C_2 = tsplit(vecmul(MATRIX_RGB_TO_YC_1_C_2, RGB)) 

101 

102 C = np.hypot(C_1, C_2) 

103 

104 with sdiv_mode(): 

105 C_1_C = sdiv(C_1, C) 

106 

107 arcos_C_1_C_2 = zeros(C_1_C.shape) 

108 arcos_C_1_C_2[C_1_C != 0] = np.arccos(C_1_C[C_1_C != 0]) 

109 

110 H = np.where(C_2 <= 0, arcos_C_1_C_2, (np.pi * 2) - arcos_C_1_C_2) 

111 

112 S = np.maximum(np.maximum(R, G), B) - np.minimum(np.minimum(R, G), B) 

113 

114 HYS = tstack([H, Y, S]) 

115 

116 return from_range_1(HYS) 

117 

118 

119def IHLS_to_RGB(HYS: Domain1) -> Range1: 

120 """ 

121 Convert from *IHLS* (Improved HLS) colourspace to *RGB* colourspace. 

122 

123 Parameters 

124 ---------- 

125 HYS 

126 *IHLS* colourspace array. 

127 

128 Returns 

129 ------- 

130 :class:`numpy.ndarray` 

131 *RGB* colourspace array. 

132 

133 Notes 

134 ----- 

135 +------------+-----------------------+---------------+ 

136 | **Domain** | **Scale - Reference** | **Scale - 1** | 

137 +============+=======================+===============+ 

138 | ``HYS`` | 1 | 1 | 

139 +------------+-----------------------+---------------+ 

140 

141 +------------+-----------------------+---------------+ 

142 | **Range** | **Scale - Reference** | **Scale - 1** | 

143 +============+=======================+===============+ 

144 | ``RGB`` | 1 | 1 | 

145 +------------+-----------------------+---------------+ 

146 

147 References 

148 ---------- 

149 :cite:`Hanbury2003` 

150 

151 Examples 

152 -------- 

153 >>> HYS = np.array([6.26160518, 0.12162712, 0.42555869]) 

154 >>> IHLS_to_RGB(HYS) # doctest: +ELLIPSIS 

155 array([ 0.4559557..., 0.0303970..., 0.0408724...]) 

156 """ 

157 

158 H, Y, S = tsplit(to_domain_1(HYS)) 

159 

160 pi_3 = np.pi / 3 

161 

162 k = np.floor(H / pi_3) 

163 H_s = H - k * pi_3 

164 C = (np.sqrt(3) * S) / (2 * np.sin(2 * pi_3 - H_s)) 

165 

166 C_1 = C * np.cos(H) 

167 C_2 = -C * np.sin(H) 

168 

169 RGB = vecmul(MATRIX_YC_1_C_2_TO_RGB, tstack([Y, C_1, C_2])) 

170 

171 return from_range_1(RGB)