Swift中来自图像的一致二进制数据 [英] Consistent binary data from images in Swift

查看:72
本文介绍了Swift中来自图像的一致二进制数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于一个小项目,我正在制作一个iOS应用程序,该应用程序应该做两件事:

For a small project, I'm making an iOS app which should do two things:

  1. 拍照
  2. 从图片数据中获取哈希值(并将其打印到xcode控制台)

然后,我想将图片导出到笔记本电脑并确认哈希.我尝试通过AirDrop,Photos.app,电子邮件和iCloud导出(Photos.app压缩照片,iCloud将其转换为 .png ).

Then, I want to export the picture to my laptop and confirm the hash. I tried exporting via AirDrop, Photos.app, email and iCloud (Photos.app compresses the photo and iCloud transforms it into an .png).

问题是,我无法降低哈希值.这意味着导出的图片与应用程序中的图片不同.我试图一一排除一些变量.要从图片获取 NSData ,可以使用 UIImagePNGRepresentation UIImageJPEGRepresentation 函数,在提取数据之前强制使用格式表示形式的图像.坦白地说,我不确定这些函数的作用(除了转换为 NSData 之外),但是它们之间的功能有所不同,因为它们彼此之间的比较结果和与之相比所得出的结果不同导出的数据( .jpg ).

Problem is, I can't repodruce the hash. This means that the exported picture differs from the picture in the app. There are some variables I tried to rule out one by one. To get NSData from a picture, one can use the UIImagePNGRepresentation and UIImageJPEGRepresentation functions, forcing the image in a format representation before extracting the data. To be honest, I'm not completely sure what these functions do (other than transforming to NSData), but they do something different from the other because they give a different result compared to each other and compared to the exported data (which is .jpg).

有些事情让我不清楚,Swift/Apple在导出时对我的(图片)数据做了什么.我在多个地方读过Apple转换(或删除)EXIF的信息,但对我来说不清楚是哪个部分.我试图通过在应用程序中都进行哈希处理之前自己显式删除EXIF数据来预料到这一点(通过函数 ImageHelper.removeExifData (找到

There are some things unclear to me what Swift/Apple is doing to my (picture)data upon exporting. I read in several places that Apple transforms (or deletes) the EXIF but to me it is unclear what part. I tried to anticipate this by explicitly removing the EXIF data myself before hashing in both the app (via function ImageHelper.removeExifData (found here) and via exiftools on the command line), but to no avail.

我尝试对手机上的现有照片进行哈希处理.我有一张通过邮件发送给我的照片,但是在我的应用程序和命令行中对此进行哈希处理得出了不同的结果. string 在应用程序和命令行中给出了相似的结果,因此哈希函数不是问题.

I tried hashing an existing photo on my phone. I had a photo send to me by mail but hashing this in my app and on the command line gave different results. A string gave similar results in the app and on command line so the hash function(s) are not the problem.

所以我的问题是:

  1. 导出照片时是否有防止变换的方法
  2. 是否有 UIImagePNGRepresentation/UIImageJPEGRepresentation 函数的替代方法
  1. Is there a way to prevent transformation when exporting a photo
  2. Are there alternatives to UIImagePNGRepresentation / UIImageJPEGRepresentation functions

(3.这完全有可能还是iOS/Apple太像黑匣子了?)

(3. Is this at all possible or is iOS/Apple too much of a black box?)

非常感谢任何帮助或指向更多文档的指针!

这是我的代码

//
//  ViewController.swift
//  camera test

import UIKit
import ImageIO

// extension on NSData format, to enable conversion to String type
extension NSData {    
    func toHexString() -> String {
        var hexString: String = ""
        let dataBytes =  UnsafePointer<CUnsignedChar>(self.bytes)
        for (var i: Int=0; i<self.length; ++i) {
            hexString +=  String(format: "%02X", dataBytes[i])
            }
        return hexString
    }
}

// function to remove EXIF data from image
class ImageHelper {
    static func removeExifData(data: NSData) -> NSData? {
        guard let source = CGImageSourceCreateWithData(data, nil) else {
            return nil
        }
        guard let type = CGImageSourceGetType(source) else {
            return nil
        }
        let count = CGImageSourceGetCount(source)
        let mutableData = NSMutableData(data: data)
        guard let destination = CGImageDestinationCreateWithData(mutableData, type, count, nil) else {
            return nil
        }
        // Check the keys for what you need to remove
        // As per documentation, if you need a key removed, assign it kCFNull
        let removeExifProperties: CFDictionary = [String(kCGImagePropertyExifDictionary) : kCFNull, String(CGImagePropertyOrientation): kCFNull]

        for i in 0..<count {
            CGImageDestinationAddImageFromSource(destination, source, i, removeExifProperties)
        }

        guard CGImageDestinationFinalize(destination) else {
            return nil
        }

        return mutableData;
    }
}



class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, MFMailComposeViewControllerDelegate {

    @IBOutlet weak var imageView: UIImageView!

    // creats var for picture
    var imagePicker: UIImagePickerController!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // calls Camera function and outputs picture to imagePicker
    @IBAction func cameraAction(sender: UIButton) {
        imagePicker = UIImagePickerController()
        imagePicker.delegate = self
        imagePicker.sourceType = .Camera

        presentViewController(imagePicker, animated: true, completion: nil)
    }

    // calls camera app, based on cameraAction
    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
        imagePicker.dismissViewControllerAnimated(true, completion: nil)
        imageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
    }

    // calls photoHash function based on button hashAction
    @IBAction func hashAction(sender: AnyObject) {
        photoHash()
    }


    // converts latest picture to binary to sha256 hash and outputs to console 
    func photoHash(){
        let img = ImageHelper.removeExifData(UIImagePNGRepresentation(imageView.image!)!)
        let img2 = ImageHelper.removeExifData(UIImageJPEGRepresentation(imageView.image!, 1.0)!)
        let imgHash = sha256_bin(img!)
        let imgHash2 = sha256_bin(img2!)
        print(imgHash)
        print(imgHash2)

        // write image to photo library
        UIImageWriteToSavedPhotosAlbum(imageView.image!, nil, nil, nil)
    }

    // Digests binary data from picture into sha256 hash, output: hex string
    func sha256_bin(data : NSData) -> String {
        var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
        CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
        let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))

        let resString = res.toHexString() 
        return resString
    }



}

规格:

MacBook Pro retina 2013,OS X 10.11.5
xcode版本7.3.1
迅捷2
iPhone 5S
通过 shasum -a 256 filename.jpg

MacBook Pro retina 2013, OS X 10.11.5
xcode version 7.3.1
swift 2
iphone 5S
hash on command line via shasum -a 256 filename.jpg

推荐答案

自从上周发布问题以来,我了解到Apple从元数据中分离出了图像数据(图像数据存储在 UIIMage对象中),因此对UIImage对象进行哈希处理将从不产生的哈希值与在命令行(或python或任何地方)中消化的哈希值相同.这是因为对于python/perl/etc,存在元数据(即使使用 Exiftool 这样的工具,exif数据也已标准化,但仍然存在,而在应用程序环境中,exif数据很简单不存在,我想这与低级语言还是高级语言有关,但不确定).

Since posting my question last week I learned that Apple seperates the image data from the meta data (image data is stored in UIIMage object), so hashing the UIImage object will never result in a hash that is the same as a hash digested on the command line (or in python or where ever). This is because for python/perl/etc, the meta data is present (even with a tool as Exiftool, the exif data is standardized but still there, whereas in the app environment, the exif data is simply not there, I guess this has something to do with low level vs high level languages but not sure).

尽管有一些方法可以访问UIImage的EXIF数据(或一般的元数据),但这并不容易.这是保护用户隐私(除其他事项外)的功能.

Although there are some ways to access the EXIF data (or meta data in general) of a UIImage, it is not easy. This is a feature to protect the privacy (among other things) of the user.

我已经通过另一种途径找到了解决我们特定问题的解决方案:事实证明,iOS确实将所有图像数据和元数据保存在磁盘上的一张照片中.通过使用 Photos API ,我可以通过此调用访问这些文件(我在SO的答案中找到了此文件,但我只是不记得我是如何结束的.如果您识别出此代码段,,请让我知道):

I have found a solution to our specific problem via a different route: turns out that iOS does save all the image data and meta data in one place on disk for a photo. By using the Photos API, I can get access to these with this call (I found this in an answer on SO, but I just don't remember how I ended up there. If you recognise this snippet, please let me know):

func getLastPhoto() {
    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]

    let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)

    if let lastAsset: PHAsset = fetchResult.lastObject as? PHAsset {
        let manager = PHImageManager.defaultManager()
        let imageRequestOptions = PHImageRequestOptions()

        manager.requestImageDataForAsset(lastAsset, options: imageRequestOptions) {
            (let imageData: NSData?, let dataUTI: String?,
            let orientation: UIImageOrientation,
            let info: [NSObject : AnyObject]?) -> Void in

                // Doing stuff to the NSDAta in imageData

        }
    }

按相反的日期排序,第一个条目(显然)是最新的照片.只要不将其加载到 imageView 中,我就可以处理所需的数据(在这种情况下,将其发送到哈希函数中).

By sorting on date in reverse order the first entry is (obviously) the most recent photo. And as long as I don't load it into an imageView, I can do with the data what I want (sending it to a hash function in this case).

因此流程如下:用户拍照,照片被保存到图库中并导入到 imageView .然后,用户按下哈希按钮,在该按钮上会从磁盘上获取包含元数据和全部内容的最新添加的照片(imageView中的一张照片).然后,我可以通过空投从库中导出照片(目前,在以后的体育馆中使用https请求),并在笔记本电脑上复制哈希值.

So the flow is as follows: user takes photo, photo is saved to the library and imported to the imageView. The user then presses the hash button upon which the most recently added photo (the one in the imageView) is fetched from disk with meta data and all. I can then export the photo from the library by airdrop (for now, https request in later stadium) and reproduce the hash on my laptop.

这篇关于Swift中来自图像的一致二进制数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆