Windows Server 2008 R2 中的手写识别

Windows Server 2008 R2 支持服务器端手写识别。 服务器端识别允许服务器识别来自网页上的笔输入的内容。 当网络上的用户将指定使用自定义字典解释的术语时,这尤其有用。 例如,如果你有一个医疗应用程序查询服务器数据库的患者姓名,则可以将这些名称添加到另一个数据库,该数据库将在从手写的 Silverlight 表单执行搜索时交叉引用。

为服务器设置Server-Side识别

应遵循以下步骤来设置服务器端识别。

  • 安装墨迹和手写服务
  • 安装对 Web 服务器的支持 (IIS) 和应用程序服务器
  • 启用桌面体验角色
  • 启动平板电脑输入服务

安装墨迹和手写服务

若要安装 Ink 和手写服务,请单击“快速启动”托盘中的服务器管理器图标,打开服务器管理器。 在 “功能 ”菜单中,单击“ 添加功能”。 请确保选中“墨迹和手写服务检查框。 下图显示了 “选择功能 ”对话框,其中选择了 “墨迹和手写服务 ”。

选中了墨迹和手写服务检查框的“选择功能”对话框
选中了墨迹和手写服务检查框的“选择功能”对话框

对 Web 服务器 (IIS) 和应用程序服务器的安装支持

像第一步一样打开服务器管理器。 接下来,需要添加 Web 服务器 (IIS) 和应用程序服务器角色。 在 “角色 ”菜单中,单击“ 添加角色”。 此时将显示“添加角色”向导。 单击“下一步” 。 确保选择了 “应用程序服务器 ”和“ Web 服务器 (IIS) ”。 下图显示了“ 选择服务器角色 ”对话框,其中选择了 “Web 服务器 (IIS) 应用程序服务器 角色”。

“选择服务器角色”对话框,其中选择了 Web 服务器 (iis) 和应用程序服务器角色
“选择服务器角色”对话框,其中选择了 Web 服务器 (iis) 和应用程序服务器角色

选择“ 应用程序服务器”时,系统会要求你安装 ASP.NET 框架。 单击“ 添加所需功能 ”按钮。 单击“ 下一步”后,将显示概述对话框;单击“ 下一步”。 “ 选择角色服务 ”对话框现在应可用。 确保已选择 “Web 服务器 (IIS) ”。 下图显示了“ 选择角色服务 ”对话框,其中启用了 Web 服务器 (IIS) 。

启用了 web 服务器 (iis) 的“选择角色服务”对话框
启用了 web 服务器 (iis) 的“选择角色服务”对话框

单击“下一步” 。 此时会显示“概述”对话框;再次单击“ 下一步 ”。 现在会显示一个页面,其中提供了 Web 服务器 (IIS) 角色的选项。 单击“下一步” 。 在下一页上, “安装 ”按钮将变为活动状态。 单击“ 安装 ”,将安装对 Web 服务器的支持 (IIS) 和应用程序服务器。

启用桌面体验角色

若要启用桌面体验,请单击“开始”,单击“管理工具”,然后单击“服务器管理器”。 选择“ 添加服务 ”,然后选择 “桌面体验 服务”。 下图显示了已安装桌面体验项的“ 选择功能 ”对话框。

“选择功能”对话框,其中选择了“桌面体验服务”
“选择功能”对话框,其中选择了“桌面体验服务”

单击“ 下一步 ”安装桌面体验。

启动平板电脑服务

安装桌面体验服务后,平板电脑电脑输入服务将显示在 “服务 ”菜单中。 若要访问“ 服务 ”菜单,请单击“ 开始”,单击“ 管理工具”,然后单击“ 服务”。 若要启动该服务,请右键单击“ 平板电脑输入服务 ”,然后单击“ 启动”。 下图显示了启动平板电脑输入服务的“ 服务 ”菜单。

启动平板电脑输入服务的服务菜单
启动平板电脑输入服务的服务菜单

使用 Silverlight 执行Server-Side识别

本部分介绍如何创建使用 Silverlight 捕获手写输入的 Web 应用程序。 使用以下步骤在 Visual Studio 2008 中对识别器进行编程。

  • 安装并更新 Visual Studio 2008 以添加对 Silverlight 的支持。
  • 在 Visual Studio 2008 中创建新的 Silverlight 项目。
  • 向项目添加必要的服务引用。
  • 创建用于墨迹识别的 Silverlight WCF 服务。
  • 将服务引用添加到客户端项目。
  • 将 InkCollector 类添加到 InkRecognition 项目。
  • 从客户端配置中删除安全传输指令

安装并更新 Visual Studio 2008 以添加对 Silverlight 的支持

在开始之前,必须在 Windows Server 2008 R2 服务器上执行以下步骤。

安装这些应用程序和更新后,即可创建服务器端识别 Web 应用程序。

在 Visual Studio 2008 中创建新的 Silverlight Web 项目

在“ 文件 ”菜单中,单击“ 新建项目”。 从 Visual C# 项目列表中选择 Silverlight 应用程序模板。 将项目命名为 InkRecognition,然后单击“ 确定”。 下图显示了已选择并命名为 InkRecognition 的 C# Silverlight 项目。

选择了 c# silverlight 项目,名称为 inkrecognition
选择了 c# silverlight 项目,名称为 inkrecognition

单击“ 确定”后,会显示一个对话框,提示你向项目添加 Silverlight 应用程序。 选择“ 将新的 ASP.NET Web 项目添加到托管 Silverlight 的解决方案” ,然后单击“ 确定”。 下图显示了在单击“ 确定”之前如何设置示例项目。

提示将 silverlight 应用程序添加到项目的对话框
提示将 silverlight 应用程序添加到项目的对话框

向项目添加必要的服务引用

现在,你已将泛型 Silverlight 客户端项目 (InkRecognition) ,并在解决方案中设置了一个 Web 项目 (InkRecognition.Web) 。 项目将在 Page.xaml 和 Default.aspx 打开的情况下打开。 关闭这些窗口,通过右键单击 InkRecognition 项目中的 references 文件夹并选择“添加引用”,将 System.Runtime.Serialization 和 System.ServiceModel 引用添加到 InkRecognition 项目。 下图显示了选择了必要引用的对话框。

“添加引用”对话框,其中选择了 system.runtime.serialization 和 system.servicemodel
“添加引用”对话框,其中选择了 system.runtime.serialization 和 system.servicemodel

接下来,需要将 System.ServiceModel 和 Microsoft.Ink 引用添加到 InkRecognition.Web 项目。 默认情况下,Microsoft.Ink 引用不会显示在 .NET 引用中,因此请在 Windows 文件夹中搜索Microsoft.Ink.dll。 找到 DLL 后,将程序集添加到项目引用:选择“ 浏览 ”选项卡,更改为包含Microsoft.Ink.dll的文件夹,选择“Microsoft.Ink.dll”,然后单击“ 确定”。 下图显示了 Windows 资源管理器中项目的解决方案,其中添加了所有引用程序集。

windows 资源管理器中添加了所有引用程序集的 inkrecognition 项目
windows 资源管理器中添加了所有引用程序集的 inkrecognition 项目

创建用于墨迹识别的 Silverlight WCF 服务

接下来,将向项目添加用于墨迹识别的 WCF 服务。 右键单击 InkRecognition.Web 项目,单击“ 添加”,然后单击“ 新建项”。 选择 WCF Silverlight 服务模板,将名称更改为 InkRecogitionService,然后单击“ 添加”。 下图显示了“ 添加新项 ”对话框,其中已选择并命名了 Silverlight WCF 服务。

选中并命名了 silverlight wcf 服务的“添加新项”对话框
选中并命名了 silverlight wcf 服务的“添加新项”对话框

添加 WCF Silverlight 服务后,将打开服务代码 InkRecognitionService.cs。 将服务代码替换为以下代码。

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Collections.Generic;
using System.Text;
using Microsoft.Ink;

namespace InkRecognition.Web
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class InkRecognitionService
    {
        [OperationContract]
        public string[] Recognize(int[][] packets)
        {
            // Deserialize ink.
            Ink ink = new Ink();
            Tablet tablet = new Tablets().DefaultTablet;
            TabletPropertyDescriptionCollection desc = new TabletPropertyDescriptionCollection();
            desc.Add(new TabletPropertyDescription(PacketProperty.X, tablet.GetPropertyMetrics(PacketProperty.X)));
            desc.Add(new TabletPropertyDescription(PacketProperty.Y, tablet.GetPropertyMetrics(PacketProperty.Y)));
            int numOfStrokes = packets.GetUpperBound(0) + 1;
            for (int i = 0; i < numOfStrokes; i++)
            {
                ink.CreateStroke(packets[i], desc);
            }

            // Recognize ink.
            RecognitionStatus recoStatus;
            RecognizerContext recoContext = new RecognizerContext();
            recoContext.RecognitionFlags = RecognitionModes.LineMode | RecognitionModes.TopInkBreaksOnly;
            recoContext.Strokes = ink.Strokes;
            RecognitionResult recoResult = recoContext.Recognize(out recoStatus);
            RecognitionAlternates alternates = recoResult.GetAlternatesFromSelection();
            string[] results = new string[alternates.Count];
            for (int i = 0; i < alternates.Count; i++)
            {
                results[i] = alternates[i].ToString();
            }

            // Send results to client.
            return results;
        }
    }
}

将服务引用添加到客户端项目

现在,你已拥有用于 InkRecognition 的 Silverlight WCF 服务,你将从客户端应用程序使用该服务。 右键单击 InkRecognition 项目,然后选择“ 添加服务引用”。 在出现的“ 添加服务引用 ”对话框中,选择“ 发现 ”以发现当前解决方案中的服务。 InkRecognitionService 将显示在“服务”窗格中。 从“服务”窗格中双击 InkRecognitionService,将命名空间更改为 InkRecognitionServiceReference,然后单击“ 确定”。 下图显示了“ 添加服务引用 ”对话框,其中选择了 InkRecognitionService 并更改了命名空间。

“添加服务引用”对话框,其中选择了 inkrecognitionservice 并更改了命名空间
“添加服务引用”对话框,其中选择了 inkrecognitionservice 并更改了命名空间

将 InkCollector 类添加到 InkRecognition 项目

右键单击 InkRecognition 项目,单击“ 添加”,然后单击“ 新建项”。 在 Visual C# 菜单中,选择“ C# 类”。 将类命名为 InkCollector。 下图显示了选中并命名了 C# 类的对话框。

选中并命名了 c# 类的“添加新项”对话框
选中并命名了 c# 类的“添加新项”对话框

添加 InkCollector 类后,类定义将打开。 将墨迹收集器中的代码替换为以下代码。

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace InkRecognition
{
    public class InkCollector : IDisposable
    {
        public InkCollector(InkPresenter presenter)
        {
            _presenter = presenter;
            _presenter.Cursor = Cursors.Stylus;
            _presenter.MouseLeftButtonDown += new MouseButtonEventHandler(_presenter_MouseLeftButtonDown);
            _presenter.MouseMove += new MouseEventHandler(_presenter_MouseMove);
            _presenter.MouseLeftButtonUp += new MouseButtonEventHandler(_presenter_MouseLeftButtonUp);
        }

        void _presenter_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _presenter.CaptureMouse();
            if (!e.StylusDevice.Inverted)
            {
                _presenter.Cursor = Cursors.Stylus;
                _stroke = new Stroke(e.StylusDevice.GetStylusPoints(_presenter));
                _stroke.DrawingAttributes = _drawingAttributes;
                _presenter.Strokes.Add(_stroke);
            }
            else
            {
                _presenter.Cursor = Cursors.Eraser;
                _erasePoints = e.StylusDevice.GetStylusPoints(_presenter);
            }
        }

        void _presenter_MouseMove(object sender, MouseEventArgs e)
        {
            if (!e.StylusDevice.Inverted)
            {
                _presenter.Cursor = Cursors.Stylus;
            }
            else
            {
                _presenter.Cursor = Cursors.Eraser;
            }
            if (_stroke != null)
            {
                _stroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(_presenter));
            }
            else if (_erasePoints != null)
            {
                _erasePoints.Add(e.StylusDevice.GetStylusPoints(_presenter));
                StrokeCollection hitStrokes = _presenter.Strokes.HitTest(_erasePoints);
                if (hitStrokes.Count > 0)
                {
                    foreach (Stroke hitStroke in hitStrokes)
                    {
                        _presenter.Strokes.Remove(hitStroke);
                    }
                }
            }
        }

        void _presenter_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _presenter.ReleaseMouseCapture();
            if (_stroke != null)
            {
                _stroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(_presenter));
            }
            _stroke = null;
            _erasePoints = null;
        }

        public DrawingAttributes DefaultDrawingAttributes
        {
            get { return _drawingAttributes; }
            set { _drawingAttributes = value; }
        }

        public void Dispose()
        {
            _presenter.MouseLeftButtonDown -= new MouseButtonEventHandler(_presenter_MouseLeftButtonDown);
            _presenter.MouseMove -= new MouseEventHandler(_presenter_MouseMove);
            _presenter.MouseLeftButtonUp -= new MouseButtonEventHandler(_presenter_MouseLeftButtonUp);
            _presenter = null;
        }

        private InkPresenter _presenter = null;
        private Stroke _stroke = null;
        private StylusPointCollection _erasePoints = null;
        private DrawingAttributes _drawingAttributes = new DrawingAttributes();
    }
}

更新默认页面的 XAML,并为手写识别添加代码隐藏

现在,你有一个收集墨迹的类,你需要使用以下 XAML 更新 page.xaml 中的 XAML。 以下代码将黄色渐变添加到输入区域、InkCanvas 控件和将触发识别的按钮。

<UserControl x:Class="InkRecognition.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Border Margin="5" Grid.Row="0" BorderThickness="4" BorderBrush="Black" CornerRadius="5" Height="200">
            <Grid>
                <InkPresenter x:Name="inkCanvas">
                    <InkPresenter.Background>
                        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                            <GradientStop Offset="0" Color="Yellow"/>
                            <GradientStop Offset="1" Color="LightYellow"/>
                        </LinearGradientBrush>
                    </InkPresenter.Background>
                </InkPresenter>
                <Button Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" Content="Recognize" Click="RecoButton_Click"/>
            </Grid>
        </Border>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" FontSize="28" Text="Result: "/>
            <ComboBox x:Name="results" Grid.Column="1" FontSize="28"/>
        </Grid>
    </Grid>
</UserControl>

将隐藏页 Page.xaml.cs 的代码替换为以下代码,该代码将为 “识别 ”按钮添加事件处理程序。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using InkRecognition.InkRecognitionServiceReference;

namespace InkRecognition
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            inkCol = new InkCollector(inkCanvas);
            recoClient = new InkRecognitionServiceClient();
            recoClient.RecognizeCompleted += new EventHandler<RecognizeCompletedEventArgs>(recoClient_RecognizeCompleted);
        }

        private void RecoButton_Click(object sender, RoutedEventArgs e)
        {
            // Serialize the ink into an array on ints.
            ObservableCollection<ObservableCollection<int>> packets = new ObservableCollection<ObservableCollection<int>>();
            double pixelToHimetricMultiplier = 2540d / 96d;

            foreach (Stroke stroke in inkCanvas.Strokes)
            {
                packets.Add(new ObservableCollection<int>());
                int index = inkCanvas.Strokes.IndexOf(stroke);
                for (int i = 0; i < stroke.StylusPoints.Count; i += 2)
                {
                    packets[index].Add(Convert.ToInt32(stroke.StylusPoints[i].X * pixelToHimetricMultiplier));
                    packets[index].Add(Convert.ToInt32(stroke.StylusPoints[i].Y * pixelToHimetricMultiplier));
                }
            }

            // Call the Web service.
            recoClient.RecognizeAsync(packets);
        }

        void recoClient_RecognizeCompleted(object sender, RecognizeCompletedEventArgs e)
        {
            // We have received results from the server, now display them.
            results.ItemsSource = e.Result;
            UpdateLayout();
            results.SelectedIndex = 0;
            inkCanvas.Strokes.Clear();
        }

        private InkRecognitionServiceClient recoClient = null;
        private InkCollector inkCol = null;
    }
}

从客户端配置中删除安全传输指令

在使用 WCF 服务之前,必须从服务配置中删除所有安全传输选项,因为 Silverlight 2.0 WCF 服务当前不支持安全传输。 从 InkRecognition 项目中,更新 ServiceReferences.ClientConfig 中的安全设置。 将安全 XML 从

          <security mode="None">
            <transport>
              <extendedProtectionPolicy policyEnforcement="WhenSupported" />
            </transport>
          </security>

to

       <security mode="None"/>

现在,应用程序应运行。 下图显示了应用程序在 webpage 中的外观,其中在识别框中输入了一些手写内容。

应用程序在 webpage 中输入了一些笔迹进入识别框
应用程序在 webpage 中输入了一些笔迹进入识别框

下图显示了“ 结果 ”下拉列表中已识别的文本。

在结果下拉列表中具有已识别文本的 webpage 中的应用程序
在结果下拉列表中具有已识别文本的 webpage 中的应用程序