解决方案必须是通用的(使用不同的字体和颜色)并且稳定。
输入数据是带有 X、Y 坐标的点,输出数据是矩形或更复杂的形状,其中包含单词或短语。
现在我使用hocr选项对整个图像进行 tesseract 识别,然后从输出 html 中提取所有矩形,最后找到最接近点的 reactangle。代码如下所示。但它效率低下,因为整个图像识别。
当然,可以识别不是整个图像,而是部分图像,但这也不是一个明确的解决方案,因为不同的字体大小和无用的单词识别都是一样的。
更新
public class WordRecognizerTesseractHocr
{
const string HelperFileName = "temp";
public string NextVariant()
{
Bitmap.Save(HelperFileName + ".png", ImageFormat.Png);
var startInfo = new ProcessStartInfo("tesseract.exe", HelperFileName + ".png temp hocr");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
var process = Process.Start(startInfo);
process.WaitForExit();
var result = GetNearestWord(File.ReadAllText(HelperFileName + ".html"), Position);
return result;
}
public string GetNearestWord(string tesseractHtml, Point position)
{
var xml = XDocument.Parse(tesseractHtml);
RectsWords = new Dictionary<Rectangle, string>();
var ocr_words = xml.Descendants("span").Where(element => element.Attribute("class").Value == "ocr_word").ToList();
foreach (var ocr_word in ocr_words)
{
var strs = ocr_word.Attribute("title").Value.Split(' ');
int left = int.Parse(strs[1]);
int top = int.Parse(strs[2]);
int width = int.Parse(strs[3]) - left + 1;
int height = int.Parse(strs[4]) - top + 1;
RectsWords.Add(new Rectangle(left, top, width, height), ocr_word.Value);
}
var nearestWords = RectsWords.OrderBy(rectWord => Distance(position, rectWord.Key));
return nearestWords.Count() != 0 ? nearestWords.First().Value : string.Empty;
}
public static double Distance(Point pos, Rectangle rect)
{
if (pos.X < rect.Left)
{
if (pos.Y < rect.Top)
return Math.Sqrt((rect.X - pos.X) * (rect.X - pos.X) + (rect.Top - pos.Y) * (rect.Top - pos.Y));
else if (pos.Y < rect.Top + rect.Height)
return rect.Left - pos.X;
else
return Math.Sqrt((rect.X - pos.X) * (rect.X - pos.X) +
(rect.Top + rect.Height - 1 - pos.Y) * (rect.Top + rect.Height - 1 - pos.Y));
}
else if (pos.X < rect.Left + rect.Width)
{
if (pos.Y < rect.Top)
return rect.Top - pos.Y;
else if (pos.Y < rect.Top + rect.Height)
return 0;
else
return pos.Y - (rect.Top + rect.Height - 1);
}
else
{
if (pos.Y < rect.Top)
return Math.Sqrt((rect.X + rect.Width - 1 - pos.X) * (rect.X + rect.Width - 1 - pos.X) +
(rect.Top - pos.Y) * (rect.Top - pos.Y));
else if (pos.Y < rect.Top + rect.Height)
return pos.X - (rect.Left + rect.Width - 1);
else
return Math.Sqrt((rect.X + rect.Width - 1 - pos.X) * (rect.X + rect.Width - 1 - pos.X) +
(rect.Top + rect.Height - 1 - pos.Y) * (rect.Top + rect.Height - 1 - pos.Y));
}
}
public IDictionary<Rectangle, string> RectsWords
{
get;
protected set;
}
}