A. iOS 開發中的圖片壓縮
在項目中經常遇到要上傳圖片,如果直接上傳,那麼會上傳比較大的圖片,導致費流量,刷新時載入圖片時間過長,手機內存佔用率高等問題。
一、先來介紹下概念:
圖片的壓縮其實是倆概念,
1、是 「壓」 文件體積變小,但是像素數不變,長寬尺寸不變,那麼質量可能下降,
2、是 「縮」 文件的尺寸變小,也就是像素數減少。長寬尺寸變小,文件體積同樣會減小。
二、解決方法(以上傳頭像為例),先縮再壓:
2.1 矯正圖片方向(照片是有方向的,避免出現「倒立」的情況)
- (UIImage*)fixOrientation:(UIImage*)aImage {
// No-op if the orientation is already correct
if(aImage.imageOrientation==UIImageOrientationUp)
returnaImage;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransformtransform =CGAffineTransformIdentity;
switch(aImage.imageOrientation) {
caseUIImageOrientationDown:
:
transform =CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform =CGAffineTransformRotate(transform,M_PI);
break;
caseUIImageOrientationLeft:
:
transform =CGAffineTransformTranslate(transform, aImage.size.width,0);
transform =CGAffineTransformRotate(transform,M_PI_2);
break;
caseUIImageOrientationRight:
:
transform =CGAffineTransformTranslate(transform,0, aImage.size.height);
transform =CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch(aImage.imageOrientation) {
:
:
transform =CGAffineTransformTranslate(transform, aImage.size.width,0);
transform =CGAffineTransformScale(transform, -1,1);
break;
:
:
transform =CGAffineTransformTranslate(transform, aImage.size.height,0);
transform =CGAffineTransformScale(transform, -1,1);
break;
default:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRefctx =CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage),0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch(aImage.imageOrientation) {
caseUIImageOrientationLeft:
:
caseUIImageOrientationRight:
:
CGContextDrawImage(ctx,CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;
default:
CGContextDrawImage(ctx,CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
CGImageRef cgimg =CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImageimageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
2.2 拿到上面矯正過的圖片,縮小圖片尺寸,調用下面方法傳入newSize,如(200,200):
+ (UIImage*)imageWithImageSimple:(UIImage*)image scaledToSize:(CGSize)newSize
{
UIGraphicsBeginImageContext(newSize);
[imagedrawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage =();
UIGraphicsEndImageContext();
return newImage;
}
2.3 將2.2的圖片再壓,這個方法可以重復壓
//調整大小
NSData *imageData =UIImageJPEGRepresentation(newImage,rate);
NSUIntegersizeOrigin = [image Datalength];//多少KB
NSUIntegersizeOriginKB = sizeOrigin /1024;//多少KB
2.4 上傳頭像
調用後台介面,把imageData二進制數據上傳即可
總結:對圖片壓縮處理時,在保證圖片清晰度變化不大時,減小圖片文件大小。方法2.2中的newSize 和 2.3中的rate要以實際效果來設置,我在自己項目中上傳的頭像最終尺寸是200*200像素,大小為4KB左右。
B. iOS-圖片壓縮和裁剪
項目工程裡面用到了圖片壓縮,這邊做了個筆記整理了一下。
壓縮:圖片文件的體積變小,長寬尺寸不變,質量可能下降。
裁剪:文件尺寸變小,長寬尺寸變小,文件體積大小會變小。
在iOS中圖片壓縮使用到兩個關鍵方法: UIImageJPEGRepresentation 和 UIImagePNGRepresentation 使用方式如下。
下面我們用圖片測試一下兩個方法的差異。就使用的本文上面的那張傍晚iPhone拍攝的圖片,具體參數如圖:
使用 UIImagePNGRepresentation(image) 返回的數據量大小為 1,299,138 位元組(磁碟上的1.3 MB) 。
使用 UIImageJPEGRepresentation(image, 1.0) 返回的數據量大小為 183,531 位元組(磁碟上的184 KB 。
可以看到 UIImagePNGRepresentation(UIImage \*image) 要比 UIImageJPEGRepresentation(UIImage* image, 1.0) 返回的圖片數據量大很多。
使用 UIImageJPEGRepresentation 可以大幅度降低圖片的數據量,比如剛才拍攝的那張圖片,通過該方法壓縮之後,數據大小僅為 184 KB 。 更改壓縮系數為0.5再讀取數據時,返回的數據大小隻有 111 KB 。圖片大小壓縮了,但是清晰度並沒有像差多少,質量也沒有明顯的降低。
所以在讀取圖片數據的時候,建議優先使用 UIImageJPEGRepresentation ,根據實際業務需求,設置壓縮系數。
C. iOS 截取、剪裁、壓縮和拉伸圖片
在 iOS 開發過程中,對圖片的處理不僅僅局限於顯示、渲染樣式,還常常遇到對view指定區域截圖,以及對圖片的壓縮、拉伸等操作。下面我們介紹一下類似的操作過程:
註:通過 (CGSize size, BOOL opaque, CGFloat scale) 和 drawViewHierarchyInRect 的配合來截取並渲染出來的圖片位置和大小,是由前者的size和後者的rect共同決定的。 即,畫布相當於父view,其尺寸為size,截圖繪制到畫布中的位置和尺寸為rect。
想試一試的同學,可以創建一個demo,取不同的size和rect值,來觀察畫布和截圖的位置。也可以試著將畫布和截圖完全吻合,即將截圖完整的渲染出來,這並不難。
裁剪圖片就是對當前的圖片按照指定的大小范圍生成一個新的圖片。需要注意的是如果當前顯示圖片是2倍圖或者3倍圖,要麼可能尺寸不對,要麼截出來的圖片很模糊,因此,需要在截圖前調整rect值。
註:UIImageJPEGRepresentation 兩個參數:圖片引用 和壓縮系數,而 UIImagePNGRepresentation 只需圖片引用作為參數。在實際使用過程中,UIImagePNGRepresentation(UIImage* image) 一般要比UIImageJPEGRepresentation(UIImage* image, 1.0) 返回的圖片數據量大,在處理圖片時,若對圖片質量要求不高,則建議使用UIImageJPEGRepresentation,根據自己的實際使用場景設置壓縮系數,進一步降低圖片數據量大小。
在 >= iOS 5.0 時,UIImage的新方法可以處理圖片的拉伸問題:
使用過程:
在 >= iOS 6.0 時,UIImage的新方法可以處理圖片的拉伸問題:
使用過程:
參考文章:
https://www.jianshu.com/p/164b8373d17e
https://blog.csdn.net/q199109106q/article/details/8615661
D. iOS Gif圖片壓縮
gifImageView.image = UIImage.sd_image(withGIFData: data as Data?)
以上就是gif壓縮的整體思路流程了,附上 github 地址.希望能對需要的人有一點幫助吧.
E. IOS圖片壓縮(PNG壓縮 JPEG壓縮 非UIImageJPEGRepresentation)
Google出來的iOS壓縮圖片幾乎都是用的 UIImageJPEGRepresentation加上調整參數修改尺寸之類的,並不能滿足帶透明通道的PNG圖片壓縮!
在參考了壓圖神器
後選出pngquant jpegtran 兩個圖片壓縮庫進行了封裝在此以供各位同仁參考 。 (Demo圍觀戳這里)
後續會整理Google的zopflipng無損png壓縮庫 (壓縮速度有點慢不過壓縮質量相當棒)! zopfli圍觀請戳
F. iOS--圖片壓縮
我們可以在不減小圖片的解析度(質量可以適當減小)的情況下,顯著減小圖片的大小
上面方法等價於下面: 壓縮圖片質量
//將圖片壓縮到指定比例
等比例壓縮
圖片的壓縮其實是倆概念,
1、是 「壓」 文件體積變小,但是像素數不變,長寬尺寸不變,那麼質量可能下降,
2、是 「縮」 文件的尺寸變小,也就是像素數減少。長寬尺寸變小,文件體積同樣會減小。
這個 UIImageJPEGRepresentation(image, 0.0),是1的功能。
這個 [sourceImage drawInRect:CGRectMake(0,0,targetWidth, targetHeight)] 是2的功能。
所以,這倆你得結合使用來滿足需求,不然你一味的用1,導致,圖片模糊的不行,但是尺寸還是很大。
我們還可以對圖片進行部分截取
//--------------截取部分圖片到指定位置-------------------------
G. iOS開發圖片壓縮的兩種方式2019-01-18
工作中遇到需要將圖片壓縮之後上傳的需求。經過多方查詢資料,目前總結出來兩種方式總結一下備用。
UIImageJPEGRepresentation(image, compression)
這個方法可以將iPhone拍攝的照片壓縮到幾百Kb的極限值,到極限值之後不管compression這個參數多小,該函數返回的數據大小都不會再改變。也就是說這個方法的壓縮是有最小值的,得到的是jpg格式。
另外有一個方法UIImagePNGRepresentation(<#UIImage * _Nonnull image#>)這個方法得到的數據會比之前那個方法得到的數據佔用空間更大。
為了達到壓縮的目的,這種方法是有損的,就是會降低圖片質量。
這種方法的到的圖片,newSize越小質量越差,但是得到的圖片佔用內存越小。設置多大的newSize自己斟酌決定。
綜合一下自己平常在開發中常用的就是,先使用第一種方法保持精度不變,compression選擇0.6或者0.7進行第一次壓縮,然後再用第二種方法進行尺寸壓縮,得到的就是我們最終想要的圖片。
H. iOS 圖片顯示中遇到的壓縮與裁剪問題
我們假設要在截圖中的舉行圖片展示區顯示圖片,由於原圖片的寬高比例與圖片顯示窗口的寬高比例不一定相同,所以,直接將圖片扔進去會改變圖片的寬高比例,展示效果不好。
這時你可能想到設置UIImageView的屬性 _imageView.contentMode = UIViewContentModeCenter; 設置該屬性後,我們會發現圖片的寬高比例確實正確了,但是窗口中只顯示了圖片的一部分,這說明圖片整體尺寸沒有壓縮,按照原圖進行顯示了。怎樣才能達到既縮小圖片又不改變原來的寬高比例呢?
方法一:將圖片按照原來的寬高比例壓縮到與窗口合適的大小,然後在設置了_imageView.contentMode = UIViewContentModeCenter; 這個屬性的UIImageView中展示壓縮後的圖片。
//壓縮圖片
- (UIImage *)image:(UIImage*)image scaledToSize:(CGSize)newSize
{
// 創建一個圖形上下文形象
UIGraphicsBeginImageContext(newSize);
// 告訴舊圖片畫在這個新的環境,所需的
// 新的尺寸
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
// 新形象從上下文
UIImage* newImage = ();
// 結束上下文
UIGraphicsEndImageContext();
// 返回新形象
return newImage;
}
上面方法的參數newSize是和圖片顯示窗口差不多大的,結果出現了原圖清晰,但壓縮後圖片不清晰的情況。
方法二:按照窗口寬高比例,將原圖橫向或者縱向裁剪掉多餘的部分,然後不設置UIImageView的contentMode屬性,將裁剪後的圖片送進去,使其自動適應窗口。
//裁剪圖片
- (UIImage *)cutImage:(UIImage*)image
{
//壓縮圖片
CGSize newSize;
CGImageRef imageRef = nil;
if ((image.size.width / image.size.height) < (_headerView.bgImgView.size.width / _headerView.bgImgView.size.height)) {
newSize.width = image.size.width;
newSize.height = image.size.width * _headerView.bgImgView.size.height / _headerView.bgImgView.size.width;
imageRef = CGImageCreateWithImageInRect([image CGImage], CGRectMake(0, fabs(image.size.height - newSize.height) / 2, newSize.width, newSize.height));
} else {
newSize.height = image.size.height;
newSize.width = image.size.height * _headerView.bgImgView.size.width / _headerView.bgImgView.size.height;
imageRef = CGImageCreateWithImageInRect([image CGImage], CGRectMake(fabs(image.size.width - newSize.width) / 2, 0, newSize.width, newSize.height));
}
return [UIImage imageWithCGImage:imageRef];
}
結果表明方法二效果更好。
I. IOS 解決問題:相似、截屏照片清理和圖片壓縮
用來提供相冊的清理相似圖片、清理截屏圖片、壓縮圖片三個功能。
額外需要引入的框架:
Photos 引用了系統的 AssetsLibrary 框架,需要額外導入,否則會報錯:
引入方式:
作用是無需創建實例,通過類方法直接調用。
在這個方法中載入的圖片包括相似圖、截屏圖以及可瘦身的圖片,所以是公共部分。
第一步: 載入照片之前首先要清除舊數據。
第二步: 判斷相冊授權狀態。如果相冊授權狀態為沒決定,則開啟許可權提示,在 info.plist 中添加上 Privacy - Photo Library Usage Description ,提示語句可為:獲取相冊許可權。
如果相冊授權狀態為拒絕,則彈出提示框,點擊前往設置則跳轉到設置APP開啟許可權。
第三步: 如果已經授權則直接獲取相冊中的數據。
第四步: requestImageWithIndex: 方法通過圖片索引位置來獲取 assetArray 中對應的圖片。這個方法在 index+1 後不斷遞歸調用自己,直到遍歷完整個 assetArray 。傳入縮略圖和原圖後,即進入了本文章的關鍵部分,即圖片的處理流程了。
第五步: 圖片的處理方法 dealImageWithIndex: 對傳入的縮略圖和原圖進行三步處理,分別判斷其是否為相似圖片、截屏圖片、可以瘦身的圖片,如果是則將其加入到對應數組中保存作為數據源。處理完一張圖片後將 index+1 ,再調用 requestImageWithIndex: 處理下一張圖片,這是個遞歸過程,直到全部圖片處理完成。
因為圖片資源數組 assetArray 是按照創建時間排序的,所以可以通過和上一個圖片的創建時間相比較來對圖片按照是否為同一天創建的標准來分組顯示。如果是同一天創建的圖片,則將其分為一組。是否為同一天的比較方法如下:
第六步: 圖片載入完成,計算相似、截屏、可瘦身的圖片的數量和可節省內存的大小,最後將各類可節省內存的大小加起來得到總的可節省內存的大小。
其中獲取圖片數量及可以節省的內存空間大小的 getInfoWithDataArray: 方法的實現如下:
在 ClearPhotoManager 中定義:
當我們在相冊新增加了一張圖片的時候,控制台輸出如下:
在 ClearPhotoViewController 中使用:頁面顯示前或者相冊發生變動了則更新數據源
在 中使用:刪除後通知更新相冊和數據源
在 ThinPhotoViewController 中使用:壓縮後通知更新相冊和數據源
刪除一張圖片後的內存變化:
注意:這里進行了個小實驗,可以看到即使照片相同,但是如果創建日期不同的話,APP也對它進行了單獨分組,因為我們是按照日期的標准來劃分組的。實際使用過程中,這種情況很少,因為會產生相似圖片的原因大都是復制和連拍,而復制的圖片、連拍的圖片日期都是相同的。
屬性的懶載入方法實現如下:
這里用到了圖片相似度演算法,其原理包括五步:
1、縮小尺寸
2、簡化色彩
3、計算平均值
4、比較像素的灰度
5、計算哈希值
該演算法的具體實現我們不用去關心,只需要導入 ImageCompare.h 這個文件即可。在該文件內部用到了:
所以還需要導入這個框架: opencv2.framework 。這兩份文件在我的 demo 里都已經提供了。
如果上一張圖片存在並且是現在的圖片的創建時間是同一天,而且還滿足相似度演算法,則更新相似圖片數據源,否則說明和上一張圖片並不相似。
其中以創建日期作為字典的 key ,其方法 stringWithDate: 的實現如下:
這是最關鍵的一步,也是非常繞腦袋的一步,理解的關鍵是分清楚下面的數組和字典的作用和區別:
similarArray的內容列印結果如下:
similarArray中每一個lastDictionary列印結果如下:
lastDictionary的allValues: