Quelle est la meilleure façon de redimensionner / optimiser une taille d'image avec l'iPhone SDK?

voix
107

Mon application télécharge un ensemble de fichiers d'image du réseau, et de les enregistrer sur le disque local iPhone. Certaines de ces images sont assez grande taille (largeurs plus de 500 pixels, par exemple). Depuis l'iPhone ne dispose même pas d'un écran assez grand pour afficher l'image dans sa taille d'origine, je prévois redimensionner l'image à quelque chose d'un peu plus petit pour économiser de l'espace / performance.

En outre, certaines de ces images sont et ils sont JPEGs pas enregistrés en tant que paramètre de qualité de 60% d'habitude.

Comment puis-je redimensionner une image avec le SDK iPhone, et comment puis-je modifier le réglage de la qualité d'une image JPEG?

Créé 04/03/2009 à 20:42
source utilisateur
Dans d'autres langues...                            


18 réponses

voix
240

Quelques suggestions sont fournies comme des réponses à cette question . Je l' avais suggéré la technique décrite dans cet article , avec le code correspondant:

+ (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 ce qui concerne le stockage de l'image, le plus rapide format d'image à utiliser avec l'iPhone est PNG, car il a des optimisations pour ce format. Cependant, si vous voulez stocker ces images au format JPEG, vous pouvez prendre votre UIImage et procédez comme suit:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

Cela crée une instance NSData contenant les octets bruts pour une image JPEG à un paramètre de qualité de 60%. Le contenu de cette instance NSData peuvent être écrites sur le disque ou mises en mémoire cache.

Créé 05/03/2009 à 03:20
source utilisateur

voix
64

La redimensionner vos images manière la plus simple et la plus directe serait cette

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();
Créé 05/03/2009 à 05:35
source utilisateur

voix
23

Les méthodes ci - dessus fonctionnent bien pour les petites images, mais lorsque vous essayez de redimensionner une image très grande, vous courrez rapidement de la mémoire et de crash de l'application. Une bien meilleure façon est d'utiliser CGImageSourceCreateThumbnailAtIndexpour redimensionner l'image sans décoder complètement en premier.

Si vous avez le chemin de l'image que vous voulez redimensionner, vous pouvez utiliser ceci:

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

Plus de détails ici .

Créé 01/09/2014 à 12:03
source utilisateur

voix
18

Le meilleur moyen d'échelle des images sans perdre le rapport d'aspect (à savoir sans étirer le imgage) est d'utiliser cette méthode:

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

}

Ajouter cette méthode à votre classe utilitaire afin que vous puissiez l'utiliser tout au long de votre projet, et l'accès comme ceci:

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

Cette méthode prend soin de mise à l'échelle, tout en maintenant le rapport d'aspect. Il ajoute également tirets à l'image dans le cas où l'image réduite a plus large que la hauteur (ou vice versa).

Créé 03/05/2013 à 09:40
source utilisateur

voix
7

Si vous avez le contrôle sur le serveur, je recommande fortement le redimensionnement du côté du serveur d'images avec ImageMagik . Téléchargement de grandes images et les redimensionner sur le téléphone est une perte de nombreuses ressources précieuses - la bande passante, la batterie et de la mémoire. Tous qui sont rares sur les téléphones.

Créé 05/03/2009 à 10:23
source utilisateur

voix
5

J'ai développé une solution ultime pour mise à l'échelle de l'image dans Swift.

Vous pouvez l'utiliser pour redimensionner l'image pour remplir, remplir aspect ou aspect adapter la taille spécifiée.

Vous pouvez aligner l'image au centre ou l'un des quatre bords et quatre angles.

Et vous pouvez également rogner l'espace supplémentaire qui est ajouté si des rapports d'aspect de l'image d'origine et la taille de la cible ne sont pas égaux.

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

Il existe des exemples d'application de cette solution ci-dessous.

Le rectangle gris est l' image cible du site sera redimensionnée à. Les cercles bleus dans le rectangle bleu clair est l'image (je les cercles parce qu'il est facile de voir quand il est mis à l' échelle sans aspect préservation). Marque la lumière couleur orange des zones qui seront rognées si vous passez trim: true.

Aspect adapter avant et après mise à l' échelle:

Aspect forme 1 (avant) Aspect adapter 1 (après)

Un autre exemple d' aspect en forme :

Aspect ajustement 2 (avant) Aspect adapter 2 (après)

Aspect correspond à l' alignement du haut:

Aspect ajustement 3 (avant) Aspect adapter 3 (après)

Remplissage Aspect :

remplissage d'image (avant) remplissage d'image (après)

remplir :

Remplir (avant) Remplir (après)

Je upscaling dans mes exemples, car il est plus simple de démontrer, mais la solution fonctionne aussi pour downscaling comme question.

Pour la compression JPEG, vous devez utiliser ceci:

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

Vous pouvez consulter mon point essentiel avec aire de jeux Xcode.

Créé 23/03/2016 à 13:51
source utilisateur

voix
3

Pour Swift 3, le code ci - dessous échelle de l'image en conservant le rapport d'aspect. Vous pouvez en savoir plus sur la ImageContext dans la documentation d'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!
    }
}

Pour l' utiliser, appelez la resizeImage()méthode:

UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
Créé 14/04/2017 à 03:50
source utilisateur

voix
2

vous pouvez utiliser ce code à l'échelle image taille requise.

+ (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;
}
Créé 30/09/2015 à 10:51
source utilisateur

voix
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
}
Créé 12/04/2018 à 10:25
source utilisateur

voix
1

Ajoutant à la flopée de réponses, mais je suis allé pour une solution qui redimensionne par taille de fichier, plutôt que les dimensions.

Cela permettra à la fois de réduire les dimensions et la qualité de l'image jusqu'à ce qu'elle atteigne la taille donnée.

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
}

Crédit pour mise à l' échelle par réponse taille

Créé 10/07/2017 à 11:24
source utilisateur

voix
0

Si vous l' image est dans le répertoire de documents, Ajouter cette URL extension:

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

Usage:

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

Note: - Change <LocalImagePath.jpg> avec votre chemin de l'image locale jpg.

Créé 18/05/2018 à 10:02
source utilisateur

voix
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;
}
Créé 03/11/2016 à 08:37
source utilisateur

voix
0

Je voulais juste répondre à cette question pour les programmeurs Cocoa Swift. Cette fonction retourne NSImage avec la nouvelle taille. Vous pouvez utiliser cette fonction comme celui-ci.

        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

}
Créé 13/07/2016 à 09:00
source utilisateur

voix
0

Essayez cela, il travaille pour moi,

       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];
Créé 18/03/2016 à 11:48
source utilisateur

voix
0

Je fini par utiliser la technique Brads pour créer une scaleToFitWidthméthode UIImage+Extensionssi cela est utile à tout le monde ...

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

alors où vous voulez

#import "UIImage+Extensions.h"

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

Prenant note également une valeur que vous pouvez déplacer cet autre en une UIView+Extensionsclasse si vous voulez rendre des images à partir d' un UIView

Créé 15/03/2016 à 14:49
source utilisateur

voix
0

Si quelqu'un cherche toujours meilleure option

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

}
Créé 23/06/2015 à 07:39
source utilisateur

voix
0

Un problème qui pourrait se produire sur les écrans de la rétine est que l'échelle de l'image est définie par ImageCapture ou si. Les fonctions de redimensionnement ci-dessus ne changera rien. Dans ces cas, le redimensionnement ne fonctionnera pas correctement.

Dans le code ci - dessous, l'échelle est réglée sur 1 (mise à l' échelle) et l'image renvoyée a la taille que vous attendez. Cela se fait dans l' UIGraphicsBeginImageContextWithOptionsappel.

-(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;
}
Créé 06/04/2015 à 10:24
source utilisateur

voix
-2

Pour redimensionner une image que j'ai de meilleurs résultats (graphiques) en utilisant cette fonction au lieu 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;
}

Format d'image est pris en charge automatiquement pour

Créé 01/09/2013 à 15:46
source utilisateur

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