iOS圖片壓縮方案

一纷铣、概述

“壓” 是指文件體積變小,但是像素數(shù)不變战转,長寬尺寸不變搜立,那么質(zhì)量可能下降。(圖片壓完之后匣吊,占用內(nèi)存的bitMap不變)
“縮” 是指文件的尺寸變小儒拂,也就是像素數(shù)減少,而長寬尺寸變小色鸳,文件體積同樣會減小社痛。

二、“壓”

1命雀、圖片質(zhì)量壓縮原理
采用一些算法蒜哀,將幾個像素點用一個像素點的數(shù)據(jù)表示
2、圖片壓完之后吏砂,占用內(nèi)存的bitMap不變論證:

UIImage *image = [UIImage imageNamed:@"WechatIMG435.jpeg"];
    NSData *imgData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"WechatIMG435" ofType:@"jpeg"]];
    NSLog(@"imgData:%ld",imgData.length);
    
    ;
    CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
    NSLog(@"rawData.length:%ld",(long)CFDataGetLength(rawData));
    
    NSLog(@"----------------------我是分割線---------------------");
    
    NSData *compressData = UIImageJPEGRepresentation(image, 0.1);
    UIImage *compressImg = [UIImage imageWithData:compressData];
    
    
    NSString *newPath = [NSString stringWithFormat:@"%@/%@",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject],@"compressImg.jpeg"];
   BOOL a =  [compressData writeToFile:newPath atomically:YES];
    NSLog(@"imgData:%ld",compressData.length);
    CFDataRef compressRawData = CGDataProviderCopyData(CGImageGetDataProvider(compressImg.CGImage));
    NSLog(@"rawData.length:%ld",(long)CFDataGetLength(compressRawData));

打印結(jié)果:

2019-07-09 18:01:38.172504+0800 Img[47267:15114761] libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
2019-07-09 18:01:38.263858+0800 Img[47267:15114761] [framework] CUIThemeStore: No theme registered with id=0
2019-07-09 18:01:38.330603+0800 Img[47267:15114761] imgData:10983
2019-07-09 18:01:38.331847+0800 Img[47267:15114761] rawData.length:480000
2019-07-09 18:01:38.331955+0800 Img[47267:15114761] ----------------------我是分割線---------------------
2019-07-09 18:01:38.334699+0800 Img[47267:15114761] imgData:5560
2019-07-09 18:01:38.335806+0800 Img[47267:15114761] rawData.length:480000

如上撵儿,圖片"壓"后,占用內(nèi)存的bitmap沒有改變

三狐血、“縮”
1淀歇、原理
圖片尺寸壓縮,減小體積匈织,圖片bitmap減小
2浪默、方案
使用ImageIO接口牡直,避免在改變圖片大小的過程中產(chǎn)生臨時的bitmap,就能夠在很大程度上減少內(nèi)存的占有從而避免由此導(dǎo)致的app閃退問題纳决。

    UIImage *image = [UIImage imageNamed:@"WechatIMG435.jpeg"];
    NSData *imgData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"WechatIMG435" ofType:@"jpeg"]];
    NSLog(@"imgData:%ld",imgData.length);
    
    ;
    CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
    NSLog(@"rawData.length:%ld",(long)CFDataGetLength(rawData));
    
    NSLog(@"----------------------我是分割線---------------------");
    
    UIImage *sizeCompressImg = [self thumbnailForData:imgData maxPixelSize:image.size.width * 0.25];
    CFDataRef sizeCompressRawData = CGDataProviderCopyData(CGImageGetDataProvider(sizeCompressImg.CGImage));
    NSLog(@"rawData.length:%ld",(long)CFDataGetLength(sizeCompressRawData));

打印結(jié)果

2019-07-09 20:10:04.300806+0800 Img[48279:15418157] libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
2019-07-09 20:10:04.378632+0800 Img[48279:15418157] [framework] CUIThemeStore: No theme registered with id=0
2019-07-09 20:10:04.448211+0800 Img[48279:15418157] imgData:10983
2019-07-09 20:10:04.449632+0800 Img[48279:15418157] rawData.length:480000
2019-07-09 20:10:04.449805+0800 Img[48279:15418157] ----------------------我是分割線---------------------
2019-07-09 20:10:04.450898+0800 Img[48279:15418157] rawData.length:30000

分析:圖片bitmap占用從 480000 減小到 30000減小了16倍


image.png

四碰逸、圖片質(zhì)量壓縮方案探索
1、分次循環(huán)壓縮

    ///  對圖片進(jìn)行質(zhì)量壓縮
    ///
    /// - Parameter targetByte: 壓縮目標(biāo)字節(jié)數(shù)
    /// - Returns: 壓縮之后的data 對象 
    func compressByQulity(toTargetByte targetByte:NSInteger)->NSData?{
        let bigImage = self
        
        let imgData = bigImage.dataFromImage()
        guard imgData != nil else{
            return nil
        }
        var targetImageData:Data = imgData!
        print("lalala compress before:%d",targetImageData.count)
        //分次循環(huán)壓縮
        //當(dāng)前imgData 大小
        var targetImageDataLength:Int = targetImageData.count
        //上次壓縮imgData 大小
        var lastTargetImageDataLength : Int = 0
        //png jpeg應(yīng)機(jī)器學(xué)習(xí)同學(xué)要求阔加,同時采用jpeg壓縮格式
        while(targetImageDataLength > targetByte ){
            //上次壓縮圖片大小賦值
            lastTargetImageDataLength = targetImageDataLength;
            
            //壓縮
            let img =  UIImage.init(data: targetImageData)
            guard img != nil else{
                break
            }
            let compressImgData = img!.jpegData(compressionQuality: CGFloat(UIImage.qualityPerCompressRate))
            guard compressImgData != nil else{
                break
            }
            targetImageData = compressImgData!
            targetImageDataLength = targetImageData.count
            guard targetImageDataLength < lastTargetImageDataLength else {
                break
            }
        }
        print("lalala compress after:%d",targetImageData.count)
        return targetImageData as NSData
    }
lalala =-------我是分割線-----------------
lalala compress before:%d 1811323
lalala compress after:%d 543473
lalala compress after:%d 544901
lalala =-------我是分割線-----------------
lalala compress before:%d 1559291
lalala compress after:%d 526630
lalala compress after:%d 527984
lalala =-------我是分割線-----------------
lalala compress before:%d 2522415
lalala compress after:%d 766597
lalala compress after:%d 767281
lalala =-------我是分割線-----------------
lalala compress before:%d 2287580
lalala compress after:%d 753524
lalala compress after:%d 754047

以上算法存在以下問題
(1)饵史、第二次一定比第一次大,所以只能跑一遍
(2)胜榔、UIImage 與 data互轉(zhuǎn) 來回互轉(zhuǎn) data數(shù)據(jù)會改變即 UIImageJPEGRepresentation 1.0 從本地讀取到data數(shù)據(jù)也會改變胳喷,圖片的質(zhì)量也會受到影響

  UIImage *image = [UIImage imageNamed:@"WechatIMG435.jpeg"];
    NSData *imgData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"WechatIMG435" ofType:@"jpeg"]];
    NSLog(@"imgData:%ld",imgData.length);
    
    NSData *jpegData = UIImageJPEGRepresentation(image, 1.0);
    NSLog(@"jpegData:%ld",jpegData.length);
    
    UIImage *newImage = [UIImage imageWithData:jpegData];
    NSData *newData = UIImageJPEGRepresentation(newImage, 1.0);
    NSLog(@"newData:%ld",newData.length);

打印結(jié)果:

2019-07-09 20:56:55.355619+0800 Img[48690:15554037] imgData:10983
2019-07-09 20:56:55.359325+0800 Img[48690:15554037] jpegData:47681
2019-07-09 20:56:55.363122+0800 Img[48690:15554037] newData:51046

2、 二分法尋找最佳壓縮率
(1)UIImageJPEGRepresentation(image, scale) API調(diào)研
a苗分、圖片壓縮測試 原圖大醒岜巍:7801942

scale 實際 正常比例計算
0.1 114823 780192
0.2 127503 1560384
0.3 156226 2340577
0.4 204067
0.5 261299
0.6 331804
0.7 438994
0.8 508924
0.9 640913

從上圖得出 實際壓縮率不是傳入的值,實際壓縮率一定大于壓縮結(jié)果
b摔癣、壓縮極限測試
原圖大信:193737
壓縮比率
0.01 113710
對上面壓縮之后的值再次壓縮
0.01 113710
壓縮到極限值之后,壓縮之后的數(shù)據(jù)择浊,將不在壓縮
(2)戴卜、壓縮目標(biāo)
壓縮 目標(biāo) 物理大小 500k,容錯范圍 5000.8 ~~5001.2
(3)、UIImageJPEGRepresentation能力之外數(shù)據(jù)處理方案
a琢岩、圖片壓縮0.01之后還是不能到500k,直接返回壓縮最小值
b投剥、循環(huán)到第6次數(shù),壓縮率為0.984担孔,已經(jīng)壓縮為最大值江锨,還沒有進(jìn)入理想數(shù)據(jù)范圍,將不在進(jìn)行壓縮糕篇,直接返回UIImageJPEGRepresentation 1.0壓縮數(shù)值
c啄育、本來就是有損壓縮,無論png還是jpeg 統(tǒng)一使用jpeg壓縮方案與UIImage->data轉(zhuǎn)化方法

 func compressByQulity(toTargetByte targetByte:NSInteger)->NSData?{
        let bigImage = self
        
        //本來就是有損壓縮拌消,無論png還是jpeg 統(tǒng)一使用jpeg 注意:如果為png格式挑豌,轉(zhuǎn)化之后filesize會比源文件小很多,這里將進(jìn)行一次有損壓縮
        let imgData = bigImage.jpegData(compressionQuality: 1.0)
        guard imgData != nil else{
            return nil
        }
        
        var targetImageData:Data = imgData!
        print("lalala =-------我是分割線-----------------")
        print("lalala compress before---",targetImageData.count)
        guard targetImageData.count > targetByte else {
            print("lalala compress after:%d",targetImageData.count)
            return targetImageData as NSData
        }
        
        //分次循環(huán)尋找compressRate大小
        //png jpeg應(yīng)機(jī)器學(xué)習(xí)同學(xué)要求墩崩,同時采用jpeg壓縮格式
        var compressionRate:CGFloat = 1.0
        var max:CGFloat = 1
        var min:CGFloat = 0
        //指數(shù)二分處理氓英,s首先計算最小值
        compressionRate = CGFloat(pow(2.0, -6.0))
        let smallestImgData = bigImage.jpegData(compressionQuality: compressionRate)
        guard smallestImgData != nil else {
            return nil
        }
        
        if smallestImgData!.count < targetByte{
            for _ in 0..<6 {
                compressionRate = (max + min)/2.0
                let compressData = bigImage.jpegData(compressionQuality: compressionRate)
                guard compressData != nil else {
                    break
                }
                
                //容錯區(qū)間范圍0.8~1.2 409600 614400
                if compressData!.count < Int(Double(targetByte) * 0.8){
                    min = compressionRate
                }else if compressData!.count > Int(Double(targetByte) * 1.2){
                    max = compressionRate
                }else{
                    //找到壓縮最佳值
                    targetImageData = compressData!
                    print("lalala ---0.9:%d~~~1.1:%d",Int(Double(targetByte) * 0.8),Int(Double(targetByte) * 1.2))
                    break
                }
                print("lalala ---\(compressionRate)----compressDatalenth:%d",compressData?.count)
            }
        }
        print("lalala compress after:%d",targetImageData.count)
        return targetImageData as NSData
    }

原因:image data 相互轉(zhuǎn)換 data忽大忽小
結(jié)果:可能是300k,但是已經(jīng)是jpeg能提供的最佳壓縮率了

lalala compress before--- 1006180
lalala ---0.5----compressDatalenth:%d Optional(156856)
lalala ---0.75----compressDatalenth:%d Optional(266978)
lalala ---0.875----compressDatalenth:%d Optional(321495)
lalala ---0.9375----compressDatalenth:%d lalala ---0.96875----compressDatalenth:%d Optional(352076)
lalala ---0.984375----compressDatalenth:%d Optional(359699)

此方案基于 UIImageJPEGRepresentation 傳入?yún)?shù)值 由小到大,壓縮值 也 由小變大規(guī)律
規(guī)律論證如下:

參考鏈接:iOS圖片壓縮處理
為什么實際大小和占用空間不一樣

iOS優(yōu)秀的圖片壓縮處理方案(系統(tǒng)UIimagejpeg和uikit以及imageI/處理)
圖像壓縮原理
圖片有損壓縮原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹦筹,一起剝皮案震驚了整個濱河市铝阐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铐拐,老刑警劉巖徘键,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芳誓,死亡現(xiàn)場離奇詭異,居然都是意外死亡啊鸭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門匿值,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赠制,“玉大人,你說我怎么就攤上這事挟憔≈有” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵绊谭,是天一觀的道長政恍。 經(jīng)常有香客問我,道長达传,這世上最難降的妖魔是什么篙耗? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮宪赶,結(jié)果婚禮上宗弯,老公的妹妹穿的比我還像新娘。我一直安慰自己搂妻,他們只是感情好蒙保,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欲主,像睡著了一般邓厕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扁瓢,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天详恼,我揣著相機(jī)與錄音,去河邊找鬼涤妒。 笑死单雾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的她紫。 我是一名探鬼主播硅堆,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贿讹!你這毒婦竟也來了渐逃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤民褂,失蹤者是張志新(化名)和其女友劉穎茄菊,沒想到半個月后疯潭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡面殖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年竖哩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脊僚。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡相叁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辽幌,到底是詐尸還是另有隱情增淹,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布乌企,位于F島的核電站虑润,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏加酵。R本人自食惡果不足惜拳喻,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一损拢、第九天 我趴在偏房一處隱蔽的房頂上張望髓帽。 院中可真熱鬧,春花似錦栋操、人聲如沸码撰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脖岛。三九已至朵栖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柴梆,已是汗流浹背陨溅。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留绍在,地道東北人门扇。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像偿渡,于是被迫代替她去往敵國和親臼寄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

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