¿Cómo escalar un UIImage y hacerlo crujiente / nítido al mismo tiempo en lugar de borroso?

Necesito escalar una imagen, pero de una manera nítida. En Photoshop, por ejemplo, están las opciones de reducción de tamaño de imagen “Bicubic Smoother” (borrosa) y “Bicubic Sharper”.

¿Este algoritmo de reducción de imagen está abierto o documentado en alguna parte o el SDK ofrece métodos para hacerlo?

Simplemente usar imageWithCGImage no es suficiente. Se escalará, pero el resultado será borroso y subóptimo si se escala o se escala.

Si quiere obtener el alias correcto y deshacerse de los “jaggies”, necesita algo como esto: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right- forma / .

Mi código de prueba de trabajo se parece a esto, que es la solución de Trevor con un pequeño ajuste para trabajar con mis PNG transparentes:

 - (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize { CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); CGImageRef imageRef = image.CGImage; UIGraphicsBeginImageContextWithOptions(newSize, NO, 0); CGContextRef context = UIGraphicsGetCurrentContext(); // Set the quality level to use when rescaling CGContextSetInterpolationQuality(context, kCGInterpolationHigh); CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height); CGContextConcatCTM(context, flipVertical); // Draw into the context; this scales the image CGContextDrawImage(context, newRect, imageRef); // Get the resized image from the context and a UIImage CGImageRef newImageRef = CGBitmapContextCreateImage(context); UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; CGImageRelease(newImageRef); UIGraphicsEndImageContext(); return newImage; } 

Para aquellos que usan Swift aquí está la respuesta aceptada en Swift:

 func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) { let newRect = CGRectIntegral(CGRectMake(0,0, newSize.width, newSize.height)) let imageRef = image.CGImage UIGraphicsBeginImageContextWithOptions(newSize, false, 0) let context = UIGraphicsGetCurrentContext() // Set the quality level to use when rescaling CGContextSetInterpolationQuality(context, kCGInterpolationHigh) let flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height) CGContextConcatCTM(context, flipVertical) // Draw into the context; this scales the image CGContextDrawImage(context, newRect, imageRef) let newImageRef = CGBitmapContextCreateImage(context) as CGImage let newImage = UIImage(CGImage: newImageRef) // Get the resized image from the context and a UIImage UIGraphicsEndImageContext() return newImage } 

Si alguien está buscando la versión Swift, aquí está la versión Swift de la respuesta aceptada de @Dan Rosenstark:

 func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage { let scale = newHeight / image.size.height let newWidth = image.size.width * scale UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight)) image.drawInRect(CGRectMake(0, 0, newWidth, newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } 

Si conserva la relación de aspecto original de la imagen durante la escala, siempre terminará con una imagen nítida, sin importar cuánto baje.

Puede usar el siguiente método para escalar:

 + (UIImage *)imageWithCGImage:(CGImageRef)imageRef scale:(CGFloat)scale orientation:(UIImageOrientation)orientation 

Para Swift 3

 func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) { let newRect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height).integral UIGraphicsBeginImageContextWithOptions(newSize, false, 0) let context = UIGraphicsGetCurrentContext() // Set the quality level to use when rescaling context!.interpolationQuality = CGInterpolationQuality.default let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newSize.height) context!.concatenate(flipVertical) // Draw into the context; this scales the image context?.draw(image.cgImage!, in: CGRect(x: 0.0,y: 0.0, width: newRect.width, height: newRect.height)) let newImageRef = context!.makeImage()! as CGImage let newImage = UIImage(cgImage: newImageRef) // Get the resized image from the context and a UIImage UIGraphicsEndImageContext() return newImage } 

@YAR su solución está funcionando correctamente.

Solo hay una cosa que no se ajusta a mis requisitos: se cambia el tamaño de la imagen completa. Escribí un Método que lo hizo como la photos app on iphone . Esto calcula el “lado más largo” y corta la “superposición”, lo que permite obtener resultados mucho mejores con respecto a la calidad de la imagen.

 - (UIImage *)resizeImageProportionallyIntoNewSize:(CGSize)newSize; { CGFloat scaleWidth = 1.0f; CGFloat scaleHeight = 1.0f; if (CGSizeEqualToSize(self.size, newSize) == NO) { //calculate "the longer side" if(self.size.width > self.size.height) { scaleWidth = self.size.width / self.size.height; } else { scaleHeight = self.size.height / self.size.width; } } //prepare source and target image UIImage *sourceImage = self; UIImage *newImage = nil; // Now we create a context in newSize and draw the image out of the bounds of the context to get // A proportionally scaled image by cutting of the image overlay UIGraphicsBeginImageContext(newSize); //Center image point so that on each egde is a little cutoff CGRect thumbnailRect = CGRectZero; thumbnailRect.size.width = newSize.width * scaleWidth; thumbnailRect.size.height = newSize.height * scaleHeight; thumbnailRect.origin.x = (int) (newSize.width - thumbnailRect.size.width) * 0.5; thumbnailRect.origin.y = (int) (newSize.height - thumbnailRect.size.height) * 0.5; [sourceImage drawInRect:thumbnailRect]; newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); if(newImage == nil) NSLog(@"could not scale image"); return newImage ; } 

Esta extensión debe escalar la imagen manteniendo la relación de aspecto original. El rest de la imagen está recortada. (Swift 3)

 extension UIImage { func thumbnail(ofSize proposedSize: CGSize) -> UIImage? { let scale = min(size.width/proposedSize.width, size.height/proposedSize.height) let newSize = CGSize(width: size.width/scale, height: size.height/scale) let newOrigin = CGPoint(x: (proposedSize.width - newSize.width)/2, y: (proposedSize.height - newSize.height)/2) let thumbRect = CGRect(origin: newOrigin, size: newSize).integral UIGraphicsBeginImageContextWithOptions(proposedSize, false, 0) draw(in: thumbRect) let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result } }