SDWebImage / SDWebImageWebPCoder

A WebP coder plugin for SDWebImage, use libwebp
MIT License
217 stars 84 forks source link

Encoding Results in Washed-Out Colors #89

Closed lucasfischer closed 10 months ago

lucasfischer commented 10 months ago

macOS: 14.1 Xcode 15.0 SDWebImage 5.18.4 SDWebImageWebPCoder 0.14.1

I tried to implement this package into an existing macOS app, when I noticed that the conversion results in washed-out colors. I tried this with converting PNGs and JPGs to WebP so far. Are there any options to preserve the original image colors?

Tried many different option combinations, but the images in the ZIP were created with .encodeCompressionQuality = 0.8 and an NSImage.

Thank you for working on this project. I hope you are having a nice day.

examples.zip

dreampiggy commented 10 months ago

conversion results in washed-out colors

Seems Bug of color space decoding/encoding. Any demo ?

And I remember, the encode of WebP does not support some color format. In history we use the vImage (vImageConvert_AnyToAny) to convert like ARGB8 -> RGB8 (BGRA8 -> RGB8) to use libwebp. But after that we revert back to use libwebp's own processing. Seems bug maybe related to this.

dreampiggy commented 10 months ago

Seems not bug of color format (which means, RGBA or ARGB or BGRA, the order)

Seems bug of color space, like sRGB, P3, PQ

dreampiggy commented 10 months ago

Where's your source PNG image ? (You means, you convert PNG to JPEG and WebP. The converted JPEG and WebP use different color space ?)

dreampiggy commented 10 months ago

image

I guess this cause issue. The color space should not use device RGB

Should use CGImage.colorspace. Which means, input image (from PNG) may contains color space.

dreampiggy commented 10 months ago

I'm sure by changing the color space to use CGImage's color space, the issue been fixed.

pexels-egor-kamelev-920163.zip

See:

image

lucasfischer commented 10 months ago

Thank you for this quick reply!

Seems like your instinct with it being an issue with the color space has been spot on. I downloaded your ZIP file and compared the files in them. It looks like it's fixed!

You are so fast. Thank you so much for looking into this. ❤️

dreampiggy commented 10 months ago

Just use that PR's commits and test by yourself.

Then I'll merge in and release a patch version

lucasfischer commented 10 months ago

I just tried the PR and for most of my tests it spits out perfect results. 🤩

Although, the problem still persists for all HEIC files from my iPhone and for one of the PNGs I tested.

Do you have another quick idea?

examples.zip

dreampiggy commented 10 months ago

For you case, actually we should not only ensure the WebP encoding process, also need to ensure The decoded image, like HEIF, use the correct color space.

Because it's "Transcoding", right ?

Actually, it do 3 steps:

  1. Decode original data into image buffer (will this contains bug? HEIF Decoder ? PNG Decoder)
  2. Encode the image buffer into new data (will this contains bug? WebP Encoder ?) // -> I fixed this
  3. Decode the new data into new image buffer and render (will this contains bug? WebP Decoder ?)

You want the image buffer in 1. match the new image buffer in 3., so all of thesse steps need to check again

dreampiggy commented 10 months ago

How do you get the original input image, using the SDWebImage's API (NSImage.sd_imageWithData) or AppKit API (like NSImage(data:)` ?

Which means, which decoder of input image you used.

dreampiggy commented 10 months ago

png.zip

- (void)viewDidLoad {
    [[SDImageCodersManager sharedManager] addCoder:[SDImageWebPCoder sharedCoder]];

    NSString *inputName = @"png/before.png";
    NSString *inputPath = [NSBundle.mainBundle pathForResource:inputName ofType:nil inDirectory:@"examples"];
    NSData *inputData = [NSData dataWithContentsOfFile:inputPath];
    UIImage *inputImage = [[UIImage alloc] initWithData:inputData];

    NSData *outputData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:inputImage format:SDImageFormatWebP options:nil];
    [outputData writeToFile:@"/tmp/output.webp" atomically:YES];
}

Using AppKit's PNG decoder seems work well. Is this SDWebImage's PNG decoder's bug ?

dreampiggy commented 10 months ago

output-PNG-decoder.zip

Even use the UIImage sd_imageWithData: to decode the PNG from your input, the result is the same

I guess is your App's business code, which does not give the correct input (before feeding into SDImageWebPCoder), check your logic 's bug?

You can even use LLDB debugger, to visualize the image object (NSImage) in memory and preview.app, is that match what you see in Mac's Finder ?

dreampiggy commented 10 months ago

And one intersting thing is that your after.webp is only 42KB, but mine using the default options produce 170KB, so I think maybe there are some other thing effect the result.

dreampiggy commented 10 months ago

But whatever, I guess The second issue you report, maybe not related to SDWebImageWebPCoder itself

It's a bug from either your business code, or the input format's decoder (like SDWebImage, which use Apple's ImageIO for PNG decode)

Or even Apple's bug, like Apple's PNG decode contains bug on macOS 14.0. I don't know whether they fix that bug or not. (See: https://github.com/SDWebImage/SDWebImage/issues/3605). You can use macOS 13 or iPhone Simulator iOS 16 to test this behavior first ?


Anyway, for now, I just release SDWebImageWebPCoder with new version. 0.14.2 For your second issue, please check my suggestions one by one. If any new idea found, comment here and maybe I can provide help

lucasfischer commented 10 months ago

Thank you for your quick response as always. Sorry it took me so long to get back to you.

Sorry for the confusion. My images were a bit smaller, because I used .encodeCompressionQuality.

I now use the following code to use this library:

static func convertToWebPData(source: URL) throws -> Data {
        let image = NSImage(byReferencing: source)

        let data = SDImageWebPCoder.shared.encodedData(with: image, format: .webP)
        guard let data else {
            throw ImageConverterError.cannotConvertToWebP
        }

        return data
}

Thank you for shipping 0.14.2 with improvements.

I checked the last 2 zip files you attached here and see the same issue on the photo with the woman in the desert. If you look closely, you can see that the colors of her gold chain are washed-out and duller than in the original. But 0.14.2 is already a huge improvement.

The problem still persists when I try to convert photos taken with my iPhone camera. Colors get washed-out and dull. Maybe you have another idea?

examples.zip

I hope you are having a nice week so far.