diff --git a/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs b/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs index 0c9a0ca41..a7af76a65 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs @@ -13,7 +13,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components internal static class Quantization { /// - /// Threshold at which given luminance quantization table should not be considered 'standard'. + /// Threshold at which given luminance quantization table should be considered 'standard'. + /// Bigger the variance - more likely it to be a non-ITU complient table. /// /// /// Jpeg does not define either 'quality' nor 'standard quantization table' properties @@ -22,7 +23,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components public const double StandardLuminanceTableVarianceThreshold = 10.0; /// - /// Threshold at which given luminance quantization table should not be considered 'standard'. + /// Threshold at which given chrominance quantization table should be considered 'standard'. + /// Bigger the variance - more likely it to be a non-ITU complient table. /// /// /// Jpeg does not define either 'quality' nor 'standard quantization table' properties @@ -66,8 +68,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components 99, 99, 99, 99, 99, 99, 99, 99, }; - // https://github.com/ImpulseAdventure/JPEGsnoop/blob/9732ee0961f100eb69bbff4a0c47438d5997abee/source/JfifDecode.cpp#L4570-L4694 - public static void EstimateQuality(ref Block8x8F table, ReadOnlySpan target, out double quality, out double variance) + /// Ported from JPEGsnoop: + /// https://github.com/ImpulseAdventure/JPEGsnoop/blob/9732ee0961f100eb69bbff4a0c47438d5997abee/source/JfifDecode.cpp#L4570-L4694 + /// + /// Estimates jpeg quality based on quantization table. + /// + /// + /// This technically can be used with any given table but internal decoder code uses ITU spec tables: + /// and . + /// + /// Input quantization table. + /// Quantization to estimate against. + /// Variance threshold after which given table is considered non-complient. + /// Estimated quality + /// indicating if given table is target-complient + private static bool EstimateQuality(ref Block8x8F table, ReadOnlySpan target, double varianceThreshold, out double quality) { // 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. @@ -80,10 +95,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components 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'. + // According to jpeg creators, top of the line quality is 99, 100 is just a technical 'limit' will affect result filesize drastically. + // Quality=100 shouldn't be used in usual use case. quality = 100; - variance = 0; - return; + return true; } for (int i = 0; i < Block8x8F.Size; i++) @@ -115,7 +130,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components // If the variance is high, it is unlikely to be the case. sumPercent /= 64.0; sumPercentSqr /= 64.0; - variance = sumPercentSqr - (sumPercent * sumPercent); + double variance = sumPercentSqr - (sumPercent * sumPercent); // Generate the equivalent IJQ "quality" factor if (sumPercent <= 100.0) @@ -126,6 +141,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { quality = 5000.0 / sumPercent; } + + return variance <= varianceThreshold; } + + /// + /// Estimates jpeg luminance quality. + /// + /// Luminance quantization table. + /// Output jpeg quality. + /// indicating if given table is ITU-complient. + public static bool EstimateLuminanceQuality(ref Block8x8F luminanceTable, out double quality) + => EstimateQuality(ref luminanceTable, UnscaledQuant_Luminance, StandardLuminanceTableVarianceThreshold, out quality); + + /// + /// Estimates jpeg chrominance quality. + /// + /// Chrominance quantization table. + /// Output jpeg quality. + /// indicating if given table is ITU-complient. + public static bool EstimateChrominanceQuality(ref Block8x8F chrominanceTable, out double quality) + => EstimateQuality(ref chrominanceTable, UnscaledQuant_Chrominance, StandardChrominanceTableVarianceThreshold, out quality); } }