web-dev-qa-db-de.com

Wie kann ich die Größe eines Bildes mit iOS ändern?

Meine Anwendung lädt eine Reihe von Bilddateien aus dem Netzwerk herunter und speichert sie auf der lokalen iPhone-Festplatte. Einige dieser Bilder sind ziemlich groß (z. B. Breiten größer als 500 Pixel). Da das iPhone nicht einmal über eine ausreichend große Anzeige verfügt, um das Bild in seiner ursprünglichen Größe darzustellen, plane ich, das Bild auf etwas kleiner zu skalieren, um Platz und Leistung zu sparen.

Bei einigen dieser Bilder handelt es sich ebenfalls um JPEG-Bilder, die nicht in der üblichen Einstellung für 60% Qualität gespeichert werden.

Wie kann ich die Größe eines Bildes mit dem iPhone SDK ändern und wie kann ich die Qualitätseinstellung eines JPEG-Bildes ändern?

108
jpm

Ein paar Vorschläge werden als Antwort auf diese Frage gegeben. Ich hatte die in this post beschriebene Technik mit dem entsprechenden Code vorgeschlagen:

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

   return newImage;
}

Was das Speichern des Bildes anbelangt, so ist PNG das schnellste Bildformat, das mit dem iPhone verwendet werden kann, da es für dieses Format Optimierungen aufweist. Wenn Sie diese Bilder jedoch als JPEG-Dateien speichern möchten, können Sie Ihre UIImage wie folgt verwenden:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

Dadurch wird eine NSData-Instanz erstellt, die die Rohbytes für ein JPEG-Bild mit einer Qualitätseinstellung von 60% enthält. Der Inhalt dieser NSData-Instanz kann dann auf die Festplatte geschrieben oder im Arbeitsspeicher zwischengespeichert werden.

240
Brad Larson

Dies ist der einfachste und einfachste Weg, um die Größe Ihrer Bilder zu ändern

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();
64
lostInTransit

Die oben genannten Methoden funktionieren gut für kleine Bilder. Wenn Sie jedoch versuchen, die Größe eines sehr großen Bildes zu ändern, wird der Speicher schnell aufgebraucht und die App zum Absturz gebracht. Ein viel besserer Weg ist, CGImageSourceCreateThumbnailAtIndex zu verwenden, um die Größe des Bildes zu ändern, ohne es vorher vollständig dekodieren zu müssen. 

Wenn Sie den Pfad zu dem Bild haben, dessen Größe Sie ändern möchten, können Sie Folgendes verwenden: 

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

Weitere Details hier .

24
Pulkit Goyal

Die beste Möglichkeit, Bilder zu skalieren, ohne das Seitenverhältnis zu verlieren (d. H. Ohne das Bild zu dehnen), ist die Verwendung dieser Methode:

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

}

Fügen Sie diese Methode Ihrer Utility-Klasse hinzu, damit Sie sie im gesamten Projekt verwenden können und auf diese Weise wie folgt zugreifen:

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

Diese Methode sorgt für die Skalierung, während das Seitenverhältnis beibehalten wird ..__ Sie fügt dem Bild außerdem Einrückungen hinzu, falls das verkleinerte Bild mehr Breite als Höhe hat (oder umgekehrt).

18
codeburn

Wenn Sie die Kontrolle über den Server haben, würde ich dringend empfehlen, die Imageserverseite mit ImageMagik zu ändern. Das Herunterladen großer Bilder und deren Größenänderung am Telefon ist eine Verschwendung vieler kostbarer Ressourcen - Bandbreite, Batterie und Speicher. All dies ist auf Handys knapp.

7
Rog

Ich habe eine ultimative Lösung für die Bildskalierung in Swift entwickelt.

Sie können es verwenden, um die Größe des Bildes auf die angegebene Größe zu ändern. 

Sie können das Bild auf die Mitte oder auf vier Kanten und vier Ecken ausrichten.

Sie können auch zusätzlichen Platz zuschneiden, der hinzugefügt wird, wenn die Seitenverhältnisse von Originalbild und Zielgröße nicht gleich sind.

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

Nachfolgend finden Sie Beispiele für die Anwendung dieser Lösung.

Graues Rechteck ist das Ziel-Site-Bild wird in ... verkleinert. Blaue Kreise in hellblauem Rechteck sind das Bild (ich habe Kreise verwendet, da es leicht zu sehen ist, wenn es skaliert wird, ohne dass der Aspekt erhalten bleibt) wird abgeschnitten, wenn Sie trim: true übergeben.

Aspect Fit vor und nach der Skalierung:

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

Ein weiteres Beispiel für aspect fit:

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

Aspect Fit mit oberer Ausrichtung:

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

Aspektfüllung:

 Aspect fill (before)  Aspect fill (after)

Füllen:

 Fill (before)  Fill (after)

Ich habe in meinen Beispielen das Upscaling verwendet, weil es einfacher zu demonstrieren ist, aber die Lösung auch für das Downscaling funktioniert.

Für die JPEG-Komprimierung sollten Sie Folgendes verwenden:

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

Sie können meine Gist mit Xcode-Spielplatz ausprobieren.

5
mixel

Bei Swift 3 skaliert der folgende Code das Bild unter Beibehaltung des Seitenverhältnisses. Weitere Informationen zum ImageContext finden Sie in Apples Dokumentation :

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

Rufen Sie zur Verwendung die resizeImage()-Methode auf:

UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
3
Alexandre Lara

sie können diesen Code verwenden, um das Bild in der erforderlichen Größe zu skalieren.

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

Ich füge noch mehr Antworten hinzu, aber ich habe mich für eine Lösung entschieden, deren Größe sich nach Dateigröße und nicht nach Abmessungen richtet. 

Dadurch werden sowohl die Abmessungen als auch die Qualität des Bildes reduziert, bis es Ihre vorgegebene Größe erreicht.

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
}

Gutschrift für Skalierung nach Größenantwort

1
Harry Bloom

Am Ende habe ich die Brads-Technik benutzt, um eine scaleToFitWidth-Methode in UIImage+Extensions zu erstellen, falls dies für jeden nützlich ist ...

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

dann wo immer du magst

#import "UIImage+Extensions.h"

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

Beachten Sie auch, dass Sie dies weiter unten in eine UIView+Extensions-Klasse verschieben können, wenn Sie Bilder aus einer UIView rendern möchten

0
Magoo

Wenn sich das Bild im Dokumentverzeichnis befindet, fügen Sie dieseURLextension hinzu:

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

Verwendungszweck:

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

Hinweis: - Ändern Sie <LocalImagePath.jpg> mit Ihrem lokalen JPG-Bildpfad.

0
Zaid Pathan
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
}
0
Softlabsindia

Ich wollte diese Frage nur für Cocoa Swift-Programmierer beantworten. Diese Funktion gibt NSImage mit neuer Größe zurück. Sie können diese Funktion so verwenden. 

        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

}
0
Hope

Ein Problem, das bei Retina-Displays auftreten kann, besteht darin, dass die Bildskala von ImageCapture oder dergleichen festgelegt wird. Die Größenänderungsfunktionen oben ändern daran nichts. In diesen Fällen funktioniert die Größenänderung nicht richtig.

Im folgenden Code ist der Maßstab auf 1 gesetzt (nicht skaliert) und das zurückgegebene Bild hat die erwartete Größe. Dies geschieht im Aufruf UIGraphicsBeginImageContextWithOptions.

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