UIImage: Cambiar el tamaño, luego Crop

He estado atacando mi cara en este momento por literalmente días y aunque siento constantemente que estoy al borde de la revelación, simplemente no puedo lograr mi objective.

Pensé, antes de tiempo en las fases conceptuales de mi diseño, que sería una cuestión trivial obtener una imagen de la cámara o biblioteca del iPhone, reducirla a una altura específica, usando una función equivalente a la opción Relleno de aspecto de UIImageView (completamente en código), y luego cortar todo lo que no encaja dentro de un CGRect pasado.

Obtener la imagen original de la cámara o la biblioteca, fue trivial. Estoy sorprendido de lo difícil que han resultado los otros dos pasos.

La imagen adjunta muestra lo que estoy tratando de lograr. ¿Podría alguien por favor ser tan amable de sostener mi mano? Cada ejemplo de código que he encontrado hasta ahora parece destruir la imagen, estar al revés, parecer basura, salir de los límites o simplemente no funcionar correctamente.

Necesitaba lo mismo, en mi caso, elegir la dimensión que se ajusta una vez escalada, y luego recortar cada extremo para ajustar el rest al ancho. (Estoy trabajando en paisaje, por lo que podría no haber notado ninguna deficiencia en el modo retrato.) Aquí está mi código: es parte de una categoría en UIImage. El tamaño de destino en mi código siempre se establece en el tamaño de pantalla completa del dispositivo.

@implementation UIImage (Extras) #pragma mark - #pragma mark Scale and crop image - (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize { UIImage *sourceImage = self; UIImage *newImage = nil; CGSize imageSize = sourceImage.size; CGFloat width = imageSize.width; CGFloat height = imageSize.height; CGFloat targetWidth = targetSize.width; CGFloat targetHeight = targetSize.height; CGFloat scaleFactor = 0.0; CGFloat scaledWidth = targetWidth; CGFloat scaledHeight = targetHeight; CGPoint thumbnailPoint = CGPointMake(0.0,0.0); if (CGSizeEqualToSize(imageSize, targetSize) == NO) { CGFloat widthFactor = targetWidth / width; CGFloat heightFactor = targetHeight / height; if (widthFactor > heightFactor) { scaleFactor = widthFactor; // scale to fit height } else { scaleFactor = heightFactor; // scale to fit width } scaledWidth = width * scaleFactor; scaledHeight = height * scaleFactor; // center the image if (widthFactor > heightFactor) { thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; } else { if (widthFactor < heightFactor) { thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5; } } } UIGraphicsBeginImageContext(targetSize); // this will crop CGRect thumbnailRect = CGRectZero; thumbnailRect.origin = thumbnailPoint; thumbnailRect.size.width = scaledWidth; thumbnailRect.size.height = scaledHeight; [sourceImage drawInRect:thumbnailRect]; newImage = UIGraphicsGetImageFromCurrentImageContext(); if(newImage == nil) { NSLog(@"could not scale image"); } //pop the context to get back to the default UIGraphicsEndImageContext(); return newImage; } 

Una publicación anterior contiene código para un método para cambiar el tamaño de su UIImage. La parte relevante es la siguiente:

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

En lo que respecta al recorte, creo que si modifica el método para usar un tamaño diferente para la escala que para el contexto, la imagen resultante se debe recortar a los límites del contexto.

 + (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize { //If scaleFactor is not touched, no scaling will occur CGFloat scaleFactor = 1.0; //Deciding which factor to use to scale the image (factor = targetSize / imageSize) if (image.size.width > targetSize.width || image.size.height > targetSize.height) if (!((scaleFactor = (targetSize.width / image.size.width)) > (targetSize.height / image.size.height))) //scale to fit width, or scaleFactor = targetSize.height / image.size.height; // scale to fit heigth. UIGraphicsBeginImageContext(targetSize); //Creating the rect where the scaled image is drawn in CGRect rect = CGRectMake((targetSize.width - image.size.width * scaleFactor) / 2, (targetSize.height - image.size.height * scaleFactor) / 2, image.size.width * scaleFactor, image.size.height * scaleFactor); //Draw the image into the rect [image drawInRect:rect]; //Saving the image, ending image context UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return scaledImage; } 

Propongo este. ¿No es ella una belleza? 😉

Hay una gran pieza de código relacionada con el cambio de tamaño de imágenes + varias otras operaciones. Me encontré con esto al tratar de descubrir cómo cambiar el tamaño de las imágenes … http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

Aqui tienes. Este es perfecto 😉

EDITAR: vea el comentario a continuación – “No funciona con ciertas imágenes, falla con: CGContextSetInterpolationQuality: error de contexto inválido 0x0”

 // Resizes the image according to the given content mode, taking into account the image's orientation - (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode imageToScale:(UIImage*)imageToScale bounds:(CGSize)bounds interpolationQuality:(CGInterpolationQuality)quality { //Get the size we want to scale it to CGFloat horizontalRatio = bounds.width / imageToScale.size.width; CGFloat verticalRatio = bounds.height / imageToScale.size.height; CGFloat ratio; switch (contentMode) { case UIViewContentModeScaleAspectFill: ratio = MAX(horizontalRatio, verticalRatio); break; case UIViewContentModeScaleAspectFit: ratio = MIN(horizontalRatio, verticalRatio); break; default: [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode]; } //...and here it is CGSize newSize = CGSizeMake(imageToScale.size.width * ratio, imageToScale.size.height * ratio); //start scaling it CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); CGImageRef imageRef = imageToScale.CGImage; CGContextRef bitmap = CGBitmapContextCreate(NULL, newRect.size.width, newRect.size.height, CGImageGetBitsPerComponent(imageRef), 0, CGImageGetColorSpace(imageRef), CGImageGetBitmapInfo(imageRef)); CGContextSetInterpolationQuality(bitmap, quality); // Draw into the context; this scales the image CGContextDrawImage(bitmap, newRect, imageRef); // Get the resized image from the context and a UIImage CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; // Clean up CGContextRelease(bitmap); CGImageRelease(newImageRef); return newImage; } 

Esta es una versión de la respuesta de Jane Sales en Swift. ¡Aclamaciones!

 public func resizeImage(image: UIImage, size: CGSize) -> UIImage? { var returnImage: UIImage? var scaleFactor: CGFloat = 1.0 var scaledWidth = size.width var scaledHeight = size.height var thumbnailPoint = CGPointMake(0, 0) if !CGSizeEqualToSize(image.size, size) { let widthFactor = size.width / image.size.width let heightFactor = size.height / image.size.height if widthFactor > heightFactor { scaleFactor = widthFactor } else { scaleFactor = heightFactor } scaledWidth = image.size.width * scaleFactor scaledHeight = image.size.height * scaleFactor if widthFactor > heightFactor { thumbnailPoint.y = (size.height - scaledHeight) * 0.5 } else if widthFactor < heightFactor { thumbnailPoint.x = (size.width - scaledWidth) * 0.5 } } UIGraphicsBeginImageContextWithOptions(size, true, 0) var thumbnailRect = CGRectZero thumbnailRect.origin = thumbnailPoint thumbnailRect.size.width = scaledWidth thumbnailRect.size.height = scaledHeight image.drawInRect(thumbnailRect) returnImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return returnImage } 

Modifiqué el Código de Brad Larson. Será aspecto llenar la imagen en rect dado.

 -(UIImage*) scaleAndCropToSize:(CGSize)newSize; { float ratio = self.size.width / self.size.height; UIGraphicsBeginImageContext(newSize); if (ratio > 1) { CGFloat newWidth = ratio * newSize.width; CGFloat newHeight = newSize.height; CGFloat leftMargin = (newWidth - newHeight) / 2; [self drawInRect:CGRectMake(-leftMargin, 0, newWidth, newHeight)]; } else { CGFloat newWidth = newSize.width; CGFloat newHeight = newSize.height / ratio; CGFloat topMargin = (newHeight - newWidth) / 2; [self drawInRect:CGRectMake(0, -topMargin, newSize.width, newSize.height/ratio)]; } UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } 

La versión de Xamarin.iOS para la respuesta aceptada sobre cómo cambiar el tamaño y luego recortar UIImage (Aspect Fill) está debajo

  public static UIImage ScaleAndCropImage(UIImage sourceImage, SizeF targetSize) { var imageSize = sourceImage.Size; UIImage newImage = null; var width = imageSize.Width; var height = imageSize.Height; var targetWidth = targetSize.Width; var targetHeight = targetSize.Height; var scaleFactor = 0.0f; var scaledWidth = targetWidth; var scaledHeight = targetHeight; var thumbnailPoint = PointF.Empty; if (imageSize != targetSize) { var widthFactor = targetWidth / width; var heightFactor = targetHeight / height; if (widthFactor > heightFactor) { scaleFactor = widthFactor;// scale to fit height } else { scaleFactor = heightFactor;// scale to fit width } scaledWidth = width * scaleFactor; scaledHeight = height * scaleFactor; // center the image if (widthFactor > heightFactor) { thumbnailPoint.Y = (targetHeight - scaledHeight) * 0.5f; } else { if (widthFactor < heightFactor) { thumbnailPoint.X = (targetWidth - scaledWidth) * 0.5f; } } } UIGraphics.BeginImageContextWithOptions(targetSize, false, 0.0f); var thumbnailRect = new RectangleF(thumbnailPoint, new SizeF(scaledWidth, scaledHeight)); sourceImage.Draw(thumbnailRect); newImage = UIGraphics.GetImageFromCurrentImageContext(); if (newImage == null) { Console.WriteLine("could not scale image"); } //pop the context to get back to the default UIGraphics.EndImageContext(); return newImage; } 

Convertí la guía de Sam Wirch en rápida y funcionó bien para mí, aunque hay un ligero “aplastamiento” en la imagen final que no pude resolver.

 func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage { var ratio: CGFloat = 0 var delta: CGFloat = 0 var offset = CGPointZero 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) } let clipRect = CGRectMake(-offset.x, -offset.y, (ratio * image.size.width) + delta, (ratio * image.size.height) + delta) UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0) UIRectClip(clipRect) image.drawInRect(clipRect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } 

Si alguien quiere la versión objetiva c, está en su sitio web.

El siguiente código simple funcionó para mí.

 [imageView setContentMode:UIViewContentModeScaleAspectFill]; [imageView setClipsToBounds:YES]; 
 scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,ScreenWidth,ScreenHeigth)]; [scrollView setBackgroundColor:[UIColor blackColor]]; [scrollView setDelegate:self]; [scrollView setShowsHorizontalScrollIndicator:NO]; [scrollView setShowsVerticalScrollIndicator:NO]; [scrollView setMaximumZoomScale:2.0]; image=[image scaleToSize:CGSizeMake(ScreenWidth, ScreenHeigth)]; imageView = [[UIImageView alloc] initWithImage:image]; UIImageView* imageViewBk = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]]; [self.view addSubview:imageViewBk]; CGRect rect; rect.origin.x=0; rect.origin.y=0; rect.size.width = image.size.width; rect.size.height = image.size.height; [imageView setFrame:rect]; [scrollView setContentSize:[imageView frame].size]; [scrollView setMinimumZoomScale:[scrollView frame].size.width / [imageView frame].size.width]; [scrollView setZoomScale:[scrollView minimumZoomScale]]; [scrollView addSubview:imageView]; [[self view] addSubview:scrollView]; 

entonces puedes tomar capturas de pantalla a tu imagen por este

 float zoomScale = 1.0 / [scrollView zoomScale]; CGRect rect; rect.origin.x = [scrollView contentOffset].x * zoomScale; rect.origin.y = [scrollView contentOffset].y * zoomScale; rect.size.width = [scrollView bounds].size.width * zoomScale; rect.size.height = [scrollView bounds].size.height * zoomScale; CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect); UIImage *cropped = [UIImage imageWithCGImage:cr]; CGImageRelease(cr); 

Descubrí que el Swift 3 publicado por Evgenii Kanvets no escala de manera uniforme la imagen.

Aquí está mi versión Swift 4 de la función que no aplasta la imagen:

 static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? { // This function returns a newImage, based on image // - image is scaled uniformaly to fit into a rect of size newSize // - if the newSize rect is of a different aspect ratio from the source image // the new image is cropped to be in the center of the source image // (the excess source image is removed) var ratio: CGFloat = 0 var delta: CGFloat = 0 var drawRect = CGRect() if newSize.width > newSize.height { ratio = newSize.width / image.size.width delta = (ratio * image.size.height) - newSize.height drawRect = CGRect(x: 0, y: -delta / 2, width: newSize.width, height: newSize.height + delta) } else { ratio = newSize.height / image.size.height delta = (ratio * image.size.width) - newSize.width drawRect = CGRect(x: -delta / 2, y: 0, width: newSize.width + delta, height: newSize.height) } UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0) image.draw(in: drawRect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } 
 - (UIImage*)imageScale:(CGFloat)scaleFactor cropForSize:(CGSize)targetSize { targetSize = !targetSize.width?self.size:targetSize; UIGraphicsBeginImageContext(targetSize); // this will crop CGRect thumbnailRect = CGRectZero; thumbnailRect.size.width = targetSize.width*scaleFactor; thumbnailRect.size.height = targetSize.height*scaleFactor; CGFloat xOffset = (targetSize.width- thumbnailRect.size.width)/2; CGFloat yOffset = (targetSize.height- thumbnailRect.size.height)/2; thumbnailRect.origin = CGPointMake(xOffset,yOffset); [self drawInRect:thumbnailRect]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); if(newImage == nil) { NSLog(@"could not scale image"); } UIGraphicsEndImageContext(); return newImage; } 

Debajo del ejemplo de trabajo: imagen izquierda – (imagen de origen); Imagen derecha con escala x2

enter image description here

Si desea escalar la imagen pero conserva su marco (proporciones), llame al método de esta manera:

 [yourImage imageScale:2.0f cropForSize:CGSizeZero]; 

Esta pregunta parece haber sido detenida, pero en mi búsqueda de una solución que pudiera entender más fácilmente (y escrita en Swift), llegué a esto (también publicado en: ¿Cómo recortar el UIImage? )


Quería poder recortar de una región en función de una relación de aspecto y escalar a un tamaño basado en una extensión límite externa. Aquí está mi variación:

 import AVFoundation import ImageIO class Image { class func crop(image:UIImage, crop source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage { let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source) let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(origin: CGPointZero, size: outputExtent)) let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale) let scale = max( targetRect.size.width / sourceRect.size.width, targetRect.size.height / sourceRect.size.height) let drawRect = CGRect(origin: -sourceRect.origin * scale, size: image.size * scale) image.drawInRect(drawRect) let scaledImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return scaledImage } } 

Hay un par de cosas que encuentro confusas, las preocupaciones por separado de recortar y redimensionar. El recorte se maneja con el origen del rect que pasa a drawInRect, y la escala se maneja mediante la porción de tamaño. En mi caso, necesitaba relacionar el tamaño del recorte de la fuente con mi salida de la misma relación de aspecto. El factor de escala es entonces salida / entrada, y esto debe aplicarse al drawRect (pasado a drawInRect).

Una advertencia es que este enfoque supone efectivamente que la imagen que está dibujando es más grande que el contexto de la imagen. No he probado esto, pero creo que puede usar este código para manejar el recorte / acercamiento, pero definiendo explícitamente el parámetro de escala como el parámetro de escala antes mencionado. Por defecto, UIKit aplica un multiplicador basado en la resolución de la pantalla.

Finalmente, se debe tener en cuenta que este enfoque de UIKit tiene un nivel más alto que los enfoques CoreGraphics / Quartz y Core Image, y parece que maneja los problemas de orientación de la imagen. También vale la pena mencionar que es bastante rápido, después de ImageIO, de acuerdo con este post aquí: http://nshipster.com/image-resizing/

Versión Swift:

  static func imageWithImage(image:UIImage, newSize:CGSize) ->UIImage { UIGraphicsBeginImageContextWithOptions(newSize, true, UIScreen.mainScreen().scale); image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height)) let newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage } 

Aquí hay una versión de Swift 3 de la guía de Sam Wirch publicada rápidamente por William T.

 extension UIImage { static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? { var ratio: CGFloat = 0 var delta: CGFloat = 0 var offset = CGPoint.zero if image.size.width > image.size.height { ratio = newSize.width / image.size.width delta = (ratio * image.size.width) - (ratio * image.size.height) offset = CGPoint(x: delta / 2, y: 0) } else { ratio = newSize.width / image.size.height delta = (ratio * image.size.height) - (ratio * image.size.width) offset = CGPoint(x: 0, y: delta / 2) } let clipRect = CGRect(x: -offset.x, y: -offset.y, width: (ratio * image.size.width) + delta, height: (ratio * image.size.height) + delta) UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0) UIRectClip(clipRect) image.draw(in: clipRect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } }