Browse Source

CalculateLevelCosts

pull/1552/head
Brian Popow 6 years ago
parent
commit
6eee467c62
  1. 8
      src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
  2. 18
      src/ImageSharp/Formats/WebP/Lossy/Vp8CostArray.cs
  3. 169
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncProba.cs
  4. 45
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  5. 18
      src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs
  6. 51
      src/ImageSharp/Formats/WebP/WebPLookupTables.cs

8
src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs

@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
public int PutCoeffs(int ctx, Vp8Residual residual)
{
int n = residual.First;
Vp8ProbaArray p = residual.Prob[n][ctx];
Vp8ProbaArray p = residual.Prob[n].Probabilities[ctx];
if (!this.PutBit(residual.Last >= 0, p.Probabilities[0]))
{
return 0;
@ -68,13 +68,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
int v = sign ? -c : c;
if (!this.PutBit(v != 0, p.Probabilities[1]))
{
p = residual.Prob[WebPConstants.Bands[n]][0];
p = residual.Prob[WebPConstants.Bands[n]].Probabilities[0];
continue;
}
if (!this.PutBit(v > 1, p.Probabilities[2]))
{
p = residual.Prob[WebPConstants.Bands[n]][1];
p = residual.Prob[WebPConstants.Bands[n]].Probabilities[1];
}
else
{
@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
}
}
p = residual.Prob[WebPConstants.Bands[n]][2];
p = residual.Prob[WebPConstants.Bands[n]].Probabilities[2];
}
this.PutBitUniform(sign ? 1 : 0);

18
src/ImageSharp/Formats/WebP/Lossy/Vp8CostArray.cs

@ -0,0 +1,18 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
internal class Vp8CostArray
{
/// <summary>
/// Initializes a new instance of the <see cref="Vp8CostArray"/> class.
/// </summary>
public Vp8CostArray()
{
this.Costs = new ushort[WebPConstants.NumCtx * (67 + 1)];
}
public ushort[] Costs { get; }
}
}

169
src/ImageSharp/Formats/WebP/Lossy/Vp8EncProba.cs

@ -0,0 +1,169 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
internal class Vp8EncProba
{
/// <summary>
/// Last (inclusive) level with variable cost.
/// </summary>
private const int MaxVariableLevel = 67;
/// <summary>
/// Initializes a new instance of the <see cref="Vp8EncProba"/> class.
/// </summary>
public Vp8EncProba()
{
this.Dirty = true;
this.UseSkipProba = false;
this.Segments = new byte[3];
this.Coeffs = new Vp8BandProbas[WebPConstants.NumTypes][];
for (int i = 0; i < this.Coeffs.Length; i++)
{
this.Coeffs[i] = new Vp8BandProbas[WebPConstants.NumBands];
for (int j = 0; j < this.Coeffs[i].Length; j++)
{
this.Coeffs[i][j] = new Vp8BandProbas();
}
}
this.LevelCost = new Vp8CostArray[WebPConstants.NumTypes][];
for (int i = 0; i < this.LevelCost.Length; i++)
{
this.LevelCost[i] = new Vp8CostArray[WebPConstants.NumBands];
for (int j = 0; j < this.LevelCost[i].Length; j++)
{
this.LevelCost[i][j] = new Vp8CostArray();
}
}
this.RemappedCosts = new Vp8CostArray[WebPConstants.NumTypes][];
for (int i = 0; i < this.RemappedCosts.Length; i++)
{
this.RemappedCosts[i] = new Vp8CostArray[16];
for (int j = 0; j < this.RemappedCosts[i].Length; j++)
{
this.RemappedCosts[i][j] = new Vp8CostArray();
}
}
// Initialize with default probabilities.
this.Segments.AsSpan().Fill(255);
for (int t = 0; t < WebPConstants.NumTypes; ++t)
{
for (int b = 0; b < WebPConstants.NumBands; ++b)
{
for (int c = 0; c < WebPConstants.NumCtx; ++c)
{
Vp8ProbaArray dst = this.Coeffs[t][b].Probabilities[c];
for (int p = 0; p < WebPConstants.NumProbas; ++p)
{
dst.Probabilities[p] = WebPLookupTables.DefaultCoeffsProba[t, b, c, p];
}
}
}
}
}
/// <summary>
/// Gets the probabilities for segment tree.
/// </summary>
public byte[] Segments { get; }
/// <summary>
/// Gets the final probability of being skipped.
/// </summary>
public byte SkipProba { get; }
/// <summary>
/// Gets a value indicating whether to use the skip probability. Note: we always use SkipProba for now.
/// </summary>
public bool UseSkipProba { get; }
public Vp8BandProbas[][] Coeffs { get; }
public Vp8CostArray[][] LevelCost { get; }
public Vp8CostArray[][] RemappedCosts { get; }
/// <summary>
/// Gets or sets the number of skipped blocks.
/// </summary>
public int NbSkip { get; set; }
/// <summary>
/// Gets or sets a value indicating whether CalculateLevelCosts() needs to be called.
/// </summary>
public bool Dirty { get; set; }
public void CalculateLevelCosts()
{
if (!this.Dirty)
{
return; // nothing to do.
}
for (int ctype = 0; ctype < WebPConstants.NumTypes; ++ctype)
{
for (int band = 0; band < WebPConstants.NumBands; ++band)
{
for (int ctx = 0; ctx < WebPConstants.NumCtx; ++ctx)
{
Vp8ProbaArray p = this.Coeffs[ctype][band].Probabilities[ctx];
Span<ushort> table = this.LevelCost[ctype][band].Costs.AsSpan(ctx * MaxVariableLevel);
int cost0 = (ctx > 0) ? this.BitCost(1, p.Probabilities[0]) : 0;
int costBase = this.BitCost(1, p.Probabilities[1]) + cost0;
int v;
table[0] = (ushort)(this.BitCost(0, p.Probabilities[1]) + cost0);
for (v = 1; v <= MaxVariableLevel; ++v)
{
table[v] = (ushort)(costBase + this.VariableLevelCost(v, p.Probabilities));
}
// Starting at level 67 and up, the variable part of the cost is actually constant.
}
}
for (int n = 0; n < 16; ++n)
{
for (int ctx = 0; ctx < WebPConstants.NumCtx; ++ctx)
{
Span<ushort> dst = this.RemappedCosts[ctype][n].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel);
Span<ushort> src = this.LevelCost[ctype][WebPConstants.Bands[n]].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel);
src.CopyTo(dst);
}
}
}
this.Dirty = false;
}
private int VariableLevelCost(int level, Span<byte> probas)
{
int pattern = WebPLookupTables.Vp8LevelCodes[level - 1][0];
int bits = WebPLookupTables.Vp8LevelCodes[level - 1][1];
int cost = 0;
for (int i = 2; pattern != 0; ++i)
{
if ((pattern & 1) != 0)
{
cost += this.BitCost(bits & 1, probas[i]);
}
bits >>= 1;
pattern >>= 1;
}
return cost;
}
// Cost of coding one event with probability 'proba'.
private int BitCost(int bit, byte proba)
{
return bit == 0 ? WebPLookupTables.Vp8EntropyCost[proba] : WebPLookupTables.Vp8EntropyCost[255 - proba];
}
}
}

45
src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs

@ -62,6 +62,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// </summary>
private Vp8MacroBlockInfo[] mbInfo;
/// <summary>
/// Probabilities.
/// </summary>
private Vp8EncProba proba;
private int dqUvDc;
private int dqUvAc;
@ -130,6 +135,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.mbInfo[i] = new Vp8MacroBlockInfo();
}
this.proba = new Vp8EncProba();
// this.Preds = this.memoryAllocator.Allocate<byte>(predSize);
this.Preds = this.memoryAllocator.Allocate<byte>(predSize * 2); // TODO: figure out how much mem we need here. This is too much.
this.predsWidth = (4 * this.mbw) + 1;
@ -223,6 +230,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.AssignSegments(segmentInfos, alphas);
this.SetSegmentParams(segmentInfos);
this.SetSegmentProbas(segmentInfos);
this.ResetStats();
it.Init();
it.InitFilter();
do
@ -288,10 +296,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
var distAccum = new int[NumMbSegments];
// Bracket the input.
for (n = 0; n <= WebPConstants.MaxAlpha && alphas[n] == 0; ++n) { }
for (n = 0; n <= WebPConstants.MaxAlpha && alphas[n] == 0; ++n)
{
}
minA = n;
for (n = WebPConstants.MaxAlpha; n > minA && alphas[n] == 0; --n) { }
for (n = WebPConstants.MaxAlpha; n > minA && alphas[n] == 0; --n)
{
}
maxA = n;
rangeA = maxA - minA;
@ -407,8 +419,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int nb = this.segmentHeader.NumSegments;
int snsStrength = 50; // TODO: Spatial Noise Shaping, hardcoded for now.
double amp = WebPConstants.SnsToDq * snsStrength / 100.0d / 128.0d;
double Q = this.quality / 100.0d;
double cBase = this.QualityToCompression(Q);
double cBase = this.QualityToCompression(this.quality / 100.0d);
for (int i = 0; i < nb; ++i)
{
// We modulate the base coefficient to accommodate for the quantization
@ -430,6 +441,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
// and make it safe.
this.dqUvAc = this.Clip(this.dqUvAc, WebPConstants.QuantEncMinDqUv, WebPConstants.QuantEncMaxDqUv);
// We also boost the dc-uv-quant a little, based on sns-strength, since
// U/V channels are quite more reactive to high quants (flat DC-blocks
// tend to appear, and are unpleasant).
this.dqUvDc = -4 * snsStrength / 100;
this.dqUvDc = this.Clip(this.dqUvDc, -15, 15); // 4bit-signed max allowed
this.SetupMatrices(dqm);
}
@ -441,6 +458,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
// TODO: SetSegmentProbas
}
private void ResetStats()
{
Vp8EncProba proba = this.proba;
proba.CalculateLevelCosts();
proba.NbSkip = 0;
}
private void SetupMatrices(Vp8SegmentInfo[] dqm)
{
for (int i = 0; i < dqm.Length; ++i)
@ -687,15 +711,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
var pos1 = this.bitWriter.Pos;
if (i16)
{
residual.Init(0, 1);
residual.Init(0, 1, this.proba);
residual.SetCoeffs(rd.YDcLevels);
int res = this.bitWriter.PutCoeffs(it.TopNz[8] + it.LeftNz[8], residual);
it.TopNz[8] = it.LeftNz[8] = res;
residual.Init(1, 0);
residual.Init(1, 0, this.proba);
}
else
{
residual.Init(0, 3);
residual.Init(0, 3, this.proba);
}
// luma-AC
@ -713,7 +737,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
var pos2 = this.bitWriter.Pos;
// U/V
residual.Init(0, 2);
residual.Init(0, 2, this.proba);
for (ch = 0; ch <= 2; ch += 2)
{
for (y = 0; y < 2; ++y)
@ -789,11 +813,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc);
int nz = 0;
int n;
var tmp = new short[8* 16];
var tmp = new short[8 * 16];
for (n = 0; n < 8; n += 2)
{
this.FTransform2(src.Slice(WebPLookupTables.Vp8ScanUv[n]),
this.FTransform2(
src.Slice(WebPLookupTables.Vp8ScanUv[n]),
reference.Slice(WebPLookupTables.Vp8ScanUv[n]),
tmp.AsSpan(n * 16, 16),
tmp.AsSpan((n + 1) * 16, 16));

18
src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs

@ -10,18 +10,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// </summary>
internal class Vp8Residual
{
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Residual"/> class.
/// </summary>
public Vp8Residual()
{
this.Prob = new Vp8ProbaArray[3][];
for (int i = 0; i < 3; i++)
{
this.Prob[i] = new Vp8ProbaArray[11];
}
}
public int First { get; set; }
public int Last { get; set; }
@ -30,15 +18,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
public short[] Coeffs { get; set; }
public Vp8ProbaArray[][] Prob { get; }
public Vp8BandProbas[] Prob { get; set; }
public void Init(int first, int coeffType)
public void Init(int first, int coeffType, Vp8EncProba prob)
{
this.First = first;
this.CoeffType = coeffType;
this.Prob = prob.Coeffs[this.CoeffType];
// TODO:
// res->prob = enc->proba_.coeffs_[coeff_type];
// res->stats = enc->proba_.stats_[coeff_type];
// res->costs = enc->proba_.remapped_costs_[coeff_type];
}

51
src/ImageSharp/Formats/WebP/WebPLookupTables.cs

@ -81,6 +81,57 @@ namespace SixLabors.ImageSharp.Formats.WebP
241, 243, 245, 247, 249, 251, 253, 127
};
public static readonly ushort[] Vp8EntropyCost =
{
1792, 1792, 1792, 1536, 1536, 1408, 1366, 1280, 1280, 1216,
1178, 1152, 1110, 1076, 1061, 1024, 1024, 992, 968, 951,
939, 911, 896, 878, 871, 854, 838, 820, 811, 794,
786, 768, 768, 752, 740, 732, 720, 709, 704, 690,
683, 672, 666, 655, 647, 640, 631, 622, 615, 607,
598, 592, 586, 576, 572, 564, 559, 555, 547, 541,
534, 528, 522, 512, 512, 504, 500, 494, 488, 483,
477, 473, 467, 461, 458, 452, 448, 443, 438, 434,
427, 424, 419, 415, 410, 406, 403, 399, 394, 390,
384, 384, 377, 374, 370, 366, 362, 359, 355, 351,
347, 342, 342, 336, 333, 330, 326, 323, 320, 316,
312, 308, 305, 302, 299, 296, 293, 288, 287, 283,
280, 277, 274, 272, 268, 266, 262, 256, 256, 256,
251, 248, 245, 242, 240, 237, 234, 232, 228, 226,
223, 221, 218, 216, 214, 211, 208, 205, 203, 201,
198, 196, 192, 191, 188, 187, 183, 181, 179, 176,
175, 171, 171, 168, 165, 163, 160, 159, 156, 154,
152, 150, 148, 146, 144, 142, 139, 138, 135, 133,
131, 128, 128, 125, 123, 121, 119, 117, 115, 113,
111, 110, 107, 105, 103, 102, 100, 98, 96, 94,
92, 91, 89, 86, 86, 83, 82, 80, 77, 76,
74, 73, 71, 69, 67, 66, 64, 63, 61, 59,
57, 55, 54, 52, 51, 49, 47, 46, 44, 43,
41, 40, 38, 36, 35, 33, 32, 30, 29, 27,
25, 24, 22, 21, 19, 18, 16, 15, 13, 12,
10, 9, 7, 6, 4, 3
};
public static readonly ushort[][] Vp8LevelCodes =
{
new ushort[] { 0x001, 0x000 }, new ushort[] { 0x007, 0x001 }, new ushort[] { 0x00f, 0x005 },
new ushort[] { 0x00f, 0x00d }, new ushort[] { 0x033, 0x003 }, new ushort[] { 0x033, 0x003 }, new ushort[] { 0x033, 0x023 },
new ushort[] { 0x033, 0x023 }, new ushort[] { 0x033, 0x023 }, new ushort[] { 0x033, 0x023 }, new ushort[] { 0x0d3, 0x013 },
new ushort[] { 0x0d3, 0x013 }, new ushort[] { 0x0d3, 0x013 }, new ushort[] { 0x0d3, 0x013 }, new ushort[] { 0x0d3, 0x013 },
new ushort[] { 0x0d3, 0x013 }, new ushort[] { 0x0d3, 0x013 }, new ushort[] { 0x0d3, 0x013 }, new ushort[] { 0x0d3, 0x093 },
new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 },
new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 },
new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 },
new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x0d3, 0x093 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 },
new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x053 }, new ushort[] { 0x153, 0x153 },
};
/// <summary>
/// Lookup table for small values of log2(int).
/// </summary>

Loading…
Cancel
Save