Hva er den enkleste måten å endre størrelse / optimalisere en bildestørrelse med iPhone SDK?

stemmer
108

Min søknad er å laste ned et sett med bildefiler fra nettet og lagre dem til den lokale iPhone disk. Noen av disse bildene er ganske store i størrelse (bredder større enn 500 bildeelementer, for eksempel). Siden iPhone ikke selv har en stor nok skjerm til å vise bildet i sin opprinnelige størrelse, planlegger jeg på å endre størrelse på bildet til noe litt mindre for å spare plass / ytelse.

Også noen av disse bildene er JPEG, og de blir ikke lagret som vanlig 60% kvalitetsinnstilling.

Hvordan kan jeg endre størrelsen på et bilde med iPhone SDK, og hvordan kan jeg endre kvalitetsinnstillingen til et JPEG-bilde?

Publisert på 04/03/2009 klokken 19:42
kilden bruker
På andre språk...                            


18 svar

stemmer
240

Et par av forslagene er gitt som svar på dette spørsmålet . Jeg hadde foreslått teknikken beskrevet i dette innlegget , med relevant kode:

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

   return newImage;
}

Såvidt lagring av bildet, den raskeste bildeformatet som skal brukes med iPhone er PNG, fordi den har optimaliseringer for dette formatet. Men hvis du ønsker å lagre disse bildene som JPEG, kan du ta med UIImage og gjør følgende:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

Dette skaper en NSData eksempel inneholder de rå byte for en JPEG-bildet med en kvalitetsinnstilling 60%. Innholdet i at NSData eksempel kan deretter skrives til disk eller lagret i minnet.

Svarte 05/03/2009 kl. 02:20
kilden bruker

stemmer
64

Den enkleste og enkleste måten å endre størrelsen på bildene ville være dette

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();
Svarte 05/03/2009 kl. 04:35
kilden bruker

stemmer
24

De ovennevnte metodene fungerer godt for små bilder, men når du prøver å endre størrelsen på et veldig stort bilde, vil du raskt gå tom for minne og krasjer programmet. En mye bedre måte er å bruke CGImageSourceCreateThumbnailAtIndexfor å endre størrelse på bildet uten helt å dekode det første.

Hvis du har banen til bildet du ønsker å endre størrelse, kan du bruke denne:

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

Flere detaljer her .

Svarte 01/09/2014 kl. 11:03
kilden bruker

stemmer
18

Beste måte for å skalere bilder uten å miste den sideforhold (dvs. uten å strekke imgage), er å bruke denne metoden:

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

}

Legg denne metoden til Utility klasse slik at du kan bruke den i hele prosjektet, og få tilgang til den slik:

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

Denne metoden sørger for å skalere mens sideforhold opprettholdes. Den legger også innrykk til bildet i tilfelle skalert ned bildet har mer bredde enn høyde (eller vice versa).

Svarte 03/05/2013 kl. 08:40
kilden bruker

stemmer
7

Hvis du har kontroll over serveren, vil jeg sterkt anbefale å endre størrelse på bildene serversiden med Imagemagik . Nedlasting av store bilder og endre størrelse på dem på telefonen er en sløsing med mange verdifulle ressurser - båndbredde, batteri og minne. Alt som er mangelvare på telefoner.

Svarte 05/03/2009 kl. 09:23
kilden bruker

stemmer
5

Jeg utviklet en endelig løsning for bildeskalering i Swift.

Du kan bruke den til å endre størrelsen på bildet for å fylle, aspektet fylle eller aspekt passe bestemt størrelse.

Du kan justere bildet til sentrum eller en av fire kanter og fire hjørner.

Og også kan du trimme ekstra plass som er lagt til dersom sideforhold på originalbildet og målet størrelse er ikke like.

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

Det finnes eksempler på anvendelsen av denne løsningen nedenfor.

Grå rektangelet er målområde bilde vil bli endret til. Blå sirkler i lys blå firkant er bildet (jeg brukte sirkler fordi det er lett å se når det skaleres uten bevare aspektet). Lys oransje fargen markerer områder som vil bli trimmet hvis du passerer trim: true.

Aspect passe før og etter skalering:

Aspect pasning 1 (før) Aspect passe 1 (etter)

Et annet eksempel på aspekt fit :

Aspect fit 2 (før) Aspect passe 2 (etter)

Aspect passe med toppjustering:

Aspect fit 3 (før) Aspect passe 3 (etter)

Aspect fill :

Aspect fyll (før) Aspect fyll (etter)

Fyll :

Fyll (før) Fyll (etter)

Jeg brukte oppskalering i mine eksempler fordi det er enklere å demonstrere, men løsningen fungerer også for nedskalering som i spørsmålet.

For JPEG-komprimering bør du bruke denne:

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

Du kan sjekke ut min kjerne med Xcode lekeplass.

Svarte 23/03/2016 kl. 13:51
kilden bruker

stemmer
3

For Swift 3, skalerer under kode bildet holder bildeforholdet. Du kan lese mer om ImageContext i Apples dokumentasjon :

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

Å bruke den, kaller resizeImage()metoden:

UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
Svarte 14/04/2017 kl. 03:50
kilden bruker

stemmer
2

kan du bruke denne koden for å skalere bildet i ønsket størrelse.

+ (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;
}
Svarte 30/09/2015 kl. 10:51
kilden bruker

stemmer
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
}
Svarte 12/04/2018 kl. 10:25
kilden bruker

stemmer
1

Legge til massevis av svar her, men jeg har gått for en løsning som endrer størrelsen etter filstørrelse, snarere enn dimensjoner.

Dette vil både redusere dimensjonene og kvalitet av bildet til den når din gitt størrelse.

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
}

Credit for skalering av størrelsen svar

Svarte 10/07/2017 kl. 11:24
kilden bruker

stemmer
0

Hvis du bildet er i dokument, Legg denne URL forlengelse:

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

bruk:

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

Merk: - Endre <LocalImagePath.jpg> med din lokale jpg bilde banen.

Svarte 18/05/2018 kl. 10:02
kilden bruker

stemmer
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;
}
Svarte 03/11/2016 kl. 08:37
kilden bruker

stemmer
0

Jeg ville bare svare på det spørsmålet for Cocoa Swift programmerere. Denne funksjonen returnerer NSImage med nye størrelsen. Du kan bruke denne funksjonen som dette.

        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

}
Svarte 13/07/2016 kl. 09:00
kilden bruker

stemmer
0

Prøv dette, det fungerer for meg,

       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];
Svarte 18/03/2016 kl. 11:48
kilden bruker

stemmer
0

Jeg endte opp med å bruke Brads teknikk for å lage en scaleToFitWidthmetode UIImage+Extensionshvis det er nyttig for alle ...

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

deretter hvor du vil

#import "UIImage+Extensions.h"

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

Også verdt å merke seg at du kan flytte denne lenger ned i en UIView+Extensionsklasse hvis du ønsker å gjengi bilder fra en UIView

Svarte 15/03/2016 kl. 14:49
kilden bruker

stemmer
0

Hvis noen fortsatt på jakt etter bedre alternativ

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

}
Svarte 23/06/2015 kl. 07:39
kilden bruker

stemmer
0

Et problem som kan oppstå på retinaskjermer er at omfanget av bildet er satt av Imagecapture eller så. Resize funksjoner ovenfor vil ikke endre det. I disse tilfellene resize vil fungere ordentlig.

I koden nedenfor, er skalaen satt til en (ikke skalert) og den returnerte bildet har den størrelsen du forventer. Dette gjøres i UIGraphicsBeginImageContextWithOptionssamtalen.

-(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;
}
Svarte 06/04/2015 kl. 10:24
kilden bruker

stemmer
-2

For å endre størrelsen på et bilde jeg har bedre (grafiske) resultater ved hjelp av denne funksjonen i stedet for 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;
}

Størrelsesforhold blir ivaretatt for automatisk

Svarte 01/09/2013 kl. 14:46
kilden bruker

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