Browse Source

Use DistanceSquared

af/merge-core
James Jackson-South 8 years ago
parent
commit
84099e4793
  1. 4
      src/ImageSharp/Processing/Dithering/Processors/PaletteDitherProcessorBase.cs
  2. 34
      src/ImageSharp/Processing/Quantization/FrameQuantizers/FrameQuantizerBase{TPixel}.cs
  3. 40
      src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs

4
src/ImageSharp/Processing/Dithering/Processors/PaletteDitherProcessorBase.cs

@ -42,8 +42,8 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
} }
// Not found - loop through the palette and find the nearest match. // Not found - loop through the palette and find the nearest match.
float leastDistance = int.MaxValue; float leastDistance = float.MaxValue;
float secondLeastDistance = int.MaxValue; float secondLeastDistance = float.MaxValue;
var vector = pixel.ToVector4(); var vector = pixel.ToVector4();
TPixel closest = default; TPixel closest = default;

34
src/ImageSharp/Processing/Quantization/FrameQuantizers/FrameQuantizerBase{TPixel}.cs

@ -125,11 +125,10 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
protected byte GetClosestPixel(TPixel pixel, TPixel[] colorPalette, Dictionary<TPixel, byte> cache) protected byte GetClosestPixel(TPixel pixel, TPixel[] colorPalette, Dictionary<TPixel, byte> cache)
{ {
// Check if the color is in the lookup table // Check if the color is in the lookup table
if (cache.TryGetValue(pixel, out byte value)) // if (cache.TryGetValue(pixel, out byte value))
{ // {
return value; // return value;
} // }
return this.GetClosestPixelSlow(pixel, colorPalette, cache); return this.GetClosestPixelSlow(pixel, colorPalette, cache);
} }
@ -137,34 +136,31 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
private byte GetClosestPixelSlow(TPixel pixel, TPixel[] colorPalette, Dictionary<TPixel, byte> cache) private byte GetClosestPixelSlow(TPixel pixel, TPixel[] colorPalette, Dictionary<TPixel, byte> cache)
{ {
// Loop through the palette and find the nearest match. // Loop through the palette and find the nearest match.
byte colorIndex = 0; int colorIndex = 0;
float leastDistance = int.MaxValue; float leastDistance = float.MaxValue;
var vector = pixel.ToVector4(); var vector = pixel.ToVector4();
for (int index = 0; index < colorPalette.Length; index++) for (int index = 0; index < colorPalette.Length; index++)
{ {
float distance = Vector4.Distance(vector, colorPalette[index].ToVector4()); ref TPixel candidate = ref colorPalette[index];
float distance = Vector4.DistanceSquared(vector, candidate.ToVector4());
// Greater... Move on. if (distance < leastDistance)
if (!(distance < leastDistance))
{ {
continue; colorIndex = index;
leastDistance = distance;
} }
colorIndex = (byte)index; // If it's an exact match, exit the loop
leastDistance = distance; if (distance == 0)
// And if it's an exact match, exit the loop
if (MathF.Abs(distance) < Constants.Epsilon)
{ {
break; break;
} }
} }
// Now I have the index, pop it into the cache for next time // Now I have the index, pop it into the cache for next time
cache.Add(pixel, colorIndex); // cache.Add(pixel, colorIndex);
return (byte)colorIndex;
return colorIndex;
} }
} }
} }

40
src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
@ -18,11 +19,6 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
internal sealed class OctreeFrameQuantizer<TPixel> : FrameQuantizerBase<TPixel> internal sealed class OctreeFrameQuantizer<TPixel> : FrameQuantizerBase<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// A lookup table for colors
/// </summary>
private readonly Dictionary<TPixel, byte> colorMap = new Dictionary<TPixel, byte>();
/// <summary> /// <summary>
/// Maximum allowed color depth /// Maximum allowed color depth
/// </summary> /// </summary>
@ -33,6 +29,11 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
/// </summary> /// </summary>
private readonly Octree octree; private readonly Octree octree;
/// <summary>
/// A lookup table for colors
/// </summary>
private Dictionary<TPixel, byte> colorMap = new Dictionary<TPixel, byte>();
/// <summary> /// <summary>
/// The reduced image palette /// The reduced image palette
/// </summary> /// </summary>
@ -476,7 +477,6 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
| ((rgba.R & Mask[level]) >> shift); | ((rgba.R & Mask[level]) >> shift);
OctreeNode child = this.children[index]; OctreeNode child = this.children[index];
if (child == null) if (child == null)
{ {
// Create a new child node and store it in the array // Create a new child node and store it in the array
@ -501,12 +501,13 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
// Loop through all children and add their information to this node // Loop through all children and add their information to this node
for (int index = 0; index < 8; index++) for (int index = 0; index < 8; index++)
{ {
if (this.children[index] != null) OctreeNode child = this.children[index];
if (child != null)
{ {
this.red += this.children[index].red; this.red += child.red;
this.green += this.children[index].green; this.green += child.green;
this.blue += this.children[index].blue; this.blue += child.blue;
this.pixelCount += this.children[index].pixelCount; this.pixelCount += child.pixelCount;
++childNodes; ++childNodes;
this.children[index] = null; this.children[index] = null;
} }
@ -528,14 +529,10 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
{ {
if (this.leaf) if (this.leaf)
{ {
// This seems faster than using Vector4 // Set the color of the palette entry
byte r = (this.red / this.pixelCount).ToByte(); var vector = Vector3.Clamp(new Vector3(this.red, this.green, this.blue) / this.pixelCount, Vector3.Zero, new Vector3(255));
byte g = (this.green / this.pixelCount).ToByte();
byte b = (this.blue / this.pixelCount).ToByte();
// And set the color of the palette entry
TPixel pixel = default; TPixel pixel = default;
pixel.PackFromRgba32(new Rgba32(r, g, b, 255)); pixel.PackFromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, byte.MaxValue));
palette[index] = pixel; palette[index] = pixel;
// Consume the next palette index // Consume the next palette index
@ -573,13 +570,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
| ((rgba.G & Mask[level]) >> (shift - 1)) | ((rgba.G & Mask[level]) >> (shift - 1))
| ((rgba.R & Mask[level]) >> shift); | ((rgba.R & Mask[level]) >> shift);
if (this.children[pixelIndex] != null) OctreeNode child = this.children[pixelIndex];
if (child != null)
{ {
index = this.children[pixelIndex].GetPaletteIndex(ref pixel, level + 1, ref rgba); index = child.GetPaletteIndex(ref pixel, level + 1, ref rgba);
} }
else else
{ {
throw new Exception($"Cannot retrive a pixel at the given index {pixelIndex}."); throw new Exception($"Cannot retrieve a pixel at the given index {pixelIndex}.");
} }
} }

Loading…
Cancel
Save