JPEG YCbCr のサポート

Windows 8.1以降、Windows イメージング コンポーネント (WIC) JPEG コーデックでは、ネイティブの YCbCr 形式での画像データの読み取りと書き込みがサポートされています。 WIC YCbCr のサポートを Direct2D と組み合わせて使用して、YCbCr ピクセル データを画像効果でレンダリングできます。 さらに、WIC JPEG コーデックは、Media Foundation を介して特定のカメラ ドライバーによって生成された YCbCr ピクセル データを使用できます。

YCbCr ピクセル データは、標準の BGRA ピクセル形式よりもメモリ消費が大幅に少なくなります。 さらに、YCbCr データにアクセスすると、JPEG デコード/エンコード パイプラインの一部のステージを、GPU アクセラレーションされた Direct2D にオフロードできます。 YCbCr を使用すると、アプリで JPEG メモリの消費量を減らし、同じサイズと品質の画像の読み込み時間を短縮できます。 または、アプリでは、パフォーマンスの低下に悩まされることなく、より高解像度の JPEG 画像を使用できます。

このトピックでは、YCbCr データのしくみと、WIC および Direct2D でデータを使用する方法について説明します。

JPEG YCbCr データについて

このセクションでは、WIC での YCbCr のサポートのしくみとその主な利点を理解するために必要な主要な概念について説明します。

YCbCr カラー モデル

Windows 8以前の WIC では、4 つの異なるカラー モデルがサポートされています。最も一般的なのは RGB/BGR です。 このカラー モデルでは、赤、緑、青のコンポーネントを使用して色データを定義します。第4のアルファ成分も使用することができる。

赤、緑、青の各成分に分解された画像を次に示します。

赤、緑、青の各成分に分解された画像。

YCbCr は、輝度成分 (Y) と 2 つのクロム成分 (C b および C r) を使用して色データを定義する代替カラー モデルです。 これは、一般的にデジタルイメージングやビデオのシナリオで使用されます。 YCbCr という用語は、多くの場合、YUV と同じ意味で使用されますが、2 つは技術的には異なります。

色空間とダイナミック レンジの定義が異なる YCbCr にはいくつかのバリエーションがあります。WIC では、JPEG JFIF YCbCr データが特にサポートされています。 詳細については、 JPEG ITU-T81 仕様を参照してください。

ここでは、Y、Cb、および C r の各コンポーネントに分解された画像を示 します。

y、cb、cr の各コンポーネントに分解されたイメージ。

平面型とインターリーブ型のメモリ レイアウト

このセクションでは、RGB ピクセル データへのアクセスとメモリへの格納と YCbCr データの違いについて説明します。

RGB ピクセル データは、通常、インターリーブ メモリ レイアウトを使用して格納されます。 つまり、単一色コンポーネントのデータはピクセル間でインターリーブされ、各ピクセルは連続してメモリに格納されます。

インターリーブ メモリ レイアウトに格納されている RGBA ピクセル データを示す図を次に示します。

インターリーブ メモリ レイアウトに格納されている rgba ピクセル データを示す図。

YCbCr データは、通常、平面メモリ レイアウトを使用して格納されます。 つまり、各色コンポーネントは、合計 3 つの平面に対して、独自の連続した平面に個別に格納されます。 もう 1 つの一般的な構成では、Cb コンポーネントと Cr コンポーネントはインターリーブされて一緒に格納され、Y コンポーネントはそれ自体の平面に残り、合計で 2 つの平面になります。

ここでは、平面 Yとインターリーブされた CbCr ピクセル データと、一般的な YCbCr メモリ レイアウトを示す図である。

平面 y とインターリーブ cbcr ピクセル データ、一般的な ycbcr メモリ レイアウトを示す図。

WIC と Direct2D の両方で、各カラー プレーンは独自の個別のオブジェクト ( IWICBitmapSource または ID2D1Bitmap) として扱われ、これらのプレーンをまとめて YCbCr イメージのバッキング データを形成します。

WIC では 2 プレーン構成と 3 プレーン構成の両方で YCbCr データへのアクセスがサポートされていますが、Direct2D では前者 (Y と CbCr) のみがサポートされます。 したがって、WIC と Direct2D を一緒に使用する場合は、常に 2 プレーン YCbCr 構成を使用する必要があります。

彩度サブサンプリング

YCbCr カラー モデルは、人間の視覚システムの特定の側面を利用できるため、デジタルイメージングシナリオに適しています。 特に、人間は画像の輝度(明るさ)の変化に対してより敏感であり、クロム(色)に対する感度が低くなります。 色データを別々の輝度成分とクロム成分に分割することで、クロム成分だけを選択的に圧縮し、品質の損失を最小限に抑えながら省スペース化を実現します。

これを行うための 1 つの手法は、彩度サブサンプリングと呼ばれます。 Cb 平面と Cr 平面は、水平寸法と垂直寸法の一方または両方でサブサンプリング (スケールダウン) されます。 歴史的な理由から、各彩度サブサンプリングモードは、一般的に3つの部分J:a:b比を使用して参照されます。

サブサンプリング モード 水平ダウンスケール 垂直ダウンスケール ピクセルあたりのビット数*
4:4:4 1x 1x 24
4:2:2 2x 1x 16
4:4:0 1x 2x 16
4:2:0 2x 2x 12

 

*Yデータを含みます。

上の表から、YCbCr を使用して圧縮されていない画像データを格納する場合、使用される彩度サブサンプリング モードに応じて、ピクセル RGBA データあたり 32 ビットに対して 25% から 62.5% のメモリ節約を実現できます。

JPEG YCbCr の使用法

大まかに言えば、JPEG 圧縮解除パイプラインは次のステージで構成されます。

  1. エントロピ (Huffman など) の展開を実行する
  2. dequantization を実行する
  3. 逆離散コサイン変換を実行する
  4. CbCr データに対して彩度アップサンプリングを実行する
  5. YCbCr データを RGBA に変換する (必要な場合)

JPEG コーデックで YCbCr データを生成することで、デコード プロセスの最後の 2 つの手順を回避するか、GPU に遅延させることができます。 前のセクションで示したメモリ節約に加えて、これにより、イメージのデコードに必要な全体的な時間が大幅に短縮されます。 YCbCr データをエンコードする場合も、同じ節約が適用されます。

JPEG YC b Cr データ使用

このセクションでは、WIC と Direct2D を使用して YCbCr データを操作する方法について説明します。

実際に使用されるこのドキュメントのガイダンスについては、 Direct2D と WIC の JPEG YCbCr 最適化のサンプル を参照してください。このサンプルでは、Direct2D アプリで YCbCr コンテンツをデコードおよびレンダリングするために必要なすべての手順を示します。

YCbCr JPEG 画像の使用

JPEG 画像の大部分は YCbCr として格納されます。 一部の JPEG には CMYK またはグレースケール データが含まれており、YCbCr は使用されません。 つまり、通常は、既存の JPEG コンテンツを変更せずに直接使用できるわけではありません。

WIC と Direct2D では、考えられるすべての YCbCr 構成がサポートされるわけではありません。Direct2D での YCbCr のサポートは、基になるグラフィックス ハードウェアとドライバーに依存します。 このため、汎用イメージング パイプラインは、YCbCr (PNG や BMP などの他の一般的な画像形式を含む) を使用しない画像や、YCbCr のサポートが利用できない場合に堅牢である必要があります。 既存の BGRA ベースのイメージング パイプラインを保持し、使用可能な場合はパフォーマンス最適化として YCbCr を有効にすることをお勧めします。

Windows Imaging コンポーネント API

Windows 8.1の WIC では、JPEG YC b Cr データへのアクセスを提供する 3 つの新しいインターフェイス追加されます。

IWICPlanarBitmapSourceTransform

IWICPlanarBitmapSourceTransformIWICBitmapSourceTransform に似ていますが、YCbCr データを含む平面構成でピクセルが生成される点が除きます。 このインターフェイスを取得するには、平面アクセスをサポートする IWICBitmapSource の実装で QueryInterface を呼び出します。 これには、IWICBitmapFrameDecode、IWICBitmapScalerIWICBitmapFlipRotator、およびIWICColorTransform の JPEG コーデックの実装が含まれます。

IWICPlanarBitmapFrameEncode

IWICPlanarBitmapFrameEncode は、YCbC r データを含む平面ピクセル データをエンコードする機能 提供します。 このインターフェイスを取得するには、JPEG コーデックの IWICBitmapFrameEncode の実装で QueryInterface を呼び出します。

IWICPlanarFormatConverter

IWICPlanarFormatConverter を使用すると、 IWICFormatConverter は YCbCr を含む平面ピクセル データを使用し、インターリーブピクセル形式に変換できます。 インターリーブピクセルデータを平面形式に変換する機能は公開されません。 このインターフェイスを取得するには、Windows で提供される IWICFormatConverter の実装で QueryInterface を呼び出します。

Direct2D API

Windows 8.1 の Direct2D では、新しい YCbCr イメージ効果を使用して YCbCr 平面ピクセル データがサポートされます。 この効果は、YCbC r データをレンダリングする機能 提供します。 この効果は、入力として 2 つの ID2D1Bitmap インターフェイスを受け取ります。1 つはDXGI_FORMAT_R8_UNORM形式の平面 Y データを含み、もう 1 つはDXGI_FORMAT_R8G8_UNORM形式のインターリーブ CbCr データを含みます。 通常、この効果は、BGRA ピクセル データを含む ID2D1Bitmap の代わりに使用します。

YC b Cr イメージ効果は、YCbC r データを提供する WIC YCbCr API と組み合わせて使用することを目的としています。 これは、デコード作業の一部を CPU から GPU にオフロードするために効果的に機能します。この場合、はるかに迅速かつ並列に処理できます。

YCbCr 構成がサポートされているかどうかを判断する

前に説明したように、YCbCr のサポートが利用できない場合に対して、アプリは堅牢である必要があります。 このセクションでは、アプリがチェックする必要がある条件について説明します。 次のいずれかのチェックが失敗した場合、アプリは標準の BGRA ベースのパイプラインにフォールバックする必要があります。

WIC コンポーネントは YCbCr データ アクセスをサポートしていますか?

Windows が提供する JPEG コーデックと特定の WIC 変換のみが YCbC r データ アクセス サポートします。 完全な一覧については、「 Windows Imaging Component API」 セクションを参照してください。

平面 YCbCr インターフェイスのいずれかを取得するには、元のインターフェイスで QueryInterface を呼び出します。 コンポーネントが YCbCr データ アクセスをサポートしていない場合、これは失敗します。

要求された WIC 変換は YCbCr でサポートされていますか?

IWICPlanarBitmapSourceTransform を取得したら、最初に DoesSupportTransform を呼び出す必要があります。 このメソッドは、平面 YCbCr データに適用する変換の完全なセットを入力パラメーターとして受け取り、サポートを示すブール型 (Boolean) と、返すことができる要求されたサイズに最も近いディメンションを返します。 IWICPlanarBitmapSourceTransform::CopyPixels を使用してピクセル データにアクセスする前に、3 つの値をすべてチェックする必要があります。

このパターンは、 IWICBitmapSourceTransform の使用方法に似ています。

グラフィックス ドライバーは、Direct2D で YCbCr を使用するために必要な機能をサポートしていますか?

このチェックは、Direct2D YCbCr 効果を使用して YCbCr コンテンツをレンダリングする場合にのみ必要です。 Direct2D は、DXGI_FORMAT_R8_UNORMとDXGI_FORMAT_R8G8_UNORMピクセル形式を使用して YCbCr データを格納します。これは、すべてのグラフィックス ドライバーから利用できるわけではありません。

YCbCr イメージ効果を使用する前に、 ID2D1DeviceContext::IsDxgiFormatSupported を呼び出して、両方の形式がドライバーでサポートされていることを確認する必要があります。

サンプル コード

推奨されるチェックを示すコード例を次に示します。 この例は、 Direct2D および WIC サンプルの JPEG YCbCr 最適化から取得したものです。

bool DirectXSampleRenderer::DoesWicSupportRequestedYCbCr()
{
    ComPtr<IWICPlanarBitmapSourceTransform> wicPlanarSource;
    HRESULT hr = m_wicScaler.As(&wicPlanarSource);
    if (SUCCEEDED(hr))
    {
        BOOL isTransformSupported;
        uint32 supportedWidth = m_cachedBitmapPixelWidth;
        uint32 supportedHeight = m_cachedBitmapPixelHeight;
        DX::ThrowIfFailed(
            wicPlanarSource->DoesSupportTransform(
                &supportedWidth,
                &supportedHeight,
                WICBitmapTransformRotate0,
                WICPlanarOptionsDefault,
                SampleConstants::WicYCbCrFormats,
                m_planeDescriptions,
                SampleConstants::NumPlanes,
                &isTransformSupported
                )
            );

        // The returned width and height may be larger if IWICPlanarBitmapSourceTransform does not
        // exactly support what is requested.
        if ((isTransformSupported == TRUE) &&
            (supportedWidth == m_cachedBitmapPixelWidth) &&
            (supportedHeight == m_cachedBitmapPixelHeight))
        {
            return true;
        }
    }

    return false;
}

bool DirectXSampleRenderer::DoesDriverSupportYCbCr()
{
    auto d2dContext = m_deviceResources->GetD2DDeviceContext();

    return (d2dContext->IsDxgiFormatSupported(DXGI_FORMAT_R8_UNORM)) &&
        (d2dContext->IsDxgiFormatSupported(DXGI_FORMAT_R8G8_UNORM));
}

YCbC r ピクセル データ デコード

YCbCr ピクセル データを取得する場合は、 IWICPlanarBitmapSourceTransform::CopyPixels を呼び出す必要があります。 このメソッドは、ピクセル データを入力された WICBitmapPlane 構造体の配列にコピーします。これは、必要なデータのプレーンごとに 1 つ (Y や CbCr など) です。 WICBitmapPlane には、ピクセル データに関する情報が含まれており、データを受信するメモリ バッファーを指します。

YCbCr ピクセル データを他の WIC API と共に使用する場合は、適切に構成された IWICBitmap を作成し、Lock を呼び出して基になるメモリ バッファーを取得し、バッファーを YCbCr ピクセル データの受信に使用される WICBitmapPlane に関連付ける必要があります。 その後、 IWICBitmap を通常どおりに使用できます。

最後に、Direct2D で YCbCr データをレンダリングする場合は、各 IWICBitmap から ID2D1Bitmap を作成し、YCbCr イメージ効果のソースとして使用する必要があります。 WIC を使用すると、複数の平面構成を要求できます。 Direct2D と相互運用する場合は、2 つのプレーンを要求する必要があります。1 つは GUID_WICPixelFormat8bppY を使用し、もう 1 つは GUID_WICPixelFormat16bppCbCr を使用します。これは Direct2D で想定される構成であるためです。

コード サンプル

Direct2D で YCbCr データをデコードしてレンダリングする手順を示すコード例を次に示します。 この例は、 Direct2D および WIC サンプルの JPEG YCbCr 最適化から取得したものです。

void DirectXSampleRenderer::CreateYCbCrDeviceResources()
{
    auto wicFactory = m_deviceResources->GetWicImagingFactory();
    auto d2dContext = m_deviceResources->GetD2DDeviceContext();

    ComPtr<IWICPlanarBitmapSourceTransform> wicPlanarSource;
    DX::ThrowIfFailed(
        m_wicScaler.As(&wicPlanarSource)
        );

    ComPtr<IWICBitmap> bitmaps[SampleConstants::NumPlanes];
    ComPtr<IWICBitmapLock> locks[SampleConstants::NumPlanes];
    WICBitmapPlane planes[SampleConstants::NumPlanes];

    for (uint32 i = 0; i < SampleConstants::NumPlanes; i++)
    {
        DX::ThrowIfFailed(
            wicFactory->CreateBitmap(
                m_planeDescriptions[i].Width,
                m_planeDescriptions[i].Height,
                m_planeDescriptions[i].Format,
                WICBitmapCacheOnLoad,
                &bitmaps[i]
                )
            );

        LockBitmap(bitmaps[i].Get(), WICBitmapLockWrite, nullptr, &locks[i], &planes[i]);
    }

    DX::ThrowIfFailed(
        wicPlanarSource->CopyPixels(
            nullptr, // Copy the entire source region.
            m_cachedBitmapPixelWidth,
            m_cachedBitmapPixelHeight,
            WICBitmapTransformRotate0,
            WICPlanarOptionsDefault,
            planes,
            SampleConstants::NumPlanes
            )
        );

    DX::ThrowIfFailed(d2dContext->CreateEffect(CLSID_D2D1YCbCr, &m_d2dYCbCrEffect));

    ComPtr<ID2D1Bitmap1> d2dBitmaps[SampleConstants::NumPlanes];
    for (uint32 i = 0; i < SampleConstants::NumPlanes; i++)
    {
        // IWICBitmapLock must be released before using the IWICBitmap.
        locks[i] = nullptr;

        // First ID2D1Bitmap1 is DXGI_FORMAT_R8 (Y), second is DXGI_FORMAT_R8G8 (CbCr).
        DX::ThrowIfFailed(d2dContext->CreateBitmapFromWicBitmap(bitmaps[i].Get(), &d2dBitmaps[i]));
        m_d2dYCbCrEffect->SetInput(i, d2dBitmaps[i].Get());
    }
}

void DirectXSampleRenderer::LockBitmap(
    _In_ IWICBitmap *pBitmap,
    DWORD bitmapLockFlags,
    _In_opt_ const WICRect *prcSource,
    _Outptr_ IWICBitmapLock **ppBitmapLock,
    _Out_ WICBitmapPlane *pPlane
    )
{
    // ComPtr guarantees the IWICBitmapLock is released if an exception is thrown.
    ComPtr<IWICBitmapLock> lock;
    DX::ThrowIfFailed(pBitmap->Lock(prcSource, bitmapLockFlags, &lock));
    DX::ThrowIfFailed(lock->GetStride(&pPlane->cbStride));
    DX::ThrowIfFailed(lock->GetDataPointer(&pPlane->cbBufferSize, &pPlane->pbBuffer));
    DX::ThrowIfFailed(lock->GetPixelFormat(&pPlane->Format));
    *ppBitmapLock = lock.Detach();
}

YCbCr ピクセル データの変換

YCbCr データの変換はデコードとほぼ同じです。どちらも IWICPlanarBitmapSourceTransform が必要です。 唯一の違いは、インターフェイスを取得した WIC オブジェクトです。 Windows では、スケーラー、反転回転子、色変換がすべて YCbCr アクセスをサポートしています。

変換を連結する

WIC では、複数の変換を連結する概念がサポートされています。 たとえば、次の WIC パイプラインを作成できます。

jpeg デコーダーから始まる wic パイプラインの図。

その後、 IWICColorTransform で QueryInterface を呼び出して 、IWICPlanarBitmapSourceTransform を取得できます。 色変換は、上記の変換と通信でき、パイプライン内のすべてのステージの集計機能を公開できます。 WIC では、プロセス全体を通じて YCbCr データが確実に保持されます。 このチェーンは、YCbCr アクセスをサポートするコンポーネントを使用する場合にのみ機能します。

JPEG コーデックの最適化

IWICBitmapSourceTransform の JPEG フレーム デコード実装と同様に、IWICPlanarBitmapSourceTransform の JPEG フレーム デコード実装では、ネイティブ JPEG DCT ドメインのスケーリングと回転がサポートされます。 JPEG デコーダーから直接、2 つのダウンスケールまたは回転の電力を要求できます。 これは通常、不連続変換を使用するよりも品質とパフォーマンスが向上します。

さらに、JPEG デコーダーの後に 1 つ以上の WIC 変換をチェーンすると、ネイティブの JPEG スケーリングと回転を利用して、要求された集計操作を満たすことができます。

書式変換

IWICPlanarFormatConverter を使用して、平面 YCbCr ピクセル データをインターリーブピクセル形式 (GUID_WICPixelFormat32bppPBGRA など) に変換します。 Windows 8.1の WIC では、平面 YCbCr ピクセル形式に変換する機能は提供されません。

YCbC r ピクセル データ エンコード

IWICPlanarBitmapFrameEncode を使用して、YCbCr ピクセル データを JPEG エンコーダーにエンコードします。 YCbCr データ IWICPlanarBitmapFrameEncode のエンコードは似ていますが、 IWICBitmapFrameEncode を使用したインターリーブ データのエンコードと同じではありません。 平面インターフェイスでは、平面フレーム イメージ データを書き込む機能のみが公開されます。フレーム エンコード インターフェイスを引き続き使用して、メタデータまたはサムネイルを設定し、操作の最後にコミットする必要があります。

一般的なケースでは、次の手順に従う必要があります。

  1. IWICBitmapFrameEncode を通常どおり取得します。 彩度サブサンプリングを構成する場合は、フレームの作成時に JpegYCrCbSubsampling エンコーダー オプションを設定します。
  2. メタデータまたはサムネイルを設定する必要がある場合は、通常どおり IWICBitmapFrameEncode を 使用してこれを行います。
  3. IWICPlanarBitmapFrameEncode の QueryInterface。
  4. IWICPlanarBitmapFrameEncode::WriteSource または IWICPlanarBitmapFrameEncode::WritePixels を使用して、YCbCr ピクセル データを設定します。 対応する IWICBitmapFrameEncode とは異なり、これらのメソッドには、YCbCr ピクセル プレーンを含む IWICBitmapSource または WICBitmapPlane の配列を指定します。
  5. 完了したら、 IWICBitmapFrameEncode::Commit を呼び出します。

Windows 10での YCbCr ピクセル データのデコード

ビルド 1507 Windows 10以降、Direct2D は ID2D1ImageSourceFromWic を提供します。これは、YCbCr の最適化を活用しながら、JPEG を Direct2D にデコードする簡単な方法です。 ID2D1ImageSourceFromWic は、必要なすべての YCbCr 機能チェックを自動的に実行します。可能な場合は最適化されたコードパスを使用し、それ以外の場合はフォールバックを使用します。 また、一度に必要なイメージのサブリージョンのみをキャッシュするなど、新しい最適化も可能になります。

ID2D1ImageSourceFromWic の使用の詳細については、Direct2D Photo Adjustment SDK サンプルを参照してください。