[iOS 10] Live Photos のコンテンツ編集について

2016.10.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは。モバイルアプリサービス部の平屋です。

iOS 10 で「Live Photos」を編集することができるようになりました。本記事では、Photos framework のクラスを使用して Live Photos を編集する手順を紹介していきます。

今回解説するコードは以下のリポジトリで公開してますのであわせてご覧ください。

Live Photos とは

Live Photos は iPhone 6s / 6s Plus 以降の機種で、標準の「カメラ」アプリで撮影できます。

撮影するときの操作は静止画を撮影する場合と同様ですが、静止画だけでなく「写真を撮影したタイミングの前後1.5秒の映像・音声」も記録されます。

ios-10-editing-live-photos-00

実装

さっそく、Live Photos を編集する手順を紹介していきます。

Live Photos を編集するために、Photos framework の機能を使用します。Photos framework の基本事項については以下のカテゴリの記事をご覧ください。

また、Live Photos を編集する手順は、静止画を編集する場合とほとんど同じです。静止画を編集する手順は以下の記事で解説しています。

func applyFilter() {
    guard let asset = self.asset else { return }

    let formatIdentifier = Bundle.main.bundleIdentifier!
    let formatVersion = "1.0"
    let filterName = "CISepiaTone"

    // [1] PHContentEditingInputRequestOptions を作成する
    let options = PHContentEditingInputRequestOptions()
    options.canHandleAdjustmentData = { adjustmentData in
        return adjustmentData.formatIdentifier == formatIdentifier && adjustmentData.formatVersion == formatVersion
    }

    // [2] コンテンツの編集がサポートされているかを問い合わせる
    if !asset.canPerform(.content) { return }

    // [3] コンテンツ編集セッションを開始するために必要な情報をリクエストする
    asset.requestContentEditingInput(with: options, completionHandler: { input, info in
        guard let input = input else { fatalError("can't get content editing input: \(info)") }

        // [4] PHAdjustmentData を作成する
        let adjustmentData = PHAdjustmentData(formatIdentifier: formatIdentifier,
                                              formatVersion: formatVersion,
                                              data: filterName.data(using: .utf8)!)

        // [5] PHContentEditingOutput を作成する
        let output = PHContentEditingOutput(contentEditingInput: input)
        output.adjustmentData = adjustmentData

        // [6] PHLivePhotoEditingContext を作成する
        guard let livePhotoContext = PHLivePhotoEditingContext(livePhotoEditingInput: input) else { fatalError("can't get live photo to edit") }

        // [7] PHLivePhotoEditingContext に編集処理を行うブロックをセットする
        livePhotoContext.frameProcessor = { frame, _ in
            return frame.image.applyingFilter(filterName, withInputParameters: nil)
        }

        // [8] 編集処理を実行し、レンダリング結果を PHContentEditingOutput に渡す
        livePhotoContext.saveLivePhoto(to: output) { success, error in
            if success {
                // [9] 編集結果をフォトライブラリにコミットする
                PHPhotoLibrary.shared().performChanges({
                    let request = PHAssetChangeRequest(for: asset)
                    request.contentEditingOutput = output
                    }, completionHandler: { success, error in
                        if !success {
                            print(Date(), #function, #line, "cannot edit asset: \(error)")
                        }
                })
            } else {
                print(Date(), #function, #line, "cannot output live photo")
            }
        }
    })
}

[1] PHContentEditingInputRequestOptions を作成する

手順 [3] で使用する PHContentEditingInputRequestOptions をここで作成します。

canHandleAdjustmentData プロパティにセットするブロック内で true を返した場合、編集用のオリジナルデータが提供されます。false を返した場合は、以前行なった編集がレンダリングされたデータが提供されます。

ブロック内では PHAdjustmentData が保持する formatIdentifierformatVersion をチェックした結果を返しています。PHAdjustmentData は Asset に対する編集履歴を保持するクラスです。

[2] コンテンツの編集がサポートされているかを問い合わせる

PHAssetcanPerform(_:) メソッドを使用して編集処理がサポートされているかをチェックします。

[3] コンテンツ編集セッションを開始するために必要な情報をリクエストする

PHAssetrequestContentEditingInput(with:completionHandler:) メソッドを使用して PHAsset の編集セッションを開始するために必要な PHContentEditingInput をリクエストします。

1 つ目の引数には、手順 [1] で作成した PHContentEditingInputRequestOptions を渡します。completionHandler 引数に渡すブロックは、PHContentEditingInput が用意出来たタイミングで呼ばれます。

[4] PHAdjustmentData を作成する

formatIdentifier, formatVersion, data から、PHAdjustmentData を作成します。data にはフィルタ名を指定しています。

[5] PHContentEditingOutput を作成する

PHAsset のコンテンツの編集結果を表すオブジェクトである PHContentEditingOutput を作成し、手順 [4] で作成した PHAdjustmentData をセットします。

[6] PHLivePhotoEditingContext を作成する

PHContentEditingInput から PHLivePhotoEditingContext を作成します。PHLivePhotoEditingContext は Live Photos のコンテンツ編集セッションを管理するクラスです。

[7] PHLivePhotoEditingContext に編集処理を行うブロックをセットする

PHLivePhotoEditingContextframeProcessor プロパティに編集処理を行うブロックをセットします。

ブロック内の処理は Live Photos の各フレームに対して実行されます。今回は CIImageapplyingFilter(_:withInputParameters:) メソッドを使用して画像を加工しました。

上記実装例では CISepiaTone という Core Image API のフィルタを使用しています。Core Image API が提供するフィルタは以下のページに載っています。

[8] 編集処理を実行し、レンダリング結果を PHContentEditingOutput に渡す

PHLivePhotoEditingContextsaveLivePhoto(to:options:completionHandler:) メソッドを呼びます。手順 [7] でセットした編集処理が実行され、手順[5] で作成したPHContentEditingOutput にレンダリング結果が渡されます。

[9] 編集結果をフォトライブラリにコミットする

最後に、PHPhotoLibraryperformChanges(_:completionHandler:) メソッドを使用して、編集結果をフォトライブラリにコミットします。

1 つ目のブロック内に編集処理を記述します。PHAssetChangeRequest を作成し、contentEditingOutput プロパティに手順[5] で作成したPHContentEditingOutput をセットします。

2 つめのブロックに処理完了時の処理を記述します。

編集結果

3 種類のフィルタを使用して、Live Photos の編集を試してみました。

オリジナル

CIPhotoEffectInstant

CISepiaTone

CIPhotoEffectNoir

さいごに

Photos framework のクラスを使用して Live Photos を編集する手順を紹介しました。編集を完了させるまでの手順は多いですが、比較的簡単にコンテンツを編集することができます。

今回解説したコードは以下のリポジトリで公開してますので参考にしてみてください。

参考資料

WWDC documents

API Reference