facebookincubator / spectrum

A client-side image transcoding library.

Home Page:https://libspectrum.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SpectrumKit not working with screenshot images in iOS 13

rahulvyas opened this issue · comments

I have following version of spectrum
SpectrumCore (1.1.0)
SpectrumKit (1.1.0)
mozjpeg (3.3.2)
spectrum-folly (2019.01.21.00)

When I try to compress a screenshot I'm getting a plain white image. I'm not getting any error. I'm testing on iPhone XR iOS 13.1.2.

Attaching some input images and some screenshot of what I'm getting as output.

Skype_Picture_2020_09_04T05_51_17_725Z
IMG_1070

I'm not getting any kind of error when compressing these images just plain white image I'm getting

Screenshot 2020-09-04 at 11 10 39 AM
Screenshot 2020-09-04 at 11 11 14 AM

This is my image picking code

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
   NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
   if ([mediaType isEqualToString:@"public.image"]){
        UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
        UIImage *transcoded = [self transcodeImage:image];
   }
}


- (UIImage*)transcodeImage:(UIImage*)image {
    
    FSPSpectrum *spectrum = [[FSPSpectrum alloc] initWithPlugins:@[[FSPJpegPlugin new]]
    configuration:nil];
    
    FSPEncodeRequirement *encodeRequirement =
        [FSPEncodeRequirement encodeRequirementWithFormat:FSPEncodedImageFormat.jpeg
                                                     mode:FSPEncodeRequirementModeLossy
                                                  quality:70];
    
    FSPConfiguration * configuration = [[FSPConfiguration alloc]init];

    FSPTransformations *transformations = [FSPTransformations new];
    transformations.resizeRequirement =
        [[FSPResizeRequirement alloc] initWithMode:FSPResizeRequirementModeExactOrSmaller
                                        targetSize:CGSizeMake(1024, 1024)];

    FSPEncodeOptions *options =
        [FSPEncodeOptions encodeOptionsWithEncodeRequirement:encodeRequirement
                                             transformations:transformations
                                                    metadata:nil
                                               configuration:configuration
                         outputPixelSpecificationRequirement:nil];

    NSError *error;
    FSPResultData *result = [spectrum encodeImage:image options:options error:&error];
    if (result.result.didSucceed && result.data) {
        UIImage* image = [UIImage imageWithData:result.data];
        if (image) {
            return image;
        }
    }
    return image;
}

Does anyone knows any fix. I have to release the app on store and it's the only issue I'm stuck with.

I've tried same images on facebook and these are working perfectly.

Edit : Updating targetSize:CGSizeMake(2048, 2048) in FSPResizeRequirement produces the below image

Screenshot 2020-09-04 at 12 06 37 PM

Edit 2 : Updating targetSize:CGRectZero in FSPResizeRequirement always gives error and I'm not able to compress the image

Here is the error log

Printing description of error:
Error Domain=com.facebook.spectrum Code=255 "(null)" UserInfo={com.facebook.spectrum.error-name=scalingX > 0, com.facebook.spectrum.error-location=facebook::spectrum::core::proc::ScalingBlockImpl::ScalingBlockImpl(const image::pixel::Specification &, const image::Size &, const image::Size &):62}

Edit 3 : Same code works fine in iPhone 6 (iOS 12.4.6)

@wizh @cuva @diegosanchezr can you look into it ?

Are the contributors still active in this library ?

Hi @rahulvyas

We are sorry for the delay.

The issue is connected with the fact that the images are 16-bits per channel and we are able to reproduce it on your images, however we are getting into the error in this line

SPECTRUM_ERROR_FORMAT_IF(bitsPerComponent != 8, "unsupported_image_pixel_spec_rgb_bits_per_component", "%d", bitsPerComponent);
so not sure why you are getting a white screen instead.

A way for you to mitigate this will be to use an image with 8-bit per channel instead.

We currently don't support images with different number of bits per component but we would be very happy to welcome contributions in form of PRs if you'd like to add that enhancement.

@zmroczek Thanks for the heads Up. However if you read my whole thread I'm getting different results based on TargetSize as I've mentioned. Also sometimes I get exception but it's not what you're telling. I request you to please go through the whole question again. As far as PR's , I am not much into image manipulations and C++. It would be great if someone experienced work on it. Isn't there any active contributor who can fix this ?

@zmroczek I wonder how Facebook able to compress the same image properly ? Isn't the Facebook iOS app uses spectrum itself ?

Hi @rahulvyas

First of all, I'd like to explain that we found why you are not getting an exception but a distorted result instead - it's because you're using an old version of Spectrum (1.1.0) which didn't have the code in place for throwing an error that I pointed out above. So if you switch to the newest version you will be able to catch the error as well instead of displaying those broken results.

The reason why those screenshots are causing problems might be connected to this specific iOS version saving screenshots as 16bits per channel instead of 8 and therefore you might have started getting the problem because of that.

Currently we are not supporting images with different number of bits per channel than 8 on Spectrum for iOS but we are investigating on fixing that.

@zmroczek Here is my podfile and podfile.lock enteries

Podfile
pod 'SpectrumKit/Plugins/Jpeg'

Podfile.lock

- spectrum-folly (2019.01.21.00)
  - SpectrumCore/Base (1.1.0):
    - spectrum-folly (~> 2019.01.21.00)
  - SpectrumCore/Plugins/Jpeg (1.1.0):
    - mozjpeg (= 3.3.2)
    - spectrum-folly (~> 2019.01.21.00)
    - SpectrumCore/Base (= 1.1.0)
  - SpectrumKit/Base (1.1.0):
    - spectrum-folly (~> 2019.01.21.00)
    - SpectrumCore/Base
  - SpectrumKit/Plugins/Jpeg (1.1.0):
    - spectrum-folly (~> 2019.01.21.00)
    - SpectrumCore/Plugins/Jpeg
    - SpectrumKit/Base (= 1.1.0)

Is there any specific version you want me to upgrade to ? If yes please suggest.

Spectrum 1.2 was released back in June, and includes the check mentioned above.

@AurelC2G, @zmroczek I wonder why I was getting different results based on the target size value ? Anyone have any idea about that ?

@rahulvyas I'm suspecting that this is due to bytes per pixel in image specification not being passed correctly for those images when we were not throwing an error in the older version of Spectrum you're using:

+ (instancetype)imagePixelSpecificationFromImage:(UIImage *)image

Possibly this affects how the output image looked like but we need to dig deeper into that.

Thank you for providing this repro case, we will use it for investigating the solution for this enhancement. If you'd like to dig deeper nevertheless and contribute you are also very welcome to do that.

@zmroczek I wonder how facebook able to compress the same image properly ? aren't the facebook iOS app uses spectrum itself ?

@zmroczek Anyone knows about this ?

@rahulvyas I'm suspecting that this is due to bytes per pixel in image specification not being passed correctly for those images when we were not throwing an error in the older version of Spectrum you're using:

+ (instancetype)imagePixelSpecificationFromImage:(UIImage *)image

Possibly this affects how the output image looked like but we need to dig deeper into that.

Thank you for providing this repro case, we will use it for investigating the solution for this enhancement. If you'd like to dig deeper nevertheless and contribute you are also very welcome to do that.

Any updates on this ?

Hi @rahulvyas,

We're still looking into adding the support for images with other values in bits per component than 8 in the encoding function.

In the meantime to get the correct output, you could try using transcodeImage function rather than encodeImage by passing the input file directly there rather than decoded bitmap (as in the encoding function you're currently using). Let us know if this fixes the issue for you.

Thanks @zmroczek. I'll change the code as you've suggested. Thanks for your help.

Hello!
For anyone also having this issue I fixed this problem by converting the Images to have 8bit depth, using the Apple Accelerate Framework. This code works on IOS 13 and up, don't forget to import Accelerate.

private static func converTo8BitDepth(image: UIImage) -> UIImage? {
     
     guard (image.cgImage?.bitsPerPixel != 32) else {
         return image
     }
     
     guard let cgImage = image.cgImage,
           let sourceImageFormat = vImage_CGImageFormat(cgImage: cgImage),
           let destImageFormat = vImage_CGImageFormat(bitsPerComponent: 8, bitsPerPixel: 32, colorSpace: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue), renderingIntent: .defaultIntent) else {
               // Handle error.
               return nil
           }
     
     guard let sourceBuffer = try? vImage_Buffer(cgImage: cgImage),
           var rgbDestBuffer = try? vImage_Buffer(width: Int(sourceBuffer.width), height: Int(sourceBuffer.height), bitsPerPixel: destImageFormat.bitsPerPixel) else {
               // Handle error.
               return nil
           }
     defer {
         sourceBuffer.free()
         rgbDestBuffer.free()
     }
     
     do {
         let toRgbConverter = try vImageConverter.make(sourceFormat: sourceImageFormat, destinationFormat: destImageFormat)
         
         try toRgbConverter.convert(source: sourceBuffer, destination: &rgbDestBuffer)
     } catch {
         // Handle error.
         print(error.localizedDescription)
     }
     if let cgImage = try? rgbDestBuffer.createCGImage(format: destImageFormat) {
         return UIImage(cgImage: cgImage)
     } else {
         // Handle error.
         return nil
     }
 }