Skip to content

Saving JPEG Quality defaults to below 5 - ignoring default of 75 #845

Description

@Marcel0024

Prerequisites

  • I have written a descriptive issue title
  • I have verified that I am running the latest version of ImageSharp
  • I have verified if the problem exist in both DEBUG and RELEASE mode
  • I have searched open and closed issues to ensure it has not already been reported

Description

Saving certain Jpeg images without defining the JpegEnconder quality defaults to a quality below 5.
There's a few jpeg I've seem with this behavior, can't pin point an common trait. Examples further below.

Steps to Reproduce

using (var img = Image.Load("original.jpg"))
{
    using (FileStream outImage = File.Create("result.jpg"))
    {
        img.SaveAsJpeg(outImage);
    }
}

Results:

image

With Quality at 50:

using (var img = Image.Load("original.jpg"))
{
    using (FileStream outImage = File.Create("result.jpg"))
    {
        img.SaveAsJpeg(outImage, new JpegEncoder { Quality = 50 } );
    }
}

Results:

image

As you can see quality 50 produces a higher quality image.
Here are 3 examples images:
quality-loss-example.zip

I've managed to track it down to SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.QualityEvaluator#EstimateQuality method.
'Normal' jpeg this method returns a 75 default quality. My example images the method returns 3 or 2.
image

System Configuration

  • ImageSharp version: 1.0.0-beta0006
  • Environment (Operating system, version and so on): Windows 10 pro -Windows Server 2016
  • .NET Framework version: .Net Core 2.1

Activity

  1. Marcel0024 commented on Mar 9, 2019

    @Marcel0024
    Author

    Going down the rabbit hole, it seems ImageSharp does a wrong quality estimation based on the Quantization Table. The estimation is based on a formula of ImageMagick.

    Further digging i found this paper:
    Determining JPEG Image Standard Quality Factor from the Quantization Tables

    The code provided in the paper is what JpegSnoop uses to determine the quality factor. JpegSnoop gives me a 98.25 approx quality factor on the same image i used above
    image
    I got a 3 in ImageSharp (while the quality is clearly high)

    The paper is also mention the method used in ImageMagick is fairly inaccurate
    image

    Is this where my problem lies? Is it worth further investigation?

    Edit:
    Using ImageMagick directly produces these qualities:
    image
    Is it just wrongly implemented in ImageSharp?

  2. JimBobSquarePants commented on Mar 9, 2019

    @JimBobSquarePants
    Member

    We based ours on ImageMagick but I did the port and don’t read C well at all. I’ve most likely messed it up so please have a look if you can.

  3. dlemstra commented on Mar 9, 2019

    @dlemstra
    Member

    I can take a look at this when I am back home in two weeks. Might be possible we made some changes in ImageMagick since this was added to ImageSharp.

  4. added this to the 1.0.0-rc1 milestone on Mar 9, 2019
  5. JimBobSquarePants commented on Mar 15, 2019

    @JimBobSquarePants
    Member

    @dlemstra Just compared the current master source to the point in time source i ported from. It's identical so I must have missed something.

  6. Marcel0024 commented on Mar 15, 2019

    @Marcel0024
    Author

    I've compared the code to the ImageMagick one. All the logic and table seems the same.

    The quality here is set to 3, and then it always keeps hitting continue on line 109, until it exits the method with quality 3.

    The only thing i don't understand is ImageMagic's version of the quality variable here what does quantval do? its not a c syntax no? does it only get the value at that position like its translated in c#?

  7. dlemstra commented on Mar 15, 2019

    @dlemstra
    Member

    There is a difference in the code. The sum does not check if the quantum tables are null. But that does not seem to be the case here.

  8. JimBobSquarePants commented on Mar 15, 2019

    @JimBobSquarePants
    Member

    @dlemstra Good spot! That should simply mean we're adding a bunch of zeros we don't need to but I've just realized Block8x8F doesn't implement IEquatable<T> so we might be getting issues there.

    @Marcel0024 By my understanding they are simply the tables themselves.

    jpeg_info->quant_tbl_ptrs[0]->quantval[53] is the 53rd index of the first quantization table.

  9. JimBobSquarePants commented on Mar 15, 2019

    @JimBobSquarePants
    Member

    @dlemstra Confirmed. Test example1.jpg now returns 99. I'll push a fix.

  10. Marcel0024 commented on Mar 15, 2019

    @Marcel0024
    Author

    Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions