代码之家  ›  专栏  ›  技术社区  ›  Nicolas Miari

照片扩展:无法保存未定向“向上”的图像

  •  2
  • Nicolas Miari  · 技术社区  · 6 年前

    热释光;DR:iOS照片编辑扩展无法保存对照片的更改,除非这些更改是在中使用设备拍摄的 左侧横向 方向。


    我正在尝试在iOS上开发一个照片编辑扩展。

    我的代码基于Xcode模板, Apple's sample code ,以及一些在线教程。

    我注意到有些照片在应用更改后无法保存;我收到一封信 警报视图 上面写着:

    无法保存更改

    保存时出错。请稍后再试。

    在web上搜索会让我想到以下两个关于堆栈溢出的问题:

    1. iOS photo extension finishContentEditingWithCompletionHandler: Unable to Save Changes (已应用修复,在我的情况下不起作用)
    2. IOS) Photo Extension Unable To Save Changes Issue (这个问题没有有用的答案)

    我尝试了几个编辑扩展,只是为了确保我的设备没有问题,发现问题出现在:

    1. 我的应用程序,
    2. 苹果自己的示例代码 ,
    3. 一些 文学地 ),而不是其他(例如。, 比特摄像机 -我希望我可以联系该应用程序的开发人员,要求一些提示。

    我注意到,对于库中给定的照片资源,问题要么总是发生,要么永远不会发生。也就是说,这似乎取决于正在编辑的照片的某些属性(因此,在这种情况下,整个“稍后再试”业务毫无意义)。

    finishContentEditing(completionHandler:) 方法(调用以将修改后的图像保存到框架指定的URL),并检查 PHContentEditingInput 在编辑会话开始时传递的对象。

    肖像画 纵向倒置 ,或 景观权 方向,只有这样。拍摄的照片 (右侧的Home按钮)可以毫无问题地保存。

    苹果的示例代码是:

    1. 创建 CIIMage fullSizeImageURL 财产 PHContentEditingInput输入 实例。
    2. 创建 定向拷贝 通过调用 applyingOrientation() 在上面传递 fullSizeImageOrientation 输入的属性。
    3. 对点2的全尺寸定向图像应用适当的CoreImage过滤器。
    4. CIContext .
    5. 使用上下文调用 writeJPEGRepresentation(of:to:colorSpace:) 通过在#3中获得的修改后的CIImage renderedContentURL PHContentEditingOutput 以及原作的色彩空间 CIImage .

    实际代码:

    DispatchQueue.global(qos: .userInitiated).async {
        // Load full-size image to process from input.
        guard let url = input.fullSizeImageURL
            else { fatalError("missing input image url") }
        guard let inputImage = CIImage(contentsOf: url)
            else { fatalError("can't load input image to apply edit") }
    
        // Define output image with Core Image edits.
        let orientedImage = inputImage//.applyingOrientation(input.fullSizeImageOrientation)
        let outputImage: CIImage
        switch selectedFilterName {
            case .some(wwdcFilter):
                outputImage = orientedImage.applyingWWDCDemoEffect()
            case .some(let filterName):
                outputImage = orientedImage.applyingFilter(filterName, parameters: [:])
            default:
                outputImage = orientedImage
        }
    
        // Usually you want to create a CIContext early and reuse it, but
        // this extension uses one (explicitly) only on exit.
        let context = CIContext()
        // Render the filtered image to the expected output URL.
        if #available(OSXApplicationExtension 10.12, iOSApplicationExtension 10.0, *) {
            // Use Core Image convenience method to write JPEG where supported.
            do {
                try context.writeJPEGRepresentation(of: outputImage, to: output.renderedContentURL, colorSpace: inputImage.colorSpace!)
                completionHandler(output)
            } catch let error {
                NSLog("can't write image: \(error)")
                completionHandler(nil)
            }
        } else {
            // Use CGImageDestination to write JPEG in older OS.
            guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent)
                else { fatalError("can't create CGImage") }
            guard let destination = CGImageDestinationCreateWithURL(output.renderedContentURL as CFURL, kUTTypeJPEG, 1, nil)
                else { fatalError("can't create CGImageDestination") }
            CGImageDestinationAddImage(destination, cgImage, nil)
            let success = CGImageDestinationFinalize(destination)
            if success {
                completionHandler(output)
            } else {
                completionHandler(nil)
            }
        }
    }
    

    (稍作修改后发到这里。上面代码的污点存在于 分离 方法,从调度队列块中调用)


    景观权 方向:

    enter image description here

    …选择苹果的示例代码图片编辑扩展:

    enter image description here

    …应用“乌贼墨”过滤器并轻敲“完成”:

    enter image description here

    …我得到了可怕的警告:

    enter image description here

    enter image description here

    (例如,在 景观权 旋转180度,在 肖像画 旋转90度等)

    单击“完成”或“取消”,然后单击“放弃更改”完成会话,图像将恢复到正确的方向:

    enter image description here

    显然,有一些陷阱,无论是我,还是开发人员 文学地 ,苹果2016年的示例代码也不知道(但是 比特摄像机 是)。

    发生什么事?


    解决方法?

    如果我用iPhone拍照 肖像画 并尝试在调试器上编辑它 全尺寸图像方向 .right 编辑失败了。

    但是如果我使用默认工具将图像旋转180度:

    enter image description here

    …保存、再次编辑和旋转 再转180度 (或者,90+270度,但始终处于 ),将其返回到 ,和 然后 尝试使用扩展名编辑,现在是 全尺寸图像方向 .up 保存成功 . 我相信这是因为这个工具可以旋转 像素数据 而不是仅仅修改方向 元数据 (事实上,它可以在 任意角度 ,不只是90度的倍数,我想它会泄露……)

    当然,这需要不方便的用户交互,因此它不是一个真正的解决方法(不过,编程等效的方法是)。


    我使用的是xcode10.0,上面的内容在运行ios12gm的iphone8和运行ios11.4.1的iphone5s上都得到了证实。

    1 回复  |  直到 6 年前
        1
  •  3
  •   matt    6 年前

    我当然看到了保存失败,因为定向的东西是错误的,但是下面的架构目前似乎适合我:

    func startContentEditing(with contentEditingInput: PHContentEditingInput, placeholderImage: UIImage) {
        self.input = contentEditingInput
        if let im = self.input?.displaySizeImage {
            self.displayImage = CIImage(image:im, options: [.applyOrientationProperty:true])!
            // ... other stuff depending on what the adjustment data was ...
        }
        self.mtkview.setNeedsDisplay()
    }
    func finishContentEditing(completionHandler: @escaping ((PHContentEditingOutput?) -> Void)) {
        DispatchQueue.global(qos:.default).async {
            let inurl = self.input!.fullSizeImageURL!
            let output = PHContentEditingOutput(contentEditingInput:self.input!)
            let outurl = output.renderedContentURL
            var ci = CIImage(contentsOf: inurl, options: [.applyOrientationProperty:true])!
            let space = ci.colorSpace!
            // ... apply real filter to `ci` based on user edits ...
            try! CIContext().writeJPEGRepresentation(
                of: ci, to: outurl, colorSpace: space)
            let data = // whatever
            output.adjustmentData = PHAdjustmentData(
                formatIdentifier: self.myidentifier, formatVersion: "1.0", data: data)
            completionHandler(output)
        }
    }