Как легко изменить размер / оптимизировать размер изображения с iOS? - PullRequest
109 голосов
/ 04 марта 2009

Мое приложение загружает набор файлов изображений из сети и сохраняет их на локальном диске iPhone. Некоторые из этих изображений имеют довольно большой размер (например, ширина более 500 пикселей). Поскольку iPhone даже не имеет достаточно большого дисплея, чтобы отобразить изображение в его исходном размере, я планирую изменить его размер, чтобы он был немного меньше, чтобы сэкономить место / производительность.

Кроме того, некоторые из этих изображений представляют собой файлы JPEG, и они не сохраняются в качестве обычной настройки качества 60%.

Как изменить размер изображения с помощью iPhone SDK и как изменить настройку качества изображения JPEG?

Ответы [ 17 ]

241 голосов
/ 05 марта 2009

В качестве ответов на этот вопрос приведены несколько предложений. Я предложил методику, описанную в в этом посте , с соответствующим кодом:

+ (UIImage*)imageWithImage:(UIImage*)image 
               scaledToSize:(CGSize)newSize;
{
   UIGraphicsBeginImageContext( newSize );
   [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
   UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();

   return newImage;
}

Что касается хранения изображения, самый быстрый формат изображения, который можно использовать с iPhone, - это PNG, поскольку он имеет оптимизации для этого формата. Однако, если вы хотите сохранить эти изображения в формате JPEG, вы можете взять свой UIImage и сделать следующее:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

Это создает экземпляр NSData, содержащий необработанные байты для изображения JPEG с настройкой качества 60%. Содержимое этого экземпляра NSData затем может быть записано на диск или кэшировано в памяти.

64 голосов
/ 05 марта 2009

Самый простой и простой способ изменить размер изображений - это

float actualHeight = image.size.height;
float actualWidth = image.size.width;
float imgRatio = actualWidth/actualHeight;
float maxRatio = 320.0/480.0;

if(imgRatio!=maxRatio){
    if(imgRatio < maxRatio){
        imgRatio = 480.0 / actualHeight;
        actualWidth = imgRatio * actualWidth;
        actualHeight = 480.0;
    }
    else{
        imgRatio = 320.0 / actualWidth;
        actualHeight = imgRatio * actualHeight;
        actualWidth = 320.0;
    }
}
CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
UIGraphicsBeginImageContext(rect.size);
[image drawInRect:rect];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
24 голосов
/ 01 сентября 2014

Вышеуказанные методы хорошо работают для маленьких изображений, но когда вы попытаетесь изменить размер очень большого изображения, вы быстро исчерпаете память и завершите работу приложения. Гораздо лучший способ - использовать CGImageSourceCreateThumbnailAtIndex для изменения размера изображения без его полного декодирования.

Если у вас есть путь к изображению, размер которого вы хотите изменить, вы можете использовать это:

- (void)resizeImageAtPath:(NSString *)imagePath {
    // Create the image source (from path)
    CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL);

    // To create image source from UIImage, use this
    // NSData* pngData =  UIImagePNGRepresentation(image);
    // CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);

    // Create thumbnail options
    CFDictionaryRef options = (__bridge CFDictionaryRef) @{
            (id) kCGImageSourceCreateThumbnailWithTransform : @YES,
            (id) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
            (id) kCGImageSourceThumbnailMaxPixelSize : @(640)
    };
    // Generate the thumbnail
    CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options); 
    CFRelease(src);
    // Write the thumbnail at path
    CGImageWriteToFile(thumbnail, imagePath);
}

Подробнее здесь .

18 голосов
/ 03 мая 2013

Лучший способ масштабировать изображения без потери соотношения сторон (т.е. без растягивания изображения) - использовать этот метод:

//to scale images without changing aspect ratio
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize {

    float width = newSize.width;
    float height = newSize.height;

    UIGraphicsBeginImageContext(newSize);
    CGRect rect = CGRectMake(0, 0, width, height);

    float widthRatio = image.size.width / width;
    float heightRatio = image.size.height / height;
    float divisor = widthRatio > heightRatio ? widthRatio : heightRatio;

    width = image.size.width / divisor;
    height = image.size.height / divisor;

    rect.size.width  = width;
    rect.size.height = height;

    //indent in case of width or height difference
    float offset = (width - height) / 2;
    if (offset > 0) {
        rect.origin.y = offset;
    }
    else {
        rect.origin.x = -offset;
    }

    [image drawInRect: rect];

    UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return smallImage;

}

Добавьте этот метод в свой класс Utility, чтобы вы могли использовать его во всем проекте и получать к нему доступ следующим образом:

xyzImageView.image = [Utility scaleImage:yourUIImage toSize:xyzImageView.frame.size];

Этот метод заботится о масштабировании, сохраняя соотношение сторон. Он также добавляет отступы к изображению в случае, если уменьшенное изображение имеет большую ширину, чем высоту (или наоборот).

7 голосов
/ 05 марта 2009

Если у вас есть контроль над сервером, я настоятельно рекомендую изменить размер стороны сервера изображений с помощью ImageMagik . Загрузка больших изображений и изменение их размера на телефон - это пустая трата многих ценных ресурсов - пропускной способности, аккумулятора и памяти. Все, чего не хватает на телефонах.

5 голосов
/ 23 марта 2016

Я разработал оптимальное решение для масштабирования изображений в Swift.

Вы можете использовать его, чтобы изменить размер изображения, чтобы заполнить, заполнить аспект или соответствовать указанному размеру.

Вы можете выровнять изображение по центру или любому из четырех краев и четырех углов.

А также вы можете обрезать дополнительное пространство, которое добавляется, если пропорции исходного изображения и размер цели не равны.

enum UIImageAlignment {
    case Center, Left, Top, Right, Bottom, TopLeft, BottomRight, BottomLeft, TopRight
}

enum UIImageScaleMode {
    case Fill,
    AspectFill,
    AspectFit(UIImageAlignment)
}

extension UIImage {
    func scaleImage(width width: CGFloat? = nil, height: CGFloat? = nil, scaleMode: UIImageScaleMode = .AspectFit(.Center), trim: Bool = false) -> UIImage {
        let preWidthScale = width.map { $0 / size.width }
        let preHeightScale = height.map { $0 / size.height }
        var widthScale = preWidthScale ?? preHeightScale ?? 1
        var heightScale = preHeightScale ?? widthScale
        switch scaleMode {
        case .AspectFit(_):
            let scale = min(widthScale, heightScale)
            widthScale = scale
            heightScale = scale
        case .AspectFill:
            let scale = max(widthScale, heightScale)
            widthScale = scale
            heightScale = scale
        default:
            break
        }
        let newWidth = size.width * widthScale
        let newHeight = size.height * heightScale
        let canvasWidth = trim ? newWidth : (width ?? newWidth)
        let canvasHeight = trim ? newHeight : (height ?? newHeight)
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(canvasWidth, canvasHeight), false, 0)

        var originX: CGFloat = 0
        var originY: CGFloat = 0
        switch scaleMode {
        case .AspectFit(let alignment):
            switch alignment {
            case .Center:
                originX = (canvasWidth - newWidth) / 2
                originY = (canvasHeight - newHeight) / 2
            case .Top:
                originX = (canvasWidth - newWidth) / 2
            case .Left:
                originY = (canvasHeight - newHeight) / 2
            case .Bottom:
                originX = (canvasWidth - newWidth) / 2
                originY = canvasHeight - newHeight
            case .Right:
                originX = canvasWidth - newWidth
                originY = (canvasHeight - newHeight) / 2
            case .TopLeft:
                break
            case .TopRight:
                originX = canvasWidth - newWidth
            case .BottomLeft:
                originY = canvasHeight - newHeight
            case .BottomRight:
                originX = canvasWidth - newWidth
                originY = canvasHeight - newHeight
            }
        default:
            break
        }
        self.drawInRect(CGRectMake(originX, originY, newWidth, newHeight))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

Ниже приведены примеры применения этого решения.

Серый прямоугольник - изображение целевого сайта, размер которого будет изменен. Синие круги в голубом прямоугольнике - это изображение (я использовал круги, потому что это легко увидеть, если масштабировать без сохранения аспекта). Светло-оранжевым цветом отмечены области, которые будут обрезаны при прохождении trim: true.

Соотношение сторон до и после масштабирования:

Aspect fit 1 (before) Aspect fit 1 (after)

Еще один пример аспектного соответствия :

Aspect fit 2 (before) Aspect fit 2 (after)

Соотношение сторон с верхним выравниванием:

Aspect fit 3 (before) Aspect fit 3 (after)

Заполнение формата :

Aspect fill (before) Aspect fill (after)

Заполните

Fill (before) Fill (after)

Я использовал масштабирование в моих примерах, потому что это проще продемонстрировать, но решение также работает для уменьшения масштаба, как и в вопросе.

Для сжатия JPEG вы должны использовать это:

let compressionQuality: CGFloat = 0.75 // adjust to change JPEG quality
if let data = UIImageJPEGRepresentation(image, compressionQuality) {
  // ...
}

Вы можете проверить мою сущность с игровой площадкой Xcode.

3 голосов
/ 14 апреля 2017

Для Swift 3 приведенный ниже код масштабирует изображение с сохранением соотношения сторон. Вы можете прочитать больше о ImageContext в документации Apple :

extension UIImage {
    class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {
        let scale = newHeight / image.size.height
        let newWidth = image.size.width * scale
        UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
        image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}

Чтобы использовать его, вызовите resizeImage() метод:

UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
2 голосов
/ 30 сентября 2015

Вы можете использовать этот код для масштабирования изображения в необходимом размере.

+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize
{
    CGSize actSize = image.size;
    float scale = actSize.width/actSize.height;

    if (scale < 1) {
        newSize.height = newSize.width/scale;
    } 
    else {
        newSize.width = newSize.height*scale;
    }

    UIGraphicsBeginImageContext(newSize);
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}
1 голос
/ 10 июля 2017

Добавление к множеству ответов здесь, но я выбрал решение, которое изменяет размер файла, а не размеры.

Это уменьшит размеры и качество изображения, пока оно не достигнет заданного вами размера.

func compressTo(toSizeInMB size: Double) -> UIImage? {
    let bytes = size * 1024 * 1024
    let sizeInBytes = Int(bytes)
    var needCompress:Bool = true
    var imgData:Data?
    var compressingValue:CGFloat = 1.0

    while (needCompress) {

        if let resizedImage = scaleImage(byMultiplicationFactorOf: compressingValue), let data: Data = UIImageJPEGRepresentation(resizedImage, compressingValue) {

            if data.count < sizeInBytes || compressingValue < 0.1 {
                needCompress = false
                imgData = data
            } else {
                compressingValue -= 0.1
            }
        }
    }

    if let data = imgData {
        print("Finished with compression value of: \(compressingValue)")
        return UIImage(data: data)
    }
    return nil
}

private func scaleImage(byMultiplicationFactorOf factor: CGFloat) -> UIImage? {
    let size = CGSize(width: self.size.width*factor, height: self.size.height*factor)
    UIGraphicsBeginImageContext(size)
    draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    if let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() {
        UIGraphicsEndImageContext()
        return newImage;
    }
    return nil
}

Кредит за масштабирование по размеру ответа

0 голосов
/ 18 мая 2018

Если изображение находится в каталоге документов, добавьте это URL расширение:

extension URL {
    func compressedImageURL(quality: CGFloat = 0.3) throws -> URL? {
        let imageData = try Data(contentsOf: self)
        debugPrint("Image file size before compression: \(imageData.count) bytes")

        let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + NSUUID().uuidString + ".jpg")

        guard let actualImage = UIImage(data: imageData) else { return nil }
        guard let compressedImageData = UIImageJPEGRepresentation(actualImage, quality) else {
            return nil
        }
        debugPrint("Image file size after compression: \(compressedImageData.count) bytes")

        do {
            try compressedImageData.write(to: compressedURL)
            return compressedURL
        } catch {
            return nil
        }
    }
}

Использование:

guard let localImageURL = URL(string: "< LocalImagePath.jpg >") else {
    return
}

//Here you will get URL of compressed image
guard let compressedImageURL = try localImageURL.compressedImageURL() else {
    return
}

debugPrint("compressedImageURL: \(compressedImageURL.absoluteString)")

Примечание. - Измените на локальный путь к файлу JPG.

...