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

Posted

技术标签:

【中文标题】如何缩小 UIImage 并使其同时变得清晰/锐利而不是模糊?【英文标题】:How to scale down a UIImage and make it crispy / sharp at the same time instead of blurry? 【发布时间】:2011-09-02 17:37:08 【问题描述】:

我需要缩小图像,但要以锐利的方式。例如,在 Photoshop 中有图像尺寸缩小选项“Bicubic Smoother”(模糊)和“Bicubic Sharper”。

此图像缩小算法是否在某处开源或记录在案,或者 SDK 是否提供了执行此操作的方法?

【问题讨论】:

How to scale a UIImage with high quality.的可能重复 另见Any code/library to scale down an UIImage?。 看到这个问题。 ***.com/questions/2658738/… 投票最多的答案是我找到的这个问题的最简单的解决方案。 【参考方案1】:

对于 Swift 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 的回答。【参考方案2】:

如果有人在寻找 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

【讨论】:

感谢您的回答,我们也可以将其用作扩展 public extension UIImage func ... 雷姐妹在以下链接中的回答对我来说非常适合***.com/questions/6052188/…【参考方案3】:

此扩展程序应在保持原始纵横比的同时缩放图像。图像的其余部分被裁剪。 (斯威夫特 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
    

【讨论】:

【参考方案4】:

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

【讨论】:

这将图像视为方面填充,有没有办法让它适合方面?谢谢 这不起作用。当我打印newRect.size 时,它给了我正确的缩小尺寸。但是,如果我打印newImage.size,我会看到image 的原始尺寸。任何想法为什么会这样?弄清楚了。 UIGraphicsBeginImageContextWithOptions(newSize, false, 0) 防止任何缩放。比例参数为0。如果你改用UIGraphicsBeginImageContext(newRect.size),一切都很好。【参考方案5】:

仅仅使用imageWithCGImage 是不够的。它会缩放,但无论放大还是缩小,结果都会变得模糊和次优。

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

我的工作测试代码看起来像这样,这是 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 你是说我提供的代码缺少处理在元数据中设置方向的 jpeg 所必需的部分吗?如果它不会太复杂,请随意编辑我的代码。我只是撕掉了我不需要的东西 在 Trevor 的代码中,他正在检查 imageOrientation(我假设是 EXIF 数据?)并根据其值进行额外的转换。不确定是否有必要。我会玩弄它。 谢谢@pixelfreak,那太好了。如您所见,我只是想让代码对一个用例来说简单。然后我做了一个 UIImageView 缓存不同的缩放版本(在一个静态数组中),以减轻这个调整大小例程的缓慢,并中断 setFrame 方法。我不确定这是否具有普遍意义:) @Yar 没错。如果您已经知道您将拥有的背景,例如在您的表格单元格中,您可以先绘制背景:[[UIColor whiteColor] set]; UIRectFill(newRect);【参考方案6】:

对于那些使用 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

【讨论】:

酷!那么你也可以把它作为一个扩展来做。 为什么需要翻转图片来调整大小(即为什么需要CGContextConcatCTM(context, flipVertical) kCGInterpolationHigh -> Swift 2 中的 CGInterpolation.High 否则会出现编译错误 @Crashalot 使用 CGInterpolationQuality.High 而不是 kCGInterpolationHigh 用于 swift 2.0 当我使用它时,每次调用 CGContextDrawImage 都会丢失 40MB。有没有人遇到过这样的问题?这对我来说无法使用,因为我正在尝试缩小大量图像并立即耗尽内存。【参考方案7】:

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

【讨论】:

【参考方案8】:

如果您在缩放时保留图像的原始纵横比,那么无论您缩小多少,最终都会得到清晰的图像。

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

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

【讨论】:

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

以上是关于如何缩小 UIImage 并使其同时变得清晰/锐利而不是模糊?的主要内容,如果未能解决你的问题,请参考以下文章

如何删除 UIImage 背景颜色并使其透明?

如何捕获 UITableView / UIScrollView 的完整内容的 UIImage 并使其在 ios 设备上工作

prefixtext 更改 textInputlayout 高度并使其变得巨大

裁剪 UIImage 的部分以获得新的纵横比并缩小到大小

只需一点小修改,HTC Vive画面会更清晰锐利

如何使用找到的组的新值更新行并使其尽可能快?