기본 잉크 분석 샘플

기본 잉크 분석 샘플에서는 InkAnalyzer 클래스가 잉크를 다양한 단어 및 그리기 세그먼트로 나누는 방법을 보여 줍니다.

이 샘플은 Ink Divider 샘플업데이트된 버전입니다. Ink Divider 샘플은 Divider 클래스를 사용하는 반면, 이 샘플에서는 최신 및 기본 설정 InkAnalysis API를 사용합니다. InkAnalysis API는 RecognizerContext 및 Divider를 하나의 API로 결합하고 둘 다의 기능을 확장합니다.

양식을 업데이트할 때 샘플은 분석된 각 단위(단어, 선, 단락, 쓰기 영역, 드로잉 및 글머리 기호)를 중심으로 사각형을 그립니다. 폼은 다른 단위에 대해 서로 다른 색을 사용합니다. 사각형도 다른 사각형에 의해 가려지지 않도록 서로 다른 크기로 확대됩니다.

다음 표에서는 분석된 각 단위의 색과 확대를 지정합니다.

분석된 단위 사각형 색 사각형 확대(픽셀)
단어
녹색
1

자홍색
3
단락
파랑
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 ( );
            }
        }

모드 메뉴에서 자동 레이아웃 분석은 기본적으로 설정되어 있습니다. 이 옵션을 선택하면 InkOverlay 개체의 StrokeStrokesDeleting 이벤트 처리기는 스트로크를 만들거나 삭제할 때마다 BackgroundAnalyze 메서드를 호출합니다.

메모

여러 개의 스트로크가 있는 InkAnalyzer 개체의 Analyze 메서드를 호출하면 애플리케이션에서 눈에 띄는 지연이 발생합니다. 분석이 동기 잉크 분석 작업이기 때문입니다. 실제로 결과가 필요한 경우에만 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의을 계산합니다. 다음과 같은 여러 가지 유용한 이유로 애플리케이션의 회전된 경계 상자에서 정보에 액세스할 수 있습니다.

  • 한 줄, 단락 또는 다른 단위의 경계를 검색하거나 그립니다.
  • 줄 또는 단락이 기록되는 각도를 결정합니다.
  • 줄, 단락 또는 기타 단원에 대한 선택과 같은 기능을 구현합니다.

기본 인식 및 잉크 분석

스캔한 서류 양식 샘플

잉크 구분선 샘플