libvmaf/src/feature/luminance_tools.c (69 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 <errno.h> #include <math.h> #include <string.h> #include "log.h" #include "luminance_tools.h" #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define BT1886_GAMMA (2.4) #define BT1886_LW (300.0) #define BT1886_LB (0.01) static inline int clip(int value, int low, int high) { return value < low ? low : (value > high ? high : value); } /* * Standard range for 8 bit: [16, 235] * Standard range for 10 bit: [64, 940] * Full range for 8 bit: [0, 255] * Full range for 10 bit: [0, 1023] */ static inline int range_foot_head(int bitdepth, enum VmafPixelRange pix_range, int *foot, int *head) { switch (pix_range) { case VMAF_PIXEL_RANGE_LIMITED: *foot = 16 * (1 << (bitdepth - 8)); *head = 235 * (1 << (bitdepth - 8)); break; case VMAF_PIXEL_RANGE_FULL: *foot = 0; *head = (1 << bitdepth) - 1; break; default: vmaf_log(VMAF_LOG_LEVEL_ERROR, "unknown pixel range received"); return -EINVAL; } return 0; } static inline double normalize_range(int sample, VmafLumaRange range) { int clipped_sample = clip(sample, range.foot, range.head); return (double)(clipped_sample - range.foot) / (range.head - range.foot); } int vmaf_luminance_init_luma_range(VmafLumaRange *luma_range, int bitdepth, enum VmafPixelRange pix_range) { int err = range_foot_head(bitdepth, pix_range, &(luma_range->foot), &(luma_range->head)); return err; } int vmaf_luminance_init_eotf(VmafEOTF *eotf, const char *eotf_str) { if (strcmp(eotf_str, "bt1886") == 0) { *eotf = vmaf_luminance_bt1886_eotf; } else if (strcmp(eotf_str, "pq") == 0) { *eotf = vmaf_luminance_pq_eotf; } else { vmaf_log(VMAF_LOG_LEVEL_ERROR, "unknown EOTF received"); return -EINVAL; } return 0; } double vmaf_luminance_bt1886_eotf(double V) { double a = pow(pow(BT1886_LW, 1.0 / BT1886_GAMMA) - pow(BT1886_LB, 1.0 / BT1886_GAMMA), BT1886_GAMMA); double b = pow(BT1886_LB, 1.0 / BT1886_GAMMA) / (pow(BT1886_LW, 1.0 / BT1886_GAMMA) - pow(BT1886_LB, 1.0 / BT1886_GAMMA)); return a * pow(MAX(V + b, 0), BT1886_GAMMA); } double vmaf_luminance_pq_eotf(double V) { const double m_1 = 0.1593017578125; const double m_2 = 78.84375; const double c_1 = 0.8359375; const double c_2 = 18.8515625; const double c_3 = 18.6875; // c_3 = c_1 + c_2 - 1 double num = pow(V, 1.0 / m_2) - c_1; double num_clipped = MAX(num, 0); double den = c_2 - c_3 * pow(V, 1.0 / m_2); return 10000 * pow(num_clipped / den, 1.0 / m_1); } double vmaf_luminance_get_luminance(int sample, VmafLumaRange luma_range, VmafEOTF eotf) { double normalized = normalize_range(sample, luma_range); return eotf(normalized); }