Esempio n. 1
0
        /// <summary>
        /// Create a pair from indices "idx1" and "idx2" provided its cost is inferior to "threshold", a negative entropy.
        /// </summary>
        /// <returns>The cost of the pair, or 0 if it superior to threshold.</returns>
        private static double HistoPriorityListPush(List <HistogramPair> histoList, int maxSize, List <Vp8LHistogram> histograms, int idx1, int idx2, double threshold, Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy)
        {
            var pair = new HistogramPair();

            if (histoList.Count == maxSize)
            {
                return(0.0d);
            }

            if (idx1 > idx2)
            {
                int tmp = idx2;
                idx2 = idx1;
                idx1 = tmp;
            }

            pair.Idx1 = idx1;
            pair.Idx2 = idx2;
            Vp8LHistogram h1 = histograms[idx1];
            Vp8LHistogram h2 = histograms[idx2];

            HistoListUpdatePair(h1, h2, stats, bitsEntropy, threshold, pair);

            // Do not even consider the pair if it does not improve the entropy.
            if (pair.CostDiff >= threshold)
            {
                return(0.0d);
            }

            histoList.Add(pair);

            HistoListUpdateHead(histoList, pair);

            return(pair.CostDiff);
        }
Esempio n. 2
0
 /// <summary>
 /// Check whether a pair in the list should be updated as head or not.
 /// </summary>
 private static void HistoListUpdateHead(List <HistogramPair> histoList, HistogramPair pair)
 {
     if (pair.CostDiff < histoList[0].CostDiff)
     {
         // Replace the best pair.
         int oldIdx = histoList.IndexOf(pair);
         histoList[oldIdx] = histoList[0];
         histoList[0]      = pair;
     }
 }
Esempio n. 3
0
        /// <summary>
        /// Update the cost diff and combo of a pair of histograms. This needs to be called when the the histograms have been merged with a third one.
        /// </summary>
        private static void HistoListUpdatePair(Vp8LHistogram h1, Vp8LHistogram h2, Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy, double threshold, HistogramPair pair)
        {
            double sumCost = h1.BitCost + h2.BitCost;

            pair.CostCombo = 0.0d;
            h1.GetCombinedHistogramEntropy(h2, stats, bitsEntropy, sumCost + threshold, costInitial: pair.CostCombo, out double cost);
            pair.CostCombo = cost;
            pair.CostDiff  = pair.CostCombo - sumCost;
        }
Esempio n. 4
0
        private static void HistogramCombineGreedy(List <Vp8LHistogram> histograms)
        {
            int histoSize = histograms.Count(h => h != null);

            // Priority list of histogram pairs.
            var histoPriorityList = new List <HistogramPair>();
            int maxSize           = histoSize * histoSize;
            var stats             = new Vp8LStreaks();
            var bitsEntropy       = new Vp8LBitEntropy();

            for (int i = 0; i < histoSize; i++)
            {
                if (histograms[i] == null)
                {
                    continue;
                }

                for (int j = i + 1; j < histoSize; j++)
                {
                    if (histograms[j] == null)
                    {
                        continue;
                    }

                    HistoPriorityListPush(histoPriorityList, maxSize, histograms, i, j, 0.0d, stats, bitsEntropy);
                }
            }

            while (histoPriorityList.Count > 0)
            {
                int idx1 = histoPriorityList[0].Idx1;
                int idx2 = histoPriorityList[0].Idx2;
                HistogramAdd(histograms[idx2], histograms[idx1], histograms[idx1]);
                histograms[idx1].BitCost = histoPriorityList[0].CostCombo;

                // Remove merged histogram.
                histograms[idx2] = null;

                // Remove pairs intersecting the just combined best pair.
                for (int i = 0; i < histoPriorityList.Count;)
                {
                    HistogramPair p = histoPriorityList.ElementAt(i);
                    if (p.Idx1 == idx1 || p.Idx2 == idx1 || p.Idx1 == idx2 || p.Idx2 == idx2)
                    {
                        // Replace item at pos i with the last one and shrinking the list.
                        histoPriorityList[i] = histoPriorityList[histoPriorityList.Count - 1];
                        histoPriorityList.RemoveAt(histoPriorityList.Count - 1);
                    }
                    else
                    {
                        HistoListUpdateHead(histoPriorityList, p);
                        i++;
                    }
                }

                // Push new pairs formed with combined histogram to the list.
                for (int i = 0; i < histoSize; i++)
                {
                    if (i == idx1 || histograms[i] == null)
                    {
                        continue;
                    }

                    HistoPriorityListPush(histoPriorityList, maxSize, histograms, idx1, i, 0.0d, stats, bitsEntropy);
                }
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Perform histogram aggregation using a stochastic approach.
        /// </summary>
        /// <returns>true if a greedy approach needs to be performed afterwards, false otherwise.</returns>
        private static bool HistogramCombineStochastic(List <Vp8LHistogram> histograms, int minClusterSize)
        {
            uint seed = 1;
            int  triesWithNoSuccess = 0;
            int  numUsed            = histograms.Count(h => h != null);
            int  outerIters         = numUsed;
            int  numTriesNoSuccess  = outerIters / 2;
            var  stats       = new Vp8LStreaks();
            var  bitsEntropy = new Vp8LBitEntropy();

            if (numUsed < minClusterSize)
            {
                return(true);
            }

            // Priority list of histogram pairs. Its size impacts the quality of the compression and the speed:
            // the smaller the faster but the worse for the compression.
            var histoPriorityList = new List <HistogramPair>();
            int maxSize           = 9;

            // Fill the initial mapping.
            int[] mappings = new int[histograms.Count];
            for (int j = 0, iter = 0; iter < histograms.Count; iter++)
            {
                if (histograms[iter] == null)
                {
                    continue;
                }

                mappings[j++] = iter;
            }

            // Collapse similar histograms.
            for (int iter = 0; iter < outerIters && numUsed >= minClusterSize && ++triesWithNoSuccess < numTriesNoSuccess; iter++)
            {
                double bestCost  = histoPriorityList.Count == 0 ? 0.0d : histoPriorityList[0].CostDiff;
                int    numTries  = numUsed / 2;
                uint   randRange = (uint)((numUsed - 1) * numUsed);

                // Pick random samples.
                for (int j = 0; numUsed >= 2 && j < numTries; j++)
                {
                    // Choose two different histograms at random and try to combine them.
                    uint tmp  = MyRand(ref seed) % randRange;
                    int  idx1 = (int)(tmp / (numUsed - 1));
                    int  idx2 = (int)(tmp % (numUsed - 1));
                    if (idx2 >= idx1)
                    {
                        idx2++;
                    }

                    idx1 = mappings[idx1];
                    idx2 = mappings[idx2];

                    // Calculate cost reduction on combination.
                    double currCost = HistoPriorityListPush(histoPriorityList, maxSize, histograms, idx1, idx2, bestCost, stats, bitsEntropy);

                    // Found a better pair?
                    if (currCost < 0)
                    {
                        bestCost = currCost;

                        if (histoPriorityList.Count == maxSize)
                        {
                            break;
                        }
                    }
                }

                if (histoPriorityList.Count == 0)
                {
                    continue;
                }

                // Get the best histograms.
                int bestIdx1 = histoPriorityList[0].Idx1;
                int bestIdx2 = histoPriorityList[0].Idx2;

                int        mappingIndex = Array.IndexOf(mappings, bestIdx2);
                Span <int> src          = mappings.AsSpan(mappingIndex + 1, numUsed - mappingIndex - 1);
                Span <int> dst          = mappings.AsSpan(mappingIndex);
                src.CopyTo(dst);

                // Merge the histograms and remove bestIdx2 from the list.
                HistogramAdd(histograms[bestIdx2], histograms[bestIdx1], histograms[bestIdx1]);
                histograms.ElementAt(bestIdx1).BitCost = histoPriorityList[0].CostCombo;
                histograms[bestIdx2] = null;
                numUsed--;

                for (int j = 0; j < histoPriorityList.Count;)
                {
                    HistogramPair p          = histoPriorityList[j];
                    bool          isIdx1Best = p.Idx1 == bestIdx1 || p.Idx1 == bestIdx2;
                    bool          isIdx2Best = p.Idx2 == bestIdx1 || p.Idx2 == bestIdx2;
                    bool          doEval     = false;

                    // The front pair could have been duplicated by a random pick so
                    // check for it all the time nevertheless.
                    if (isIdx1Best && isIdx2Best)
                    {
                        histoPriorityList[j] = histoPriorityList[histoPriorityList.Count - 1];
                        histoPriorityList.RemoveAt(histoPriorityList.Count - 1);
                        continue;
                    }

                    // Any pair containing one of the two best indices should only refer to
                    // bestIdx1. Its cost should also be updated.
                    if (isIdx1Best)
                    {
                        p.Idx1 = bestIdx1;
                        doEval = true;
                    }
                    else if (isIdx2Best)
                    {
                        p.Idx2 = bestIdx1;
                        doEval = true;
                    }

                    // Make sure the index order is respected.
                    if (p.Idx1 > p.Idx2)
                    {
                        int tmp = p.Idx2;
                        p.Idx2 = p.Idx1;
                        p.Idx1 = tmp;
                    }

                    if (doEval)
                    {
                        // Re-evaluate the cost of an updated pair.
                        HistoListUpdatePair(histograms[p.Idx1], histograms[p.Idx2], stats, bitsEntropy, 0.0d, p);
                        if (p.CostDiff >= 0.0d)
                        {
                            histoPriorityList[j] = histoPriorityList[histoPriorityList.Count - 1];
                            histoPriorityList.RemoveAt(histoPriorityList.Count - 1);
                            continue;
                        }
                    }

                    HistoListUpdateHead(histoPriorityList, p);
                    j++;
                }

                triesWithNoSuccess = 0;
            }

            bool doGreedy = numUsed <= minClusterSize;

            return(doGreedy);
        }