Browse Source

Added comments, docs and improved estimation performances with intrinsics (not tested)

pull/1706/head
Dmitry Pentin 5 years ago
parent
commit
7772ff12ee
  1. 41
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  2. 33
      src/ImageSharp/Formats/Jpeg/Components/Quantization.cs
  3. 8
      src/ImageSharp/Formats/Jpeg/JpegMetadata.cs

41
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -830,5 +830,46 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
d.V7R.W = this.V7R.W;
}
}
/// <summary>
/// Compares entire 8x8 block to a single scalar value.
/// </summary>
/// <param name="value">Value to compare to.</param>
public bool EqualsToScalar(int value)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
const int equalityMask = unchecked((int)0b1111_1111_1111_1111_1111_1111_1111_1111);
var targetVector = Vector256.Create(value);
ref Vector256<float> blockStride = ref this.V0;
for (int i = 0; i < RowCount; i++)
{
Vector256<int> areEqual = Avx2.CompareEqual(Avx.ConvertToVector256Int32WithTruncation(Unsafe.Add(ref this.V0, i)), targetVector);
if (Avx2.MoveMask(areEqual.AsByte()) != equalityMask)
{
return false;
}
}
return true;
}
#endif
{
ref float scalars = ref Unsafe.As<Block8x8F, float>(ref this);
for (int i = 0; i < Size; i++)
{
if ((int)Unsafe.Add(ref scalars, i) != value)
{
return false;
}
}
return true;
}
}
}
}

33
src/ImageSharp/Formats/Jpeg/Components/Quantization.cs

@ -69,37 +69,44 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
// https://github.com/ImpulseAdventure/JPEGsnoop/blob/9732ee0961f100eb69bbff4a0c47438d5997abee/source/JfifDecode.cpp#L4570-L4694
public static void EstimateQuality(ref Block8x8F table, ReadOnlySpan<byte> target, out double quality, out double variance)
{
// This method can be SIMD'ified if standard table is injected as Block8x8F
// Or when we go to full-int16 spectral code implementation and inject both tables as Block8x8
// This method can be SIMD'ified if standard table is injected as Block8x8F.
// Or when we go to full-int16 spectral code implementation and inject both tables as Block8x8.
double comparePercent;
double sumPercent = 0;
double sumPercentSqr = 0;
bool allOnes = true;
// Corner case - all 1's => 100 quality
// It would fail to deduce using algorithm below without this check
if (table.EqualsToScalar(1))
{
// While this is a 100% to be 100 quality, any given table can be scaled to all 1's.
// According to jpeg creators, top of the line quality is 99, 100 is just a technical 'limit'.
quality = 100;
variance = 0;
return;
}
for (int i = 0; i < Block8x8F.Size; i++)
{
float coeff = table[i];
int coeffInteger = (int)coeff;
// coefficients are actually int16 casted to float numbers so there's no truncating error
// Coefficients are actually int16 casted to float numbers so there's no truncating error.
if (coeffInteger != 0)
{
comparePercent = 100.0 * (table[i] / target[i]);
}
else
{
// No 'valid' quantization table should contain zero at any position
// while this is okay to decode with, it will throw DivideByZeroException at encoding proces stage.
// Not sure what to do here, we can't throw as this technically correct
// but this will screw up the encoder.
comparePercent = 999.99;
}
sumPercent += comparePercent;
sumPercentSqr += comparePercent * comparePercent;
// Check just in case entire table are ones (Quality 100)
if (coeffInteger != 1)
{
allOnes = false;
}
}
// Perform some statistical analysis of the quality factor
@ -111,11 +118,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
variance = sumPercentSqr - (sumPercent * sumPercent);
// Generate the equivalent IJQ "quality" factor
if (allOnes)
{
quality = 100;
}
else if (sumPercent <= 100.0)
if (sumPercent <= 100.0)
{
quality = (200 - sumPercent) / 2;
}

8
src/ImageSharp/Formats/Jpeg/JpegMetadata.cs

@ -46,8 +46,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
this.Quality = other.Quality;
this.ColorType = other.ColorType;
this.lumaQuantizationTable = other.lumaQuantizationTable;
this.chromaQuantizationTable = other.chromaQuantizationTable;
this.LumaQuality = other.LumaQuality;
this.ChromaQuality = other.ChromaQuality;
}
/// <summary>
@ -64,6 +67,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
/// <summary>
/// Gets a value indicating whether jpeg was encoded using ITU section spec K.1 quantization tables
/// </summary>
public bool ItuSpecQuantization => !this.lumaQuantizationTable.HasValue && !this.chromaQuantizationTable.HasValue;
/// <summary>
/// Gets or sets the encoded quality.
/// </summary>

Loading…
Cancel
Save