Domanda

La mia applicazione è quello di scaricare una serie di file di immagine dalla rete, e di salvarli in locale iPhone disco.Alcune di queste immagini sono abbastanza grandi in termini di dimensioni (larghezza maggiore di 500 pixel, per esempio).Dal momento che l'iPhone anche se non ha una abbastanza grande display per visualizzare l'immagine nelle sue dimensioni originali, sto pensando su come ridimensionare l'immagine di qualcosa di un po ' più piccolo per risparmiare spazio/prestazioni.

Inoltre, alcune di queste sono immagini Jpeg e non vengono salvate come al solito 60% impostazione di qualità.

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

È stato utile?

Soluzione

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

+ (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 dell'immagine più veloce da utilizzare con l'iPhone è PNG, perché ha ottimizzazioni per quel formato. Tuttavia, se desideri archiviare queste immagini come JPEG, puoi prendere il tuo UIImage ed eseguire le seguenti operazioni:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

Questo crea un'istanza NSData contenente i byte grezzi per un'immagine JPEG con un'impostazione di qualità del 60%. I contenuti di tale istanza NSData possono quindi essere scritti su disco o memorizzati nella cache.

Altri suggerimenti

Il modo più semplice e diretto per ridimensionare le tue 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();

I metodi sopra indicati funzionano bene per le immagini piccole, ma quando si tenta di ridimensionare un'immagine molto grande, si esaurisce rapidamente la memoria e si blocca l'app. Un modo molto migliore è usare CGImageSourceCreateThumbnailAtIndex per ridimensionare l'immagine senza prima decodificarla completamente.

Se hai il percorso dell'immagine che desideri ridimensionare, puoi usare 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 .

Miglior modo per ridimensionare le immagini senza perdere il rapporto di aspetto (cioèsenza allungare il imgage) è quello 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 il vostro programma di Utilità di classe, in modo che si può utilizzare per tutto il progetto, e accedere in questo modo:

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

Questo metodo prende cura di scala mantenendo le proporzioni.Inoltre, aggiunge che rientri l'immagine nel caso in cui il ridimensionato l'immagine è più in larghezza che in altezza (o viceversa).

Se hai il controllo del server, ti consiglio vivamente di ridimensionare il lato server delle immagini con ImageMagik . Scaricare immagini di grandi dimensioni e ridimensionarle al telefono è uno spreco di molte risorse preziose: larghezza di banda, batteria e memoria. Tutto ciò è scarso sui telefoni.

Ho sviluppato una soluzione definitiva per il ridimensionamento delle immagini in Swift.

Puoi usarlo per ridimensionare l'immagine in modo da riempire, riempire l'aspetto o adattarlo alle dimensioni specificate.

Puoi allineare l'immagine al centro o su uno dei quattro bordi e quattro angoli.

Inoltre puoi tagliare lo spazio extra che viene aggiunto se le proporzioni dell'immagine originale e la dimensione del bersaglio 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
    }
}

Di seguito sono riportati alcuni esempi di applicazione di questa soluzione.

Il rettangolo grigio è l'immagine del sito target su cui verrà ridimensionato. I cerchi blu nel rettangolo azzurro sono l'immagine (ho usato i cerchi perché è facile vedere quando è ridimensionato senza preservare l'aspetto). Il colore arancione chiaro indica le aree che verranno ritagliate se si passa trim: true.

Aspetto adattato prima e dopo il ridimensionamento:

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

Un altro esempio di aspetto adattato :

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

Aspetto adattato con allineamento superiore:

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

Riempimento aspetto :

 Riempimento aspetto (prima)  Riempimento aspetto (dopo)

Riempire :

 Fill (before)  Fill (after)

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

Per la compressione JPEG dovresti usare questo:

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

Puoi dare un'occhiata al mio gist con Xcode playground.

Per Swift 3, il codice seguente ridimensiona l'immagine mantenendo le proporzioni. Puoi leggere ulteriori informazioni su ImageContext in 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, chiama resizeImage() metodo:

UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)

puoi usare questo codice per ridimensionare l'immagine nella dimensione richiesta.

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

Aggiungendo alla serie di risposte qui, ma ho cercato una soluzione che ridimensiona in base alle dimensioni del file, piuttosto che alle dimensioni.

Ciò ridurrà sia le dimensioni che la qualità dell'immagine fino a quando non raggiungerà le dimensioni indicate.

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 ridimensionamento in base alla risposta per dimensione

Un problema che potrebbe verificarsi sui display retina è che la scala dell'immagine è impostata da ImageCapture o giù di lì. Le funzioni di ridimensionamento sopra non cambieranno questo. In questi casi il ridimensionamento non funzionerà correttamente.

Nel codice seguente, la scala è impostata su 1 (non ridimensionata) e l'immagine restituita ha le dimensioni che ci si aspetterebbe. Questo viene fatto nella UIGraphicsBeginImageContextWithOptions chiamata.

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

Ho finito per usare la tecnica Brads per creare un scaleToFitWidth metodo in 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];

Vale anche la pena notare che è possibile spostarlo ulteriormente in una UIView+Extensions classe se si desidera eseguire il rendering di immagini da una vista UIV

Volevo solo rispondere a questa domanda per i programmatori Cocoa Swift. Questa funzione restituisce NSImage con nuove dimensioni. Puoi usare quella funzione in questo modo.

        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

}

Se l'immagine si trova nella directory dei documenti, aggiungi questa estensione 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
        }
    }
}

Utilizzo:

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 & Gt; con il percorso dell'immagine jpg locale.

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
}

Se qualcuno sta ancora cercando un'opzione migliore

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

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

Per ridimensionare un'immagine ho risultati migliori (grafici) usando questa funzione invece 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;
}

Le proporzioni vengono gestite automaticamente

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top