JPEG YCbCr 支援
從 Windows 8.1 開始,Windows 映像元件 (WIC) JPEG 編解碼器支援在其原生 YCbCr表單中讀取和寫入影像資料。 WIC YCbCr 支援可以搭配 Direct2D 使用,以使用影像效果轉譯 YCbCr 圖元資料。 此外,WIC JPEG 編解碼器可以透過媒體基礎取用特定相機驅動程式所產生的 YCbCr 圖元資料。
YCbcr 圖元資料耗用比標準 BGRA 像素格式還少的記憶體。 此外,存取 YCbCr 資料可讓您將 JPEG 解碼/編碼管線的某些階段卸載至 GPU 加速的 Direct2D。 藉由使用 YCbCr,您的應用程式可以降低相同大小和品質影像的 JPEG 記憶體耗用量和負載時間。 或者,您的應用程式可以使用更高解析度的 JPEG 影像,而不會產生效能負面影響。
本主題描述 YCbCr 資料的運作方式,以及如何在 WIC 和 Direct2D 中使用。
關於 JPEG YCbCr 資料
本節說明瞭解 WIC 中 YCbCr 支援的運作方式及其重要優點所需的一些重要概念。
YCbCr 色彩模型
Windows 8和更早版本中的 WIC 支援四種不同的色彩模型,最常見的是 RGB/BGR。 此色彩模型會使用紅色、綠色和藍色元件來定義色彩資料;也可以使用第四個 Alpha 元件。
以下是分解成其紅色、綠色和藍色元件的影像。
YCbCr 是一種替代色彩模型,使用亮度元件定義色彩資料, (Y) 和兩個色階元件 (Cb 和 Cr) 。 它通常用於數位影像處理和視訊案例。 YCbCr 一詞通常會與 YUV 交換使用,但兩者在技術上是相異的。
YCbCr 有數種變化,其差異在於色彩空間和動態範圍定義 – WIC 特別支援 JPEG JFIF YCbCr 資料。 如需詳細資訊,請參閱 JPEGWT-T81 規格。
以下是分解成其 Y、Cb和 Cr 元件的映射。
平面記憶體配置與交錯記憶體配置
本節說明在記憶體中存取和儲存 RGB 圖元資料與 YCbCr 資料之間的一些差異。
RGB 圖元資料通常會使用交錯記憶體配置來儲存。 這表示單一色彩元件的資料會在圖元之間交錯,而且每個圖元會連續儲存在記憶體中。
下圖顯示儲存在交錯記憶體配置中的 RGBA 圖元資料。
YCbCr 資料通常會使用平面記憶體配置來儲存。 這表示每個色彩元件會分別儲存在自己的連續平面中,總共有三個平面。 在另一個常見的組態中,Cb 和 Cr 元件會交錯並儲存在一起,而 Y 元件會保留在自己的平面中,總共有兩個平面。
下圖顯示平面 Y 和交錯 Cbr 圖元資料,這是常見的 YCb Cr記憶體配置。
在 WIC 和 Direct2D 中,每個色彩平面都會視為自己的不同物件, (IWICBitmapSource 或 ID2D1Bitmap) ,而且這些平面共同構成 YCbCr 影像的支援資料。
雖然 WIC 支援在 2 和 3 平面設定中存取 YCbCr 資料,但 Direct2D 僅支援先前的 (Y 和 Cbcr) 。 因此,一起使用 WIC 和 Direct2D 時,您應該一律使用 2 平面 YCbCr 組態。
Chroma 子取樣
YCbc r色彩模型非常適合數位影像處理案例,因為它可以利用人類視覺系統的某些層面。 特別是,人類對影像亮度 (亮度) 的變更更敏感,而且對色彩 (色彩) 較不敏感。 藉由將色彩資料分割成個別的亮度和色度元件,我們可以選擇性地只壓縮 chrominance 元件,以節省空間,同時降低品質損失。
執行此動作的其中一種技術稱為 chroma 子取樣。 Cb 和 Cr 平面會在一個或兩個水準和垂直維度中,子取樣 (縮小) 。 基於歷史原因,每個 chroma 子取樣模式通常會使用三個部分 J:a:b 比例來參考。
子取樣模式 | 水準向下調整 | 垂直向下調整 | 每個圖元的位* |
---|---|---|---|
4:4:4 | 1 倍 | 1 倍 | 24 |
4:2:2 | 2x | 1 倍 | 16 |
4:4:0 | 1 倍 | 2x | 16 |
4:2:0 | 2x | 2x | 12 |
* 包含 Y 資料。
從上表中,如果您使用 YCbCr 來儲存未壓縮的影像資料,您可以根據使用的 chroma 子取樣模式,達到每圖元 RGBA 資料的 25% 到 62.5% 與 32 位的記憶體節省。
YCbcr的 JPEG 使用量
概括而言,JPEG 解壓縮管線包含下列階段:
- 執行 entropy (,例如 Huffman) 解壓縮
- 執行取消量化
- 執行反向余弦轉換
- 在 CbCr 資料上執行 chroma upsampling
- 視) 需要將 YCbCr 資料轉換成 RGBA (
藉由讓 JPEG 編解碼器產生 YCbCr 資料,我們可以避免解碼程式的最後兩個步驟,或將其延遲至 GPU。 除了上一節所列的記憶體節省,這可大幅減少解碼影像所需的整體時間。 編碼 YCbCr 資料時,會套用相同的節省成本。
使用 JPEG YCbCr 資料
本節說明如何使用 WIC 和 Direct2D 操作 YCbCr 資料。
若要查看本檔在實務中使用的指引,請參閱 Direct2D 和 WIC 範例中的 JPEG YCbCr 優化 ,其中示範在 Direct2D 應用程式中解碼和轉譯 YCbCr 內容所需的所有步驟。
使用 YCbCr JPEG 映射
大部分的 JPEG 映射會儲存為 YCbCr。 某些 JPEG 包含 CMYK 或灰階資料,且不使用 YCbCr。 這表示您通常不一定可以直接使用預先存在的 JPEG 內容,而不需進行任何修改。
WIC 和 Direct2D 不支援 Direct2D 中的每個可能 YCbCr 組態,而 Direct2D 中的 YCbCr 支援則取決於基礎圖形硬體和驅動程式。 因此,一般用途映射管線必須強固到不使用 YCbCr 的映射 (包括其他常見的影像格式,例如 PNG 或 BMP) ,或無法使用 YCbCr 支援的情況。 建議您保留現有的 BGRA 型映射管線,並在可用時啟用 YCbCr 作為效能優化。
Windows 映像元件 API
Windows 8.1中的 WIC 新增三個新的介面,以提供 JPEG YCbCr資料的存取權。
IWICPlanarBitmapSourceTransform
IWICPlanarBitmapSourceTransform 類似于 IWICBitmapSourceTransform,不同之處在于它會在平面設定中產生圖元,包括 YCbCr 資料。 您可以在支援平面存取的 IWICBitmapSource 實作上呼叫 QueryInterface 來取得此介面。 這包括 JPEG 編解碼器的IWICBitmapFrameDecode實作,以及IWICBitmapScaler、IWICBitmapFlipRotator和IWICColorTransform。
IWICPlanarBitmapFrameEncode
IWICPlanarBitmapFrameEncode 可讓您編碼平面圖元資料,包括 YCbCr 資料。 您可以在 JPEG 編解碼器的 IWICBitmapFrameEncode實作上呼叫 QueryInterface 來取得此介面。
IWICPlanarFormatConverter
IWICPlanarFormatConverter 可讓 IWICFormatConverter 取用平面圖元資料,包括 YCbCr,並將其轉換成交錯像素格式。 它不會公開將交錯圖元資料轉換成平面格式的能力。 您可以在 Windows 提供的 IWICFormatConverter實作上呼叫 QueryInterface 來取得此介面。
Direct2D API
Windows 8.1中的 Direct2D 支援具有新 YCbCr影像效果的 YCbCr平面圖元資料。 此效果可讓您轉譯 YCbCr 資料。 效果會採用輸入兩個 ID2D1Bitmap 介面:一個包含DXGI_FORMAT_R8_UNORM格式的平面 Y 資料,另一個包含DXGI_FORMAT_R8G8_UNORM格式的交錯 CbCr 資料。 您通常會使用此效果來取代包含 BGRA 圖元資料的 ID2D1Bitmap 。
YCbCr 影像效果旨在與 WIC YCbCr API 搭配使用,以提供 YCbCr 資料。 這可有效地將部分解碼工作從 CPU 卸載至 GPU,以便更快速且平行處理。
判斷是否支援 YCbcr 組態
如先前所述,您的應用程式應該對 YCbCr 支援無法使用的情況強固。 本節將討論您的應用程式應該檢查的條件。 如果下列任一檢查失敗,您的應用程式應該回復為標準 BGRA 型管線。
WIC 元件是否支援 YCbCr 資料存取?
只有 Windows 提供的 JPEG 編解碼器和特定 WIC 轉換支援 YCbCr 資料存取。 如需完整清單,請參閱 Windows 映像元件 API 一節。
若要取得其中一個平面 YCbCr 介面,請在原始介面上呼叫 QueryInterface。 如果元件不支援 YCbCr 資料存取,這會失敗。
YCbCr是否支援要求的 WIC 轉換?
取得 IWICPlanarBitmapSourceTransform之後,您應該先呼叫 DoesSupportTransform。 這個方法會採用您想要套用至平面 YCbCr 資料的完整轉換集,並傳回布林值,指出支援,以及最接近可傳回的要求大小維度。 您應該先檢查這三個值,再使用 IWICPlanarBitmapSourceTransform::CopyPixels存取圖元資料。
此模式類似于 IWICBitmapSourceTransform 的使用方式。
圖形驅動程式是否支援搭配 Direct2D 使用 YCbcr 所需的功能?
只有在您使用 Direct2D YCbCr 效果來轉譯 YCbCr 內容時,才需要此檢查。 Direct2D 會使用DXGI_FORMAT_R8_UNORM和DXGI_FORMAT_R8G8_UNORM像素格式來儲存 YCbr資料,這些格式無法從所有圖形驅動程式取得。
使用 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));
}
解碼 YCb r圖元資料
如果您想要取得 YCbCr 圖元資料,您應該呼叫 IWICPlanarBitmapSourceTransform::CopyPixels。 這個方法會將圖元資料複製到填滿 WICBitmapPlane 結構的陣列,其中一個用於您想要 (的每個平面,例如 Y 和 Cbcr) 。 WICBitmapPlane包含圖元資料的相關資訊,並指向將接收資料的記憶體緩衝區。
如果您想要搭配其他 WIC API 使用 YCbr圖元資料,您應該建立適當設定的IWICBitmap、呼叫Lock以取得基礎記憶體緩衝區,並將緩衝區與用來接收 YCbCr圖元資料的WICBitmapPlane產生關聯。 然後,您可以正常使用 IWICBitmap 。
最後,如果您想要在 Direct2D 中轉譯 YCbCr資料,您應該從每個IWICBitmap建立ID2D1Bitmap,並將其作為 YCbCr影像效果的來源。 WIC 可讓您要求多個平面設定。 與 Direct2D 互通時,您應該要求兩個平面,一個使用 GUID_WICPixelFormat8bppY,另一個則使用 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();
}
轉換 YCbc r圖元資料
轉換 YCbCr 資料與解碼幾乎完全相同,因為兩者都牽涉 到 IWICPlanarBitmapSourceTransform。 唯一的差異在於您從中取得介面的 WIC 物件。 Windows 提供的縮放器、翻轉旋轉器和色彩轉換全都支援 YCbCr 存取。
將轉換鏈結在一起
WIC 支援將多個轉換鏈結在一起的概念。 例如,您可以建立下列 WIC 管線:
然後,您可以在 IWICColorTransform 上呼叫 QueryInterface,以取得 IWICPlanarBitmapSourceTransform。 色彩轉換可以與上述轉換通訊,而且可以公開管線中每個階段的匯總功能。 WIC 可確保 YCbCr 資料會透過整個程式保留。 此鏈結僅適用于使用支援 YCbCr 存取的元件時。
JPEG 編解碼器優化
類似于 IWICBitmapSourceTransform的 JPEG 框架解碼實作, IWICPlanarBitmapSourceTransform 的 JPEG 框架解碼實作支援原生 JPEG DCT 網域縮放和旋轉。 您可以直接從 JPEG 解碼器要求兩個縮小或旋轉的電源。 這通常會產生比使用離散轉換更高的品質和效能。
此外,當您在 JPEG 解碼器之後鏈結一或多個 WIC 轉換時,可以利用原生 JPEG 縮放和旋轉來滿足匯總要求的作業。
格式轉換
使用 IWICPlanarFormatConverter 將平面 YCbCr 圖元資料轉換成交錯的像素格式,例如 GUID_WICPixelFormat32bppPBGRA。 Windows 8.1中的 WIC 無法轉換為平面 YCbc r像素格式。
編碼 YCb r圖元資料
使用 IWICPlanarBitmapFrameEncode 將 YCbcr 圖元資料編碼為 JPEG 編碼器。 編碼 YCbCr 資料 IWICPlanarBitmapFrameEncode 類似,但與使用 IWICBitmapFrameEncode編碼交錯的資料不同。 平面介面只會公開撰寫平面框架影像資料的能力,而且您應該繼續使用框架編碼介面來設定中繼資料或縮圖,並在作業結束時認可。
針對一般案例,您應該遵循下列步驟:
- 如常取得 IWICBitmapFrameEncode 。 如果您想要設定 chroma 子取樣,請在建立畫面時設定 JpegYCrCbSubsampling 編碼器選項。
- 如果您需要設定中繼資料或縮圖,請使用 IWICBitmapFrameEncode 做為一般。
- IWICPlanarBitmapFrameEncode的 QueryInterface。
- 使用IWICPlanarBitmapFrameEncode::WriteSource或IWICPlanarBitmapFrameEncode::WritePixels設定 YCbr圖元資料。 不同于其IWICBitmapFrameEncode對應專案,您可以使用IWICBitmapSource或包含 YCbCr圖元平面的WICBitmapPlane陣列來提供這些方法。
- 完成時,請呼叫 IWICBitmapFrameEncode::Commit。
在 Windows 10 中解碼 YCbc r圖元資料
從Windows 10組建 1507 開始,Direct2D 提供ID2D1ImageSourceFromWic,這是將 JPEG 解碼為 Direct2D 的簡單方式,同時運用 YCbC r優化。 ID2D1ImageSourceFromWic會自動為您執行所有必要的 YCbc r功能檢查;它會盡可能使用優化的程式碼路徑,否則會使用後援。 它也會啟用新的優化,例如只快取一次所需的影像子集。
如需使用 ID2D1ImageSourceFromWic的詳細資訊,請參閱 Direct2D 相片調整 SDK 範例。