Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

大图使用有内存暴增的问题 #12

Open
kukumaluCN opened this issue Dec 22, 2018 · 13 comments
Open

大图使用有内存暴增的问题 #12

kukumaluCN opened this issue Dec 22, 2018 · 13 comments

Comments

@kukumaluCN
Copy link

压缩一张20M以上的图片,瞬时内存暴增250M,有没有好的解决方式呢?

@lovelyelfpop
Copy link

应该是反复压缩内存没释放

@kukumaluCN
Copy link
Author

只是处理了一张高清的大图。

@GuoZhiQiang
Copy link
Owner

@kukumaluCN 把图片发给我,我调试一下,看这个 250M 是怎么增加的

@kukumaluCN
Copy link
Author

这张图在我们的内网测试环境,外网链接我没法提供,原图太大,图床不支持上传,可以邮件私发你。

这个是我写的测试demo,原图加载时,转data估算的大小:

这个是用您的方法压缩后测算的大小,以及对应的内存峰值。可以看到,原始图片读入内存瞬时峰值在230M左右,压缩峰值瞬时能到248M!我的测试机是7P。

我参考了微信的压缩机制,改用了自己的一套压缩算法,效果如下,相对优化了很多,但是还是有压缩瞬间的内存峰值:

测试的核心代码如下:

//鲁班压缩:
if (self.flag) {
    self.flag = NO;
    UIImage *image = [UIImage imageNamed:@"ada9dcab6fe0452488539bd31782a272.JPG"];
    //鲁班压缩
    NSData *data = [UIImage lubanCompressImage:image];
    UIImage *finalCompressImage = [UIImage imageWithData:data];
    self.imageView.image = finalCompressImage;
    
    CGFloat le = data.length/1024.0/1024.0;
    self.label.text = [NSString stringWithFormat:@"压缩后:%.0fx%.0f - %fMB", self.imageView.image.size.width, self.imageView.image.size.height, le];
}
else {
    self.flag = YES;
    self.imageView.image = [UIImage imageNamed:@"ada9dcab6fe0452488539bd31782a272.JPG"];
    
    NSData *data = UIImageJPEGRepresentation(self.imageView.image, 1.0);
    CGFloat le = data.length/1024.0/1024.0;
    self.label.text = [NSString stringWithFormat:@"原图:%.0fx%.0f - %fMB", self.imageView.image.size.width, self.imageView.image.size.height, le];
}
//微信压缩:
if (self.flag) {
    self.flag = NO;
    UIImage *image = [UIImage imageNamed:@"ada9dcab6fe0452488539bd31782a272.JPG"];
    //裁剪压缩,以1280为边界值动态裁剪
    UIImage *compressImage = [image wx_compressLargeImage];
    //二次压缩图像,防止裁剪后依旧为高清图
    NSData *data = UIImageJPEGRepresentation(compressImage, 0.5);
    UIImage *finalCompressImage = [UIImage imageWithData:data];
    self.imageView.image = finalCompressImage;
    
    CGFloat le = data.length/1024.0/1024.0;
    self.label.text = [NSString stringWithFormat:@"压缩后:%.0fx%.0f - %fMB", self.imageView.image.size.width, self.imageView.image.size.height, le];
}
else {
    self.flag = YES;
    self.imageView.image = [UIImage imageNamed:@"ada9dcab6fe0452488539bd31782a272.JPG"];
    
    NSData *data = UIImageJPEGRepresentation(self.imageView.image, 1.0);
    CGFloat le = data.length/1024.0/1024.0;
    self.label.text = [NSString stringWithFormat:@"原图:%.0fx%.0f - %fMB", self.imageView.image.size.width, self.imageView.image.size.height, le];
}

@kukumaluCN
Copy link
Author

其实最消耗内存的是UIImageJPEGRepresentation(self.imageView.image, 1.0);这个方法,取消之后,加载原始图片,基本没有那么高的瞬时内存尖峰。

@GuoZhiQiang
Copy link
Owner

@kukumaluCN [email protected] 另外我看了下,你测试的代码是为了较少峰值而牺牲了压缩质量,我个人认为这样压缩出来的大小并不是最理想的。

你测试的工程如果方便的话也发给我一下下。另外压缩这么大的图片,我还是第一次,也算是个机会看压缩超大图能不能造出来另外一种机制。

@kukumaluCN
Copy link
Author

关注内存峰值是必须的,因为我们项目的特殊性,需要兼容比较老的设备,运存512M那种,我们测试发现,瞬时内存峰值超过200M,就会造成crash,所以没办法,必须优化内存占用和内存峰值。
关于压缩质量,其实我做过对比,上面的那些图看不清楚,其实微信的压缩算法很优秀,压缩过后的大小能控制在400k左右,但是清晰度高很多,您压缩后的图片大小会小很多,但是清晰度不太够。
下面是对比,图片放大了5X的局部展示:

这个是微信的压缩算法,微信以1280为边界值做裁剪压缩处理,压缩系数0.5:

这个是微信的压缩算法,微信以1280为边界值做裁剪压缩处理,压缩系数0.1:

这个是您提供的压缩机制:

微信的压缩算法我参考了这个:iOS 图片压缩----微信图片处理策略,并做了算法优化,原理是一致的,关于压缩系数,取了0.5,这个产出和文中说的一致,和微信朋友圈的相当。
不过,这套算法依旧使用了UIImageJPEGRepresentation这个函数,这个是导致内存峰值的罪魁祸首,目前还没找到更好的方法,有人说微信还使用了ImageIO,算法如下,但是经过测算,内存占用不降反升。。。我也很想找到一个压缩大图的比较好的方案,很希望和您探讨。

+ (UIImage *)jxt_scaledImageWithData:(NSData *)data toSize:(CGSize)size
{
    if (!data) {
        return nil;
    }
    UIImage *thumbnail = nil;
    CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);
    if (sourceRef) {
        CGFloat maxPixelSize = MAX(size.width, size.height);
        NSDictionary *options = @{
                                  (__bridge id)kCGImageSourceCreateThumbnailFromImageAlways:(__bridge id)kCFBooleanTrue,
                                  (__bridge id)kCGImageSourceCreateThumbnailWithTransform:(__bridge id)kCFBooleanTrue,
                                  (__bridge id)kCGImageSourceThumbnailMaxPixelSize:[NSNumber numberWithFloat:maxPixelSize]
                                  };
        CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
        if (imageRef) {
            thumbnail = [UIImage imageWithCGImage:imageRef];
            CGImageRelease(imageRef);
        }
        CFRelease(sourceRef);
    }
    return thumbnail;
}

@kukumaluCN
Copy link
Author

我还发现,下面两个方式获取到的data的length,同一张图片,是有较大偏差的,有人说,第一个方法,系数要取0.7才可以。。。

NSData *data = UIImageJPEGRepresentation(self.imageView.image, 1.0);
NSData *data = [NSData dataWithContentsOfFile:retinaPath];

@kukumaluCN
Copy link
Author

图片已经发送至您的邮箱,期待进一步交流。

@GuoZhiQiang
Copy link
Owner

@kukumaluCN 好的好的👌。微信压缩的那个我之前看过也实现过,但 压缩后的大小我还是接受不了,清晰是很清晰,不适合多图上传的场景。于是实现了luban。在实际场景中,10张图片,用微信那个压缩后5M左右,luban压缩完1M左右,清晰度也还可以,当然了如果要更清晰的可以下载原图。

@GuoZhiQiang
Copy link
Owner

另外给你们产品经理商量一下,为什么有这么大的图片?

@GuoZhiQiang
Copy link
Owner

GuoZhiQiang commented Nov 22, 2019

对于一般场景为了避免使用 UIImageJPEGRepresentation 造成的内存暴涨 可以使用下面的方法进行压缩处理:

//imageURL 图片的本地存储地址; size 图片大小; scale 压缩比 0.0~1.0
- (UIImage* _Nullable)downsampleImageURL:(NSURL *)imageURL sampleSize:(CGSize)size scale:(CGFloat)scale
{
    CFDictionaryRef sourceOpt = (__bridge CFDictionaryRef)@{(NSString *)kCGImageSourceShouldCache: @NO};
    CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)imageURL, sourceOpt);
    if (!source) {
        NSLog(@"imageSource is Null!");
        return nil;
    }
    int maxDimension = MAX(size.width, size.height) * scale;
    CFDictionaryRef downsampleOpt = (__bridge CFDictionaryRef)@{
        (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
        (NSString *)kCGImageSourceShouldCacheImmediately : @YES ,
        (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
        (NSString *)kCGImageSourceThumbnailMaxPixelSize : @(maxDimension)};
    CGImageRef downsampleImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOpt);
    CFRelease(source);
    
    return [UIImage imageWithCGImage:downsampleImage];
}

在本例中使用 可以这样子:

    NSString *filePaht = [[NSBundle mainBundle] pathForResource:@"IMG_1998" ofType:@"JPG"];
    UIImage *imgData   = [UIImage imageWithContentsOfFile:filePaht];
    NSURL *urlPath = [NSURL fileURLWithPath:filePaht];
    self.imgCompressed    = [self downsampleImageURL:urlPath sampleSize:imgData.size scale:0.3];
    self.img_brower.image = _imgCompressed;

@kukumaluCN @lovelyelfpop

@zhaozzq
Copy link

zhaozzq commented May 17, 2022

   //裁剪压缩,以1280为边界值动态裁剪
   UIImage *compressImage = [image wx_compressLargeImage];

@kukumaluCN 您好,这里的微信压缩有开源的实现或者能提供一下代码吗?我想测试一下效果

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants