0

我正在使用 Windows 图像处理组件库对大量图像进行编码。理想情况下,我想使用某些属性设置一次编码器,然后为我的所有图像重新使用该编码器。但是,在我看到的所有示例中,似乎为单个图像创建了一个编码器。

我正在读取和写入字节流,而不是文件,并且可能有多个线程同时运行。

这是一段代码:

CComPtr<IWICBitmapEncoder> pEncoder;
CComPtr<IWICBitmapFrameEncode> pBitmapFrame;
CComPtr<IPropertyBag2> pPropertyBag;
CComPtr<IWICStream> pStream;
CComPtr<IStream> pOutputStream;

HRESULT hr;
//  Setup memory stream, which is needed to stage raw image bits
if (CreateStreamOnHGlobal(NULL, TRUE, &pOutputStream) != S_OK)
{
    LogAssert(false, "Could not create pOutputStream. Err (%d)", GetLastError());
}
//Setup WIC stream which encapsulates the output stream
hr = m_pFactory->CreateStream(&pStream);

hr = pStream->InitializeFromIStream(pOutputStream);
hr = m_pFactory->CreateEncoder(GUID_ContainerFormatWmp, NULL, &pEncoder);
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
hr = pEncoder->CreateNewFrame(&pBitmapFrame, &pPropertyBag);
SetEncodingProperties(pPropertyBag);
hr = pBitmapFrame->Initialize(pPropertyBag);

问题 1:我正在写入使用 CreateStreamOnHGlobal 创建的 IStream。我可以重复使用pStream多个pOutputStream图像吗?任何线程安全问题?

问题2:这段代码的哪些部分可以做一次,哪些部分需要针对不同的图像重复?所有的初始化似乎都相互关联。

4

1 回答 1

2

所有内置 WIC 对象都在 Windows 7 中更新为线程安全的,如下所述:http: //msdn.microsoft.com/en-us/library/ee720061%28v=vs.85%29.aspx#_multi_threaded_apartment_support

在以前的 Windows 版本中,对象不是线程安全的,Windows 会自动将调用编组到另一个线程中,以便可以从多个线程访问它。这仅在您正确初始化 COM 时才有效 - 如果您要从多个线程访问对象,您需要使用多线程选项从所有可以访问您的对象的线程中调用 CoInitializeEx,并且您需要确保 CoInitializeEx 返回成功( S_OK 或 S_FALSE)。

一次为单个 IStream 对象使用两个不同的编码器是不安全的。在编码器对象上调用 Commit 后,在其他地方使用流是安全的,但与另一个编码器一起使用它并没有真正意义。您不能在一个文件中包含多个图像(除非图像格式支持多个帧,但您只需要一个编码器对象)。我想您可以在将流大小与另一个编码器一起使用之前将其设置为 0,但分配 HGLOBAL 流可能并不比这样做快。

在您的情况下创建 IWICStream 没有意义,因为您已经有一个 IStream,而这正是编码器所需要的。IWICStream 主要作为一种便利函数而存在,用于从文件、固定大小的内存缓冲区或现有流的一部分创建流。InitializeFromIStream 方法存在的原因是 IWICStream 不支持 Clone 方法。InitializeFromIStream 是一种变通方法,当不支持 Clone 时,它​​可以使用独立游标获取新的 IStream 对象,但这样做的方式不是(也不能是)线程安全的。(为了让它工作,底层流一次只能被一个线程访问。通常,编码器或解码器对象会确保这一点,只要流一次只分配给一个编码器/解码器。 )

由于您关心性能,因此您应该知道像 WIC 编码器可能做的那样,分段写入 HGLOBAL 流是 O(n**2),因为扩大 HGLOBAL 涉及将所有现有数据复制到新位置.

如果您要在 Windows 7 之前从多个线程访问 WIC,我建议使用单线程单元并确保您在一个线程中初始化的对象只能从该线程访问。这将为您节省将调用编组到另一个线程的成本。

只要编码器类相同并且您不尝试使用多个单线程单元(或单线程和多线程单元)。

但是,我认为您太关心对性能影响可以忽略不计的事情,而您应该更关心写入图像数据的过程。如果您的图像文件很大(尽管,如果您正在处理非常大的图像,您可能应该考虑使用 imagemagick 代替)。为每个线程提供完全独立的对象(因为单个对象很可能一次只能在单个线程中工作)也可能有所帮助。

于 2012-09-23T18:47:13.287 回答