/// <summary> /// Recognized single char /// </summary> /// <param name="cleanLetter">B&W thresholded image with single char as white on black background</param> /// <returns>Best match recognition data</returns> RecognizedLetter ProcessSingleLetter(Mat cleanLetter) { // ocr int[] classes; double[] confidences; ocr.Eval(cleanLetter, out classes, out confidences); // process if (confidences.Length > 0) { var unit = new RecognizedLetter(); unit.Data = vocabulary[classes[0]].ToString(); unit.Confidence = confidences[0]; return(unit); } return(null); }
/// <summary> /// Recognizes letter on the image /// </summary> /// <param name="clean">B&W thresholded image with black background and white letters</param> /// <param name="possibilities">ROI as a list of RotatedRect's with letters marked</param> /// <returns>Recognized letters</returns> IList <RecognizedLetter> RecognizeLetters(Mat clean, IList <RotatedRect> possibilities) { var result = new List <RecognizedLetter>(); foreach (RotatedRect potentialLetter in possibilities) { var corners = Cv2.BoxPoints(potentialLetter); var chunk = clean.UnwrapShape(corners, 64); // check aspect as scanner might have issues with some "tall" chars like { I, J, L, etc. } and we can // help the detection here: as there are no that much wide chars, we presume all strongly disproportional chars // to be tall and reduce rotation options Mat[] letters = null; var size = potentialLetter.Size; var aspect = Math.Max(size.Width, size.Height) / Math.Min(size.Width, size.Height); if (aspect >= 2.0) { if (chunk.Cols > chunk.Rows) // it lays on "broadside", let's make it tall again { chunk = chunk.Rotate(RotateFlags.Rotate90Clockwise); } letters = new Mat[] { chunk, chunk.Rotate(RotateFlags.Rotate180) }; } else { // as we're not sure how exactly is letter rotated, we recognize it 4 times, // with a 90 degrees step, than the best match (best "confidence" from OCR) is // used letters = new Mat[] { chunk, chunk.Rotate(RotateFlags.Rotate90Clockwise), chunk.Rotate(RotateFlags.Rotate180), chunk.Rotate(RotateFlags.Rotate90CounterClockwise) }; } // ocr int selectedIndex = -1; RecognizedLetter unit = null; var lowerCases = new List <KeyValuePair <int, RecognizedLetter> >(); for (int i = 0; i < letters.Length; ++i) { var local = ProcessSingleLetter(letters[i]); if (null != local && (null == unit || (null != unit && null != local && local.Confidence > unit.Confidence))) { if (char.IsUpper(local.Data[0])) { unit = local; selectedIndex = i; } else { lowerCases.Add(new KeyValuePair <int, RecognizedLetter>(i, local)); } } //Cv2.ImShow(string.Format("#{0}: {1} - {2:00.0}%", i, local.Data, local.Confidence * 100), letters[i]); } if (null == unit && lowerCases.Count > 0) { lowerCases.Sort((x, y) => x.Value.Confidence.CompareTo(y.Value.Confidence)); unit = lowerCases[0].Value; selectedIndex = lowerCases[0].Key; } // process if (null != unit) { // this will show each separated letter in it's own window, un-wrapped and rotated according // to the best found match //Cv2.ImShow(unit.Data, letters[selectedIndex]); unit.Rect = Array.ConvertAll(corners, p => new Point(Math.Round(p.X), Math.Round(p.Y))); unit.Angle = (potentialLetter.Angle + selectedIndex * 90) % 360; result.Add(unit); } } return(result); }