libvmaf/src/feature/adm_tools.h (258 lines of code) (raw):

/** * * Copyright 2016-2020 Netflix, Inc. * * Licensed under the BSD+Patent License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://opensource.org/licenses/BSDplusPatent * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include <math.h> #include "common/macros.h" #ifndef M_PI #define M_PI 3.14159265358979323846264338327 #endif #pragma once #ifndef ADM_TOOLS_H_ #define ADM_TOOLS_H_ // i = 0, j = 0: indices y: 1,0,1, x: 1,0,1 #define ADM_CM_THRESH_S_0_0(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ sum += flt_ptr[src_px_stride + 1]; \ sum += flt_ptr[src_px_stride]; \ sum += flt_ptr[src_px_stride + 1]; \ sum += flt_ptr[1]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[0]); \ sum += flt_ptr[1]; \ sum += flt_ptr[src_px_stride + 1]; \ sum += flt_ptr[src_px_stride]; \ sum += flt_ptr[src_px_stride + 1]; \ *accum += sum; \ } \ } // i = 0, j = w-1: indices y: 1,0,1, x: w-2, w-1, w-1 #define ADM_CM_THRESH_S_0_W_M_1(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ sum += flt_ptr[src_px_stride + w - 2]; \ sum += flt_ptr[src_px_stride + w - 1]; \ sum += flt_ptr[src_px_stride + w - 1]; \ sum += flt_ptr[w - 2]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[w - 1]); \ sum += flt_ptr[w - 1]; \ sum += flt_ptr[src_px_stride + w - 2]; \ sum += flt_ptr[src_px_stride + w - 1]; \ sum += flt_ptr[src_px_stride + w - 1]; \ *accum += sum; \ } \ } // i = 0, j = 1, ..., w-2: indices y: 1,0,1, x: j-1,j,j+1 #define ADM_CM_THRESH_S_0_J(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ sum += flt_ptr[src_px_stride + j - 1]; \ sum += flt_ptr[src_px_stride + j]; \ sum += flt_ptr[src_px_stride + j + 1]; \ sum += flt_ptr[j - 1]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[j]); \ sum += flt_ptr[j + 1]; \ sum += flt_ptr[src_px_stride + j - 1]; \ sum += flt_ptr[src_px_stride + j]; \ sum += flt_ptr[src_px_stride + j + 1]; \ *accum += sum; \ } \ } // i = h-1, j = 0: indices y: h-2,h-1,h-1, x: 1,0,1 #define ADM_CM_THRESH_S_H_M_1_0(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ src_ptr += (src_px_stride * (h - 2)); \ flt_ptr += (src_px_stride * (h - 2)); \ sum += flt_ptr[1]; \ sum += flt_ptr[0]; \ sum += flt_ptr[1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[1]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[0]); \ sum += flt_ptr[1]; \ sum += flt_ptr[1]; \ sum += flt_ptr[0]; \ sum += flt_ptr[1]; \ *accum += sum; \ } \ } // i = h-1, j = w-1: indices y: h-2,h-1,h-1, x: w-2, w-1, w-1 #define ADM_CM_THRESH_S_H_M_1_W_M_1(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ src_ptr += (src_px_stride * (h - 2)); \ flt_ptr += (src_px_stride * (h - 2)); \ sum += flt_ptr[w - 2]; \ sum += flt_ptr[w - 1]; \ sum += flt_ptr[w - 1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[w - 2]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[w - 1]); \ sum += flt_ptr[w - 1]; \ sum += flt_ptr[w - 2]; \ sum += flt_ptr[w - 1]; \ sum += flt_ptr[w - 1]; \ *accum += sum; \ } \ } // i = h-1, j = 1, ..., w-2: indices y: h-2,h-1,h-1, x: j-1,j,j+1 #define ADM_CM_THRESH_S_H_M_1_J(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ src_ptr += (src_px_stride * (h - 2)); \ flt_ptr += (src_px_stride * (h - 2)); \ sum += flt_ptr[j - 1];\ sum += flt_ptr[j]; \ sum += flt_ptr[j + 1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[j - 1]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[j]); \ sum += flt_ptr[j + 1]; \ sum += flt_ptr[j - 1]; \ sum += flt_ptr[j]; \ sum += flt_ptr[j + 1]; \ *accum += sum; \ } \ } // i = 1,..,h-2, j = 1,..,w-2: indices y: i-1,i,i+1, x: j-1,j,j+1 #define ADM_CM_THRESH_S_I_J(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ src_ptr += (src_px_stride * (i - 1)); \ flt_ptr += (src_px_stride * (i - 1)); \ sum += flt_ptr[j - 1]; \ sum += flt_ptr[j]; \ sum += flt_ptr[j + 1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[j - 1]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[j]); \ sum += flt_ptr[j + 1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[j - 1]; \ sum += flt_ptr[j]; \ sum += flt_ptr[j + 1]; \ *accum += sum; \ } \ } // i = 1,..,h-2, j = 0: indices y: i-1,i,i+1, x: 1,0,1 #define ADM_CM_THRESH_S_I_0(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ src_ptr += (src_px_stride * (i - 1)); \ flt_ptr += (src_px_stride * (i - 1)); \ sum += flt_ptr[1]; \ sum += flt_ptr[0]; \ sum += flt_ptr[1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[1]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[0]); \ sum += flt_ptr[1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[1]; \ sum += flt_ptr[0]; \ sum += flt_ptr[1]; \ *accum += sum; \ } \ } // i = 1,..,h-2, j = w-1: indices y: i-1,i,i+1, x: w-2,w-1,w-1 #define ADM_CM_THRESH_S_I_W_M_1(angles,flt_angles,src_px_stride,accum,w,h,i,j) \ { \ *accum = 0; \ for (int theta = 0; theta < 3; ++theta) { \ const float *src_ptr = angles[theta]; \ const float *flt_ptr = flt_angles[theta]; \ float sum = 0; \ src_ptr += (src_px_stride * (i-1)); \ flt_ptr += (src_px_stride * (i - 1)); \ sum += flt_ptr[w - 2]; \ sum += flt_ptr[w - 1]; \ sum += flt_ptr[w - 1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[w - 2]; \ sum += FLOAT_ONE_BY_15 * fabsf(src_ptr[w - 1]); \ sum += flt_ptr[w - 1]; \ src_ptr += src_px_stride; \ flt_ptr += src_px_stride; \ sum += flt_ptr[w - 2]; \ sum += flt_ptr[w - 1]; \ sum += flt_ptr[w - 1]; \ *accum += sum; \ } \ } typedef struct adm_dwt_band_t_s { float *band_a; /* Low-pass V + low-pass H. */ float *band_v; /* Low-pass V + high-pass H. */ float *band_h; /* High-pass V + low-pass H. */ float *band_d; /* High-pass V + high-pass H. */ } adm_dwt_band_t_s; typedef struct adm_dwt_band_t_d { double *band_a; /* Low-pass V + low-pass H. */ double *band_v; /* Low-pass V + high-pass H. */ double *band_h; /* High-pass V + low-pass H. */ double *band_d; /* High-pass V + high-pass H. */ } adm_dwt_band_t_d; float adm_sum_cube_s(const float *x, int w, int h, int stride, double border_factor); void adm_decouple_s(const adm_dwt_band_t_s *ref, const adm_dwt_band_t_s *dis, const adm_dwt_band_t_s *r, const adm_dwt_band_t_s *a, int w, int h, int ref_stride, int dis_stride, int r_stride, int a_stride, double border_factor, double adm_enhn_gain_limit); void adm_csf_s(const adm_dwt_band_t_s *src, const adm_dwt_band_t_s *dst, const adm_dwt_band_t_s *flt, int orig_h, int scale, int w, int h, int src_stride, int dst_stride, double border_factor, double adm_norm_view_dist, int adm_ref_display_height, int adm_csf_mode); void adm_cm_thresh_s(const adm_dwt_band_t_s *src, float *dst, int w, int h, int src_stride, int dst_stride); float adm_csf_den_scale_s(const adm_dwt_band_t_s *src, int orig_h, int scale, int w, int h, int src_stride, double border_factor, double adm_norm_view_dist, int adm_ref_display_height, int adm_csf_mode); float adm_cm_s(const adm_dwt_band_t_s *src, const adm_dwt_band_t_s *dst, const adm_dwt_band_t_s *csf_a, int w, int h, int src_stride, int dst_stride, int csf_a_stride, double border_factor, int scale, double adm_norm_view_dist, int adm_ref_display_height, int adm_csf_mode); void dwt2_src_indices_filt_s(int **src_ind_y, int **src_ind_x, int w, int h); void adm_dwt2_s(const float *src, const adm_dwt_band_t_s *dst, int **ind_y, int **ind_x, int w, int h, int src_stride, int dst_stride); void adm_dwt2_d(const double *src, const adm_dwt_band_t_d *dst, int **ind_y, int **ind_x, int w, int h, int src_stride, int dst_stride); /* ================= */ /* Noise floor model */ /* ================= */ /* * The following dwt visibility threshold parameters are taken from * "Visibility of Wavelet Quantization Noise" * by A. B. Watson, G. Y. Yang, J. A. Solomon and J. Villasenor * IEEE Trans. on Image Processing, Vol. 6, No 8, Aug. 1997 * Page 1170, formula (7) and corresponding Table IV * Table IV has 2 entries for Cb and Cr thresholds * Chose those corresponding to subject "sfl" since they are lower * These thresholds were obtained and modeled for the 7-9 biorthogonal wavelet basis */ struct dwt_model_params { float a; float k; float f0; float g[4]; }; // 0 -> Y, 1 -> Cb, 2 -> Cr static const struct dwt_model_params dwt_7_9_YCbCr_threshold[3] = { { .a = 0.495, .k = 0.466, .f0 = 0.401, .g = { 1.501, 1.0, 0.534, 1.0} }, { .a = 1.633, .k = 0.353, .f0 = 0.209, .g = { 1.520, 1.0, 0.502, 1.0} }, { .a = 0.944, .k = 0.521, .f0 = 0.404, .g = { 1.868, 1.0, 0.516, 1.0} } }; /* * The following dwt basis function amplitudes, A(lambda,theta), are taken from * "Visibility of Wavelet Quantization Noise" * by A. B. Watson, G. Y. Yang, J. A. Solomon and J. Villasenor * IEEE Trans. on Image Processing, Vol. 6, No 8, Aug. 1997 * Page 1172, Table V * The table has been transposed, i.e. it can be used directly to obtain A[lambda][theta] * These amplitudes were calculated for the 7-9 biorthogonal wavelet basis */ static const float dwt_7_9_basis_function_amplitudes[6][4] = { { 0.62171, 0.67234, 0.72709, 0.67234 }, { 0.34537, 0.41317, 0.49428, 0.41317 }, { 0.18004, 0.22727, 0.28688, 0.22727 }, { 0.091401, 0.11792, 0.15214, 0.11792 }, { 0.045943, 0.059758, 0.077727, 0.059758 }, { 0.023013, 0.030018, 0.039156, 0.030018 } }; /* * lambda = 0 (finest scale), 1, 2, 3 (coarsest scale); * theta = 0 (ll), 1 (lh - vertical), 2 (hh - diagonal), 3(hl - horizontal). */ static FORCE_INLINE float dwt_quant_step(const struct dwt_model_params *params, int lambda, int theta, double adm_norm_view_dist, int adm_ref_display_height) { // Formula (1), page 1165 - display visual resolution (DVR), in pixels/degree of visual angle. This should be 56.55 float r = adm_norm_view_dist * adm_ref_display_height * M_PI / 180.0; // Formula (9), page 1171 float temp = log10(pow(2.0,lambda+1)*params->f0*params->g[theta]/r); float Q = 2.0*params->a*pow(10.0,params->k*temp*temp)/dwt_7_9_basis_function_amplitudes[lambda][theta]; return Q; } #endif /* ADM_TOOLS_H_ */