一纷铣、概述
“壓” 是指文件體積變小,但是像素數(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倍
四碰逸、圖片質(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/處理)
圖像壓縮原理
圖片有損壓縮原理