View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

その他のビデオ

ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。

  • 概要
  • トランスクリプト
  • コード
  • Core Image、Metal、SwiftUIでのEDRコンテンツの表示

    Core ImageベースのマルチプラットフォームSwiftUI Appから、レンダリングのサポートにExtended Dynamic Range(EDR)を追加する方法をご覧ください。ここでは、ViewRepresentableを使用して、CIImagesをMTKViewに表示するベストプラクティスを紹介します。また、EDRレンダリングを有効化する簡単な手順や、EDRをサポートする内蔵CIFilter(150個以上)の例も紹介します。

    リソース

    • Core Image
    • Generating an animation with a Core Image Render Destination

    関連ビデオ

    Tech Talks

    • リファレンスモードについて

    WWDC22

    • 機械学習を組み込んだAppの開発手法
    • AVFoundationやMetalによるHDR動画のEDR表示
    • iOSのEDRの詳細
  • このビデオを検索

    ようこそDavid Haywardです Core Imageチームの ソフトウェアエンジニアです 拡張ダイナミックレンジコンテンツを Core Imageで表示する方法をご説明します 4つのパートに分けてお話ししていきます まず 当社プラットフォームにおける EDRの重要な用語についてご紹介します 次は 新しいCore Imageの サンプルプロジェクトでの EDRのサポートの追加方法を説明します 最後にCIFiltersを使用してEDRコンテンツを 生成する画像を作成する方法を紹介します

    では 重要な用語についてご説明します SDRとは RGBの黒を0白を1として 正規化して表現する従来の方法です これに対し EDRは通常の範囲を超えて RGBの色を表現するために 推奨されている方法です

    SDRと同様 0が黒1がSDRの白と 同じ明るさを表します しかし EDRでは1より大きな値を使用して さらに明るい画素を 表現することができます

    ただし1より大きな値も許容されますが ヘッドルームを超える値は クリップされることに注意してください

    ヘッドルームは ディスプレイの最大Nitsを SDRホワイトのNitsで割ったものです

    ヘッドルームの値はディスプレイの種類や 明るさの変化 周囲の状況で 変わることがあります

    「iOSのEDRについて詳しく見る」で より深くご理解いただけると思います EDRコンテンツにはいくつかのソースがあります TIFFやOpenEXRなどのファイルフォーマットは EDR用の浮動小数点値を格納することができます

    AVFoundationでHDRビデオ からフレームを取得します

    Metal APIは EDR環境を テキスチャにレンダリングします ProRAWのDNGファイルはEDRを格納できます

    「ProRAW画像の撮影と処理」もご覧ください

    次のパートではSwiftUI Appで Core ImageをMetalと併用する方法を説明します 後でEDR対応を追加する方法についても 紹介します

    SwiftUI のマルチプラットフォームAppで Core Image と Metal Kit View を組み合わせる 新しいサンプルコードを最近リリースしました ぜひダウンロードしてコードをご覧ください この場を借りて 見た目と機能についてご紹介します

    サンプルはMetalビューに表示される アニメーション化されたCIImageを描画します 最適な機能実現のために MTKViewを使用しています Appが必要とするコンテンツのプロキシとして アニメのチェッカーボードCIImageを表示します

    SwiftUIを採用しているため macOS iOS iPadOSの各プラットフォームで 共通のコードベースを使用することができます

    これはいくつかの短いソースファイルから 構築されています クラスがどのように相互作用するか説明します

    このAppには3つの重要なパーツがあります まず 最も重要なのは「MetalView」です MTKViewクラスをラップする SwiftUIと互換性があるView実装を提供します

    MTKViewクラスはmacOSのNSViewと 他のプラットフォームの UIViewがベースとなっているため MetalView実装はViewRepresentableを使って SwiftUIとMTKViewクラスの間をブリッジします

    ただしMTKViewが直接レンダリングをしません 代わりにデリゲートを使って作業を行います

    レンダラクラスがMTKViewのデリゲートです Metalコマンドキューや Core Imageコンテキスト等の グラフィックス状態オブジェクトの 初期化を担当します

    必要なdraw()メソッドも実装しています

    しかし レンダラがどのような画像を描画するか 決定するわけではありません imageProviderブロックを使い 描画するCIImageを取得します

    ContentViewクラスが レンダリングされるCIImageを 提供するコードブロックを実装しています

    MetalViewは デリゲートを 呼び出して描画します Rendererのdraw()メソッドは ContentViewを呼び出して 描画する画像を提供します

    3つのクラスのコードについて もう少し詳しく説明します まず MetalViewクラスです makeView()のコードから MTKViewを作成するためにmakeView()を呼んで デリゲートをレンダラステート オブジェクトに設定します NSView や UIView をラップした SwiftUI のビューを 実装するための標準的なアプローチです

    次にpreferredFramesPerSecondです ビューのレンダリング頻度を指定します このプロパティはビューの描画を 駆動するものを決定するため重要です この仕組みについて説明します

    コードはview.preferredFramesPerSecondを 目的のフレームレートに設定します

    この設定によりMTKViewは ビュー自体が描画イベントの タイミングを駆動するように構成されます

    レンダリングデリゲートが 定期的にdraw()を実行し 現在時刻のCIImageを作成するように要求します

    アニメーションが一時停止するまで 繰り返されます

    また 画像編集Appの場合 ビューを描画するタイミングを操作するために ユーザーがコントロールを 操作するのが最適です

    enableSetNeedsDisplayをtrueに設定すると タイミングを駆動できるように MTKViewが構成されます コントロール移動時は updateView()を呼び出し

    ビューのデリゲートが draw()を1回呼び出します

    各描画はコンテンツプロバイダーに 現在の制御状態のCIImageを 作成するように要求します

    このアプローチはビデオフレームの到着が 描画イベントを駆動する場合にも適しています

    MetalViewクラスについては以上です レンダラデリゲートで最も重要なコードは draw()メソッドです

    周期的なフレームレートで呼びだされます draw()メソッドが呼び出されると ディスプレイの解像度を反映するコンテンツの スケールファクターを決定する必要があります CIImagesはポイントではなく ピクセルで測定するからです ビューが別のディスプレイに移動されると プロパティが変更される可能性があるため draw()メソッドを呼び出すたびに 行うことが重要です

    次にmtlTextureProviderを使って CIRenderDestinationを作成します

    コンテンツプロバイダーを 呼び出して 現在の時間と スケールファクターに使うCIImageを作成します この画像は ビューの可視領域で センタリングされ 不透明な背景の上にブレンド そしてCIImageをビューデスティネーションに レンダリングを開始します

    ContentViewクラスで最も重要なコードは init()メソッドです

    コンテンツビューの本体を 作成するためのものです レンダラクラスと MetalViewクラスの接続が確立されます

    はじめに画像プロバイダーブロックで レンダラオブジェクトを作成します

    そのブロックは 要求された時間とスケールの CIImageを返す役割を果たします

    最後にContentViewの本体を レンダラを使用するMetalViewに設定します

    これでCore Imageを使用して レンダリングできる シンプルなSwiftUI Appができました 次にEDRヘッドルームを使う レンダリングに対応させます

    EDR対応にするのはとても簡単です 1番はEDR用のビューの初期化 2番は毎回のレンダリング前に ヘッドルームを計算 3番はヘッドルームを使用する CIImage を構築します 実際のコードをご覧ください MetalView クラスに1つ追加事項があります ビューを作成するとき必要なレイヤーに ExtendedDynamicRangeContent を通知し そのpixelFormatが .rgba16Floatであり 色空間が拡張されてリニアであると ビューに通知する必要があります

    次に レンダラクラスのdraw()メソッドに いくつかの変更が必要です

    draw()メソッドでは ビューの現在の画面を取得し 画面に現在のEDRヘッドルームを要求する コードを追加する必要があります

    ヘッドルームは画像プロバイダブロックに パラメーターとして渡されます draw()メソッドを呼び出すたびに行います ヘッドルームは周囲の状況や ディスプレイの輝度の変化に応じて 変化する動的な特性があります

    3つ目の変更点はContentViewクラスの プロバイダブロックです

    ここでヘッドルーム引数を ブロック宣言に追加します CIFiltersでヘッドルームを使って EDRディスプレイで見栄えする CIImageを返します このAppにEDRサポートを追加する 3つの簡単なステップをまとめます EDRのビューを初期化 すべてのレンダリングの 前にヘッドルームを決定 ヘッドルームを指定してCIImageを構築します これが後半のトピックです EDRに対応したのでCIFiltersを使って CIImagesを作り EDRコンテンツを表示します

    コアイメージに搭載された 150種類以上のフィルターが EDRに対応しています つまり これらのフィルタは EDRコンテンツを含む画像を生成するか EDRコンテンツを含む画像を 処理することができます CIcolorcontrolsと CIExposureAdjustフィルターを使って EDRカラーの画像の 輝度 色相 彩度 コントラストを変更できます グラデーションフィルターなど使って EDRのカラーパラメーターを指定し 画像を生成できます

    3つの新しいフィルターも EDR画像に対応しています CIAreaLogarithmicHistogramは 任意の範囲の輝度値に対する ヒストグラムを生成することができます

    CIColorCubeフィルターは EDR入力画像対応するため 今年更新されたフィルターの1つです

    Core Imageの作業色空間は 固定しておらず 線形なので 0~1の範囲外のRGB値も許容されるため 内蔵フィルターがすべて機能します フィルターがEDR対応しているか確認できます

    フィルタのインスタンスを 作成し フィルターの属性に そのカテゴリーを尋ねてから配列に kCICategoryHighDynamic Rangeがあるか確認します その他 追加した新機能はCIFilter変数の XcodeQuickLookデバッグサポートです 各入力パラメーターのカテゴリーと要件を含む 各Filterクラスのドキュメントを表示します

    これらのEDRフィルターがあれば コンテンツに適用できる効果は 無限に広がります 今日説明する例では サンプルAppの市松模様に 明るい鏡面反射を伴う 波紋効果を追加してみます

    この効果を作成するには rippleTransitionフィルターの インスタンスが必要です

    入力画像とターゲット画像を チェッカー画像に設定します

    次に 波紋の中心時間と遷移時間を制御する フィルター入力を設定します

    そして波紋に鏡面反射ハイライトを 発生させるグラデーションを shadingImageに設定します

    最後に設定したすべての フィルター入力を指定して フィルターにoutputImageを要求します

    また 波紋効果の鏡面反射ハイライトを 作成するために使用する shadingImageの作成方法について説明します ビットマップデータから作成もできますが CIImageを生成してパフォーマンスを向上します

    linearGradientフィルタの インスタンスを作成します 2つのポイントと2つのCIColorを指定して

    グラデーションを作成します 鏡面反射光を白にし 現在のヘッドルームに基づいた明るさで 妥当な最大値に制限する必要があります

    使用するリミットは適用するエフェクトの 外観によって異なります

    color0は固定されていない線形色空間で 白レベルを使用して作成します

    color1はクリアカラーに設定されています

    Point0とpoint1は鏡面反射光を 左上方向から表示するように 座標に設定されています

    フィルターのoutputImageが波紋フィルターに 必要なサイズにトリミングされます

    結果として生じる鏡面反射効果のある波紋は 単純なプロキシに過ぎません これは重要な原則を示しています 通常 明るい画素は適度に使用します 少ないほうがいいのです 明るい画素の方がインパクトがあります

    2つの内蔵CIFiltersを使うAppが完成しました 他の内蔵EDRフィルターも試してみてください 次に CIColorCubeフィルタの最適な使用方法と 独自のフィルタを作成する際の 注意点について説明します

    IColorCubeWithColorSpaceは 大変人気あるフィルタです SDR画像に外観を適用するために使用されます Process Instant Tonalなどの効果を 写真Appに実装するためにも使用されます

    このような外観に使うキューブデータには 0から1の範囲のRGBカラーしか 入出力できないという 重大な制約がありました

    制限を回避する一つの方法としては CIColorCubeWithColorSpaceフィルターで EDR色空間を使用するように指示をします

    最適な結果をもたらすように 色空間範囲に有効な新しいキューブデータを 作成する必要があります キューブのサイズを増やす 必要があるかもしれません 代わりにSDRキューブデータを使えます SDRキューブデータ推定と フィルターに指示できます 機能を有効にするには SDRキューブデータを設定します フィルターの新しいextrapolate プロパティを設定します

    「true」に設定するとフィルターに EDR入力画像を与えられて EDR出力画像を得られます

    最後のトピックは 独自のカスタムCIKernelを 作成する場合の最適な方法についてです

    まずカーネルのコードを見直して clamp min maxなどの関数で RGBの値を0から1の範囲に制限するような 計算が行われていないかを確認します

    多くの場合 制限は安全に取り除くことができ カーネルは正しく機能します

    RGB値が0~1の範囲を 超える可能性がある場合でも アルファ値は0~1の間でなければなりません そうしないと 画像をブレンド または表示するときに 未定義の動作が発生します

    正しい動作はRGB値に5を乗じるだけなのに カーネルが誤ってアルファチャンネルに 5を乗じてしまっています

    以上でプレゼンテーションを終わります 今日はCore Image SwiftUI Appに EDRヘッドルームのサポートを追加する方法と EDRコンテンツを作成変更するために 様々な内蔵CIFiltersを 使用する方法を学びました ご視聴ありがとうございました

    • 5:17 - Metal View

      // Metal View
      
      struct MetalView: ViewRepresentable {
          
          @StateObject var renderer: Renderer
          
          func makeView(context: Context) -> MTKView {
              let view = MTKView(frame: .zero, device: renderer.device)
             
              view.delegate = renderer
      
              // Suggest to Core Animation, through MetalKit, how often to redraw the view.
              view.preferredFramesPerSecond = 30
             
              // Allow Core Image to render to the view using Metal's compute pipeline.
              view.framebufferOnly = false
              
             return view
          }
    • 7:12 - Renderer

      // Renderer
      
      func draw(in view: MTKView) {
        if let commandBuffer = commandQueue.makeCommandBuffer(),
           let drawable = view.currentDrawable {
            // Calculate content scale factor so CI can render at Retina resolution.
        #if os(macOS)
            var contentScale = view.convertToBacking(CGSize(width: 1.0, height: 1.0)).width
        #else
            var contentScale = view.contentScaleFactor
        #endif
      
            let destination = CIRenderDestination(width: Int(view.drawableSize.width),
                                height: Int(view.drawableSize.height), 
                                pixelFormat: view.colorPixelFormat,
                                commandBuffer: commandBuffer,
                                mtlTextureProvider: { () -> MTLTexture in
                                         return drawable.texture
                                })
             
            let time = CFTimeInterval(CFAbsoluteTimeGetCurrent() - self.startTime)
      
            // Create a displayable image for the current time.
            var image = self.imageProvider(time, contentScaleFactor)
      
            image = image.transformed(by: CGAffineTransform(translationX: shiftX, y: shiftY))
            image = image.composited(over: self.opaqueBackground)
                      
            _ = try? self.cicontext.startTask(toRender: image, from: backBounds,
                                                   to: destination, at: CGPoint.zero)
    • 8:09 - ContentView

      // ContentView
      
      import CoreImage.CIFilterBuiltins
      
      init(struct ContentView: View {
          var body: some View {
             // Create a Metal view with its own renderer.
             let renderer = Renderer(
                  imageProvider: { (time: CFTimeInterval, scaleFactor: CGFloat) -> CIImage in
                  
                  var image: CIImage
      
                  // create image using CIFilter.checkerboardGenerator...
      
                  return image
              })
              MetalView(renderer: renderer)
          }
      }
    • 9:17 - MetalView changes

      if let caMtlLayer = view.layer as? CAMetalLayer  {
          caMtlLayer.wantsExtendedDynamicRangeContent = true
          view.colorPixelFormat = MTLPixelFormat.rgba16Float
          view.colorspace = CGColorSpace(name: CGColorSpace.extendedLinearDisplayP3)
      }
    • 9:35 - Get headroom

      let screen = view.window?.screen;
      #if os(macOS)
           let headroom = screen?.maximumExtendedDynamicRangeColorComponentValue ?? 1.0
      #else
           let headroom = screen?.currentEDRHeadroom ?? 1.0
      #endif
           var image = self.imageProvider(time, contentScaleFactor, headroom)
    • 10:05 - Use headroom

      imageProvider: { (time: CFTimeInterval, scaleFactor: CGFloat,
                                    headroom: CGFloat) -> CIImage in
                 var image: CIImage
      
                 // Use CIFilters to create image for time / scale / headroom / ...
                 return image
              })
    • 12:42 - Ripple effect

      let ripple = CIFilter.rippleTransition()
      ripple.inputImage = image
      ripple.targetImage = image
      ripple.center = CGPoint(x: 512.0, y: 384.0)
      ripple.time = Float(fmod(time*0.25, 1.0))
      ripple.shadingImage = shading
      image = ripple.outputImage
    • 13:34 - Generating the shading image

      let gradient = CIFilter.linearGradient()
      let w = min( headroom, 8.0 )
      gradient.color0 = CIColor(red: w, green: w, blue: w, 
                                colorSpace: CGColorSpace(name: CGColorSpace.extendedLinearSRGB)!)!
      gradient.color1 = CIColor.clear
      gradient.point0 = CGPoint(x: sin(angle)*90.0 + 100.0, y: cos(angle)*90.0 + 100.0)
      gradient.point1 = CGPoint(x: sin(angle)*85.0 + 100.0, y: cos(angle)*85.0 + 100.0)
      let shading = gradient.outputImage?.cropped(to: CGRect(x: 0, y: 0, width: 200, height: 200))
    • 16:13 - CIColorCube and EDR

      let f = CIFilter.colorCubeWithColorSpace()
      f.cubeDimension = 32
      f.cubeData = sdrData
      f.extrapolate = true
      f.inputImage = edrImage
      let edrResult = f.outputImage

Developer Footer

  • ビデオ
  • WWDC22
  • Core Image、Metal、SwiftUIでのEDRコンテンツの表示
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン