Browse Source

Fix issues in HistogramCombineStochastic (still needs another review)

pull/1552/head
Brian Popow 6 years ago
parent
commit
d6aa3f6c9c
  1. 11
      src/ImageSharp/Formats/WebP/Lossless/CostManager.cs
  2. 32
      src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs

11
src/ImageSharp/Formats/WebP/Lossless/CostManager.cs

@ -2,17 +2,18 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace SixLabors.ImageSharp.Formats.WebP.Lossless namespace SixLabors.ImageSharp.Formats.WebP.Lossless
{ {
/// <summary> /// <summary>
/// The CostManager is in charge of managing intervals and costs. /// The CostManager is in charge of managing intervals and costs.
/// It caches the different CostCacheInterval, caches the different /// It caches the different CostCacheInterval, caches the different
/// GetLengthCost(costModel, k) in cost_cache_ and the CostInterval's. /// GetLengthCost(costModel, k) in costCache and the CostInterval's.
/// </summary> /// </summary>
internal class CostManager internal class CostManager
{ {
private CostInterval head;
public CostManager(short[] distArray, int pixCount, CostModel costModel) public CostManager(short[] distArray, int pixCount, CostModel costModel)
{ {
int costCacheSize = (pixCount > BackwardReferenceEncoder.MaxLength) ? BackwardReferenceEncoder.MaxLength : pixCount; int costCacheSize = (pixCount > BackwardReferenceEncoder.MaxLength) ? BackwardReferenceEncoder.MaxLength : pixCount;
@ -63,15 +64,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
cur.End = i + 1; cur.End = i + 1;
} }
// Set the initial costs_ high for every pixel as we will keep the minimum. // Set the initial costs high for every pixel as we will keep the minimum.
for (int i = 0; i < pixCount; i++) for (int i = 0; i < pixCount; i++)
{ {
this.Costs[i] = 1e38f; this.Costs[i] = 1e38f;
} }
} }
private CostInterval head;
/// <summary> /// <summary>
/// Gets or sets the number of stored intervals. /// Gets or sets the number of stored intervals.
/// </summary> /// </summary>
@ -230,7 +229,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return; return;
} }
ConnectIntervals(interval.Previous, interval.Next); this.ConnectIntervals(interval.Previous, interval.Next);
this.Count--; this.Count--;
} }

32
src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs

@ -48,6 +48,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Copies the histograms and computes its bitCost. histogramSymbols is optimized. // Copies the histograms and computes its bitCost. histogramSymbols is optimized.
HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols); HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols);
numUsed = imageHisto.Count(h => h != null);
var entropyCombine = (numUsed > entropyCombineNumBins * 2) && (quality < 100); var entropyCombine = (numUsed > entropyCombineNumBins * 2) && (quality < 100);
if (entropyCombine) if (entropyCombine)
@ -319,12 +320,17 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Priority queue of histogram pairs. Its size impacts the quality of the compression and the speed: // Priority queue of histogram pairs. Its size impacts the quality of the compression and the speed:
// the smaller the faster but the worse for the compression. // the smaller the faster but the worse for the compression.
var histoPriorityList = new List<HistogramPair>(); var histoPriorityList = new List<HistogramPair>();
int histoQueueMaxSize = histograms.Count * histograms.Count; int maxSize = 9;
// Fill the initial mapping. // Fill the initial mapping.
var mappings = new int[histograms.Count]; var mappings = new int[histograms.Count];
for (int j = 0, iter = 0; iter < histograms.Count; iter++) for (int j = 0, iter = 0; iter < histograms.Count; iter++)
{ {
if (histograms[iter] == null)
{
continue;
}
mappings[j++] = iter; mappings[j++] = iter;
} }
@ -353,15 +359,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
idx2 = mappings[idx2]; idx2 = mappings[idx2];
// Calculate cost reduction on combination. // Calculate cost reduction on combination.
var currCost = HistoPriorityListPush(histoPriorityList, histoQueueMaxSize, histograms, idx1, idx2, bestCost); var currCost = HistoPriorityListPush(histoPriorityList, maxSize, histograms, idx1, idx2, bestCost);
// Found a better pair? // Found a better pair?
if (currCost < 0) if (currCost < 0)
{ {
bestCost = currCost; bestCost = currCost;
// Empty the queue if we reached full capacity. if (histoPriorityList.Count == maxSize)
if (histoPriorityList.Count == histoQueueMaxSize)
{ {
break; break;
} }
@ -377,10 +382,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
bestIdx1 = histoPriorityList[0].Idx1; bestIdx1 = histoPriorityList[0].Idx1;
bestIdx2 = histoPriorityList[0].Idx2; bestIdx2 = histoPriorityList[0].Idx2;
// TODO: Review this again, not sure why this is needed in the reference implementation.
// Pop bestIdx2 from mappings. // Pop bestIdx2 from mappings.
var mappingIndex = Array.BinarySearch(mappings, bestIdx2); // var mappingIndex = Array.BinarySearch(mappings, bestIdx2);
// memmove(mapping_index, mapping_index + 1, sizeof(*mapping_index) *((*num_used) - (mapping_index - mappings) - 1));
// TODO: memmove(mapping_index, mapping_index + 1, sizeof(*mapping_index) *((*num_used) - (mapping_index - mappings) - 1));
// Merge the histograms and remove bestIdx2 from the queue. // Merge the histograms and remove bestIdx2 from the queue.
HistogramAdd(histograms[bestIdx2], histograms[bestIdx1], histograms[bestIdx1]); HistogramAdd(histograms[bestIdx2], histograms[bestIdx1], histograms[bestIdx1]);
@ -388,8 +393,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
histograms.RemoveAt(bestIdx2); histograms.RemoveAt(bestIdx2);
numUsed--; numUsed--;
var indicesToRemove = new List<int>();
int lastIndex = histoPriorityList.Count - 1;
for (int j = 0; j < histoPriorityList.Count;) for (int j = 0; j < histoPriorityList.Count;)
{ {
HistogramPair p = histoPriorityList.ElementAt(j); HistogramPair p = histoPriorityList.ElementAt(j);
@ -401,9 +404,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// check for it all the time nevertheless. // check for it all the time nevertheless.
if (isIdx1Best && isIdx2Best) if (isIdx1Best && isIdx2Best)
{ {
indicesToRemove.Add(lastIndex); histoPriorityList.RemoveAt(histoPriorityList.Count - 1);
numUsed--; numUsed--;
lastIndex--;
continue; continue;
} }
@ -434,8 +436,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
HistoListUpdatePair(histograms[p.Idx1], histograms[p.Idx2], 0.0d, p); HistoListUpdatePair(histograms[p.Idx1], histograms[p.Idx2], 0.0d, p);
if (p.CostDiff >= 0.0d) if (p.CostDiff >= 0.0d)
{ {
indicesToRemove.Add(lastIndex); histoPriorityList.RemoveAt(histoPriorityList.Count - 1);
lastIndex--;
numUsed--; numUsed--;
continue; continue;
} }
@ -578,10 +579,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
/// <summary> /// <summary>
/// Create a pair from indices "idx1" and "idx2" provided its cost /// Create a pair from indices "idx1" and "idx2" provided its cost is inferior to "threshold", a negative entropy.
/// is inferior to "threshold", a negative entropy.
/// </summary> /// </summary>
/// <returns>The cost of the pair, or 0. if it superior to threshold.</returns> /// <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) private static double HistoPriorityListPush(List<HistogramPair> histoList, int maxSize, List<Vp8LHistogram> histograms, int idx1, int idx2, double threshold)
{ {
var pair = new HistogramPair(); var pair = new HistogramPair();

Loading…
Cancel
Save