Поделиться через


Пример нескольких распознавителей

В этом примере демонстрируются расширенные возможности api api автоматизации компьютеров MicrosoftTablet, используемого для распознавания рукописного ввода .

Это включает в себя следующее:

  • Перечисление установленных распознавителей
  • Создание контекста распознавателя с помощью определенного распознавателя языка
  • Сериализация результатов распознавания с помощью коллекции росчерков
  • Упорядочение коллекций росчерков в настраиваемую коллекцию в объекте InkDisp
  • Сериализация объектов рукописного ввода в и получение их из файла сериализованного формата рукописного ввода (ISF)
  • Настройка направляющих ввода распознавателя
  • Использование синхронного и асинхронного распознавания

Рукописные заголовки

Во-первых, добавьте заголовки для интерфейсов автоматизации планшетного ПК. Они устанавливаются вместе с пакетом sdk microsoft Windows XP Tablet PC Edition.

#include <msinkaut.h>
#include <msinkaut_i.c>

Файл EventSinks.h определяет интерфейсы IInkEventsImpl и IInkRecognitionEventsImpl.

#include "EventSinks.h"

Перечисление установленных распознавителей

Метод LoadMenu приложения заполняет меню Создать новые штрихи доступными распознавателями. Создается Объект InkRecognizers . Если свойство Languages объекта InkRecognizers не является пустым, распознаватель является распознавателем текста и значение его свойства Name добавляется в меню.

// Create the enumerator for the installed recognizers
hr = m_spIInkRecognizers.CoCreateInstance(CLSID_InkRecognizers);
...
    // Filter out non-language recognizers by checking for
    // the languages supported by the recognizer - there is not
    // any if it is a gesture or object recognizer.
    CComVariant vLanguages;
    if (SUCCEEDED(spIInkRecognizer->get_Languages(&vLanguages)))
    {
        if ((VT_ARRAY == (VT_ARRAY & vLanguages.vt))           // it should be an array
            && (NULL != vLanguages.parray)
            && (0 < vLanguages.parray->rgsabound[0].cElements)) // with at least one element
        {
            // This is a language recognizer. Add its name to the menu.
            CComBSTR bstrName;
            if (SUCCEEDED(spIInkRecognizer->get_Name(&bstrName)))
                ...
        }
    }

Создание сборщика рукописного ввода

Метод OnCreate приложения создает объект InkCollector , подключает его к источнику событий и включает сбор рукописного ввода.

// Create an ink collector object.
hr = m_spIInkCollector.CoCreateInstance(CLSID_InkCollector);

// Establish a connection to the collector's event source.
hr = IInkCollectorEventsImpl<CMultiRecoApp>::DispEventAdvise(m_spIInkCollector);

// Enable ink input in the m_wndInput window
hr = m_spIInkCollector->put_hWnd((long)m_wndInput.m_hWnd);
hr = m_spIInkCollector->put_Enabled(VARIANT_TRUE);

Создание контекста распознавателя

Метод CreateRecoContext приложения создает и инициализирует новый контекст распознавателя, а также настраивает руководства, поддерживаемые соответствующим языком. Метод CreateRecognizerContext объекта IInkRecognizer создает объект IInkRecognizerContext2 для языка. При необходимости заменяется старый контекст распознавателя. Контекст связан с источником событий. Наконец, проверяется свойство Capabilities контекста распознавателя, для которого поддерживается контекст распознавателя.

// Create a recognizer context
CComPtr<IInkRecognizerContext2> spNewContext;
if (FAILED(pIInkRecognizer2->CreateRecognizerContext(&spNewContext)))
    return false;

// Replace the current context with the new one
if (m_spIInkRecoContext != NULL)
{
    // Close the connection to the recognition events source
    IInkRecognitionEventsImpl<CMultiRecoApp>::DispEventUnadvise(m_spIInkRecoContext);
}
m_spIInkRecoContext.Attach(spNewContext.Detach());

// Establish a connection with the recognizer context's event source
if (FAILED(IInkRecognitionEventsImpl<CMultiRecoApp>::DispEventAdvise(m_spIInkRecoContext)))
    ...

// Set the guide if it's supported by the recognizer and has been created 
int cRows = 0, cColumns = 0;
InkRecognizerCapabilities dwCapabilities = IRC_DontCare;
if (SUCCEEDED(pIInkRecognizer->get_Capabilities(&dwCapabilities)))
    ...

Сбор росчерков и отображение результатов распознавания

Метод OnStroke приложения обновляет InkStrokes сборщика рукописного ввода, отменяет существующие запросы асинхронного распознавания и создает запрос распознавания в контексте распознавателя.

// Add the new stroke to the current collection
hr = m_spIInkStrokes->Add(pIInkStroke);

if (SUCCEEDED(hr))
{
    // Cancel the previous background recognition requests
    // which have not been processed yet
    m_spIInkRecoContext->StopBackgroundRecognition();

    // Ask the context to update the recognition results with newly added strokes
    // When the results are ready, the recognizer context returns them
    // through the corresponding event RecognitionWithAlternates
    CComVariant vCustomData;
    m_spIInkRecoContext->BackgroundRecognize(vCustomData);
}

Метод приложения OnRecognition отправляет результаты запроса распознавания в метод окна UpdateString вывода.

// Update the output window with the new results
m_wndResults.UpdateString(bstrRecognizedString);

Удаление штрихов и результатов распознавания

Метод OnClear приложения удаляет все штрихи и результаты распознавания из объекта InkDisp и очищает окна. Связь контекста распознавателя с его коллекцией InkStrokes удаляется.

// Detach the current stroke collection from the recognizer context and release it
if (m_spIInkRecoContext != NULL)
    m_spIInkRecoContext->putref_Strokes(NULL);

m_spIInkStrokes.Release();

// Clear the custom strokes collection
if (m_spIInkCustomStrokes != NULL)
    m_spIInkCustomStrokes->Clear();

// Delete all strokes from the Ink object
// Passing NULL as a stroke collection pointer means asking to delete all strokes
m_spIInkDisp->DeleteStrokes(NULL);

// Get a new stroke collection from the ink object
...
// Ask for an empty collection by passing an empty variant 
if (SUCCEEDED(m_spIInkDisp->CreateStrokes(v, &m_spIInkStrokes)))
{
    // Attach it to the recognizer context
    if (FAILED(m_spIInkRecoContext->putref_Strokes(m_spIInkStrokes)))
        ...
}

Изменение контекстов распознавателя

Метод OnNewStrokes приложения вызывается, когда пользователь выбирает распознаватель в меню Создать новые штрихи. Текущий объект InkStrokes сохраняется. Если был выбран другой распознаватель языка, создается новый контекст распознавателя. Затем к новому контексту распознавателя присоединяется новый объект InkStrokes .

// Save the current stroke collection if there is any
if (m_spIInkRecoContext != NULL)
{
    // Cancel the previous background recognition requests
    // which have not been processed yet
    m_spIInkRecoContext->StopBackgroundRecognition();
    
    // Let the context know that there'll be no more input 
    // for the attached stroke collection
    m_spIInkRecoContext->EndInkInput();

    // Add the stroke collection to the Ink object's CustomStrokes collection
    SaveStrokeCollection();
}
...
// If a different recognizer was selected, create a new recognizer context
// Else, reuse the same recognizer context
if (wID != m_nCmdRecognizer)
{
    // Get a pointer to the recognizer object from the recognizer collection  
    CComPtr<IInkRecognizer> spIInkRecognizer;
    if ((m_spIInkRecognizers == NULL)
        || FAILED(m_spIInkRecognizers->Item(wID - ID_RECOGNIZER_FIRST,
                                             &spIInkRecognizer))
        || (false == CreateRecoContext(spIInkRecognizer)))
    {
        // restore the cursor
        ::SetCursor(hCursor);
        return 0;
    }

    // Update the status bar
    m_bstrCurRecoName.Empty();
    spIInkRecognizer->get_Name(&m_bstrCurRecoName);
    UpdateStatusBar();

    // Store the selected recognizer's command id
    m_nCmdRecognizer = wID;
}

Затем он вызывает StartNewStrokeCollection, который создает пустой объект InkStrokes и присоединяет его к контексту распознавателя.

Сохранение коллекции strokes для контекста распознавателя

Метод приложения SaveStrokeCollection проверяет наличие существующего контекста распознавателя и завершает распознавание текущей коллекции штрихов. Затем коллекция InkStrokes добавляется в CustomStrokes объекта ink.

if (m_spIInkRecoContext != NULL)
{
    if (SUCCEEDED(m_spIInkStrokes->get_Count(&lCount)) && 0 != lCount)
    {
        CComPtr<IInkRecognitionResult> spIInkRecoResult;
        InkRecognitionStatus RecognitionStatus;
        if (SUCCEEDED(m_spIInkRecoContext->Recognize(&RecognitionStatus, &spIInkRecoResult)))
        {
            if (SUCCEEDED(spIInkRecoResult->SetResultOnStrokes()))
            {
                CComBSTR bstr;
                spIInkRecoResult->get_TopString(&bstr);
                m_wndResults.UpdateString(bstr);
            }
            ...
        }
    }
    // Detach the stroke collection from the old recognizer context
    m_spIInkRecoContext->putref_Strokes(NULL);
}

// Now add it to the ink's custom strokes collection
// Each item (stroke collection) of the custom strokes must be identified
// by a unique string. Here we generate a GUID for this.
if ((0 != lCount) && (m_spIInkCustomStrokes != NULL))
{
    GUID guid;
    WCHAR szGuid[40]; // format: "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
    if (SUCCEEDED(::CoCreateGuid(&guid)) 
        && (::StringFromGUID2(guid, szGuid, countof(szGuid)) != 0))
    {
        CComBSTR bstrGuid(szGuid);
        if (FAILED(m_spIInkCustomStrokes->Add(bstrGuid, m_spIInkStrokes)))
            ...