圖片壓縮算法

最近項目中要做圖片壓縮,由于之前沒有接觸過,所以在做的過程中遇到了幾個問題前塔,在這做下整理,希望看到的同學(xué)遇到相似的問題可以有點啟發(fā)承冰。

1.如何獲取圖片大小

我們一開始定的策略是上傳圖片時华弓,20M以上不讓選擇,1-20M以內(nèi)壓縮60%困乒,1M以內(nèi)不壓縮(由于之前都沒有接觸過寂屏,也沒有調(diào)查微信、微博等主流App的壓縮算法,所以暫時定了這個壓縮比例)迁霎。
想要做壓縮吱抚,首先需要獲取圖片的大小,我們知道考廉,在iOS上有兩個獲取圖片大小的方法秘豹,UIImagePNGRepresentation和UIImageJPEGRepresentation。
UIImagePNGRepresentation我們在這里不過多贅述昌粤。

1.為什么不提UIImagePNGRepresentation?

回復(fù):據(jù)說這個讀取圖片的大小會比較大既绕,因為是png格式,讀取的內(nèi)容會有多圖層的的問題導(dǎo)致讀取的會顯示比較大涮坐,而且比較耗時間凄贩。網(wǎng)上有人做過測試:同樣是讀取攝像頭拍攝的同樣景色的照片,UIImagePNGRepresentation() 返回的數(shù)據(jù)量大小為199K袱讹,而 UIImageJPEGRepresentation(UIImage* image, 1.0) 返回的數(shù)據(jù)量大小只為 140KB疲扎,比前者少了50多KB。如果對圖片的清晰度要求不高廓译,還可以通過設(shè)置 UIImageJPEGRepresentation 函數(shù)的第二個參數(shù)评肆,大幅度降低圖片數(shù)據(jù)量。
如果還有什么問題可以繼續(xù)百度非区,這里不進行過多贅述瓜挽。

2.關(guān)于UIImageJPEGRepresentation,首先第一個參數(shù)是我們都知道的圖片image征绸,但是第二個參數(shù)scale久橙,一個0~1的浮點型比率,你以為0就是沒有管怠,壓縮到0b大小淆衷,1.0就是原圖大小渤弛?答案是祝拯?:錯,首先你的圖片的大小是根據(jù)(圖片的寬圖片的高每一個色彩的深度她肯,這個和手機的系統(tǒng)有關(guān)佳头,一般是4)。你的圖片只會按照你的手機像素的分辨率[UIScreen mainScreen].scale來讀取值晴氨。其次康嘉,第二個參數(shù)蘋果官方并沒有明確說明這個參數(shù)的具體意義。對于大圖片來說籽前,即使你的scale選的很小亭珍,比如:0.0000000(n個0)001敷钾,但是得到的結(jié)果還是很大,這里做了一個實驗:一個10M左右的圖片肄梨,處理后大小為2M多阻荒。有點像是“壓不動”的感覺。當然如果是小圖片的話那就是沒問題峭范,能滿足你的希望的壓縮到的大小财松。

既然兩種方式都不可行,那我們應(yīng)該如何獲取纱控。通過閱讀TZImagePicker的源碼,發(fā)現(xiàn)他是用以下方法獲取的菜秦。

- (void)getOriginalDataFromSource:(PHAsset *)source completion:(void (^)(NSData *data))completion{
    PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
    options.resizeMode = PHImageRequestOptionsResizeModeFast;
    options.networkAccessAllowed = YES;
    
    [[PHImageManager defaultManager] requestImageDataForAsset:source options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
        completion(imageData);
    }];
}

那這個PHAsset又是什么東西甜害?

查看它的定義:

A representation of an image, video, or Live Photo in the Photos library.

原來,它就是我們相冊中圖片球昨、視頻的展現(xiàn)方式尔店。所以我們可以通過上面的方法獲取到圖片的原始大小,做為對比主慰,UIImageJPEGRepresentation(image, 1.0)獲取到的也是jpeg壓縮后的圖片大小嚣州。

所以,我們需要在相冊中獲取該圖片的大泄猜荨(如果是相機拍照獲取的圖片该肴,需要先保存到相冊,然后再通過該方式獲取大忻瓴弧)

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    [picker dismissViewControllerAnimated:YES completion:nil];
    UIImage *image = nil;
    if (_isEditedImage) {
        image = [info objectForKey:UIImagePickerControllerEditedImage];
    } else {
        image = [info objectForKey:UIImagePickerControllerOriginalImage];
    }
    
    WS(weakSelf)
    [[TZImageManager manager]savePhotoWithImage:image completion:^(PHAsset *asset, NSError *error) {
        [weakSelf getOriginalDataFromSource:asset completion:^(NSData *data) {
            if (weakSelf.imageBlock) {
                GKTImageModel *model = [GKTImageModel new];
                model.image = image;
                model.assert = asset;
                model.imageData = data;
                model.isOriginal = YES;
                weakSelf.imageBlock(@[model]);
            }
        }];
 
        
    }];
}

保存的代碼如下:

- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(PHAsset *asset, NSError *error))completion {
    [self savePhotoWithImage:image location:nil completion:completion];
}

- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(PHAsset *asset, NSError *error))completion {
    __block NSString *localIdentifier = nil;
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
        localIdentifier = request.placeholderForCreatedAsset.localIdentifier;
        if (location) {
            request.location = location;
        }
        request.creationDate = [NSDate date];
    } completionHandler:^(BOOL success, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (success && completion) {
                PHAsset *asset = [[PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil] firstObject];
                completion(asset, nil);
            } else if (error) {
                NSLog(@"保存照片出錯:%@",error.localizedDescription);
                if (completion) {
                    completion(nil, error);
                }
            }
        });
    }];
}

2.拿到圖片大小匀哄,然后就可以進行壓縮了

關(guān)于壓縮,網(wǎng)絡(luò)上的說法也是很多(安卓好像有[Luban算法]雏蛮,iOS沒找到對應(yīng)的)涎嚼。這里只說純粹的質(zhì)量壓縮。
一般采取的都是二分法壓縮挑秉。

imageData = UIImageJPEGRepresentation(image, compression);
    if (imageData.length < fImageBytes) {
        //二分最大10次法梯,區(qū)間范圍精度最大可達0.00097657;最大6次犀概,精度可達0.015625
        for (int i = 0; i < 6; ++i) {
            compression = (max + min) / 2;
            imageData = UIImageJPEGRepresentation(image, compression);
            //容錯區(qū)間范圍0.9~1.0
            if (imageData.length < fImageBytes * 0.9) {
                min = compression;
            } else if (imageData.length > fImageBytes) {
                max = compression;
            } else {
                break;
            }
        }
        
        block(imageData);
        return;
    }

以上方法可以正常壓縮圖片立哑,但是如果用戶一下選取了9張圖片進行壓縮的話,就會內(nèi)存暴增阱冶,網(wǎng)上找到了別人寫的方式刁憋,其基本原理就是以下一句話:

使用ImageIO接口,避免在改變圖片大小的過程中產(chǎn)生臨時的bitmap木蹬,就能夠在很大程度上減少內(nèi)存的占有從而避免由此導(dǎo)致的app閃退問題至耻。

這里直接附原文地址:iOS優(yōu)秀的圖片壓縮處理方案

后來發(fā)現(xiàn)用這種方式壓縮的圖片若皱,基本接近設(shè)置的壓縮比(誤差大約在10%以內(nèi),針對1-20M圖片)尘颓,但是發(fā)現(xiàn)按40%壓縮后的圖片走触,上傳依然很慢。
這時疤苹,我們才想到研究下微信的壓縮互广,于是發(fā)現(xiàn)了別人的壓縮都是尺寸壓縮+質(zhì)量壓縮,而質(zhì)量壓縮卧土,都是壓縮比例很大惫皱,于是,我們重新定義了一套壓縮策略尤莺,直接用UIImageJPEGRepresentation進行壓縮旅敷。

3.新的圖片壓縮策略:

圖片壓縮策略.png

以下是代碼實現(xiàn):

+(void)zipNSDataWithImage:(UIImage *)sourceImage imageBlock:(ImageBlock)block{
    //進行圖像尺寸的壓縮
    CGSize imageSize = sourceImage.size;//取出要壓縮的image尺寸
    CGFloat width = imageSize.width;    //圖片寬度
    CGFloat height = imageSize.height;  //圖片高度
    
    CGFloat scale = height/width;
    //0.寬高比例大于8
    if (scale > 8.0 || scale < 1/8.) {
        if (scale > 8.0) {
            if (width > 1080) {
                width = 1080;
                height = width * scale;
            }else {
                //不壓縮
            }
        }else {
            if (height > 1080.) {
                height = 1080;
                width = height / scale;
            }else {
                //不壓縮
            }
        }
        //1.寬高大于1080(寬高比不按照2來算,按照1來算)
    }else if (width>1080 && height>1080) {
        if (height > width) {
            CGFloat scale = height/width;
            width = 1080;
            height = width*scale;
        }else{
            CGFloat scale = width/height;
            height = 1080;
            width = height*scale;
        }
        //2.寬大于1080高小于1080
    }else if(width>1080 && height<1080){
        CGFloat scale = height/width;
        width = 1080;
        height = width*scale;
        //3.寬小于1080高大于1080
    }else if(width<1080 && height>1080){
        CGFloat scale = width/height;
        height = 1080;
        width = height*scale;
        //4.寬高都小于1080
    }else{
    }
    UIGraphicsBeginImageContext(CGSizeMake(width, height));
    [sourceImage drawInRect:CGRectMake(0,0,width,height)];
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    //進行圖像的畫面質(zhì)量壓縮,統(tǒng)一按0.5壓縮
    NSData *data=UIImageJPEGRepresentation(newImage, 0.5);
//    if (data.length>100*1024) {
//        if (data.length>1024*1024) {//1M以及以上
//            data=UIImageJPEGRepresentation(newImage, 0.5);
//        }else if (data.length>512*1024) {//0.5M-1M
//            data=UIImageJPEGRepresentation(newImage, 0.8);
//        }else {
//            //0.25M-0.5M
//            data=UIImageJPEGRepresentation(newImage, 0.9);
//        }
//    }
    block(data);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颤霎,一起剝皮案震驚了整個濱河市媳谁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌友酱,老刑警劉巖晴音,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缔杉,居然都是意外死亡锤躁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門壮吩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來进苍,“玉大人,你說我怎么就攤上這事鸭叙【醢。” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵沈贝,是天一觀的道長杠人。 經(jīng)常有香客問我,道長宋下,這世上最難降的妖魔是什么嗡善? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮学歧,結(jié)果婚禮上罩引,老公的妹妹穿的比我還像新娘。我一直安慰自己枝笨,他們只是感情好袁铐,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布揭蜒。 她就那樣靜靜地躺著,像睡著了一般剔桨。 火紅的嫁衣襯著肌膚如雪屉更。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天洒缀,我揣著相機與錄音瑰谜,去河邊找鬼。 笑死树绩,一個胖子當著我的面吹牛萨脑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饺饭,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼砚哗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砰奕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤提鸟,失蹤者是張志新(化名)和其女友劉穎军援,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體称勋,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胸哥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赡鲜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片空厌。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖银酬,靈堂內(nèi)的尸體忽然破棺而出嘲更,到底是詐尸還是另有隱情,我是刑警寧澤揩瞪,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布赋朦,位于F島的核電站,受9級特大地震影響李破,放射性物質(zhì)發(fā)生泄漏宠哄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一嗤攻、第九天 我趴在偏房一處隱蔽的房頂上張望毛嫉。 院中可真熱鬧,春花似錦妇菱、人聲如沸承粤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽密任。三九已至颜启,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浪讳,已是汗流浹背缰盏。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淹遵,地道東北人口猜。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像透揣,于是被迫代替她去往敵國和親济炎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容