访问 UIImage 属性而不将图像加载到内存中

问题描述 投票:0回答:4

如您所知,iPhone 指南不鼓励加载大于 1024x1024 的

UIImage

我需要加载的图像大小各不相同,我想检查我要加载的图像的大小;然而,使用

.size
UIImage
属性需要加载图像......这正是我想要避免的。

我的推理有问题还是有解决办法?

iphone image uiimage size loading
4个回答
39
投票

从 iOS 4.0 开始,iOS SDK 包含

CGImageSource...
函数(在 ImageIO 框架中)。这是一个非常灵活的 API,可以查询元数据,而无需将图像加载到内存中。获取图像的像素尺寸应该像这样工作(确保在您的目标中包含 ImageIO.framework):

#import <ImageIO/ImageIO.h>

NSURL *imageFileURL = [NSURL fileURLWithPath:...];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageFileURL, NULL);
if (imageSource == NULL) {
    // Error loading image
    ...
    return;
}

CGFloat width = 0.0f, height = 0.0f;
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);

CFRelease(imageSource);

if (imageProperties != NULL) {

    CFNumberRef widthNum  = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
    if (widthNum != NULL) {
        CFNumberGetValue(widthNum, kCFNumberCGFloatType, &width);
    }

    CFNumberRef heightNum = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
    if (heightNum != NULL) {
        CFNumberGetValue(heightNum, kCFNumberCGFloatType, &height);
    }

    // Check orientation and flip size if required
    CFNumberRef orientationNum = CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation);
    if (orientationNum != NULL) {
        int orientation;
        CFNumberGetValue(orientationNum, kCFNumberIntType, &orientation);
        if (orientation > 4) {
            CGFloat temp = width;
            width = height;
            height = temp;
        }
    }

    CFRelease(imageProperties);
}

NSLog(@"Image dimensions: %.0f x %.0f px", width, height);

(改编自 Gelphman 和 Laden 的“Programming with Quartz”,清单 9.5,第 228 页)


7
投票

Swift 3版本的答案:

import Foundation
import ImageIO

func sizeForImage(at url: URL) -> CGSize? {

    guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil)
        , let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [AnyHashable: Any]
        , let pixelWidth = imageProperties[kCGImagePropertyPixelWidth as String]
        , let pixelHeight = imageProperties[kCGImagePropertyPixelHeight as String]
        , let orientationNumber = imageProperties[kCGImagePropertyOrientation as String]
        else {
            return nil
    }

    var width: CGFloat = 0, height: CGFloat = 0, orientation: Int = 0

    CFNumberGetValue(pixelWidth as! CFNumber, .cgFloatType, &width)
    CFNumberGetValue(pixelHeight as! CFNumber, .cgFloatType, &height)
    CFNumberGetValue(orientationNumber as! CFNumber, .intType, &orientation)

    // Check orientation and flip size if required
    if orientation > 4 { let temp = width; width = height; height = temp }

    return CGSize(width: width, height: height)
}

4
投票

Swift 5 中,使用

ImageIO

extension URL{
    var sizeOfImage: CGSize?{
        guard let imageSource = CGImageSourceCreateWithURL(self as CFURL, nil)
            , let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [AnyHashable: Any]
            , let pixelWidth = imageProperties[kCGImagePropertyPixelWidth as String] as! CFNumber?
            , let pixelHeight = imageProperties[kCGImagePropertyPixelHeight as String] as! CFNumber?
            else {
                return nil
        }
        var width: CGFloat = 0, height: CGFloat = 0
        CFNumberGetValue(pixelWidth, .cgFloatType, &width)
        CFNumberGetValue(pixelHeight, .cgFloatType, &height)
       }
       return CGSize(width: width, height: height)
    }
}

imageProperties[kCGImagePropertyOrientation as String] 
可能为零。

为零,我用

png
图像文件测试过

正如苹果所说

kCGImagePropertyOrientation

此键的数值根据 TIFF 和 Exif 规范对图像的预期显示方向进行编码。


0
投票

仅供记录...

我试过这个:

for ( NSString* filename in imagefiles ) 
{
    NSURL * imgurl = [NSURL fileURLWithPath: filename isDirectory: NO];
    CGImageSourceRef sourceref = CGImageSourceCreateWithURL( (__bridge CFURLRef) imgurl, NULL );
}

对于存储在我的内部 SSD 上的大约 30 万张图像,这需要 1 分钟。 这样就可以了。

但是! ..如果在存储在外部硬盘上的文件夹上执行,我会得到以下计时:

     - 20 min for 150k images (45 GB) 
     - 12 min for 150k images (45 GB), second time
     - 150 sec for 25k images (18 GB)
     - 170 sec for 25k images (18 GB), with the lines below (*)
     - 80 sec for 22k (3 GB) images
     - 80 sec for 22k (3 GB) images, with the lines below (*)

所有实验均在同一硬盘、WD MyPassport Ultra、1 TB、连接 Macbook Air M2 的 USB-A 连接器上的不同文件夹上进行。 具有相同数量的文件/GB 的时间分别是相同的文件夹。
(*):这些是我在循环中添加以下几行的时间:

   CFDictionaryRef fileProps = CGImageSourceCopyPropertiesAtIndex( image, 0, NULL );
   bool success = CFDictionaryGetValueIfPresent( fileProps, kCGImagePropertyExifDictionary, (const void **) & exif_dict );
   CFDictionaryGetValueIfPresent( exif_dict, kCGImagePropertyExifDateTimeDigitized, (const void **) & dateref );
   iso_date = [isoDateFormatter_ dateFromString: (__bridge NSString * _Nonnull)(dateref) ];
   [datesAndTimes_ addObject: iso_date ];

(加上一些错误检查,我在这里省略。)

首先我们可以看到,绝大多数时间都花在了CGImageSourceCreateWithURL()上。 其次,似乎有一些缓存效果,虽然我很难理解这一点,但这不是重点。 第三,持续时间不是线性的;我想这可能也与文件的大小有关,但同样,没有进一步调查。

所以,在我看来 CGImageSourceCreateWithURL() 确实加载了完整的图像文件。

我不明白为什么 Ole Bergmann 可以声称他的方法没有加载整个图像。

© www.soinside.com 2019 - 2024. All rights reserved.