在C#中使用WIA.docx
- 文档编号:3707604
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:17
- 大小:810.04KB
在C#中使用WIA.docx
《在C#中使用WIA.docx》由会员分享,可在线阅读,更多相关《在C#中使用WIA.docx(17页珍藏版)》请在冰豆网上搜索。
在C#中使用WIA
在C#中使用WIA获取扫描仪数据
(一)
WIA(WindowsImageAcquire,最新版本2.0)是Windows中一组从设备中捕获图像的标准API集合,它可以从设备(例如扫描仪、数码相机)中获取静态图像,以及管理这些设备。
它既是API,又是DDI(DeviceDriverInterface)。
因此,只要是满足这个规范的设备,都能够利用WIA直接和应用程序交互,而不是通过驱动。
WIA甚至提供了统一的对话框来获取图片。
WIA是基于Com的,有两种使用方式:
c++:
使用WIA自定义接口
其他:
使用WIAAL(WIAAutomationLayer)。
注:
在WindowsXPsp1以前的版本,WIAAL还不存在,因此第二种方式用的是WIAScriptingModel。
在.Net中使用WIA,我们用的是第二种方法。
接下来做一个简单的图像扫描程序:
界面
新建一个WinForm应用程序,在上面添加一个按钮和一个图片框,点击按钮时启动扫描进程,然后在图片框中显示图像,应用程序界面如下:
使用WIA
VisualStudio2008有一个好处,可以自动装配Com组件,在工程中添加一个WIA的COM引用:
点击确定后,会在工程引用中添加一个WIA.Interop.dll的文件,可以在对象浏览器中查看它:
打开扫描对话框
接下来可以利用WIA来进行扫描了,步骤很简单,首先引用命名空间:
usingWIA;接下来,在button的Click事件中,添加如下代码:
ImageFileimageFile=null;
CommonDialogClasscdc=newWIA.CommonDialogClass();
try
{
imageFile=cdc.ShowAcquireImage(WIA.WiaDeviceType.ScannerDeviceType,
WIA.WiaImageIntent.TextIntent,
WIA.WiaImageBias.MaximizeQuality,
"{00000000-0000-0000-0000-000000000000}",
true,
true,
false);
}
catch(System.Runtime.InteropServices.COMException)
{
imageFile=null;
}WIA会自动弹出标准扫描对话框,进行扫描操作:
获取图像
调用ShowAcquireImage后,扫描后的数据就保存在ImageFile对象里了。
用以下方法读取ImageFile中的数据(该方法很傻很傻……很傻)
if(imageFile!
=null)
{
imageFile.SaveFile(@"c:
\1.bmp");
using(FileStreamstream=newFileStream(@"c:
\1.bmp",FileMode.Open,
FileAccess.Read,FileShare.Read))
{
pictureBox1.Image=Image.FromStream(stream);
}File.Delete(@"c:
\1.bmp");
}结果如下:
在C#中使用WIA获取扫描仪数据(二、WIAAutomationLayer)
前文说过,在WIA2.0里,有一个叫AutomationLayer的东西,来负责WIA和应用程序交互。
既然被命名为Automation了,那么意味着比直接试用WIA接口,WIAAL更容易、更方便。
实际上的确如此。
关于WIAAutomationLayer
文档上说,WIAAutomationLayer是一个高级的,全能的图像操作组件,能为应用程序(例如ASP,C#)提供首尾相连的处理能力。
利用WIAAL,在程序中可以很容易地从诸如数码相机、扫描仪等图像设备中捕获图像,以及进行简单处理(缩放、旋转)。
对象分级结构
WIAAL的对象不多,总的来说分成来两块,第一块是可以被创建的类(例如在c#里我们用关键字new来创建),另一部分是不能被创建的类(在c#里,这些类虽然也有构造函数,不过即使创建了,也没有任何东西),它们必须由第一种类创建。
如下图:
可见,上面有我们熟悉的CommonDialog(在Interop后,这些类后面都加上了Class表示实现,例如CommonDialogClass就是CommonDialog的实现)。
改进的例子
在前面的文章中,我用了一个很“囧”的方法来保存图片,实际上大可不必如此,从上面的关系图可以看到,ImageFile对象有一个Vector的对象,该对象保存了图片的像素值。
修改代码如下:
if(imageFile!
=null)
{
varbuffer=imageFile.FileData.get_BinaryData()asbyte[];
using(MemoryStreamms=newMemoryStream())
{
ms.Write(buffer,0,buffer.Length);
pictureBox1.Image=Image.FromStream(ms);
}
}
在C#中使用WIA获取扫描仪数据(三、利用Filter处理图片)
WIAAutomationLayer不仅能从设备中捕获照片,还能进行简单的处理。
当WIAAutomationLayer从设备中捕获照片,保存为一个ImageFile对象,我们可以通过访问该ImageFile对象来访问照片的属性。
然而,为了保护原来的照片,不能直接通过修改该ImageFile对象的方法修改图片。
代替的方法是,使用ImageProcess和一个或多个Filter对象创建一个副本,修改图片。
代码
以下代码把扫描得到的图片顺时针旋转90度:
if(imageFile!
=null)
{
ImageProcessip=newImageProcessClass();
objectfilterName="RotateFlip";
ObjectpropertyName="RotationAngle";
ObjectpropertyValue=90;
ip.Filters.Add(ip.FilterInfos.get_Item(reffilterName).FilterID,0);
ip.Filters[1].Properties.get_Item(refpropertyName).set_Value(refpropertyValue);
varbuffer=ip.Apply(imageFile).FileData.get_BinaryData()asbyte[];
using(MemoryStreamms=newMemoryStream())
{
ms.Write(buffer,0,buffer.Length);
pictureBox1.Image=Image.FromStream(ms);
}
}
FilterID
以下是可用的FilterID
RotateFlip
以90度增量旋转,以及水平或垂直翻转。
RotationAngle-如果希望旋转,可将RotationAngle属性设置为90、180或270,
否则设置为0[默认值]
FlipHorizontal-如果希望水平翻转图像,可将FlipHorizontal属性设置为True,
否则设置为False[默认值]
FlipVertical-如果希望垂直翻转图像,可将FlipVertical属性设置为True,
否则设置为False[默认值]
FrameIndex-如果希望修改除ActiveFrame之外的帧,
可将FrameIndex属性设置为帧的索引,
否则设置为0[默认值]Crop
以指定的左、右、上、下边距裁剪图像。
Left-如果希望沿左侧裁剪,可将Left属性设置为左边距(单位为像素),
否则设置为0[默认值]
Top-如果希望沿顶部裁剪,可将Top属性设置为上边距(单位为像素),
否则设置为0[默认值]
Right-如果希望沿右侧裁剪,可将Right属性设置为右边距(单位为像素),
否则设置为0[默认值]
Bottom-如果希望沿底部裁剪,可将Bottom属性设置为下边距(单位为像素),
否则设置为0[默认值]
FrameIndex-如果希望修改除ActiveFrame之外的帧,
可将FrameIndex属性设置为帧的索引,否则设置为0[默认值]Scale
将图像缩放到指定的最大宽度和最大高度,如有必要,保留纵横比。
MaximumWidth-将MaximumWidth属性设置为希望将图像缩放到的宽度(单位为像素)。
MaximumHeight-将MaximumHeight属性设置为希望将图像缩放到的高度(单位为像素)。
PreserveAspectRatio-如果希望保持图像当前的纵横比,可将PreserveAspectRatio属性设置为True[默认值],
否则设置为False,图像将被拉伸到MaximumWidth和MaximumHeight
FrameIndex-如果希望修改除ActiveFrame之外的帧,可将FrameIndex属性设置为帧的索引,
否则设置为0[默认值]Stamp
在指定的Left和Top坐标处标记指定的ImageFile。
ImageFile-将ImageFile属性设置为希望标记的ImageFile对象
Left-将Left属性设置为希望将ImageFile标记到的从左侧开始的偏移(单位为像素)[默认值为0]
Top-将Top属性设置为希望将ImageFile标记到的从顶部开始的偏移(单位为像素)[默认值为0]
FrameIndex-如果希望修改除ActiveFrame之外的帧,可将FrameIndex属性设置为帧的索引,否则设置为0[默认值]Exif
添加/删除指定的Exif属性。
Remove-如果希望删除指定的Exif属性,可将Remove属性设置为True,否则设置为False[默认值]以添加
指定的exif属性
ID-将ID属性设置为希望添加或删除的PropertyID
Type-设置Type属性以指示希望添加的Exif属性的WiaImagePropertyType(对于删除则忽略)
Value-将Value属性设置为希望添加的Exif属性的值(对于删除则忽略)
FrameIndex-如果希望修改除ActiveFrame之外的帧,可将FrameIndex属性设置为帧的索引,否则设置为0[默认值]Frame
Remove-如果希望删除指定的FrameIndex,可将Remove属性设置为True,
否则设置为False[默认值]以在指定的FrameIndex之前插入ImageFile
ImageFile-将ImageFile属性设置为希望添加其ActiveFrame的ImageFile对象(对于删除则忽略)
FrameIndex-对于删除,将FrameIndex属性设置为希望删除的帧的索引,
对于添加,将FrameIndex设置为要在其之前插入ImageFile的帧的索引,否则设置为0[默认值]
以从指定的ImageFile追加帧ARGB
ARGBData-将ARGBData属性设置为表示指定FrameIndex的ARGB数据的Longs的矢量(宽度和高度必须匹配)
FrameIndex-将FrameIndex属性设置为希望修改其ARGB数据的帧的索引,否则设置为0[默认值]以修改ActiveFrameConvert
将得到的ImageFile转换为指定的类型。
FormatID-将FormatID属性设置为所需支持的光栅图像格式,当前可选择的格式有wiaFormatBMP、
wiaFormatPNG、wiaFormatGIF、wiaFormatJPEG或wiaFormatTIFF
Quality-对于JPEG文件,可将Quality属性设置为从1到100[默认值]之间的任何值,以指定JPEG压缩的质量
Compression-对于TIFF文件,可将Compression属性设置为CCITT3、CCITT4、RLE或Uncompressed以指定压缩方案,
否则可设置为LZW[默认值]小节
总的来说,在c#中利用AutomationLayer中的Filter非常麻烦(要写一堆Object),这些简单的图像处理操作还不如用GDI+来实现。
在C#中使用WIA获取扫描仪数据(四、通过编程方式扫描图像)
在前面几节,我通过调用CommonDialog对象的ShowAcquireImage方法来扫描图像,这是一个弹出选择设备对话框,让用户自己扫描的过程。
有时候,我们不想把过程弄得那么复杂,只想用户点击按钮后,自动开始扫描。
本节我将尝试这个需求。
WIAAL模型
在开始代码前,再回顾以下WIAAL模型,这里选取其中的一小部分:
从上图不难想象,一台扫描仪,实际上就是一个Device对象,因此,我们可以通过DeviceManager来“获取”这台设备的“引用”,然后通过得到的Device对象,执行相应的扫描工作。
从而跳过了使用ShowAcquireImage方法带来的一系列“多余的鼠标操作问题”。
获取Device对象
按照上面思路,首先需要建立一个DeviceManager对象:
DeviceManagermanager=newDeviceManagerClass();然后获取Device对象,在这里,我假设我的电脑上只有一台扫描仪,因此不做诸如“判断使用哪台扫描仪进行扫描”之类的操作。
Devicedevice=null;
foreach(DeviceInfoinfoinmanager.DeviceInfos)
{
if(info.Type!
=WiaDeviceType.ScannerDeviceType)continue;
device=info.Connect();
break;
}扫描图像
WIA把Device设备的图像数据看做一个个Item对象,可以通过方法GetItem(ItemID)来实现。
不过,对于扫描仪做种东西,和数码相机不同,一般只有一个Item对象,因此可以简单的使用数组的方法(注意:
index是从1开始的,而不是从0):
Itemitem=device.Items[1];最后,调用CommonDialog的ShowTransfer方法,用一个进度条,来显示扫描过程:
CommonDialogClasscdc=newWIA.CommonDialogClass();
ImageFileimageFile=cdc.ShowTransfer(item,
"{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}",
true)asImageFile;
if(imageFile!
=null)
{
varbuffer=imageFile.FileData.get_BinaryData()asbyte[];
using(MemoryStreamms=newMemoryStream())
{
ms.Write(buffer,0,buffer.Length);
pictureBox1.Image=Image.FromStream(ms);
}
}关于ShowTransfer方法
CommonDialog的ShowTransfer方法,实际上就是ShowAcquireImage方法的最后一个步骤,显示一个获取图片的进度条:
声明如下:
publicvirtualobjectShowTransfer(ItemItem,stringFormatID,boolCancelError);对于第二个参数,FormatID,可以使用以下值:
wiaFormatBMP({B96B3CAB-0728-11D3-9D7B-0000F81EF32E})
wiaFormatPNG({B96B3CAF-0728-11D3-9D7B-0000F81EF32E})
wiaFormatGIF({B96B3CB0-0728-11D3-9D7B-0000F81EF32E})
wiaFormatJPEG({B96B3CAE-0728-11D3-9D7B-0000F81EF32E})
wiaFormatTIFF({B96B3CB1-0728-11D3-9D7B-0000F81EF32E})
在C#中使用WIA获取扫描仪数据(五、注册事件)
好了,现在我们能在c#里通过编程扫描图像了。
还不满足?
对,在前面的例子里,需要扫描的时候总是要按下一个扫描按钮,既傻又费事。
现在的扫描仪,上面往往会多几个额外的按钮用来和用户交互,例如我是用的HPG2410上就有两个按钮:
扫描及复制。
那么,能不能用这两个按钮来代替程序里的那个难看的按钮呢?
注意左上角那个难看的按钮了吗?
在WIAAL里,我们可以同过注册设备事件,监听事件等方式和设备上的按钮交互。
注册事件
还记得我们在上节提到的DeviceManager对象吗?
MSDN官方文档描述:
TheMicrosoftWindowsImageAcquisition(WIA)DeviceManagerisanextensionoftheStillImage(STI)EventMonitor.TheWIADeviceManagerprovidesobjects,methods,andinterfacesforthefollowing:
Installingdevices
Enumeratingdevices
Queryingpropertiesofinstalleddevices
Creatingdeviceobjects
Monitoringdeviceevents
Acquiringimages
Registeringdestinationapplications.
和传统.Net编程不同,WIA的事件,需要先通过DeviceManager的RegisterEvent的方法注册,才能使用。
RegisterEvent定义如下:
voidRegisterEvent(stringEventID,stringDeviceID);其中,EventID是事件的GUID,DeviceID是扫描仪的GUID。
在类EventID里,WIA定义了几种基本的事件类型,从定义上不难理解这些ID的所代表的具体事件:
publicconststringwiaEventDeviceConnected="{A28BBADE-64B6-11D2-A231-00C04FA31809}";
publicconststringwiaEventDeviceDisconnected="{143E4E83-6497-11D2-A231-00C04FA31809}";
publicconststringwiaEventItemCreated="{4C8F4EF5-E14F-11D2-B326-00C04F68CE61}";
publicconststringwiaEventItemDeleted="{1D22A559-E14F-11D2-B326-00C04F68CE61}";
publicconststringwiaEventScanEmailImage="{C686DCEE-54F2-419E-9A27-2FC7F2E98F9E}";
publicconststringwiaEventScanFaxImage="{C00EB793-8C6E-11D2-977A-0000F87A926F}";
publicconststringwiaEventScanFilmImage="{9B2B662C-6185-438C-B68B-E39EE25E71CB}";
publicconststringwiaEventScanImage="{A6C5A715-8C6E-11D2-977A-0000F87A926F}";
publicconststringwiaEventScanImage2="{FC4767C1-C8B3-48A2-9CFA-2E90CB3D3590}";
publicconststringwiaEventScanImage3="{154E27BE-B617-4653-ACC5-0FD7BD4C65CE}";
publicconststringwiaEventScanImage4="{A65B704A-7F3C-4447-A75D-8A26DFCA1FDF}";
publicconststringwiaEventScanOCRImage="{9D095B89-37D6-4877-AFED-62A297DC6DBE}";
publicconststringwiaEventScanPrintImage="{B441F425-8C6E-11D2-977A-0000F87A926F}";例如,我们可以使用以下来吗来注册一个事件,并监听它:
manager.RegisterEvent(EventID.wiaEventScanImage,device.DeviceID);
manager.OnEvent+=(eventID,deviceID,itemID)=>
{
//…………
}枚举设备事件
如果你向我这般,兴冲冲地在OnEvent里加入扫描处理逻辑,然后按下HPG2410上的扫描按钮,你一定会像我一样,在漫长的等待中渐渐失望:
扫描仪根本没有按我所想的那样扫描图片。
也就是说,wiaEventScanImage这个事件根本不起作用。
幸好能够通过Device类来枚举设备支持的事件,我写了以下一段代码:
Console.WriteLine("Events:
");
foreach(DeviceEventeveindevice.Events)
{
Console.WriteLine("{0}:
{1}:
{2}",eve.EventID,eve.Name,eve.Description);
}运行后,发现该扫描仪仅仅支持wiaEventDeviceConnec
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 使用 WIA