按大小压缩图像 - iPhone SDK

26

我想要压缩图片(来自相机/照片库),并将其发送到服务器。我知道可以通过高度和宽度来压缩,但我希望仅通过大小将图像压缩到固定的大小(200 KB),同时保留原始的高度和宽度。JPEGRepresentation中的比例因子不代表大小,仅表示压缩质量。如何在不使用任何第三方库的情况下实现这一目标(压缩到固定大小)?感谢任何帮助。


SDK中没有相应的API。就像你已经发现的那样,可以通过分辨率或质量来进行调整大小。我们曾经遇到过同样的问题,最终决定根据屏幕显示大小进行调整,并将压缩比设置为85%,这样可以大大减小文件大小,但图像质量仍然非常好。 - Gilad Novik
7个回答

63

这里是一些示例代码,将尝试为您压缩图像,以使其不超过最大压缩或最大文件大小。

CGFloat compression = 0.9f;
CGFloat maxCompression = 0.1f;
int maxFileSize = 250*1024;

NSData *imageData = UIImageJPEGRepresentation(yourImage, compression);

while ([imageData length] > maxFileSize && compression > maxCompression)
{
    compression -= 0.1;
    imageData = UIImageJPEGRepresentation(yourImage, compression);
}

2
上面写的代码非常好:)。但我想让我的图片压缩到20 kb,我试着改了你的代码。但只能得到大约200 kb的大小,请问你能指导我如何实现20 kb吗? - Deepak Khiwani
@DeepK 上述解决方案的作用是压缩原始图像。最大压缩可能无法得到您期望的解决方案。因此,从压缩数据创建图像,然后再次压缩图像,直到获得所需结果。这只是一个想法。 - parveen
@Codetrix 感谢您提供解决方案。 - Deepak Khiwani
@kgutteidge 这段代码转换成 Swift 后能正常工作吗?我在 while 循环中遇到了一个错误,所以想问问你。 - Bhavin Bhadani

3

一种方法是通过循环重新压缩文件,直到找到所需的大小。您可以先找到高度和宽度,并猜测压缩因子(更大的图像更多压缩),然后在压缩后检查大小,并再次分割差异。

我知道这不是非常有效,但我不相信有一个单一的调用可以实现特定大小的图像。


1

在这里,JPEGRepresentation占用的内存非常大,如果我们在循环中使用它,那么内存消耗会非常高。因此,请使用以下代码,图像大小不会超过200KB。

UIImage* newImage = [self captureView:yourUIView];


- (UIImage*)captureView:(UIView *)view {  
CGRect rect = view.bounds;
UIGraphicsBeginImageContext(rect.size);  
CGContextRef context = UIGraphicsGetCurrentContext();  

[view.layer renderInContext:context];  
UIImage* img = [UIImage alloc]init];
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();  
NSLog(@"img=%@",img);
return img;
}

1

Swift 4:

extension UIImage {
    func compressTo(bytes: Int) -> UIImage {
        var compression: CGFloat = 0.9
        let maxCompression: CGFloat = 0.1
        let maxSize: Int = bytes * 1024

        var imageData = jpegData(compressionQuality: compression)!
        while imageData.count > maxSize && compression > maxCompression {
            compression -= 0.1
            imageData = jpegData(compressionQuality: compression)!
        }

        return UIImage(data: imageData)!
    }
}

1
使用 while 循环来处理如此繁重的任务是不安全的! - Ahmadreza

1

我采用了@kgutteridge的答案,使用递归为Swift 3.0创建了类似的解决方案:

extension UIImage {
    static func compress(image: UIImage, maxFileSize: Int, compression: CGFloat = 1.0, maxCompression: CGFloat = 0.4) -> Data? {

        if let data = UIImageJPEGRepresentation(image, compression) {

            let bcf = ByteCountFormatter()
            bcf.allowedUnits = [.useMB] // optional: restricts the units to MB only
            bcf.countStyle = .file
            let string = bcf.string(fromByteCount: Int64(data.count))
            print("Data size is: \(string)")

            if data.count > (maxFileSize * 1024 * 1024) && (compression > maxCompression) {
                let newCompression = compression - 0.1
                let compressedData = self.compress(image: image, maxFileSize: maxFileSize, compression: newCompression, maxCompression: maxCompression)
                return compressedData
            }

            return data
        }

        return nil
    }
}

0

经过几次测试,我成功地找到了 图像大小和压缩值之间的关系。由于在压缩小于1的所有值时这种关系是线性的,所以我创建了一个算法,尝试将图像始终压缩到特定的值。

//Use 0.99 because at 1, the relationship between the compression and the file size is not linear
NSData *image = UIImageJPEGRepresentation(currentImage, 0.99);
float maxFileSize = MAX_IMAGE_SIZE * 1024;

//If the image is bigger than the max file size, try to bring it down to the max file size
if ([image length] > maxFileSize) {
    image = UIImageJPEGRepresentation(currentImage, maxFileSize/[image length]);
}

2
这个答案是不正确和误导性的。请考虑将其删除。 - ikbal

-1
- (UIImage *)resizeImageToSize:(CGSize)targetSize
{
    UIImage *sourceImage = captureImage;
    UIImage *newImage = nil;

    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;

    CGFloat targetWidth = targetSize.width;
    CGFloat targetHeight = targetSize.height;

    CGFloat scaleFactor = 0.0;
    CGFloat scaledWidth = targetWidth;
    CGFloat scaledHeight = targetHeight;

    CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

    if (CGSizeEqualToSize(imageSize, targetSize) == NO) {

        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;

        if (widthFactor < heightFactor)
            scaleFactor = widthFactor;
        else
            scaleFactor = heightFactor;

        scaledWidth  = width * scaleFactor;
        scaledHeight = height * scaleFactor;

        // make image center aligned
        if (widthFactor < heightFactor)
        {
            thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
        }
        else if (widthFactor > heightFactor)
        {
            thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
        }
    }

    UIGraphicsBeginImageContext(targetSize);
    CGRect thumbnailRect = CGRectZero;
    thumbnailRect.origin = thumbnailPoint;
    thumbnailRect.size.width  = scaledWidth;
    thumbnailRect.size.height = scaledHeight;

    [sourceImage drawInRect:thumbnailRect];
    newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    if(newImage == nil)
        NSLog(@"could not scale image");

    return newImage ;
}

问题特别询问的是文件大小,而不是图像尺寸。 - bengoesboom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接