251 lines
8.6 KiB
C#
251 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using Tesseract;
|
|
using System.Text.RegularExpressions;
|
|
using System.Drawing.Drawing2D;
|
|
|
|
namespace OCR_Decode
|
|
{
|
|
public class Window
|
|
{
|
|
private Rectangle _bounds;
|
|
private Bitmap _image;
|
|
private string _name;
|
|
//protected static TesseractEngine Engine = null;
|
|
public Rectangle Bounds { get => _bounds; private set => _bounds = value; }
|
|
public Bitmap Image { get => _image; set => _image = value; }
|
|
public string Name { get => _name; protected set => _name = value; }
|
|
|
|
public static DirectoryInfo TESS_DATA_FOLDER = new DirectoryInfo(@"C:\Users\Moi\Pictures\SeleniumScreens\TessData");
|
|
|
|
public Bitmap WindowImage
|
|
{
|
|
get
|
|
{
|
|
Bitmap sample = new Bitmap(Bounds.Width, Bounds.Height);
|
|
Graphics g = Graphics.FromImage(sample);
|
|
g.DrawImage(Image, new Rectangle(0, 0, sample.Width, sample.Height), Bounds, GraphicsUnit.Pixel);
|
|
return sample;
|
|
}
|
|
}
|
|
public Window(Bitmap image, Rectangle bounds)
|
|
{
|
|
Image = image;
|
|
Bounds = bounds;
|
|
/*
|
|
if (Engine == null)
|
|
{
|
|
Engine = new TesseractEngine(TESS_DATA_FOLDER.FullName, "eng", EngineMode.Default);
|
|
Engine.DefaultPageSegMode = PageSegMode.SingleLine;
|
|
}
|
|
*/
|
|
}
|
|
public virtual async Task<Object> DecodePng()
|
|
{
|
|
return "NaN";
|
|
}
|
|
public virtual async Task<Object> DecodePng(List<string> drivers)
|
|
{
|
|
return "NaN";
|
|
}
|
|
public static byte[] ImageToByte(Image img)
|
|
{
|
|
using (var stream = new MemoryStream())
|
|
{
|
|
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
|
|
return stream.ToArray();
|
|
}
|
|
}
|
|
public static async Task<int> GetTimeFromPng(Bitmap wImage,OcrImage.WindowType type, TesseractEngine Engine)
|
|
{
|
|
string rawResult = "";
|
|
int result = 0;
|
|
|
|
switch (type)
|
|
{
|
|
case OcrImage.WindowType.Sector:
|
|
Engine.SetVariable("tessedit_char_whitelist", "0123456789.");
|
|
break;
|
|
case OcrImage.WindowType.LapTime:
|
|
Engine.SetVariable("tessedit_char_whitelist", "0123456789.:");
|
|
break;
|
|
case OcrImage.WindowType.Gap:
|
|
Engine.SetVariable("tessedit_char_whitelist", "0123456789.+");
|
|
break;
|
|
default:
|
|
Engine.SetVariable("tessedit_char_whitelist", "");
|
|
break;
|
|
}
|
|
|
|
|
|
Bitmap enhancedImage = new OcrImage(wImage).Enhance(type);
|
|
|
|
var tessImage = Pix.LoadFromMemory(ImageToByte(enhancedImage));
|
|
|
|
Page page = Engine.Process(tessImage);
|
|
Graphics g = Graphics.FromImage(enhancedImage);
|
|
// Get the iterator for the page layout
|
|
using (var iter = page.GetIterator())
|
|
{
|
|
// Loop over the elements of the page layout
|
|
iter.Begin();
|
|
do
|
|
{
|
|
// Get the text for the current element
|
|
try
|
|
{
|
|
rawResult += iter.GetText(PageIteratorLevel.Word);
|
|
}
|
|
catch
|
|
{
|
|
//nothing we just dont add it if its not a number
|
|
}
|
|
} while (iter.Next(PageIteratorLevel.Word));
|
|
}
|
|
|
|
//enhancedImage.Save(Reader.DEBUG_DUMP_FOLDER + Regex.Replace(rawResult, "[^0-9A-Za-z]", "") + ".png");
|
|
|
|
List<string> rawNumbers;
|
|
|
|
//In the gaps we can find '+' but we dont care about it its redondant
|
|
if(type == OcrImage.WindowType.Gap)
|
|
rawResult = Regex.Replace(rawResult, "[^0-9.:]", "");
|
|
|
|
//Splits into minuts seconds miliseconds
|
|
rawNumbers = rawResult.Split('.', ':').ToList<string>();
|
|
//removes any empty cells (tho this usually sign of a really bad OCR implementation tbh will have to be fixed higher in the chian)
|
|
rawNumbers.RemoveAll(x => ((string)x) == "");
|
|
|
|
if (rawNumbers.Count == 3)
|
|
{
|
|
//mm:ss:ms
|
|
result = (Convert.ToInt32(rawNumbers[0]) * 1000 * 60) + (Convert.ToInt32(rawNumbers[1]) * 1000) + Convert.ToInt32(rawNumbers[2]);
|
|
}
|
|
else
|
|
{
|
|
if (rawNumbers.Count == 2)
|
|
{
|
|
//ss:ms
|
|
result = (Convert.ToInt32(rawNumbers[0]) * 1000) + Convert.ToInt32(rawNumbers[1]);
|
|
}
|
|
else
|
|
{
|
|
if (rawNumbers.Count == 1)
|
|
{
|
|
try
|
|
{
|
|
result = Convert.ToInt32(rawNumbers[0]);
|
|
}
|
|
catch
|
|
{
|
|
//It can be because the input is empty or because its the LEADER bracket
|
|
result = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Auuuugh
|
|
result = 0;
|
|
}
|
|
}
|
|
}
|
|
page.Dispose();
|
|
return result;
|
|
}
|
|
public static async Task<string> GetStringFromPng(Bitmap image, TesseractEngine Engine, string allowedChars = "",OcrImage.WindowType windowType = OcrImage.WindowType.Text)
|
|
{
|
|
string result = "";
|
|
|
|
Engine.SetVariable("tessedit_char_whitelist", allowedChars);
|
|
|
|
Bitmap rawData = image;
|
|
Bitmap enhancedImage = new OcrImage(rawData).Enhance(windowType);
|
|
|
|
Page page = Engine.Process(enhancedImage);
|
|
using (var iter = page.GetIterator())
|
|
{
|
|
iter.Begin();
|
|
do
|
|
{
|
|
result += iter.GetText(PageIteratorLevel.Word);
|
|
} while (iter.Next(PageIteratorLevel.Word));
|
|
}
|
|
/*
|
|
if (allowedChars.Contains("S"))
|
|
{
|
|
enhancedImage.Save(Reader.DEBUG_DUMP_FOLDER + "Tyre" + result +".png");
|
|
}
|
|
else
|
|
{
|
|
enhancedImage.Save(Reader.DEBUG_DUMP_FOLDER + result +".png");
|
|
}
|
|
*/
|
|
page.Dispose();
|
|
return result;
|
|
}
|
|
protected Bitmap GetSmallBitmapFromBigOne(Bitmap bmp, Rectangle rectangle)
|
|
{
|
|
Bitmap sample = new Bitmap(rectangle.Width, rectangle.Height);
|
|
Graphics g = Graphics.FromImage(sample);
|
|
g.DrawImage(bmp, new Rectangle(0, 0, sample.Width, sample.Height), rectangle, GraphicsUnit.Pixel);
|
|
return sample;
|
|
}
|
|
protected static string FindClosestMatch(List<string> array, string target)
|
|
{
|
|
var closestMatch = "";
|
|
var closestDistance = int.MaxValue;
|
|
|
|
foreach (var item in array)
|
|
{
|
|
var distance = LevenshteinDistance(item, target);
|
|
if (distance < closestDistance)
|
|
{
|
|
closestMatch = item;
|
|
closestDistance = distance;
|
|
}
|
|
}
|
|
return closestMatch;
|
|
}
|
|
//This method has been generated with the help of ChatGPT
|
|
protected static int LevenshteinDistance(string s1, string s2)
|
|
{
|
|
if (string.IsNullOrEmpty(s1))
|
|
{
|
|
return string.IsNullOrEmpty(s2) ? 0 : s2.Length;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(s2))
|
|
{
|
|
return string.IsNullOrEmpty(s1) ? 0 : s1.Length;
|
|
}
|
|
|
|
var d = new int[s1.Length + 1, s2.Length + 1];
|
|
for (var i = 0; i <= s1.Length; i++)
|
|
{
|
|
d[i, 0] = i;
|
|
}
|
|
|
|
for (var j = 0; j <= s2.Length; j++)
|
|
{
|
|
d[0, j] = j;
|
|
}
|
|
|
|
for (var i = 1; i <= s1.Length; i++)
|
|
{
|
|
for (var j = 1; j <= s2.Length; j++)
|
|
{
|
|
var cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
|
|
d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
|
|
}
|
|
}
|
|
|
|
return d[s1.Length, s2.Length];
|
|
}
|
|
}
|
|
}
|