with uiimagepickercontrollerdelegate how and ios objective-c cocoa-touch uiimagepickercontroller image-manipulation

ios - how - uiimagepickercontrollerdelegate swift 4



Recortar un UIImage (23)

Tengo un código que cambia el tamaño de una imagen para poder obtener una porción escalada del centro de la imagen. Lo uso para tomar un UIImage y 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 de fotos. (Sé que podría usar un UIImageView y 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 la pantalla de mi tamaño objetivo. Como quiero la parte central de la imagen, establezco el argumento CGRect pasado para drawInRect a 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 sobre 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. Estaba suponiendo / esperando que dibujar en una vista fuera de la pantalla no fuera uno de estos. ¿Me equivoco?

(Oh, cómo echo de menos el NSImage''s drawInRect:fromRect:operation:fraction: method).


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


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

con la ayuda de esta función de puente CGRectMake -> CGRect (los créditos a esta respuesta contestados 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:


Actualización 2014-05-28: escribí esto cuando iOS 3 o algo así era lo nuevo, estoy seguro de que hay mejores formas de hacerlo por ahora, posiblemente incorporadas. Como muchas personas han mencionado, este método no tiene en cuenta la rotación; lee algunas respuestas adicionales y difunde un poco de amor para que las respuestas a esta pregunta sean útiles para todos.

Respuesta original:

Voy a 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 puede usar para obtener los resultados deseados: CGImageCreateWithImageInRect(CGImageRef, CGRect) lo 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);


Aquí está mi implementación de UIImage crop 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; }


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 }


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


En iOS9.2SDK, uso el siguiente método para convertir fotogramas 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; }


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


Heads up: todas estas respuestas asumen un objeto de imagen CGImage CGImage.

image.CGImage puede devolver nil, si el UIImage está respaldado por un CIImage , que sería el caso si creara esta imagen con un CIFilter .

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


La mejor solución para recortar un UIImage en Swift , en términos de precisión, escalado 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".


La versión awolf de la respuesta de awolf , 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 } }


Mire 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 orientation:UIImageOrientationUp]; CGImageRelease(contextImage); } return finalImage; } - (UIImage *)imageWithRotation:(UIImage *)image { if (image.imageOrientation == UIImageOrientationUp) return image; CGAffineTransform transform = CGAffineTransformIdentity; switch (image.imageOrientation) { case UIImageOrientationDown: case UIImageOrientationDownMirrored: transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height); transform = CGAffineTransformRotate(transform, M_PI); break; case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: transform = CGAffineTransformTranslate(transform, image.size.width, 0); transform = CGAffineTransformRotate(transform, M_PI_2); break; case UIImageOrientationRight: case UIImageOrientationRightMirrored: transform = CGAffineTransformTranslate(transform, 0, image.size.height); transform = CGAffineTransformRotate(transform, -M_PI_2); break; case UIImageOrientationUp: case UIImageOrientationUpMirrored: break; } switch (image.imageOrientation) { case UIImageOrientationUpMirrored: case UIImageOrientationDownMirrored: transform = CGAffineTransformTranslate(transform, image.size.width, 0); transform = CGAffineTransformScale(transform, -1, 1); break; case UIImageOrientationLeftMirrored: case UIImageOrientationRightMirrored: transform = CGAffineTransformTranslate(transform, image.size.height, 0); transform = CGAffineTransformScale(transform, -1, 1); break; case UIImageOrientationUp: case UIImageOrientationDown: case UIImageOrientationLeft: case UIImageOrientationRight: break; } // Now we draw the underlying CGImage into a new context, applying the transform // calculated above. 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) { case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: case UIImageOrientationRight: case UIImageOrientationRightMirrored: // Grr... CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.height, image.size.width), image.CGImage); break; default: CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); break; } // And now we just create a new UIImage from the drawing context CGImageRef cgimg = CGBitmapContextCreateImage(ctx); UIImage *img = [UIImage imageWithCGImage:cgimg]; CGContextRelease(ctx); CGImageRelease(cgimg); return img; }


Ninguna de las respuestas aquí maneja todos los problemas de escala y rotación al 100% correctamente. Aquí hay una síntesis de todo lo dicho hasta el momento, actualizado a partir de iOS7 / 8. Está destinado a ser incluido como un 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; }


No estuve satisfecho con otras soluciones porque o bien dibujan 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 partir 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();


Para recortar imágenes de retina manteniendo la misma escala y orientación, use el siguiente método en una categoría de 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; }


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)


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


Siga la Respuesta de @Arne. Me acabo de fijar a la función Categoría. Ponlo en 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; }


Yo uso el método de abajo.

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

}


Swift 2.0 Update (compatibilidad con CIImage )

Expansión de la respuesta de Maxim, pero funciona si su imagen también está basada en CIImage .

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


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


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


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 se centre. Lo que me llevó un tiempo descifrar es la línea que sigue al comentario de WTF.

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

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

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