3
* Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
5
* This file is part of libde265.
7
* libde265 is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* libde265 is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with libde265. If not, see <http://www.gnu.org/licenses/>.
34
/* There are numerical stability problems in the matrix inverse.
35
Switching to long double seems to provide enough accuracy.
36
TODO: in the long term, use a better regression algorithm.
38
typedef long double FP;
47
struct BjoentegaardParams
49
// a*log^3 R + b*log^2 R + c*log R + d
52
double minRate, maxRate;
55
std::vector<datapoint> curveA,curveB;
56
BjoentegaardParams paramsA,paramsB;
58
#define RATE_NORMALIZATION_FACTOR 1 //(1/1000.0)
62
FP invf(int i,int j,const FP* m)
69
#define e(a,b) m[ ((j+b)%4)*4 + ((i+a)%4) ]
72
+ e(+1,-1)*e(+0,+0)*e(-1,+1)
73
+ e(+1,+1)*e(+0,-1)*e(-1,+0)
74
+ e(-1,-1)*e(+1,+0)*e(+0,+1)
75
- e(-1,-1)*e(+0,+0)*e(+1,+1)
76
- e(-1,+1)*e(+0,-1)*e(+1,+0)
77
- e(+1,-1)*e(-1,+0)*e(+0,+1);
79
return (o%2)?inv : -inv;
85
bool inverseMatrix4x4(const FP *m, FP *out)
91
inv[j*4+i] = invf(i,j,m);
95
for(int k=0;k<4;k++) D += m[k] * inv[k*4];
97
if (D == 0) return false;
101
for (int i = 0; i < 16; i++)
110
BjoentegaardParams fitParams(const std::vector<datapoint>& curve)
112
// build regression matrix
114
int n = curve.size();
116
FP X[4*n]; // regression matrix
117
FP XT[n*4]; // transpose of X
119
for (int i=0;i<n;i++) {
120
FP x = log(curve[i].rate) * RATE_NORMALIZATION_FACTOR;
127
if (D) printf("%f %f %f %f ;\n",1.0,(double)x,(double)(x*x),(double)(x*x*x));
137
for (int i=0;i<n;i++) {
138
printf("%f ; ",curve[i].rate);
142
printf("distortion: ");
143
for (int i=0;i<n;i++) {
144
printf("%f ; ",curve[i].distortion);
152
for (int y=0;y<4;y++)
153
for (int x=0;x<4;x++) {
156
for (int i=0;i<n;i++)
158
sum += XT[y*n + i] * X[x + i*4];
166
inverseMatrix4x4(XTX, XTXinv);
169
for (int y=0;y<4;y++) {
170
for (int x=0;x<4;x++) {
171
printf("%f ",(double)XTXinv[y*4+x]);
177
// calculate pseudo-inverse XP = (X^T * X)^-1 * X^T
181
for (int y=0;y<4;y++) {
182
for (int x=0;x<n;x++) {
185
for (int i=0;i<4;i++)
187
sum += XTXinv[y*4 + i] * XT[x + i*n];
194
// calculate regression parameters
198
for (int k=0;k<4;k++)
202
for (int i=0;i<n;i++) {
203
sum += XP[k*n + i] * curve[i].distortion;
210
BjoentegaardParams param;
217
// find min and max rate
219
param.minRate = curve[0].rate;
220
param.maxRate = curve[0].rate;
222
for (int i=1;i<n;i++) {
223
param.minRate = std::min(param.minRate, curve[i].rate);
224
param.maxRate = std::max(param.maxRate, curve[i].rate);
230
FP evalIntegralAt(const BjoentegaardParams& p, double x)
238
// integral of: c*log(x)
240
sum += p.c * x* (log(x)-1);
242
// integral of: b*log(x)^2
244
sum += p.b * x * ((log(x)-2)*log(x)+2);
246
// integral of: a*log(x)^3
248
sum += p.a * x * (log(x)*((log(x)-3)*log(x)+6)-6);
254
double calcBjoentegaard(const BjoentegaardParams& paramsA,
255
const BjoentegaardParams& paramsB,
256
double min_rate, double max_rate)
258
double mini = std::max(paramsA.minRate, paramsB.minRate);
259
double maxi = std::min(paramsA.maxRate, paramsB.maxRate);
261
if (min_rate >= 0) mini = std::max(mini, min_rate);
262
if (max_rate >= 0) maxi = std::min(maxi, max_rate);
264
if (D) printf("range: %f %f\n",mini,maxi);
266
FP intA = evalIntegralAt(paramsA, maxi) - evalIntegralAt(paramsA, mini);
267
FP intB = evalIntegralAt(paramsB, maxi) - evalIntegralAt(paramsB, mini);
269
if (D) printf("int1:%f int2:%f\n",(double)intA,(double)intB);
271
return (intA-intB)/(maxi-mini);
275
std::vector<datapoint> readRDFile(const char* filename, float min_rate, float max_rate)
277
std::vector<datapoint> curve;
278
std::ifstream istr(filename);
287
if (line[0]=='#') continue;
289
std::stringstream sstr(line);
291
sstr >> p.rate >> p.distortion;
293
if (min_rate>=0 && p.rate < min_rate) continue;
294
if (max_rate>=0 && p.rate > max_rate) continue;
303
int main(int argc, char** argv)
309
while ((c=getopt(argc,argv, "l:h:")) != -1) {
311
case 'l': min_rate = atof(optarg); break;
312
case 'h': max_rate = atof(optarg); break;
316
curveA = readRDFile(argv[optind], min_rate, max_rate);
317
paramsA = fitParams(curveA);
319
printf("params A: %f %f %f %f\n",paramsA.a,paramsA.b,paramsA.c,paramsA.d);
321
printf("gnuplot: %f*log(x)**3+%f*log(x)**2+%f*log(x)+%f\n",paramsA.a,paramsA.b,paramsA.c,paramsA.d);
324
curveB = readRDFile(argv[optind+1], min_rate, max_rate);
325
paramsB = fitParams(curveB);
327
printf("params B: %f %f %f %f\n",paramsB.a,paramsB.b,paramsB.c,paramsB.d);
329
printf("gnuplot: %f*log(x)**3+%f*log(x)**2+%f*log(x)+%f\n",paramsB.a,paramsB.b,paramsB.c,paramsB.d);
331
double delta = calcBjoentegaard(paramsA,paramsB, min_rate,max_rate);
333
printf("Bjoentegaard delta: %f dB (A-B -> >0 -> first (A) is better)\n",delta);
336
printf("-> first is better by %f dB\n",delta);
339
printf("-> second is better by %f dB\n",-delta);