Exemple #1
0
        private bool Decimate(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8RdLevel rdOpt)
        {
            rd.InitScore();

            // We can perform predictions for Luma16x16 and Chroma8x8 already.
            // Luma4x4 predictions needs to be done as-we-go.
            it.MakeLuma16Preds();
            it.MakeChroma8Preds();

            if (rdOpt > Vp8RdLevel.RdOptNone)
            {
                QuantEnc.PickBestIntra16(it, ref rd, this.SegmentInfos, this.Proba);
                if (this.method >= WebpEncodingMethod.Level2)
                {
                    QuantEnc.PickBestIntra4(it, ref rd, this.SegmentInfos, this.Proba, this.maxI4HeaderBits);
                }

                QuantEnc.PickBestUv(it, ref rd, this.SegmentInfos, this.Proba);
            }
            else
            {
                // At this point we have heuristically decided intra16 / intra4.
                // For method >= 2, pick the best intra4/intra16 based on SSE (~tad slower).
                // For method <= 1, we don't re-examine the decision but just go ahead with
                // quantization/reconstruction.
                QuantEnc.RefineUsingDistortion(it, this.SegmentInfos, rd, this.method >= WebpEncodingMethod.Level2, this.method >= WebpEncodingMethod.Level1, this.MbHeaderLimit);
            }

            bool isSkipped = rd.Nz == 0;

            it.SetSkip(isSkipped);

            return(isSkipped);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        /// <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();
        }
Exemple #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Vp8Encoder" /> class.
        /// </summary>
        /// <param name="memoryAllocator">The memory allocator.</param>
        /// <param name="configuration">The global configuration.</param>
        /// <param name="width">The width of the input image.</param>
        /// <param name="height">The height of the input image.</param>
        /// <param name="quality">The encoding quality.</param>
        /// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param>
        /// <param name="entropyPasses">Number of entropy-analysis passes (in [1..10]).</param>
        /// <param name="filterStrength">The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).</param>
        /// <param name="spatialNoiseShaping">The spatial noise shaping. 0=off, 100=maximum.</param>
        public Vp8Encoder(
            MemoryAllocator memoryAllocator,
            Configuration configuration,
            int width,
            int height,
            int quality,
            WebpEncodingMethod method,
            int entropyPasses,
            int filterStrength,
            int spatialNoiseShaping)
        {
            this.memoryAllocator     = memoryAllocator;
            this.configuration       = configuration;
            this.Width               = width;
            this.Height              = height;
            this.quality             = Numerics.Clamp(quality, 0, 100);
            this.method              = method;
            this.entropyPasses       = Numerics.Clamp(entropyPasses, 1, 10);
            this.filterStrength      = Numerics.Clamp(filterStrength, 0, 100);
            this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100);
            this.rdOptLevel          = method is WebpEncodingMethod.BestQuality ? Vp8RdLevel.RdOptTrellisAll
                : method >= WebpEncodingMethod.Level5 ? Vp8RdLevel.RdOptTrellis
                : method >= WebpEncodingMethod.Level3 ? Vp8RdLevel.RdOptBasic
                : Vp8RdLevel.RdOptNone;

            int pixelCount = width * height;

            this.Mbw = (width + 15) >> 4;
            this.Mbh = (height + 15) >> 4;
            int uvSize = ((width + 1) >> 1) * ((height + 1) >> 1);

            this.Y             = this.memoryAllocator.Allocate <byte>(pixelCount);
            this.U             = this.memoryAllocator.Allocate <byte>(uvSize);
            this.V             = this.memoryAllocator.Allocate <byte>(uvSize);
            this.YTop          = new byte[this.Mbw * 16];
            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);
            this.TopDerr       = new sbyte[this.Mbw * 4];

            // TODO: make partition_limit configurable?
            int limit = 100; // original code: limit = 100 - config->partition_limit;

            this.maxI4HeaderBits =
                256 * 16 * 16 * limit * limit / (100 * 100);  // ... modulated with a quadratic curve.

            this.MbInfo = new Vp8MacroBlockInfo[this.Mbw * this.Mbh];
            for (int i = 0; i < this.MbInfo.Length; i++)
            {
                this.MbInfo[i] = new Vp8MacroBlockInfo();
            }

            this.SegmentInfos = new Vp8SegmentInfo[4];
            for (int i = 0; i < 4; i++)
            {
                this.SegmentInfos[i] = new Vp8SegmentInfo();
            }

            this.FilterHeader = new Vp8FilterHeader();
            int predSize = (((4 * this.Mbw) + 1) * ((4 * this.Mbh) + 1)) + this.PredsWidth + 1;

            this.PredsWidth = (4 * this.Mbw) + 1;
            this.Proba      = new Vp8EncProba();
            this.Preds      = new byte[predSize + this.PredsWidth + this.Mbw];

            // Initialize with default values, which the reference c implementation uses,
            // to be able to compare to the original and spot differences.
            this.Preds.AsSpan().Fill(205);
            this.Nz.AsSpan().Fill(3452816845);

            this.ResetBoundaryPredictions();
        }