Cleaned Window.cs

This commit is contained in:
2023-04-25 15:16:08 +02:00
parent 7cb27ca69c
commit 32b20ff317
4 changed files with 110 additions and 60 deletions

View File

@@ -54,7 +54,7 @@ namespace OCR_Decode
/// Enhances the image depending on wich type of window the image comes from
/// </summary>
/// <param name="type">The type of the window. Depending on it different enhancing features will be applied</param>
/// <returns></returns>
/// <returns>The enhanced Bitmap</returns>
public Bitmap Enhance(WindowType type = WindowType.Text)
{
Bitmap outputBitmap = (Bitmap)InputBitmap.Clone();
@@ -128,7 +128,7 @@ namespace OCR_Decode
/// Method that convert a colored RGB bitmap into a GrayScale image
/// </summary>
/// <param name="inputBitmap">The Bitmap you want to convert</param>
/// <returns></returns>
/// <returns>The bitmap in grayscale</returns>
public static Bitmap Grayscale(Bitmap inputBitmap)
{
Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height);
@@ -165,7 +165,7 @@ namespace OCR_Decode
/// </summary>
/// <param name="inputBitmap">the bitmap you want to convert to binary colors</param>
/// <param name="threshold">The floor at wich the color is considered as white or black</param>
/// <returns></returns>
/// <returns>The binarised bitmap</returns>
public static Bitmap Tresholding(Bitmap inputBitmap, int threshold)
{
Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height);
@@ -203,7 +203,7 @@ namespace OCR_Decode
/// Method that removes the pixels that are flagged as background
/// </summary>
/// <param name="inputBitmap">The bitmap you want to remove the background from</param>
/// <returns></returns>
/// <returns>The Bitmap without the background</returns>
public static Bitmap RemoveBG(Bitmap inputBitmap)
{
Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height);
@@ -237,7 +237,7 @@ namespace OCR_Decode
/// Method that removes all the useless things from the image and returns hopefully only the numbers
/// </summary>
/// <param name="inputBitmap">The bitmap you want to remove useless things from (Expects a cropped part of the TyreWindow)</param>
/// <returns></returns>
/// <returns>The bitmap with (hopefully) only the digits</returns>
public unsafe static Bitmap RemoveUseless(Bitmap inputBitmap)
{
//Note you can use something else than a cropped tyre window but I would recommend checking the code first to see if it fits your intended use
@@ -336,7 +336,7 @@ namespace OCR_Decode
/// Recovers the average colors from the Image. NOTE : It wont take in account colors that are lower than the background
/// </summary>
/// <param name="inputBitmap">The bitmap you want to get the average color from</param>
/// <returns></returns>
/// <returns>The average color of the bitmap</returns>
public static Color GetAvgColorFromBitmap(Bitmap inputBitmap)
{
Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height);
@@ -383,7 +383,7 @@ namespace OCR_Decode
/// This method simply inverts all the colors in a Bitmap
/// </summary>
/// <param name="inputBitmap">the bitmap you want to invert the colors from</param>
/// <returns></returns>
/// <returns>The bitmap with inverted colors</returns>
public static Bitmap InvertColors(Bitmap inputBitmap)
{
Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height);
@@ -415,7 +415,7 @@ namespace OCR_Decode
/// </summary>
/// <param name="inputBitmap">The bitmap you want to resize</param>
/// <param name="resizeFactor">The factor of resizing you want to use. I recommend using even numbers</param>
/// <returns></returns>
/// <returns>The bitmap witht the new size</returns>
public static Bitmap Resize(Bitmap inputBitmap, int resizeFactor)
{
var resultBitmap = new Bitmap(inputBitmap.Width * resizeFactor, inputBitmap.Height * resizeFactor);
@@ -432,7 +432,7 @@ namespace OCR_Decode
/// method that Highlights the countours of a Bitmap
/// </summary>
/// <param name="inputBitmap">The bitmap you want to highlight the countours of</param>
/// <returns></returns>
/// <returns>The bitmap with countours highlighted</returns>
public static Bitmap HighlightContours(Bitmap inputBitmap)
{
Bitmap outputBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height);
@@ -475,7 +475,7 @@ namespace OCR_Decode
/// </summary>
/// <param name="inputBitmap">The bitmap you want to erode</param>
/// <param name="kernelSize">The amount of Erosion you want (be carefull its expensive on ressources)</param>
/// <returns></returns>
/// <returns>The Bitmap with the eroded contents</returns>
public static Bitmap Erode(Bitmap inputBitmap, int kernelSize)
{
Bitmap outputBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height);
@@ -534,7 +534,7 @@ namespace OCR_Decode
/// </summary>
/// <param name="inputBitmap">The bitmap you want to use dilatation on</param>
/// <param name="kernelSize">The amount of dilatation you want (be carefull its expensive on ressources)</param>
/// <returns></returns>
/// <returns>The Bitmap after Dilatation</returns>
public static Bitmap Dilatation(Bitmap inputBitmap, int kernelSize)
{
Bitmap outputBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height);

View File

@@ -200,7 +200,7 @@ namespace OCR_Decode
/// Method that calls all the zones and windows to get the content they can find on the image to display them
/// </summary>
/// <param name="idImage">The id of the image we are working with</param>
/// <returns></returns>
/// <returns>a string representation of all the returns</returns>
public async Task<string> Decode(int idImage)
{
string result = "";
@@ -236,7 +236,7 @@ namespace OCR_Decode
/// Method that can be used to convert an amount of miliseconds into a more readable human form
/// </summary>
/// <param name="amountOfMs">The given amount of miliseconds ton convert</param>
/// <returns></returns>
/// <returns>A human readable string that represents the ms</returns>
public static string ConvertMsToTime(int amountOfMs)
{
//Convert.ToInt32 would round upand I dont want that
@@ -250,7 +250,7 @@ namespace OCR_Decode
/// Old method that can draw on an image where the windows and zones are created. mostly used for debugging
/// </summary>
/// <param name="idImage">the #id of the image we are working with</param>
/// <returns></returns>
/// <returns>the drawed bitmap</returns>
public Bitmap Draw(int idImage)
{
Bitmap result;

View File

@@ -1,4 +1,10 @@
using System;
/// Author : Maxime Rohmer
/// Date : 25/04/2023
/// File : Window.cs
/// Brief : Parent for futur windows and framework for the childrens
/// Version : 0.1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -20,13 +26,14 @@ namespace OCR_Decode
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; }
//This will have to be changed if you want to make it run on your machine
public static DirectoryInfo TESS_DATA_FOLDER = new DirectoryInfo(@"C:\Users\Moi\Pictures\SeleniumScreens\TessData");
public Bitmap WindowImage
{
get
{
//This little trickery lets you have the image that the window sees
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);
@@ -40,36 +47,61 @@ namespace OCR_Decode
Engine = new TesseractEngine(TESS_DATA_FOLDER.FullName, "eng", EngineMode.Default);
Engine.DefaultPageSegMode = PageSegMode.SingleLine;
}
/// <summary>
/// Method that will have to be used by the childrens to let the model make them decode the images they have
/// </summary>
/// <returns>Returns an object because we dont know what kind of return it will be</returns>
public virtual async Task<Object> DecodePng()
{
return "NaN";
}
public virtual async Task<Object> DecodePng(List<string> drivers)
/// <summary>
/// Method that will have to be used by the childrens to let the model make them decode the images they have
/// </summary>
/// <param name="driverList">This is a list of the different possible drivers in the race. It should not be too big but NEVER be too short</param>
/// <returns>Returns an object because we dont know what kind of return it will be</returns>
public virtual async Task<Object> DecodePng(List<string> driverList)
{
return "NaN";
}
public static byte[] ImageToByte(Image img)
/// <summary>
/// This converts an image into a byte[]. It can be usefull when doing unsafe stuff. Use at your own risks
/// </summary>
/// <param name="inputImage">The image you want to convert</param>
/// <returns>A byte array containing the image informations</returns>
public static byte[] ImageToByte(Image inputImage)
{
using (var stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
inputImage.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
return stream.ToArray();
}
}
public static async Task<int> GetTimeFromPng(Bitmap wImage,OcrImage.WindowType type, TesseractEngine Engine)
/// <summary>
/// This method is used to recover a time from a PNG using Tesseract OCR
/// </summary>
/// <param name="windowImage">The image where the text is</param>
/// <param name="windowType">The type of window it is</param>
/// <param name="Engine">The Tesseract Engine</param>
/// <returns>The time in milliseconds</returns>
public static async Task<int> GetTimeFromPng(Bitmap windowImage,OcrImage.WindowType windowType, TesseractEngine Engine)
{
//Kind of a big method but it has a lot of error handling and has to work with three special cases
string rawResult = "";
int result = 0;
switch (type)
switch (windowType)
{
case OcrImage.WindowType.Sector:
//The usual sector is in this form : 33.456
Engine.SetVariable("tessedit_char_whitelist", "0123456789.");
break;
case OcrImage.WindowType.LapTime:
//The usual Lap time is in this form : 1:45:345
Engine.SetVariable("tessedit_char_whitelist", "0123456789.:");
break;
case OcrImage.WindowType.Gap:
//The usual Gap is in this form : + 34.567
Engine.SetVariable("tessedit_char_whitelist", "0123456789.+");
break;
default:
@@ -78,7 +110,7 @@ namespace OCR_Decode
}
Bitmap enhancedImage = new OcrImage(wImage).Enhance(type);
Bitmap enhancedImage = new OcrImage(windowImage).Enhance(windowType);
var tessImage = Pix.LoadFromMemory(ImageToByte(enhancedImage));
@@ -105,8 +137,8 @@ namespace OCR_Decode
List<string> rawNumbers;
//In the gaps we can find '+' but we dont care about it its redondant
if(type == OcrImage.WindowType.Gap)
//In the gaps we can find '+' but we dont care about it its redondant a driver will never be - something
if(windowType == OcrImage.WindowType.Gap)
rawResult = Regex.Replace(rawResult, "[^0-9.:]", "");
//Splits into minuts seconds miliseconds
@@ -125,8 +157,6 @@ namespace OCR_Decode
{
//ss:ms
result = (Convert.ToInt32(rawNumbers[0]) * 1000) + Convert.ToInt32(rawNumbers[1]);
//1239
//931
if (result > 999999)
{
@@ -160,17 +190,24 @@ namespace OCR_Decode
}
}
}
//enhancedImage.Save(Reader.DEBUG_DUMP_FOLDER + Regex.Replace(rawResult, "[^0-9A-Za-z]", "") + ".png");
page.Dispose();
return result;
}
public static async Task<string> GetStringFromPng(Bitmap image, TesseractEngine Engine, string allowedChars = "",OcrImage.WindowType windowType = OcrImage.WindowType.Text)
/// <summary>
/// Method that recovers strings from an image using Tesseract OCR
/// </summary>
/// <param name="WindowImage">The image of the window that contains text</param>
/// <param name="Engine">The Tesseract engine</param>
/// <param name="allowedChars">The list of allowed chars</param>
/// <param name="windowType">The type of window the text is on. Depending on the context the OCR will behave differently</param>
/// <returns>the string it found</returns>
public static async Task<string> GetStringFromPng(Bitmap WindowImage, TesseractEngine Engine, string allowedChars = "",OcrImage.WindowType windowType = OcrImage.WindowType.Text)
{
string result = "";
Engine.SetVariable("tessedit_char_whitelist", allowedChars);
Bitmap rawData = image;
Bitmap rawData = WindowImage;
Bitmap enhancedImage = new OcrImage(rawData).Enhance(windowType);
Page page = Engine.Process(enhancedImage);
@@ -181,36 +218,37 @@ namespace OCR_Decode
{
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)
/// <summary>
/// Get a smaller image from a bigger one
/// </summary>
/// <param name="inputBitmap">The big bitmap you want to get a part of</param>
/// <param name="newBitmapDimensions">The dimensions of the new bitmap</param>
/// <returns>The little bitmap</returns>
protected Bitmap GetSmallBitmapFromBigOne(Bitmap inputBitmap, Rectangle newBitmapDimensions)
{
Bitmap sample = new Bitmap(rectangle.Width, rectangle.Height);
Bitmap sample = new Bitmap(newBitmapDimensions.Width, newBitmapDimensions.Height);
Graphics g = Graphics.FromImage(sample);
g.DrawImage(bmp, new Rectangle(0, 0, sample.Width, sample.Height), rectangle, GraphicsUnit.Pixel);
g.DrawImage(inputBitmap, new Rectangle(0, 0, sample.Width, sample.Height), newBitmapDimensions, GraphicsUnit.Pixel);
return sample;
}
protected static string FindClosestMatch(List<string> array, string target)
/// <summary>
/// Returns the closest string from a list of options
/// </summary>
/// <param name="options">an array of all the possibilities</param>
/// <param name="testString">the string you want to compare</param>
/// <returns>The closest option</returns>
protected static string FindClosestMatch(List<string> options, string testString)
{
var closestMatch = "";
var closestDistance = int.MaxValue;
foreach (var item in array)
foreach (var item in options)
{
var distance = LevenshteinDistance(item, target);
var distance = LevenshteinDistance(item, testString);
if (distance < closestDistance)
{
closestMatch = item;
@@ -220,39 +258,45 @@ namespace OCR_Decode
return closestMatch;
}
//This method has been generated with the help of ChatGPT
protected static int LevenshteinDistance(string s1, string s2)
/// <summary>
/// Method that computes a score of distance between two strings
/// </summary>
/// <param name="string1">The first string (order irrelevant)</param>
/// <param name="string2">The second string (order irrelevant)</param>
/// <returns>The levenshtein distance</returns>
protected static int LevenshteinDistance(string string1, string string2)
{
if (string.IsNullOrEmpty(s1))
if (string.IsNullOrEmpty(string1))
{
return string.IsNullOrEmpty(s2) ? 0 : s2.Length;
return string.IsNullOrEmpty(string2) ? 0 : string2.Length;
}
if (string.IsNullOrEmpty(s2))
if (string.IsNullOrEmpty(string2))
{
return string.IsNullOrEmpty(s1) ? 0 : s1.Length;
return string.IsNullOrEmpty(string1) ? 0 : string1.Length;
}
var d = new int[s1.Length + 1, s2.Length + 1];
for (var i = 0; i <= s1.Length; i++)
var d = new int[string1.Length + 1, string2.Length + 1];
for (var i = 0; i <= string1.Length; i++)
{
d[i, 0] = i;
}
for (var j = 0; j <= s2.Length; j++)
for (var j = 0; j <= string2.Length; j++)
{
d[0, j] = j;
}
for (var i = 1; i <= s1.Length; i++)
for (var i = 1; i <= string1.Length; i++)
{
for (var j = 1; j <= s2.Length; j++)
for (var j = 1; j <= string2.Length; j++)
{
var cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
var cost = (string1[i - 1] == string2[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];
return d[string1.Length, string2.Length];
}
}
}

View File

@@ -1,3 +1,9 @@
# OCR decode
Project that takes config files in the JSON format and decodes screenshots from the F1TV data channel.
Project that takes config files in the JSON format and decodes screenshots from the F1TV data channel.
If you want to use it go change thoses variables :
Reader.cs "DEBUG_DUMP_FOLDER"
Window.cs "TESS_DATA_FOLDER"