Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В примере "Базовый анализ рукописного ввода" показано, как класс InkAnalyzer делит рукописный ввод на различные сегменты слов и рисунков.
Этот пример представляет собой обновленную версию примера разделителя рукописного ввода. В то время как в примере разделителя рукописного ввода используется класс Divider , в этом примере используется более новый и предпочтительный API InkAnalysis. API InkAnalysis объединяет RecognizerContext и Divider в один API и расширяет функциональные возможности обоих api.
При обновлении формы образец рисует прямоугольник вокруг каждой анализируемой единицы: слов, линий, абзацев, областей записи, рисунков и маркеров. Форма использует разные цвета для разных единиц измерения. Прямоугольники также увеличиваются на различные величины, чтобы гарантировать, что ни один прямоугольник не будет скрыт другими.
В следующей таблице указаны цвет и увеличение для каждой анализируемой единицы.
| Анализируемая единица | Цвет прямоугольника | Увеличение прямоугольника (пиксели) |
|---|---|---|
| Word |
Зеленый |
1 |
| График |
Пурпурный |
3 |
| Paragraph |
Синий |
5 |
| Область записи |
Желтый |
7 |
| Рисование |
Красный |
1 |
| Пуля |
Оранжевый |
1 |
Вы можете стереть штрихи в форме. В примере приложения можно переключаться между режимами рукописного ввода и стирания, чтобы изменить функцию пера.
private void miInk_Click(object sender, System.EventArgs e)
{
// Turn on the inking mode
myInkOverlay.EditingMode = InkOverlayEditingMode.Ink;
// Update the state of the Ink and Erase menu items
miInk.Checked = true;
miErase.Checked = false;
// Update the UI
this.Refresh();
}
private void miErase_Click(object sender, System.EventArgs e)
{
// Turn on the ink deletion mode
myInkOverlay.EditingMode = InkOverlayEditingMode.Delete;
// Update the state of the Ink and Erase menu items
miInk.Checked = false;
miErase.Checked = true;
// Update the UI
this.Refresh();
}
При добавлении или удалении штрихов примеры обновляют InkAnalyzer.
private void myInkOverlay_Stroke(object sender, InkCollectorStrokeEventArgs e)
{
// Filter out the eraser stroke.
if (InkOverlayEditingMode.Ink == myInkOverlay.EditingMode)
{
// Add the new stroke to the InkAnalyzer's stroke collection
myInkAnalyzer.AddStroke(e.Stroke);
if (miAutomaticLayoutAnalysis.Checked)
{
// Invoke an analysis operation on the background thread
myInkAnalyzer.BackgroundAnalyze();
}
}
}
void myInkOverlay_StrokeDeleting(object sender, InkOverlayStrokesDeletingEventArgs e)
{
// Remove the strokes to be deleted from the InkAnalyzer's stroke collection
myInkAnalyzer.RemoveStrokes(e.StrokesToDelete);
// If automatic layout analysis is turned on, analyze the ink on the background thread
if ( miAutomaticLayoutAnalysis.Checked )
{
// Invoke an analysis operation on the background thread
myInkAnalyzer.BackgroundAnalyze ( );
}
}
Обратите внимание, что в меню Режим автоматический анализ макета включен по умолчанию. Если выбран этот параметр, обработчики событий Stroke и StrokesDeleting объекта InkOverlay вызывают метод BackgroundAnalyze при каждом создании или удалении росчерка.
Примечание
Вызов метода Analyze объекта InkAnalyzer с более чем несколькими штрихами приводит к заметной задержке в приложении. Это связано с тем, что анализ является синхронной операцией анализа рукописного ввода. На практике метод Analyze вызывается только в том случае, если вам нужен результат. В противном случае используйте асинхронный метод BackgroundAnalyze , как показано в примере.
Обработка результатов анализа
В примере создаются два массива для хранения различных прямоугольников: горизонтальных или повернутых. Используйте повернутый ограничивающий прямоугольник, чтобы получить угол, на котором написана строка слов. В примере показаны свойства, возвращаемые InkAnalyzer , и отображается ограничивающий прямоугольник или повернутый ограничивающий прямоугольник (в зависимости от выбора меню).
private Rectangle[] GetHorizontalBBoxes(Guid nodeType, int inflate)
{
// Declare the array of rectangles to hold the result
Rectangle[] analysisRects;
// Get the division units from the division result of division type
ContextNodeCollection nodes = myInkAnalyzer.FindNodesOfType(nodeType);
// If there is at least one unit, we construct the rectangles
if ((null != nodes) && (0 < nodes.Count))
{
// We need to convert rectangles from ink units to
// pixel units. For that, we need Graphics object
// to pass to InkRenderer.InkSpaceToPixel method
using (Graphics g = drawArea.CreateGraphics())
{
// Construct the rectangles
analysisRects = new Rectangle[nodes.Count];
// InkRenderer.InkSpaceToPixel takes Point as parameter.
// Create two Point objects to point to (Top, Left) and
// (Width, Height) properties of rectangle. (Width, Height)
// is used instead of (Right, Bottom) because (Right, Bottom)
// are read-only properties on Rectangle
Point ptLocation = new Point();
Point ptSize = new Point();
// Index into the bounding boxes
int i = 0;
// Iterate through the collection of division units to obtain the bounding boxes
foreach (ContextNode node in nodes)
{
// Get the bounding box of the strokes of the division unit
analysisRects[i] = node.Location.GetBounds();
// The bounding box is in ink space unit. Convert them into pixel unit.
ptLocation = analysisRects[i].Location;
ptSize.X = analysisRects[i].Width;
ptSize.Y = analysisRects[i].Height;
// Convert the Location from Ink Space to Pixel Space
myInkOverlay.Renderer.InkSpaceToPixel(g, ref ptLocation);
// Convert the Size from Ink Space to Pixel Space
myInkOverlay.Renderer.InkSpaceToPixel(g, ref ptSize);
// Assign the result back to the corresponding properties
analysisRects[i].Location = ptLocation;
analysisRects[i].Width = ptSize.X;
analysisRects[i].Height = ptSize.Y;
// Inflate the rectangle by inflate pixels in both directions
analysisRects[i].Inflate(inflate, inflate);
// Increment the index
++i;
}
} // Relinquish the Graphics object
}
else
{
// Otherwise we return null
analysisRects = null;
}
// Return the Rectangle[] object
return analysisRects;
}
private System.Collections.ArrayList GetRotatedBBoxes(Guid nodeType, int inflate)
{
//Find the correct collection of results nodes.
ContextNodeCollection Nodes = myInkAnalyzer.FindNodesOfType(nodeType);
// Declare the array list to hold the results;
// This array represents the four points of a rectangle, with the first point
// copied again to complete the cycle of points.
ArrayList polygonPoints = new ArrayList(Nodes.Count);
// Cycle through each results node, get and convert the
// rotated bounding box points
foreach (ContextNode node in Nodes)
{
//Declare the point array
Point[] rotatedBoundingBox = null;
//Switch on the type of ContextNode to cast into the
//appropriate type. This is required to access the
//type specific property "RotatedBoundingBox" which
//is not found on all ContextNode types.
if (nodeType == ContextNodeType.InkWord)
{
rotatedBoundingBox = ((InkWordNode)node).GetRotatedBoundingBox();
}
else if (nodeType == ContextNodeType.Line)
{
rotatedBoundingBox = ((LineNode)node).GetRotatedBoundingBox();
}
else if (nodeType == ContextNodeType.Paragraph)
{
rotatedBoundingBox = ((ParagraphNode)node).GetRotatedBoundingBox();
}
else if (nodeType == ContextNodeType.WritingRegion ||
nodeType == ContextNodeType.InkDrawing ||
nodeType == ContextNodeType.InkBullet )
{
// Rotated Bounding Boxes are not a supported option for
// Writing Regions or Drawings. We return the axis aligned
// bounding box instead
Rectangle rect = node.Location.GetBounds();
// We need to create a looped list of 4 points to be consistent
// with the way InkAnalysis represents rotated bounding boxes.
rotatedBoundingBox = new Point[4];
rotatedBoundingBox[0] = new Point(rect.X, rect.Y);
rotatedBoundingBox[1] = new Point(rect.Right, rect.Y);
rotatedBoundingBox[2] = new Point(rect.Right, rect.Bottom);
rotatedBoundingBox[3] = new Point(rect.X, rect.Bottom);
}
if (null != rotatedBoundingBox)
{
// We need to convert rectangles from ink units to
// pixel units. For that, we need Graphics object
// to pass to InkRenderer.InkSpaceToPixel method
using (Graphics g = drawArea.CreateGraphics())
{
// convert each of the points from ink space to pixel space
for (int i = 0; i < rotatedBoundingBox.Length; i++)
{
myInkOverlay.Renderer.InkSpaceToPixel(g, ref rotatedBoundingBox[i]);
}
//inflate the points by calling helper method
InflateHelperMethod(ref rotatedBoundingBox, inflate);
// increment the node portion of the polygonPoints array
polygonPoints.Add(rotatedBoundingBox);
}
}
}
//Return the results
return polygonPoints;
}
Средство синтаксического анализа вычисляет GetRotatedBoundingBox во время анализа. Вы можете получить доступ к информации из повернутых ограничивающих прямоугольник в приложении по ряду полезных причин:
- Обнаружение или рисование границ одной линии, абзаца или другой единицы измерения.
- Определите угол записи строки или абзаца.
- Реализуйте такие функции, как выделение для строки, абзаца или другой единицы измерения.