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: