使用WIC枚举PNG帧级元数据丢失大量项,求代码排查
问题:使用WIC读取PNG帧级元数据时丢失大量信息
我用以下C++代码导出PNG的帧级元数据树,但输出的元数据远少于系统和ExifTool显示的内容,请问代码存在什么问题?
main.cc
#include <wincodec.h> #include <cassert> #include <iostream> void PrintMetadata(IWICMetadataQueryReader* query_reader, int32_t indentation) { IEnumString* enumerator{}; if (FAILED(query_reader->GetEnumerator(&enumerator))) { return; } while (true) { LPOLESTR name{}; ULONG fetched{}; if (enumerator->Next(1, &name, &fetched) == S_OK) { PROPVARIANT var; ::PropVariantInit(&var); if (query_reader->GetMetadataByName(name, &var) == S_OK) { std::wcout << std::wstring(indentation, L'\t') << name; if (var.vt == VT_UNKNOWN) { std::wcout << std::endl; IWICMetadataQueryReader* sub_query_reader{}; if (SUCCEEDED(var.punkVal->QueryInterface(&sub_query_reader))) { PrintMetadata(sub_query_reader, indentation + 1); sub_query_reader->Release(); } } else { std::wcout << " type: " << var.vt; } std::wcout << std::endl; } ::PropVariantClear(&var); ::CoTaskMemFree(name); } else { break; } } enumerator->Release(); } void DumpMetadatas(PCWSTR path) { IWICImagingFactory* factory{}; HRESULT result = ::CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), (void**)&factory); if (FAILED(result)) { return; } IWICBitmapDecoder* decoder{}; result = factory->CreateDecoderFromFilename( path, nullptr, GENERIC_READ, WICDecodeOptions::WICDecodeMetadataCacheOnLoad, &decoder); if (FAILED(result)) { factory->Release(); return; } IWICBitmapFrameDecode* frame{}; result = decoder->GetFrame(0, &frame); if (FAILED(result)) { decoder->Release(); factory->Release(); return; } IWICMetadataQueryReader* query_reader{}; result = frame->GetMetadataQueryReader(&query_reader); if (FAILED(result)) { frame->Release(); decoder->Release(); factory->Release(); return; } PrintMetadata(query_reader, 0); query_reader->Release(); frame->Release(); decoder->Release(); factory->Release(); } int wmain(int /*argc*/, wchar_t** argv) { ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); DumpMetadatas(argv[1]); ::CoUninitialize(); return 0; }
我的代码输出:
/sRGB /RenderingIntent type: 17 /gAMA /ImageGamma type: 19
ExifTool可显示的完整元数据:
ExifTool Version Number : 12.98 File Name : red.png Directory : C:/images File Size : 1147 bytes File Modification Date/Time : 2024:10:08 15:06:37+08:00 File Access Date/Time : 2024:10:10 16:05:36+08:00 File Creation Date/Time : 2024:10:08 15:06:37+08:00 File Permissions : -rw-rw-rw- File Type : PNG File Type Extension : png MIME Type : image/png Image Width : 400 Image Height : 300 Bit Depth : 8 Color Type : RGB with Alpha Compression : Deflate/Inflate Filter : Adaptive Interlace : Noninterlaced SRGB Rendering : Perceptual Gamma : 2.2 Pixels Per Unit X : 4724 Pixels Per Unit Y : 4724 Pixel Units : meters Image Size : 400x300 Megapixels : 0.120
问题分析与解决方案
你的代码存在三个核心问题:
1. WIC帧元数据查询器仅暴露部分PNG元数据块
IWICBitmapFrameDecode::GetMetadataQueryReader返回的查询器,只会读取PNG中被WIC标记为"标准元数据"的块(比如sRGB、gAMA),而PNG的IHDR块(图像尺寸、位深度、颜色类型等)、**pHYs块(像素密度)**这类基础信息,WIC不会将其放入帧元数据查询器,而是通过IWICBitmapFrameDecode的专属接口提供。
2. 文件系统属性未被读取
ExifTool显示的文件名、修改时间、文件大小等属于文件系统属性,WIC的元数据查询器不包含这些内容,需要通过Windows原生API(如GetFileAttributesEx)单独获取。
3. 未枚举容器级元数据
PNG的部分元数据块属于容器级而非帧级,你需要通过IWICBitmapDecoder::GetMetadataQueryReader获取解码器的元数据查询器,才能枚举到这些块。
修改后的代码示例
以下代码补充了图像基础信息、像素密度、文件属性的读取,并同时枚举帧级和容器级元数据:
#include <wincodec.h> #include <windows.h> #include <cassert> #include <iostream> #include <iomanip> #include <shlwapi.h> #pragma comment(lib, "shlwapi.lib") void PrintMetadata(IWICMetadataQueryReader* query_reader, int32_t indentation) { if (!query_reader) return; IEnumString* enumerator{}; if (FAILED(query_reader->GetEnumerator(&enumerator))) { return; } while (true) { LPOLESTR name{}; ULONG fetched{}; if (enumerator->Next(1, &name, &fetched) != S_OK) { break; } PROPVARIANT var; ::PropVariantInit(&var); if (SUCCEEDED(query_reader->GetMetadataByName(name, &var))) { std::wcout << std::wstring(indentation, L'\t') << name; if (var.vt == VT_UNKNOWN) { std::wcout << std::endl; IWICMetadataQueryReader* sub_query_reader{}; if (SUCCEEDED(var.punkVal->QueryInterface(&sub_query_reader))) { PrintMetadata(sub_query_reader, indentation + 1); sub_query_reader->Release(); } } else { std::wcout << L" type: " << var.vt; // 打印部分常见值的实际内容 if (var.vt == VT_R8) { std::wcout << L" value: " << var.dblVal; } else if (var.vt == VT_UI4) { std::wcout << L" value: " << var.ulVal; } else if (var.vt == VT_UI2) { std::wcout << L" value: " << var.uiVal; } } std::wcout << std::endl; } ::PropVariantClear(&var); ::CoTaskMemFree(name); } enumerator->Release(); } void PrintFileAttributes(PCWSTR path) { WIN32_FILE_ATTRIBUTE_DATA fileData{}; if (GetFileAttributesEx(path, GetFileExInfoStandard, &fileData)) { std::wcout << L"File Name: " << PathFindFileName(path) << std::endl; ULONGLONG fileSize = (static_cast<ULONGLONG>(fileData.nFileSizeHigh) << 32) + fileData.nFileSizeLow; std::wcout << L"File Size: " << fileSize << L" bytes" << std::endl; SYSTEMTIME st{}; FileTimeToSystemTime(&fileData.ftLastWriteTime, &st); std::wcout << L"File Modification Date/Time: " << std::setw(4) << st.wYear << L":" << std::setw(2) << std::setfill(L'0') << st.wMonth << L":" << std::setw(2) << st.wDay << L" " << std::setw(2) << st.wHour << L":" << std::setw(2) << st.wMinute << L":" << std::setw(2) << st.wSecond << std::endl; std::setfill(L' '); } } void DumpMetadatas(PCWSTR path) { IWICImagingFactory* factory{}; HRESULT result = ::CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), (void**)&factory); if (FAILED(result)) { return; } IWICBitmapDecoder* decoder{}; result = factory->CreateDecoderFromFilename( path, nullptr, GENERIC_READ, WICDecodeOptions::WICDecodeMetadataCacheOnLoad, &decoder); if (FAILED(result)) { factory->Release(); return; } // 打印容器级元数据 std::wcout << L"=== 容器级元数据 ===" << std::endl; IWICMetadataQueryReader* container_query{}; if (SUCCEEDED(decoder->GetMetadataQueryReader(&container_query))) { PrintMetadata(container_query, 0); container_query->Release(); } IWICBitmapFrameDecode* frame{}; result = decoder->GetFrame(0, &frame); if (FAILED(result)) { decoder->Release(); factory->Release(); return; } // 打印图像基础信息 std::wcout << L"\n=== 图像基础信息 ===" << std::endl; UINT width, height; if (SUCCEEDED(frame->GetSize(&width, &height))) { std::wcout << L"Image Width: " << width << std::endl; std::wcout << L"Image Height: " << height << std::endl; } UINT bitDepth, channels; WICPixelFormatGUID format; if (SUCCEEDED(frame->GetPixelFormat(&format)) && SUCCEEDED(WICGetPixelFormatInfo(format, &bitDepth, &channels))) { std::wcout << L"Bit Depth: " << bitDepth << std::endl; std::wcout << L"Color Channels: " << channels << std::endl; } // 打印帧级元数据 std::wcout << L"\n=== 帧级元数据 ===" << std::endl; IWICMetadataQueryReader* frame_query{}; if (SUCCEEDED(frame->GetMetadataQueryReader(&frame_query))) { PrintMetadata(frame_query, 0); frame_query->Release(); } // 打印文件属性 std::wcout << L"\n=== 文件系统属性 ===" << std::endl; PrintFileAttributes(path); frame->Release(); decoder->Release(); factory->Release(); } int wmain(int argc, wchar_t** argv) { if (argc < 2) { std::wcout << L"Usage: " << argv[0] << L" <png file path>" << std::endl; return 1; } ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); DumpMetadatas(argv[1]); ::CoUninitialize(); return 0; }
内容的提问来源于stack exchange,提问作者wixom
相关产品推荐
相关产品推荐

