【问题标题】:WICConvertBitmapSource + CopyPixels results in blue imageWICConvertBitmapSource + CopyPixels 生成蓝色图像
【发布时间】:2017-12-05 08:50:26
【问题描述】:

我正在尝试使用 WIC 将图像加载到内存缓冲区中以进行进一步处理,然后在完成后将其写回文件。具体来说:

  1. 将图像加载到 IWICBitmapFrameDecode 中。
  2. 加载的 IWICBitmapFrameDecode 报告其像素格式为 GUID_WICPixelFormat24bppBGR。我想使用 32bpp RGBA,所以我打电话给WICConvertBitmapSource
  3. 在转换后的帧上调用CopyPixels以获取内存缓冲区。
  4. 使用 WritePixels 将内存缓冲区写回 IWICBitmapFrameEncode。

这会生成可识别的图像,但生成的图像大部分是蓝色的,就好像红色通道被解释为蓝色一样。

如果我调用WriteSource 直接写入转换后的帧,而不是写入内存缓冲区,它可以工作。如果我从原始未转换的帧中调用 CopyPixels(并相应地更新我的步幅和像素格式),它就可以工作。只是 WICConvertBitmapSource 的组合加上内存缓冲区(CopyPixels + WritePixels)的使用导致了问题,但我无法弄清楚我做错了什么。

这是我的代码。

int main() {

IWICImagingFactory *pFactory;
IWICBitmapDecoder *pDecoder = NULL;

CoInitializeEx(NULL, COINIT_MULTITHREADED);

CoCreateInstance(
    CLSID_WICImagingFactory,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_IWICImagingFactory,
    (LPVOID*)&pFactory
);

// Load the image.
pFactory->CreateDecoderFromFilename(L"input.png", NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pDecoder);

IWICBitmapFrameDecode *pFrame = NULL;
pDecoder->GetFrame(0, &pFrame);

// pFrame->GetPixelFormat shows that the image is 24bpp BGR.
// Convert to 32bpp RGBA for easier processing.
IWICBitmapSource *pConvertedFrame = NULL;
WICConvertBitmapSource(GUID_WICPixelFormat32bppRGBA, pFrame, &pConvertedFrame);

// Copy the 32bpp RGBA image to a buffer for further processing.
UINT width, height;
pConvertedFrame->GetSize(&width, &height);

const unsigned bytesPerPixel = 4;
const unsigned stride = width * bytesPerPixel;
const unsigned bitmapSize = width * height * bytesPerPixel;
BYTE *buffer = new BYTE[bitmapSize];
pConvertedFrame->CopyPixels(nullptr, stride, bitmapSize, buffer);

// Insert image buffer processing here.  (Not currently implemented.)

// Create an encoder to turn the buffer back into an image file.
IWICBitmapEncoder *pEncoder = NULL;
pFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &pEncoder);

IStream *pStream = NULL;
SHCreateStreamOnFileEx(L"output.png", STGM_WRITE | STGM_CREATE, FILE_ATTRIBUTE_NORMAL, true, NULL, &pStream);

pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);

IWICBitmapFrameEncode *pFrameEncode = NULL;
pEncoder->CreateNewFrame(&pFrameEncode, NULL);

pFrameEncode->Initialize(NULL);
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppRGBA;
pFrameEncode->SetPixelFormat(&pixelFormat);
pFrameEncode->SetSize(width, height);
pFrameEncode->WritePixels(height, stride, bitmapSize, buffer);

pFrameEncode->Commit();
pEncoder->Commit();
pStream->Commit(STGC_DEFAULT);

return 0;
}

【问题讨论】:

  • 首先,确保您检查了每个返回它的函数的HRESULT。您可以使用SUCCEEEDFAILED 宏来做到这一点,或者您可以使用ThrowIfFailed 之类的东西。
  • 您在这里对运行时像素格式做了很多假设,而您没有检查。一个很好的例子是查看WICTextureLoaderScreenGrab。有关使用 WIC 的更多示例代码,请查看 DirectXTex

标签: c++ image image-processing wic


【解决方案1】:

PNG 编码器仅支持GUID_WICPixelFormat32bppBGRA (BGR) for 32bpp,如PNG Native Codec 官方文档中所述。当您使用GUID_WICPixelFormat32bppRGBA 调用它时,它不会 进行频道切换。变态只会使用你的像素,因为它们是 BGR,而不是 RGB,不会告诉你有问题。

我不知道您要做什么,但是在您的示例中,您可以在对 WICConvertBitmapSource 的调用中将 GUID_WICPixelFormat32bppRGBA 替换为 GUID_WICPixelFormat32bppBGRA (并替换最后一个 pixelFormat 变量的定义以确保您的源代码是正确的,但它不会改变任何东西)。

PS:您可以使用 Wic 来保存文件,无需使用其他 API 创建流,请在此处查看我的答案:Capture screen using DirectX

【讨论】:

  • 这里要明确,您必须检查pixelFormat 的值您调用pFrameEncode->SetPixelFormat(&pixelFormat); 之后。它不一定与您在输入时提供的格式相同(这就是为什么它需要一个指向 GUID 的指针而不仅仅是一个 GUID)。正如这个答案所示,PNG 不一定是GUID_WICPixelFormat32bppRGBA。它可以是编解码器支持的其他东西。
猜你喜欢
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
  • 2016-02-24
  • 1970-01-01
  • 2015-07-01
  • 2016-06-08
  • 1970-01-01
  • 2014-08-11
相关资源
最近更新 更多