20#include "common/macros.h"
25#ifndef BARTEN_CSF_TOOLS_H_
26#define BARTEN_CSF_TOOLS_H_
28static float linear_interpolate(
float left_position,
float left_value,
29 float right_position,
float right_value,
30 float sample_position) {
31 return left_value + ((right_value - left_value) / (right_position - left_position)) * (sample_position - left_position);
39static const float barten_csf_param_anchors[6] = { 0.002, 0.02, 0.2, 2, 20, 150 };
41static const float barten_csf_params[6][5] = {
42 { 0.0160737, 0.991265, 3.74038, 0.50722, 4.46044 },
43 { 0.383873, 0.800889, 3.54104, 0.682505, 4.94958 },
44 { 0.929301, 0.476505, 4.37453, 0.750315, 5.28678 },
45 { 1.29776, 0.405782, 4.40602, 0.935314, 5.61425 },
46 { 1.49222, 0.334278, 3.79542, 1.07327, 6.4635 },
47 { 1.46213, 0.394533, 2.7755, 1.16577, 7.45665 }
50static const float barten_csf_sa[4] = { 30.162, 4.0627, 1.6596, 0.2712 };
51static const float barten_mtf_params_a[4] = { 0.424838596301290, 0.572435103936480, 0.000167576239164937, 0.00255872352306433 };
52static const float barten_mtf_params_b[4] = { 0.028, 0.37, 37, 360 };
55static float barten_rod_cone_sens(
float luminance_level)
57 float cvi_sens_drop = barten_csf_sa[1];
58 float cvi_trans_slope = barten_csf_sa[2];
59 float cvi_low_slope = barten_csf_sa[3];
60 return barten_csf_sa[0] * pow(pow(cvi_sens_drop / luminance_level, cvi_trans_slope) + 1, - cvi_low_slope);
64static float barten_mtf(
float spatial_frequency)
67 for (
int i = 0; i <= 3; i++)
68 mtf = mtf + barten_mtf_params_a[i] * exp(- barten_mtf_params_b[i] * spatial_frequency);
72#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
80static FORCE_INLINE
inline float barten_csf(
int lambda,
double adm_norm_view_dist,
81 int adm_ref_display_height,
double adm_csf_lum_level,
double adm_csf_scale)
84 float r = adm_norm_view_dist * adm_ref_display_height *
M_PI / 180.0;
86 float spatial_frequency = r / pow(2, lambda + 1);
88 double clamped_lum =
CLAMP(adm_csf_lum_level, barten_csf_param_anchors[0], barten_csf_param_anchors[5]);
90 int left_lum_index = 0, right_lum_index = 0;
93 if (clamped_lum >= barten_csf_param_anchors[i] && clamped_lum <= barten_csf_param_anchors[i + 1]) {
95 right_lum_index = i + 1;
101 float left_lum = barten_csf_param_anchors[left_lum_index];
102 float right_lum = barten_csf_param_anchors[right_lum_index];
104 float left_position = log10(left_lum);
105 float right_position = log10(right_lum);
106 float sample_position = log10(clamped_lum);
108 float p_0 = linear_interpolate(left_position, barten_csf_params[left_lum_index][1], right_position,
109 barten_csf_params[right_lum_index][1], sample_position);
110 float p_1 = linear_interpolate(left_position, barten_csf_params[left_lum_index][2], right_position,
111 barten_csf_params[right_lum_index][2], sample_position);
112 float p_2 = linear_interpolate(left_position, barten_csf_params[left_lum_index][3], right_position,
113 barten_csf_params[right_lum_index][3], sample_position);
114 float p_3 = linear_interpolate(left_position, barten_csf_params[left_lum_index][4], right_position,
115 barten_csf_params[right_lum_index][4], sample_position);
120 float a = 1.0f + pow(p_0 * spatial_frequency, p_1);
121 float b = 1.0f / pow(1 - exp(- pow(spatial_frequency / 7, 2)), p_2);
124 float csf = p_3 / pow(a * b, 0.5);
127 return csf * barten_mtf(spatial_frequency) * barten_rod_cone_sens(adm_csf_lum_level) * adm_csf_scale;
130static const float BLENDED_CSF_1080_3H[2][4] = {
131 {0.01183, 0.025026, 0.04295, 0.058621},
132 {0.004302, 0.011778, 0.023918, 0.035901}
135static const float BLENDED_CSF_1080_5H[2][4] = {
136 {0.004212, 0.014809, 0.029642, 0.047464},
137 {0.000984, 0.005852, 0.0146, 0.027574}
140static const float BLENDED_CSF_2160_3H[2][4] = {
141 {0.00226, 0.01183, 0.025026, 0.04295},
142 {0.000479, 0.004302, 0.011778, 0.023918}
145static const float BLENDED_CSF_2160_5H[2][4] = {
146 {0.000092, 0.004212, 0.014809, 0.029642},
147 {0.000050, 0.000984, 0.005852, 0.0146}
150static const float BLENDED_CSF_720_3H[2][4] = {
151 {0.018715, 0.035637, 0.052798, 0.061509},
152 {0.007999, 0.018396, 0.031851, 0.037718}
155static const float BLENDED_CSF_720_5H[2][4] = {
156 {0.010144, 0.022561, 0.040309, 0.05672},
157 {0.003463, 0.010282, 0.021839, 0.034641}
160static const float BLENDED_CSF_480_3H[2][4] = {
161 {0.027961, 0.045875, 0.060275, 0.056234},
162 {0.013572, 0.026277, 0.036959, 0.034511}
165static const float BLENDED_CSF_480_5H[2][4] = {
166 {0.016781, 0.032822, 0.05032, 0.061594},
167 {0.00691, 0.016545, 0.029917, 0.037777}
174static const float BLENDED_CSF_1080_3H_MAE[2][4] = {
175 {0.011249, 0.022606, 0.035930, 0.045673},
176 {0.004097, 0.010921, 0.021430, 0.031313}
179static const float BLENDED_CSF_1080_5H_MAE[2][4] = {
180 {0.004052, 0.013939, 0.026298, 0.038833},
181 {0.000927, 0.005544, 0.013415, 0.024515}
184static const float BLENDED_CSF_2160_3H_MAE[2][4] = {
185 {0.002166, 0.011249, 0.022606, 0.035930},
186 {0.000447, 0.004097, 0.010921, 0.021430}
189static const float BLENDED_CSF_2160_5H_MAE[2][4] = {
190 {0.000077, 0.004052, 0.013939, 0.026298},
191 {0.000045, 0.000927, 0.005544, 0.013415}
194static const float BLENDED_CSF_720_3H_MAE[2][4] = {
195 {0.017329, 0.030870, 0.042134, 0.047410},
196 {0.007509, 0.016707, 0.028072, 0.032722}
199static const float BLENDED_CSF_720_5H_MAE[2][4] = {
200 {0.009689, 0.020577, 0.034162, 0.044523},
201 {0.003302, 0.009579, 0.019661, 0.030318}
204static const float BLENDED_CSF_480_3H_MAE[2][4] = {
205 {0.024969, 0.037825, 0.046676, 0.043974},
206 {0.012511, 0.023424, 0.032139, 0.030166}
209static const float BLENDED_CSF_480_5H_MAE[2][4] = {
210 {0.015665, 0.028766, 0.040612, 0.047483},
211 {0.006514, 0.015107, 0.026476, 0.032776}
218static FORCE_INLINE
inline float barten_watson_blend_csf_mae(
int scale,
int theta,
double adm_norm_view_dist,
219 int adm_ref_display_height) {
220 if (adm_ref_display_height == 1080 && adm_norm_view_dist == 3.0) {
221 return BLENDED_CSF_1080_3H_MAE[theta][scale];
222 }
else if (adm_ref_display_height == 1080 && adm_norm_view_dist == 5.0) {
223 return BLENDED_CSF_1080_5H_MAE[theta][scale];
224 }
else if (adm_ref_display_height == 2160 && adm_norm_view_dist == 1.5) {
226 return BLENDED_CSF_1080_3H_MAE[theta][scale];
227 }
else if (adm_ref_display_height == 2160 && adm_norm_view_dist == 3.0) {
228 return BLENDED_CSF_2160_3H_MAE[theta][scale];
229 }
else if (adm_ref_display_height == 2160 && adm_norm_view_dist == 5.0) {
230 return BLENDED_CSF_2160_5H_MAE[theta][scale];
231 }
else if (adm_ref_display_height == 720 && adm_norm_view_dist == 3.0) {
232 return BLENDED_CSF_720_3H_MAE[theta][scale];
233 }
else if (adm_ref_display_height == 720 && adm_norm_view_dist == 5.0) {
234 return BLENDED_CSF_720_5H_MAE[theta][scale];
235 }
else if (adm_ref_display_height == 480 && adm_norm_view_dist == 3.0) {
236 return BLENDED_CSF_480_3H_MAE[theta][scale];
237 }
else if (adm_ref_display_height == 480 && adm_norm_view_dist == 5.0) {
238 return BLENDED_CSF_480_5H_MAE[theta][scale];
248static FORCE_INLINE
inline float barten_watson_blend_csf(
int scale,
int theta,
double adm_norm_view_dist,
249 int adm_ref_display_height) {
250 if (adm_ref_display_height == 1080 && adm_norm_view_dist == 3.0) {
251 return BLENDED_CSF_1080_3H[theta][scale];
252 }
else if (adm_ref_display_height == 1080 && adm_norm_view_dist == 5.0) {
253 return BLENDED_CSF_1080_5H[theta][scale];
254 }
else if (adm_ref_display_height == 2160 && adm_norm_view_dist == 1.5) {
256 return BLENDED_CSF_1080_3H[theta][scale];
257 }
else if (adm_ref_display_height == 2160 && adm_norm_view_dist == 3.0) {
258 return BLENDED_CSF_2160_3H[theta][scale];
259 }
else if (adm_ref_display_height == 2160 && adm_norm_view_dist == 5.0) {
260 return BLENDED_CSF_2160_5H[theta][scale];
261 }
else if (adm_ref_display_height == 720 && adm_norm_view_dist == 3.0) {
262 return BLENDED_CSF_720_3H[theta][scale];
263 }
else if (adm_ref_display_height == 720 && adm_norm_view_dist == 5.0) {
264 return BLENDED_CSF_720_5H[theta][scale];
265 }
else if (adm_ref_display_height == 480 && adm_norm_view_dist == 3.0) {
266 return BLENDED_CSF_480_3H[theta][scale];
267 }
else if (adm_ref_display_height == 480 && adm_norm_view_dist == 5.0) {
268 return BLENDED_CSF_480_5H[theta][scale];