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

Jablair
la source
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);
HitScan
la source
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;
}
Arne
la source
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
@Dschee Oui. Voir stackoverflow.com/questions/7800174/…
Klaas
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];
Vilém Kurz
la source
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

Maxim Shoustin
la source
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;
}
Sergii Rudchenko
la source
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;
}
colinta
la source
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;
}
un loup
la source
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
    }
}
Mark Leonard
la source
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.

Jordan
la source
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)
    }
}
neoneye
la source
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
    }
}
Octet épique
la source
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".

Roi-sorcier
la source
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
    }
}
luhuiya
la source
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)
MuniekMg
la source
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
    }
}
huync
la source
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;
}
d'or
la source
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;
}
Bhushan_pawar
la source
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;
}
user5176302
la source
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
   }
}
NoodleOfDeath
la source
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
}
voidref
la source
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;
}
Linh Nguyen
la source
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();
Matthieu Rouif
la source
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;

}

user5176302
la source
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;

}
Nowytech
la source