Saturday, November 06, 2004

 

Some notes for Windows Image Acquisition Library (WIA)

1.
The foundamentals of WIA can be found in
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wia/wia/overviews/startpage.asp

2.
A simple introduction in Chinese is found as below

By Yonsm
From http://yonsm.reg365.com/blog.php?job=art&articleid=a_20041008_205647

一、 WIA 简介
1.关于 WIA
WIA 是 Windows Image Acquisition 的简称,当前可用版本是 WIA 1.0,它是 Windows Millennium Edition (Windows Me) 或者更高版本的 Windows 系统中提供的数字图像获取服务,同时它也能用于管理数字图像设备。

WIA 接口既是应用程序接口(WIA API),又是设备驱动程序接口(WIA DDI),下面要讲述的都是有关 WIA API 的内容。

通过 WIA API,应用程序可以:

运行在强壮稳定的环境中;
最大可能地减少协同配合问题;
枚举可用的图像获取设备;
同时连接多个设备;
用标准的、可扩展的方式查询设备属性;
用标准的、高性能的传送机制获取数据;
在数据传送过程中维持图像属性;
获取大量的设备事件通知消息。
2.WIA 架构
WIA 是使用进程外(Out of process)服务实现的 COM 组件,和大多数进程外服务程序不同的是,WIA 通过提供自己的数据传送机制(IWiaDataTransfer 接口),避免了图像数据传送过程中的性能损失。高性能的 IWiaDataTransfer 接口使用共享内存来传送数据给客户程序。

WIA 有三个主要的组件:Device Manager,Minidriver Service Library 和 Device Minidriver。

Device Manager: 枚举图像设备,获取设备属,为设备建立事件和创建设备对象;
Minidriver Service Library: 执行所有设备无关的服务;
Device Minidriver 解释映射: WIA 属性和命令到特定的设备。
二、 使用WIA
1.选择设备
应用程序既可以用 WIA 内置的对话框来选择设备,也可以不使用 WIA 的用户界面。下面的程序将弹出一个 WIA 选择设备对话框:

#include #pragma comment (lib, "WiaGuid.lib")int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ HRESULT hResult; IWiaItem *pItemRoot; IWiaDevMgr *pWiaDevMgr; CoInitialize(NULL); __try { // Create WIA Device Manager instance pWiaDevMgr = NULL; hResult = CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr, (void**) &pWiaDevMgr); if (hResult != S_OK) { MessageBox(NULL, "Error: CoCreateInstance().", NULL, MB_ICONSTOP); __leave; } // Display a WIA select device dialog pItemRoot = NULL; hResult = pWiaDevMgr->SelectDeviceDlg(NULL, 0, WIA_SELECT_DEVICE_NODEFAULT, NULL, &pItemRoot); // User canceled if (hResult == S_FALSE) { MessageBox(NULL, "User canceled.", NULL, MB_ICONINFORMATION); __leave; } // No device available else if (hResult == WIA_S_NO_DEVICE_AVAILABLE) { MessageBox(NULL, "No device available.", NULL, MB_ICONINFORMATION); __leave; } // OK, Then .......... } __finally { // Release COM interface. if (pItemRoot) pItemRoot->Release(); if (pWiaDevMgr) pWiaDevMgr->Release(); CoUninitialize(); } return 0; }
2.获取图像到文件中
WIA 获取图像非常简单,直接调用 IWiaDevMgr::GetImageDlg(),它集成了 Select Device 和 Select Image 对话框,在传送图像的时候还会自动出现一个进度指示对话框,下面是一个例子:

// ...// Create WIA Device Manager object.hResult = CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr, (void**) &pWiaDevMgr);if (hResult == S_OK){ // Get a image. hResult = pWiaDevMgr->GetImageDlg(hWnd, 0, WIA_DEVICE_DIALOG_SINGLE_IMAGE, WIA_INTENT_MAXIMIZE_QUALITY, NULL, wszFilename, &guidFormat); // ...... }// ......
不过由于 IWiaDevMgr::GetImageDlg() 是以图片文件的形式返回数据的,有的时候并不能满足我们的需要,这时候我们就需要使用 IWiaDataTransfer 接口来传送图片。

3.获取内存中的图像数据
在 IWiaDevMgr::SelectDeviceDlg() 之后,可以用它返回的 RootItem 对象的 IWiaItem::DeviceDlg() 方法显示一个对话框浏览 WIA 设备中图片,请看下面的例子:

// ......// Display a WIA dialog box to the user to prepare for image acquisition.hResult = pRootItem->DeviceDlg(hWnd, 0, WIA_INTENT_MAXIMIZE_QUALITY, &cItem, &ppWiaItems);if (hResult == S_OK){ for (i = 0; i < cItem; i++) { // ...... // ppWiaItems[i] } }// ......
IWiaItem::DeviceDlg() 返回选取的图片总数和每个图片的 WiaItem 指针,我们可以用用 IWiaDataTransfer 接口来传送图片。在传送每个 WiaItem 数据之前,应该先调用 IID_IWiaPropertyStorage 接口设置相应的属性:

// ......// Get the IWiaPropertyStorage interface so we can set required properties.hResult = pWiaItem->QueryInterface(IID_IWiaPropertyStorage, (void**) &pWiaPropertyStorage);if (hResult == S_OK){ // Prepare PROPSPECs and PROPVARIANTs for setting the media type and format. psPropSpec[0].ulKind = PRSPEC_PROPID; psPropSpec[0].propid = WIA_IPA_FORMAT; psPropSpec[1].ulKind = PRSPEC_PROPID; psPropSpec[1].propid = WIA_IPA_TYMED; guidOutputFormat = WiaImgFmt_MEMORYBMP; pvPropVariant[0].vt = VT_CLSID; pvPropVariant[0].puuid = &guidOutputFormat; pvPropVariant[1].vt = VT_I4; pvPropVariant[1].lVal = TYMED_CALLBACK; // Set the properties. hResult = pWiaPropertyStorage->WriteMultiple(sizeof(pvPropVariant) / sizeof(pvPropVariant[0]), psPropSpec, pvPropVariant, WIA_IPA_FIRST); // ...... }// ......
如果用 IWiaDataTransfer 接口传送数据,我们还需要自己写代码实现 IWiaDataCallback 接口,其中 在我们的 IWiaDataCallback::BandedDataCallback() 中可以接收到数据,例如:

// [Summary] Recieve data transfer status notifications.HRESULT CALLBACK CWiaDataCallback::BandedDataCallback(LONG lMessage, LONG lStatus, LONG lPercentComplete, LONG lOffset, LONG lLength, LONG lReserved, LONG lResLength, BYTE *pbData){ PWIA_DATA_CALLBACK_HEADER pHeader = NULL; switch (lMessage) { case IT_MSG_DATA_HEADER: // The data header contains the image's final size. pHeader = (PWIA_DATA_CALLBACK_HEADER) pbData; if ((pHeader) && (pHeader->lBufferSize)) { // Save the buffer size. m_nBufferLength = pHeader->lBufferSize; // Allocate a block of memory to hold the image. m_pBuffer = (PBYTE) HeapAlloc(GetProcessHeap(), 0, m_nBufferLength); if (m_pBuffer == NULL) return S_FALSE; } break; case IT_MSG_DATA: // Make sure a block of memory has been created. if (m_pBuffer) { // Copy the new band. CopyMemory(m_pBuffer + lOffset, pbData, lLength); // Increment the counter. m_nBytesTransfered += lLength; } break; case IT_MSG_TERMINATION: // Notify that we complete to recive a image. break; default: break; } return S_OK; }
然后,我们就可以用 IWiaDataTransfer 接口来传送数据了:

// ...// Create our callback class.pCallback = new CWiaDataCallback(hWnd);if (pCallback){ // Get the IWiaDataCallback interface from our callback class. hResult = pCallback->QueryInterface(IID_IWiaDataCallback, (void**) &pWiaDataCallback); if (hResult == S_OK) { // Perform the transfer. wdtiTransferInfo.ulSize = sizeof(WIA_DATA_TRANSFER_INFO); hResult = pWiaDataTransfer->idtGetBandedData(&wdtiTransferInfo, pWiaDataCallback); // ...... } // ...... }// ......
三、后话

WIA 是 Windows ME 及其以后的操作系统中提供的,Windows 98/2000 均不支持 WIA,因此需要在较新版本的 MSDN Library 中才有 WIA 文档。WIA 1.0 在 MSDN 的文档地址是:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wia/wia/overviews/startpage.asp,或者按目录:MSDN Library -> 图形和多媒体 -> Windows 图像获取 -> WIA 1.0。

另外,因为 Visual C++ 6.0 中没有 WIA 库,所以需要使用 Visual Studio.NET 2002/2003 编译 WIA 程序。

3.
The sample program written by Yonsm
From

可以从 WIA 设备(如数码相机、摄像机、WebCam 等等)中获取 BMP/JPG(同时可获取一个或多个两种方式),只能获取图像到文件中。要获取数据在内存中,需要实现 IDataCallback 才能传送数据,有问题的话可与我联系。


namespace WiaHelper{ // 消息: 如果图像已准备好,父窗口将收到 WM_COMMAND 消息 // 参数: LOWORD(wParam) 为 IDC_WiaHelper,HIWORD(wParam) 为总的图像个数 // 返回: 如果返回 S_FALSE,将不再传送后继的图像 // 备注: 收到此消息后,可以调用 TransferImage() 获取图像到文件中 const WORD IDC_WiaHelper = 45123; // 获取 WIA 设备个数 ULONG WINAPI GetDeviceCount(); // 功能: 从 WIA 设备中获取一个图像到文件中 // 返回: 返回 TRUE 表示成功或者用户取消,其它表示失败 // 备注: 如果调用成功,将生成 ptzFileName 指定的图像文件 BOOL WINAPI GetImage(PCTSTR ptzFileName, HWND hParent = NULL); // 功能: 从 WIA 设备中获取多个图像,并以消息回调的形式通知父窗口 // 返回: 返回 TRUE 表示成功或者用户取消,其它表示失败 // 备注: 如果调用成功,当图像准备好时,父窗口将到 WM_COMMAND 消息,请参看 IDC_WiaHelper 的说明 BOOL WINAPI GetImage(HWND hParent); // 传送一个图像到文件中 BOOL WINAPI TransferImage(PCTSTR ptzFileName); };


///////////////////////////////////////////////
// 预处理
#pragma once
#include
///////////////////////////////////////////////



///////////////////////////////////////////////
// WiaHelper 命名空间
namespace WiaHelper
{
// 消息: 如果图像已准备好,父窗口将收到 WM_COMMAND 消息
// 参数: LOWORD(wParam) 为 IDC_WiaHelper,HIWORD(wParam) 为总的图像个数
// 返回: 如果返回 S_FALSE,将不再传送后继的图像
// 备注: 收到此消息后,可以调用 TransferImage() 获取图像到文件中
const WORD IDC_WiaHelper = 45123;

// 获取 WIA 设备个数
ULONG WINAPI GetDeviceCount();

// 功能: 从 WIA 设备中获取一个图像到文件中
// 返回: 返回 TRUE 表示成功或者用户取消,其它表示失败
// 备注: 如果调用成功,将生成 ptzFileName 指定的图像文件
BOOL WINAPI GetImage(PCTSTR ptzFileName, HWND hParent = NULL);

// 功能: 从 WIA 设备中获取多个图像,并以消息回调的形式通知父窗口
// 返回: 返回 TRUE 表示成功或者用户取消,其它表示失败
// 备注: 如果调用成功,当图像准备好时,父窗口将到 WM_COMMAND 消息,请参看 IDC_WiaHelper 的说明
BOOL WINAPI GetImage(HWND hParent);

// 传送一个图像到文件中
BOOL WINAPI TransferImage(PCTSTR ptzFileName);
};
///////////////////////////////////////////////




///////////////////////////////////////////////
// 预处理
#include "WiaHelper.h"
#include
#include
///////////////////////////////////////////////



///////////////////////////////////////////////
// WiaHelper 命名空间
namespace WiaHelper
{
// 用于传送图像的 IWiaItem 指针
IWiaItem *m_pWiaItem = NULL;
};
////////////////////////////////////////////



////////////////////////////////////////////
// 获取 WIA 设备个数
ULONG WINAPI WiaHelper::GetDeviceCount()
{
ULONG ulResult;
HRESULT hResult;
IWiaDevMgr *pWiaDevMgr;
IEnumWIA_DEV_INFO *pWiaDevEnum;

ulResult = 0;
hResult = CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr, (PVOID *) &pWiaDevMgr);
if (hResult == S_OK)
{
hResult = pWiaDevMgr->EnumDeviceInfo(WIA_DEVINFO_ENUM_LOCAL, &pWiaDevEnum);
if (hResult == S_OK)
{
pWiaDevEnum->GetCount(&ulResult);
pWiaDevEnum->Release();
}

pWiaDevMgr->Release();
}
return ulResult;
}

// 功能: 从 WIA 设备中获取一个图像到文件中
// 返回: 返回 TRUE 表示成功或者用户取消,其它表示失败
// 备注: 如果调用成功,将生成 ptzFileName 指定的图像文件
BOOL WINAPI WiaHelper::GetImage(PCTSTR ptzFileName, HWND hParent)
{
BOOL bResult;
GUID guidFormat;
IWiaDevMgr *pWiaDevMgr;
WCHAR wszFileName[MAX_PATH];

// 初始化变量
bResult = FALSE;

// 检查参数
if (ptzFileName)
{
// 获得接口指针
CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr, (PVOID *) &pWiaDevMgr);
if (pWiaDevMgr)
{
// 获取图像
if (lstrcmpi(ptzFileName + lstrlen(ptzFileName) - 4, TEXT(".jpg")) == 0)
{
guidFormat = WiaImgFmt_JPEG;
}
else
{
guidFormat = WiaImgFmt_BMP;
}

#ifdef _UNICODE
lstrcpy(wszFileName, ptzFileName);
#else // _UNICODE
MultiByteToWideChar(CP_ACP, 0, ptzFileName, -1, wszFileName, MAX_PATH);
#endif // _UNICODE

if (SUCCEEDED(pWiaDevMgr->GetImageDlg(hParent, 0, WIA_DEVICE_DIALOG_SINGLE_IMAGE,
WIA_INTENT_MAXIMIZE_QUALITY, NULL, wszFileName, &guidFormat)))
{
bResult = TRUE;
}


pWiaDevMgr->Release();
}
}

return bResult;
}

// 功能: 从 WIA 设备中获取多个图像,并以消息回调的形式通知父窗口
// 返回: 返回 TRUE 表示成功或者用户取消,其它表示失败
// 备注: 如果调用成功,当图像准备好时,父窗口将到 WM_COMMAND 消息,请参看 IDC_WiaHelper 的说明
BOOL WINAPI WiaHelper::GetImage(HWND hParent)
{
int i;
LONG lWiaItem;
HRESULT hResult;
IWiaItem *pRootItem;
IWiaItem **ppWiaItems;
IWiaDevMgr *pWiaDevMgr;

// 检查参数
if (hParent == NULL)
{
return FALSE;
}

// 获得接口指针
hResult = CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr, (PVOID *) &pWiaDevMgr);
if (hResult == S_OK)
{
// 显示选择设备对话框
hResult = pWiaDevMgr->SelectDeviceDlg(hParent, 0, 0, NULL, &pRootItem);
if (hResult == S_OK)
{
// 显示选择图片对话框
hResult = pRootItem->DeviceDlg(hParent, 0, WIA_INTENT_MAXIMIZE_QUALITY, &lWiaItem, &ppWiaItems);
if (hResult == S_OK)
{
// 发送消息给父窗口,告知图像已准备好
for (i = 0; i < lWiaItem; i++)
{
m_pWiaItem = ppWiaItems[i];
if (SendMessage(hParent, WM_COMMAND, MAKELONG(IDC_WiaHelper, lWiaItem), 0) == S_FALSE)
{
break;
}
}

m_pWiaItem = NULL;

// 释放
for (i = 0; i < lWiaItem; i++)
{
if (ppWiaItems[i])
{
ppWiaItems[i]->Release();
}
}
}

// 释放
pRootItem->Release();
}

// 释放
pWiaDevMgr->Release();
}

return SUCCEEDED(hResult);
}

// 传送图像到文件中
BOOL WINAPI WiaHelper::TransferImage(PCTSTR ptzFileName)
{
BOOL bResult;
GUID guidFormat;
STGMEDIUM stgmMedium;
PROPSPEC prpsProp[2];
PROPVARIANT prpvProp[2];
WCHAR wszFileName[MAX_PATH];
TCHAR tzFileName[MAX_PATH];
IWiaDataTransfer *pWiaDataTransfer;
IWiaPropertyStorage *pWiaPropertyStorage;

// 初始化变量
bResult = FALSE;

// 检查参数
if (m_pWiaItem && ptzFileName)
{
// 获取 IWiaPropertyStorage 接口指针
m_pWiaItem->QueryInterface(IID_IWiaPropertyStorage, (PVOID *) &pWiaPropertyStorage);
if (pWiaPropertyStorage)
{
// 获取文件名,设置文件格式
lstrcpy(tzFileName, ptzFileName);
lstrcat(tzFileName, TEXT("~"));

#ifdef _UNICODE
lstrcpy(wszFileName, tzFileName);
#else // _UNICODE
MultiByteToWideChar(CP_ACP, 0, tzFileName, -1, wszFileName, MAX_PATH);
#endif // _UNICODE

if (lstrcmpi(ptzFileName + lstrlen(ptzFileName) - 4, TEXT(".jpg")) == 0)
{
guidFormat = WiaImgFmt_JPEG;
}
else
{
guidFormat = WiaImgFmt_BMP;
}

// 设置属性参数
prpsProp[0].ulKind = PRSPEC_PROPID;
prpsProp[0].propid = WIA_IPA_FORMAT;
prpsProp[1].ulKind = PRSPEC_PROPID;
prpsProp[1].propid = WIA_IPA_TYMED;

prpvProp[0].vt = VT_CLSID;
prpvProp[0].puuid = &guidFormat;
prpvProp[1].vt = VT_I4;
prpvProp[1].lVal = TYMED_FILE;

// 设置属性
if (pWiaPropertyStorage->WriteMultiple(2, prpsProp, prpvProp, WIA_IPA_FIRST) == S_OK)
{
// 获取 IWiaDataTransfer 接口指针
m_pWiaItem->QueryInterface(IID_IWiaDataTransfer, (PVOID *) &pWiaDataTransfer);
if (pWiaDataTransfer)
{
// 获取图像
stgmMedium.tymed = TYMED_FILE;
stgmMedium.pUnkForRelease = NULL;
stgmMedium.lpszFileName = wszFileName;

// 重名命图像文件
if (pWiaDataTransfer->idtGetData(&stgmMedium, NULL) == S_OK)
{
MoveFile(tzFileName, ptzFileName);
bResult = TRUE;
}

// 释放
ReleaseStgMedium(&stgmMedium);
pWiaDataTransfer->Release();
}
}

// 释放
pWiaPropertyStorage->Release();
}
}

return bResult;
}

4.
It should be pointed out that WIA supported image formats include PNG, JPG, GIF, BMP and TIFF. These are defined by the FormatID Constants in the WIA library.

A good example for Dotnet user is the famous FotoVision project
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/FotoVisionDesktop.asp

5.
Another more straightforward example is

WIA Scripting and .NET
By NETMaster
http://www.codeproject.com/dotnet/wiascriptingdotnet.asp
How to use Windows Image Acquisition on Windows XP. Useful for integrating scanners, digital cameras, webcams and still-video.

Although WIA is initially developed for C/C++ users, I found it is more easy to handle it in Dotnet.



<< Home

This page is powered by Blogger. Isn't yours?