private long OneStatPass(int width, int height, int yStride, int uvStride, Vp8RdLevel rdOpt, int nbMbs, PassStats stats) { 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.TopDerr, this.Mbw, this.Mbh); long size = 0; long sizeP0 = 0; long distortion = 0; long pixelCount = nbMbs * 384; it.Init(); this.SetLoopParams(stats.Q); var info = new Vp8ModeScore(); do { info.Clear(); it.Import(y, u, v, yStride, uvStride, width, height, false); if (this.Decimate(it, ref info, rdOpt)) { // Just record the number of skips and act like skipProba is not used. ++this.Proba.NbSkip; } this.RecordResiduals(it, info); size += info.R + info.H; sizeP0 += info.H; distortion += info.D; it.SaveBoundary(); }while (it.Next() && --nbMbs > 0); sizeP0 += this.SegmentHeader.Size; if (stats.DoSizeSearch) { size += this.Proba.FinalizeSkipProba(this.Mbw, this.Mbh); size += this.Proba.FinalizeTokenProbas(); size = ((size + sizeP0 + 1024) >> 11) + HeaderSizeEstimate; stats.Value = size; } else { stats.Value = GetPsnr(distortion, pixelCount); } return(sizeP0); }
/// <summary> /// Only collect statistics(number of skips, token usage, ...). /// This is used for deciding optimal probabilities. It also modifies the /// quantizer value if some target (size, PSNR) was specified. /// </summary> private void StatLoop(int width, int height, int yStride, int uvStride) { int targetSize = 0; // TODO: target size is hardcoded. float targetPsnr = 0.0f; // TODO: targetPsnr is hardcoded. bool doSearch = targetSize > 0 || targetPsnr > 0; bool fastProbe = (this.method == 0 || this.method == WebpEncodingMethod.Level3) && !doSearch; int numPassLeft = this.entropyPasses; Vp8RdLevel rdOpt = this.method >= WebpEncodingMethod.Level3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone; int nbMbs = this.Mbw * this.Mbh; var stats = new PassStats(targetSize, targetPsnr, QMin, QMax, this.quality); this.Proba.ResetTokenStats(); // Fast mode: quick analysis pass over few mbs. Better than nothing. if (fastProbe) { if (this.method == WebpEncodingMethod.Level3) { // We need more stats for method 3 to be reliable. nbMbs = nbMbs > 200 ? nbMbs >> 1 : 100; } else { nbMbs = nbMbs > 200 ? nbMbs >> 2 : 50; } } while (numPassLeft-- > 0) { bool isLastPass = (MathF.Abs(stats.Dq) <= DqLimit) || (numPassLeft == 0) || (this.maxI4HeaderBits == 0); long sizeP0 = this.OneStatPass(width, height, yStride, uvStride, rdOpt, nbMbs, stats); if (sizeP0 == 0) { return; } if (this.maxI4HeaderBits > 0 && sizeP0 > (long)Partition0SizeLimit) { ++numPassLeft; this.maxI4HeaderBits >>= 1; // strengthen header bit limitation... continue; // ...and start over } if (isLastPass) { break; } // If no target size: just do several pass without changing 'q' if (doSearch) { stats.ComputeNextQ(); if (MathF.Abs(stats.Dq) <= DqLimit) { break; } } } if (!doSearch || !stats.DoSizeSearch) { // Need to finalize probas now, since it wasn't done during the search. this.Proba.FinalizeSkipProba(this.Mbw, this.Mbh); this.Proba.FinalizeTokenProbas(); } // Finalize costs. this.Proba.CalculateLevelCosts(); }