iOS 拍照中加入GPS和具體地理位置


最近有一個需求,要求用手機拍個照片,並切需要拍攝時間,拍攝gps,拍攝具體街道信息。

首先要感謝PhotoGPSdemo的作者,你可以到這里下載demo http://www.cocoachina.com/bbs/read.php?tid=130501

以前,總認為jpg就是包含了像素信息的2進制文件,其實,jpg中還可以包含許多起它的信息,只不過我們平常用查看jpg屬性時,系統沒有給我們把信息全部顯示出來而已!

在iOS中,提供了一組函數來操作jpg的這些額外的信息,你需要#import <ImageIO/ImageIO.h>才能使用他們。

首先,需要注意的是,UIImage對象中是沒有這些信息的,它僅僅包含圖像本身,jpg才包含這些信息,請不要弄混。

先看看怎么從jpg圖片中取得各種額外信息,代碼如下

- (IBAction)test
{
    NSString * home = NSHomeDirectory();
    NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/Documents/xx.jpg",home]];
    
    CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    
    CFDictionaryRef metaDataDicRef = CGImageSourceCopyPropertiesAtIndex(imgSource,0,nil);
    NSLog(@"cfdictionary %@",(__bridge NSDictionary *)metaDataDicRef);
    
    NSDictionary *dic = (__bridge NSDictionary *)metaDataDicRef;
    
    NSDictionary *exifDic = [dic objectForKey:(NSString *)kCGImagePropertyExifDictionary];
    
    NSString *location = [exifDic objectForKey:(NSString *)kCGImagePropertyExifCameraOwnerName];
    NSLog(@"location is %@",location);
    
    NSString *DateTimeOriginal = [exifDic objectForKey:(NSString *)kCGImagePropertyExifDateTimeOriginal];
    NSLog(@"time is %@",DateTimeOriginal);
    
    CFRelease(metaDataDicRef);
    CFRelease(imgSource);
}

首先,需要用到CGImageSourceRef類,不能用UIImage類型(需要驗證。。。),這種CGImageSourceRef類型可以通過函數取出jpg的額外數據,這些數據叫做metaData,翻譯過來就是元數據。得到的metaData是以字典形式返回的,其中的key是固定的,具體聲明見下方

CFStringRef kCGImagePropertyTIFFDictionary;
CFStringRef kCGImagePropertyGIFDictionary;
CFStringRef kCGImagePropertyJFIFDictionary;
CFStringRef kCGImagePropertyExifDictionary;
CFStringRef kCGImagePropertyPNGDictionary;
CFStringRef kCGImagePropertyIPTCDictionary;
CFStringRef kCGImagePropertyGPSDictionary;
CFStringRef kCGImagePropertyRawDictionary;
CFStringRef kCGImagePropertyCIFFDictionary;
CFStringRef kCGImageProperty8BIMDictionary;
CFStringRef kCGImagePropertyDNGDictionary;
CFStringRef kCGImagePropertyExifAuxDictionary;

這些key中,有許多key對應的對象還是一個字典,比如kCGImagePropertyExifDictionary所對應的對象包含一下key值

const CFStringRef kCGImagePropertyExifExposureTime;
const CFStringRef kCGImagePropertyExifFNumber;
const CFStringRef kCGImagePropertyExifExposureProgram;
const CFStringRef kCGImagePropertyExifSpectralSensitivity;
const CFStringRef kCGImagePropertyExifISOSpeedRatings;
const CFStringRef kCGImagePropertyExifOECF;
const CFStringRef kCGImagePropertyExifVersion;
const CFStringRef kCGImagePropertyExifDateTimeOriginal;
const CFStringRef kCGImagePropertyExifDateTimeDigitized;
const CFStringRef kCGImagePropertyExifComponentsConfiguration;
const CFStringRef kCGImagePropertyExifCompressedBitsPerPixel;
const CFStringRef kCGImagePropertyExifShutterSpeedValue;
const CFStringRef kCGImagePropertyExifApertureValue;
const CFStringRef kCGImagePropertyExifBrightnessValue;
const CFStringRef kCGImagePropertyExifExposureBiasValue;
const CFStringRef kCGImagePropertyExifMaxApertureValue;
const CFStringRef kCGImagePropertyExifSubjectDistance;
const CFStringRef kCGImagePropertyExifMeteringMode;
const CFStringRef kCGImagePropertyExifLightSource;
const CFStringRef kCGImagePropertyExifFlash;
const CFStringRef kCGImagePropertyExifFocalLength;
const CFStringRef kCGImagePropertyExifSubjectArea;
const CFStringRef kCGImagePropertyExifMakerNote;
const CFStringRef kCGImagePropertyExifUserComment;
const CFStringRef kCGImagePropertyExifSubsecTime;
const CFStringRef kCGImagePropertyExifSubsecTimeOrginal;
const CFStringRef kCGImagePropertyExifSubsecTimeDigitized;
const CFStringRef kCGImagePropertyExifFlashPixVersion;
const CFStringRef kCGImagePropertyExifColorSpace;
const CFStringRef kCGImagePropertyExifPixelXDimension;
const CFStringRef kCGImagePropertyExifPixelYDimension;
const CFStringRef kCGImagePropertyExifRelatedSoundFile;
const CFStringRef kCGImagePropertyExifFlashEnergy;
const CFStringRef kCGImagePropertyExifSpatialFrequencyResponse;
const CFStringRef kCGImagePropertyExifFocalPlaneXResolution;
const CFStringRef kCGImagePropertyExifFocalPlaneYResolution;
const CFStringRef kCGImagePropertyExifFocalPlaneResolutionUnit;
const CFStringRef kCGImagePropertyExifSubjectLocation;
const CFStringRef kCGImagePropertyExifExposureIndex;
const CFStringRef kCGImagePropertyExifSensingMethod;
const CFStringRef kCGImagePropertyExifFileSource;
const CFStringRef kCGImagePropertyExifSceneType;
const CFStringRef kCGImagePropertyExifCFAPattern;
const CFStringRef kCGImagePropertyExifCustomRendered;
const CFStringRef kCGImagePropertyExifExposureMode;
const CFStringRef kCGImagePropertyExifWhiteBalance;
const CFStringRef kCGImagePropertyExifDigitalZoomRatio;
const CFStringRef kCGImagePropertyExifFocalLenIn35mmFilm;
const CFStringRef kCGImagePropertyExifSceneCaptureType;
const CFStringRef kCGImagePropertyExifGainControl;
const CFStringRef kCGImagePropertyExifContrast;
const CFStringRef kCGImagePropertyExifSaturation;
const CFStringRef kCGImagePropertyExifSharpness;
const CFStringRef kCGImagePropertyExifDeviceSettingDescription;
const CFStringRef kCGImagePropertyExifSubjectDistRange;
const CFStringRef kCGImagePropertyExifImageUniqueID;
const CFStringRef kCGImagePropertyExifGamma;
const CFStringRef kCGImagePropertyExifCameraOwnerName;
const CFStringRef kCGImagePropertyExifBodySerialNumber;
const CFStringRef kCGImagePropertyExifLensSpecification;
const CFStringRef kCGImagePropertyExifLensMake;
const CFStringRef kCGImagePropertyExifLensModel;
const CFStringRef kCGImagePropertyExifLensSerialNumber;

這里的每一個key都有具體的含義,代表了jpg的一些信息。這些key和key的值時有嚴格的格式限定的,你用了自己定義的key或者不符合規則的值,是不能正確地寫入jpg文件的(待會要提到如何寫入metaData信息)。


 

如何用UIImagePickerController拍攝照片后加入信息呢?用這個控件拍照后,默認生成的metaData信息較少,只有拍攝時間等幾個信息,沒有gps信息,更沒有街道信息,我們需要手動加入。

 
         

#pragma mark - UIImagePickerControllerDelegate

 
         

 

 
         

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

 
         

{

 
         

    _mediaInfo =[NSMutableDictionarydictionaryWithDictionary:info];

 
         

    

 
         

    if (!_locationManager) {

 
         

        _locationManager = [[CLLocationManager alloc]init];

 
         

        [_locationManagersetDelegate:self];

 
         

        [_locationManagersetDistanceFilter:kCLDistanceFilterNone];

 
         

        [_locationManagersetDesiredAccuracy:kCLLocationAccuracyBest];

 
         

    }

 
         

    [_locationManagerstartUpdatingLocation];

 
         

    

 
         

    [picker dismissViewControllerAnimated:YEScompletion:^

 
         

     {

 
         

         

 
         

     }];

 
         

}

 
         

 

 
         

#pragma mark CLLocationManagerDelegate

 
         

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{

 
         

    [manager stopUpdatingLocation];

 
         

    

 
         

    UIImage *originalImage= (UIImage *) [_mediaInfoobjectForKey:UIImagePickerControllerOriginalImage];

 
         

    NSData *imageNSData = UIImageJPEGRepresentation(originalImage,1);

 
         

    

 
         

    CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageNSData, NULL);

 
         

    //this is the type of image (e.g., public.jpeg)

 
         

    CFStringRef UTI = CGImageSourceGetType(imgSource);

 
         

    

 
         

    //this will be the data CGImageDestinationRef will write into

 
         

    NSMutableData *newImageData = [NSMutableDatadata];

 
         

    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge  CFMutableDataRef)newImageData, UTI, 1, NULL);

 
         

    

 
         

    if(!destination)

 
         

    {

 
         

        NSLog(@"***Could not create image destination ***");

 
         

        return;

 
         

    }

 
         

    

 
         

    //get original metadata

 
         

    NSDictionary *dict = [_mediaInfoobjectForKey:UIImagePickerControllerMediaMetadata];

 
         

    NSMutableDictionary *metadata = [NSMutableDictionarydictionaryWithDictionary:dict];

 
         

    

 
         

    //set gps info into metadata,we need a dictionary

 
         

    NSDictionary * gpsDict=[newLocation GPSDictionary];

 
         

    if (metadata && gpsDict) {

 
         

        [metadata setValue:gpsDict forKey:(NSString*)kCGImagePropertyGPSDictionary];

 
         

    }

 
         

    

 
         

    //get location detail by CLGeocoder, this needs wifi

 
         

    CLGeocoder *geocoder = [[CLGeocoder alloc] init];

 
         

    [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error){

 
         

        if(error != nil)

 
         

        {

 
         

            NSLog(@"CLGeocoder error :%@ ",error);

 
         

        }

 
         

        else//if we can get place info ,we set it into meteData dic with kCGImagePropertyExifCameraOwnerName key

 
         

        {

 
         

            if(placemarks.count > 0)

 
         

            {

 
         

                CLPlacemark *placemark = [placemarks objectAtIndex:0];

 
         

                NSMutableDictionary *eifDic = (NSMutableDictionary *)[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary];

 
         

                [eifDic setObject:placemark.nameforKey:(NSString *)kCGImagePropertyExifCameraOwnerName];

 
         

            }

 
         

        }

 
         

        

 
         

        //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata

 
         

        CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge  CFDictionaryRef) metadata);

 
         

        

 
         

        BOOL success = NO;

 
         

        success = CGImageDestinationFinalize(destination);

 
         

        

 
         

        if(!success) {

 
         

            NSLog(@"***Could not create data from image destination ***");

 
         

            return ;

 
         

        }

 
         

        

 
         

        CFRelease(imgSource);

 
         

        CFRelease(destination);

 
         

        

 
         

        [self sendData:newImageData];

 
         

        [self writeTest:newImageData];

 
         

        

 
         

    }];

 
         

    

 
         

    

 
         

}

 
         

 

 

@implementation CLLocation (GPSDictionary)

 

-(NSDictionary*)GPSDictionary{

    NSDateFormatter *formatter  = [[NSDateFormatteralloc] init];

    [formatter setDateFormat:@"hh:mm:ss.SS"];

    

    CLLocation *location=self;

    NSDictionary *gpsDict   = [NSDictionarydictionaryWithObjectsAndKeys:

                               [NSNumbernumberWithFloat:fabs(location.coordinate.latitude)], kCGImagePropertyGPSLatitude,

                               ((location.coordinate.latitude >= 0) ? @"N" : @"S"), kCGImagePropertyGPSLatitudeRef,

                               [NSNumbernumberWithFloat:fabs(location.coordinate.longitude)], kCGImagePropertyGPSLongitude,

                               ((location.coordinate.longitude >= 0) ? @"E" : @"W"), kCGImagePropertyGPSLongitudeRef,

                               [formatter stringFromDate:[location timestamp]], kCGImagePropertyGPSTimeStamp,

                               nil];

    NSLog(@"gpsDict i %@",gpsDict);

    

    return gpsDict;

}

 

 

 

 

@end

 

 

 

上面代碼需要手動改改,它的最終目的就是用 CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadata);函數用新的metaData信息生成新的jpg數據。

其中gps數據用了系統自定義的key值寫入,街道信息沒有找到合適的key,就冒用了kCGImagePropertyExifCameraOwnerName這個key。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM