最近項目中要做圖片壓縮,由于之前沒有接觸過,所以在做的過程中遇到了幾個問題前塔,在這做下整理,希望看到的同學(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.新的圖片壓縮策略:
以下是代碼實現(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);
}