video-matrices.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /******************************************************************************
  2. Copyright (C) 2014 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "video-io.h"
  15. #include "../util/bmem.h"
  16. #include "../graphics/matrix3.h"
  17. #include <assert.h>
  18. //#define LOG_MATRICES
  19. static struct {
  20. float range_min[3];
  21. float range_max[3];
  22. float black_levels[2][3];
  23. float float_range_min[3];
  24. float float_range_max[3];
  25. } bpp_info[9];
  26. static struct {
  27. enum video_colorspace const color_space;
  28. float const Kb, Kr;
  29. float matrix[OBS_COUNTOF(bpp_info)][2][16];
  30. } format_info[] = {
  31. {
  32. VIDEO_CS_601,
  33. 0.114f,
  34. 0.299f,
  35. },
  36. {
  37. VIDEO_CS_709,
  38. 0.0722f,
  39. 0.2126f,
  40. },
  41. {
  42. VIDEO_CS_2100_PQ,
  43. 0.0593f,
  44. 0.2627f,
  45. },
  46. };
  47. #define NUM_FORMATS (sizeof(format_info) / sizeof(format_info[0]))
  48. #ifdef LOG_MATRICES
  49. static void log_matrix(float const matrix[16])
  50. {
  51. blog(LOG_DEBUG,
  52. "\n% f, % f, % f, % f"
  53. "\n% f, % f, % f, % f"
  54. "\n% f, % f, % f, % f"
  55. "\n% f, % f, % f, % f",
  56. matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5],
  57. matrix[6], matrix[7], matrix[8], matrix[9], matrix[10], matrix[11],
  58. matrix[12], matrix[13], matrix[14], matrix[15]);
  59. }
  60. #endif
  61. static void initialize_matrix(float const Kb, float const Kr,
  62. float bit_range_max, float const range_min[3],
  63. float const range_max[3],
  64. float const black_levels[3], float matrix[16])
  65. {
  66. struct matrix3 color_matrix;
  67. float const yvals = range_max[0] - range_min[0];
  68. float const uvals = (range_max[1] - range_min[1]) / 2.f;
  69. float const vvals = (range_max[2] - range_min[2]) / 2.f;
  70. float const yscale = bit_range_max / yvals;
  71. float const uscale = bit_range_max / uvals;
  72. float const vscale = bit_range_max / vvals;
  73. float const Kg = (1.f - Kb - Kr);
  74. vec3_set(&color_matrix.x, yscale, 0.f, vscale * (1.f - Kr));
  75. vec3_set(&color_matrix.y, yscale, uscale * (Kb - 1.f) * Kb / Kg,
  76. vscale * (Kr - 1.f) * Kr / Kg);
  77. vec3_set(&color_matrix.z, yscale, uscale * (1.f - Kb), 0.f);
  78. struct vec3 offsets, multiplied;
  79. vec3_set(&offsets, -black_levels[0] / bit_range_max,
  80. -black_levels[1] / bit_range_max,
  81. -black_levels[2] / bit_range_max);
  82. vec3_rotate(&multiplied, &offsets, &color_matrix);
  83. matrix[0] = color_matrix.x.x;
  84. matrix[1] = color_matrix.x.y;
  85. matrix[2] = color_matrix.x.z;
  86. matrix[3] = multiplied.x;
  87. matrix[4] = color_matrix.y.x;
  88. matrix[5] = color_matrix.y.y;
  89. matrix[6] = color_matrix.y.z;
  90. matrix[7] = multiplied.y;
  91. matrix[8] = color_matrix.z.x;
  92. matrix[9] = color_matrix.z.y;
  93. matrix[10] = color_matrix.z.z;
  94. matrix[11] = multiplied.z;
  95. matrix[12] = matrix[13] = matrix[14] = 0.;
  96. matrix[15] = 1.;
  97. #ifdef LOG_MATRICES
  98. log_matrix(matrix);
  99. #endif
  100. }
  101. static void initialize_matrices()
  102. {
  103. static const float full_range_min3[] = {0, 0, 0};
  104. float min_value = 16.f;
  105. float max_luma = 235.f;
  106. float max_chroma = 240.f;
  107. float range = 256.f;
  108. for (uint32_t bpp = 8; bpp <= 16; ++bpp) {
  109. const uint32_t bpp_index = bpp - 8;
  110. bpp_info[bpp_index].range_min[0] = min_value;
  111. bpp_info[bpp_index].range_min[1] = min_value;
  112. bpp_info[bpp_index].range_min[2] = min_value;
  113. bpp_info[bpp_index].range_max[0] = max_luma;
  114. bpp_info[bpp_index].range_max[1] = max_chroma;
  115. bpp_info[bpp_index].range_max[2] = max_chroma;
  116. const float mid_chroma = 0.5f * (min_value + max_chroma);
  117. bpp_info[bpp_index].black_levels[0][0] = min_value;
  118. bpp_info[bpp_index].black_levels[0][1] = mid_chroma;
  119. bpp_info[bpp_index].black_levels[0][2] = mid_chroma;
  120. bpp_info[bpp_index].black_levels[1][0] = 0.f;
  121. bpp_info[bpp_index].black_levels[1][1] = mid_chroma;
  122. bpp_info[bpp_index].black_levels[1][2] = mid_chroma;
  123. const float range_max = range - 1.f;
  124. bpp_info[bpp_index].float_range_min[0] = min_value / range_max;
  125. bpp_info[bpp_index].float_range_min[1] = min_value / range_max;
  126. bpp_info[bpp_index].float_range_min[2] = min_value / range_max;
  127. bpp_info[bpp_index].float_range_max[0] = max_luma / range_max;
  128. bpp_info[bpp_index].float_range_max[1] = max_chroma / range_max;
  129. bpp_info[bpp_index].float_range_max[2] = max_chroma / range_max;
  130. for (size_t i = 0; i < NUM_FORMATS; i++) {
  131. float full_range_max3[] = {range_max, range_max,
  132. range_max};
  133. initialize_matrix(format_info[i].Kb, format_info[i].Kr,
  134. range_max, full_range_min3,
  135. full_range_max3,
  136. bpp_info[bpp_index].black_levels[1],
  137. format_info[i].matrix[bpp_index][1]);
  138. initialize_matrix(format_info[i].Kb, format_info[i].Kr,
  139. range_max,
  140. bpp_info[bpp_index].range_min,
  141. bpp_info[bpp_index].range_max,
  142. bpp_info[bpp_index].black_levels[0],
  143. format_info[i].matrix[bpp_index][0]);
  144. }
  145. min_value *= 2.f;
  146. max_luma *= 2.f;
  147. max_chroma *= 2.f;
  148. range *= 2.f;
  149. }
  150. }
  151. static bool matrices_initialized = false;
  152. static const float full_min[3] = {0.0f, 0.0f, 0.0f};
  153. static const float full_max[3] = {1.0f, 1.0f, 1.0f};
  154. static bool video_format_get_parameters_for_bpc(
  155. enum video_colorspace color_space, enum video_range_type range,
  156. float matrix[16], float range_min[3], float range_max[3], uint32_t bpc)
  157. {
  158. if (!matrices_initialized) {
  159. initialize_matrices();
  160. matrices_initialized = true;
  161. }
  162. if ((color_space == VIDEO_CS_DEFAULT) || (color_space == VIDEO_CS_SRGB))
  163. color_space = VIDEO_CS_709;
  164. else if (color_space == VIDEO_CS_2100_HLG)
  165. color_space = VIDEO_CS_2100_PQ;
  166. if (bpc < 8)
  167. bpc = 8;
  168. if (bpc > 16)
  169. bpc = 16;
  170. const uint32_t bpc_index = bpc - 8;
  171. assert(bpc_index < OBS_COUNTOF(bpp_info));
  172. bool success = false;
  173. for (size_t i = 0; i < NUM_FORMATS; i++) {
  174. success = format_info[i].color_space == color_space;
  175. if (success) {
  176. const bool full_range = range == VIDEO_RANGE_FULL;
  177. memcpy(matrix,
  178. format_info[i].matrix[bpc_index][full_range],
  179. sizeof(float) * 16);
  180. if (range_min) {
  181. const float *src_range_min =
  182. full_range ? full_min
  183. : bpp_info[bpc_index]
  184. .float_range_min;
  185. memcpy(range_min, src_range_min,
  186. sizeof(float) * 3);
  187. }
  188. if (range_max) {
  189. const float *src_range_max =
  190. full_range ? full_max
  191. : bpp_info[bpc_index]
  192. .float_range_max;
  193. memcpy(range_max, src_range_max,
  194. sizeof(float) * 3);
  195. }
  196. break;
  197. }
  198. }
  199. return success;
  200. }
  201. bool video_format_get_parameters(enum video_colorspace color_space,
  202. enum video_range_type range, float matrix[16],
  203. float range_min[3], float range_max[3])
  204. {
  205. uint32_t bpc = (color_space == VIDEO_CS_2100_PQ ||
  206. color_space == VIDEO_CS_2100_HLG)
  207. ? 10
  208. : 8;
  209. return video_format_get_parameters_for_bpc(color_space, range, matrix,
  210. range_min, range_max, bpc);
  211. }
  212. bool video_format_get_parameters_for_format(enum video_colorspace color_space,
  213. enum video_range_type range,
  214. enum video_format format,
  215. float matrix[16],
  216. float range_min[3],
  217. float range_max[3])
  218. {
  219. uint32_t bpc;
  220. switch (format) {
  221. case VIDEO_FORMAT_I010:
  222. case VIDEO_FORMAT_P010:
  223. case VIDEO_FORMAT_I210:
  224. case VIDEO_FORMAT_V210:
  225. bpc = 10;
  226. break;
  227. case VIDEO_FORMAT_I412:
  228. case VIDEO_FORMAT_YA2L:
  229. bpc = 12;
  230. break;
  231. case VIDEO_FORMAT_P216:
  232. case VIDEO_FORMAT_P416:
  233. bpc = 16;
  234. break;
  235. default:
  236. bpc = 8;
  237. break;
  238. }
  239. return video_format_get_parameters_for_bpc(color_space, range, matrix,
  240. range_min, range_max, bpc);
  241. }