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; 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 // 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) 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 // 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 // Or when we go to full-int16 spectral code implementation and inject both tables as Block8x8.
double comparePercent; double comparePercent;
double sumPercent = 0; double sumPercent = 0;
double sumPercentSqr = 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++) for (int i = 0; i < Block8x8F.Size; i++)
{ {
float coeff = table[i]; float coeff = table[i];
int coeffInteger = (int)coeff; 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) if (coeffInteger != 0)
{ {
comparePercent = 100.0 * (table[i] / target[i]); comparePercent = 100.0 * (table[i] / target[i]);
} }
else 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; comparePercent = 999.99;
} }
sumPercent += comparePercent; sumPercent += comparePercent;
sumPercentSqr += comparePercent * 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 // Perform some statistical analysis of the quality factor
@ -111,11 +118,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
variance = sumPercentSqr - (sumPercent * sumPercent); variance = sumPercentSqr - (sumPercent * sumPercent);
// Generate the equivalent IJQ "quality" factor // Generate the equivalent IJQ "quality" factor
if (allOnes) if (sumPercent <= 100.0)
{
quality = 100;
}
else if (sumPercent <= 100.0)
{ {
quality = (200 - sumPercent) / 2; 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.Quality = other.Quality;
this.ColorType = other.ColorType; this.ColorType = other.ColorType;
this.lumaQuantizationTable = other.lumaQuantizationTable; this.lumaQuantizationTable = other.lumaQuantizationTable;
this.chromaQuantizationTable = other.chromaQuantizationTable; this.chromaQuantizationTable = other.chromaQuantizationTable;
this.LumaQuality = other.LumaQuality;
this.ChromaQuality = other.ChromaQuality;
} }
/// <summary> /// <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> /// <summary>
/// Gets or sets the encoded quality. /// Gets or sets the encoded quality.
/// </summary> /// </summary>

Loading…
Cancel
Save