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

发布于 2024-10-02 17:49:41 字数 196 浏览 4 评论 0原文

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

我需要加载的图像的大小各不相同,我想检查我要加载的图像的大小;但是,使用 UIImage.size 属性需要加载图像......这正是我想要避免的。

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

As you know the iPhone guidelines discourage loading UIImages that are greater than 1024x1024.

The size of the images that I would have to load varies, and I would like to check the size of the image I am about to load; however using the .size property of UIImage requires the image to be loaded... which is exactly what I am trying to avoid.

Is there something wrong in my reasoning or is there a solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

尘世孤行 2024-10-09 17:49:41

从 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 页)

As of iOS 4.0, the iOS SDK includes the CGImageSource... functions (in the ImageIO framework). It's a very flexible API to query metadata without loading the image into memory. Getting the pixel dimensions of an image should work like this (make sure to include the ImageIO.framework in your target):

#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);

(adapted from "Programming with Quartz" by Gelphman and Laden, listing 9.5, page 228)

待天淡蓝洁白时 2024-10-09 17:49:41

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)
}

Swift 3 version of the answer:

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)
}
没有心的人 2024-10-09 17:49:41

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 图像文件进行了测试

,如 Apple 所说

kCGImagePropertyOrientation

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

In Swift 5, with 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] may be nil.

It is nil, I tested with png image file

as Apple says

kCGImagePropertyOrientation

The numeric value for this key encodes the intended display orientation for the image according to the TIFF and Exif specifications.

唯憾梦倾城 2024-10-09 17:49:41

仅供记录...

我已经尝试过:

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

对于存储在我的内部 SSD 上的大约 300k 图像,这需要 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 可以声称他的方法没有加载整个图像。

Just for the record ...

I have tried this:

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

This takes 1 minute for around 300k images stored on my internal SSD.
That would be OK.

However! .. if performed on a folder stored on an external hard disk, I get the following timings:

     - 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 (*)

All experiments were done on different folders on the same hard disk, WD MyPassport Ultra, 1 TB, USB-A connector to Macbook Air M2.
Timings with the same number of files/GB were the same folders, resp.
(*): these were timings where I added the following lines to the loop:

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

(Plus some error checking, which I omit here.)

First of all, we can see that the vast majority of time is spent on CGImageSourceCreateWithURL().
Second, there seem to be some caching effects, although I have a hard time understanding that, but that is not the point.
Third, the durations are not linear; I guess it might have something to do with the sizes of the files, too, but again, didn't investigate further.

So, it looks to me like CGImageSourceCreateWithURL() really loads the complete image file.

I don't see why Ole Bergmann can claim his approach does not load the whole image.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文