iOS UIImage圖片縮放性能對比

前言

  • 最近做挺多的圖片處理,透視、縮放、拼接、裁剪、效果等等,那麼今天就先來詳細對比一下系統API處理縮放的性能,這樣也好方便選擇那種更優的方式來處理
GitHub地址:KJExtensionHandler
大致分爲以下五種API:
  1. UIKit,畫布 drawInRect:UIGraphicsGetImageFromCurrentImageContext
  2. CoreGraphics / Quartz 2D,位圖上下文CGContextScaleCTMCGContextDrawImage
  3. ImageIO,創建省略圖 CGImageSourceCreateWithDataCGImageSourceCreateThumbnailAtIndex
  4. CoreImage,濾鏡 CILanczosScaleTransform
  5. Accelerate,CGBitmapContextCreateCGContextDrawImage

API

/// UIKit方式
- (UIImage*)kj_UIKitChangeImageSize:(CGSize)size;
/// Quartz 2D
- (UIImage*)kj_QuartzChangeImageSize:(CGSize)size;
/// ImageIO
- (UIImage*)kj_ImageIOChangeImageSize:(CGSize)size;
/// CoreImage
- (UIImage*)kj_CoreImageChangeImageSize:(CGSize)size;
/// Accelerate
- (UIImage*)kj_AccelerateChangeImageSize:(CGSize)size;
UIKit

畫布的形式,使用臨時圖形上下文來渲染縮放

- (UIImage*)kj_UIKitChangeImageSize:(CGSize)size{
    UIGraphicsBeginImageContext(size);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
CoreGraphics / Quartz 2D

這個其實和UIKit差不多

- (UIImage*)kj_QuartzChangeImageSize:(CGSize)size{
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0.0, size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), self.CGImage);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
ImageIO

使用導入 #import <ImageIO/ImageIO.h>
CGImageSource的鍵值說明

  • kCGImageSourceCreateThumbnailWithTransform - 設置縮略圖是否進行Transfrom變換
  • kCGImageSourceCreateThumbnailFromImageAlways - 設置是否創建縮略圖,無論原圖像有沒有包含縮略圖,默認kCFBooleanFalse,影響 CGImageSourceCreateThumbnailAtIndex 方法
  • kCGImageSourceCreateThumbnailFromImageIfAbsent - 設置是否創建縮略圖,如果原圖像有沒有包含縮略圖,則創建縮略圖,默認kCFBooleanFalse,影響 CGImageSourceCreateThumbnailAtIndex 方法
  • kCGImageSourceThumbnailMaxPixelSize - 設置縮略圖的最大寬/高尺寸 需要設置爲CFNumber值,設置後圖片會根據最大寬/高 來等比例縮放圖片
  • kCGImageSourceShouldCache - 設置是否以解碼的方式讀取圖片數據 默認爲kCFBooleanTrue,如果設置爲true,在讀取數據時就進行解碼 如果爲false 則在渲染時才進行解碼
- (UIImage*)kj_ImageIOChangeImageSize:(CGSize)size{
    NSData *date = UIImagePNGRepresentation(self);
    CGFloat max = size.width;
    if (max < size.height) max = size.height;
    CFDictionaryRef dicOptionsRef = (__bridge CFDictionaryRef) @{(id)kCGImageSourceCreateThumbnailFromImageIfAbsent : @(YES),
                                                                 (id)kCGImageSourceThumbnailMaxPixelSize : @(max),
                                                                 (id)kCGImageSourceShouldCache : @(YES),};
    CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)date, nil);
    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(src, 0, dicOptionsRef);
    UIImage *newImage = [UIImage imageWithCGImage:imageRef];
    if (imageRef != nil) CFRelease(imageRef);
    CFRelease(src);
    return newImage;
}
CoreImage

濾鏡的處理方式,綜合對比下來,這種是最差
使用導入 #import <CoreImage/CoreImage.h>

- (UIImage*)kj_CoreImageChangeImageSize:(CGSize)size{
    CIImage *ciImage = [CIImage imageWithCGImage:self.CGImage];
    CGFloat scale = fminf(size.height/self.size.height, size.width/self.size.width);
    NSDictionary *dict = @{kCIInputScaleKey:@(scale),kCIInputAspectRatioKey:@(1.),kCIInputImageKey:ciImage};
    CIFilter *filter = [CIFilter filterWithName:@"CILanczosScaleTransform" withInputParameters:dict];
    UIImage *newImage = [UIImage imageWithCIImage:filter.outputImage];
    return newImage;
}
Accelerate

使用CPU的矢量處理器處理大圖像
使用導入 #import <Accelerate/Accelerate.h>

- (UIImage*)kj_AccelerateChangeImageSize:(CGSize)size{
    const size_t width = size.width, height = size.height;
    const size_t bytesPerRow = width * 4;
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
    int bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
#else
    int bitmapInfo = kCGImageAlphaPremultipliedLast;
#endif
    CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, space, bitmapInfo);
    CGColorSpaceRelease(space);
    if (!bmContext) return nil;
    CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), self.CGImage);
    UInt8 * data = (UInt8*)CGBitmapContextGetData(bmContext);
    if (!data){
        CGContextRelease(bmContext);
        return nil;
    }
    CGImageRef imageRef = CGBitmapContextCreateImage(bmContext);
    UIImage *newImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGContextRelease(bmContext);
    return newImage;
}

測試示例

- (void)viewDidLoad {
    [super viewDidLoad];
    CGFloat x,y;
    CGFloat sp = kAutoW(10);
    CGFloat w = (kScreenW-sp*2)/2.;
    CGFloat h = (kScreenH-4*sp-kSTATUSBAR_NAVIGATION_HEIGHT)/3;
    NSArray *names = @[@"原圖",@"UIKit",@"Quartz 2D",@"ImageIO",@"CoreImage",@"Accelerate"];
    UIImage *image = kGetImage(@"xxsf");
    CGSize size = CGSizeMake(image.size.width/4.3, image.size.height/4.3);
    for (int k=0; k<names.count; k++) {
        x = k%2*(w+sp)+sp/2;
        y = k/2*(h+sp)+sp+kSTATUSBAR_NAVIGATION_HEIGHT;
        UILabel *label = [UILabel kj_createLabelWithText:names[k] FontSize:16 TextColor:UIColor.orangeColor];
        label.frame = CGRectMake(x, y, w, 20);
        [self.view addSubview:label];
        UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(x, y+25, w, h-25)];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        imageView.backgroundColor = [UIColor.orangeColor colorWithAlphaComponent:0.1];
        [self.view addSubview:imageView];
        if (k==0) {
            imageView.image = image;
            NSData *date = UIImagePNGRepresentation(image);
            NSLog(@"OriginalData:%lu", (unsigned long)date.length);
        }else if (k==1) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_UIKitChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"UIKitTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==2) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_QuartzChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"QuartzTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==3) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_ImageIOChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"ImageIOTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==4) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_CoreImageChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"CoreImageTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==5) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_AccelerateChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"AccelerateTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }
    }
}

png圖片耗時對比,等比縮放

------- 🎈 給我點贊 🎈 -------
編譯時間:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:38
打印信息:OriginalData:466290

------- 🎈 給我點贊 🎈 -------
編譯時間:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:43
打印信息:UIKitTime:0.006449,Data:36902

------- 🎈 給我點贊 🎈 -------
編譯時間:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:49
打印信息:QuartzTime:0.005489,Data:36901

------- 🎈 給我點贊 🎈 -------
編譯時間:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:55
打印信息:ImageIOTime:0.058294,Data:38576

------- 🎈 給我點贊 🎈 -------
編譯時間:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:61
打印信息:CoreImageTime:0.139110,Data:140840

------- 🎈 給我點贊 🎈 -------
編譯時間:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:67
打印信息:AccelerateTime:0.005719,Data:35748

JPG耗時比較

------- 🎈 給我點贊 🎈 -------
編譯時間:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:38
打印信息:OriginalData:183457

------- 🎈 給我點贊 🎈 -------
編譯時間:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:43
打印信息:UIKitTime:0.003134,Data:19614

------- 🎈 給我點贊 🎈 -------
編譯時間:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:49
打印信息:QuartzTime:0.001823,Data:19632

------- 🎈 給我點贊 🎈 -------
編譯時間:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:55
打印信息:ImageIOTime:0.016345,Data:17158

------- 🎈 給我點贊 🎈 -------
編譯時間:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:61
打印信息:CoreImageTime:0.134511,Data:72229

------- 🎈 給我點贊 🎈 -------
編譯時間:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行號:67
打印信息:AccelerateTime:0.001897,Data:18922

綜合耗時比較

類型 UIKit CoreGraphics ImageIO CoreImage Accelerate
PNG 0.006449 0.005489 0.058294 0.139110 0.005719
JPG 0.003134 0.001823 0.016345 0.134511 0.001897

Accelerate 壓縮出來質量最小
ImageIO 肉眼感覺清晰度最高
ImageIO 和 CoreImage 只能做等比縮放

感興趣的朋友可以換不同尺寸的圖片多次測試,這樣就可得到每種方式在不同區域的性能對比

備註:本文用到的部分函數方法和Demo,均來自三方庫KJExtensionHandler,如有需要的朋友可自行pod 'KJExtensionHandler'引入即可

圖片縮放性能對比介紹就到此完畢,後面有相關再補充,寫文章不容易,還請點個小星星傳送門

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章