如何缩小UIImage并使其同时变脆/清晰而不是模糊?


75

我需要按比例缩小图像,但要采用清晰的方式。例如,在Photoshop中,图像尺寸缩小选项为“ Bicubic Smoother”(模糊)和“ Bicubic Sharper”。

这个图像缩小算法是在某个地方开源或记录的,还是SDK提供了执行此操作的方法?




看到这个问题。stackoverflow.com/questions/2658738/…投票最多的答案是我发现的最简单的解决方案。
alper_k 2013年

Answers:


127

仅仅使用imageWithCGImage是不够的。它将缩放,但是无论缩放还是缩小,结果都将是模糊且欠佳的。

如果您想正确地使用别名并摆脱“锯齿”,则需要这样的东西:http : //vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-方式/

我的工作测试代码如下所示,这是Trevor的解决方案,并做了一些小的调整才能与我的透明PNG一起使用:

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

谢谢!在寻找这个。这适用于PNG,但绕过Trevor的transformForOrientation。该功能真的必要吗?仅适用于带有方向元数据的JPG吗?
pixelfreak 2011年

@pixelfreak,您是说我提供的代码缺少使用元数据中已设置方向的jpeg所需的代码吗?如果代码不太复杂,请随意编辑。我只是剔除了不需要的东西
Dan Rosenstark 2011年

1
在Trevor的代码中,他正在检查imageOrientation(我假设是EXIF数据?)并根据其值进行额外的转换。不知道是否有必要。我会玩的。
pixelfreak 2011年

谢谢@pixelfreak,那太好了。如您所见,我只是试图使一个用例的代码简单。然后,我做一个UIImageView,它缓存不同的缩放版本(在静态数组中)以减轻此调整大小例程的速度,并中断setFrame方法。我不确定这是否引起人们的普遍关注:)
Dan Rosenstark 2011年

1
@Yar是的。如果您已经知道例如表单元格中将具有哪种背景,则可以先绘制背景:[[UIColor whiteColor] set]; UIRectFill(newRect);
Holtwick

19

对于那些使用Swift的用户,以下是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
}

凉!那么您也可以将其作为扩展。
Dan Rosenstark 2015年

2
为什么需要翻转图像以调整大小(即,为什么需要CGContextConcatCTM(context, flipVertical)
Crashalot16年

5
Swift 2中的kCGInterpolationHigh-> CGInterpolation.High或您遇到编译错误
Crashalot 2016年

1
@Crashalot为快速2.0使用CGInterpolationQuality.High而不是kCGInterpolationHigh
Deepak Thakur

使用此方法时,每次调用CGContextDrawImage都会丢失40MB。有人遇到过这样的问题吗?这对我来说是无法使用的,因为我正尝试缩小大量图像并立即用尽内存。
RowanPD

19

如果有人正在寻找Swift版本,这是@Dan Rosenstark接受的答案的Swift版本:

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
}

1
感谢您的回答,我们也可以将其用作扩展公共扩展UIImage {func ...}
mert 2016年

雷大姐在下面的链接的答案完全适用于我stackoverflow.com/questions/6052188/...
迪帕克·塔库尔

12

如果在缩放时保留图像的原始宽高比,则无论缩小多少,都将始终获得清晰的图像。

您可以使用以下方法进行缩放:

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

似乎在视图上应用了转换,但没有调整基础数据的大小
Cbas 16'7

12

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

这是将图像视为宽高比填充,有没有办法使它适合宽高比?谢谢
Coder221

这是行不通的。当我打印时newRect.size,它将为我提供正确的缩小尺寸。但是,如果我进行打印newImage.size,则可以看到的原始尺寸image。有什么想法吗?弄清楚了。UIGraphicsBeginImageContextWithOptions(newSize, false, 0)防止任何缩放。scale参数为0。如果UIGraphicsBeginImageContext(newRect.size)改用,一切都很好。
Schnodderbalken

2

@YAR您的解决方案工作正常。

只有一件事不符合我的要求:调整了整个图像的大小。我写了一个方法,就像photos app on iphone。这样可以计算出“较长的一面”,并切断“覆盖层”,从而获得与图像质量有关的更好的结果。

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

0

对于快速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
    }
}

@ShahbazSaleem您能澄清一下质量问题吗?我看到您对Jovan Stankovic的回答持相同的“理由”。
gabriel_vincent

-1

该扩展名应该缩放图像,同时保持原始的宽高比。其余图像被裁剪。(快速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
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.