Recadrer un UIImage


209

J'ai un code qui redimensionne une image afin que je puisse obtenir un morceau à l'échelle du centre de l'image - je l'utilise pour prendre un UIImageet retourner une petite représentation carrée d'une image, similaire à ce qui est vu dans la vue de l'album de l'application Photos. (Je sais que je pourrais utiliser un UIImageViewet ajuster le mode de recadrage pour obtenir les mêmes résultats, mais ces images sont parfois affichées dans UIWebViews).

J'ai commencé à remarquer des plantages dans ce code et je suis un peu perplexe. J'ai deux théories différentes et je me demande si l'une ou l'autre est sur la base.

Théorie 1) Je réalise le recadrage en dessinant dans un contexte d'image hors écran de ma taille cible. Puisque je veux la partie centrale de l'image, je mets l' CGRectargument passé àdrawInRect à quelque chose qui est plus grand que les limites de mon contexte d'image. J'espérais que c'était casher, mais est-ce que j'essaye plutôt de dessiner sur d'autres souvenirs que je ne devrais pas toucher?

Théorie 2) Je fais tout cela dans un fil de fond. Je sais qu'il y a des parties d'UIKit qui sont limitées au thread principal. Je supposais / espérais que dessiner sur une vue hors écran n'en faisait pas partie. Ai-je tort?

(Oh, comment je manque la NSImage's drawInRect:fromRect:operation:fraction:méthode.)


Si vous essayez de diagnostiquer un plantage, vous devez exécuter l'application sous le débogueur et noter ce qui se passe quand il se bloque. Vous n'avez même pas identifié si une exception est levée ou si vous obtenez EXC_BAD_ACCESS en raison d'un pointeur suspendu. Une fois que vous le savez, vous pouvez commencer à faire des théories.
benzado

C'est suffisant. Je n'ai pas reproché cela sous le débogueur, bien que j'ai des messages EXC_BAD_ACCESS dans le journal des plantages. Quand j'ai posté cela, je travaillais sous l'hypothèse que j'avais fait une erreur stupide dans mon implémentation et que quelqu'un sauterait dessus (comme oublier un chemin de détourage).
Jablair

Malgré ce que cela vaut et les bonnes réponses ci-dessous, il existe une bonne enquête sur les techniques et leurs performances sur NSHipster: nshipster.com/image-resizing . Le puriste en moi voulait utiliser CIImage, mais le pragmatique a choisi le contexte UIKit / image.
Chris Conover

Réponses:


236

Mise à jour 2014-05-28: J'ai écrit cela lorsque iOS 3 ou quelque chose de nouveau était chaud, je suis certain qu'il existe de meilleures façons de le faire maintenant, peut-être intégré. Comme beaucoup de personnes l'ont mentionné, cette méthode ne prend pas en compte la rotation; lisez quelques réponses supplémentaires et répandez un peu d'amour pour que les réponses à cette question soient utiles à tout le monde.

Réponse originale:

Je vais copier / coller ma réponse à la même question ailleurs:

Il n'y a pas de méthode de classe simple pour ce faire, mais il existe une fonction que vous pouvez utiliser pour obtenir les résultats souhaités: CGImageCreateWithImageInRect(CGImageRef, CGRect)vous aidera.

Voici un court exemple d'utilisation:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);

13
Lorsque le imageOrientationest réglé sur autre chose que vers le haut, le CGImagefait pivoter par rapport au UIImageet le rectangle de recadrage sera incorrect. Vous pouvez faire pivoter le rectangle de recadrage en conséquence, mais le fraîchement créé UIImageaura imageOrientation, de sorte que le recadré CGImagesera pivoté à l'intérieur.
zoul

25
Je tiens à souligner que sur l'écran de la rétine, vous devez doubler la largeur et la hauteur de votre cropRect en utilisant cette méthode. Juste quelque chose que j'ai rencontré.
petrocket

21
pour le faire fonctionner dans n'importe quelle orientation:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis

3
Vous devez ajouter un support pour Retina !!
Mário Carvalho

1
@NicosKaralis qui ne le fera pivoter que dans la bonne orientation. Cependant, la zone que vous recadrez sera toujours erronée.
Tim Bodeit

90

Pour recadrer des images de la rétine tout en conservant la même échelle et la même orientation, utilisez la méthode suivante dans une catégorie UIImage (iOS 4.0 et supérieur):

- (UIImage *)crop:(CGRect)rect {
    if (self.scale > 1.0f) {
        rect = CGRectMake(rect.origin.x * self.scale,
                          rect.origin.y * self.scale,
                          rect.size.width * self.scale,
                          rect.size.height * self.scale);
    }

    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}

14
Vous pouvez simplement tuer le conditionnel ici et toujours le multiplier self.scale, non?
Michael Mior

2
Vous avez raison, Michael, mais je pense que la solution ci-dessus est un peu plus rapide sur les appareils non rétiniens car elle ne fait qu'une seule vérification au lieu de quatre multiplications plus une affectation. Sur les appareils rétiniens, ce n'est qu'une simple vérification booléenne plus que nécessaire, c'est donc une question de préférence personnelle ou d'ajustement.
CGee

2
Est-il correct d'utiliser CGImageRelease(imageRef);avec ARC activé?
CGee


3
En fait, 4 multiplications peuvent être plus rapides que l'évaluation d'une condition :) Les multiplications peuvent être assez bien optimisées sur les processeurs modernes alors que les conditions ne peuvent être prédites. Mais vous avez raison, c'est une question de préférence. Je préfère le code plus court / plus simple en raison de la lisibilité accrue.
marsbear

65

Vous pouvez créer une catégorie UIImage et l'utiliser où vous en avez besoin. Basé sur la réponse et les commentaires de HitScans ci-dessous.

@implementation UIImage (Crop)

- (UIImage *)crop:(CGRect)rect {

    rect = CGRectMake(rect.origin.x*self.scale, 
                      rect.origin.y*self.scale, 
                      rect.size.width*self.scale, 
                      rect.size.height*self.scale);       

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef 
                                          scale:self.scale 
                                    orientation:self.imageOrientation]; 
    CGImageRelease(imageRef);
    return result;
}

@end

Vous pouvez l'utiliser de cette façon:

UIImage *imageToCrop = <yourImageToCrop>;
CGRect cropRect = <areaYouWantToCrop>;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];

3
Ça ne devrait pas [[UIScreen mainScreen] scale]être juste self.scale? L'échelle de l'image peut ne pas être la même que l'échelle de l'écran.
Michael Mior

Salut, j'ai essayé votre réponse et elle me donne No visible @interface for 'UIImage' declares the selector 'crop'même si j'ai mis les fichiers de catégorie .h et .m dans mon projet et importé le .h dans la classe que j'utilise dans la catégorie. Une idée?
Ali

A corrigé. J'ai manqué de mettre l'en-tête de la méthode dans le fichier UIImage + Crop.h.
Ali

Je souhaite recadrer l'image en forme circulaire. De sorte que nous ne pouvons voir que le chemin circulaire et l'autre chemin reste transparent.
Alfa

Que vous pouvez utiliser CGImageCreateWithMask ou CGImageCreateWithMaskingColors au lieu de CGImageCreateWithImageInRect
Vilém Kurz

54

Version Swift 3

func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{
    
    let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)!
    let cropped:UIImage = UIImage(cgImage:imageRef)
    return cropped
}


let imageTop:UIImage  = UIImage(named:"one.jpg")! // add validation

entrez la description de l'image ici

avec l'aide de cette fonction de pont CGRectMake-> CGRect(crédits à cette réponse répondu par @rob mayoff):

 func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
    return CGRect(x: x, y: y, width: width, height: height)
}

L'utilisation est:

if var image:UIImage  = UIImage(named:"one.jpg"){
   let  croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake(
        image.size.width/4,
        0,
        image.size.width/2,
        image.size.height)
    )
}

Production:

entrez la description de l'image ici


Je ne sais pas pourquoi il a été rejeté, j'ai pensé que c'était une bonne réponse. La seule chose à laquelle je peux penser est qu'il ne gère pas la mise à l'échelle, donc cela ne fonctionnera pas si vous avez des images @ 2x / @ 3x et que les noms de vos fonctions ne correspondent pas.
William T.

4
Vous devez guardla première ligne en cas de UIImage.CGImageretours nilet pourquoi l'utiliser varquand letfonctionne parfaitement.
NoodleOfDeath

C'est correct, TOUTEFOIS, lisez cropping(to:)très attentivement les documents. Le résultat CGImagecontient une forte référence à la plus grande à CGImagepartir de laquelle il a été recadré. Donc , si vous voulez seulement accrocher à l'image recadrée et ont pas besoin original, jetez un oeil sur les autres solutions.
jsadler du

Cela ne fonctionne pas avec les images dotées de balises d'orientation. La zone de recadrage ne tourne pas correctement.
Max

1
@Max c'est parce que cgImage n'a pas de support d'orientation. Si UIImage a rightalors le cgImage devrait être tourné de 90 degrés ccw, donc le rect de recadrage devrait aussi être tourné
MK Yung

49

Voici mon implémentation de recadrage UIImage qui obéit à la propriété imageOrientation. Toutes les orientations ont été minutieusement testées.

inline double rad(double deg)
{
    return deg / 180.0 * M_PI;
}

UIImage* UIImageCrop(UIImage* img, CGRect rect)
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}

7
L'avertissement implicit declaration of function 'rad' is invalid in c99peut être supprimé en remplaçant: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster

Oops désolé. Ajout de la fonction utilitaire rad () à l'extrait de code source.
Sergii Rudchenko

contains undefined reference for architecture armv7Suis-je absent d'une bibliothèque? CoreGraphics est importé.
Bob Spryn

1
@SergiiRudchenko "Toutes les orientations ont été minutieusement testées." - Cela inclut-il les orientations reflétées?
Tim Bodeit du

@BobSpryn Non, vous ne manquez pas de bibliothèque. Bien que je ne puisse pas expliquer ce que signifie l'erreur, le remplacement de rad (), comme l'a suggéré SoundBlaster, corrige également cette erreur.
Tim Bodeit

39

CGImageAttention : toutes ces réponses supposent un objet image à dos.

image.CGImagepeut retourner nil, si le UIImageest soutenu par un CIImage, ce qui serait le cas si vous créiez cette image à l'aide d'un CIFilter.

Dans ce cas, vous devrez peut-être dessiner l'image dans un nouveau contexte et renvoyer cette image ( lentement ).

UIImage* crop(UIImage *image, rect) {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]);
    [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

5
C'est exactement ce dont j'avais besoin, fonctionne dans tous les scénarios donc mieux que toutes les autres solutions!
David Thompson

2
image.size dans la première ligne doit être rect.size UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Brett

2
Merci Brett, définitivement là. J'ai mis à jour le code pour inclure ce correctif.
colinta

Pourquoi est -rect.origin.x non seulement rect.origin.x?
Aggressor

2
Parce que nous recadrons; en d'autres termes, l'argument "rect" dit "Je veux que la partie supérieure gauche de la nouvelle image commence, disons, [10, 10]". Pour ce faire, nous dessinons l'image de sorte que [10, 10] soit à l'origine de la nouvelle image.
colinta

35

Aucune des réponses ici ne traite correctement tous les problèmes d'échelle et de rotation. Voici une synthèse de tout ce qui a été dit jusqu'à présent, à jour depuis iOS7 / 8. Il est censé être inclus en tant que méthode dans une catégorie sur UIImage.

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}

1
Pourquoi cette réponse n'est-elle pas plus votée? J'ai essayé toutes les réponses ci-dessus et rencontré des tonnes de problèmes (notamment la conversion d'UIImage en CIImage et la perte de données de transformation appropriées). C'EST LE SEUL QUI FONCTIONNE POUR MOI!
Aggressor

1
Merci @Aggressor, heureux que cela fonctionne pour vous. Ma réponse a été publiée il y a seulement un mois alors que beaucoup de ces réponses existent depuis 5 ans. Je suppose que cela explique le manque de votes positifs comparables.
awolf

J'utilise cela pour faire glisser les filtres sur les images (comme le fait Snapchat) et j'ai de légers problèmes de mise à l'échelle. Y a-t-il quelque chose dans votre code que vous pouvez suggérer que j'examine qui pourrait contenir la cause de cette erreur de mise à l'échelle? J'ai posté ma solution qui utilise votre code ici: stackoverflow.com/questions/23319497/…
Aggressor

une fonction très utile, merci. des conseils ce qui devrait être modifié pour recadrer en se concentrant sur la partie centrale de l'image?
privé

CECI DEVRAIT ÊTRE LA RÉPONSE.
horseshoe7

17

Version rapide de awolfla réponse de, qui a fonctionné pour moi:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}

10
CGSize size = [originalImage size];
int padding = 20;
int pictureSize = 300;
int startCroppingPosition = 100;
if (size.height > size.width) {
    pictureSize = size.width - (2.0 * padding);
    startCroppingPosition = (size.height - pictureSize) / 2.0; 
} else {
    pictureSize = size.height - (2.0 * padding);
    startCroppingPosition = (size.width - pictureSize) / 2.0;
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that 
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation];
[m_photoView setImage:newImage];
CGImageRelease(imageRef);

La plupart des réponses que j'ai vues ne concernent qu'une position de (0, 0) pour (x, y). Ok c'est un cas mais j'aimerais que mon opération de recadrage soit centrée. Ce qui m'a pris du temps à comprendre, c'est la ligne qui suit le commentaire de la WTF.

Prenons le cas d'une image capturée avec une orientation portrait:

  1. La hauteur de l'image d'origine est supérieure à sa largeur (Woo, pas de surprise jusqu'à présent!)
  2. L'image que la méthode CGImageCreateWithImageInRect imagine dans son propre monde n'est pas vraiment un portrait mais un paysage (c'est aussi pourquoi si vous n'utilisez pas l'argument d'orientation dans le constructeur imageWithCGImage, il apparaîtra comme tourné à 180).
  3. Donc, vous devriez imaginer que c'est un paysage, la position (0, 0) étant le coin supérieur droit de l'image.

J'espère que cela a du sens! Si ce n'est pas le cas, essayez différentes valeurs, vous verrez que la logique est inversée quand il s'agit de choisir les bons x, y, largeur et hauteur pour votre cropRect.


8

swift3

extension UIImage {
    func crop(rect: CGRect) -> UIImage? {
        var scaledRect = rect
        scaledRect.origin.x *= scale
        scaledRect.origin.y *= scale
        scaledRect.size.width *= scale
        scaledRect.size.height *= scale
        guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else {
            return nil
        }
        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}

7

Extension rapide

extension UIImage {
    func crop(var rect: CGRect) -> UIImage {
        rect.origin.x*=self.scale
        rect.origin.y*=self.scale
        rect.size.width*=self.scale
        rect.size.height*=self.scale

        let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
        let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
        return image
    }
}

4

Meilleure solution pour recadrer un UIImage dans Swift , en terme de précision, mise à l'échelle des pixels ...:

private func squareCropImageToSideLength(let sourceImage: UIImage,
    let sideLength: CGFloat) -> UIImage {
        // input size comes from image
        let inputSize: CGSize = sourceImage.size

        // round up side length to avoid fractional output size
        let sideLength: CGFloat = ceil(sideLength)

        // output size has sideLength for both dimensions
        let outputSize: CGSize = CGSizeMake(sideLength, sideLength)

        // calculate scale so that smaller dimension fits sideLength
        let scale: CGFloat = max(sideLength / inputSize.width,
            sideLength / inputSize.height)

        // scaling the image with this scale results in this output size
        let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale,
            inputSize.height * scale)

        // determine point in center of "canvas"
        let center: CGPoint = CGPointMake(outputSize.width/2.0,
            outputSize.height/2.0)

        // calculate drawing rect relative to output Size
        let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0,
            center.y - scaledInputSize.height/2.0,
            scaledInputSize.width,
            scaledInputSize.height)

        // begin a new bitmap context, scale 0 takes display scale
        UIGraphicsBeginImageContextWithOptions(outputSize, true, 0)

        // optional: set the interpolation quality.
        // For this you need to grab the underlying CGContext
        let ctx: CGContextRef = UIGraphicsGetCurrentContext()
        CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)

        // draw the source image into the calculated rect
        sourceImage.drawInRect(outputRect)

        // create new image from bitmap context
        let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()

        // clean up
        UIGraphicsEndImageContext()

        // pass back new image
        return outImage
}

Instructions utilisées pour appeler cette fonction:

let image: UIImage = UIImage(named: "Image.jpg")!
let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320)
self.myUIImageView.image = squareImage

Remarque: l'inspiration initiale du code source écrite en Objective-C a été trouvée sur le blog "Cocoanetics".


3

L'extrait de code ci-dessous peut être utile.

import UIKit

extension UIImage {
    func cropImage(toRect rect: CGRect) -> UIImage? {
        if let imageRef = self.cgImage?.cropping(to: rect) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }
}

2

Semble un peu étrange mais fonctionne très bien et prend en compte l'orientation de l'image:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)

1
mais cela casse évidemment l'échelle
Sulthan

2

Swift 5:

extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        guard let cgImage = cgImage else { return nil }

        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        let context = UIGraphicsGetCurrentContext()

        context?.translateBy(x: 0.0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.draw(cgImage, in: CGRect(x: rect.minX, y: rect.minY, width: self.size.width, height: self.size.height), byTiling: false)


        let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return croppedImage
    }
}

1
- (UIImage *)getSubImage:(CGRect) rect{
    CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));

    UIGraphicsBeginImageContext(smallBounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, smallBounds, subImageRef);
    UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
    UIGraphicsEndImageContext();

    return smallImg;
}

1
 (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    double ratio;
    double delta;
    CGPoint offset;

    //make a new square size, that is the resized imaged width
    CGSize sz = CGSizeMake(newSize.width, newSize.width);

    //figure out if the picture is landscape or portrait, then
    //calculate scale factor and offset
    if (image.size.width > image.size.height) {
        ratio = newSize.width / image.size.width;
        delta = (ratio*image.size.width - ratio*image.size.height);
        offset = CGPointMake(delta/2, 0);
    } else {
        ratio = newSize.width / image.size.height;
        delta = (ratio*image.size.height - ratio*image.size.width);
        offset = CGPointMake(0, delta/2);
    }

    //make the final clipping rect based on the calculated values
    CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                                 (ratio * image.size.width) + delta,
                                 (ratio * image.size.height) + delta);


    //start a new context, with scale factor 0.0 so retina displays get
    //high quality image
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
    } else {
        UIGraphicsBeginImageContext(sz);
    }
    UIRectClip(clipRect);
    [image drawInRect:clipRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

1

Sur iOS9.2SDK, j'utilise la méthode ci-dessous pour convertir le cadre de UIView en UIImage

-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
  CGSize cropSize = rect.size;
  CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width;
  CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height;
  cropSize = CGSizeMake(rect.size.width*widthScale, 
              rect.size.height*heightScale);
  CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale,
             rect.origin.y*heightScale);
  rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height);
  CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
  UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
  CGImageRelease(subImage);

  return croppedImage;
}

1

Mise à jour Swift 2.0 ( CIImagecompatibilité)

Extension de la réponse de Maxim, mais fonctionne également si votre image est CIImagebasée.

public extension UIImage {
    func imageByCroppingToRect(rect: CGRect) -> UIImage? {
        if let image = CGImageCreateWithImageInRect(self.CGImage, rect) {
            return UIImage(CGImage: image)
        } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) {
            return UIImage(CIImage: image)
        }
       return nil
   }
}

1

Voici une version mise à jour de Swift 3 basée sur la réponse de Noodles

func cropping(to rect: CGRect) -> UIImage? {

    if let cgCrop = cgImage?.cropping(to: rect) {
        return UIImage(cgImage: cgCrop)
    }
    else if let ciCrop = ciImage?.cropping(to: rect) {
        return UIImage(ciImage: ciCrop)
    }

    return nil
}

1

Suivez la réponse de @Arne. Je fixe juste la fonction Catégorie. mettez-le dans la catégorie de UIImage.

-(UIImage*)cropImage:(CGRect)rect{

    UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]);
    [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

0

Je n'étais pas satisfait des autres solutions car elles consomment plusieurs fois (en utilisant plus de puissance que nécessaire) ou ont des problèmes d'orientation. Voici ce que j'ai utilisé pour une image recadrée carrée à l'échelle à partir d'une image UIImage *.

CGFloat minimumSide = fminf(image.size.width, image.size.height);
CGFloat finalSquareSize = 600.;

//create new drawing context for right size
CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize);
CGFloat scalingRatio = 640.0/minimumSide;
UIGraphicsBeginImageContext(rect.size);

//draw
[image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)];

UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

0

J'utilise la méthode ci-dessous.

  -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
  {
    CGSize cropSize = rect.size;
    CGFloat widthScale =  
    image.size.width/self.imageViewOriginal.bounds.size.width;
    CGFloat heightScale = 
    image.size.height/self.imageViewOriginal.bounds.size.height;
    cropSize = CGSizeMake(rect.size.width*widthScale,  
    rect.size.height*heightScale);
    CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
    rect.origin.y*heightScale);
    rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
    cropSize.height);
    CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
    UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
    CGImageRelease(subImage);
    return croppedImage;

}


0

Regardez https://github.com/vvbogdan/BVCropPhoto

- (UIImage *) croppedImage {
    Échelle CGFloat = self.sourceImage.size.width / self.scrollView.contentSize.width;

    UIImage * finalImage = nil;
    CGRect targetFrame = CGRectMake ((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * échelle,
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * échelle,
            échelle self.cropSize.width *,
            échelle self.cropSize.height *);

    CGImageRef contextImage = CGImageCreateWithImageInRect ([[self imageWithRotation: self.sourceImage] CGImage], targetFrame);

    if (contextImage! = NULL) {
        finalImage = [UIImage imageWithCGImage: contextImage
                                         scale: self.sourceImage.scale
                                   orientation: UIImageOrientationUp];

        CGImageRelease (contextImage);
    }

    return finalImage;
}


- (UIImage *) imageWithRotation: (UIImage *) image {


    if (image.imageOrientation == UIImageOrientationUp) renvoie l'image;
    CGAffineTransform transform = CGAffineTransformIdentity;

    commutateur (image.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, image.size.height);
            transform = CGAffineTransformRotate (transform, M_PI);
            Pause;

        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformRotate (transform, M_PI_2);
            Pause;

        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, 0, image.size.height);
            transform = CGAffineTransformRotate (transform, -M_PI_2);
            Pause;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            Pause;
    }

    commutateur (image.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            Pause;

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.height, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            Pause;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            Pause;
    }

    // Maintenant, nous dessinons la CGImage sous-jacente dans un nouveau contexte, en appliquant la transformation
    // calculé ci-dessus.
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage),
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, transform);
    commutateur (image.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr ...
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.height, image.size.width), image.CGImage);
            Pause;

        défaut:
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.width, image.size.height), image.CGImage);
            Pause;
    }

    // Et maintenant, nous créons juste un nouveau UIImage à partir du contexte de dessin
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [UIImage imageWithCGImage: cgimg];
    CGContextRelease (ctx);
    CGImageRelease (cgimg);
    return img;

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.