web-dev-qa-db-ja.com

iOS iPhoneでカメラから返された画像からGPSデータを読み取る

IOSデバイスのカメラで撮影した画像のGPS座標を取得する必要があります。 UIImagePickerControllerSourceTypeCameraで撮影した画像だけで、カメラロールの画像は気にしません。

IImageからExifデータを取得する-UIImagePickerController など、多くのstackoverflowの回答を読みました。画像からではなく、アプリ自体からの緯度/経度。

CoreLocationの使用はオプションではありません。シャッターボタンが押されたとき、それは私に座標を与えません。 (CoreLocationベースのソリューションでは、カメラビューを表示する前または後に座標を記録する必要があります。もちろん、デバイスが移動している場合は座標が間違っています。この方法は固定デバイスで動作します。)

私はiOS5のみなので、古いデバイスをサポートする必要はありません。これは商用製品でもあるため、 http://code.google.com/p/iphone-exif/ を使用できません。

では、iOS5のカメラから返された画像からGPSデータを読み取るためのオプションは何ですか?私が今考えることができるのは、カメラロールに画像を保存してからAssetsLibraryを使用することだけですが、それはひどいようです。

ありがとう!


これが、カレブの答えに基づいて書いたコードです。

    UIImage *image =  [info objectForKey:UIImagePickerControllerOriginalImage];

    NSData *jpeg = UIImageJPEGRepresentation(image,1.0);
    CGImageSourceRef  source ;
    source = CGImageSourceCreateWithData((__bridge CFDataRef)jpeg, NULL);

    NSDictionary *metadataNew = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);  

    NSLog(@"%@",metadataNew);

私のコンソールは以下を示しています:

    2012-04-26 14:15:37:137 ferret[2060:1799] {
        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;
        };
    }

緯度/経度はありません。

34
Paul Cezanne

1つの可能性は、カメラが表示されているときにCoreLocationを実行したままにすることです。サンプルの時間とともに各CCLocationを配列に記録します。写真が戻ってきたら、その時間を見つけて、配列から最も近いCClocationと一致させます。

気味悪いですが、動作します。

4
Paul Cezanne

問題は、iOS 4 UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];は、位置情報を取り除きます。この問題を解決するには、元の写真パスを使用して完全な画像メタデータにアクセスする必要があります。このようなもので:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL];
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library assetForURL:referenceURL resultBlock:^(ALAsset *asset) {
        ALAssetRepresentation *rep = [asset defaultRepresentation];
        NSDictionary *metadata = rep.metadata;
        NSLog(@"%@", metadata);

        CGImageRef iref = [rep fullScreenImage] ;

        if (iref) {
            self.imageView.image = [UIImage imageWithCGImage:iref];
        }
    } failureBlock:^(NSError *error) {
        // error handling
    }];

出力は次のようになります。

{
    ColorModel = RGB;
    DPIHeight = 72;
    DPIWidth = 72;
    Depth = 8;
    Orientation = 6;
    PixelHeight = 1936;
    PixelWidth = 2592;
    "{Exif}" =     {
        ApertureValue = "2.970854";
        BrightnessValue = "1.115874";
        ColorSpace = 1;
        ComponentsConfiguration =         (
            0,
            0,
            0,
            1
        );
        DateTimeDigitized = "2012:07:14 21:55:05";
        DateTimeOriginal = "2012:07:14 21:55:05";
        ExifVersion =         (
            2,
            2,
            1
        );
        ExposureMode = 0;
        ExposureProgram = 2;
        ExposureTime = "0.06666667";
        FNumber = "2.8";
        Flash = 24;
        FlashPixVersion =         (
            1,
            0
        );
        FocalLength = "3.85";
        ISOSpeedRatings =         (
            200
        );
        MeteringMode = 5;
        PixelXDimension = 2592;
        PixelYDimension = 1936;
        SceneCaptureType = 0;
        SensingMethod = 2;
        Sharpness = 2;
        ShutterSpeedValue = "3.9112";
        SubjectArea =         (
            1295,
            967,
            699,
            696
        );
        WhiteBalance = 0;
    };
    "{GPS}" =     {
        Altitude = "1167.528";
        AltitudeRef = 0;
        ImgDirection = "278.8303";
        ImgDirectionRef = T;
        Latitude = "15.8235";
        LatitudeRef = S;
        Longitude = "47.99416666666666";
        LongitudeRef = W;
        TimeStamp = "00:55:04.59";
    };
    "{TIFF}" =     {
        DateTime = "2012:07:14 21:55:05";
        Make = Apple;
        Model = "iPhone 4";
        Orientation = 6;
        ResolutionUnit = 2;
        Software = "5.1.1";
        XResolution = 72;
        YResolution = 72;
        "_YCbCrPositioning" = 1;
    };
}
16
Carlos Borges

カメラとUIImagePickerControllerで多くの作業を行いましたが、少なくともiOS 5.1.1までは、UIImagePickerControllerで撮影した写真またはビデオのメタデータで位置データを返しません。 。

カメラアプリで位置情報サービスが有効になっているかどうかは関係ありません。これは、UIImagePickerController内のカメラ機能ではなく、カメラアプリの位置情報サービスの使用を制御します。

アプリはCLLocationクラスを使用して場所を取得し、カメラから返された画像またはビデオに追加する必要があります。アプリが位置情報を取得できるかどうかは、ユーザーがアプリの位置情報サービスへのアクセスを許可するかどうかによって異なります。また、ユーザーはSettings > Location Servicesを介して、いつでもアプリ(またはデバイス全体)の位置情報サービスを無効にできることに注意してください。

12
Chris Markle

投稿したコードでカメラの画像データを使用しておらず、JPEG表現を生成しているため、本質的にすべてのメタデータが破棄されます。使用する image.CGImageカレブが提案したように。

また:

これは商用製品でもあるため、 http://code.google.com/p/iphone-exif/ を使用できません。

著者は、商用ライセンスが利用可能であることを明確に述べています。

4
Jim

私はこれを自分のもので正確に行う必要があると言うことはできませんが、ドキュメントから_UIImagePickerControllerを使用している場合は、ユーザーが_-imagePicker:didFinishPickingMediaWithInfo:_デリゲートメソッド。キーUIImagePickerControllerOriginalImageを使用して画像を取得します。

画像を取得したら、 QA1654 ImageIOを使用した画像プロパティへのアクセス で説明されているように、EXIFデータを含むそのプロパティにアクセスできるはずです。 CGImageSourceを作成するには、CGImageSourceCreateWithData()を見て、UIImageのCGImageメソッドから取得したデータを使用します。画像ソースを取得したら、CGImageSourceCopyProperties()を介してさまざまな属性にアクセスできます。

3
Caleb

Chris Markle 、Appleは、EXIFからGPSデータを取り除きます。ただし、画像のRAWデータを開き、自分でデータを解析するか、使用することができます の場合、それを行うサードパーティのライブラリ。

サンプルコードを次に示します。

- (void) imagePickerController: (UIImagePickerController *) picker
 didFinishPickingMediaWithInfo: (NSDictionary *) info {

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL]
             resultBlock:^(ALAsset *asset) {

                 ALAssetRepresentation *image_representation = [asset defaultRepresentation];
                 NSUInteger size = (NSUInteger)image_representation.size;
                 // create a buffer to hold image data
                 uint8_t *buffer = (Byte*)malloc(size);
                 NSUInteger length = [image_representation getBytes:buffer fromOffset: 0.0  length:size error:nil];

                 if (length != 0)  {

                     // buffer -> NSData object; free buffer afterwards
                     NSData *adata = [[NSData alloc] initWithBytesNoCopy:buffer length:size freeWhenDone:YES];

                     EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];
                     [jpegScanner scanImageData: adata];
                     EXFMetaData* exifData = jpegScanner.exifMetaData;

                     id latitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLatitude]];
                     id longitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLongitude]];
                     id datetime = [exifData tagValue:[NSNumber numberWithInt:EXIF_DateTime]];
                     id t = [exifData tagValue:[NSNumber numberWithInt:EXIF_Model]];

                     self.locationLabel.text = [NSString stringWithFormat:@"Local: %@ - %@",latitudeValue,longitudeValue];
                     self.dateLavel.text = [NSString stringWithFormat:@"Data: %@", datetime];

                 }
                 else {
                     NSLog(@"image_representation buffer length == 0");
                 }
             }
            failureBlock:^(NSError *error) {
                NSLog(@"couldn't get asset: %@", error);
            }
     ];
}

UIImagePickerControllerデリゲートで、次を実行します。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
  NSDictionary *metadata = [info valueForKey:UIImagePickerControllerMediaMetadata];

  // metadata now contains all the image metadata.  Extract GPS data from here.
}
0
rekle

Swift回答:

import AssetsLibrary
import CoreLocation


// MARK: - UIImagePickerControllerDelegate
extension ViewController: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        defer {
            dismiss(animated: true, completion: nil)
        }
        guard picker.sourceType == .photoLibrary else {
            return
        }
        guard let url = info[UIImagePickerControllerReferenceURL] as? URL else {
            return
        }

        let library = ALAssetsLibrary()
        library.asset(for: url, resultBlock: { (asset) in
            guard let coordinate = asset?.value(forProperty: ALAssetPropertyLocation) as? CLLocation else {
                return
            }
            print("\(coordinate)")

            // Getting human-readable address.
            let geocoder = CLGeocoder()
            geocoder.reverseGeocodeLocation(coordinate, completionHandler: { (placemarks, error) in
                guard let placemark = placemarks?.first else {
                    return
                }
                print("\(placemark.addressDictionary)")
            })
        }, failureBlock: { (error: Error?) in
            print("Unable to read metadata: \(error)")
        })
    }
}
0
Andrey Gordeev

これはiOS 8でテストされており、動画でも機能するため、いくつかの調整を加えた写真でも同様に機能します。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    NSURL *videoUrl = (NSURL *)[info objectForKey:UIImagePickerControllerMediaURL];
    NSString *moviePath = [videoUrl path];

    if ( UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(moviePath) ) {

        ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];

        [assetLibrary assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) {

            CLLocation *location = [asset valueForProperty:ALAssetPropertyLocation];
            NSLog(@"Location Meta: %@", location);

        } failureBlock:^(NSError *error) {
            NSLog(@"Video Date Error: %@", error);
        }];

    }

}
0
Nur Iman Izam