diff --git a/OCR_Decode/DriverNameWindow.cs b/OCR_Decode/DriverNameWindow.cs index b4d0bf1..7788275 100644 --- a/OCR_Decode/DriverNameWindow.cs +++ b/OCR_Decode/DriverNameWindow.cs @@ -18,7 +18,7 @@ namespace OCR_Decode public override object DecodePng(List DriverList) { string result = ""; - result = GetStringFromPng(); + result = GetStringFromPng(WindowImage); if (!IsADriver(DriverList, result)) { diff --git a/OCR_Decode/DriverPositionWindow.cs b/OCR_Decode/DriverPositionWindow.cs index 827c88d..91915cb 100644 --- a/OCR_Decode/DriverPositionWindow.cs +++ b/OCR_Decode/DriverPositionWindow.cs @@ -15,7 +15,7 @@ namespace OCR_Decode } public override object DecodePng() { - string result = GetStringFromPng(true); + string result = GetStringFromPng(WindowImage,"0123456789"); int position; try diff --git a/OCR_Decode/DriverTyresWindow.cs b/OCR_Decode/DriverTyresWindow.cs index 2f2921d..39d2c5c 100644 --- a/OCR_Decode/DriverTyresWindow.cs +++ b/OCR_Decode/DriverTyresWindow.cs @@ -11,6 +11,13 @@ namespace OCR_Decode internal class DriverTyresWindow : Window { private static Random rnd = new Random(); + + public static Color SOFT_TYRE_COLOR = Color.FromArgb(0xff,0x00,0x00); + public static Color MEDIUM_TYRE_COLOR = Color.FromArgb(0xf5,0xbf,0x00); + public static Color HARD_TYRE_COLOR = Color.FromArgb(0xd9,0xd8,0xd4); + public static Color INTER_TYRE_COLOR = Color.FromArgb(0x00,0xa4,0x2e); + public static Color WET_TYRE_COLOR = Color.FromArgb(0x27,0x60,0xa6); + public DriverTyresWindow(Bitmap image, Rectangle bounds) : base(image, bounds) { Name = "Tyres"; @@ -23,50 +30,52 @@ namespace OCR_Decode } private Tyre GetTyreInfos() { - /* - string result = ""; + Bitmap tyreZone = GetSmallBitmapFromBigOne(WindowImage, FindTyreZone()); + //MHIWsmhiw so we can detect Soft Medium Hard Inters and Wet tyres + //If it is the first lap with the tyre just the letter is displayed + string text = GetStringFromPng(tyreZone,"0123456789SMHIWsmhiw",OcrImage.WindowType.Tyre); - Bitmap rawData = WindowImage; - //Hards softs mediums Inter Wet -> HSMIW - Engine.SetVariable("tessedit_char_whitelist", "0123456789HSMIW"); - Page page = Engine.Process(rawData); - Rectangle TyreRadius = new Rectangle(0, 0, 0, 0); - - using (var iter = page.GetIterator()) + tyreZone.Save(Reader.DEBUG_DUMP_FOLDER + "ZONE" + ".png"); + Tyre.Type type = Tyre.Type.Undefined; + int laps = -1; + if (text.All(char.IsDigit)) { - iter.Begin(); - do + //We have a lap count + if (text != "") { - Rect boundingBox; - - // Get the bounding box for the current element - if (iter.TryGetBoundingBox(PageIteratorLevel.Word, out boundingBox)) - { - int xOffset = ((boundingBox.Width / 100) * 20) / 2; - int yOffset = ((boundingBox.Height / 100) * 20) / 2; - using (Graphics g = Graphics.FromImage(WindowImage)) - { - //TyreRadius = new Rectangle(boundingBox.X1 - xOffset, boundingBox.Y1 - yOffset, boundingBox.Width + xOffset * 2, boundingBox.Height + yOffset * 2); - TyreRadius = new Rectangle(boundingBox.X1,boundingBox.Y1,boundingBox.Width,boundingBox.Height); - g.DrawRectangle(Pens.AliceBlue,TyreRadius); - GetSmallBitmapFromBigOne(WindowImage,TyreRadius).Save(Reader.DEBUG_DUMP_FOLDER + "Tyres" + result + ".png"); - } - - } - - result += iter.GetText(PageIteratorLevel.Word); - } while (iter.Next(PageIteratorLevel.Word)); + laps = Convert.ToInt32(text); + type = GetTyreTypeFromColor(OcrImage.GetAvgColorFromBitmap(tyreZone)); + } } - - //rawData.Save(Reader.DEBUG_DUMP_FOLDER + "Tyres" + result + ".png"); - - page.Dispose(); - */ - - GetSmallBitmapFromBigOne(WindowImage,FindTyreZone()).Save(Reader.DEBUG_DUMP_FOLDER + "Tyres" + rnd.Next(0,1000) + ".png"); - - - return new Tyre(Tyre.Type.Undefined,0); + else + { + //We have a tire type + laps = 0; + text = text.ToUpper(); + switch (text[0]) + { + case 'S': + type = Tyre.Type.Soft; + break; + case 'M': + type = Tyre.Type.Medium; + break; + case 'H': + type = Tyre.Type.Hard; + break; + case 'I': + type = Tyre.Type.Inter; + break; + case 'W': + type = Tyre.Type.Wet; + break; + default: + type = Tyre.Type.Undefined; + break; + } + } + tyreZone.Save(Reader.DEBUG_DUMP_FOLDER + "Tyre" + type + "Laps" + laps + '#' + rnd.Next(0, 1000) + ".png"); + return new Tyre(type,laps); } private Rectangle FindTyreZone() { @@ -76,7 +85,7 @@ namespace OCR_Decode Color limitColor = Color.FromArgb(0x50,0x50,0x50); Color currentColor = Color.FromArgb(0,0,0); - Size newWindowSize = new Size(bmp.Height,bmp.Height); + Size newWindowSize = new Size(bmp.Height -Convert.ToInt32((float)bmp.Height / 100f * 40f),bmp.Height - Convert.ToInt32((float)bmp.Height / 100f * 40f)); while(currentColor.R <= limitColor.R && currentColor.G <= limitColor.G && currentColor.B <= limitColor.B && currentPosition > 0) { @@ -84,27 +93,51 @@ namespace OCR_Decode currentColor = bmp.GetPixel(currentPosition,height); } - //Its here to let the new window include a little bit of the right side - int offset = Convert.ToInt32((float)newWindowSize.Width / 100f * 20f); - int CorrectedX = currentPosition - (newWindowSize.Width - offset); + //Its here to let the new window include a little bit of the right + int CorrectedX = currentPosition - (newWindowSize.Width); + int CorrectedY = Convert.ToInt32((float)newWindowSize.Height / 100f * 30f); if (CorrectedX <= 0) return new Rectangle(0,0,newWindowSize.Width,newWindowSize.Height); - return new Rectangle(CorrectedX,0,newWindowSize.Width,newWindowSize.Height); + return new Rectangle(CorrectedX,CorrectedY,newWindowSize.Width,newWindowSize.Height); } - private Bitmap GetSmallBitmapFromBigOne(Bitmap bmp, Rectangle rectangle) + //This method has been created with the help of chatGPT + public Tyre.Type GetTyreTypeFromColor(Color inputColor) { - 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; + Tyre.Type type = Tyre.Type.Undefined; + List colors = new List(); + colors.Add(SOFT_TYRE_COLOR); + colors.Add(MEDIUM_TYRE_COLOR); + colors.Add(HARD_TYRE_COLOR); + colors.Add(INTER_TYRE_COLOR); + colors.Add(WET_TYRE_COLOR); + + Color closestColor = colors[0]; + int closestDistance = int.MaxValue; + foreach (Color color in colors) + { + int distance = Math.Abs(color.R - inputColor.R) + Math.Abs(color.G - inputColor.G) + Math.Abs(color.B - inputColor.B); + if (distance < closestDistance) + { + closestColor = color; + closestDistance = distance; + } + } + + //We cant use a switch as the colors cant be constants ... + if (closestColor == SOFT_TYRE_COLOR) + type = Tyre.Type.Soft; + if (closestColor == MEDIUM_TYRE_COLOR) + type = Tyre.Type.Medium; + if (closestColor == HARD_TYRE_COLOR) + type = Tyre.Type.Hard; + if (closestColor == INTER_TYRE_COLOR) + type = Tyre.Type.Inter; + if (closestColor == WET_TYRE_COLOR) + type = Tyre.Type.Wet; + + return type; } - /* - public Tyre.Type GetColor(Bitmap bmp) - { - return Color.Violet; - } - */ } struct Tyre { diff --git a/OCR_Decode/Form1.cs b/OCR_Decode/Form1.cs index fa0a230..46856f2 100644 --- a/OCR_Decode/Form1.cs +++ b/OCR_Decode/Form1.cs @@ -24,6 +24,7 @@ namespace OCR_Decode private void Form1_Load(object sender, EventArgs e) { + Reader = new Reader(CONFIG_FILE,TEMPORARY_IMAGE_FOLDER); RefreshUi(); } diff --git a/OCR_Decode/OcrImage.cs b/OCR_Decode/OcrImage.cs index 1037a4f..572322d 100644 --- a/OCR_Decode/OcrImage.cs +++ b/OCR_Decode/OcrImage.cs @@ -21,6 +21,7 @@ namespace OCR_Decode Text, Sector, Gap, + Tyre, } public OcrImage(Bitmap inputImage) { @@ -46,6 +47,15 @@ namespace OCR_Decode result = Resize(result); result = Dilatation(result, 1); break; + case WindowType.Tyre: + //result = Tresholding(result, 200); + result = RemoveBG(result); + result = Grayscale(result); + result = InvertColors(result); + result = Resize(result); + //result = Resize(result); + //result = Dilatation(result,1); + break; default: result = Tresholding(result, 165); result = Resize(result); @@ -120,6 +130,79 @@ namespace OCR_Decode return bmp; } + public static Bitmap RemoveBG(Bitmap input) + { + Color limitColor = Color.FromArgb(0x50, 0x50, 0x50); + Bitmap bmp = input; + Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); + BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat); + int bytesPerPixel = Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8; + + unsafe + { + byte* ptr = (byte*)bmpData.Scan0.ToPointer(); + for (int y = 0; y < bmp.Height; y++) + { + byte* currentLine = ptr + (y * bmpData.Stride); + for (int x = 0; x < bmp.Width; x++) + { + byte* pixel = currentLine + (x * bytesPerPixel); + + int B = pixel[0]; + int G = pixel[1]; + int R = pixel[2]; + + if (R <= limitColor.R && G <= limitColor.G && B <= limitColor.B) + pixel[0] = pixel[1] = pixel[2] = 0; + } + } + } + bmp.UnlockBits(bmpData); + + return bmp; + } + public static Color GetAvgColorFromBitmap(Bitmap bmp) + { + Color limitColor = Color.FromArgb(0x50, 0x50, 0x50); + Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); + BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat); + int bytesPerPixel = Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8; + + int totR = 0; + int totG = 0; + int totB = 0; + + int totPixels = 1; + + unsafe + { + byte* ptr = (byte*)bmpData.Scan0.ToPointer(); + for (int y = 0; y < bmp.Height; y++) + { + byte* currentLine = ptr + (y * bmpData.Stride); + for (int x = 0; x < bmp.Width; x++) + { + byte* pixel = currentLine + (x * bytesPerPixel); + + int B = pixel[0]; + int G = pixel[1]; + int R = pixel[2]; + + //We remove the background pixels + if (R >= limitColor.R && G >= limitColor.G && B >= limitColor.B) + { + totPixels++; + totB += pixel[0]; + totG += pixel[1]; + totR += pixel[2]; + } + } + } + } + bmp.UnlockBits(bmpData); + + return Color.FromArgb(255,totR / totPixels,totG / totPixels,totB / totPixels); + } public static Bitmap InvertColors(Bitmap input) { Bitmap bmp = input; diff --git a/OCR_Decode/Window.cs b/OCR_Decode/Window.cs index 25bba75..91bc748 100644 --- a/OCR_Decode/Window.cs +++ b/OCR_Decode/Window.cs @@ -153,22 +153,14 @@ namespace OCR_Decode page.Dispose(); return result; } - protected string GetStringFromPng(bool onlyDigit = false) + public static string GetStringFromPng(Bitmap image,string allowedChars = "",OcrImage.WindowType windowType = OcrImage.WindowType.Text) { string result = ""; - if (onlyDigit) - { - Engine.SetVariable("tessedit_char_whitelist", "0123456789"); - } - else - { - Engine.SetVariable("tessedit_char_whitelist", ""); - } - + Engine.SetVariable("tessedit_char_whitelist", allowedChars); - Bitmap rawData = WindowImage; - Bitmap enhancedImage = new OcrImage(rawData).Enhance(); + Bitmap rawData = image; + Bitmap enhancedImage = new OcrImage(rawData).Enhance(windowType); Page page = Engine.Process(enhancedImage); using (var iter = page.GetIterator()) @@ -185,7 +177,13 @@ namespace OCR_Decode page.Dispose(); return result; } - //This method has been gnerated using ChatGPT + 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 array, string target) { var closestMatch = ""; @@ -202,7 +200,7 @@ namespace OCR_Decode } return closestMatch; } - //This is a tool to be able to compare strings + //This method has been generated with the help of ChatGPT protected static int LevenshteinDistance(string s1, string s2) { if (string.IsNullOrEmpty(s1))