Ich muss ein Bild verkleinern, aber auf scharfe Weise. In Photoshop gibt es beispielsweise die Optionen zur Reduzierung der Bildgröße "Bicubic Smoother" (verschwommen) und "Bicubic Sharper".
Ist dieser Bild-Downscaling-Algorithmus Open Source oder irgendwo dokumentiert oder bietet das SDK Methoden an, um dies zu tun?
Antworten:
Die bloße Verwendung
imageWithCGImage
reicht nicht aus. Es wird skaliert, aber das Ergebnis ist verschwommen und suboptimal, unabhängig davon, ob es vergrößert oder verkleinert wird.Wenn Sie das Aliasing richtig machen und die "Zacken" loswerden möchten, benötigen Sie Folgendes: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right- Weg / .
Mein Arbeitstestcode sieht ungefähr so aus. Dies ist Trevors Lösung mit einer kleinen Anpassung, um mit meinen transparenten PNGs zu arbeiten:
- (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; }
quelle
transformForOrientation
. Ist diese Funktion wirklich notwendig? Ist es nur für JPG mit Orientierungsmetadaten?imageOrientation
(EXIF-Daten, nehme ich an?) und führt je nach Wert eine zusätzliche Transformation durch. Ich bin mir nicht sicher, ob es notwendig ist. Ich werde damit herumspielen.[[UIColor whiteColor] set]; UIRectFill(newRect);
Für diejenigen, die Swift verwenden, ist hier die akzeptierte Antwort in 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 }
quelle
CGContextConcatCTM(context, flipVertical)
?Wenn jemand nach einer Swift-Version sucht, ist hier die Swift-Version von @Dan Rosenstarks akzeptierter Antwort:
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 }
quelle
Wenn Sie beim Skalieren das ursprüngliche Seitenverhältnis des Bildes beibehalten, erhalten Sie immer ein scharfes Bild, unabhängig davon, wie stark Sie es verkleinern.
Sie können die folgende Methode zur Skalierung verwenden:
+ (UIImage *)imageWithCGImage:(CGImageRef)imageRef scale:(CGFloat)scale orientation:(UIImageOrientation)orientation
quelle
Für 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 }
quelle
newRect.size
, erhalte ich die richtigen, verkleinerten Abmessungen. Wenn ich jedoch druckenewImage.size
, sehe ich dort die Originalabmessungen vonimage
. Irgendwelche Ideen, warum das so ist? Herausgefunden.UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
verhindert jegliche Skalierung. Der Skalierungsparameter ist0
. Wenn SieUIGraphicsBeginImageContext(newRect.size)
stattdessen verwenden, ist alles in Ordnung.@YAR Ihre Lösung funktioniert ordnungsgemäß.
Es gibt nur eine Sache, die nicht meinen Anforderungen entspricht: Die Größe des gesamten Bildes wird geändert. Ich habe eine Methode geschrieben, die es mag
photos app on iphone
. Dies berechnet die "längere Seite" und schneidet die "Überlagerung" ab, was zu viel besseren Ergebnissen hinsichtlich der Bildqualität führt.- (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 ; }
quelle
Diese Erweiterung sollte das Bild skalieren und dabei das ursprüngliche Seitenverhältnis beibehalten. Der Rest des Bildes wird zugeschnitten. (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 } }
quelle
Für schnelles 4.2:
extension UIImage { func resized(By coefficient:CGFloat) -> UIImage? { guard coefficient >= 0 && coefficient <= 1 else { print("The coefficient must be a floating point number between 0 and 1") return nil } let newWidth = size.width * coefficient let newHeight = size.height * coefficient UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight)) draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } }
quelle