Kézírás felismerés a WPF-ben

Korábbi igéreteimhez hűen, itt az idő, hogy megmutassam milyen egyszerű a kézírás-felismerés használata a WPF-ben (köszönet Velvárt Andrásnak és a Response Kft.-nek az ötletért). Én ugyan nem foglalkozom Tablet PC fejlesztéssel, de ezt érdekesnek találtam, így hát muszály voltam kipróbálni :).

Először is készítsünk egy új WPF alkalmazást. Adjuk hozzá a következő két assemblyt referenciaként:

IALoader.dll – C:\Program Files\Reference Assemblies\Microsoft\Tablet PC\v1.7\IAWinFX.dll
IAWinFX.dll – C:\Program Files\Reference Assemblies\Microsoft\Tablet PC\v1.7\IAWinFX.dll

Következő lépés, hogy tegyünk le egy InkCanvas-t. Az InkCanvas képes stroke objektumokat kezelni. Ezen kívül (ami így se túl sok), még pár funkciót kitehetünk az InkCanvas mellé a könnyebb kezelés érdekében. Megint jobb lesz ha beillesztem ide az egészet és a két szemünkkel láthatjuk azt, hogy milyen egyszerű ez:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
    xmlns:Ink="clr-namespace:System.Windows.Ink;assembly=PresentationCore"    
    xmlns:WPFInkApi="clr-namespace:WPFInkApi"        
    x:Class="WPFInkApi.WinMain"
    Title="WPF InkCanvas" SizeToContent="Width" Height="300"
    WindowStartupLocation="CenterScreen">

    <Grid>
        <!-- a grid felosztása három sorra -->
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <!-- az első sorba kerül a "menü" -->
        <StackPanel
            Grid.Row="0"            
            Margin="5,5"
            Orientation="Horizontal"                    
            Button.Click="Button_Click">
            
            <RadioButton
                Content="Rajzolás"                
                Margin="5,5"                
                Tag="Ink"
                IsChecked="True"/>
            <RadioButton
                Content="Kiválasztás"
                Margin="5,5"
                Tag="Select"/>            
            <RadioButton
                Content="Stroke törlése"
                Tag="EraseByStroke"
                Margin="5,5"/>
            <RadioButton
                Content="Pont törlése"
                Tag="EraseByPoint"
                Margin="5,5"/>
            <Button
                Content="Vászon törlése"
                Tag="Clear"
                Margin="5,5"/>
            
            <ComboBox       
                Name="ComboBox_DrawingAttrWidth"
                Margin="5,5"
                Width="50"
                SelectionChanged="ComboBox_DrawingAttrWidth_SelectionChanged">
                <ComboBox.Items>
                    <ComboBoxItem Content="2"/>
                    <ComboBoxItem Content="4" IsSelected="True"/>
                    <ComboBoxItem Content="8"/>
                    <ComboBoxItem Content="12"/>
                    <ComboBoxItem Content="16"/>
                    <ComboBoxItem Content="20"/>
                    <ComboBoxItem Content="24"/>
                    <ComboBoxItem Content="28"/>
                    <ComboBoxItem Content="32"/>
                </ComboBox.Items>
            </ComboBox>
            
            <Button
                Name="Button_Analyze"
                Content="Analizál"
                Margin="5,5"
                FontWeight="Bold"
                Click="Button_Analyze_Click"/>
        </StackPanel>

        <!-- vászon -->
        <InkCanvas 
            Grid.Row="1"
            Name="ICanvas">
            <InkCanvas.DefaultDrawingAttributes>
                <Ink:DrawingAttributes
                            xmlns:ink="system-windows-ink"
                            Color="Indigo"
                            Height="4" Width="4"/>
            </InkCanvas.DefaultDrawingAttributes>
        </InkCanvas>

        <!-- az eredmény megjelenítése -->
        <TextBlock 
            Grid.Row="2"
            Name="TextBlock_RecognizedText"
            Margin="5,5"
            Background="LightYellow"
            FontSize="32"/>
    </Grid>
</Window>

Megjegyzés: A StackPanelben látható egy Button.Click="Button_Click" rész. Nincs szükség minden radiobuttonnál egyesével manuálisan megadni a megfelelő eseménykezelőt, hála a RoutedEventeknek, ami egyedülálló a WPF-ben. Ezzel csak annyi a probléma, hogy a többi vezérlő esetében is – ha nem írjuk felül a Click eseményt – le fog futni. Ezt kezelni kell majd a codebehindban.

Az InkCanvasnak beállítottam a színét, illetve a méretét (Ink:DrawingAttributes). Ebbe a kódba több érdekesség nincs is, úgyhogy itt van még a codebehind is hozzá:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Ink;

namespace WPFInkApi
{
    /// <summary>
    /// Interakciós logika a WinMain-hez
    /// </summary>
    public partial class WinMain : Window
    {
        public WinMain()
        {
            InitializeComponent();
        }

        /// <summary>
        /// InkCanvas mód kiválasztása
        /// </summary>
        private void Button_Click(object sender, RoutedEventArgs e)
        {            
            Control control = e.Source as Control;
            if (control == null)
                return;

            // megfelelő mód kiválasztása
            switch ((string)control.Tag)
            {
                case "Ink":
                    ICanvas.EditingMode = InkCanvasEditingMode.Ink;
                    break;

                case "Select":
                    ICanvas.EditingMode = InkCanvasEditingMode.Select;
                    break;

                case "EraseByStroke":
                    ICanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
                    break;

                case "EraseByPoint":
                    ICanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
                    break;

                case "Clear":
                    ICanvas.Strokes.Clear();
                    break;
            }
        }
       
        /// <summary>
        /// Vonal vastagság
        /// </summary>
        private void ComboBox_DrawingAttrWidth_SelectionChanged(object sender,
            SelectionChangedEventArgs e)
        {
            // ez kell mert egyszer le fog futni
            // induláskor a ComboBoxItem IsSelected="True" beállítása miatt!
            if (ICanvas == null)
                return;

            ComboBoxItem item = ComboBox_DrawingAttrWidth.SelectedValue as ComboBoxItem;
            // ha van kiválasztva valami...
            if (item != null)
            {
                double size = Convert.ToDouble(item.Content);

                ICanvas.DefaultDrawingAttributes.Width = size;
                ICanvas.DefaultDrawingAttributes.Height = size;
            }
        }

        /// <summary>
        /// Analizálás
        /// </summary>  
        private void Button_Analyze_Click(object sender, RoutedEventArgs e)
        {
            // az AddStrokes() dob egy exceptiont
            // ha üres kollekciót adunk át (vagy null-t)
            if (ICanvas.Strokes.Count == 0)
                return;

            // InkAnalyzer létrehozása
            InkAnalyzer Analyzer = new InkAnalyzer(this.Dispatcher);
            
            // az InkCanvas tartalmának átadása
            // egy nyelv azonosítóval (magyar nincs)
            Analyzer.AddStrokes(ICanvas.Strokes, 1033);

            // analizálás (ha sikerül, akkor a legjobb
            // eredményt adja vissza: GetRecognizedString())
            AnalysisStatus Status = Analyzer.Analyze();
            if (Status.Successful)
                TextBlock_RecognizedText.Text = Analyzer.GetRecognizedString();
        }
    }
}

S az eredmény (remélem senki se gondolja ezt komolyan rólam :)):

WPF InkCanvas

Kész is volnánk, semmi több. Annyi gáz van vele, hogy a magyar ékezetes karaktereket így sajnos nem ismeri fel. Erre lehet használni a ch9 videóban is látott kis felülvonós trükköt. Sajnos ez még ugyanúgy, akárcsak a beszédfelismerés gyerekcipőben jár, s nemhogy csak gyerekcipőben, de szerintem megint mi magyarok leszünk az utolsók, akiknek lesz ilyenünk, mégha kész is lesz valaha (lsd. Vista beszédfelismerés). Hosszútávon viszont bízok benne, hogy kezdünk átlépni egy tényleg új, csodálatos korszakba, ahol az eddigi álmok valóra válnak!

1 thoughts on “Kézírás felismerés a WPF-ben

  1. Dósai László

    Szia neked hogy van meg az a két assembly reference, mert én windows 7 használok letöltöttem az SDK meg frameworkerst is a legújabbat, de egyszerűen nem tudom beaplikálni azt a kát referenciát(utána olvastam nem vagyok vele egyedül) esetleg tudnál segíteni hogy mi lehet a baj?

Hozzászólás