打开MPO文件并提取嵌入的图像

时间:2017-03-13 18:03:44

标签: swift nsdata decoding mpo

用这个项目搔痒 - 或者更确切地说是两个。一个是尝试使用Xcode编写macOS应用程序,另一个是使用3D照片(如果你完全正确则使用StereoPhotos)。

我在LiveCode中试过这个,但它很慢而且很麻烦。我得到了我喜欢的东西,但你很快就达到了性能墙。所以我决定重新启动Xcode。我想做的一件事是从富士3D相机导入MPO文件,提取左右图像,并在我的应用程序中显示它们。在LiveCode中,这很简单(可能太简单了) - 您将MPO文件作为文本字符串导入,使用JPEG标记将其拆分为数组,并将该数据设置为图像对象。结果是两个JPEG文件。

我一直在Swift尝试这个,但是我的失败很严重。到目前为止,在将Playground添加到我的应用程序之前尝试了它。我一直在采用与LiveCode相同的逻辑 - 加载数据,用标记分割文件,保存数据。

import Cocoa
import Foundation

// Files and destinations
let MPOString = "/path/to/DSCF0523.MPO"
let myLeft = URL(string:"file:///path/to/left.jpg")
let myRight = URL(string:"file:///path/to/right.jpg")

var leftImage:NSImage
var rightImage:NSImage

//JPEG Marker FFD8FFE1
let myJpegMarker = "\u{FF}\u{D8}\u{FF}\u{E1}"

do {
    myMPOData = try String(contentsOfFile: MPOString , encoding:String.Encoding.isoLatin1) as NSString
}
catch {
    print("Error getting MPO - \(myMPOData)\n\(MPOString)")
}

/*
At this point, I realise that while the data has been split,
the markers are removed, so I add them back.
This is wrong (but worked in LiveCode)
*/
let imageArray = myMPOData.components(separatedBy: myJpegMarker)

var myLeftImage = myJpegMarker+imageArray[1]
var myRightImage = myJpegMarker+imageArray[2]

do {
    try myLeftImage.write(to: myLeft! , atomically: false, encoding: String.Encoding.utf8)
}
catch {
    print("Error writing file: " + error.localizedDescription)
}

print ("Saving image \(myRight)")
do {
    try myRightImage.write(to: myRight! , atomically: false, encoding: String.Encoding.utf8)
}
catch {
    print("Error writing file: " + error.localizedDescription)
}

上面的代码执行关闭但不够接近的。我的意思是它将数据分开,并转储出两个文件。但是文件不对。十六进制转储显示JPEG标记不正确(我在文件的开头寻找FFD8FFE1,但那不存在 - 有8个额外的字符,我期望它是编码的结果)。此外,文件大小太多 - 6.5Mb而不是预期的4,这告诉我有些东西不太正确。

我花了相当多的时间试图找到解决方案,但我发现自己围绕着相同的谷歌搜索结果。

有些东西告诉我,我应该能够将图像加载到对象中,并通过引用对象[0]和对象[1]来获取它们。无论如何,无论解决方案如何,我预计它可能比我预期的更简单。

我已经在DropBox上放置了一个示例MPO文件,以供任何人认为他们可以提供帮助的参考。 https://dl.dropboxusercontent.com/u/613685/DSCF0523.MPO.zip

1 个答案:

答案 0 :(得分:0)

我认为问题在于使用(NS)字符串而不是(NS)数据作为您的方法的基础。

虽然可能不是最好的解决方案(我的想法是在Objective-C模式下,所以我非常非常怀疑以下Swift可能是两者中令人不安的混合),但这应该提取嵌入式图像:

import Foundation

let jpegMarker:Data = Data(bytes: UnsafePointer<UInt8>([0xFF, 0xD8, 0xFF, 0xE1] as [uint8]), count: 4)

let baseURL = "file:///path/to/images/"
let mpoURL = URL(string: baseURL + "DSCF0523.MPO")
var mpoData:Data

do {
    try mpoData = Data.init(contentsOf: mpoURL!)
    var markerLocations:[Int] = []
    var markerOffset:Range? = mpoData.range(of: jpegMarker, options:[], in:0 ..< mpoData.count)

    while (markerOffset != nil) {

        // We've found a marker - add it to our array
        markerLocations.append(markerOffset!.lowerBound)

        // Update our current offset before we iterate
        markerOffset = mpoData.range(of: jpegMarker as Data, options:[], in:markerOffset!.upperBound ..< mpoData.count)
    }


    // Did we find any markers?
    if (markerLocations.count != 0) {

        // Time to extract the data betwen the markers
        for (index, currentMarkerOffset) in markerLocations.enumerated() {

            let endDataOffset = index == markerLocations.count - 1 ? mpoData.count : markerLocations[index + 1]
            let imageBytes:Data = mpoData.subdata(in:currentMarkerOffset ..< endDataOffset)

            // Write the image data to disk
            do {
                let outputPath = baseURL + "Image-" + String(index) + ".jpeg"
                try imageBytes.write(to: URL(string: outputPath)!)
            }
            catch {
                print("Unable to write image file - " + error.localizedDescription)
            }
        }

    } else {
        print("JPEG marker not found in source MPO file.")
    }

} catch {
    print("Unable to open source MPO file - " + error.localizedDescription)
}

我正在做的是在迭代这些标记并逐个提取它们之前,首先在while循环中获取文件中的所有标记(因为它可能有两个以上的嵌入图像)。

在这次迭代中,我们必须处理最终标记的情况,因此使用相当难看的三元条件运算符。

顺便提一下,这使用了Swift 3的数据(而不是2.x风格的NSData)等。