Browse Source

CorrectDCValues

pull/1552/head
Brian Popow 6 years ago
parent
commit
dd7032c693
  1. 2
      src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
  2. 28
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
  3. 84
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  4. 3
      tests/ImageSharp.Tests/TestImages.cs
  5. 3
      tests/Images/Input/WebP/peak.png

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

@ -564,7 +564,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
// Writes the partition #0 modes (that is: all intra modes)
private void CodeIntraModes(Vp8BitWriter bitWriter)
{
var it = new Vp8EncIterator(this.enc.YTop, this.enc.UvTop, this.enc.Nz, this.enc.MbInfo, this.enc.Preds, this.enc.Mbw, this.enc.Mbh);
var it = new Vp8EncIterator(this.enc.YTop, this.enc.UvTop, this.enc.Nz, this.enc.MbInfo, this.enc.Preds, this.enc.TopDerr, this.enc.Mbw, this.enc.Mbh);
int predsWidth = this.enc.PredsWidth;
do

28
src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs

@ -97,19 +97,21 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private int uvTopIdx;
public Vp8EncIterator(byte[] yTop, byte[] uvTop, uint[] nz, Vp8MacroBlockInfo[] mb, byte[] preds, int mbw, int mbh)
public Vp8EncIterator(byte[] yTop, byte[] uvTop, uint[] nz, Vp8MacroBlockInfo[] mb, byte[] preds, sbyte[] topDerr, int mbw, int mbh)
{
this.YTop = yTop;
this.UvTop = uvTop;
this.Nz = nz;
this.Mb = mb;
this.Preds = preds;
this.TopDerr = topDerr;
this.LeftDerr = new sbyte[2 * 2];
this.mbw = mbw;
this.mbh = mbh;
this.Mb = mb;
this.currentMbIdx = 0;
this.nzIdx = 1;
this.yTopIdx = 0;
this.uvTopIdx = 0;
this.YTop = yTop;
this.UvTop = uvTop;
this.Nz = nz;
this.Preds = preds;
this.predsWidth = (4 * mbw) + 1;
this.predIdx = this.predsWidth;
this.YuvIn = new byte[WebPConstants.Bps * 16];
@ -180,6 +182,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// </summary>
public byte[] UvLeft { get; }
/// <summary>
/// Gets the left error diffusion (u/v).
/// </summary>
public sbyte[] LeftDerr { get; }
/// <summary>
/// Gets the top luma samples at position 'X'.
/// </summary>
@ -211,6 +218,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// </summary>
public uint[] Nz { get; }
/// <summary>
/// Gets the diffusion error.
/// </summary>
public sbyte[] TopDerr { get; }
/// <summary>
/// Gets 32+5 boundary samples needed by intra4x4.
/// </summary>
@ -1284,6 +1296,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
vLeft.Slice(1, 8).Fill(129);
this.LeftNz[8] = 0;
this.LeftDerr.AsSpan().Fill(0);
}
private void InitTop()
@ -1297,6 +1311,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int predsH = (4 * this.mbh) + 1;
int predsSize = predsW * predsH;
this.Preds.AsSpan(predsSize + this.predsWidth, this.mbw).Fill(0);
this.TopDerr.AsSpan().Fill(0);
}
private int Bit(uint nz, int n)

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

@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.WebP.BitWriter;
@ -142,6 +141,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
// TODO: filterStrength is hardcoded, should be configurable.
private const int FilterStrength = 60;
// Diffusion weights. We under-correct a bit (15/16th of the error is actually
// diffused) to avoid 'rainbow' chessboard pattern of blocks at q~=0.
private const int C1 = 7; // fraction of error sent to the 4x4 block below
private const int C2 = 8; // fraction of error sent to the 4x4 block on the right
private const int DSHIFT = 4;
private const int DSCALE = 1; // storage descaling, needed to make the error fit byte
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Encoder"/> class.
/// </summary>
@ -175,7 +181,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.UvTop = new byte[this.mbw * 16 * 2];
this.Nz = new uint[this.mbw + 1];
this.MbHeaderLimit = 256 * 510 * 8 * 1024 / (this.mbw * this.mbh);
int predSize = (((4 * this.mbw) + 1) * ((4 * this.mbh) + 1)) + this.predsWidth + 1;
this.TopDerr = new sbyte[this.mbw * 4];
// TODO: make partition_limit configurable?
int limit = 100; // original code: limit = 100 - config->partition_limit;
@ -196,6 +202,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
this.filterHeader = new Vp8FilterHeader();
int predSize = (((4 * this.mbw) + 1) * ((4 * this.mbh) + 1)) + this.predsWidth + 1;
this.proba = new Vp8EncProba();
this.Preds = new byte[predSize * 2]; // TODO: figure out how much mem we need here. This is too much.
this.predsWidth = (4 * this.mbw) + 1;
@ -340,6 +347,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// </summary>
public byte[] Preds { get; }
/// <summary>
/// Gets the diffusion error.
/// </summary>
public sbyte[] TopDerr { get; }
/// <summary>
/// Gets a rough limit for header bits per MB.
/// </summary>
@ -364,7 +376,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int yStride = width;
int uvStride = (yStride + 1) >> 1;
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.mbInfo, this.Preds, this.mbw, this.mbh);
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.mbInfo, this.Preds, this.TopDerr, this.mbw, this.mbh);
var alphas = new int[WebPConstants.MaxAlpha + 1];
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
@ -487,7 +499,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.proba.FinalizeTokenProbas();
}
this.proba.CalculateLevelCosts(); // finalize costs
this.proba.CalculateLevelCosts(); // Finalize costs.
}
private long OneStatPass(int width, int height, int yStride, int uvStride, Vp8RdLevel rdOpt, int nbMbs, PassStats stats)
@ -495,7 +507,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
Span<byte> y = this.Y.GetSpan();
Span<byte> u = this.U.GetSpan();
Span<byte> v = this.V.GetSpan();
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.mbInfo, this.Preds, this.Mbw, this.Mbh);
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.mbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh);
long size = 0;
long sizeP0 = 0;
long distortion = 0;
@ -1277,11 +1289,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
tmp.AsSpan((n + 1) * 16, 16));
}
/* TODO:
if (it->top_derr_ != NULL)
{
CorrectDCValues(it, &dqm->uv_, tmp, rd);
}*/
this.CorrectDCValues(it, dqm.Uv, tmp, rd);
for (n = 0; n < 8; n += 2)
{
@ -1340,6 +1348,62 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
}
private void CorrectDCValues(Vp8EncIterator it, Vp8Matrix mtx, short[] tmp, Vp8ModeScore rd)
{
#pragma warning disable SA1005 // Single line comments should begin with single space
// | top[0] | top[1]
// --------+--------+---------
// left[0] | tmp[0] tmp[1] <-> err0 err1
// left[1] | tmp[2] tmp[3] err2 err3
//
// Final errors {err1,err2,err3} are preserved and later restored
// as top[]/left[] on the next block.
#pragma warning restore SA1005 // Single line comments should begin with single space
for (int ch = 0; ch <= 1; ++ch)
{
Span<sbyte> top = it.TopDerr.AsSpan((it.X * 4) + ch, 2);
Span<sbyte> left = it.LeftDerr.AsSpan(ch, 2);
int err0, err1, err2, err3;
Span<short> c = tmp.AsSpan(ch * 4 * 16, 4 * 16);
c[0] += (short)(((C1 * top[0]) + (C2 * left[0])) >> (DSHIFT - DSCALE));
err0 = QuantizeSingle(c, mtx);
c[1 * 16] += (short)(((C1 * top[1]) + (C2 * err0)) >> (DSHIFT - DSCALE));
err1 = QuantizeSingle(c.Slice(1 * 16), mtx);
c[2 * 16] += (short)(((C1 * err0) + (C2 * left[1])) >> (DSHIFT - DSCALE));
err2 = QuantizeSingle(c.Slice(2 * 16), mtx);
c[3 * 16] += (short)(((C1 * err1) + (C2 * err2)) >> (DSHIFT - DSCALE));
err3 = QuantizeSingle(c.Slice(3 * 16), mtx);
// TODO: set errors in rd
// rd->derr[ch][0] = (int8_t)err1;
// rd->derr[ch][1] = (int8_t)err2;
// rd->derr[ch][2] = (int8_t)err3;
}
}
// Quantize as usual, but also compute and return the quantization error.
// Error is already divided by DSHIFT.
private static int QuantizeSingle(Span<short> v, Vp8Matrix mtx)
{
int v0 = v[0];
bool sign = v0 < 0;
if (sign)
{
v0 = -v0;
}
if (v0 > (int)mtx.ZThresh[0])
{
int qV = QuantDiv((uint)v0, mtx.IQ[0], mtx.Bias[0]) * mtx.Q[0];
int err = v0 - qV;
v[0] = (short)(sign ? -qV : qV);
return (sign ? -err : err) >> DSCALE;
}
v[0] = 0;
return (sign ? -v0 : v0) >> DSCALE;
}
private void FTransformWht(Span<short> input, Span<short> output)
{
var tmp = new int[16];

3
tests/ImageSharp.Tests/TestImages.cs

@ -497,6 +497,9 @@ namespace SixLabors.ImageSharp.Tests
public static class WebP
{
// Reference image as png
public const string Peak = "WebP/Peak.png";
public static class Animated
{
public const string Animated1 = "WebP/animated-webp.webp";

3
tests/Images/Input/WebP/peak.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b9b56ed5c1278664222c77f9a452b824b4f9215c819502b3f6b0e0d44270e7e7
size 26456
Loading…
Cancel
Save