图片压缩总结

摘要:

图片压缩的指标考虑包括图片尺寸裁剪,图片尺寸等比缩放,图片质量压缩等。特定场景下,从保证图片压缩的质量考虑,上述指标需要兼顾处理。

1.详细综述:

1.图片裁剪(Copy)到指定的『区域大小』
注意:图片的裁剪,可能会导致图片的质量变大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+ (NSImage *)imageResize:(NSImage*)anImage newSize:(NSSize)newSize {
NSImage *sourceImage = anImage;
// Report an error if the source isn't a valid image
if (!sourceImage.valid){
NSLog(@"Invalid Image");
} else {
NSImage *smallImage = [[NSImage alloc] initWithSize: newSize];
[smallImage lockFocus];
sourceImage.size = newSize;
[NSGraphicsContext currentContext].imageInterpolation = NSImageInterpolationHigh;
[sourceImage drawAtPoint:NSZeroPoint fromRect:CGRectMake(0, 0, newSize.width, newSize.height) operation:NSCompositeCopy fraction:1.0];
[smallImage unlockFocus];
return smallImage;
}
return nil;
}

2.图片等比缩放到指定的『区域大小』

思路:就是预先根据图片的大小与目标大小,进行选择『等比缩放』or[填充拉伸]尺寸。

3.图片质量压缩到指定质量比率

4.图片质量压缩到目标质量大小(模糊图)

思路:
(1)图片压缩的最终比例,除了改变质量外,宽高比也可以适度缩放。

5.类微信的高清鲁班压缩策略

Luban

原则:

Step1.压缩图片第一指标是大小。

在满足预期大小的指标下,根据图片本身的『宽高大小,宽高比以及质量大小』,计算目标图片尺寸的最优『图片尺寸』;
Step2.根据目标图片尺寸+计算得到的ImageSize, 进行”最终的压缩”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
+ (NSDictionary*)_compressedInfoBylubanAlgorithm:(NSData*)imageData calibrateSize:(int)calibrateSize{
CGSize imageSize = [[self class] getImageSizeWithData:imageData];
int width = imageSize.width;
int height = imageSize.height;
// 缩略图尺寸设为2的倍数,为后面的缩放做准备
int thumbW = width % 2 == 1 ? width + 1 : width;
int thumbH = height % 2 == 1 ? height + 1 : height;
// width = 短
// height = 长
BOOL exchanged = NO;
if (thumbW > thumbH) {
width = thumbH;
height = thumbW;
exchanged = YES;
}
double scale = ((double) width / height);
double size = 0;
if (scale <= 1 && scale > 0.5625) {
if (height < 1664) {
if (imageData.length / 1024 < 150) {
size = imageData.length/1024;
}
else{
size = (width * height) / pow(1664, 2) * 150;
size = size < 60 ? 60 : size;
}
} else if (height >= 1664 && height < 4990) {
thumbW = width / 2;
thumbH = height / 2;
size = (thumbW * thumbH) / pow(2495, 2) * 300;
size = size < 60 ? 60 : size;
} else if (height >= 4990 && height < 10240) {
thumbW = width / 4;
thumbH = height / 4;
size = (thumbW * thumbH) / pow(2560, 2) * 300;
size = size < 100 ? 100 : size;
} else {
int multiple = height / 1280 == 0 ? 1 : height / 1280;
thumbW = width / multiple;
thumbH = height / multiple;
size = (thumbW * thumbH) / pow(2560, 2) * 300;
size = size < 100 ? 100 : size;
}
} else if (scale <= 0.5625 && scale > 0.5) {
if (height < 1280 && imageData.length / 1024 < 200) {
size = imageData.length/1024;
}
else{
int multiple = height / 1280 == 0 ? 1 : height / 1280;
thumbW = width / multiple;
thumbH = height / multiple;
size = (thumbW * thumbH) / (1440.0 * 2560.0) * 200;
size = size < 100 ? 100 : size;
}
} else {
int multiple = (int) ceil(height / (1280.0 / scale));
thumbW = width / multiple;
thumbH = height / multiple;
size = ((thumbW * thumbH) / (1280.0 * (1280 / scale))) * 500;
size = size < 100 ? 100 : size;
}
if (exchanged) {
thumbW = thumbW + thumbH;
thumbH = thumbW - thumbH;
thumbW = thumbW - thumbH;
}
//不能超过服务端限制 1M
size = size * 1024;
size = MAX(size, calibrateSize);// size < calibrateSize(用于降低压缩的程度,提升压缩速度) < M1
size = MIN(size, imageData.length); //不超过初始大小
size = MIN(size, M1);
NSDictionary* compressedInfo = @{@"size":@(size),
@"scale":@(scale),
@"thumbW":@(thumbW),
@"thumbH":@(thumbH)};
return compressedInfo;
}

#2.压缩具体实现探讨:

1.基于质量压缩优先:兼顾最大像素

CGImageSourceCreateWithData
CGImageSourceCreateThumbnailAtIndex
CGImageDestiantionCreatWithData
CGImageDestiantionAddImage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
+ (NSData *)_thumbnailImageDataWithImageData:(NSData *)imageData
maxPixelSize:(CGFloat)maxPixelSize
quality:(CGFloat)quality {
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef) imageData, NULL);
NSDictionary *imageOptions = @{
(__bridge NSString *) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
(__bridge NSString *) kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithUnsignedInteger:maxPixelSize],
(__bridge NSString *) kCGImageSourceCreateThumbnailWithTransform : @YES,
};
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(imageSource,
0,
(__bridge CFDictionaryRef) imageOptions);
// release
if (imageSource) {
CFRelease(imageSource);
}
NSMutableData *thumbnailImageData = [[NSMutableData alloc] init];
CGImageDestinationRef destRef = CGImageDestinationCreateWithData((__bridge CFMutableDataRef) thumbnailImageData,
kUTTypeJPEG,
1,
NULL);
NSDictionary *imageAddOptions = @{
(__bridge NSString *) kCGImageDestinationLossyCompressionQuality : [NSNumber numberWithFloat:quality],
//(NSString *)kCGImageDestinationOptimizeColorForSharing:@(YES) //仅支持10.12及其以上
};
CGImageDestinationAddImage(destRef,
imageRef,
(__bridge CFDictionaryRef) imageAddOptions);
CGImageDestinationFinalize(destRef);
// release
if (imageRef) {
CFRelease(imageRef);
}
// release
if (destRef) {
CFRelease(destRef);
}
return thumbnailImageData;
}

2.利用NSBitmapImageRep进行JPEG压缩输出:

1
2
3
4
5
6
7
8
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:data];
if (imgType == ImageSuffixType_png) {
data = [imageRep representationUsingType:NSPNGFileType properties:@{}];
}
else {
NSDictionary* imageProps = @{NSImageCompressionFactor: @(compression)};
data = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
}

3.压缩图片的异常处理:

Step1: 去除额外的图片信息

NSImageColorSyncProfileData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+ (NSData*) convertProfileImageTosRGB:(NSImage*)image{
if(image != nil) {
CGImageRef cgImage = [ImageHelper newCGImageRefFromNSImage:image];
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
CGImageRelease(cgImage);
if(![imageRep.colorSpace.localizedName containsString:@"RGB"]){
imageRep = [imageRep bitmapImageRepByConvertingToColorSpace:[NSColorSpace sRGBColorSpace]renderingIntent:NSColorRenderingIntentDefault];
[imageRep setProperty:NSImageColorSyncProfileData withValue:nil];
NSData* imageData = [NSBitmapImageRep representationOfImageRepsInArray:@[imageRep] usingType:NSJPEGFileType properties:@{NSImageCompressionFactor:@(1)}];
return imageData;
}
}
return nil;
}

Step2:获取图片的实际像素大小[基于像素点的大小]

https://stackoverflow.com/questions/9264051/nsimage-size-not-real-size-with-some-pictures

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+(NSSize)getImageSizeWithData:(NSData *)imageData{
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:imageData];
return NSMakeSize(rep.pixelsWide, rep.pixelsHigh);
}
NSImage size method return size in points. To get size represented in pixels you need inspect NSImage.representations property that contains an array of NSImageRep objects with pixelWide/pixelHigh properties and simple change size NSImage object:
NSImage size method returns size information that is screen resolution dependent. To get the size represented in the actual file image you need to use an NSImageRep. You can get an NSImageRepfrom an NSImage using the representations method. Alternatively you can create a NSBitmapImageRep subclass instance directly like this:
NSArray * imageReps = [NSBitmapImageRep imageRepsWithContentsOfFile:@"<path to image>"];
NSInteger width = 0;
NSInteger height = 0;
for (NSImageRep * imageRep in imageReps) {
if ([imageRep pixelsWide] > width) width = [imageRep pixelsWide];
if ([imageRep pixelsHigh] > height) height = [imageRep pixelsHigh];
}
NSLog(@"Width from NSBitmapImageRep: %f",(CGFloat)width);
NSLog(@"Height from NSBitmapImageRep: %f",(CGFloat)height);

我的第一篇博文

摘要

基本流程,只做第一个博客,需要准备的材料有以下:

1.工具软件

1.0 Git软件

1
代码存储,博客文章存储,博客主题下载,Github Pages能力等

1.1 nodejs

1
2
3
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。
Node.js 的包管理器 npm,是全球最大的开源库生态系统。

1.2 npm

1
2
npm is the package manager for JavaScript and the world’s largest software registry. Discover packages of reusable code — and assemble them in powerful new ways.
`

安转Hexo示例

1
npm install -g hexo-cli

1.3 hexo

1
2
A fast, simple & powerful blog framework
提供丰富界面Theme的博客文章,用户可以傻瓜式的生成博客文章。

1.4 hexo-deployer-git

1
Hexo 提供了快速方便的一键部署功能,让您只需一条命令就能将网站部署到服务器上。

部署Hexo Blog示例:
hexo 使用deploy命令就可以一键把pulic文件夹的静态页面文件提交到远程Github仓库里
在开始之前,您必须先在 _config.yml 中修改参数,一个正确的部署配置中至少要有 type 参数,例如:

1
2
3
4
deploy:
type: git
repository: https://github.com/{github账户名}/{github账户名}.github.io.git
branch: master

1.4 nvm

2.基本技能:

2.1 MarkDown

1
**Markdown**是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。

2.2 github页面支持

1
2
3
4
5
6
7
配置[Github Pages](https://pages.github.com/)
默认博客地址:{github账户名}.github.io
```
2.3 创建本地hexo目录
``` bash
hexo init hexo目录名字

2.4 下载主题

1
2
cd hexo目录
git clone https://github.com/iissnan/hexo-theme-next themes/next

3.博文管理

所有命令,均在hexo的根目录下。

3.0 配置博客
_config.yml文件,这个文件就是整个博客的配置文件,你可以修改里面的参数来对你的博客进行个性化的设置,比如设置博客的名称、主题什么的。我现在将主题修改成刚才下载的主题的名字maupassant,然后使用下面的命令预览一下新的主题。

3.1 新建博文

1
hexo new "博文名字"

3.2 预览博文

1
hexo server --watch

3.3 生成博文静态页面

1
hexo generate

3.4 部署到Github Pages【1.4+2.2+3.0】

1
2
-- 安装 npm install hexo-deployer-git --save
-- 发布 hexo d -g

4.hexo的目录展示

1
2
3
4
5
6
7
8
9
_config.yml 全局配置文件。要注意的是,该文件格式要求极为严格,缺少一个空格都会导致运行错误。小提示:不要用Tab缩进,两个空格符, 冒号:后面只用一个空格即可 。
themes 存放主题的文件夹
source 博客文章资源文件夹
source/_drafts 草稿文件夹
source/_posts 文章文件夹