图片压缩总结

摘要:

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

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);