¿Cuál es la forma más fácil de redimensionar / optimizar un tamaño de imagen con el iPhone SDK?

votos
107

Mi aplicación descarga un conjunto de archivos de imagen de la red y los guarda en el disco local de iPhone. Algunas de esas imágenes son bastante grandes (anchuras de más de 500 píxeles, por ejemplo). Como el iPhone ni siquiera tiene una pantalla lo suficientemente grande como para mostrar la imagen en su tamaño original, estoy planeando cambiar el tamaño de la imagen a algo un poco más pequeño para ahorrar espacio / rendimiento.

Además, algunas de esas imágenes son JPEG y no se guardan como la configuración de calidad del 60% habitual.

¿Cómo puedo cambiar el tamaño de una imagen con el iPhone SDK, y cómo puedo cambiar la configuración de calidad de una imagen JPEG?

Publicado el 04/03/2009 a las 20:42
fuente por usuario
En otros idiomas...                            


18 respuestas

votos
240

Un par de sugerencias se proporcionan como respuestas a esta pregunta . Le sugerí la técnica descrita en esta publicación , con el código correspondiente:

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

   return newImage;
}

En cuanto al almacenamiento de la imagen, el formato de imagen más rápido para usar con el iPhone es PNG, porque tiene optimizaciones para ese formato. Sin embargo, si desea almacenar estas imágenes como archivos JPEG, puede tomar su UIImage y hacer lo siguiente:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

Esto crea una instancia de NSData que contiene los bytes sin formato para una imagen JPEG con una configuración de calidad del 60%. El contenido de esa instancia de NSData se puede escribir en el disco o guardar en caché en la memoria.

Respondida el 05/03/2009 a las 03:20
fuente por usuario

votos
64

La forma más fácil y directa de redimensionar sus imágenes sería esta

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();
Respondida el 05/03/2009 a las 05:35
fuente por usuario

votos
23

Los métodos anteriores funcionan bien para las pequeñas imágenes, pero cuando intenta cambiar el tamaño de una imagen muy grande, que se ejecutarán rápidamente de la memoria y caída de la aplicación. Una manera mucho mejor es utilizar CGImageSourceCreateThumbnailAtIndexpara cambiar el tamaño de la imagen sin decodificar completamente primero.

Si usted tiene la ruta de la imagen que desea cambiar el tamaño, puede utilizar lo siguiente:

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

Más detalles aquí .

Respondida el 01/09/2014 a las 12:03
fuente por usuario

votos
18

Mejor manera de escalar imágenes sin perder la relación de aspecto (es decir, sin estirar la imgage) es utilizar este método:

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

}

Añadir este método para su clase de utilidad para que pueda utilizarlo en todo el proyecto, y acceder a él de este modo:

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

Este método se encarga de escalar mientras se mantiene la relación de aspecto. También agrega guiones a la imagen en caso de que la imagen reducido tiene más anchura que la altura (o viceversa).

Respondida el 03/05/2013 a las 09:40
fuente por usuario

votos
7

Si tiene control sobre el servidor, recomiendo encarecidamente cambiar el tamaño del lado del servidor de imágenes con ImageMagik . Descargar imágenes grandes y cambiar su tamaño en el teléfono es un desperdicio de muchos recursos valiosos: ancho de banda, batería y memoria. Todo lo cual es escaso en los teléfonos.

Respondida el 05/03/2009 a las 10:23
fuente por usuario

votos
5

He desarrollado una solución definitiva para el escalado de imágenes en Swift.

Se puede utilizar para cambiar el tamaño de la imagen para llenar, llenar aspecto o aspecto encaja tamaño especificado.

Puede alinear la imagen en el centro o cualquiera de los cuatro bordes y cuatro esquinas.

Y también se puede recortar el espacio adicional que se añade si las relaciones de aspecto de la imagen original y tamaño de destino no son iguales.

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
    }
}

Hay ejemplos de la aplicación de esta solución a continuación.

Rectángulo gris es la imagen lugar objetivo será redimensionada a. Círculos azules en rectángulo de color azul claro es la imagen (utilicé círculos porque es fácil de ver cuando se escala sin conservar de aspecto). De color naranja claro marca áreas que serán recortadas si pasa trim: true.

Aspecto encaja antes y después de la escala:

Aspecto ajuste 1 (antes) Aspecto encajar 1 (después)

Otro ejemplo de ajuste aspecto :

Aspecto ajuste 2 (antes) Aspecto encajar 2 (después)

Aspecto encaja con la parte superior de alineación:

Aspecto ajuste 3 (antes) Aspecto encajar 3 (después)

Aspecto de relleno :

relleno de aspecto (antes) relleno de Aspecto (después)

llenar :

Llenar (antes) Llenar (después)

Solía ​​escalado en mis ejemplos porque es más fácil de demostrar, pero la solución también funciona para la reducción de escala que en la pregunta.

Para la compresión JPEG se debe utilizar la siguiente:

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

Se puede extraer de mi GIST con Xcode parque infantil.

Respondida el 23/03/2016 a las 10:51
fuente por usuario

votos
3

Para Swift 3, el código de abajo escala la imagen manteniendo la relación de aspecto. Puede leer más sobre el ImageContext en la documentación de 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!
    }
}

Para usarlo, llame al resizeImage()método:

UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
Respondida el 14/04/2017 a las 00:50
fuente por usuario

votos
2

se puede utilizar este código para escalar la imagen en el tamaño requerido.

+ (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;
}
Respondida el 30/09/2015 a las 07:51
fuente por usuario

votos
1
func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage? {
    let scale = newWidth / image.size.width
    let newHeight = CGFloat(200.0)
    UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
    image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}
Respondida el 12/04/2018 a las 07:25
fuente por usuario

votos
1

Agregando a la gran cantidad de respuestas aquí, pero he ido a una solución que cambia de tamaño por el tamaño del archivo, en lugar de dimensiones.

Esto tanto para reducir las dimensiones y la calidad de la imagen hasta que llegue a su tamaño dado.

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
}

El crédito para escalar por la respuesta tamaño

Respondida el 10/07/2017 a las 08:24
fuente por usuario

votos
0

Si la imagen está en el directorio de documentos, Añadir este URL extensión:

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
        }
    }
}

Uso:

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

Nota: - Cambio <LocalImagePath.jpg> con su ruta de la imagen jpg local.

Respondida el 18/05/2018 a las 07:02
fuente por usuario

votos
0
- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
    CGImageRef imageRef = image.CGImage;

    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);

    CGContextConcatCTM(context, flipVertical);
    CGContextDrawImage(context, newRect, imageRef);

    CGImageRef newImageRef = CGBitmapContextCreateImage(context);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

    CGImageRelease(newImageRef);
    UIGraphicsEndImageContext();

    return newImage;
}
Respondida el 03/11/2016 a las 05:37
fuente por usuario

votos
0

Sólo quería responder a esta pregunta para los programadores de cacao Swift. Esta función devuelve NSImage con el nuevo tamaño. Puede utilizar esa función como esta.

        let sizeChangedImage = changeImageSize(image, ratio: 2)






 // changes image size

    func changeImageSize (image: NSImage, ratio: CGFloat) -> NSImage   {

    // getting the current image size
    let w = image.size.width
    let h = image.size.height

    // calculating new size
    let w_new = w / ratio 
    let h_new = h / ratio 

    // creating size constant
    let newSize = CGSizeMake(w_new ,h_new)

    //creating rect
    let rect  = NSMakeRect(0, 0, w_new, h_new)

    // creating a image context with new size
    let newImage = NSImage.init(size:newSize)



    newImage.lockFocus()

        // drawing image with new size in context
        image.drawInRect(rect)

    newImage.unlockFocus()


    return newImage

}
Respondida el 13/07/2016 a las 06:00
fuente por usuario

votos
0

Prueba de esto, el trabajo para mí,

       CGRect rect =CGRectMake (0, 0,1200,900);
       UIGraphicsBeginImageContext( rect.size );
       [image drawInRect:rect];
       UIImage *picture1 = UIGraphicsGetImageFromCurrentImageContext();
       UIGraphicsEndImageContext();
       NSData *imageData = UIImagePNGRepresentation(picture1);
       UIImage *img=[UIImage imageWithData:imageData];
      [imageArray addObject:img];
Respondida el 18/03/2016 a las 08:48
fuente por usuario

votos
0

Terminé usando clavitos técnica para crear un scaleToFitWidthmétodo en UIImage+Extensionssi eso es útil para cualquier persona ...

-(UIImage *)scaleToFitWidth:(CGFloat)width
{
    CGFloat ratio = width / self.size.width;
    CGFloat height = self.size.height * ratio;

    NSLog(@"W:%f H:%f",width,height);

    UIGraphicsBeginImageContext(CGSizeMake(width, height));
    [self drawInRect:CGRectMake(0.0f,0.0f,width,height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

a continuación, donde quiera

#import "UIImage+Extensions.h"

UIImage *newImage = [image scaleToFitWidth:100.0f];

También digno de mención que podría pasar esto a su vez en una UIView+Extensionsclase si desea procesar las imágenes de un UIView

Respondida el 15/03/2016 a las 11:49
fuente por usuario

votos
0

Si alguien todavía en busca de una mejor opción

-(UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize {


    UIImage *sourceImage = image;
    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;

        // center the image


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


    // this is actually the interesting part:

    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 ;

}
Respondida el 23/06/2015 a las 07:39
fuente por usuario

votos
0

Un problema que pueda ocurrir en las pantallas de retina es que la escala de la imagen es fijada por ImageCapture más o menos. Las funciones de cambio de tamaño superior no va a cambiar eso. En estos casos, el cambio de tamaño no funcionará correctamente.

En el código siguiente, la escala se establece en 1 (no a escala) y la imagen devuelta tiene el tamaño que se puede esperar. Esto se realiza en la UIGraphicsBeginImageContextWithOptionsllamada.

-(UIImage *)resizeImage :(UIImage *)theImage :(CGSize)theNewSize {
    UIGraphicsBeginImageContextWithOptions(theNewSize, NO, 1.0);
    [theImage drawInRect:CGRectMake(0, 0, theNewSize.width, theNewSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
Respondida el 06/04/2015 a las 10:24
fuente por usuario

votos
-2

Para cambiar el tamaño de una imagen que tengo (gráficos) resultados mejores mediante el uso de esta función en lugar de DrawInRect:

- (UIImage*) reduceImageSize:(UIImage*) pImage newwidth:(float) pWidth
{
    float lScale = pWidth / pImage.size.width;
    CGImageRef cgImage = pImage.CGImage;
    UIImage   *lResult = [UIImage imageWithCGImage:cgImage scale:lScale
                            orientation:UIImageOrientationRight];
    return lResult;
}

La relación de aspecto es cuidado de forma automática

Respondida el 01/09/2013 a las 15:46
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more