private void SetSegmentProbas() { int[] p = new int[NumMbSegments]; int n; for (n = 0; n < this.Mbw * this.Mbh; ++n) { Vp8MacroBlockInfo mb = this.MbInfo[n]; ++p[mb.Segment]; } if (this.SegmentHeader.NumSegments > 1) { byte[] probas = this.Proba.Segments; probas[0] = (byte)GetProba(p[0] + p[1], p[2] + p[3]); probas[1] = (byte)GetProba(p[0], p[1]); probas[2] = (byte)GetProba(p[2], p[3]); this.SegmentHeader.UpdateMap = probas[0] != 255 || probas[1] != 255 || probas[2] != 255; if (!this.SegmentHeader.UpdateMap) { this.ResetSegments(); } this.SegmentHeader.Size = (p[0] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(0, probas[1]))) + (p[1] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(1, probas[1]))) + (p[2] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(0, probas[2]))) + (p[3] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(1, probas[2]))); } else { this.SegmentHeader.UpdateMap = false; this.SegmentHeader.Size = 0; } }
public int FastMbAnalyze(int quality) { // Empirical cut-off value, should be around 16 (~=block size). We use the // [8-17] range and favor intra4 at high quality, intra16 for low quality. int q = quality; int kThreshold = 8 + ((17 - 8) * q / 100); int k; Span <uint> dc = stackalloc uint[16]; uint m; uint m2; for (k = 0; k < 16; k += 4) { LossyUtils.Mean16x4(this.YuvIn.AsSpan(YOffEnc + (k * WebpConstants.Bps)), dc.Slice(k, 4)); } for (m = 0, m2 = 0, k = 0; k < 16; k++) { m += dc[k]; m2 += dc[k] * dc[k]; } if (kThreshold * m2 < m * m) { this.SetIntra16Mode(0); // DC16 } else { byte[] modes = new byte[16]; // DC4 this.SetIntra4Mode(modes); } return(0); }
private static void RunHadamardTransformTest() { // arrange byte[] a = { 27, 27, 28, 29, 29, 28, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 26, 26, 26, 26, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 28, 27, 27, 26, 26, 27, 27, 28, 27, 28, 28, 29, 29, 28, 28, 27 }; byte[] b = { 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 }; ushort[] w = { 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 }; int expected = 2; // act int actual = LossyUtils.Vp8Disto4X4(a, b, w, new int[16]); // assert Assert.Equal(expected, actual); }
private static void RunTransformOneTest() { // arrange short[] src = { -176, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; byte[] dst = { 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129 }; byte[] expected = { 111, 111, 111, 111, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 108, 108, 108, 108, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 104, 104, 104, 104, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 101, 101, 101, 101, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129 }; int[] scratch = new int[16]; // act LossyUtils.TransformOne(src, dst, scratch); // assert Assert.True(expected.SequenceEqual(dst)); }
private static void RunVp8Sse4X4Test() { // arrange byte[] a = { 27, 27, 28, 29, 29, 28, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 26, 26, 26, 26, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 28, 27, 27, 26, 26, 27, 27, 28, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128 }; byte[] b = { 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204 }; int expected = 27; // act int actual = LossyUtils.Vp8_Sse4X4(a, b); // assert Assert.Equal(expected, actual); }
private static void RunTransformTwoTest() { // arrange short[] src = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 23, 0, 0, -23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; byte[] dst = { 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 0, 0, 0, 0 }; byte[] expected = { 105, 105, 105, 105, 105, 103, 100, 98, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 105, 105, 105, 105, 108, 105, 102, 100, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 105, 105, 105, 105, 111, 109, 106, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 105, 105, 105, 105, 113, 111, 108, 106, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 0, 0, 0, 0 }; int[] scratch = new int[16]; // act LossyUtils.TransformTwo(src, dst, scratch); // assert Assert.True(expected.SequenceEqual(dst)); }
private static void RunVp8Sse16X8Test() { // arrange byte[] a = { 107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168, 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107, 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, 154, 154, 151, 132, 92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, 171, 178, 172, 176, 152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, 102, 100, 107, 100, 101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, 160, 162, 161, 153, 150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, 171, 179, 178, 172, 171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, 86, 86, 102, 105, 102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, 154, 152, 158, 163, 150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105 }; byte[] b = { 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, 146, 149, 152, 154, 161, 164, 151, 130, 93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127, 93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, 146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 148, 148, 148, 148, 149, 158, 162, 159, 155, 155, 153, 129, 94, 87, 101, 106, 102, 100, 100, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, 151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106, 102, 105, 105, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, 154, 154, 154, 154, 156, 161, 159, 152, 155, 155, 153, 129, 94, 87, 101, 106, 102, 112, 112, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129, 94, 87, 101, 106, 102, 117, 117, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, 152, 153, 157, 162, 150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104 }; int expected = 749; // act int actual = LossyUtils.Vp8_Sse16X8(a, b); // assert Assert.Equal(expected, actual); }
public int FinalizeSkipProba(int mbw, int mbh) { int nbMbs = mbw * mbh; int nbEvents = this.NbSkip; this.SkipProba = (byte)CalcSkipProba(nbEvents, nbMbs); this.UseSkipProba = this.SkipProba < SkipProbaThreshold; int size = 256; if (this.UseSkipProba) { size += (nbEvents * LossyUtils.Vp8BitCost(1, this.SkipProba)) + ((nbMbs - nbEvents) * LossyUtils.Vp8BitCost(0, this.SkipProba)); size += 8 * 256; // cost of signaling the skipProba itself. } return(size); }
public static int ReconstructIntra16(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span <byte> yuvOut, int mode) { Span <byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]); Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc); int nz = 0; int n; Span <short> shortScratchSpan = it.Scratch2.AsSpan(); Span <int> scratch = it.Scratch3.AsSpan(0, 16); shortScratchSpan.Clear(); scratch.Clear(); Span <short> dcTmp = shortScratchSpan.Slice(0, 16); Span <short> tmp = shortScratchSpan.Slice(16, 16 * 16); for (n = 0; n < 16; n += 2) { Vp8Encoding.FTransform2( src.Slice(WebpLookupTables.Vp8Scan[n]), reference.Slice(WebpLookupTables.Vp8Scan[n]), tmp.Slice(n * 16, 16), tmp.Slice((n + 1) * 16, 16), scratch); } Vp8Encoding.FTransformWht(tmp, dcTmp, scratch); nz |= QuantizeBlock(dcTmp, rd.YDcLevels, ref dqm.Y2) << 24; for (n = 0; n < 16; n += 2) { // Zero-out the first coeff, so that: a) nz is correct below, and // b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified. tmp[n * 16] = tmp[(n + 1) * 16] = 0; nz |= Quantize2Blocks(tmp.Slice(n * 16, 32), rd.YAcLevels.AsSpan(n * 16, 32), ref dqm.Y1) << n; } // Transform back. LossyUtils.TransformWht(dcTmp, tmp, scratch); for (n = 0; n < 16; n += 2) { Vp8Encoding.ITransformTwo(reference.Slice(WebpLookupTables.Vp8Scan[n]), tmp.Slice(n * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8Scan[n]), scratch); } return(nz); }
private static 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 += LossyUtils.Vp8BitCost(bits & 1, probas[i]); } bits >>= 1; pattern >>= 1; } return(cost); }
public int FinalizeTokenProbas() { bool hasChanged = false; int size = 0; for (int t = 0; t < WebpConstants.NumTypes; ++t) { for (int b = 0; b < WebpConstants.NumBands; ++b) { for (int c = 0; c < WebpConstants.NumCtx; ++c) { for (int p = 0; p < WebpConstants.NumProbas; ++p) { uint stats = this.Stats[t][b].Stats[c].Stats[p]; int nb = (int)((stats >> 0) & 0xffff); int total = (int)((stats >> 16) & 0xffff); int updateProba = WebpLookupTables.CoeffsUpdateProba[t, b, c, p]; int oldP = WebpLookupTables.DefaultCoeffsProba[t, b, c, p]; int newP = CalcTokenProba(nb, total); int oldCost = BranchCost(nb, total, oldP) + LossyUtils.Vp8BitCost(0, (byte)updateProba); int newCost = BranchCost(nb, total, newP) + LossyUtils.Vp8BitCost(1, (byte)updateProba) + (8 * 256); bool useNewP = oldCost > newCost; size += LossyUtils.Vp8BitCost(useNewP ? 1 : 0, (byte)updateProba); if (useNewP) { // Only use proba that seem meaningful enough. this.Coeffs[t][b].Probabilities[c].Probabilities[p] = (byte)newP; hasChanged |= newP != oldP; size += 8 * 256; } else { this.Coeffs[t][b].Probabilities[c].Probabilities[p] = (byte)oldP; } } } } } this.Dirty = hasChanged; return(size); }
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]; Vp8CostArray table = this.LevelCost[ctype][band].Costs[ctx]; int cost0 = ctx > 0 ? LossyUtils.Vp8BitCost(1, p.Probabilities[0]) : 0; int costBase = LossyUtils.Vp8BitCost(1, p.Probabilities[1]) + cost0; int v; table.Costs[0] = (ushort)(LossyUtils.Vp8BitCost(0, p.Probabilities[1]) + cost0); for (v = 1; v <= MaxVariableLevel; ++v) { table.Costs[v] = (ushort)(costBase + 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) { Vp8CostArray dst = this.RemappedCosts[ctype][n].Costs[ctx]; Vp8CostArray src = this.LevelCost[ctype][WebpConstants.Vp8EncBands[n]].Costs[ctx]; src.Costs.CopyTo(dst.Costs.AsSpan()); } } } this.Dirty = false; }
private static void RunMean16x4Test() { // arrange byte[] input = { 154, 145, 102, 115, 127, 129, 126, 125, 126, 120, 133, 152, 157, 153, 119, 94, 104, 116, 111, 113, 113, 109, 105, 124, 173, 175, 177, 170, 175, 172, 166, 164, 151, 141, 99, 114, 125, 126, 135, 150, 133, 115, 127, 149, 141, 168, 100, 54, 110, 117, 115, 116, 119, 115, 117, 130, 174, 174, 174, 157, 146, 171, 166, 158, 117, 140, 96, 111, 119, 119, 136, 171, 188, 134, 121, 126, 136, 119, 59, 77, 109, 115, 113, 120, 120, 117, 128, 115, 174, 173, 173, 161, 152, 148, 153, 162, 105, 140, 96, 114, 115, 122, 141, 173, 190, 190, 142, 106, 151, 78, 66, 141, 110, 117, 123, 136, 118, 124, 127, 114, 173, 175, 166, 155, 155, 159, 159, 158 }; uint[] dc = new uint[4]; uint[] expectedDc = { 1940, 2139, 2252, 1813 }; // act LossyUtils.Mean16x4(input, dc); // assert Assert.True(dc.SequenceEqual(expectedDc)); }
public static bool PickBestIntra4(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba, int maxI4HeaderBits) { Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment]; int lambda = dqm.LambdaI4; int tlambda = dqm.TLambda; Span <byte> src0 = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc); Span <byte> bestBlocks = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc); Span <int> scratch = it.Scratch3; int totalHeaderBits = 0; var rdBest = new Vp8ModeScore(); if (maxI4HeaderBits == 0) { return(false); } rdBest.InitScore(); rdBest.H = 211; // '211' is the value of VP8BitCost(0, 145) rdBest.SetRdScore(dqm.LambdaMode); it.StartI4(); var rdi4 = new Vp8ModeScore(); var rdTmp = new Vp8ModeScore(); var res = new Vp8Residual(); Span <short> tmpLevels = new short[16]; do { int numBlocks = 1; rdi4.Clear(); int mode; int bestMode = -1; Span <byte> src = src0.Slice(WebpLookupTables.Vp8Scan[it.I4]); short[] modeCosts = it.GetCostModeI4(rd.ModesI4); Span <byte> bestBlock = bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]); Span <byte> tmpDst = it.Scratch.AsSpan(); tmpDst.Clear(); rdi4.InitScore(); it.MakeIntra4Preds(); for (mode = 0; mode < WebpConstants.NumBModes; ++mode) { rdTmp.Clear(); tmpLevels.Clear(); // Reconstruct. rdTmp.Nz = (uint)ReconstructIntra4(it, dqm, tmpLevels, src, tmpDst, mode); // Compute RD-score. rdTmp.D = LossyUtils.Vp8_Sse4X4(src, tmpDst); rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto4X4(src, tmpDst, WeightY, scratch)) : 0; rdTmp.H = modeCosts[mode]; // Add flatness penalty, to avoid flat area to be mispredicted by a complex mode. if (mode > 0 && IsFlat(tmpLevels, numBlocks, WebpConstants.FlatnessLimitI4)) { rdTmp.R = WebpConstants.FlatnessPenality * numBlocks; } else { rdTmp.R = 0; } // Early-out check. rdTmp.SetRdScore(lambda); if (bestMode >= 0 && rdTmp.Score >= rdi4.Score) { continue; } // Finish computing score. rdTmp.R += it.GetCostLuma4(tmpLevels, proba, res); rdTmp.SetRdScore(lambda); if (bestMode < 0 || rdTmp.Score < rdi4.Score) { rdi4.CopyScore(rdTmp); bestMode = mode; Span <byte> tmp = tmpDst; tmpDst = bestBlock; bestBlock = tmp; tmpLevels.CopyTo(rdBest.YAcLevels.AsSpan(it.I4 * 16, 16)); } } rdi4.SetRdScore(dqm.LambdaMode); rdBest.AddScore(rdi4); if (rdBest.Score >= rd.Score) { return(false); } totalHeaderBits += (int)rdi4.H; // <- equal to modeCosts[bestMode]; if (totalHeaderBits > maxI4HeaderBits) { return(false); } // Copy selected samples to the right place. LossyUtils.Vp8Copy4X4(bestBlock, bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4])); rd.ModesI4[it.I4] = (byte)bestMode; it.TopNz[it.I4 & 3] = it.LeftNz[it.I4 >> 2] = rdi4.Nz != 0 ? 1 : 0; }while (it.RotateI4(bestBlocks)); // Finalize state. rd.CopyScore(rdBest); it.SetIntra4Mode(rd.ModesI4); it.SwapOut(); rdBest.YAcLevels.AsSpan().CopyTo(rd.YAcLevels); // Select intra4x4 over intra16x16. return(true); }
private const int DSCALE = 1; // storage descaling, needed to make the error fit byte public static void PickBestIntra16(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba) { const int numBlocks = 16; Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment]; int lambda = dqm.LambdaI16; int tlambda = dqm.TLambda; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc); Span <int> scratch = it.Scratch3; var rdTmp = new Vp8ModeScore(); var res = new Vp8Residual(); Vp8ModeScore rdCur = rdTmp; Vp8ModeScore rdBest = rd; int mode; bool isFlat = IsFlatSource16(src); rd.ModeI16 = -1; for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { // Scratch buffer. Span <byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc); rdCur.ModeI16 = mode; // Reconstruct. rdCur.Nz = (uint)ReconstructIntra16(it, dqm, rdCur, tmpDst, mode); // Measure RD-score. rdCur.D = LossyUtils.Vp8_Sse16X16(src, tmpDst); rdCur.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto16X16(src, tmpDst, WeightY, scratch)) : 0; rdCur.H = WebpConstants.Vp8FixedCostsI16[mode]; rdCur.R = it.GetCostLuma16(rdCur, proba, res); if (isFlat) { // Refine the first impression (which was in pixel space). isFlat = IsFlat(rdCur.YAcLevels, numBlocks, WebpConstants.FlatnessLimitI16); if (isFlat) { // Block is very flat. We put emphasis on the distortion being very low! rdCur.D *= 2; rdCur.SD *= 2; } } // Since we always examine Intra16 first, we can overwrite *rd directly. rdCur.SetRdScore(lambda); if (mode == 0 || rdCur.Score < rdBest.Score) { Vp8ModeScore tmp = rdCur; rdCur = rdBest; rdBest = tmp; it.SwapOut(); } } if (rdBest != rd) { rd = rdBest; } // Finalize score for mode decision. rd.SetRdScore(dqm.LambdaMode); it.SetIntra16Mode(rd.ModeI16); // We have a blocky macroblock (only DCs are non-zero) with fairly high // distortion, record max delta so we can later adjust the minimal filtering // strength needed to smooth these blocks out. if ((rd.Nz & 0x100ffff) == 0x1000000 && rd.D > dqm.MinDisto) { dqm.StoreMaxDelta(rd.YDcLevels); } }
// Refine intra16/intra4 sub-modes based on distortion only (not rate). public static void RefineUsingDistortion(Vp8EncIterator it, Vp8SegmentInfo[] segmentInfos, Vp8ModeScore rd, bool tryBothModes, bool refineUvMode, int mbHeaderLimit) { long bestScore = Vp8ModeScore.MaxCost; int nz = 0; int mode; bool isI16 = tryBothModes || it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16; Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment]; // Some empiric constants, of approximate order of magnitude. const int lambdaDi16 = 106; const int lambdaDi4 = 11; const int lambdaDuv = 120; long scoreI4 = dqm.I4Penalty; long i4BitSum = 0; long bitLimit = tryBothModes ? mbHeaderLimit : Vp8ModeScore.MaxCost; // no early-out allowed. if (isI16) { int bestMode = -1; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc); for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span <byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]); long score = (LossyUtils.Vp8_Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16); if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit) { continue; } if (score < bestScore) { bestMode = mode; bestScore = score; } } if (it.X == 0 || it.Y == 0) { // Avoid starting a checkerboard resonance from the border. See bug #432 of libwebp. if (IsFlatSource16(src)) { bestMode = it.X == 0 ? 0 : 2; tryBothModes = false; // Stick to i16. } } it.SetIntra16Mode(bestMode); // We'll reconstruct later, if i16 mode actually gets selected. } // Next, evaluate Intra4. if (tryBothModes || !isI16) { // We don't evaluate the rate here, but just account for it through a // constant penalty (i4 mode usually needs more bits compared to i16). isI16 = false; it.StartI4(); do { int bestI4Mode = -1; long bestI4Score = Vp8ModeScore.MaxCost; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc + WebpLookupTables.Vp8Scan[it.I4]); short[] modeCosts = it.GetCostModeI4(rd.ModesI4); it.MakeIntra4Preds(); for (mode = 0; mode < WebpConstants.NumBModes; ++mode) { Span <byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]); long score = (LossyUtils.Vp8_Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4); if (score < bestI4Score) { bestI4Mode = mode; bestI4Score = score; } } i4BitSum += modeCosts[bestI4Mode]; rd.ModesI4[it.I4] = (byte)bestI4Mode; scoreI4 += bestI4Score; if (scoreI4 >= bestScore || i4BitSum > bitLimit) { // Intra4 won't be better than Intra16. Bail out and pick Intra16. isI16 = true; break; } else { // Reconstruct partial block inside YuvOut2 buffer Span <byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc + WebpLookupTables.Vp8Scan[it.I4]); nz |= ReconstructIntra4(it, dqm, rd.YAcLevels.AsSpan(it.I4 * 16, 16), src, tmpDst, bestI4Mode) << it.I4; } }while (it.RotateI4(it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc))); } // Final reconstruction, depending on which mode is selected. if (!isI16) { it.SetIntra4Mode(rd.ModesI4); it.SwapOut(); bestScore = scoreI4; } else { int intra16Mode = it.Preds[it.PredIdx]; nz = ReconstructIntra16(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.YOffEnc), intra16Mode); } // ... and UV! if (refineUvMode) { int bestMode = -1; long bestUvScore = Vp8ModeScore.MaxCost; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc); for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span <byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]); long score = (LossyUtils.Vp8_Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv); if (score < bestUvScore) { bestMode = mode; bestUvScore = score; } } it.SetIntraUvMode(bestMode); } nz |= ReconstructUv(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.UOffEnc), it.CurrentMacroBlockInfo.UvMode); rd.Nz = (uint)nz; rd.Score = bestScore; }
private static void RunVp8Sse16X16Test() { // arrange byte[] a = { 154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168, 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107, 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, 154, 154, 151, 132, 92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, 171, 178, 172, 176, 152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, 102, 100, 107, 100, 101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, 160, 162, 161, 153, 150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, 171, 179, 178, 172, 171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, 86, 86, 102, 105, 102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, 154, 152, 158, 163, 150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 151, 165, 156, 141, 137, 146, 158, 152, 159, 152, 133, 90, 88, 99, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 160, 164, 150, 126, 127, 149, 159, 155, 161, 153, 131, 84, 86, 97, 103, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 157, 167, 157, 137, 102, 128, 155, 161, 157, 159, 154, 134, 84, 82, 97, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 163, 163, 150, 113, 78, 132, 156, 162, 159, 160, 154, 132, 83, 78, 91, 97, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 163, 157, 137, 80, 78, 131, 154, 163, 157, 159, 149, 131, 82, 77, 94, 100, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 159, 151, 108, 72, 88, 132, 156, 162, 159, 157, 151, 130, 79, 78, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 151, 130, 82, 82, 89, 134, 154, 161, 161, 157, 152, 129, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204 }; byte[] b = { 150, 150, 150, 150, 146, 149, 152, 154, 164, 166, 154, 132, 99, 92, 106, 112, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, 161, 164, 151, 130, 93, 86, 100, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127, 93, 86, 100, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 148, 148, 148, 148, 149, 158, 162, 159, 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 154, 154, 154, 156, 161, 159, 152, 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 153, 157, 162, 150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 156, 158, 157, 140, 137, 145, 159, 155, 160, 150, 131, 89, 88, 102, 101, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 153, 161, 160, 149, 118, 128, 147, 162, 155, 160, 150, 131, 86, 85, 99, 98, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 165, 161, 144, 96, 128, 154, 159, 155, 160, 150, 131, 83, 82, 97, 96, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 161, 160, 149, 105, 78, 127, 156, 170, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 160, 160, 133, 85, 81, 129, 155, 167, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 156, 147, 109, 76, 85, 130, 153, 163, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 128, 87, 83, 88, 132, 152, 159, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204 }; int expected = 2063; // act int actual = LossyUtils.Vp8_Sse16X16(a, b); // assert Assert.Equal(expected, actual); }
// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability. private static int BranchCost(int nb, int total, int proba) => (nb * LossyUtils.Vp8BitCost(1, (byte)proba)) + ((total - nb) * LossyUtils.Vp8BitCost(0, (byte)proba));
public static void PickBestUv(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba) { const int numBlocks = 8; Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment]; int lambda = dqm.LambdaUv; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc); Span <byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.UOffEnc); Span <byte> dst0 = it.YuvOut.AsSpan(Vp8EncIterator.UOffEnc); Span <byte> dst = dst0; var rdBest = new Vp8ModeScore(); var rdUv = new Vp8ModeScore(); var res = new Vp8Residual(); int mode; rd.ModeUv = -1; rdBest.InitScore(); for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { rdUv.Clear(); // Reconstruct rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode); // Compute RD-score rdUv.D = LossyUtils.Vp8_Sse16X8(src, tmpDst); rdUv.SD = 0; // not calling TDisto here: it tends to flatten areas. rdUv.H = WebpConstants.Vp8FixedCostsUv[mode]; rdUv.R = it.GetCostUv(rdUv, proba, res); if (mode > 0 && IsFlat(rdUv.UvLevels, numBlocks, WebpConstants.FlatnessLimitIUv)) { rdUv.R += WebpConstants.FlatnessPenality * numBlocks; } rdUv.SetRdScore(lambda); if (mode == 0 || rdUv.Score < rdBest.Score) { rdBest.CopyScore(rdUv); rd.ModeUv = mode; rdUv.UvLevels.CopyTo(rd.UvLevels.AsSpan()); for (int i = 0; i < 2; i++) { rd.Derr[i, 0] = rdUv.Derr[i, 0]; rd.Derr[i, 1] = rdUv.Derr[i, 1]; rd.Derr[i, 2] = rdUv.Derr[i, 2]; } Span <byte> tmp = dst; dst = tmpDst; tmpDst = tmp; } } it.SetIntraUvMode(rd.ModeUv); rd.AddScore(rdBest); if (dst != dst0) { // copy 16x8 block if needed. LossyUtils.Vp8Copy16X8(dst, dst0); } // Store diffusion errors for next block. it.StoreDiffusionErrors(rd); }