blob: 26d97b7a9559a706f6c6ae3a4506659fb49549ec [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.android.media.videoquality.bdrate;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
@RunWith(JUnitParamsRunner.class)
public class BdRateCalculatorTests {
@Test
@Parameters
public void cluster_correctlyClustersPoints(
RateDistortionCurve originalCurve, RateDistortionCurve expectedClusteredCurve)
throws Exception {
RateDistortionCurve clusteredCurve = BdRateCalculator.cluster(originalCurve);
assertThat(clusteredCurve).isEqualTo(expectedClusteredCurve);
}
public Object[] parametersForCluster_correctlyClustersPoints() {
return new Object[][] {
{
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(1269000, 85.99145178011112))
.addPoint(RateDistortionPoint.create(1271000, 85.940599017292612))
.addPoint(RateDistortionPoint.create(1280000, 86.239622578028658))
// The points above should be averaged together since they
// fall within the threshold. The points after should be
// left as is.
.addPoint(RateDistortionPoint.create(1980000, 91.798753645900973))
.addPoint(RateDistortionPoint.create(2963000, 96.336865669564261))
.addPoint(RateDistortionPoint.create(3953000, 98.916661924718113))
.addPoint(RateDistortionPoint.create(4944000, 100.56809641317301))
.build(),
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(1273333.3333333333, 86.05722445847748))
.addPoint(RateDistortionPoint.create(1980000, 91.798753645900973))
.addPoint(RateDistortionPoint.create(2963000, 96.336865669564261))
.addPoint(RateDistortionPoint.create(3953000, 98.916661924718113))
.addPoint(RateDistortionPoint.create(4944000, 100.56809641317301))
.build()
},
{
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(4219000, 60.287099359895606))
.addPoint(RateDistortionPoint.create(4232000, 60.387644277419142))
.addPoint(RateDistortionPoint.create(4240000, 60.44304017191704))
.addPoint(RateDistortionPoint.create(4242000, 60.421901710401038))
.addPoint(RateDistortionPoint.create(4282000, 61.012684829487483))
// The five points above this one should be averaged into a
// single point in the result.
.addPoint(RateDistortionPoint.create(4957000, 63.627313899593304))
.addPoint(RateDistortionPoint.create(5931000, 66.970500015421848))
.addPoint(RateDistortionPoint.create(7888000, 72.73460639108508))
.addPoint(RateDistortionPoint.create(8889000, 75.131165774588851))
.addPoint(RateDistortionPoint.create(9868000, 76.961177180326459))
.addPoint(RateDistortionPoint.create(11832000, 80.0394147595267))
.addPoint(RateDistortionPoint.create(13807000, 82.538133696703369))
.addPoint(RateDistortionPoint.create(17757000, 86.1460086879475))
.addPoint(RateDistortionPoint.create(21715000, 88.803835247098448))
.build(),
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(4243000.0, 60.51047406982407))
.addPoint(RateDistortionPoint.create(4957000, 63.627313899593304))
.addPoint(RateDistortionPoint.create(5931000, 66.970500015421848))
.addPoint(RateDistortionPoint.create(7888000, 72.73460639108508))
.addPoint(RateDistortionPoint.create(8889000, 75.131165774588851))
.addPoint(RateDistortionPoint.create(9868000, 76.961177180326459))
.addPoint(RateDistortionPoint.create(11832000, 80.0394147595267))
.addPoint(RateDistortionPoint.create(13807000, 82.538133696703369))
.addPoint(RateDistortionPoint.create(17757000, 86.1460086879475))
.addPoint(RateDistortionPoint.create(21715000, 88.803835247098448))
.build()
}
};
}
/* The following data is sourced from the VMAF library's implementation of
* a BD rate calculator, which is in turn sourced from JCTVC E137 data:
*
* http://phenix.it-sudparis.eu/jct/doc_end_user/documents/5_Geneva/wg11/JCTVC-E137-v1.zip
*
* There is a slight difference in values (hence the tolerance) due to the
* difference in interpolation methods (Netflix's own PCHIP implementation
* vs the Apache Commons Math's SplineInterpolator used here).
*/
@Test
public void calculate_dataFromJCTVCE137_1() {
RateDistortionCurve baselineCurve =
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(108048.8736, 43.6471))
.addPoint(RateDistortionPoint.create(61279.976, 40.3953))
.addPoint(RateDistortionPoint.create(33905.6656, 37.247))
.addPoint(RateDistortionPoint.create(18883.6928, 34.2911))
// This point is synthetically added (generated with PCHIP) since
// the Akima Interpolation used by this calculator requires 5 points.
.addPoint(RateDistortionPoint.create(78915.635, 41.8147))
.build();
RateDistortionCurve targetCurve =
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(108061.2784, 43.6768))
.addPoint(RateDistortionPoint.create(61299.9936, 40.4232))
.addPoint(RateDistortionPoint.create(33928.7472, 37.2761))
.addPoint(RateDistortionPoint.create(18910.912, 34.3147))
// This point is synthetically added (generated with PCHIP) since
// the Akima Interpolation used by this calculator requires 5 points.
.addPoint(RateDistortionPoint.create(78532.332, 41.8147))
.build();
BdRateCalculator bdRateCalculator = BdRateCalculator.create();
double bdRate = BdRateCalculator.create().calculate(baselineCurve, targetCurve);
assertThat(bdRate).isWithin(0.00005).of(-0.00465215420752807);
}
@Test
public void calculate_dataFromJCTVCE137_2() {
RateDistortionCurve baselineCurve =
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(40433.8848, 37.5761))
.addPoint(RateDistortionPoint.create(7622.7456, 35.3756))
.addPoint(RateDistortionPoint.create(2394.488, 33.8977))
.addPoint(RateDistortionPoint.create(1017.6184, 32.0603))
// This point is synthetically added (generated with PCHIP) since
// the Akima Interpolation used by this calculator requires 5 points.
.addPoint(RateDistortionPoint.create(19179.138, 36.5822))
.build();
RateDistortionCurve targetCurve =
RateDistortionCurve.builder()
.addPoint(RateDistortionPoint.create(40370.12, 37.5982))
.addPoint(RateDistortionPoint.create(7587.0024, 35.4025))
.addPoint(RateDistortionPoint.create(2390.0944, 33.9194))
.addPoint(RateDistortionPoint.create(1017.0984, 32.0822))
// This point is synthetically added (generated with PCHIP) since
// the Akima Interpolation used by this calculator requires 5 points.
.addPoint(RateDistortionPoint.create(18727.134, 36.5822))
.build();
double bdRate = BdRateCalculator.create().calculate(baselineCurve, targetCurve);
assertThat(bdRate).isWithin(0.00005).of(-0.018779823450567612);
}
}