如何在iOS中获取图像元数据

17

我想使用iOS显示图像元数据。元数据例如光圈,快门速度,曝光补偿,ISO,镜头焦距等等。如果有人有想法,请帮帮我。


图像元数据是什么意思(名称、大小和其他一些信息)?请详细说明... - Pranav Jaiswal
10个回答

30
CGImageSourceRef source = CGImageSourceCreateWithURL( (CFURLRef) aUrl, NULL);
CGImageSourceRef source = CGImageSourceCreateWithData( (CFDataRef) theData, NULL);
NSDictionary* metadata = (NSDictionary *)CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source,0,NULL));
CFRelease(source);

查看字典内容这里

谢谢Jonas Schnelli。这对我帮助很大。谢谢伙计。 - Hardik Shah
4
当然,你需要提到要在头文件中添加 #import <ImageIO/ImageIO.h> 才能使用该类... - jesses.co.tt
1
如果您正在使用ARC,请按以下方式拆分以避免内存泄漏: CFDictionaryRef dictRef = CGImageSourceCopyPropertiesAtIndex(source,0,NULL); NSDictionary* metadata = (__bridge NSDictionary *)dictRef; <do your thing> CFRelease(source); CFRelease(dictRef); - Drew H

14

以下是获取图片元数据的代码:

NSData *imagedata = [NSData dataWithContentsOfFile:imagePath];
CGImageSourceRef source = CGImageSourceCreateWithData((CFMutableDataRef)imagedata, NULL);
NSDictionary *metadata = [(NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source,0,NULL)autorelease];

或者,如果使用 Swift 4.0:

var imagedata = Data(contentsOfFile: imagePath) 
var source: CGImageSourceRef = CGImageSourceCreateWithData((imagedata as? CFMutableDataRef), nil) 
var metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [AnyHashable: Any]

如何在Swift3或Swift4中实现? - Saurabh Prajapati
1
变量 imagedata = Data(contentsOfFile: imagePath) var source: CGImageSourceRef = CGImageSourceCreateWithData((imagedata as? CFMutableDataRef), nil) var metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [AnyHashable: Any] - Usman Nisar
希望它能对你有所帮助。 - Usman Nisar
是的!我也尝试了同样的方法,它有效!我认为你需要在你的答案中更新这个。 - Saurabh Prajapati
很抱歉在这么长时间之后再提出这个问题,但是基准测试显示,读取元数据的速度取决于图像的大小(这不应该是这样的,因为元数据只是Data的一个固定前缀)。 我是否漏掉了什么? 具体来说,读取一个50KiB图像的元数据需要50纳秒,而读取一个1.7MiB图像的元数据需要800纳秒(与UIImage(data:)相同)。 - Benny K

5

swift 4

static func imageDataProperties(_ imageData: Data) -> NSDictionary? {
    if let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil)
    {
      if let dictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) {
        return dictionary
      }
    }
    return nil
  }

2
通过Swift结构包装的Core Graphics读取图像属性:
struct ImageMetadata {

    var imageProperties : [CFString: Any]

    init?(data: Data) {
        let options = [kCGImageSourceShouldCache: kCFBooleanFalse]
        if let imageSource = CGImageSourceCreateWithData(data as CFData, options as CFDictionary),
            let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any] {
            self.imageProperties = imageProperties
        } else {
            return nil
        }
    }

    var dpi : Int? { imageProperties[kCGImagePropertyDPIWidth] as? Int }
    var width : Int? { imageProperties[kCGImagePropertyPixelWidth] as? Int }
    var height : Int? { imageProperties[kCGImagePropertyPixelHeight] as? Int }

}

1

使用照片框架升级到iOS 11

Objective-C:

#import <Photos/Photos.h>

- (void)imagePickerController:(UIImagePickerController *)imagePicker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {

    PHAsset* asset = info[UIImagePickerControllerPHAsset];

    [asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
        CIImage *fullImage = [CIImage imageWithContentsOfURL:contentEditingInput.fullSizeImageURL];

        NSLog(@"%@", fullImage.properties.description);
    }];

    [imagePicker dismissViewControllerAnimated:YES completion:nil];
}

你还需要获得照片库使用权限(NSPhotoLibraryUsageDescription),然后可以添加以下代码到视图加载或视图出现中

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
    switch (status) {
        case PHAuthorizationStatusAuthorized:
            NSLog(@"PHAuthorizationStatusAuthorized");
            break;
        case PHAuthorizationStatusDenied:
            NSLog(@"PHAuthorizationStatusDenied");
            break;
        case PHAuthorizationStatusNotDetermined:
            NSLog(@"PHAuthorizationStatusNotDetermined");
            break;
        case PHAuthorizationStatusRestricted:
            NSLog(@"PHAuthorizationStatusRestricted");
            break;
    }
}];

0

回答自己的问题。获取GPS元数据的内存有效且快速的方法是

let options = [kCGImageSourceShouldCache as String: kCFBooleanFalse]
if let data = NSData(contentsOfURL: url), imgSrc = CGImageSourceCreateWithData(data, options) {
    let metadata = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options) as Dictionary
    let gpsData = metadata[kCGImagePropertyGPSDictionary] as? [String : AnyObject]
}

第二个选项是:
if let img = CIImage(contentsOfURL: url), metadata = img.properties(), 
gpsData = metadata[kCGImagePropertyGPSDictionary] as? [String : AnyObject] {  }

在 Swift 中看起来更漂亮,但使用的内存更多(通过 Profiler 测试)。


0
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
  NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL];

    if(referenceURL) {
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library assetForURL:referenceURL resultBlock:^(ALAsset *asset) {
        ALAssetRepresentation *rep = [asset defaultRepresentation];
        NSDictionary *metadata = rep.metadata;
        NSLog(@"image data %@", metadata);


    } failureBlock:^(NSError *error) {
        // error handling
    }];
}

在你的回答中给出一些解释会更好。 - Envil

0

这是我用来获取图像大小的代码:

+ (CGSize) sizeOfImage:(NSString *) fileName withPath: (NSURL *) fullDirectoryPath;
{
    NSURL *imageNameWithPath = [NSURL URLWithString:fileName relativeToURL:fullDirectoryPath];

    CGImageSourceRef source = CGImageSourceCreateWithURL( (CFURLRef) imageNameWithPath, NULL);
    if (!source) return CGSizeZero;

    CFDictionaryRef dictRef = CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
    NSDictionary* metadata = (__bridge NSDictionary *)dictRef;
    NSLog(@"metadata= %@", metadata);
    CGSize result = CGSizeZero;
    CGFloat width = [metadata[@"PixelWidth"] floatValue];
    CGFloat height = [metadata[@"PixelHeight"] floatValue];
    NSLog(@"width= %f, height= %f", width, height);

    // The orientation in the metadata does *not* map to UIImageOrientation. Rather, see: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGImageProperties_Reference/index.html#//apple_ref/doc/constant_group/Individual_Image_Properties
    // This idea of orientation seems a little odd to me, but it seems it translates to even numbers need to be switched in width/height, odd numbers do not.
    NSUInteger orientation = [metadata[@"Orientation"] integerValue];

    switch (orientation) {
        // Comments give "Location of the origin of the image"
        case 1: // Top, left
        case 3: // Bottom, right
        case 5: // Left, top
        case 7: // Right, bottom
            result = CGSizeMake(width, height);
            break;

        case 2: // Top, right
        case 4: // Bottom, left
        case 6: // Right, top
        case 8: // Left, bottom
            result = CGSizeMake(height, width);
            break;

        default:
            NSAssert(NO, @"Should not get to here!");
            break;
    }

    CFRelease(source);
    NSLog(@"size: %@, orientation: %d", NSStringFromCGSize(result), orientation);

    return result;
}

/* Example meta data:
    ColorModel = RGB;
    Depth = 8;
    Orientation = 6;
    PixelHeight = 1936;
    PixelWidth = 2592;
    "{Exif}" =     {
        ColorSpace = 1;
        PixelXDimension = 2592;
        PixelYDimension = 1936;
    };
    "{JFIF}" =     {
        DensityUnit = 0;
        JFIFVersion =         (
                               1,
                               1
                               );
        XDensity = 1;
        YDensity = 1;
    };
    "{TIFF}" =     {
        Orientation = 6;
    };
}
*/

0
这是一个示例代码,可以帮助我们读取光圈、快门速度、曝光补偿、ISO、镜头、焦距等信息,并将其转换为字典格式。
考虑单个类的所有以下函数。
guard let imageSource = CGImageSourceCreateWithURL(fileURL as CFURL, nil),
      let metadata = CGImageSourceCopyMetadataAtIndex(imageSource, 0, nil),
      let tags = CGImageMetadataCopyTags(metadata),
      let imageInfo = self.readMetadataTagArr(tagArr: tags) else { return }

然后有一些辅助函数,实际上是完成所有艰苦工作,将数据提取/转换为字典格式。

    /// Reads the Arrays of tags and convert them into a dictionary of [String: Any]
private static func readMetadataTagArr(tagArr: CFArray) -> [String: Any]? {
    var result = [String: Any]()
    for (_, tag) in (tagArr as NSArray).enumerated() {
        let tagMetadata = tag as! CGImageMetadataTag
        if let cfName = CGImageMetadataTagCopyName(tagMetadata) {
            let name = String(cfName)
            result[name] = self.readMetadataTag(metadataTag: tagMetadata)
        }
    }
    return result
}

    /// convert CGImageMetadataTag to a dictionary of [String: Any]
private static func readMetadataTag(metadataTag: CGImageMetadataTag) -> [String: Any] {
    var result = [String: Any]()
    guard let cfName = CGImageMetadataTagCopyName(metadataTag) else { return result }
    let name = String(cfName)
    let value = CGImageMetadataTagCopyValue(metadataTag)
    
    /// checking the type of `value` object and then performing respective operation on `value`
    if CFGetTypeID(value) == CFStringGetTypeID() {
        let valueStr = String(value as! CFString)
        result[name] = valueStr
    } else if CFGetTypeID(value) == CFDictionaryGetTypeID() {
        let nsDict: NSDictionary = value as! CFDictionary
        result[name] = self.getDictionary(from: nsDict)
    } else if CFGetTypeID(value) == CFArrayGetTypeID() {
        let valueArr: NSArray = value as! CFArray
        for (_, item) in valueArr.enumerated() {
            let tagMetadata = item as! CGImageMetadataTag
            result[name] = self.readMetadataTag(metadataTag: tagMetadata)
        }
    } else {
        // when the data was of some other type
        let descriptionString: CFString = CFCopyDescription(value);
        let str = String(descriptionString)
        result[name] = str
    }
    return result
}

    /// Converting CGImage Metadata dictionary to [String: Any]
private static func getDictionary(from nsDict: NSDictionary) -> [String: Any] {
    var subDictionary = [String: Any]()
    for (key, val) in nsDict {
        guard let key = key as? String else { continue }
        let tempDict: [String: Any] = [key: val]
        if JSONSerialization.isValidJSONObject(tempDict) {
            subDictionary[key] = val
        } else {
            let mData = val as! CGImageMetadataTag
            let tempDict: [String: Any] = [key: self.readMetadataTag(metadataTag: mData)]
            if JSONSerialization.isValidJSONObject(tempDict) {
                subDictionary[key] = tempDict
            }
        }
    }
    return subDictionary
}

这是提取的所有字典键列表 这是提取的所有字典键列表

这是一些示例字典值,如FocalLength,ApertureValue,LensSpecification等 输入图像描述

LensModel,ShutterSpeedValue等 详细信息


-2

您需要使用资产库来读取图像的exif元数据。

#import <AssetsLibrary/AssetsLibrary.h>

然后添加以下代码:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    __block NSMutableDictionary *imageMetadata = nil;
    [library assetForURL:assetURL
             resultBlock:^(ALAsset *asset)  {
                 NSDictionary *metadata = asset.defaultRepresentation.metadata;
                 imageMetadata = [[NSMutableDictionary alloc] initWithDictionary:metadata];
                 NSLog(@"%@",imageMetadata.description);
             }
            failureBlock:^(NSError *error) {
            }];
}

希望这能有所帮助


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接