2016-06-23 13 views
5

Używam UIImagePickerController do wybierania zdjęć w mojej aplikacji na iOS i wiem, że informacje exif można uzyskać za pomocą informacji [UIImagePickerControllerMediaMetadata]. Ale kiedy przesyłam mój obraz do mojego serwera przez UIImage, większość informacji exif została rozłożona. Zastanawiam się, czy mogę dodać informacje exif do mojego obrazu w żądaniu HTTP (obrazek przesłany jako jpg po tym). Jeśli nie, w jaki sposób powinienem rozwiązać ten problem? Chcę zmiany marki, modelu atrybutów (innymi słowy, co urządzenie zostało użyte, aby wziąć ten obraz)Swift, jak modyfikować informacje exif na zdjęciach zrobionych aparatem komórkowym

Poniżej są moje fragmenty kodu:

func Tapped() { 
    let myPickerController = UIImagePickerController() 

    myPickerController.delegate = self 
    myPickerController.sourceType = UIImagePickerControllerSourceType.Camera 
    myPickerController.allowsEditing = false 
    self.presentViewController(myPickerController, animated: true, completion: nil) 
} 
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { 
    let image = info[UIImagePickerControllerOriginalImage] as? UIImage 
    myImageView.image = image 
    UIImageWriteToSavedPhotosAlbum(image!, self, #selector(ViewController.image(_:didFinishSavingWithError:contextInfo:)), nil) 
    self.dismissViewControllerAnimated(true, completion: nil) 
} 

func myImageUploadRequest() 
{ 

    let myUrl = NSURL(string: "http://XXXXXX/Uploadfile") 

    let request = NSMutableURLRequest(URL:myUrl!) 
    request.HTTPMethod = "POST" 

    let param = [ 
     "userId" : "7" 
    ] 

    let boundary = generateBoundaryString() 

    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") 


    let imageData = UIImageJPEGRepresentation(myImageView.image!, 1) 

    if(imageData == nil) { return; } 

    request.HTTPBody = createBodyWithParameters(param, filePathKey: "file", imageDataKey: imageData!, boundary: boundary) 



    myActivityIndicator.startAnimating() 

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { 
     data, response, error in 

     if error != nil { 
      print("error=\(error)") 
      return 
     } 

     // You can print out response object 
     print("******* response = \(response)") 

     // Print out response body 
     let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding) 
     print("****** response data = \(responseString!)") 

     do{ 
      let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary 

     }catch{ 
      print(error) 
     } 


     dispatch_async(dispatch_get_main_queue(),{ 
      self.myActivityIndicator.stopAnimating() 
      self.myImageView.image = nil 
     }) 

    } 

    task.resume() 
} 

func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, imageDataKey: NSData, boundary: String) -> NSData { 
    let body = NSMutableData(); 

    if parameters != nil { 
     for (key, value) in parameters! { 
      body.appendString("--\(boundary)\r\n") 
      body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n") 
      body.appendString("\(value)\r\n") 
     } 
    } 

    let filename = "test.jpg" 

    let mimetype = "image/jpg" 

    body.appendString("--\(boundary)\r\n") 
    body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n") 
    body.appendString("Content-Type: \(mimetype)\r\n\r\n") 
    body.appendData(imageDataKey) 
    body.appendString("\r\n") 



    body.appendString("--\(boundary)--\r\n") 

    return body 
} 

func generateBoundaryString() -> String { 
    return "Boundary-\(NSUUID().UUIDString)" 
} 

extension NSMutableData { 

func appendString(string: String) { 
    let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) 
    appendData(data!) 
} 
} 
+0

Wszelkie aktualizacje na ten temat? Ja też nie mogę znaleźć prostego sposobu robienia tego. – jonmecer

Odpowiedz

2

Tak! W końcu zrobiłem sztuczkę, aby zmodyfikować informacje EXIF. Na początku możesz uzyskać informacje EXIF ​​z informacji [UIImagePickerControllerMediaMetadata] i NSData bez EXIF ​​z wybranego UIImage przez UIImageJPEGRepresentation. Następnie możesz utworzyć nowy NSDictionary ze zmodyfikowanymi informacjami EXIF. Następnie wywołaj moją funkcję w następujący sposób, możesz uzyskać obraz NSData ze zmodyfikowanym EXIF!

func saveImageWithImageData(data: NSData, properties: NSDictionary, completion: (data: NSData, path: NSURL) -> Void) { 

    let imageRef: CGImageSourceRef = CGImageSourceCreateWithData((data as CFDataRef), nil)! 
    let uti: CFString = CGImageSourceGetType(imageRef)! 
    let dataWithEXIF: NSMutableData = NSMutableData(data: data) 
    let destination: CGImageDestinationRef = CGImageDestinationCreateWithData((dataWithEXIF as CFMutableDataRef), uti, 1, nil)! 

    CGImageDestinationAddImageFromSource(destination, imageRef, 0, (properties as CFDictionaryRef)) 
    CGImageDestinationFinalize(destination) 

    var paths: [AnyObject] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) 
    let savePath: String = paths[0].stringByAppendingPathComponent("exif.jpg") 

    let manager: NSFileManager = NSFileManager.defaultManager() 
    manager.createFileAtPath(savePath, contents: dataWithEXIF, attributes: nil) 

    completion(data: dataWithEXIF,path: NSURL(string: savePath)!) 

    print("image with EXIF info converting to NSData: Done! Ready to upload! ") 

} 
0

SWIFT 3

W przypadku jesteś przechwytywania wideo i coraz CMSampleBuffer istnieje sposób zaktualizować metadane EXIF. W moim przypadku w iOS9 nie dostać DateTimeOriginal, choć w iOS10 DataTimeOriginal był już w. Tak więc musiałem umieścić kilka dodatkowych par klucz-wartość w.

self.stillCameraOutput.captureStillImageAsynchronously(from: connectionVideo) { (sampleBuffer, err) in 
     if let err = err { 
      blockCompletion(nil, err as NSError?) 
     } 
     else { 
      if let sampleBuffer = sampleBuffer { 
       let rawMetadata = CMCopyDictionaryOfAttachments(nil, sampleBuffer, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate)) 
       let metadata = CFDictionaryCreateMutableCopy(nil, 0, rawMetadata) as NSMutableDictionary 

       let exifData = metadata.value(forKey: "{Exif}") as? NSMutableDictionary 

       print("EXIF DATA: \(exifData)") 

       if let dateTime = exifData?["DateTimeOriginal"] as? String { 
        print("DateTime exists \(dateTime)") 
       } 
       else { 
        exifData?.setValue(Date().exifDate(), forKey: "DateTimeOriginal") 
       } 

       if let dateTime = exifData?["DateTimeDigitized"] as? String { 
        print("DateTime exists \(dateTime)") 
       } 
       else { 
        exifData?.setValue(Date().exifDate(), forKey: "DateTimeDigitized") 
       } 

       metadata.setValue(exifData, forKey: "{Exif}") 

       CMSetAttachments(sampleBuffer, metadata as CFDictionary, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate)) 

       let rawMetadata2 = CMCopyDictionaryOfAttachments(nil, sampleBuffer, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate)) 
       let metadata2 = CFDictionaryCreateMutableCopy(nil, 0, rawMetadata2) as NSMutableDictionary 

       let exifData2 = metadata2.value(forKey: "{Exif}") as? NSMutableDictionary 

       print("EXIF DATA: \(exifData2)") 

       if let dataImage = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer) { 
        blockCompletion(dataImage, nil) 
       } 
       else { 
        blockCompletion(nil, nil) 
       } 
      } 
      else { 
       blockCompletion(nil, nil) 
      } 
     } 
    } 
+0

Próbowałem tej metody, ale moje rawMetadata jest zerowe. Czy to działa na dane obrazu z AVCapturePhotoOutput? –

+0

http://stackoverflow.com/questions/43489623/geotag-images-from-image-picker-swift-3 Czy potrafisz to również –

1

używania i mergind pewne informacje od innych stanowisk, Podszedłem do problemu używając słownika w Swift. Używałem go w captureOutput z AVFounfation zwrotnego dla AVCapturePhoto:

func photoOutput(_ output: AVCapturePhotoOutput, 
       didFinishProcessingPhoto photo: AVCapturePhoto, 
       error: Error?) { 

    //retrieve exif information 
    var photoFormatDescription: CMFormatDescription? 
    CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, photoPixelBuffer, &photoFormatDescription) 

    var metadataAttachments: Dictionary = photo.metadata as Dictionary 

    if var exifData = metadataAttachments["{Exif}"] as? [String: Any] { 
     exifData[kCGImagePropertyExifUserComment as String] = "<whatever you want to write>" 

    metadataAttachments[kCGImagePropertyExifDictionary as String] = exifData 
    } 

} 

Po że „metadataAttachments” jest używany do budowy ostateczny obraz (przy użyciu CGImageDestinationAddImage w moim przypadku)

Wydaje działa (próbowałem w kompilacji projektu z Swift 4.0)

Mam nadzieję, że to pomoże!

Powiązane problemy