Recortando un UIImage

209

Tengo un código que cambia el tamaño de una imagen para poder obtener un fragmento escalado del centro de la imagen. Utilizo esto para tomar UIImagey devolver una pequeña representación cuadrada de una imagen, similar a lo que se ve en la vista del álbum de la aplicación Fotos. (Sé que podría usar UIImageVieway ajustar el modo de recorte para lograr los mismos resultados, pero estas imágenes a veces se muestran en UIWebViews).

Empecé a notar algunos bloqueos en este código y estoy un poco perplejo. Tengo dos teorías diferentes y me pregunto si alguna de ellas está en la base.

Teoría 1) Logro el recorte dibujando en un contexto de imagen fuera de pantalla de mi tamaño objetivo. Como quiero la parte central de la imagen, configuro el CGRectargumento pasado drawInRecta algo que es más grande que los límites del contexto de mi imagen. Esperaba que esto fuera kosher, pero ¿estoy tratando de dibujar otro recuerdo que no debería tocar?

Teoría 2) Estoy haciendo todo esto en un hilo de fondo. Sé que hay partes de UIKit que están restringidas al hilo principal. Supuse / esperaba que dibujar en una vista fuera de pantalla no fuera uno de estos. ¿Me equivoco?

(Oh, cómo extraño el NSImage's drawInRect:fromRect:operation:fraction:método)

Jablair
fuente
Si está tratando de diagnosticar un bloqueo, debe ejecutar la aplicación debajo del depurador y tomar nota de lo que sucede cuando se bloquea. Ni siquiera ha identificado si se produce una excepción o si obtiene EXC_BAD_ACCESS debido a un puntero colgante. Una vez que lo sabes, puedes comenzar a hacer teorías.
benzado
Lo suficientemente justo. No he reprobado esto bajo el depurador, aunque tengo mensajes EXC_BAD_ACCESS en el registro de fallos. Cuando publiqué esto, estaba trabajando bajo el supuesto de que había cometido un error estúpido en mi implementación y alguien saltaría sobre él (como olvidar un trazado de recorte).
Jablair
A pesar de lo que vale y a pesar de las buenas respuestas a continuación, hay una buena encuesta de técnicas y rendimiento de las mismas en NSHipster: nshipster.com/image-resizing . El purista en mí quería usar CIImage, pero el pragmático eligió el contexto UIKit / image.
Chris Conover

Respuestas:

236

Actualización 28/05/2014: escribí esto cuando iOS 3 más o menos era lo más nuevo, estoy seguro de que hay mejores formas de hacerlo por ahora, posiblemente incorporado. Como muchas personas han mencionado, este método no tiene en cuenta la rotación; lea algunas respuestas adicionales y difunda un poco de amor a favor para mantener las respuestas a esta pregunta útiles para todos.

Respuesta original:

Copiaré / pegaré mi respuesta a la misma pregunta en otro lugar:

No hay un método de clase simple para hacer esto, pero hay una función que puedes usar para obtener los resultados deseados: CGImageCreateWithImageInRect(CGImageRef, CGRect)te ayudará.

Aquí hay un breve ejemplo usándolo:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);
HitScan
fuente
13
Cuando imageOrientationse establece en todo menos arriba, CGImagese gira con respecto al UIImagey el rectángulo de recorte estará equivocado. Puede rotar el rectángulo de recorte en consecuencia, pero el recién creado UIImagetendrá imageOrientationhacia arriba, de modo que el recortado CGImagese rotará dentro de él.
zoul
25
Quiero señalar que en la pantalla de retina necesita duplicar su recorte. Ancho y alto recto utilizando este método. Solo algo con lo que me encontré.
petrocket
21
para que funcione en cualquier orientación:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis
3
¡Deberías agregar soporte para Retina!
Mário Carvalho
1
@NicosKaralis que solo lo rotará en la orientación correcta. Sin embargo, el área que está recortando de la imagen seguirá siendo incorrecta.
Tim Bodeit
90

Para recortar imágenes de retina manteniendo la misma escala y orientación, use el siguiente método en una categoría UIImage (iOS 4.0 y superior):

- (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
fuente
14
Podrías matar el condicional aquí y siempre multiplicar por self.scale, ¿no?
Michael Mior
2
Tienes razón, Michael, pero creo que la solución anterior es un poco más rápida en dispositivos sin retina, ya que solo realiza una única comprobación en lugar de cuatro multiplicaciones más una tarea. En los dispositivos de retina, es solo una verificación booleana más de lo necesario, por lo que es una cuestión de preferencia personal o ajuste del objetivo.
CGee
2
¿Es correcto usarlo CGImageRelease(imageRef);con ARC habilitado?
CGee
3
@Dschee Sí. Ver stackoverflow.com/questions/7800174/…
Klaas
3
En realidad, 4 multiplicaciones pueden ser más rápidas que evaluar una condición :) Las multiplicaciones se pueden optimizar bastante bien en las CPU modernas, mientras que las condiciones solo se pueden predecir. Pero tienes razón, es una cuestión de preferencia. Prefiero el código más corto / simple debido a la mayor legibilidad.
marsbear
65

Puede crear una categoría UIImage y usarla donde lo necesite. Basado en la respuesta de HitScans y los comentarios a continuación.

@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

Puedes usarlo de esta manera:

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

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

UIImage *croppedImage = [imageToCrop crop:cropRect];
Vilém Kurz
fuente
3
¿No debería ser [[UIScreen mainScreen] scale]solo self.scale? La escala de la imagen puede no ser la misma que la escala de la pantalla.
Michael Mior
Hola, probé tu respuesta y me da No visible @interface for 'UIImage' declares the selector 'crop'a pesar de que puse los archivos de categoría .h y .m en mi proyecto e importé el .h en la clase en la que estoy usando la categoría. ¿Alguna idea?
Ali
Arreglado. Eché de menos poner el encabezado del método en el archivo UIImage + Crop.h.
Ali
Quiero recortar la imagen en forma circular. Para que podamos ver solo la ruta circular y otra ruta permanece transparente.
Alfa
Entonces puedes usar CGImageCreateWithMask o CGImageCreateWithMaskingColors en lugar de CGImageCreateWithImageInRect
Vilém Kurz
54

Versión 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

ingrese la descripción de la imagen aquí

con ayuda de esta función de puente CGRectMake-> CGRect(créditos a esta respuesta respondida por @rob mayoff):

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

El uso es:

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

Salida:

ingrese la descripción de la imagen aquí

Maxim Shoustin
fuente
No estoy seguro de por qué fue rechazado, pensé que era una buena respuesta. Lo único que se me ocurre es que no maneja el escalado, por lo que no funcionará si tiene imágenes @ 2x / @ 3x y los nombres de sus funciones no coinciden.
William T.
44
Necesita guardla primera línea en caso de UIImage.CGImagedevoluciones nily por qué usar varcuando letfunciona perfectamente.
NoodleOfDeath
Esto es correcto, SIN EMBARGO, lea los documentos con cropping(to:)mucho cuidado. El resultado CGImagetiene una fuerte referencia al más grande CGImagedel que se recortó. Por lo tanto, si solo desea conservar la imagen recortada y no necesita el original, eche un vistazo a las otras soluciones.
jsadler
Esto no funciona con imágenes que tienen etiquetas de orientación. La región de cultivo no se rota correctamente.
Max
1
@Max esto se debe a que cgImage no tiene soporte de orientación. Si UIImage está teniendo rightentonces el CGImage debe ser CCW girado 90 grados, de ahí el rect cultivo debe ser girado demasiado
MK Yung
49

Aquí está mi implementación de recorte UIImage que obedece a la propiedad imageOrientation. Todas las orientaciones fueron probadas a fondo.

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
fuente
77
La advertencia implicit declaration of function 'rad' is invalid in c99puede eliminarse reemplazando: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster
Ups, lo siento. Se agregó la función de utilidad rad () al fragmento de código fuente.
Sergii Rudchenko
contains undefined reference for architecture armv7¿Me estoy perdiendo una biblioteca? CoreGraphics es importado.
Bob Spryn
1
@SergiiRudchenko "Todas las orientaciones se probaron a fondo". - ¿Incluye esto las orientaciones reflejadas?
Tim Bodeit
@BobSpryn No, no te falta una biblioteca. Si bien no puedo explicar qué significa el error, el reemplazo de rad (), como sugiere SoundBlaster, también corrige este error.
Tim Bodeit
39

CGImageAviso : todas estas respuestas suponen un objeto de imagen respaldado.

image.CGImagepuede devolver nil, si UIImageestá respaldado por a CIImage, que sería el caso si creara esta imagen usando unCIFilter .

En ese caso, es posible que tenga que dibujar la imagen en un nuevo contexto y devolver esa imagen ( lento ).

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
fuente
55
¡Esto es exactamente lo que necesitaba, funciona en todos los escenarios, por lo tanto, mejor que cualquiera de las otras soluciones!
David Thompson
2
image.size en la primera línea debe ser rect.size UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Brett
2
Gracias Brett, definitivamente ahí mismo. Actualicé el código para incluir esta solución.
colinta
¿Por qué es -rect.origin.x no solo rect.origin.x?
Agresor
2
Porque estamos recortando; en otras palabras, el argumento 'rect' está diciendo "Quiero que la parte superior izquierda de la nueva imagen comience, digamos, [10, 10]". Para hacerlo, dibujamos la imagen de modo que [10, 10] esté en el origen de la nueva imagen.
colinta
35

Ninguna de las respuestas aquí maneja todos los problemas de escala y rotación 100% correctamente. Aquí hay una síntesis de todo lo dicho hasta ahora, actualizado a partir de iOS7 / 8. Está destinado a ser incluido como método en una categoría en 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 lobo
fuente
1
¿Por qué esta respuesta no se vota más? Intenté todas las respuestas anteriores y me encontré con un montón de problemas (especialmente la conversión de UIImage a CIImage y la pérdida de datos de transformación adecuados). ¡ESTE ES EL ÚNICO QUE ME FUNCIONA!
Agresor
1
Gracias @Aggressor, me alegro de que esto funcione para ti. Mi respuesta solo se publicó hace un mes, mientras que muchas de estas respuestas han estado aquí durante 5 años. Supongo que eso explica la falta de votos comparables.
awolf
Estoy usando esto para deslizar los filtros a través de las imágenes (como lo hace Snapchat) y estoy teniendo pequeños problemas de escala. ¿Hay algo en su código que pueda sugerir que mire y que pueda contener la causa de este error de escala? Publiqué mi solución que usa su código aquí: stackoverflow.com/questions/23319497/…
Aggressor
Una función muy útil, gracias. ¿Algún consejo sobre qué debería modificarse para recortar centrándose en la parte central de la imagen?
privado
ESTO DEBE SER LA RESPUESTA.
herradura7
17

Versión rápida de awolfla respuesta de, que funcionó para mí:

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
fuente
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 mayoría de las respuestas que he visto solo tratan con una posición de (0, 0) para (x, y). Ok, ese es un caso, pero me gustaría que mi operación de recorte esté centrada. Lo que me llevó un tiempo entender es la línea que sigue al comentario de WTF.

Tomemos el caso de una imagen capturada con orientación vertical:

  1. La altura de la imagen original es mayor que su ancho (¡Woo, no es de extrañar hasta ahora!)
  2. Sin embargo, la imagen que el método CGImageCreateWithImageInRect imagina en su propio mundo no es realmente un retrato, sino un paisaje (es por eso que si no usa el argumento de orientación en el constructor imageWithCGImage, se mostrará como 180 rotados).
  3. Entonces, debes imaginar que es un paisaje, la posición (0, 0) es la esquina superior derecha de la imagen.

Espero que tenga sentido! Si no es así, intente con diferentes valores, verá que la lógica se invierte cuando se trata de elegir la x, y, la anchura y la altura correctas para su cropRect.

Jordán
fuente
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
fuente
7

Extensión rápida

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
    }
}
Byte épico
fuente
4

La mejor solución para recortar un UIImage en Swift , en términos de precisión, escala de píxeles ...:

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
}

Instrucciones utilizadas para llamar a esta función:

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

Nota: la inspiración inicial del código fuente escrita en Objective-C se ha encontrado en el blog "Cocoanetics".

Rey Mago
fuente
3

Debajo del fragmento de código podría ayudar.

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
fuente
2

Parece un poco extraño pero funciona muy bien y tiene en cuenta la orientación de la imagen:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)
MuniekMg
fuente
1
pero obviamente rompe la escala
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
fuente
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;
}
dorado
fuente
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
fuente
1

En iOS9.2SDK, uso el siguiente método para convertir el marco de UIView a 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;
}
usuario5176302
fuente
1

Actualización Swift 2.0 ( CIImagecompatibilidad)

Se expande a partir de la Respuesta de Maxim, pero también funciona si su imagen está CIImagebasada.

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
fuente
1

Aquí hay una versión actualizada de Swift 3 basada en la respuesta 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
fuente
1

Siga la respuesta de @Arne. Solo estoy arreglando la función Categoría. ponlo en la Categoría 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
fuente
0

No estaba satisfecho con otras soluciones porque toman varias veces (usando más potencia de la necesaria) o tienen problemas con la orientación. Esto es lo que usé para una imagen recortada cuadrada a escala de una imagen 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
fuente
0

Yo uso el siguiente método.

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

}

usuario5176302
fuente
0

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

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

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

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

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

        CGImageRelease (contextImage);
    }

    return finalImage;
}


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


    if (image.imageOrientation == UIImageOrientationUp) devuelve la imagen;
    CGAffineTransform transform = CGAffineTransformIdentity;

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

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

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

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

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

    // Ahora dibujamos la imagen CG subyacente en un nuevo contexto, aplicando la transformación
    // calculado arriba.
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage),
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, transform);
    switch (image.imageOrientation) {
        caso UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr ...
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.height, image.size.width), image.CGImage);
            descanso;

        defecto:
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.width, image.size.height), image.CGImage);
            descanso;
    }

    // Y ahora solo creamos un nuevo UIImage del contexto de dibujo
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [UIImage imageWithCGImage: cgimg];
    CGContextRelease (ctx);
    CGImageRelease (cgimg);
    volver img;

}
Nowytech
fuente