快速推进您的表单处理应用程序开发





0/5 (0投票)
以前,将纸质表单中的数据输入数据库需要大量的软件开发工作。然而,借助 Accusoft Pegasus FormSuite SDK,您可以在几天内准确地从纸质表单收集信息,而不是花费数月时间。
以前,将纸质表单中的数据输入数据库需要大量的软件开发工作。然而,借助 Accusoft Pegasus FormSuite SDK,您可以在几天内准确地从纸质表单收集信息,而不是花费数月时间。
入门
第一步是下载 Accusoft Pegasus FormSuite SDK(包含示例代码)。该套件适用于 ActiveX 和 .NET 开发。我们将使用 C#.NET 来展示如何快速构建一个可用的表单处理应用程序。FormSuite 下载包包含几个组件,它们能够完成表单处理中的所有繁重工作。
- ImagXpress
用于打开和处理文档图像文件的基本图像处理组件,包括显示缩略图和在图像上绘制区域的控件 - TwainPRO 和 ISIS Xpress ImagXpress 中包含的两个组件,可让您轻松控制几乎任何扫描仪。
- FormFix
识别哪个模板与扫描的表单图像匹配,将该图像与模板对齐,移除空白表单,提取用于识别的区域,并执行复选框识别 (OMR)。 - ScanFix
提供丰富的图像清理功能,如校正倾斜、去噪等,以提高扫描图像的质量。 - SmartZone OCR/ICR 识别 FormFix 传递的字段中的机器打印文本或手写文本。
- FormDirector
充当“交通警察”,方便地在上述各种表单处理组件之间传递数据。 - FormAssist
一个示例应用程序,展示了如何将上述所有组件(扫描部分除外)组合在一起,构建一个完整的表单处理应用程序。
我们将重点关注 FormAssist 应用程序代码,它经常被构建自己表单处理系统的开发人员用作起点。通过移除任何不需要的功能并添加自己的批量处理功能,而不是从一个空项目开始,可以节省大量时间。FormAssist 的完整源代码正是为此目的提供的。FormAssist 使用的所有组件都通过 FormSuite SDK 安装程序进行安装。整个应用程序都可以通过试用许可进行编译和运行——只要它未获得许可,在访问各种组件时仍会弹出提醒屏幕。
安装 FormSuite 后,请从“开始”菜单导航到:所有程序 | Pegasus Imaging | FormSuite | Samples | .NET | C# | FormAssist Demo。此链接将启动 Visual Studio,并加载 FormAssist 项目。FormAssist 演示了完整的表单处理解决方案所需的两个重要且独立的功能。首先,它允许您构建一组 表单模板,用于识别传入图像匹配的表单以及如何处理它。图像将自动缩放到匹配的模板表单并对齐,即使它是颠倒的。对于每个模板表单,您可以精确定义每个可变数据字段(或区域)的位置以及您希望如何处理它。
第二部分是将扫描的表单批量运行匹配、对齐、提取和识别步骤的生产过程。在这里,我们将不涉及扫描步骤(您将使用 TwainPRO 或 ISIS Xpress 从物理文档创建表单图像),以及将识别的数据实际传递到数据库。这两个组件都提供了出色的文档扫描示例,而数据库访问则高度依赖于您的特定需求。
构建表单模板
因此,让我们创建一个新的表单集并将我们的第一个表单模板添加到其中。要编译和运行项目,您只需启动调试(按 F5)。FormAssist 启动后,单击“文件”|“新建表单集”。
在本文中,我们将重点介绍处理表单的代码部分,而忽略 FormAssist 示例中的大量用户界面代码、异常处理和其他细节。创建新表单集(位于 FormAssist 中)的重要代码部分如下所示。
myFormSet = new FormDirector.FormSetFile(myFormDirector);
myFormSet.Name = "NewFormSet";
myFormSet.Filename = "NewFormSet.frs";
请注意 FormDirector 被用作所有表单以及(正如我们将很快看到的那样)将在处理过程中所需的字段信息的中央存储库和控制器。FormDirector 使用一种简单的 XML 格式(已在 SDK 中完全文档化),用于记录描述整个表单处理过程的许多设置。在处理过程中,这些信息用于控制所使用的所有组件的所有功能和设置。
当然,FormAssist 允许您重命名表单集,但我们将保留默认名称,因为我们只需要一个表单集来进行演示。在此示例中,我将一个简单的空白 W4 表单扫描为 TIFF 文件。
除非您在同一家公司工作了 20 年,否则您可能还记得填写过其中之一。通常,您希望将表单保存为 TIFF 格式,作为位图 Group 4 图像,因为它在文件尺寸非常小的情况下提供无损压缩。以下代码概述了将表单添加到表单集的过程。
列表 1:将表单添加到表单集
//always Deskew the template image prior to storage
ScanFix.Enhancements enhancements;
enhancements = new ScanFix.Enhancements();
enhancements.Options.Add(new ScanFix.DeskewOptions());
myScanFix.FromHdib(imageXView1.Image.ToHdib(false));
myScanFix.ExecuteEnhancements(enhancements);
//add the FileName as new Form in the Form Set
string newfileName = nodeName + ".frd";
FormDirector.FormDefinitionFile).Filename.ToUpper() == newfileName.ToUpper())
FormDirector.FormDefinitionFile myFormDef = new
FormDirector.FormDefinitionFile(myFormSet);
FormDirector.TemplateImage myTemplateImage = new
FormDirector.TemplateImage(myFormDirector);
// We’ll store the original Image filename in OtherDataItems,
// so we can show it later as a property
FormDirector.DataItem myDataItem = new FormDirector.DataItem();
myDataItem.Type = PicConst.OriginalImage;
myDataItem.Content = formName;
myFormDef.OtherDataItems.Clear();
myFormDef.OtherDataItems.Add(myDataItem);
//take name from node in the tree
myFormDef.Name = nodeName;
myFormDef.Filename = newfileName;
// get the image from the ImagXpress view and save
myTemplateImage.Hdib = imageXView1.Image.ToHdib(false);
myFormDef.TemplateImages.Add(myTemplateImage);
myFormSet.FormDefinitions.Add(myFormDef);
dirtyFormSet = true; // remember this Form Set is not empty
即使我们在示例表单集中只需要一个表单,该模板仍将用于对传入图像进行对齐,移除空白表单部分的图像,并识别将发送进行识别的可变数据区域。当然,您的表单集中也可以包含许多不同的表单,在这种情况下,传入的图像将与每个表单进行比较,并匹配最合适的模板,或者被拒绝。
定义识别区域
我们可以定义三种类型的区域用于识别:OCR 用于机器打印文本,ICR 用于手写文本,OMR 用于标记识别。第四种称为“剪辑”的区域类型用于将图像的选定部分传递给任何外部进程。FormAssist 包含一个拖放用户界面,用于在模板表单上绘制这些区域,并详细定义如何处理每个区域。处理过程可以包括 ScanFix 功能(如校正倾斜、去噪、膨胀、腐蚀和字符平滑)、Dropout 参数(包括 dropout 方法和错位设置),以及识别的细节(ICR/OCR 的字符集、OMR 的标记识别阈值,以及许多其他)。
首先,我们将选择 OCR 字段按钮(左侧,包含 ABC,如下所示),并在表单“2009”周围绘制一个 OCR 区域。FormAssist 使用浅蓝色背景来快速识别表单上的 OCR 区域。我们将单击并拖动一个矩形,该矩形包含完整的文本,并带有至少几像素的边距以允许进行校准变化。
我将此字段命名为“Year”。如果我们选择底部的 ScanFix 选项卡,我们可以看到可用于此字段的各种图像清理功能。每个功能,例如当前高亮的 Deskew 功能,都有各种参数可以为该字段单独设置。我选择了 deskew 和 despeckle,使用上下箭头将它们移到该顺序,并保留了两者的默认设置。
现在点击底部的 Dropout 选项卡,我将其设置为 Clip 而不是 Dropout。这将保留输入表单中的此区域,而不是将其移除,以便我可以对其进行 OCR 并确定此 W-4 表单代表哪个年份。
接下来,我可以点击底部的 OCR 选项卡,并设置任何可能需要的特定参数来优化该区域的识别。
在这种情况下,我可以将识别字符集限制为我预期的三个字符。假设我知道我只想处理 2007、2008 和 2009 年的 W-4 表单。我可以将识别限制为仅数字 0、2、7、8 和 9。这将使我对在该区域找到的任何内容都是这些字符之一具有最高的置信度。如果我愿意,我可以为每个字段创建单独的自定义字符集。
一旦我们选择了要对第一个 OCR 区域执行的各种处理类型,就可以将其存储到 FormDirector 中。
列表 2:将 OCR 字段添加到表单
//Add a Form Field
FormDirector.FormDefinition mycurrentFormDef;
mycurrentFormDef = myFormSet.FormDefinitions[idxFormDef];
FormDirector.Field myField = new FormDirector.Field();
using (FormFix.DropOutProcessor dropProc = new
FormFix.DropOutProcessor(myFormFix))
{
FormDirector.DataItem myDataItem = new FormDirector.DataItem();
//field type in OtherDataItem
myDataItem.Type = PicConst.Type;
//add Field Type XML
myDataItem.Content = PicConst.OcrFieldType;
myField.OtherDataItems.Add(myDataItem);
//create the Dropout XML
dropProc.DropOutMethod = FormFix.DropOutMethod.DropOut;
dropProc.AllowableMisRegistration = (int)numericUpDownOCRMisReg.Value;
dropProc.PerformReconstruction = checkBoxOCRReconstruc.Checked;
tmpDropString = dropProc.WriteToStream();
//create & add the OCR XML
mySmartZone.Reader.Classifier = SmartZone.Classifier.MachinePrint;
mySmartZone.Reader.MinimumCharacterConfidence =
(int)numericUpDownOCRConf.Value;
//set the default rejection character
string s = textBoxRejection.Text;
if (s == "")
{
s = "~";
textBoxRejection.Text = s;
}
mySmartZone.Reader.RejectionCharacter = s[0];
//set all OCR recognition parameters from on-screen settings
mySmartZone.Reader.Area = new Rectangle(0,0,0,0);
mySmartZone.Reader.CharacterSet = SmartZone.CharacterSet.AllCharacters;
mySmartZone.Reader.CharacterSet.Language =
SmartZone.Language.WesternEuropean;
mySmartZone.Reader.Segmentation.DetectSpaces =
checkBoxOCRSpaces.Checked;
mySmartZone.Reader.Segmentation.MaximumBlobSize =
(int)numericUpDownOCRBlock.Value;
mySmartZone.Reader.Segmentation.MinimumTextLineHeight =
(int)numericUpDownOCRLine.Value;
mySmartZone.Reader.Segmentation.MultipleTextLines =
checkBoxOCRMultiple.Checked;
mySmartZone.Reader.Segmentation.SplitMergedChars =
checkBoxOCRMerged.Checked;
mySmartZone.Reader.Segmentation.SplitOverlappingChars =
checkBoxOCROverlap.Checked;
// add the OCR data item to FormDirector
tmpOCRString = mySmartZone.WriteToStream();
FormDirector.DataItem myOCRDataItem = new FormDirector.DataItem();
myOCRDataItem.Type = PicConst.RecognitionOp;
myOCRDataItem.Content = tmpOCRString;
myField.Operations.Add(myOCRDataItem);
//add Dropout xml
myField.Construction.Type = PicConst.DropoutOp;
myField.Construction.Content = tmpDropString;
}
// add the name of the field (make it unique using a sequence number)
myField.Name = "Field " + nodeFieldSequenceNumber.ToString();
fieldNode.Text = "Field " + nodeFieldSequenceNumber.ToString();
// actually add the field to the form definition file
mycurrentFormDef.Fields.Add(myField);
现在我们可以添加第一个员工信息收集字段,这是一个 ICR 区域,将用于收集提交了“扣缴津贴表”的新员工的名字和中间名首字母。FormAssist 使用橙色背景来快速识别表单上的 ICR 字段。我们只需单击第二个 ABC 框,然后在包含该数据的区域周围绘制一个矩形。
请注意,引导文本“请填写您的名字和中间名首字母。”已完全包含在区域内!如果将此文本发送进行 ICR,可能会导致大量不相关结果。这就是表单 Dropout(在下面的 Dropout 选项卡中选择)的魔力所在。选择此选项后,在将字段发送进行识别之前,模板表单上出现的任何内容都将被移除。Perform Reconstruction 参数应始终设置,以在 dropout 模板后生成清晰的结果,仅留下变量数据(新员工在空白表单上填写的信息)在输入图像中。
由于名字通常不包含数字,我们可以通过将此字段的 ICR 字符集限制为“仅字母”来提高结果,如下所示。下拉列表中也显示了其他常见的字符集。如前所述,Custom 设置用于选择任意所需的字符组合进行识别。
其余的 ICR 字段以相同的方式创建。例如,SSN 字段只能包含数字,而 Exempt 字段只能包含字母 E、X、M、P 和 T。添加此 ICR 字段的实际代码与添加上述 OCR 字段的代码非常相似。
我们将输入的最后一类字段是 OMR 复选框。这些字段在 FormAssist 中绘制并显示为浅绿色矩形。同样,您只需选择 OMR 绘图工具(带有绿色复选标记),然后在表单上绘制矩形。
我们不会详细介绍所有细节,但在这里您有多种选项可以定义标记以进行识别,包括标记阈值和返回值、各种气泡形状,以及将标记的数组(行和列)描述为单个对象的能力。OMR 功能还可以用于检测签名是否存在,如本表单所示。下面是添加 OMR 字段的相关代码快照。
列表 3:将 OMR 字段添加到表单
//Add an OMR Field
resetProperties(propType.OMR);
myDataItem.Content = PicConst.OmrFieldType;
myField.OtherDataItems.Add(myDataItem);
//create the Dropout XML
dropProc.DropOutMethod = FormFix.DropOutMethod.Clip;
dropProc.AllowableMisRegistration = (int)numericUpDownOMRMisReg.Value;
dropProc.PerformReconstruction = checkBoxOMRReconstruc.Checked;
tmpDropString = dropProc.WriteToStream();
//create & add the OMR XML
using (FormFix.OmrProcessor omrProc = new FormFix.OmrProcessor(myFormFix))
{
omrProc.Area = new Rectangle(0,0,0,0);
omrProc.AnalysisComparisonMethod = FormFix.OmrAnalysisComparisonMethod.None;
omrProc.MultiSegmentReadDirection =
FormFix.OmrMultiSegmentReadDirection.Normal;
omrProc.MarkedBubbleThreshold = (int)numericUpDownOMRMBubble.Value;
omrProc.UnmarkedBubbleThreshold = (int)numericUpDownOMRUBubble.Value;
omrProc.UnmarkedSegmentResult = textBoxOMRUSegment.Text;
omrProc.MarkScheme = FormFix.OmrMarkScheme.SingleMark;
omrProc.MultiMarkDelimiter = textBoxOMRDelim.Text;
omrProc.BubbleShape = FormFix.OmrBubbleShape.Circle;
omrProc.Orientation = FormFix.OmrOrientation.HorizontalSegments;
tmpOMRString = omrProc.WriteToStream();
FormDirector.DataItem myOMRDataItem = new FormDirector.DataItem();
myOMRDataItem.Type = PicConst.OmrOp;
myOMRDataItem.Content = tmpOMRString;
myField.Operations.Add(myOMRDataItem);
}
// add the name of the field (make it unique using a sequence number)
myField.Name = "Field " + nodeFieldSequenceNumber.ToString();
fieldNode.Text = "Field " + nodeFieldSequenceNumber.ToString();
// actually add the field to the form definition file
mycurrentFormDef.Fields.Add(myField);
此表单上的其余字段与我们刚刚定义的示例 OCR、ICR 和 OMR 字段基本相同。在不显示从对话框中获取表单集文件名代码的情况下,以下是保存已完成表单(或在本例中仅一个表单)到表单集文件的核心代码。
列表 4:保存表单集
//Save the current Form Set
int myFormCount = myFormSet.FormDefinitions.Count;
for (int i=0; i<myFormCount; i++)
{
FormDirector.FormDefinitionFile myFormDef =
(myFormSet.FormDefinitions[i] as
FormDirector.FormDefinitionFile);
}
处理实时表单
尽管实际表单的处理通常通过单独的应用程序完成,但 FormAssist 演示允许您手动尝试一些实时图像,以便您可以确认一切设置正确,并微调参数以优化您的结果。演示中的此处理代码可用于创建您自己的生产系统,该系统还可以包括文档扫描子系统,或为操作员手动审查低置信度数据字段。由于每个可疑字段的确切位置都已知,因此构建一个“关键图像”(KFI)用户界面非常容易,该界面会放大图像的正确部分,并允许操作员在数据存储到永久数据库之前输入任何更正。
FormAssist 包含大量可重用代码,您可以使用它们来构建一个生产级处理器,将每个输入图像通过 FormDirector 存储为 XML 流的形式进行表单匹配、dropout、清理和识别操作。以下代码片段旨在演示如何在您的代码中实现许多单个操作,而不是展示完整的解决方案。
列表 5:处理表单 – 图像清理
// Method to process a filled image, first performing image identification and
// alignment, then processing each field on the form if a match was found
public void ProcessImage(System.Drawing.Image inputImage)
// Set the image into a form image
filledImage = FormFix.FormImage.FromBitmap((System.Drawing.Bitmap)inputImage, FF);
filledImage.HorizontalResolution = inputImage.HorizontalResolution;
filledImage.VerticalResolution = inputImage.VerticalResolution;
imageToProcess = filledImage;
// Do the full-page ScanFix enhancements (rather than field cleanup)
ScanFix.Enhancements enhancements;
enhancements = new ScanFix.Enhancements();
// get the XML list of cleanups from FormDirector
enhancements.ReadFromStream(enhancementXML, SF);
// Set the image to be enhanced into ScanFix, then enhance
SF.FromHdib(frmImage.ToHdib(false));
SF.ExecuteEnhancements(enhancements);
// Set the results back into the form image
return FormFix.FormImage.FromHdib(SF.ToHdib(true), true, FF);
列表 6:处理表单 – 表单识别和对齐
// Set up the form identification processor
idProcessor = new FormFix.IdentificationProcessor(FF);
// set up the event handlers for processing the form
idProcessor.ReadChecksum += new
FormFix.ReadChecksumEventHandler(formIdReadChecksum);
idProcessor.ReadDataItem += new FormFix.DataItemEventHandler(formIdReadDataItem);
idProcessor.WriteDataItem += new FormFix.DataItemEventHandler(formIdWriteDataItem);
// get the FormSet setting for FormFix for identifying a form
idProcessor.ReadFromStream(FS.Identification.Content);
idProcessor.MaximumIdentificationBestMatches = maximumBestMatches;
// Add the form models to the identifier
for (int i = 0; i < FS.FormDefinitions.Count; i++)
{
formModel = new FormFix.FormModel(FF);
formModel.Tag = FS.FormDefinitions[i];
formModel.Name = FS.FormDefinitions[i].Name;
formModel.ReadDataItem += new
FormFix.DataItemEventHandler(formModelReadDataItem);
formModel.ReadFormImage += new
FormFix.ReadFormImageEventHandler(formModelReadFormImage);
formModel.ReadChecksum += new
FormFix.ReadChecksumEventHandler(formModelReadChecksum);
formModel.WriteDataItem += new
FormFix.DataItemEventHandler(formModelWriteDataItem);
idProcessor.FormModels.Add(formModel);
}
// Identify the unknown image
idResult = idProcessor.Identify(unknownImage);
if (idResult.State != FormFix.IdentificationState.NoMatchFound)
{
// Match found, align the image
alignedImage = idResult.RegistrationResult.AlignImage(unknownImage);
// After alignment, process each field
// All fields are described in the formDef
formDef = (FormDirector.FormDefinitionFile) idResult.FormModel.Tag;
for (int i = 0; i < formDef.Fields.Count; i++)
{ //processes each field according to formDef instructions
ProcessField(formDef.Fields[i], imageToProcess);
}
列表 7:处理表单 – 字段 Dropout
//for each field, Drop out the form if requested
if (field.Construction.Type == PicConst.DropoutOp)
//create a dropout processor to remove the form from the background
dropOutProcessor = new FormFix.DropOutProcessor(FF);
dropOutProcessor.ReadFromStream(field.Construction.Content);
dropOutProcessor.Area = field.Location;
// Dropout processor is executed against the enhanced or original filled image
if (enhancedImage != null)
dropOutResult = dropOutProcessor.CreateImageOfField(enhancedImage,
idResult.RegistrationResult);
else
dropOutResult = dropOutProcessor.CreateImageOfField(filledImage,
idResult.RegistrationResult);
列表 8:处理表单 – 字段清理和识别
// Next clean up the individual field
ScanFix.Enhancements enhancements;
enhancements = new ScanFix.Enhancements();
enhancements.ReadFromStream(field.Operations[idxOp].Content, SF);
SF.FromHdib(imageToProcess.ToHdib(false));
SF.ExecuteEnhancements(enhancements);
imageToProcess = FormFix.FormImage.FromHdib(SF.ToHdib(true), true, FF);
thisFieldResult.EnhancedImage = imageToProcess;
// If OMR field, do mark recognition
idxOp = field.Operations.GetIndexOfType(PicConst.OmrOp);
if (idxOp >= 0)
if (omrProcessor == null)
omrProcessor = new FormFix.OmrProcessor(FF);
// get the OMR settings from the form definition file for this field
omrProcessor.ReadFromStream(field.Operations[idxOp].Content);
// Depending on the OMR settings from choose the type of comparison
switch (omrProcessor.AnalysisComparisonMethod)
{
case FormFix.OmrAnalysisComparisonMethod.None:
// Use the image clip
omrProcessor.Area = new System.Drawing.Rectangle(0,0,0,0);
imageToOmr = imageToProcess;
break;
case FormFix.OmrAnalysisComparisonMethod.CompareClipToFormModel:
// Use the image clip
omrProcessor.OriginOfClip = new System.Drawing.Point
(field.Location.X, field.Location.Y);
omrProcessor.Area = new System.Drawing.Rectangle(0,0,0,0);
imageToOmr = imageToProcess;
omrProcessor.FormModel = idResult.FormModel;
break;
case FormFix.OmrAnalysisComparisonMethod.CompareFullImageToFormModel:
// Use the aligned image, since we are comparing to the form image
omrProcessor.Area = field.Location;
imageToOmr = alignedImage;
omrProcessor.FormModel = idResult.FormModel;
break;
}
// Perform the OMR
thisFieldResult.OmrResult = omrProcessor.AnalyzeField(imageToOmr);
// Perform OCR for the Field (similar for ICR)
idxOp = field.Operations.GetIndexOfType(PicConst.RecognitionOp);
if (idxOp >= 0)
{
SZ.ReadFromStream(field.Operations[idxOp].Content);
thisFieldResult.OcrResult =
SZ.Reader.AnalyzeField(imageToProcess.ToHdib(false));
}
如果您单击“对齐图像”缩略图,您可以看到 dropout 的确切效果。模板表单从扫描图像中移除后,只有红色显示的元素仍然保留。这包含了最终用于填充数据库的可变数据。
如果您单击“字段结果”选项卡,您可以看到每个区域的实际清理后的图像剪辑。注意引导文本(您的社会安全号码)是如何被完全移除的,从而实现了准确的识别。
结论
FormAssist 中的示例代码对于希望构建从结构化表单中提取数据的应用程序的开发人员来说,有两个重要用途。首先,它可以用于创建表单模板,为您的文档工作流程中使用的每种表单定义识别区域。其次,它也是您自己的表单定义应用程序和自定义批量表单处理解决方案的起点。如前所述,此处显示的代码代表了一些关键步骤。此处可以下载整个示例以及所有引用的组件:下载 Accusoft Pegasus FormSuite SDK
您可以在 www.accusoft.com 找到 Accusoft Pegasus 产品下载和功能。请联系我们 sales@accusoft.com 或 support@accusoft.com 获取更多信息。
关于 Accusoft Pegasus
Accusoft Pegasus 公司成立于 1991 年,原名 Pegasus Imaging,总部位于佛罗里达州坦帕市,是成像软件开发工具包(SDK)和图像查看器最大的供应商。成像技术解决方案包括条形码、压缩、DICOM、编辑、表单处理、OCR、PDF、扫描、视频和查看。技术支持 Microsoft .NET、ActiveX、Silverlight、AJAX、ASP.NET、Windows Workflow 和 Java 环境。支持多种 32 位和 64 位平台,包括 Windows、Windows Mobile、Linux、Sun Solaris、Mac OSX 和 IBM AIX。请访问 http://www.accusoft.com 了解更多信息。