/// <summary> /// This function checks all sentences if there are components which have an angle /// of 90 degrees, if so they will be merged /// </summary> public static void MergeComponentsInSentences(PageImage Image) { int index; double Angle; PageComponent Left, Right; foreach (Sentence Sentence in Image.Sentences) { for (index = 0; index < Sentence.Count - 1; index++) { Left = Sentence.Components[index]; Right = Sentence.Components[index + 1]; if (Left.Type == ePageComponentType.eCharacter && Right.Type == ePageComponentType.eCharacter) { Angle = PageComponent.AngleBetweenComponents(Left, Right); if (Angle > 88 && Angle < 92) { Left.Merge(Right); Sentence.Delete(index + 1); } } } } }
private static void GenerateBitmap(PageImage Image, PageComponent Component, List <Point> Pixels) { //Generate a bitmap of the connected component. The bitmap contains only //pixels of the connected component and no other ones. The bitmaps has the //size of the bounding rectangle of the connected component int Size; Size = Component.Stride * Component.Height; Component.Bytes = new Byte[Component.Width, Component.Height]; Component.BinaryBytes = new Byte[Component.Width, Component.Height]; for (int x = 0; x < Component.Width; x++) { for (int y = 0; y < Component.Height; y++) { Component.Bytes[x, y] = 0xFF; Component.BinaryBytes[x, y] = 0xFF; } } foreach (Point Pixel in Pixels) { Component.Bytes[Pixel.X - Component.Area.X, Pixel.Y - Component.Area.Y] = Image.Bytes[Pixel.X, Pixel.Y]; Component.BinaryBytes[Pixel.X - Component.Area.X, Pixel.Y - Component.Area.Y] = 0x00; } Pixels.Clear(); //int Source; //int Destination; // int y = Component.Area.Height; // int x = Component.Area.Width; // while (y > 0) // { // y--; // Source = (Component.Area.X + x) + (Component.Area.Y + y) * Image.Stride; // Destination = x + y * Component.Stride; // while (x > 0) // { // x--; // Destination--; // Source--; // if (Image.ImageBytes[Source] != 0xFF) // { // Component.ComponentBytes[Destination] = 0x00; // } // else // { // Component.ComponentBytes[Destination] = 0xFF; // } // } // } }
/// <summary> /// This function creates/calculates the input for a neural network /// </summary> /// <param name="Component"></param> /// <returns></returns> public static sNeuralInput CalculateNetworkInput(PageComponent Component) { sNeuralInput Input; Input = new sNeuralInput(); //Initialize input for (int index = 0; index < 48; index++) { Input.fInputs.Add(0); } //Add stroke direction for (int i = 0; i < 8; i++) { for (int j = 0; j < 4; j++) { Input.fInputs[i + 0] += Component.lStrokeDirectionX[i * 4 + j]; Input.fInputs[i + 8] += Component.lStrokeDirectionY[i * 4 + j]; } } for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { Input.fInputs[16 + i + j * 2] = Component.lStrokeMatrixNW[i, j]; Input.fInputs[20 + i + j * 2] = Component.lStrokeMatrixSW[i, j]; } } int Pointer; //Add endpoints and junctions for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { Pointer = 24 + x + y * 3; Input.fInputs[Pointer] = Component.PixelTypeProjectionEndpoint[x, y]; Pointer = 33 + x + y * 3; Input.fInputs[Pointer] = Component.PixelTypeProjectionJunction[x, y]; } } //Add width/height ratio Input.fInputs[42] = Component.AspectRatio; //Add position Input.fInputs[43] = Component.Position.Ascent ? 1.0 : 0.0; Input.fInputs[44] = Component.Position.Height ? 1.0 : 0.0; Input.fInputs[45] = Component.Position.Center ? 1.0 : 0.0; Input.fInputs[46] = Component.Position.Base ? 1.0 : 0.0; Input.fInputs[47] = Component.Position.Descent ? 1.0 : 0.0; return(Input); }
/// <summary> ///This function takes a to be deleted component and sees whether it could ///be a part of another component in the outer sentence. It could be a ///dot for example. If so, it will merged. /// </summary> /// <param name="InnerSentence"></param> /// <param name="OuterSentence"></param> public static void MergeRectangles(Sentence InnerSentence, Sentence OuterSentence) { int MinDistance = 1000; int Distance; double BestAngle = 90; double Angle; double AverageAngle; PageComponent InnerComponent; PageComponent MergeComponent = null; InnerComponent = InnerSentence.Components[0]; if (InnerComponent.Type != ePageComponentType.eCharacter) { return; } AverageAngle = OuterSentence.AverageAngle(); //we merge two rectangles if: //1. the distance between them is the minimum in comparing to other pairs in the sentence //2. if the angle between them is the best 90 degress angle in comparision to the average angle foreach (PageComponent OuterComponent in OuterSentence.Components) { Distance = PageComponent.DistanceBetweenComponents(OuterComponent, InnerComponent); if (Distance < MinDistance && OuterComponent.Type == ePageComponentType.eCharacter) { Angle = PageComponent.AngleBetweenComponents(OuterComponent, InnerComponent); Angle = (Angle % 180) - 90 + AverageAngle; if (Math.Abs(Angle) <= 10) { if (Math.Abs(Angle) <= BestAngle) { //Check if the distance isn't to great, but if the angle is exact 90 degrees always //do a merge. if (Distance <= 3 * Math.Max(OuterComponent.Width, OuterComponent.Height) || Angle == 0) { BestAngle = Math.Abs(Angle); MergeComponent = OuterComponent; MinDistance = Distance; } } } } } if (MergeComponent != null) { //we found a rectangle with which we can merge MergeComponent.Merge(InnerComponent); } }
/// <summary> /// This functions 'recognises' the given compontent by running its features through /// a neural network. /// </summary> /// <param name="Component"></param> public static void RecogniseWithoutConnectedRepair(ShapeNet ShapeNet, PageComponent Component) { sNeuralOutput Result = new sNeuralOutput(); ShapeNet.NeuralNetwork.ComputeOutput(RecogniseComponent.CalculateNetworkInput(Component), Result); for (int i = 0; i < Result.fOutputs.Count; i++) { if (Result.fOutputs[i] >= 0.01) { Component.AddRecognitionResult(Result.fOutputs[i], ShapeNet.ShapeList[i].Shape); } } }
/// <summary> /// This function calculates the angle between two component. /// </summary> /// <param name="From"></param> /// <param name="To"></param> /// <returns></returns> public static double AngleBetweenComponents(PageComponent From, PageComponent To) { double dx, dy, dInput, dAngle; int xTo, xFrom, yTo, yFrom; xTo = To.Area.X; xFrom = From.Area.X; yTo = To.Area.Y; yFrom = From.Area.Y; if (xTo <= xFrom && (xTo + To.Area.Width) > xFrom) { xTo = xFrom; } if (xFrom <= xTo && (xFrom + From.Area.Width) > xTo) { xFrom = xTo; } if (yTo <= yFrom && (yTo + To.Area.Height) > yFrom) { yTo = yFrom; } if (yFrom <= yTo && (yFrom + From.Area.Height) > yTo) { yFrom = yTo; } dx = System.Math.Abs(xTo - xFrom); dy = System.Math.Abs(yTo - yFrom); if (dx != 0) { dInput = dy / dx; dAngle = ((System.Math.Atan(dInput) * 360) / (2 * System.Math.PI)); } else { if (yTo != yFrom) { dAngle = 90; } else { dAngle = 0; } } return(dAngle); }
/// <summary> ///this function add spaces to sentences. Space are added between two ///characters when the space between the two is larger than ///60 percent of the average width of all the characters in the sentence /// </summary> /// <param name="Image"></param> public static void AddSpacesToSentences(PageImage Image) { int Pointer; int AverageWidthCharacters; int WidthBetweenCharacters; int nSpaces; PageComponent newSpace; Rectangle newArea; Point Component1, Component2; foreach (Sentence Sentence in Image.Sentences) { Pointer = Sentence.Count - 1; AverageWidthCharacters = (int)(Sentence.AverageWidth() * 0.60); if (Sentence.Components.Count > 1) { while (Pointer > 0) { WidthBetweenCharacters = (int)PageComponent.DistanceBetweenComponents(Sentence.Components[Pointer], Sentence.Components[Pointer - 1]); if (WidthBetweenCharacters >= AverageWidthCharacters && AverageWidthCharacters > 0) { nSpaces = WidthBetweenCharacters / AverageWidthCharacters; Component1 = Sentence.Components[Pointer - 1].CenterPoint; Component2 = Sentence.Components[Pointer].CenterPoint; for (int j = 0; j < nSpaces; j++) { //to do: the area part is mostly for visualising the space. Doesn't really needs to //be there. This is an option to speed it up. newSpace = new PageComponent(); newSpace.Type = ePageComponentType.eSpace; newArea = new Rectangle((Component2.X - Component1.X) / nSpaces + Component1.X, (Component2.Y - Component1.Y) / nSpaces + Component1.Y, 2, 2); newSpace.Area = newArea; Sentence.Insert(Pointer, newSpace); } } Pointer--; } } } }
/// <summary> /// Returns the average angle between the components in the sentence /// </summary> /// <returns></returns> public double AverageAngle() { if (m_AverageAngle == -1) { if (Components.Count > 0) { m_AverageAngle = PageComponent.AngleBetweenComponents(Components.First(), Components.Last()); } } //int PointerFrom, PointerTo, ComponentCount; //if (m_AverageAngle == -1) { // ComponentCount = 0; // m_AverageAngle = 0; // PointerFrom = 0; // PointerTo = 0; // do // { // while (Components[PointerTo].Type == eRectangleType.eSpace && PointerTo < Components.Count) // { // PointerTo++; // } // if (PointerTo < Components.Count) // { // m_AverageAngle += PageComponent.AngleBetweenComponents(Components[PointerFrom], Components[PointerTo]); // ComponentCount++; // } // PointerFrom = PointerTo; // PointerTo++; // } while (PointerTo < Components.Count); // if (ComponentCount > 0) // { // m_AverageAngle = m_AverageAngle / ComponentCount; // } //} return(m_AverageAngle); }
/// <summary> /// This function calculates the distance between the center points of two components /// </summary> /// <param name="From"></param> /// <param name="To"></param> /// <returns></returns> public static long Distance(PageComponent From, PageComponent To) { long lRetValue; Point Pos1; Point Pos2; double root1, root2; Pos1 = From.CenterPoint; Pos2 = To.CenterPoint; root1 = System.Math.Pow(System.Math.Abs(Pos1.X - Pos2.X), 2); root2 = System.Math.Pow(System.Math.Abs(Pos1.Y - Pos2.Y), 2); lRetValue = (long)System.Math.Sqrt(root1 + root2); return(lRetValue); }
private static void CreateTrainingData(OCR.ShapeNet ShapeNet, List <Example> Examples) { NeuralNetwork.LearningData oLearningData; OCR.PageComponent PageComponent; sNeuralInput newInput; foreach (Example Example in Examples) { PageComponent = new OCR.PageComponent(); PageComponent.LoadBitmap(Example.Filename); ExtractFeatures.ExecuteExtractFeatures(PageComponent, true); PageComponent.Position.Ascent = ShapeNet.ShapeList[Example.ShapeId].Position.Ascent; PageComponent.Position.Height = ShapeNet.ShapeList[Example.ShapeId].Position.Height; PageComponent.Position.Center = ShapeNet.ShapeList[Example.ShapeId].Position.Center; PageComponent.Position.Base = ShapeNet.ShapeList[Example.ShapeId].Position.Base; PageComponent.Position.Descent = ShapeNet.ShapeList[Example.ShapeId].Position.Descent; //Fill the oLearningData = new NeuralNetwork.LearningData(); oLearningData.oInput.fInputs.Clear(); oLearningData.oOutput.fOutputs.Clear(); newInput = RecogniseComponent.CalculateNetworkInput(PageComponent); oLearningData.oInput = newInput; for (long lIndex2 = 0; lIndex2 < ShapeNet.ShapeList.Count; lIndex2++) { if (Example.ShapeId == lIndex2) { oLearningData.oOutput.fOutputs.Add(1); } else { oLearningData.oOutput.fOutputs.Add(0); } } ShapeNet.NeuralNetwork.AddSituation(oLearningData); } ShapeNet.NeuralNetwork.ComputeInputRatios(); }
/// <summary> /// This function merges this Component with another component. /// </summary> /// <param name="MergeWith"></param> public PageComponent PartialCopy(Rectangle newArea) { //Create the new component PageComponent newComponent; newComponent = new PageComponent(); newComponent.Area = newArea; newComponent.Bytes = new Byte[newComponent.Width, newComponent.Height]; newComponent.BinaryBytes = new Byte[newComponent.Width, newComponent.Height]; newComponent.Type = ePageComponentType.eSplitCharacter; //Clear the memory block for (int x = 0; x < newComponent.Width; x++) { for (int y = 0; y < newComponent.Height; y++) { newComponent.Bytes[x, y] = 0xFF; newComponent.BinaryBytes[x, y] = 0xFF; } } //Copy the memory block of this component in the new one for (int y = newArea.Top; y < newArea.Bottom; y++) { for (int x = 0; x < newArea.Width; x++) { newComponent.Bytes[x, y - newArea.Top] = Bytes[newArea.X + x, y]; newComponent.BinaryBytes[x, y - newArea.Top] = BinaryBytes[newArea.X + x, y]; if (BinaryBytes[newArea.X + x, y] != 0xFF) { newComponent.PixelCount++; } } } return(newComponent); }
/// <summary> ///This function searches the distance matrix for the closest component ///to the given rectangle. The closest component must comply to ///the following rules: /// * Different from the given component /// * Component must not be a part of another sentence /// * Must be on the left side of given rectangle (if bLeft is true) /// * Must be on the right side of given rectangle (if bLeft is false) /// * Angle between rectangle and given rectangle must be less than 5 degrees /// </summary> /// <param name="Sentence"></param> /// <param name="CurrentComponent"></param> /// <param name="m_Areas"></param> /// <param name="Angle"></param> /// <param name="Left"></param> /// <returns></returns> public static PageComponent FindNearestComponent(Sentence Sentence, PageComponent CurrentComponent, List <DetectionArea> m_Areas, float Angle, bool Left) { int MinDistance; int Distance; MinDistance = 2000; PageComponent BestCandidate = null; int CurrentAreaIndex; DetectionArea Area; double AverageAngle = Sentence.AverageAngle(); int MaxAllowedDistance = (int)(Sentence.AverageHeight() * 1.5); int AreaIndex = CurrentComponent.DetectionAreaIndex; for (int Index = 0; Index < 6; Index++) { //the Index is converted to an area number according to the //matrix below. The start point is the center area (lIndex = 2). //lIndex 3 is on the left when bleft = true or on the right when bleft = false // ************* // * 1 * 0 * 1 * // ************* // * 3 * 2 * 3 * // ************* // * 5 * 4 * 5 * // ************* CurrentAreaIndex = AreaIndex; //translate the lIndex to the x axis of the areas if ((Index & 1) != 0) { if (Left) { CurrentAreaIndex--; } else { CurrentAreaIndex++; } } //translate the Index to the y axis of the areas CurrentAreaIndex += ((Index / 2) - 1) * NumberAreasX; //the current area number must ly within boundaries (0, number areas) //no throw here if it is outside the boundaries because when the area //lies on the edge of the image this algorithm will try to go further if (CurrentAreaIndex >= 0 && CurrentAreaIndex < m_Areas.Count) { Area = m_Areas[CurrentAreaIndex]; //loop through all the rectangles in the area foreach (PageComponent Candidate in Area.Components) { if (Candidate.Type == ePageComponentType.eCharacter) { //Candidate neighbour must be on left/right side if ((Left && Candidate.Area.X < CurrentComponent.Area.X) || (!Left && Candidate.Area.X > CurrentComponent.Area.X)) { Distance = PageComponent.DistanceBetweenComponents(CurrentComponent, Candidate); //Distance must be between current minimum and Maximum allowed if (MinDistance > Distance && MaxAllowedDistance > Distance) { //Angle between rectangle and sentence must be less than 5 degrees if (PageComponent.AngleBetweenComponents(CurrentComponent, Candidate) < 5 || (Sentence.Area.Bottom > Candidate.Area.Top && Sentence.Area.Top < Candidate.Area.Top) || (Sentence.Area.Bottom > Candidate.Area.Bottom && Sentence.Area.Top > Candidate.Area.Bottom)) { BestCandidate = Candidate; MinDistance = Distance; } } } } } } } if (BestCandidate != null) { m_Areas[BestCandidate.DetectionAreaIndex].Components.Remove(BestCandidate); } return(BestCandidate); }
private void RunThinning_Click(object sender, RoutedEventArgs e) { OCRStubControls.CharacterGrid[] CharacterGrids = new OCRStubControls.CharacterGrid[7]; OCR.PageImage PageImage; OCR.PageComponent PageComponent; //Initialize charactergrids CharacterGrids[0] = ThinningStep1; CharacterGrids[1] = ThinningStep2; CharacterGrids[2] = ThinningStep3; CharacterGrids[3] = ThinningStep4; CharacterGrids[4] = ThinningStep5; CharacterGrids[5] = ThinningStep6; CharacterGrids[6] = ThinningStep7; PageImage = new OCR.PageImage(); PageComponent = new OCR.PageComponent(); PageComponent.LoadBitmap(txtThinningSample.Text); for (int x = 0; x < PageComponent.Width; x++) { for (int y = 0; y < PageComponent.Height; y++) { PageComponent.BinaryBytes[x, y] = PageComponent.Bytes[x, y]; } } ExtractFeatures.CreateCompareMatrixWithoutPruning(PageComponent); for (int x = 0; x < PageComponent.Width; x++) { for (int y = 0; y < PageComponent.Height; y++) { PageComponent.BinaryBytes[x, y] = (PageComponent.Bytes[x, y] == 0xFF ? (Byte)0xFF : (Byte)0x00); } } CharacterGrids[0].Bytes = new byte[32, 32]; for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { CharacterGrids[0].Bytes[x, y] = PageComponent.CompareMatrix[x, y]; } } CharacterGrids[0].InvalidateVisual(); for (int i = 0; i < 6; i++) { CharacterGrids[i + 1].Bytes = new byte[32, 32]; for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { CharacterGrids[i + 1].Bytes[x, y] = PageComponent.CompareMatrix[x, y]; } } switch (ThinningAlgorithm.SelectedIndex) { case 0: //Standard ExtractFeatures.Thinning(CharacterGrids[i + 1].Bytes, 0, 0xFF, i + 1); break; case 1: //Erode ExtractFeatures.ErodeThinning(CharacterGrids[i + 1].Bytes, 0, 0xFF, i + 1); break; case 2: //Middle ExtractFeatures.MiddleThinning(CharacterGrids[i + 1].Bytes, 0, 0xFF); break; case 3: //Condensed PageComponent = new OCR.PageComponent(); PageComponent.LoadBitmap(txtThinningSample.Text); ExtractFeatures.CondensedThinning(PageImage, PageComponent, 0, 0xFF, i + 1); for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { CharacterGrids[i + 1].Bytes[x, y] = PageComponent.CompareMatrix[x, y]; } } break; case 4: //Pruning PageComponent = new OCR.PageComponent(); PageComponent.LoadBitmap(txtThinningSample.Text); for (int x = 0; x < PageComponent.Width; x++) { for (int y = 0; y < PageComponent.Height; y++) { PageComponent.BinaryBytes[x, y] = PageComponent.Bytes[x, y]; } } ExtractFeatures.ThinningPruningOnOriginalImage(PageComponent, PageComponent, 0, i + 1); ExtractFeatures.CreateCompareMatrixWithoutPruning(PageComponent); for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { CharacterGrids[i + 1].Bytes[x, y] = PageComponent.CompareMatrix[x, y]; } } break; case 5: //Pruning original PageComponent = new OCR.PageComponent(); PageComponent.LoadBitmap(txtThinningSample.Text); ExtractFeatures.ThinningPruningOnOriginalImage(PageImage, PageComponent, 2, i + 1); ExtractFeatures.CreateCompareMatrixWithoutPruning(PageComponent); for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { CharacterGrids[i + 1].Bytes[x, y] = PageComponent.CompareMatrix[x, y]; } } break; } CharacterGrids[i + 1].InvalidateVisual(); } }
private void btnTest_Click(object sender, RoutedEventArgs e) { OCR.ShapeNet SelectedShapeNet; OCR.PageComponent PageComponent; SelectedShapeNet = (OCR.ShapeNet)lstNeuralNetworks.SelectedItem; PageComponent.newID = 0; PageComponent = new OCR.PageComponent(); PageComponent.LoadBitmap(txtSample.Text); ExtractFeatures.ExecuteExtractFeatures(PageComponent, PageComponent, true); RecogniseComponent.Recognise(SelectedShapeNet, PageComponent); //Recognise the component lstResults.Items.Clear(); String ResultText = ""; foreach (RecognitionResult Result in PageComponent.RecognitionResults) { ResultText = Result.Content + " (" + Result.Probability + ")"; lstResults.Items.Add(ResultText); } //Build the original image PageComponent Original = new PageComponent(); Original.LoadBitmap(txtSample.Text); ExtractFeatures.CreateCompareMatrixWithoutPruning(Original, Original.Bytes); imgSample.Bytes = Original.CompareMatrix; for (int index = 0; index < 256; index++) { imgSample.GridBrushes[index] = new SolidColorBrush(System.Windows.Media.Color.FromRgb((byte)index, (byte)index, (byte)index)); } imgSample.InvalidateVisual(); ExtractFeatures.CreateCompareMatrixWithoutPruning(PageComponent); //Build the thinned and stroked image for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { if (PageComponent.StrokeMatrix[x, y] == 0xFF && PageComponent.CompareMatrix[x, y] != 0xFF) { PageComponent.StrokeMatrix[x, y] = 0x04; } if (PageComponent.PixelTypeMatrix[x, y] == ePixelType.End) { PageComponent.StrokeMatrix[x, y] = 0xFE; } if (PageComponent.PixelTypeMatrix[x, y] == ePixelType.Junction) { PageComponent.StrokeMatrix[x, y] = 0xFD; } } } imgProjection.Bytes = PageComponent.StrokeMatrix; imgProjection.GridBrushes[0] = new SolidColorBrush(Colors.Black); imgProjection.GridBrushes[4] = new SolidColorBrush(Colors.LightGray); imgProjection.GridBrushes[11] = new SolidColorBrush(Colors.Brown); imgProjection.GridBrushes[12] = new SolidColorBrush(Colors.Maroon); imgProjection.GridBrushes[13] = new SolidColorBrush(Colors.Magenta); imgProjection.GridBrushes[14] = new SolidColorBrush(Colors.Lime); imgProjection.GridBrushes[15] = new SolidColorBrush(Colors.LightCyan); imgProjection.GridBrushes[16] = new SolidColorBrush(Colors.Purple); imgProjection.GridBrushes[32] = new SolidColorBrush(Colors.Blue); imgProjection.GridBrushes[48] = new SolidColorBrush(Colors.Green); imgProjection.GridBrushes[64] = new SolidColorBrush(Colors.Yellow); imgProjection.GridBrushes[253] = new SolidColorBrush(Colors.Red); imgProjection.GridBrushes[254] = new SolidColorBrush(Colors.Red); imgProjection.InvalidateVisual(); lblStrokes.Content = "#Strokes: " + Convert.ToString(PageComponent.Strokes); //Bitmap StrokeMatrixBitmap = OCR.DebugTrace.DebugTrace.CreateBitmapFromByteArray(PageComponent.StrokeMatrix, new System.Drawing.Size(32, 32)); //StrokeMatrixBitmap.Save("d:\\ocr\\temp.bmp"); //imgProjection.Source = new BitmapImage(new Uri("d:\\ocr\\temp.bmp")); pbHorizontal.Clear(); int ProjectionValue; for (int x = 0; x < 3; x++) { ProjectionValue = 0; for (int y = 0; y < 3; y++) { ProjectionValue += PageComponent.PixelTypeProjectionJunction[x, y]; ProjectionValue += PageComponent.PixelTypeProjectionEndpoint[x, y]; } pbHorizontal.AddValue(ProjectionValue); } pbVertical.Clear(); for (int y = 0; y < 3; y++) { ProjectionValue = 0; for (int x = 0; x < 3; x++) { ProjectionValue += PageComponent.PixelTypeProjectionJunction[x, y]; ProjectionValue += PageComponent.PixelTypeProjectionEndpoint[x, y]; } pbVertical.AddValue(ProjectionValue); } pbStrokeHorizontal.Clear(); foreach (int Value in PageComponent.lStrokeDirectionX) { pbStrokeHorizontal.AddValue(Value); } pbStrokeVertical.Clear(); foreach (int Value in PageComponent.lStrokeDirectionY) { pbStrokeVertical.AddValue(Value); } }
public Boolean Add(PageComponent Child) { Components.Add(Child); return(true); }
/// <summary> /// Adds a component to the sentence /// </summary> /// <param name="Component"></param> public void Add(PageComponent Component) { ClearCache(); Components.Add(Component); }
/// <summary> /// Updates/replaces the given position in the sentence with a new component /// </summary> /// <param name="Position"></param> /// <param name="Component"></param> public void Update(int Position, PageComponent Component) { ClearCache(); Components[Position] = Component; }
/// <summary> /// This functions 'recognises' the given compontent by running its features through /// a neural network. /// </summary> /// <param name="Component"></param> public static void Recognise(ShapeNet ShapeNet, PageComponent Component) { RecogniseWithoutConnectedRepair(ShapeNet, Component); Component.CheckAndRepairConnected(ShapeNet); }
public static void ExecuteActivity(object Parameter) { WorkPackage WorkPackage; Rectangle Assignment; PageComponent newComponent; WorkPackage = (WorkPackage)Parameter; Assignment = (Rectangle)WorkPackage.Assignment; int y = Assignment.Top; int x = Assignment.Left; int pointer; List <Point> Pixels = new List <Point>(0); while (y < Assignment.Bottom) { x = Assignment.Left + ((y & 2) / 2); // we are using a mesh, therefor // we use y&1 to differentiate the starting point of each row. // the mesh looks like: // +-+-+-+-+ // --------- // -+-+-+-+- // --------- // +-+-+-+-+ // --------- // -+-+-+-+- // we check every other pixel, that way we can check half of the image // and still find all the component we want to find, except for lines // at exactly 45 degrees and 1 pixel wide. But at this point we find // those irrelevant; pointer = x + y * WorkPackage.Image.Stride; while (x < Assignment.Right) { if (WorkPackage.Image.BinaryBytes[x, y] == 0) { //We found a new pixel with the lock (WorkPackage.Image) { newComponent = new PageComponent(); } newComponent.Area = new Rectangle(x, y, 1, 1); Pixels.Clear(); if (GenerateConnectedComponent(WorkPackage, newComponent, Pixels)) { if (newComponent.Area.Height < WorkPackage.Image.Height * 0.9 && newComponent.Area.Width < WorkPackage.Image.Width * 0.9) { newComponent.PixelCount = Pixels.Count; GenerateBitmap(WorkPackage.Image, newComponent, Pixels); lock (WorkPackage.Image) { WorkPackage.Image.Components.Add(newComponent); } } } } x += 2; pointer += 2; } y += 2; } SignalWorkDone(); }
public static void AnalyseComponentType(PageComponent Component) { //see 'A word extraction algorithm for machine-printed documents using // a 3D neighborhood graph model' by H.Park, S.Ok, Y. Yu and H.CHo, 2001 // International Journal on Document Analysis and Recognition Component.Type = ePageComponentType.eCharacter; if ((Component.AreaSize) > (ComponentAverageArea * 25)) { Component.Type = ePageComponentType.eImageRect; } return; //const double cL0 = 0.8; //const double cD0 = 0.7; //const double cC1 = -1.09; //const double cC2 = 2.8; ////step 1, compare the area of the rectangle // //with the average rectangle size // if ((Component.AreaSize()) <= ComponentAverageArea) // { // Component.Type = eRectangleType.eCharacter; // } // else // { // //we have a rather large rectangle, proceed to step 2 // //checking of elongation. // double Elong = (double)System.Math.Min(Component.Width, Component.Height) / (double)System.Math.Max(Component.Width, Component.Height); // if (Elong <= cL0) { // //Step 3, compare density. Pictures normally have larger // //density than characters. // if (Component.Density >= cD0) // { // Component.Type = eRectangleType.eImageRect; // } // else // { // if (cC1 * CalculateRowVariance(Component) + cC2 <= CalculateColumnVariance(Component)) // { // Component.Type = eRectangleType.eInvertedCharacter; // } // else // { // Component.Type = eRectangleType.eImageRect; // } // } // } // else // { // if (cC1 * CalculateRowVariance(Component) + cC2 <= CalculateColumnVariance(Component)) // { // Component.Type = eRectangleType.eCharacter; // } // else // { // Component.Type = eRectangleType.eImageRect; // } // } //} }
private static bool GenerateConnectedComponent(WorkPackage WorkPackage, PageComponent newComponent, List <Point> Pixels) { //this functies growes the newComponent in such manner that it //surrounds connected pixels. //the function returns true if there are more than 8 connected pixels present. //The number of 8 comes from the article bu L.A.Fletcher and R.Kasturi, // 'A Robust Algorithm for Text String Separation from Mixed Text/Graphics Images' //in 'A word extraction algorithm for machine-printed documents using a // 3D neighborhood graph algorithm' a number of 6 pixels was mentioned //but for now the number of 8 seems to work allright. int pointer = 0; int Index = 0; Rectangle newArea; Point Pixel = newComponent.Area.Location; Point TopLeft = new Point(Pixel.X, Pixel.Y); Point BottomRight = new Point(Pixel.X, Pixel.Y); try { Pixels.Add(TopLeft); do { //get pixel from the queue Pixel = Pixels[Index]; //determine if we need to increase the area of the component. if (Pixel.X < TopLeft.X) { TopLeft.X = Pixel.X; } if (Pixel.Y < TopLeft.Y) { TopLeft.Y = Pixel.Y; } if (Pixel.X >= BottomRight.X) { BottomRight.X = Pixel.X + 1; } if (Pixel.Y >= BottomRight.Y) { BottomRight.Y = Pixel.Y + 1; } //calculate pointer and change color of pixel to make sure //it will not be detected for other connected components pointer = Pixel.X + WorkPackage.Image.Stride * Pixel.Y; lock (ImageWriteLocker) { if (WorkPackage.Image.BinaryBytes[Pixel.X, Pixel.Y] == (Byte)WorkPackage.Parameter || WorkPackage.Image.BinaryBytes[Pixel.X, Pixel.Y] == 0) { WorkPackage.Image.BinaryBytes[Pixel.X, Pixel.Y] = 64; //pixel is set } else { //detected a conflict with another thread, roll back! while (Index > 0) { Index--; Pixel = Pixels[Index]; pointer = Pixel.X + WorkPackage.Image.Stride * Pixel.Y; WorkPackage.Image.BinaryBytes[Pixel.X, Pixel.Y] = 0x00; } //throw an exception, leave this as quick as possible throw new ApplicationException("Conflict with another thread"); } } //look around and add interesting pixels to queue GCC_CheckNeighbour(WorkPackage, Pixel, -1, 0, Pixels); GCC_CheckNeighbour(WorkPackage, Pixel, 0, -1, Pixels); GCC_CheckNeighbour(WorkPackage, Pixel, 1, 0, Pixels); GCC_CheckNeighbour(WorkPackage, Pixel, 0, 1, Pixels); GCC_CheckNeighbour(WorkPackage, Pixel, -1, 1, Pixels); GCC_CheckNeighbour(WorkPackage, Pixel, 1, -1, Pixels); GCC_CheckNeighbour(WorkPackage, Pixel, -1, -1, Pixels); GCC_CheckNeighbour(WorkPackage, Pixel, 1, 1, Pixels); Index++; } while (Index < Pixels.Count); //Set the area of the component newArea = new Rectangle(TopLeft.X, TopLeft.Y, BottomRight.X - TopLeft.X, BottomRight.Y - TopLeft.Y); newComponent.Area = newArea; } catch (ApplicationException exp) { Console.WriteLine("Exception caught: " + exp.Message); Console.WriteLine(" in: " + exp.StackTrace); return(false); } //return true if there are more than 8 connected pixels return(Pixels.Count >= 2); // was 8 }
/// <summary> /// This function merges this Component with another component. /// </summary> /// <param name="MergeWith"></param> public void Merge(PageComponent MergeWith) { Rectangle newArea; Byte[,] newComponentBytes; Byte[,] newComponentBinaryBytes; int newStride; try { //Calculate the new area of the merged component newArea = new Rectangle(); newArea.X = Math.Min(Area.X, MergeWith.Area.X); newArea.Y = Math.Min(Area.Y, MergeWith.Area.Y); newArea.Width = Math.Max(Area.Right, MergeWith.Area.Right) - newArea.X; newArea.Height = Math.Max(Area.Bottom, MergeWith.Area.Bottom) - newArea.Y; //Calculate the size of the memory block which contains the pixels newStride = CalculateStride(newArea.Width); //Allocate the memory block newComponentBytes = new Byte[newArea.Width, newArea.Height]; newComponentBinaryBytes = new Byte[newArea.Width, newArea.Height]; //Clear the memory block int index = newComponentBytes.Length; for (int x = 0; x < newArea.Width; x++) { for (int y = 0; y < newArea.Height; y++) { newComponentBytes[x, y] = 0xFF; newComponentBinaryBytes[x, y] = 0xFF; } } //Copy the memory block of this component in the new one int PointerFrom; int PointerTo; for (int y = 0; y < Area.Height; y++) { PointerFrom = y * m_Stride; PointerTo = (Area.X - newArea.X) + (y + Area.Y - newArea.Y) * newStride; for (int x = 0; x < Area.Width; x++) { newComponentBytes[(x + Area.X - newArea.X), (y + Area.Y - newArea.Y)] = Bytes[x, y]; newComponentBinaryBytes[(x + Area.X - newArea.X), (y + Area.Y - newArea.Y)] = BinaryBytes[x, y]; } } //Copy the memory block of the merge component in the new one for (int y = 0; y < MergeWith.Area.Height; y++) { for (int x = 0; x < MergeWith.Area.Width; x++) { newComponentBytes[x + MergeWith.Area.X - newArea.X, y + MergeWith.Area.Y - newArea.Y] = MergeWith.Bytes[x, y]; newComponentBinaryBytes[x + MergeWith.Area.X - newArea.X, y + MergeWith.Area.Y - newArea.Y] = MergeWith.BinaryBytes[x, y]; } } //Update the properties of this component; Area = newArea; BinaryBytes = newComponentBinaryBytes; Bytes = newComponentBytes; PixelCount += MergeWith.PixelCount; } catch (Exception e) { Console.WriteLine("Exception caught: " + e.Message); Console.WriteLine(" In: " + e.StackTrace); } }
/// <summary> /// This function calculates the distance between the bounding boxes of two components /// </summary> /// <param name="ChildFrom"></param> /// <param name="ChildTo"></param> /// <returns></returns> public static int DistanceBetweenComponents(PageComponent From, PageComponent To) { int lRetValue; int xDelta, yDelta; int xMoved, yMoved; double Direction; Point PointFrom, PointTo, Position, Origin; lRetValue = 0; Direction = 0; PointFrom = From.CenterPoint; PointTo = To.CenterPoint; Origin = PointFrom; lRetValue = (int)Distance(From, To) + 1; if (System.Math.Abs(PointTo.X - PointFrom.X) > System.Math.Abs(PointTo.Y - PointFrom.Y)) { if (PointTo.X != PointFrom.X) { //just to be sure we don't get a division by zero Direction = (double)(PointTo.Y - PointFrom.Y) / (double)(PointTo.X - PointFrom.X); } Position = PointFrom; if (PointFrom.X > PointTo.X) { xDelta = -1; } else { xDelta = 1; } xMoved = 0; while (From.CoordinateInMe(Position)) { lRetValue--; xMoved += xDelta; Position.X += xDelta; Position.Y = (Origin.Y + (int)(xMoved * Direction)); } Position = PointTo; xMoved = 0; xDelta = xDelta * -1; //we walk to the different side while (To.CoordinateInMe(Position)) { lRetValue--; xMoved += xDelta; Position.X += xDelta; Position.Y = (Origin.Y + (int)(xMoved * Direction)); } } else { if (PointTo.Y != PointFrom.Y) //just to be sure we don't get a division by zero { Direction = (double)(PointTo.X - PointFrom.X) / (double)(PointTo.Y - PointFrom.Y); } Position = PointFrom; yDelta = (PointFrom.Y > PointTo.Y) ? -1 : 1; yMoved = 0; while (From.CoordinateInMe(Position)) { lRetValue--; yMoved += yDelta; Position.Y += yDelta; Position.X = Origin.X + (int)(yMoved * Direction); } Position = PointTo; yMoved = 0; yDelta = yDelta * -1; //we walk to the different side while (To.CoordinateInMe(Position)) { lRetValue--; yMoved += yDelta; Position.Y += yDelta; Position.X = Origin.X + (int)(yMoved * Direction); } } return(lRetValue); }
private static double CalculateColumnVariance(PageComponent Component) { List <int> RunsPerRow = new List <int>(); int Runs; int Pointer; Byte CurrentRun; double Average; double Variance; //Count number of pixels per column int y = 0; int x = Component.Width; while (x > 0) { x--; Runs = 1; y = Component.Height; Pointer = x + y * Component.Stride; CurrentRun = Component.BinaryBytes[x, y - 1]; while (y > 0) { y--; Pointer -= Component.Stride; if (CurrentRun != Component.BinaryBytes[x, y]) { Runs++; CurrentRun = Component.BinaryBytes[x, y]; } } RunsPerRow.Add(Runs); } //calculate average runs Average = 0; int i; i = RunsPerRow.Count - 1; while (i > 0) { i--; Average += System.Math.Abs(RunsPerRow[i + 1] - RunsPerRow[i]); } Average = Average / (RunsPerRow.Count - 1); //calculate variance; Variance = 0; i = RunsPerRow.Count - 1; while (i > 0) { i--; Variance += System.Math.Pow((System.Math.Abs(RunsPerRow[i + 1] - RunsPerRow[i]) - Average), 2); } Variance = Variance / (RunsPerRow.Count - 1); return(Variance); }
/// <summary> /// Inserts a component at the given position /// </summary> /// <param name="Position"></param> /// <param name="Component"></param> public void Insert(int Position, PageComponent Component) { ClearCache(); Components.Insert(Position, Component); }