JPEG YCbCr 지원
Windows 8.1 부터 WIC(Windows 이미징 구성 요소) JPEG 코덱은 네이티브 YCbCr 형식의 이미지 데이터 읽기 및 쓰기를 지원합니다. WIC YCbCr 지원은 Direct2D 와 함께 사용하여 이미지 효과로 YCbCr 픽셀 데이터를 렌더링할 수 있습니다. 또한 WIC JPEG 코덱은 Media Foundation을 통해 특정 카메라 드라이버에서 생성된 YCbCr 픽셀 데이터를 사용할 수 있습니다.
YCbCr 픽셀 데이터는 표준 BGRA 픽셀 형식보다 훨씬 적은 메모리를 사용합니다. 또한 YCbCr 데이터에 액세스하면 JPEG 디코딩/인코딩 파이프라인의 일부 단계를 GPU가 가속화된 Direct2D로 오프로드할 수 있습니다. 앱은 YCbCr을 사용하여 동일한 크기 및 품질 이미지에 대한 JPEG 메모리 사용량 및 로드 시간을 줄일 수 있습니다. 또는 앱에서 성능 저하 없이 더 높은 해상도의 JPEG 이미지를 사용할 수 있습니다.
이 항목에서는 YCbCr 데이터의 작동 방식과 WIC 및 Direct2D에서 데이터를 사용하는 방법을 설명합니다.
JPEG YCbC r 데이터정보
이 섹션에서는 WIC의 YCbCr 지원 작동 방식과 주요 이점을 이해하는 데 필요한 몇 가지 주요 개념을 설명합니다.
YCbCr 색 모델
Windows 8 이하의 WIC는 네 가지 색 모델을 지원하며, 그 중 가장 일반적인 것은 RGB/BGR입니다. 이 색 모델은 빨간색, 녹색 및 파란색 구성 요소를 사용하여 색 데이터를 정의합니다. 네 번째 알파 구성 요소를 사용할 수도 있습니다.
다음은 빨간색, 녹색 및 파란색 구성 요소로 분해된 이미지입니다.
YCbCr 은 Y(광도 구성 요소)와 두 개의 색 구성 요소(Cb 및 Cr)를 사용하여 색 데이터를 정의하는 대체 색 모델입니다. 디지털 이미징 및 비디오 시나리오에서 일반적으로 사용됩니다. YCbCr 이라는 용어는 기술적으로 서로 다르지만 YUV와 서로 바꿔서 사용되는 경우가 많습니다.
색 공간 및 동적 범위 정의가 다른 YCbCr 의 여러 변형이 있습니다. WIC는 특히 JPEG JFIF YCbCr 데이터를 지원합니다. 자세한 내용은 JPEG ITU-T81 사양을 참조하세요.
Y, Cb 및 Cr 구성 요소로 분해된 이미지는 다음과 같습니다.
평면 및 인터리브 메모리 레이아웃
이 섹션에서는 RGB 픽셀 데이터에 액세스하고 메모리에 저장하는 것과 YCbC r 데이터 간의 몇 가지 차이점을 설명합니다.
RGB 픽셀 데이터는 일반적으로 인터리브 메모리 레이아웃을 사용하여 저장됩니다. 즉, 단일 색 구성 요소에 대한 데이터는 픽셀 간에 인터리브되고 각 픽셀은 메모리에 연속적으로 저장됩니다.
다음은 인터리브 메모리 레이아웃에 저장된 RGBA 픽셀 데이터를 보여 주는 그림입니다.
YCbCr 데이터는 일반적으로 평면 메모리 레이아웃을 사용하여 저장됩니다. 즉, 각 색 구성 요소는 총 3개의 평면에 대해 고유한 연속 평면에 별도로 저장됩니다. 또 다른 일반적인 구성에서 Cb 및 Cr 구성 요소는 인터리빙되고 함께 저장되며, Y 구성 요소는 총 두 평면에 대해 자체 평면에 유지됩니다.
다음은 평면 Y 및 인터리브된 CbCr 픽셀 데이터, 일반적인 YCbCr 메모리 레이아웃을 보여 주는 그림입니다.
WIC와 Direct2D 모두에서 각 색 평면은 고유한 개체( IWICBitmapSource 또는 ID2D1Bitmap)로 처리되며, 이러한 평면은 전체적으로 YCbCr 이미지에 대한 지원 데이터를 형성합니다.
WIC는 2와 3 평면 구성 모두에서 YCbCr 데이터 액세스를 지원하지만 Direct2D는 전자(Y 및 CbCr)만 지원합니다. 따라서 WIC와 Direct2D를 함께 사용하는 경우 항상 2 평면 YCbCr 구성을 사용해야 합니다.
크로마 하위 샘플링
YCbCr 색 모델은 인간 시각적 시스템의 특정 측면을 활용할 수 있으므로 디지털 이미징 시나리오에 적합합니다. 특히, 인간은 이미지의 광도(밝기)의 변화에 더 민감하고 색(색)에 덜 민감합니다. 색 데이터를 별도의 광도 및 색 구성 요소로 분할하여 색 구성 요소만 선택적으로 압축하여 최소한의 품질 손실로 공간 절약을 달성할 수 있습니다.
이를 위한 한 가지 기술은 크로마 하위 샘플링이라고 합니다. 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%의 메모리 절감 효과를 얻을 수 있습니다.
YCbCr의 JPEG 사용
높은 수준에서 JPEG 압축 해제 파이프라인은 다음 단계로 구성됩니다.
- 엔트로피(예: 허프만) 압축 해제 수행
- 따옴표 지정 수행
- 역 불연속 코사인 변환 수행
- CbCr 데이터에서 크로마 업샘플링 수행
- YCbCr 데이터를 RGBA로 변환(필요한 경우)
JPEG 코덱이 YCbCr 데이터를 생성하도록 함으로써 디코딩 프로세스의 마지막 두 단계를 방지하거나 GPU로 연기할 수 있습니다. 이전 섹션에 나열된 메모리 절약 외에도 이미지를 디코딩하는 데 필요한 전체 시간을 크게 줄일 수 있습니다. YCbCr 데이터를 인코딩할 때도 동일한 절감액이 적용됩니다.
JPEG YCbCr 데이터 사용
이 섹션에서는 WIC 및 Direct2D를 사용하여 YCbCr 데이터에서 작동하는 방법을 설명합니다.
실제로 사용되는 이 문서의 지침을 보려면 Direct2D의 JPEG YCbCr 최적화 및 Direct2D 앱에서 YCbCr 콘텐츠를 디코딩하고 렌더링하는 데 필요한 모든 단계를 보여 주는 WIC 샘플을 참조하세요.
YCbCr JPEG 이미지 사용
대부분의 JPEG 이미지는 YCbCr로 저장됩니다. 일부 JPEG는 CMYK 또는 회색조 데이터를 포함하며 YCbCr을 사용하지 않습니다. 즉, 일반적으로 항상 그렇지는 않지만 수정 없이 기존 JPEG 콘텐츠를 직접 사용할 수 있습니다.
WIC 및 Direct2D는 가능한 모든 YCbCr 구성을 지원하지 않으며 Direct2D의 YCbCr 지원은 기본 그래픽 하드웨어 및 드라이버에 따라 달라집니다. 이 때문에 범용 이미징 파이프라인은 YCbCr (PNG 또는 BMP와 같은 다른 일반적인 이미지 형식 포함)을 사용하지 않는 이미지 또는 YCbCr 지원을 사용할 수 없는 경우에 강력해야 합니다. 기존 BGRA 기반 이미징 파이프라인을 유지하고 사용 가능한 경우 성능 최적화로 YCbCr 을 사용하도록 설정하는 것이 좋습니다.
Windows 이미징 구성 요소 API
Windows 8.1 WIC는 JPEG YCbCr 데이터에 대한 액세스를 제공하는 세 개의 새 인터페이스를 추가합니다.
IWICPlanarBitmapSourceTransform
IWICPlanarBitmapSourceTransform은 YCbCr 데이터를 포함하여 평면 구성에서 픽셀을 생성한다는 점을 제외하고 IWICBitmapSourceTransform과 유사합니다. 평면 액세스를 지원하는 IWICBitmapSource 구현에서 QueryInterface를 호출하여 이 인터페이스를 가져올 수 있습니다. 여기에는 JPEG 코덱의 IWICBitmapFrameDecode 구현과 IWICBitmapScaler, IWICBitmapFlipRotator 및 IWICColorTransform이 포함됩니다.
IWICPlanarBitmapFrameEncode
IWICPlanarBitmapFrameEncode 는 YCbCr 데이터를 포함하여 평면 픽셀 데이터를 인코딩하는 기능을 제공합니다. JPEG 코덱의 IWICBitmapFrameEncode 구현에서 QueryInterface를 호출하여 이 인터페이스를 가져올 수 있습니다.
IWICPlanarFormatConverter
IWICPlanarFormatConverter 를 사용하면 IWICFormatConverter 가 YCbCr을 비롯한 평면 픽셀 데이터를 사용하고 인터리브된 픽셀 형식으로 변환할 수 있습니다. 인터리브된 픽셀 데이터를 평면 형식으로 변환하는 기능은 노출되지 않습니다. IWICFormatConverter의 Windows 제공 구현에서 QueryInterface를 호출하여 이 인터페이스를 가져올 수 있습니다.
Direct2D API
Windows 8.1 Direct2D는 새 YCbCr 이미지 효과 를 사용하여 YCbCr 평면 픽셀 데이터를 지원합니다. 이 효과는 YCbCr 데이터를 렌더링하는 기능을 제공합니다. 효과는 두 개의 ID2D1Bitmap 인터페이스로 사용됩니다. 하나는 DXGI_FORMAT_R8_UNORM 형식의 평면 Y 데이터를 포함하고 다른 하나는 DXGI_FORMAT_R8G8_UNORM 형식의 인터리브된 CbCr 데이터를 포함합니다. 일반적으로 BGRA 픽셀 데이터가 포함된 ID2D1Bitmap 대신 이 효과를 사용합니다.
YCbC r 이미지 효과는 YCbCr 데이터를 제공하는 WIC YC bCr API와 함께 사용됩니다. 이는 CPU에서 GPU로 디코딩 작업의 일부를 오프로드하는 역할을 효과적으로 수행하며, 여기서 훨씬 더 빠르고 병렬로 처리할 수 있습니다.
YCbCr 구성이 지원되는지 확인
앞에서 설명한 것처럼 앱은 YCbCr 지원을 사용할 수 없는 경우에 강력해야 합니다. 이 섹션에서는 앱이 검사 조건에 대해 설명합니다. 다음 검사 중 하나라도 실패하면 앱이 표준 BGRA 기반 파이프라인으로 대체되어야 합니다.
WIC 구성 요소가 YCbCr 데이터 액세스를 지원하나요?
Windows 제공 JPEG 코덱 및 특정 WIC 변환만 YCbCr 데이터 액세스를 지원합니다. 전체 목록은 Windows 이미징 구성 요소 API 섹션을 참조하세요 .
평면 YCbCr 인터페이스 중 하나를 가져오려면 원래 인터페이스에서 QueryInterface를 호출합니다. 구성 요소가 YCbCr 데이터 액세스를 지원하지 않는 경우 실패합니다.
요청된 WIC 변환이 YCbC r에 지원되는가요?
IWICPlanarBitmapSourceTransform을 가져온 후 먼저 DoesSupportTransform을 호출해야 합니다. 이 메서드는 평면 YCbCr 데이터에 적용하려는 전체 변환 집합을 입력 매개 변수로 사용하고 지원을 나타내는 부울과 반환할 수 있는 요청된 크기에 가장 가까운 차원을 반환합니다. IWICPlanarBitmapSourceTransform::CopyPixels를 사용하여 픽셀 데이터에 액세스하기 전에 세 가지 값을 모두 검사 합니다.
이 패턴은 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));
}
YCbCr 픽셀 데이터 디코딩
YCbCr 픽셀 데이터를 가져오려면 IWICPlanarBitmapSourceTransform::CopyPixels를 호출해야 합니다. 이 메서드는 원하는 데이터의 각 평면(예: Y 및 CbCr)에 대해 하나씩 채워진 WICBitmapPlane 구조의 배열에 픽셀 데이터를 복사합니다. WICBitmapPlane은 픽셀 데이터에 대한 정보를 포함하고 데이터를 수신할 메모리 버퍼를 가리킵니다.
YCbCr 픽셀 데이터를 다른 WIC API와 함께 사용하려면 적절하게 구성된 IWICBitmap을 만들고 Lock을 호출하여 기본 메모리 버퍼를 가져오고 버퍼를 YCbCr 픽셀 데이터를 수신하는 데 사용되는 WICBitmapPlane과 연결해야 합니다. 그런 다음 IWICBitmap 을 정상적으로 사용할 수 있습니다.
마지막으로 Direct2D에서 YCbCr 데이터를 렌더링하려면 각 IWICBitmap에서 ID2D1Bitmap을 만들고 YCbCr 이미지 효과의 원본으로 사용해야 합니다. WIC를 사용하면 여러 평면 구성을 요청할 수 있습니다. Direct2D와 상호 운용하는 경우 Direct2D에서 예상하는 구성이므로 GUID_WICPixelFormat8bppY 사용하는 평면과 GUID_WICPixelFormat16bppCbCr 사용하는 평면 두 개를 요청해야 합니다.
코드 예제
다음은 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 파이프라인을 만들 수 있습니다.
그런 다음 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는 평면 YCbCr 픽셀 형식으로 변환하는 기능을 제공하지 않습니다.
YCbCr 픽셀 데이터 인코딩
IWICPlanarBitmapFrameEncode를 사용하여 YCbCr 픽셀 데이터를 JPEG 인코더로 인코딩합니다. YCbCr 데이터 IWICPlanarBitmapFrameEncode 인코딩은 IWICBitmapFrameEncode를 사용하여 인터리브된 데이터를 인코딩하는 것과 유사하지만 동일하지는 않습니다. 평면 인터페이스는 평면 프레임 이미지 데이터를 작성하는 기능만 노출하며, 프레임 인코딩 인터페이스를 계속 사용하여 메타데이터 또는 썸네일을 설정하고 작업이 끝날 때 커밋해야 합니다.
일반적인 경우 다음 단계를 수행해야 합니다.
- IWICBitmapFrameEncode를 정상적으로 가져옵니다. chroma 하위 샘플링을 구성하려면 프레임을 만들 때 JpegYCrCbSubsampling 인코더 옵션을 설정합니다.
- 메타데이터 또는 썸네일을 설정해야 하는 경우 IWICBitmapFrameEncode 를 정상적으로 사용하여 이 작업을 수행합니다.
- IWICPlanarBitmapFrameEncode에 대한 QueryInterface입니다.
- IWICPlanarBitmapFrameEncode::WriteSource 또는 IWICPlanarBitmapFrameEncode::WritePixels를 사용하여 YCbCr 픽셀 데이터를 설정합니다. IWICBitmapFrameEncode와 달리 이러한 메서드는 YCbCr 픽셀 평면을 포함하는 IWICBitmapSource 또는 WICBitmapPlane 배열을 제공합니다.
- 완료되면 IWICBitmapFrameEncode::Commit을 호출합니다.
Windows 10 YCbCr 픽셀 데이터 디코딩
Windows 10 빌드 1507부터 Direct2D는 YCbCr 최적화를 활용하여 JPEG를 Direct2D로 디코딩하는 간단한 방법인 ID2D1ImageSourceFromWic을 제공합니다. ID2D1ImageSourceFromWic 은 필요한 모든 YCbCr 기능 검사를 자동으로 수행합니다. 가능하면 최적화된 codepath를 사용하고, 그렇지 않으면 대체를 사용합니다. 또한 한 번에 필요한 이미지의 하위 리소스만 캐싱하는 것과 같은 새로운 최적화를 사용할 수 있습니다.
ID2D1ImageSourceFromWic 사용에 대한 자세한 내용은 Direct2D 사진 조정 SDK 샘플을 참조하세요.