Browse Source

super accurate results!

af/merge-core
Anton Firszov 9 years ago
parent
commit
0d424c7c93
  1. 9
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  2. 13
      src/ImageSharp/Formats/Jpeg/Common/PostProcessing/JpegImagePostProcessor.cs
  3. 8
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs
  4. 20
      tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs
  5. 22
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
  6. 3
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
  7. 2
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs
  8. 20
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

9
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -560,6 +560,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
// TODO: Optimize this!
public void RoundInplace()
{
for (int i = 0; i < Size; i++)
{
this[i] = MathF.Round(this[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{

13
src/ImageSharp/Formats/Jpeg/Common/PostProcessing/JpegImagePostProcessor.cs

@ -3,7 +3,6 @@ using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.PostProcessing
public const int PixelRowsPerStep = 4 * 8;
private JpegComponentPostProcessor[] componentProcessors;
public JpegImagePostProcessor(IRawJpegData rawJpeg)
{
this.RawJpeg = rawJpeg;
@ -25,9 +22,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.PostProcessing
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
this.componentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray();
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray();
}
public JpegComponentPostProcessor[] ComponentProcessors { get; }
public IRawJpegData RawJpeg { get; }
public int NumberOfPostProcessorSteps { get; }
@ -38,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.PostProcessing
public void Dispose()
{
foreach (JpegComponentPostProcessor cpp in this.componentProcessors)
foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors)
{
cpp.Dispose();
}
@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.PostProcessing
throw new NotImplementedException();
}
foreach (JpegComponentPostProcessor cpp in this.componentProcessors)
foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors)
{
cpp.CopyBlocksToColorBuffer();
}
@ -76,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.PostProcessing
{
int maxY = Math.Min(destination.Height, this.CurrentImageRowInPixels + PixelRowsPerStep);
JpegComponentPostProcessor[] cp = this.componentProcessors;
JpegComponentPostProcessor[] cp = this.ComponentProcessors;
YCbCrAndRgbConverter converter = new YCbCrAndRgbConverter();

8
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs

@ -10,6 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
using System.Runtime.CompilerServices;
using SixLabors.Primitives;
/// <summary>
/// Encapsulates the implementation of processing "raw" <see cref="Buffer{T}"/>-s into Jpeg image channels.
/// </summary>
@ -74,7 +76,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.QuantizeAndTransform(decoder, component, ref sourceBlock);
this.data.ResultBlock.NormalizeColorsInplace();
this.data.ResultBlock.CopyTo(destArea);
Size divs = component.SubSamplingDivisors;
this.data.ResultBlock.RoundInplace();
this.data.ResultBlock.CopyTo(destArea, divs.Width, divs.Height);
}
/// <summary>

20
tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs

@ -37,8 +37,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private ITestOutputHelper Output { get; }
private static void SaveBuffer<TPixel>(JpegComponentPostProcessor cp, TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<Rgba32> image = cp.ColorBuffer.ToGrayscaleImage(1f / 255f))
{
image.DebugSave(provider, $"-C{cp.Component.Index}-");
}
}
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)]
[WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)]
public void DoProcessorStep<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
@ -49,7 +60,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
pp.DoPostProcessorStep(image);
image.DebugSave(provider);
JpegComponentPostProcessor[] cp = pp.ComponentProcessors;
SaveBuffer(cp[0], provider);
SaveBuffer(cp[1], provider);
SaveBuffer(cp[2], provider);
}
}
@ -78,7 +93,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image);
this.Output.WriteLine("Difference: "+ report.DifferencePercentageString);
this.Output.WriteLine($"*** {imageFile} ***");
this.Output.WriteLine($"Difference: "+ report.DifferencePercentageString);
// ReSharper disable once PossibleInvalidOperationException
Assert.True(report.TotalNormalizedDifference.Value < 0.005f);

22
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs

@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2, 200)]
public void LLM_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range)
{
float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed);
float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-range, range, seed);
var source = Block8x8F.Load(sourceArray);
@ -61,7 +61,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.CompareBlocks(expected, actual, 0.1f);
}
[Theory]
[InlineData(42, 1000)]
[InlineData(1, 1000)]
[InlineData(2, 1000)]
public void LLM_IDCT_CompareToIntegerRoundedAccurateImplementation(int seed, int range)
{
Block8x8F fSource = CreateRoundedRandomFloatBlock(-range, range, seed);
Block8x8 iSource = fSource.RoundAsInt16Block();
Block8x8 iExpected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref iSource);
Block8x8F fExpected = iExpected.AsFloatBlock();
Block8x8F fActual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref fSource);
this.CompareBlocks(fExpected, fActual, 2);
}
[Theory]
[InlineData(42)]
[InlineData(1)]

3
tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

@ -106,6 +106,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal static Block8x8F CreateRandomFloatBlock(float minValue, float maxValue, int seed = 42) =>
Block8x8F.Load(Create8x8RandomFloatData(minValue, maxValue, seed));
internal static Block8x8F CreateRoundedRandomFloatBlock(int minValue, int maxValue, int seed = 42) =>
Block8x8F.Load(Create8x8RoundedRandomFloatData(minValue, maxValue, seed));
internal void Print8x8Data<T>(T[] data) => this.Print8x8Data(new Span<T>(data));
internal void Print8x8Data<T>(Span<T> data)

2
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs

@ -20,7 +20,7 @@
}
public static ImageSimilarityReport Empty =>
new ImageSimilarityReport(null, null, Enumerable.Empty<PixelDifference>(), null);
new ImageSimilarityReport(null, null, Enumerable.Empty<PixelDifference>(), 0f);
// TODO: This should not be a nullable value!
public float? TotalNormalizedDifference { get; }

20
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -13,6 +13,10 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
namespace SixLabors.ImageSharp.Tests
{
using System.Numerics;
using SixLabors.ImageSharp.Memory;
public static class TestImageExtensions
{
/// <summary>
@ -187,5 +191,21 @@ namespace SixLabors.ImageSharp.Tests
return image;
}
internal static Image<Rgba32> ToGrayscaleImage(this Buffer2D<float> buffer, float scale)
{
var image = new Image<Rgba32>(buffer.Width, buffer.Height);
Span<Rgba32> pixels = image.Pixels;
for (int i = 0; i < buffer.Length; i++)
{
float value = buffer[i] * scale;
var v = new Vector4(value, value, value, 1f);
pixels[i].PackFromVector4(v);
}
return image;
}
}
}
Loading…
Cancel
Save