Qual è il modo più semplice per ridimensionare / ottimizzare le dimensioni dell'immagine con l'iPhone SDK?

voti
107

La mia applicazione sta scaricando un insieme di file di immagini dalla rete, e salvandoli sul disco iPhone locale. Alcune di queste immagini sono abbastanza grandi dimensioni (larghezze più grandi di 500 pixel, per esempio). Dal momento che l'iPhone non ha nemmeno un grande display sufficiente per visualizzare l'immagine nelle sue dimensioni originali, sto pensando di ridimensionare l'immagine a qualcosa di un po 'più piccolo per risparmiare spazio / prestazioni.

Inoltre, alcune di queste immagini sono JPEG e non vengono salvati come la consueta impostazione di qualità il 60%.

Come faccio a ridimensionare una foto con l'iPhone SDK, e come posso modificare l'impostazione della qualità di un'immagine JPEG?

È pubblicato 04/03/2009 alle 20:42
fonte dall'utente
In altre lingue...                            


18 risposte

voti
240

Un paio di suggerimenti sono forniti come risposte a questa domanda . Avevo suggerito la tecnica descritta in questo post , con il relativo codice:

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

   return newImage;
}

Per quanto riguarda la memorizzazione dell'immagine, il formato di immagine più veloce da utilizzare con l'iPhone è PNG, perché ha ottimizzazioni per quel formato. Tuttavia, se si desidera memorizzare queste immagini come JPEG, si può prendere l'UIImage e procedere come segue:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

Questo crea un'istanza NSData contenente i byte prime per un'immagine JPEG con un'impostazione qualità 60%. Il contenuto di tale istanza NSData possono poi essere scritti su disco o memorizzate nella cache.

Risposto il 05/03/2009 a 03:20
fonte dall'utente

voti
64

Il modo più facile e più semplice per ridimensionare le immagini sarebbe questo

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();
Risposto il 05/03/2009 a 05:35
fonte dall'utente

voti
23

I metodi di cui sopra funzionano bene per piccole immagini, ma quando si tenta di ridimensionare un'immagine di grandi dimensioni, si rapidamente esaurire la memoria e crash l'applicazione. Un modo migliore è usare CGImageSourceCreateThumbnailAtIndexper ridimensionare l'immagine senza decodifica completamente prima.

Se avete il percorso per l'immagine che si desidera ridimensionare, è possibile utilizzare questo:

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

Maggiori dettagli qui .

Risposto il 01/09/2014 a 12:03
fonte dall'utente

voti
18

Modo migliore per ridimensionare immagini senza perdere il rapporto di aspetto (cioè senza allungare l'imgage) è quella di utilizzare questo metodo:

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

}

Aggiungere questo metodo per la classe di utilità in modo da poter utilizzare tutto il progetto, e accedervi in ​​questo modo:

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

Questo metodo si occupa di scalare mantenendo le proporzioni. Si aggiunge anche trattini all'immagine nel caso l'immagine rimpicciolita ha più larghezza di altezza (o viceversa).

Risposto il 03/05/2013 a 09:40
fonte dall'utente

voti
7

Se si dispone di controllo del server, che vi consiglio vivamente il ridimensionamento lato server con immagini ImageMagik . Il download di immagini di grandi dimensioni e il ridimensionamento al telefono è uno spreco di tante risorse preziose - larghezza di banda, batteria e della memoria. Tutto ciò sono scarse sui telefoni.

Risposto il 05/03/2009 a 10:23
fonte dall'utente

voti
5

Ho sviluppato una soluzione definitiva per il ridimensionamento dell'immagine a Swift.

Si può usare per ridimensionare immagine per riempire, riempire aspetto o aspetto si adatta dimensioni specificate.

È possibile allineare l'immagine al centro o uno qualsiasi dei quattro bordi e quattro gli angoli.

E inoltre è possibile tagliare spazio in più che si aggiunge se proporzioni dell'immagine originale e le dimensioni di destinazione non sono uguali.

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

Ci sono esempi di applicazione di tale soluzione qui di seguito.

Rettangolo grigio è un'immagine sito di destinazione sarà ridimensionata a. Cerchi blu in rettangolo azzurro è l'immagine (I cerchi usato perché è facile capire quando è ridimensionata senza mantenere aspetto). Luce colore arancione segna aree che verranno ritagliate se si passa trim: true.

Aspetto in forma prima e dopo la scalatura:

Aspect fit 1 (prima) Aspect montare 1 (dopo)

Un altro esempio di aspetto in forma :

Aspect fit 2 (prima) Aspect montare 2 (dopo)

Aspect si adattano con la parte superiore di allineamento:

Aspect fit 3 (prima) Aspect Fit 3 (dopo)

Aspetto di riempimento :

riempimento Aspect (prima) riempimento Aspect (dopo)

Fill :

Fill (prima) Fill (dopo)

Ho usato upscaling nei miei esempi, perché è più semplice, ma per dimostrare soluzione funziona anche per il downscaling come in questione.

Per la compressione JPEG si dovrebbe usare questo:

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

È possibile controllare il mio succo con parco giochi Xcode.

Risposto il 23/03/2016 a 13:51
fonte dall'utente

voti
3

Per Swift 3, il codice seguente ridimensiona l'immagine mantenendo le proporzioni. Si può leggere di più sul ImageContext nella documentazione di 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!
    }
}

Per usarlo, chiamare resizeImage()il metodo:

UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
Risposto il 14/04/2017 a 03:50
fonte dall'utente

voti
2

è possibile utilizzare questo codice per scalare l'immagine in formato richiesto.

+ (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;
}
Risposto il 30/09/2015 a 10:51
fonte dall'utente

voti
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
}
Risposto il 12/04/2018 a 10:25
fonte dall'utente

voti
1

Aggiungendo alla sfilza di risposte qui, ma sono andato per una soluzione che ridimensiona in base alle dimensioni del file, piuttosto che le dimensioni.

Questo sia ridurre saranno le dimensioni e la qualità dell'immagine fino a raggiungere il data dimensione.

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
}

Credito per il ridimensionamento da risposta dimensioni

Risposto il 10/07/2017 a 11:24
fonte dall'utente

voti
0

Se l'immagine si trova nella directory del documento, Aggiungi l'URL estensione:

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: - Cambia <LocalImagePath.jpg> con il percorso di immagine jpg locale.

Risposto il 18/05/2018 a 10:02
fonte dall'utente

voti
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;
}
Risposto il 03/11/2016 a 08:37
fonte dall'utente

voti
0

Volevo solo rispondere a questa domanda per i programmatori di cacao Swift. Questa funzione restituisce NSImage con nuove dimensioni. È possibile utilizzare questa funzione come questo.

        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

}
Risposto il 13/07/2016 a 09:00
fonte dall'utente

voti
0

Prova questo, il lavoro per me,

       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];
Risposto il 18/03/2016 a 11:48
fonte dall'utente

voti
0

Ho finito per usare Brads tecnica per creare un scaleToFitWidthmetodo UIImage+Extensions, se questo è utile a chiunque ...

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

allora dove vuoi

#import "UIImage+Extensions.h"

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

Anche la pena notare che si potrebbe spostare questo, più in basso in una UIView+Extensionsclasse, se si vuole rendere le immagini da un UIView

Risposto il 15/03/2016 a 14:49
fonte dall'utente

voti
0

Se qualcuno ancora alla ricerca di una migliore opzione

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

}
Risposto il 23/06/2015 a 07:39
fonte dall'utente

voti
0

Un problema che potrebbe verificarsi su display retina è che la scala dell'immagine è impostato ImageCapture o così. Le funzioni di ridimensionamento di cui sopra non potranno cambiare la situazione. In questi casi il ridimensionamento funzionerà non adeguatamente.

Nel codice qui sotto, la scala è impostato su 1 (non in scala) e l'immagine restituita ha la dimensione che ci si aspetterebbe. Questo viene fatto nella UIGraphicsBeginImageContextWithOptionschiamata.

-(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;
}
Risposto il 06/04/2015 a 10:24
fonte dall'utente

voti
-2

Per ridimensionare un'immagine Ho migliori (grafici) risultati utilizzando questa funzione al posto di 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;
}

Aspect è curato per automaticamente

Risposto il 01/09/2013 a 15:46
fonte dall'utente

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