From c94ceabc6be6c2a07352a5e8a553f02f4e457ba3 Mon Sep 17 00:00:00 2001 From: maxluli Date: Mon, 15 May 2023 11:15:12 +0200 Subject: [PATCH] Added to doc --- docs/index.md | 312 ++++++++++- temp_annexes/Code/ConfigurationTool.md | 198 ------- temp_annexes/Code/DriverData.md | 107 ---- temp_annexes/Code/DriverDrsWindow.md | 103 ---- temp_annexes/Code/DriverGapToLeaderWindow.md | 37 -- temp_annexes/Code/DriverLapTimeWindow.md | 37 -- temp_annexes/Code/DriverNameWindow.md | 63 --- temp_annexes/Code/DriverPositionWindow.md | 47 -- temp_annexes/Code/DriverSectorWindow.md | 37 -- temp_annexes/Code/DriverTyresWindow.md | 146 ----- temp_annexes/Code/F1TVEmulator.md | 293 ---------- temp_annexes/Code/Form1.md | 32 -- temp_annexes/Code/OcrImage.md | 544 ------------------- temp_annexes/Code/Program.md | 27 - temp_annexes/Code/Reader.md | 235 -------- temp_annexes/Code/Settings.md | 420 -------------- temp_annexes/Code/Window.md | 322 ----------- temp_annexes/Code/Zone.md | 242 --------- temp_annexes/Code/recoverCookiesCSV.md | 88 --- 19 files changed, 310 insertions(+), 2980 deletions(-) delete mode 100644 temp_annexes/Code/ConfigurationTool.md delete mode 100644 temp_annexes/Code/DriverData.md delete mode 100644 temp_annexes/Code/DriverDrsWindow.md delete mode 100644 temp_annexes/Code/DriverGapToLeaderWindow.md delete mode 100644 temp_annexes/Code/DriverLapTimeWindow.md delete mode 100644 temp_annexes/Code/DriverNameWindow.md delete mode 100644 temp_annexes/Code/DriverPositionWindow.md delete mode 100644 temp_annexes/Code/DriverSectorWindow.md delete mode 100644 temp_annexes/Code/DriverTyresWindow.md delete mode 100644 temp_annexes/Code/F1TVEmulator.md delete mode 100644 temp_annexes/Code/Form1.md delete mode 100644 temp_annexes/Code/OcrImage.md delete mode 100644 temp_annexes/Code/Program.md delete mode 100644 temp_annexes/Code/Reader.md delete mode 100644 temp_annexes/Code/Settings.md delete mode 100644 temp_annexes/Code/Window.md delete mode 100644 temp_annexes/Code/Zone.md delete mode 100644 temp_annexes/Code/recoverCookiesCSV.md diff --git a/docs/index.md b/docs/index.md index c42f076..5b4bebe 100644 --- a/docs/index.md +++ b/docs/index.md @@ -634,11 +634,313 @@ Maintenant que l'on sait comment simuler et manipuler un navigateur internet, qu #### Calibration -[AJOUTER EXPLICATION] +Maintenant que l'on a des images de la page Data de la F1TV on pourrait croire que c'est tout bon on peut direct passer à la partie OCR. Mais que nenni ! + +Le gros soucis de l'OCR c'est que sa précision est grandement réduite dès que l'on augmente la taille de la zone de recherche. Même simplement deux mots sur une image, si on les prends dans images individuelles on a de grandes chances de trouver quelque chose mais si on les mets les deux sur la même et que on tente l'OCR on va avoir de résultats bien moins bons. + +Et puis il faut aussi voir que selon les données que je cherche je ne peux pas faire le même traitement. + +Par exemple, savoir si le DRS est allumé, savoir quels pneus chausse un pilote et depuis combien de tours et savoir quel est le temps de son dernier tour, ce sont des informations qui demandent des traitements qui n'ont rien à voir. + +Il faut donc pouvoir dire au programme d'OCR ou se trouvent les informations et quelle est leur nature pour qu'il puisse les décoder. + +Il faut donc faire une calibration qui puisse donner toutes les infos importantes mais qui en même temps soit facile à utiliser car un utilisateur doit être capable de le faire assez facilement. + +Voici la liste des informations que l'on doit récupèrer : + +- La liste des pilotes présent sur le Grand Prix +- La position de la zone principale +- La position de chaque zone de pilote +- La position de toutes les Window sur chaque zone de pilote + +Le but a été de retirer le plus d'étapes possibles à l'utilisateur. Techniquement j'aurais pu faire une version complêtement manuelle mais ca aurait pris trop de temps alors il y a des systèmes qui permettent de rendre cette tâche moins pénible. + +##### Liste des pilotes + +Pour la liste des pilotes j'ai pensé à utiliser une API externe pour avoir une liste dans laquelle on pourrait selectionner des noms de pilotes sauf que j'ai abandonné l'idée car je trouvais que le projet avait déja bien assez de points qui dépendent de l'exterieur. + +Il y a donc une liste de pilotes dans laquelle on peut ajouter ou supprimer des noms de pilotes. L'idéal serait de mettre tous les pilotes de reserve comme ca si un pilote est malade sur une course on a pas besoin de venir changer la liste. + +##### Zone principale + +Pour la zone principale c'est complêtement manuel, on attend de l'utilisateur deux points x,y sur l'image pour ensuite avoir une idée de ou est sensé se trouver la zone. + +!["Exemple de zone principale"](./Images/Screens/MainZoneExample.png) + +##### Zones pilotes + +C'est la que ca devient intéressant. L'utilisateur n'a pas besoin de faire quoi que ce soit pour que le programme sache ou sont les zones des pilotes. + +J'aurais pu le faire manuellement en faisant choisir à l'utilisateur de donner deux points qui correspondent à la première zone et extrapoler pour en avoir 20. Sauf que si l'utilisateur n'est pas précis au pixel près (et même comme ca parfois) le vingtième pilote se retrouve avec une zone complêtement desaxée. + +La, le programme va "simplement" effectuer une reconaissance de texte sur toute l'image. Les résultats ne nous intéressent pas vraiment tout ce que l'on veut c'est la position des textes. + +Avec les position il est facile de determiner ou sont toutes les zones de pilotes et donc sans que l'utilisateur n'aie à toucher quoi que ce soit, dès qu'il a donné les infos pour la zone principale, les zones de pilotes sont determinées. + +!["Exemple zone pilote"](./Images/Screens/DriverZoneExample.png) + +Voici un exemple du code utilisé pour trouver ou dessiner des zones de pilotes : + +```Csharp +public void AutoCalibrate() + { + List detectedText = new List(); + List zones = new List(); + + TesseractEngine engine = new TesseractEngine(Window.TESS_DATA_FOLDER.FullName, "eng", EngineMode.Default); + Image image = MainZone.ZoneImage; + var tessImage = Pix.LoadFromMemory(Window.ImageToByte(image)); + + Page page = engine.Process(tessImage); + using (var iter = page.GetIterator()) + { + iter.Begin(); + do + { + Rect boundingBox; + if (iter.TryGetBoundingBox(PageIteratorLevel.Word, out boundingBox)) + { + //var text = iter.GetText(PageIteratorLevel.Word).ToUpper(); + //We remove all the rectangles that are definitely too big + if (boundingBox.Height < image.Height / NUMBER_OF_DRIVERS) + { + //Now we add a filter to only get the boxes in the right because they are much more reliable in size + if (boundingBox.X1 > image.Width / 2) + { + //Now we check if an other square box has been found roughly in the same y axis + bool match = false; + //The tolerance is roughly half the size that a window will be + int tolerance = (image.Height / NUMBER_OF_DRIVERS) / 2; + + foreach (Rectangle rect in detectedText) + { + if (rect.Y > boundingBox.Y1 - tolerance && rect.Y < boundingBox.Y1 + tolerance) + { + //There already is a rectangle in this line + match = true; + } + } + //if nothing matched we can add it + if (!match) + detectedText.Add(new Rectangle(boundingBox.X1, boundingBox.Y1, boundingBox.Width, boundingBox.Height)); + } + } + } + } while (iter.Next(PageIteratorLevel.Word)); + } + //DEBUG + int i = 1; + foreach (Rectangle Rectangle in detectedText) + { + Rectangle windowRectangle; + Size windowSize = new Size(image.Width, image.Height / NUMBER_OF_DRIVERS); + Point windowLocation = new Point(0, (Rectangle.Y + Rectangle.Height / 2) - windowSize.Height / 2); + windowRectangle = new Rectangle(windowLocation, windowSize); + //We add the driver zones + Zone driverZone = new Zone(MainZone.ZoneImage, windowRectangle, "DriverZone"); + MainZone.AddZone(driverZone); + + //driverZone.ZoneImage.Save("Driver" + i+".png"); + i++; + } + } +``` + +##### Windows pilotes + +C'est ici que c'est le plus pénible pour l'utilisateur, il doit selectionner manuellement les positions des fenêtres de données. Ensuite dès que l'utilisateur a donnée une position pour chaque window, on applique les positions pour chaque zone de pilote. + +Il y a plusieurs types de windows et selon le type le traitement est différent comme je l'ai dit plus tôt. Voici des exemples concrets : + +!["Exemple Window de pneus"](./Images/Screens/TyreZoneExemple1.png) + +!["Exemple Window temps au tour"](./Images/Screens/DriverLapTimeExample.png) + +!["Exemple window Drs"](./Images/Screens/DRSFalse.png) + +Il est important que toutes ces zones soient transmises avec le plus de précision possible pour que l'OCR puisse bien faire son boulot. + +##### Stockage + +Ensuite quand l'utilisateur a finit de configurer son flux, la configuration est stockée pour qu'il puisse ensuite la réutiliser pour tous les autres Grand Prix de l'année. + +Le stockage est fait sous format JSON et est fait pour que le programme d'OCR puisse lire dedans toutes les infos nescessaires. + +Cela fait des fichiers plutôt gros mais je n'avais pas vraiment le choix. J'ai testé une version avec seulement les infos de la première zone de pilote mais avec l'interpolation, les derniers pilotes se retrouvent avec des zones clairement pas à la bonne taille. + +Voici un exemple de ce à quoi ressemble le JSON final : + +```Json +{ + "Main": { + "x": 36, + "y": 343, + "width": 3780, + "height": 1454, + "DriverZones": [ + { + "name": "Driver1", + "x": 0, + "y": 1, + "width": 3780, + "height": 72, + "Windows": [ + { + "Position": { + "x": 45, + "y": 3, + "width": 76, + "height": 65 + }, + "GapToLeader": { + "x": 447, + "y": 1, + "width": 206, + "height": 67 + }, + "LapTime": { + "x": 863, + "y": 3, + "width": 229, + "height": 65 + }, + "DRS": { + "x": 1095, + "y": 1, + "width": 174, + "height": 67 + }, + "Tyres": { + "x": 1274, + "y": 3, + "width": 1448, + "height": 62 + }, + "Name": { + "x": 2724, + "y": 3, + "width": 361, + "height": 65 + }, + "Sector1": { + "x": 3088, + "y": 1, + "width": 239, + "height": 65 + }, + "Sector2": { + "x": 3314, + "y": 4, + "width": 190, + "height": 62 + }, + "Sector3": { + "x": 3493, + "y": 1, + "width": 198, + "height": 67 + } + } + ] + }, + { + "name": "Driver2", + "x": 0, + "y": 72, + "width": 3780, + "height": 72, + "Windows": [ + { + "Position": { + "x": 45, + "y": 3, + "width": 76, + "height": 65 + }, + "GapToLeader": { + "x": 447, + "y": 1, + "width": 206, + "height": 67 + }, + "LapTime": { + "x": 863, + "y": 3, + "width": 229, + "height": 65 + }, + "DRS": { + "x": 1095, + "y": 1, + "width": 174, + "height": 67 + }, + "Tyres": { + "x": 1274, + "y": 3, + "width": 1448, + "height": 62 + }, + "Name": { + "x": 2724, + "y": 3, + "width": 361, + "height": 65 + }, + "Sector1": { + "x": 3088, + "y": 1, + "width": 239, + "height": 65 + }, + "Sector2": { + "x": 3314, + "y": 4, + "width": 190, + "height": 62 + }, + "Sector3": { + "x": 3493, + "y": 1, + "width": 198, + "height": 67 + } + } + ] + } + [Other pilots...] + ], + "Drivers": [ + "Perez", + "Verstappen", + "Alonso", + "Sainz", + "Russel", + "Gasly", + "Leclerc", + "Ocon", + "Hulkenberg", + "Bottas", + "Hamilton", + "Albon", + "Tsunoda", + "Zhou", + "Stroll", + "De Vries", + "Magnussen", + "Norris", + "Piastri", + "Sargeant" + ] + } +} +``` + +Et avec tout ca. L'OCR peut démarrer dans de bonnes conditions ### OCR -Ici je vais parler de la seconde partie du projet qui parle du processus de reconnaissance de data sur une image du feed DATA de la F1TV. +Maintenant que on a des images qui arrivent automatiquement et que l'on sait ou se trouvent les informations sur ces dites images, je vais parler de la seconde partie du projet qui parle du processus de reconnaissance de data sur une image du feed DATA de la F1TV. C'est je pense la partie qui a demandé le plus tests et de refactor. @@ -1125,6 +1427,12 @@ Une variante spécialisée pour la reconnaissance des pneus appelée affectueuse Il y aussi d'autre methodes comme un filtre Gaussien ou *Highlight countour* que j'ai du développer mais que je n'ai pas utilisé donc je ne vais pas en parler ici. +###### Petit point résolution + +Un petit peu embarassant, vers les deux tiers du projet j'ai découvert un moyen de récupèrer des images 4K (ou presque) et du coup la reconnaissance était beaucoup moins demandeuse en filtres et en modifications. + +Donc même si toutes ces techniques sont encore utilisées pour certains cas spécifiques. Vers la fin du projet j'ai pu simplifier énormément la reconnaissance pour certaines windows en retirant certains filtres qui étaient redondants. + ### Interprétation des données ### Stockage des données diff --git a/temp_annexes/Code/ConfigurationTool.md b/temp_annexes/Code/ConfigurationTool.md deleted file mode 100644 index fc2b89a..0000000 --- a/temp_annexes/Code/ConfigurationTool.md +++ /dev/null @@ -1,198 +0,0 @@ -# ConfigurationTool.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : ConfigurationTool.cs -/// Brief : Class that contains all the methods needed to create a config file for the OCR -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Tesseract; -using System.IO; - -namespace Test_Merge -{ - public class ConfigurationTool - { - public Zone MainZone; - public const int NUMBER_OF_DRIVERS = 20; - public const int NUMBER_OF_ZONES = 9; - public const string CONFIGS_FOLDER_NAME = "./Presets/"; - - public ConfigurationTool(Bitmap fullImage, Rectangle mainZoneDimensions) - { - MainZone = new Zone(fullImage, mainZoneDimensions,"Main"); - AutoCalibrate(); - } - public void ResetMainZone() - { - MainZone.ResetZones(); - } - public void ResetWindows() - { - MainZone.ResetWindows(); - } - public void SaveToJson(List drivers, string configName) - { - string JSON = ""; - - JSON += "{" + Environment.NewLine; - JSON += MainZone.ToJSON() + "," + Environment.NewLine; - JSON += "\"Drivers\":[" + Environment.NewLine; - - for (int i = 0; i < drivers.Count; i++) - { - JSON += "\"" + drivers[i] + "\""; - if (i < drivers.Count - 1) - JSON += ","; - JSON += Environment.NewLine; - } - - JSON += "]" + Environment.NewLine; - - JSON += "}"; - - if (!Directory.Exists(CONFIGS_FOLDER_NAME)) - Directory.CreateDirectory(CONFIGS_FOLDER_NAME); - - string path = CONFIGS_FOLDER_NAME + configName; - - if (File.Exists(path + ".json")) - { - //We need to create a new name - int count = 2; - while (File.Exists(path + "_" + count + ".json")) - { - count++; - } - path += "_" + count + ".json"; - } - else - { - path += ".json"; - } - - File.WriteAllText(path, JSON); - } - public void AddWindows(List rectangles) - { - foreach (Zone driverZone in MainZone.Zones) - { - Bitmap zoneImage = driverZone.ZoneImage; - - for (int i = 1; i <= rectangles.Count; i++) - { - switch (i) - { - case 1: - //First zone should be the driver's Position - driverZone.AddWindow(new DriverPositionWindow(driverZone.ZoneImage, rectangles[i - 1], false)); - break; - case 2: - //First zone should be the Gap to leader - driverZone.AddWindow(new DriverGapToLeaderWindow(driverZone.ZoneImage, rectangles[i - 1], false)); - break; - case 3: - //First zone should be the driver's Lap Time - driverZone.AddWindow(new DriverLapTimeWindow(driverZone.ZoneImage, rectangles[i - 1], false)); - break; - case 4: - //First zone should be the driver's DRS status - driverZone.AddWindow(new DriverDrsWindow(driverZone.ZoneImage, rectangles[i - 1], false)); - break; - case 5: - //First zone should be the driver's Tyre's informations - driverZone.AddWindow(new DriverTyresWindow(driverZone.ZoneImage, rectangles[i - 1], false)); - break; - case 6: - //First zone should be the driver's Name - driverZone.AddWindow(new DriverNameWindow(driverZone.ZoneImage, rectangles[i - 1], false)); - break; - case 7: - //First zone should be the driver's First Sector - driverZone.AddWindow(new DriverSectorWindow(driverZone.ZoneImage, rectangles[i - 1], 1, false)); - break; - case 8: - //First zone should be the driver's Second Sector - driverZone.AddWindow(new DriverSectorWindow(driverZone.ZoneImage, rectangles[i - 1], 2, false)); - break; - case 9: - //First zone should be the driver's Position Sector - driverZone.AddWindow(new DriverSectorWindow(driverZone.ZoneImage, rectangles[i - 1], 3, false)); - break; - } - } - } - } - public void AutoCalibrate() - { - List detectedText = new List(); - List zones = new List(); - - TesseractEngine engine = new TesseractEngine(Window.TESS_DATA_FOLDER.FullName, "eng", EngineMode.Default); - Image image = MainZone.ZoneImage; - var tessImage = Pix.LoadFromMemory(Window.ImageToByte(image)); - - Page page = engine.Process(tessImage); - using (var iter = page.GetIterator()) - { - iter.Begin(); - do - { - Rect boundingBox; - if (iter.TryGetBoundingBox(PageIteratorLevel.Word, out boundingBox)) - { - //var text = iter.GetText(PageIteratorLevel.Word).ToUpper(); - //We remove all the rectangles that are definitely too big - if (boundingBox.Height < image.Height / NUMBER_OF_DRIVERS) - { - //Now we add a filter to only get the boxes in the right because they are much more reliable in size - if (boundingBox.X1 > image.Width / 2) - { - //Now we check if an other square box has been found roughly in the same y axis - bool match = false; - //The tolerance is roughly half the size that a window will be - int tolerance = (image.Height / NUMBER_OF_DRIVERS) / 2; - - foreach (Rectangle rect in detectedText) - { - if (rect.Y > boundingBox.Y1 - tolerance && rect.Y < boundingBox.Y1 + tolerance) - { - //There already is a rectangle in this line - match = true; - } - } - //if nothing matched we can add it - if (!match) - detectedText.Add(new Rectangle(boundingBox.X1, boundingBox.Y1, boundingBox.Width, boundingBox.Height)); - } - } - } - } while (iter.Next(PageIteratorLevel.Word)); - } - //DEBUG - int i = 1; - foreach (Rectangle Rectangle in detectedText) - { - Rectangle windowRectangle; - Size windowSize = new Size(image.Width, image.Height / NUMBER_OF_DRIVERS); - Point windowLocation = new Point(0, (Rectangle.Y + Rectangle.Height / 2) - windowSize.Height / 2); - windowRectangle = new Rectangle(windowLocation, windowSize); - //We add the driver zones - Zone driverZone = new Zone(MainZone.ZoneImage, windowRectangle, "DriverZone"); - MainZone.AddZone(driverZone); - - driverZone.ZoneImage.Save("Driver" + i+".png"); - i++; - } - } - } -} - -``` diff --git a/temp_annexes/Code/DriverData.md b/temp_annexes/Code/DriverData.md deleted file mode 100644 index c75f38e..0000000 --- a/temp_annexes/Code/DriverData.md +++ /dev/null @@ -1,107 +0,0 @@ -# DriverData.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverData.cs -/// Brief : Class used to store Driver informations -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Test_Merge -{ - public class DriverData - { - public bool DRS; //True = Drs is opened - public int GapToLeader; //In ms - public int LapTime; //In ms - public string Name; //Ex: LECLERC - public int Position; //Ex: 1 - public int Sector1; //in ms - public int Sector2; //in ms - public int Sector3; //in ms - public Tyre CurrentTyre;//Ex Soft 11 laps - - public DriverData(bool dRS, int gapToLeader, int lapTime, string name, int position, int sector1, int sector2, int sector3, Tyre tyre) - { - DRS = dRS; - GapToLeader = gapToLeader; - LapTime = lapTime; - Name = name; - Position = position; - Sector1 = sector1; - Sector2 = sector2; - Sector3 = sector3; - CurrentTyre = tyre; - } - public DriverData() - { - DRS = false; - GapToLeader = -1; - LapTime = -1; - Name = "Unknown"; - Position = -1; - Sector1 = -1; - Sector2 = -1; - Sector3 = -1; - CurrentTyre = new Tyre(Tyre.Type.Undefined, -1); - } - /// - /// Method that displays all the data found in a string - /// - /// string containing all the driver datas - public override string ToString() - { - string result = ""; - - //Position - result += "Position : " + Position + Environment.NewLine; - //Gap - result += "GapToLeader : " + Reader.ConvertMsToTime(GapToLeader) + Environment.NewLine; - //LapTime - result += "LapTime : " + Reader.ConvertMsToTime(LapTime) + Environment.NewLine; - //DRS - result += "DRS : " + DRS + Environment.NewLine; - //Tyres - result += "Uses " + CurrentTyre.Coumpound + " tyre " + CurrentTyre.NumberOfLaps + " laps old" + Environment.NewLine; - //Name - result += "DriverName : " + Name + Environment.NewLine; - //Sector 1 - result += "Sector1 : " + Reader.ConvertMsToTime(Sector1) + Environment.NewLine; - //Sector 1 - result += "Sector2 : " + Reader.ConvertMsToTime(Sector2) + Environment.NewLine; - //Sector 1 - result += "Sector3 : " + Reader.ConvertMsToTime(Sector3) + Environment.NewLine; - - return result; - } - } - //Structure to store tyres infos - public struct Tyre - { - //If new tyres were to be added you will have to need to change this enum - public enum Type - { - Soft, - Medium, - Hard, - Inter, - Wet, - Undefined - } - public Type Coumpound; - public int NumberOfLaps; - public Tyre(Type type, int laps) - { - Coumpound = type; - NumberOfLaps = laps; - } - } -} - -``` diff --git a/temp_annexes/Code/DriverDrsWindow.md b/temp_annexes/Code/DriverDrsWindow.md deleted file mode 100644 index d5aa591..0000000 --- a/temp_annexes/Code/DriverDrsWindow.md +++ /dev/null @@ -1,103 +0,0 @@ -# DriverDrsWindow.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverDrsWindow.cs -/// Brief : Window containing DRS related method and infos -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Tesseract; - -namespace Test_Merge -{ - internal class DriverDrsWindow:Window - { - private static int EmptyDrsGreenValue = -1; - private static Random rnd = new Random(); - public DriverDrsWindow(Bitmap image, Rectangle bounds,bool generateEngine = true) : base(image, bounds,generateEngine) - { - Name = "DRS"; - } - public override async Task DecodePng() - { - bool result = false; - int greenValue = GetGreenPixels(); - if (EmptyDrsGreenValue == -1) - EmptyDrsGreenValue = greenValue; - - if (greenValue > EmptyDrsGreenValue + EmptyDrsGreenValue / 100 * 30) - result = true; - - return result; - } - private unsafe int GetGreenPixels() - { - int tot = 0; - - Bitmap bmp = WindowImage; - Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); - BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, 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); - - byte blue = pixel[0]; - byte green = pixel[1]; - byte red = pixel[2]; - - if (green > blue * 1.5 && green > red * 1.5) - { - tot++; - } - } - } - } - bmp.UnlockBits(bmpData); - - return tot; - } - public Rectangle GetBox() - { - var tessImage = Pix.LoadFromMemory(ImageToByte(WindowImage)); - Engine.SetVariable("tessedit_char_whitelist", ""); - Page page = Engine.Process(tessImage); - - using (var iter = page.GetIterator()) - { - iter.Begin(); - do - { - Rect boundingBox; - - // Get the bounding box for the current element - if (iter.TryGetBoundingBox(PageIteratorLevel.Word, out boundingBox)) - { - page.Dispose(); - return new Rectangle(boundingBox.X1, boundingBox.X2, boundingBox.Width, boundingBox.Height); - } - } while (iter.Next(PageIteratorLevel.Word)); - - page.Dispose(); - return new Rectangle(0, 0, 0, 0); - } - } - } -} - -``` diff --git a/temp_annexes/Code/DriverGapToLeaderWindow.md b/temp_annexes/Code/DriverGapToLeaderWindow.md deleted file mode 100644 index dea25f4..0000000 --- a/temp_annexes/Code/DriverGapToLeaderWindow.md +++ /dev/null @@ -1,37 +0,0 @@ -# DriverGapToLeaderWindow.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverGapToLeaderWindow.cs -/// Brief : Window containing infos about the gap to the leader of a driver -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Test_Merge -{ - internal class DriverGapToLeaderWindow:Window - { - public DriverGapToLeaderWindow(Bitmap image, Rectangle bounds, bool generateEngine = true) : base(image, bounds,generateEngine) - { - Name = "GapToLeader"; - } - /// - /// Decodes the gap to leader using Tesseract OCR - /// - /// - public override async Task DecodePng() - { - int result = await GetTimeFromPng(WindowImage, OcrImage.WindowType.Gap, Engine); - return result; - } - } -} - -``` diff --git a/temp_annexes/Code/DriverLapTimeWindow.md b/temp_annexes/Code/DriverLapTimeWindow.md deleted file mode 100644 index 4281842..0000000 --- a/temp_annexes/Code/DriverLapTimeWindow.md +++ /dev/null @@ -1,37 +0,0 @@ -# DriverLapTimeWindow.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverLapTimeWindow -/// Brief : Window containing infos about the lap time of a driver -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; - -namespace Test_Merge -{ - internal class DriverLapTimeWindow:Window - { - public DriverLapTimeWindow(Bitmap image, Rectangle bounds, bool generateEngine = true) : base(image, bounds,generateEngine) - { - Name = "LapTime"; - } - /// - /// Decodes the lap time contained in the image using OCR Tesseract - /// - /// The laptime in int (ms) - public override async Task DecodePng() - { - int result = await GetTimeFromPng(WindowImage, OcrImage.WindowType.LapTime, Engine); - return result; - } - } -} - -``` diff --git a/temp_annexes/Code/DriverNameWindow.md b/temp_annexes/Code/DriverNameWindow.md deleted file mode 100644 index 153a87b..0000000 --- a/temp_annexes/Code/DriverNameWindow.md +++ /dev/null @@ -1,63 +0,0 @@ -# DriverNameWindow.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverNameWindow -/// Brief : Window containing infos about the name of the driver -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; - -namespace Test_Merge -{ - public class DriverNameWindow : Window - { - public static Random rnd = new Random(); - public DriverNameWindow(Bitmap image, Rectangle bounds, bool generateEngine = true) : base(image, bounds,generateEngine) - { - Name = "Name"; - } - /// - /// Decodes using OCR wich driver name is in the image - /// - /// - /// The driver name in string - public override async Task DecodePng(List DriverList) - { - string result = ""; - result = await GetStringFromPng(WindowImage, Engine); - - if (!IsADriver(DriverList, result)) - { - //I put everything in uppercase to try to lower the chances of bad answers - result = FindClosestMatch(DriverList.ConvertAll(d => d.ToUpper()), result.ToUpper()); - } - return result; - } - /// - /// Verifies that the name found in the OCR is a valid name - /// - /// - /// - /// If ye or no the driver exists - private static bool IsADriver(List driverList, string potentialDriver) - { - bool result = false; - //I cant use drivers.Contains because it has missmatched cases and all - foreach (string name in driverList) - { - if (name.ToUpper() == potentialDriver.ToUpper()) - result = true; - } - return result; - } - } -} - -``` diff --git a/temp_annexes/Code/DriverPositionWindow.md b/temp_annexes/Code/DriverPositionWindow.md deleted file mode 100644 index 74ce045..0000000 --- a/temp_annexes/Code/DriverPositionWindow.md +++ /dev/null @@ -1,47 +0,0 @@ -# DriverPositionWindow.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverPosition.cs -/// Brief : Window containing infos about the position of a driver. -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; - -namespace Test_Merge -{ - public class DriverPositionWindow:Window - { - public DriverPositionWindow(Bitmap image, Rectangle bounds, bool generateEngine = true) : base(image, bounds,generateEngine) - { - Name = "Position"; - } - /// - /// Decodes the position number using Tesseract OCR - /// - /// The position of the pilot in int - public override async Task DecodePng() - { - string ocrResult = await GetStringFromPng(WindowImage, Engine, "0123456789"); - - int position; - try - { - position = Convert.ToInt32(ocrResult); - } - catch - { - position = -1; - } - return position; - } - } -} - -``` diff --git a/temp_annexes/Code/DriverSectorWindow.md b/temp_annexes/Code/DriverSectorWindow.md deleted file mode 100644 index dc342d7..0000000 --- a/temp_annexes/Code/DriverSectorWindow.md +++ /dev/null @@ -1,37 +0,0 @@ -# DriverSectorWindow.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverSectorWindow.cs -/// Brief : Window containing infos about a driver sector time. Can be the first second or third, does not matter. -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; - -namespace Test_Merge -{ - internal class DriverSectorWindow:Window - { - public DriverSectorWindow(Bitmap image, Rectangle bounds, int sectorId, bool generateEngine = true) : base(image, bounds,generateEngine) - { - Name = "Sector"+sectorId; - } - /// - /// Decodes the sector - /// - /// the sector time in int (ms) - public override async Task DecodePng() - { - int ocrResult = await GetTimeFromPng(WindowImage, OcrImage.WindowType.Sector, Engine); - return ocrResult; - } - } -} - -``` diff --git a/temp_annexes/Code/DriverTyresWindow.md b/temp_annexes/Code/DriverTyresWindow.md deleted file mode 100644 index 485c438..0000000 --- a/temp_annexes/Code/DriverTyresWindow.md +++ /dev/null @@ -1,146 +0,0 @@ -# DriverTyresWindow.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : DriverTyresWindow.cs -/// Brief : Window containing infos about a driver's tyre -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; - -namespace Test_Merge -{ - public class DriverTyresWindow:Window - { - private static Random rnd = new Random(); - int seed = rnd.Next(0, 10000); - - //Those are the colors I found but you can change them if they change in the future like in 2019 - 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(0xa4, 0xa5, 0xa8); - public static Color INTER_TYRE_COLOR = Color.FromArgb(0x00, 0xa4, 0x2e); - public static Color WET_TYRE_COLOR = Color.FromArgb(0x27, 0x60, 0xa6); - public static Color EMPTY_COLOR = Color.FromArgb(0x20, 0x20, 0x20); - - public DriverTyresWindow(Bitmap image, Rectangle bounds, bool generateEngine = true) : base(image, bounds,generateEngine) - { - Name = "Tyres"; - } - /// - /// This will decode the content of the image - /// - /// And object containing what was on the image - public override async Task DecodePng() - { - return await GetTyreInfos(); - } - /// - /// Method that will decode whats on the image and return the tyre infos it could manage to recover - /// - /// A tyre object containing tyre infos - private async Task GetTyreInfos() - { - Bitmap tyreZone = GetSmallBitmapFromBigOne(WindowImage, FindTyreZone()); - Tyre.Type type = Tyre.Type.Undefined; - type = GetTyreTypeFromColor(OcrImage.GetAvgColorFromBitmap(tyreZone)); - int laps = -1; - - string number = await GetStringFromPng(tyreZone, Engine, "0123456789", OcrImage.WindowType.Tyre); - try - { - laps = Convert.ToInt32(number); - } - catch - { - //We could not convert the number so its a letter so its 0 laps old - laps = 0; - } - //tyreZone.Save(Reader.DEBUG_DUMP_FOLDER + "Tyre" + type + "Laps" + laps + '#' + rnd.Next(0, 1000) + ".png"); - return new Tyre(type, laps); - } - /// - /// Finds where the important part of the image is - /// - /// A rectangle containing position and dimensions of the important part of the image - private Rectangle FindTyreZone() - { - Bitmap bmp = WindowImage; - int currentPosition = bmp.Width; - int height = bmp.Height / 2; - Color limitColor = Color.FromArgb(0x50, 0x50, 0x50); - Color currentColor = Color.FromArgb(0, 0, 0); - - Size newWindowSize = new Size(bmp.Height - Convert.ToInt32((float)bmp.Height / 100f * 25f), bmp.Height - Convert.ToInt32((float)bmp.Height / 100f * 35f)); - - while (currentColor.R <= limitColor.R && currentColor.G <= limitColor.G && currentColor.B <= limitColor.B && currentPosition > 0) - { - currentPosition--; - currentColor = bmp.GetPixel(currentPosition, height); - } - - //Its here to let the new window include a little bit of the right - int CorrectedX = currentPosition - (newWindowSize.Width) + Convert.ToInt32((float)newWindowSize.Width / 100f * 10f); - int CorrectedY = Convert.ToInt32((float)newWindowSize.Height / 100f * 35f); - if (CorrectedX <= 0) - return new Rectangle(0, 0, newWindowSize.Width, newWindowSize.Height); - - return new Rectangle(CorrectedX, CorrectedY, newWindowSize.Width, newWindowSize.Height); - } - //This method has been created with the help of chatGPT - /// - /// Methods that compares a list of colors to see wich is the closest from the input color and decide wich tyre type it is - /// - /// The color that you found - /// The tyre type - public Tyre.Type GetTyreTypeFromColor(Color inputColor) - { - Tyre.Type type = Tyre.Type.Undefined; - List colors = new List(); - //dont forget that if for some reason someday F1 adds a new Tyre type you will need to add it in the constants but also here in the list - //You will also need to add it below in the Tyre object's enum and add an if in the end of this method - 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); - colors.Add(EMPTY_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; - if (closestColor == EMPTY_COLOR) - return Tyre.Type.Undefined; - - return type; - } - } -} - -``` diff --git a/temp_annexes/Code/F1TVEmulator.md b/temp_annexes/Code/F1TVEmulator.md deleted file mode 100644 index d21be84..0000000 --- a/temp_annexes/Code/F1TVEmulator.md +++ /dev/null @@ -1,293 +0,0 @@ -# F1TVEmulator.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : F1TVEmulator.cs -/// Brief : Class that contains methods to emulate a browser and navigate the F1TV website -/// Version : 0.1 - -using OpenQA.Selenium; -using OpenQA.Selenium.Firefox; -using OpenQA.Selenium.Interactions; -using OpenQA.Selenium.Support.UI; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Test_Merge -{ - internal class F1TVEmulator - { - public const string COOKIE_HOST = ".formula1.com"; - public const string PYTHON_COOKIE_RETRIEVAL_FILENAME = "recoverCookiesCSV.py"; - public const string GECKODRIVER_FILENAME = @"geckodriver-v0.27.0-win64\geckodriver.exe"; - //BE CAREFULL IF YOU CHANGE IT HERE YOU NEED TO CHANGE IT IN THE PYTHON SCRIPT TOO - public const string COOKIES_CSV_FILENAME = "cookies.csv"; - - private FirefoxDriver Driver; - - private bool _ready; - private string _grandPrixUrl; - public string GrandPrixUrl { get => _grandPrixUrl; private set => _grandPrixUrl = value; } - public bool Ready { get => _ready; set => _ready = value; } - public F1TVEmulator(string grandPrixUrl) - { - GrandPrixUrl = grandPrixUrl; - Ready = false; - } - private void StartCookieRecovering() - { - string scriptPath = PYTHON_COOKIE_RETRIEVAL_FILENAME; - Process process = new Process(); - process.StartInfo.FileName = "python.exe"; - process.StartInfo.Arguments = scriptPath; - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardOutput = true; - process.Start(); - string output = process.StandardOutput.ReadToEnd(); - process.WaitForExit(); - } - public string GetCookie(string host, string name) - { - StartCookieRecovering(); - string value = ""; - List cookies = new List(); - using (var reader = new StreamReader(COOKIES_CSV_FILENAME)) - { - // Read the header row and validate column order - string header = reader.ReadLine(); - string[] expectedColumns = { "host_key", "name", "value", "path", "expires_utc", "is_secure", "is_httponly" }; - string[] actualColumns = header.Split(','); - for (int i = 0; i < expectedColumns.Length; i++) - { - if (expectedColumns[i] != actualColumns[i]) - { - throw new InvalidOperationException($"Expected column '{expectedColumns[i]}' at index {i} but found '{actualColumns[i]}'"); - } - } - - // Read each data row and parse values into a Cookie object - while (!reader.EndOfStream) - { - string line = reader.ReadLine(); - string[] fields = line.Split(','); - - string hostname = fields[0]; - string cookieName = fields[1]; - - if (hostname == host && cookieName == name) - { - value = fields[2]; - } - } - } - - return value; - } - public async Task Start() - { - Ready = false; - - string loginCookieName = "login"; - string loginSessionCookieName = "login-session"; - string loginCookieValue = GetCookie(COOKIE_HOST, loginCookieName); - string loginSessionValue = GetCookie(COOKIE_HOST, loginSessionCookieName); - - int windowWidth = 1920; - int windowHeight = 768; - - var service = FirefoxDriverService.CreateDefaultService(GECKODRIVER_FILENAME); - service.Host = "127.0.0.1"; - service.Port = 5555; - - FirefoxProfile profile = new FirefoxProfile(); - FirefoxOptions options = new FirefoxOptions(); - //profile.SetPreference("full-screen-api.ignore-widgets", true); - //profile.SetPreference("media.hardware-video-decoding.enabled", true); - //profile.SetPreference("full-screen-api.enabled", true); - options.Profile = profile; - profile.SetPreference("layout.css.devPixelsPerPx", "1.0"); - - options.AcceptInsecureCertificates = true; - options.AddArgument("--headless"); - //options.AddArgument("--start-maximized"); - //options.AddArgument("--window-size=1920x1080"); - //options.AddArgument("--width=" + windowWidth); - //options.AddArgument("--height=" + windowHeight); - //options.AddArgument("-window-size=1920x1080"); - //options.AddArgument("--width=1920"); - //options.AddArgument("--height=1080"); - //profile - - try - { - Driver = new FirefoxDriver(service, options); - } - catch - { - Ready = false; - return 101; - } - - Actions actions = new Actions(Driver); - var loginCookie = new Cookie(loginCookieName, loginCookieValue, COOKIE_HOST, "/", DateTime.Now.AddDays(5)); - var loginSessionCookie = new Cookie(loginSessionCookieName, loginSessionValue, COOKIE_HOST, "/", DateTime.Now.AddDays(5)); - - Driver.Navigate().GoToUrl("https://f1tv.formula1.com/"); - - Driver.Manage().Cookies.AddCookie(loginCookie); - Driver.Manage().Cookies.AddCookie(loginSessionCookie); - - try - { - Driver.Navigate().GoToUrl(GrandPrixUrl); - } - catch - { - //The url is not a valid url - Driver.Dispose(); - return 103; - } - - //Waits for the page to fully load - Driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(30); - - //Removes the cookie prompt - try - { - IWebElement conscentButton = Driver.FindElement(By.Id("truste-consent-button")); - conscentButton.Click(); - } - catch - { - //Could not locate the cookie button - Screenshot("ERROR104"); - Driver.Dispose(); - return 104; - } - - //Again waits for the page to fully load (when you accept cookies it takes a little time for the page to load) - //Cannot use The timeout because the feed loading is not really loading so there is not event or anything - Thread.Sleep(5000); - - //Switches to the Data channel - try - { - IWebElement dataChannelButton = Driver.FindElement(By.ClassName("data-button")); - dataChannelButton.Click(); - } - catch - { - //If the data button does not exists its because the user is not connected - Screenshot("ERROR102"); - Driver.Dispose(); - return 102; - } - - //Open settings - // Press the space key, this should make the setting button visible - // It does not matter if the feed is paused because when changing channel it autoplays - actions.SendKeys(OpenQA.Selenium.Keys.Space).Perform(); - //Clicks on the settings Icon - - int tries = 0; - bool success = false; - while (tries < 100 && !success) - { - Thread.Sleep(100); - try - { - IWebElement settingsButton = Driver.FindElement(By.ClassName("bmpui-ui-settingstogglebutton")); - settingsButton.Click(); - IWebElement selectElement = Driver.FindElement(By.ClassName("bmpui-ui-videoqualityselectbox")); - SelectElement select = new SelectElement(selectElement); - IWebElement selectOption = selectElement.FindElement(By.CssSelector("option[value^='1080_']")); - selectOption.Click(); - success = true; - } - catch - { - //Sometimes it can crash because it could not get the options to show up in time. When it happens just retry - success = false; - tries++; - } - } - - if (!success) - { - Screenshot("ERROR105"); - Driver.Dispose(); - return 105; - } - - Screenshot("BEFOREFULLSCREEN"); - - //Makes the feed fullscreen - //Driver.Manage().Window.Size = new System.Drawing.Size(windowWidth, windowHeight); - Driver.Manage().Window.Maximize(); - WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)); - try - { - IWebElement fullScreenButton = Driver.FindElement(By.ClassName("bmpui-ui-fullscreentogglebutton")); - fullScreenButton.Click(); - } - catch - { - Screenshot("ERROR106"); - Driver.Dispose(); - return 106; - } - - Screenshot("AFTERFULLSCREEN"); - - //STARTUP FINISHED READY TO SCREENSHOT - Ready = true; - return 0; - } - public Bitmap Screenshot(string name = "TEST") - { - Bitmap result = new Bitmap(4242, 6969); - try - { - //Screenshot scrsht = ((ITakesScreenshot)Driver).GetScreenshot(); - //profileriver.SetPreference("layout.css.devPixelsPerPx", "1.0"); - - //Screenshot scrsht = Driver.GetFullPageScreenshot(); - Screenshot scrsht = Driver.GetScreenshot(); - - - byte[] screenshotBytes = Convert.FromBase64String(scrsht.AsBase64EncodedString); - MemoryStream stream = new MemoryStream(screenshotBytes); - - result = new Bitmap(stream); - //result.Save(name + ".png"); - scrsht.SaveAsFile(name + ".png"); - } - catch - { - //Nothing for now - } - return result; - } - public void Stop() - { - Ready = false; - Driver.Dispose(); - } - public void ResetDriver() - { - Ready = false; - Driver.Dispose(); - Driver = null; - } - } -} - -``` diff --git a/temp_annexes/Code/Form1.md b/temp_annexes/Code/Form1.md deleted file mode 100644 index 92eb895..0000000 --- a/temp_annexes/Code/Form1.md +++ /dev/null @@ -1,32 +0,0 @@ -# Form1.cs - -``` cs -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace Test_Merge -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - - private void btnSettings_Click(object sender, EventArgs e) - { - Settings settingsForm = new Settings(); - settingsForm.ShowDialog(); - MessageBox.Show(settingsForm.GrandPrixUrl + Environment.NewLine + settingsForm.GrandPrixName + Environment.NewLine + settingsForm.GrandPrixYear); - } - } -} - -``` diff --git a/temp_annexes/Code/OcrImage.md b/temp_annexes/Code/OcrImage.md deleted file mode 100644 index f3bb55b..0000000 --- a/temp_annexes/Code/OcrImage.md +++ /dev/null @@ -1,544 +0,0 @@ -# OcrImage.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : OcrImage.cs -/// Brief : Class containing all the methods used to enhance images for OCR -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; - -namespace Test_Merge -{ - public class OcrImage - { - //this is a hardcoded value based on the colors of the F1TV data channel background you can change it if sometime in the future the color changes - //Any color that has any of its R,G or B channel higher than the treshold will be considered as being usefull information - public static Color F1TV_BACKGROUND_TRESHOLD = Color.FromArgb(0x50, 0x50, 0x50); - Bitmap InputBitmap; - public enum WindowType - { - LapTime, - Text, - Sector, - Gap, - Tyre, - } - - /// - /// Create a new Ocr image to help enhance the given bitmap for OCR - /// - /// The image you want to enhance - public OcrImage(Bitmap inputBitmap) - { - InputBitmap = inputBitmap; - } - /// - /// Enhances the image depending on wich type of window the image comes from - /// - /// The type of the window. Depending on it different enhancing features will be applied - /// The enhanced Bitmap - public Bitmap Enhance(WindowType type = WindowType.Text) - { - Bitmap outputBitmap = (Bitmap)InputBitmap.Clone(); - switch (type) - { - case WindowType.LapTime: - outputBitmap = Tresholding(outputBitmap, 185); - outputBitmap = Resize(outputBitmap, 2); - outputBitmap = Dilatation(outputBitmap, 1); - outputBitmap = Erode(outputBitmap, 1); - break; - case WindowType.Text: - outputBitmap = InvertColors(outputBitmap); - outputBitmap = Tresholding(outputBitmap, 165); - outputBitmap = Resize(outputBitmap, 2); - outputBitmap = Dilatation(outputBitmap, 1); - break; - case WindowType.Tyre: - outputBitmap = RemoveUseless(outputBitmap); - outputBitmap = Resize(outputBitmap, 4); - outputBitmap = Dilatation(outputBitmap, 1); - break; - default: - outputBitmap = Tresholding(outputBitmap, 165); - outputBitmap = Resize(outputBitmap, 4); - outputBitmap = Erode(outputBitmap, 1); - break; - } - return outputBitmap; - } - /// - /// Method that convert a colored RGB bitmap into a GrayScale image - /// - /// The Bitmap you want to convert - /// The bitmap in grayscale - public static Bitmap Grayscale(Bitmap inputBitmap) - { - Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); - BitmapData bmpData = inputBitmap.LockBits(rect, ImageLockMode.ReadWrite, inputBitmap.PixelFormat); - int bytesPerPixel = Bitmap.GetPixelFormatSize(inputBitmap.PixelFormat) / 8; - - unsafe - { - byte* ptr = (byte*)bmpData.Scan0.ToPointer(); - for (int y = 0; y < inputBitmap.Height; y++) - { - byte* currentLine = ptr + (y * bmpData.Stride); - for (int x = 0; x < inputBitmap.Width; x++) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - byte blue = pixel[0]; - byte green = pixel[1]; - byte red = pixel[2]; - - //Those a specific values to correct the weights so its more pleasing to the human eye - int gray = (int)(red * 0.3 + green * 0.59 + blue * 0.11); - - pixel[0] = pixel[1] = pixel[2] = (byte)gray; - } - } - } - inputBitmap.UnlockBits(bmpData); - - return inputBitmap; - } - /// - /// Method that binaries the input image up to a certain treshold given - /// - /// the bitmap you want to convert to binary colors - /// The floor at wich the color is considered as white or black - /// The binarised bitmap - public static Bitmap Tresholding(Bitmap inputBitmap, int threshold) - { - Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); - BitmapData bmpData = inputBitmap.LockBits(rect, ImageLockMode.ReadWrite, inputBitmap.PixelFormat); - int bytesPerPixel = Bitmap.GetPixelFormatSize(inputBitmap.PixelFormat) / 8; - - unsafe - { - byte* ptr = (byte*)bmpData.Scan0.ToPointer(); - int bmpHeight = inputBitmap.Height; - int bmpWidth = inputBitmap.Width; - Parallel.For(0, bmpHeight, y => - { - byte* currentLine = ptr + (y * bmpData.Stride); - for (int x = 0; x < bmpWidth; x++) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - byte blue = pixel[0]; - byte green = pixel[1]; - byte red = pixel[2]; - //Those a specific values to correct the weights so its more pleasing to the human eye - int gray = (int)(red * 0.3 + green * 0.59 + blue * 0.11); - int value = gray < threshold ? 0 : 255; - - pixel[0] = pixel[1] = pixel[2] = (byte)value; - } - }); - } - inputBitmap.UnlockBits(bmpData); - - return inputBitmap; - } - /// - /// Method that removes the pixels that are flagged as background - /// - /// The bitmap you want to remove the background from - /// The Bitmap without the background - public static Bitmap RemoveBG(Bitmap inputBitmap) - { - Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); - BitmapData bmpData = inputBitmap.LockBits(rect, ImageLockMode.ReadWrite, inputBitmap.PixelFormat); - int bytesPerPixel = Bitmap.GetPixelFormatSize(inputBitmap.PixelFormat) / 8; - - unsafe - { - byte* ptr = (byte*)bmpData.Scan0.ToPointer(); - for (int y = 0; y < inputBitmap.Height; y++) - { - byte* currentLine = ptr + (y * bmpData.Stride); - for (int x = 0; x < inputBitmap.Width; x++) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - int B = pixel[0]; - int G = pixel[1]; - int R = pixel[2]; - - if (R <= F1TV_BACKGROUND_TRESHOLD.R && G <= F1TV_BACKGROUND_TRESHOLD.G && B <= F1TV_BACKGROUND_TRESHOLD.B) - pixel[0] = pixel[1] = pixel[2] = 0; - } - } - } - inputBitmap.UnlockBits(bmpData); - - return inputBitmap; - } - /// - /// Method that removes all the useless things from the image and returns hopefully only the numbers - /// - /// The bitmap you want to remove useless things from (Expects a cropped part of the TyreWindow) - /// The bitmap with (hopefully) only the digits - 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 - Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); - BitmapData bmpData = inputBitmap.LockBits(rect, ImageLockMode.ReadWrite, inputBitmap.PixelFormat); - int bytesPerPixel = Bitmap.GetPixelFormatSize(inputBitmap.PixelFormat) / 8; - - byte* ptr = (byte*)bmpData.Scan0.ToPointer(); - for (int y = 0; y < inputBitmap.Height; y++) - { - byte* currentLine = ptr + (y * bmpData.Stride); - - List pixelsToRemove = new List(); - - bool fromBorder = true; - - for (int x = 0; x < inputBitmap.Width; x++) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - int B = pixel[0]; - int G = pixel[1]; - int R = pixel[2]; - - if (fromBorder && B < F1TV_BACKGROUND_TRESHOLD.B && G < F1TV_BACKGROUND_TRESHOLD.G && R < F1TV_BACKGROUND_TRESHOLD.R) - { - pixelsToRemove.Add(x); - } - else - { - if (fromBorder) - { - fromBorder = false; - pixelsToRemove.Add(x); - } - } - } - fromBorder = true; - for (int x = inputBitmap.Width - 1; x > 0; x--) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - int B = pixel[0]; - int G = pixel[1]; - int R = pixel[2]; - - if (fromBorder && B < F1TV_BACKGROUND_TRESHOLD.B && G < F1TV_BACKGROUND_TRESHOLD.G && R < F1TV_BACKGROUND_TRESHOLD.R) - { - pixelsToRemove.Add(x); - } - else - { - if (fromBorder) - { - fromBorder = false; - pixelsToRemove.Add(x); - } - } - } - - foreach (int pxPos in pixelsToRemove) - { - byte* pixel = currentLine + (pxPos * bytesPerPixel); - - pixel[0] = 0xFF; - pixel[1] = 0xFF; - pixel[2] = 0xFF; - } - } - - //Removing the color parts - for (int y = 0; y < inputBitmap.Height; y++) - { - byte* currentLine = ptr + (y * bmpData.Stride); - for (int x = 0; x < inputBitmap.Width; x++) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - int B = pixel[0]; - int G = pixel[1]; - int R = pixel[2]; - - if (R >= F1TV_BACKGROUND_TRESHOLD.R + 15 || G >= F1TV_BACKGROUND_TRESHOLD.G + 15 || B >= F1TV_BACKGROUND_TRESHOLD.B + 15) - { - pixel[0] = 0xFF; - pixel[1] = 0xFF; - pixel[2] = 0xFF; - } - } - } - - inputBitmap.UnlockBits(bmpData); - return inputBitmap; - } - /// - /// Recovers the average colors from the Image. NOTE : It wont take in account colors that are lower than the background - /// - /// The bitmap you want to get the average color from - /// The average color of the bitmap - public static Color GetAvgColorFromBitmap(Bitmap inputBitmap) - { - Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); - BitmapData bmpData = inputBitmap.LockBits(rect, ImageLockMode.ReadWrite, inputBitmap.PixelFormat); - int bytesPerPixel = Bitmap.GetPixelFormatSize(inputBitmap.PixelFormat) / 8; - - int totR = 0; - int totG = 0; - int totB = 0; - - int totPixels = 1; - - unsafe - { - byte* ptr = (byte*)bmpData.Scan0.ToPointer(); - int bmpHeight = inputBitmap.Height; - int bmpWidth = inputBitmap.Width; - Parallel.For(0, bmpHeight, y => - { - byte* currentLine = ptr + (y * bmpData.Stride); - for (int x = 0; x < bmpWidth; x++) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - int B = pixel[0]; - int G = pixel[1]; - int R = pixel[2]; - - if (R >= F1TV_BACKGROUND_TRESHOLD.R || G >= F1TV_BACKGROUND_TRESHOLD.G || B >= F1TV_BACKGROUND_TRESHOLD.B) - { - totPixels++; - totB += pixel[0]; - totG += pixel[1]; - totR += pixel[2]; - } - } - }); - } - inputBitmap.UnlockBits(bmpData); - - return Color.FromArgb(255, Convert.ToInt32((float)totR / (float)totPixels), Convert.ToInt32((float)totG / (float)totPixels), Convert.ToInt32((float)totB / (float)totPixels)); - } - /// - /// This method simply inverts all the colors in a Bitmap - /// - /// the bitmap you want to invert the colors from - /// The bitmap with inverted colors - public static Bitmap InvertColors(Bitmap inputBitmap) - { - Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); - BitmapData bmpData = inputBitmap.LockBits(rect, ImageLockMode.ReadWrite, inputBitmap.PixelFormat); - int bytesPerPixel = Bitmap.GetPixelFormatSize(inputBitmap.PixelFormat) / 8; - - unsafe - { - byte* ptr = (byte*)bmpData.Scan0.ToPointer(); - for (int y = 0; y < inputBitmap.Height; y++) - { - byte* currentLine = ptr + (y * bmpData.Stride); - for (int x = 0; x < inputBitmap.Width; x++) - { - byte* pixel = currentLine + (x * bytesPerPixel); - - pixel[0] = (byte)(255 - pixel[0]); - pixel[1] = (byte)(255 - pixel[1]); - pixel[2] = (byte)(255 - pixel[2]); - } - } - } - inputBitmap.UnlockBits(bmpData); - - return inputBitmap; - } - /// - /// Methods that applies Bicubic interpolation to increase the size and resolution of an image - /// - /// The bitmap you want to resize - /// The factor of resizing you want to use. I recommend using even numbers - /// The bitmap witht the new size - public static Bitmap Resize(Bitmap inputBitmap, int resizeFactor) - { - var resultBitmap = new Bitmap(inputBitmap.Width * resizeFactor, inputBitmap.Height * resizeFactor); - - using (var graphics = Graphics.FromImage(resultBitmap)) - { - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.DrawImage(inputBitmap, new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height)); - } - - return resultBitmap; - } - /// - /// method that Highlights the countours of a Bitmap - /// - /// The bitmap you want to highlight the countours of - /// The bitmap with countours highlighted - public static Bitmap HighlightContours(Bitmap inputBitmap) - { - Bitmap outputBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height); - - Bitmap grayscale = Grayscale(inputBitmap); - Bitmap thresholded = Tresholding(grayscale, 128); - Bitmap dilated = Dilatation(thresholded, 3); - Bitmap eroded = Erode(dilated, 3); - - for (int y = 0; y < inputBitmap.Height; y++) - { - for (int x = 0; x < inputBitmap.Width; x++) - { - Color pixel = inputBitmap.GetPixel(x, y); - Color dilatedPixel = dilated.GetPixel(x, y); - Color erodedPixel = eroded.GetPixel(x, y); - - int gray = (int)(pixel.R * 0.3 + pixel.G * 0.59 + pixel.B * 0.11); - int threshold = dilatedPixel.R; - - if (gray > threshold) - { - outputBitmap.SetPixel(x, y, Color.FromArgb(255, 255, 255)); - } - else if (gray <= threshold && erodedPixel.R == 0) - { - outputBitmap.SetPixel(x, y, Color.FromArgb(255, 0, 0)); - } - else - { - outputBitmap.SetPixel(x, y, Color.FromArgb(0, 0, 0)); - } - } - } - - return outputBitmap; - } - /// - /// Method that that erodes the morphology of a bitmap - /// - /// The bitmap you want to erode - /// The amount of Erosion you want (be carefull its expensive on ressources) - /// The Bitmap with the eroded contents - public static Bitmap Erode(Bitmap inputBitmap, int kernelSize) - { - Bitmap outputBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height); - - int[,] kernel = new int[kernelSize, kernelSize]; - - for (int i = 0; i < kernelSize; i++) - { - for (int j = 0; j < kernelSize; j++) - { - kernel[i, j] = 1; - } - } - - for (int y = kernelSize / 2; y < inputBitmap.Height - kernelSize / 2; y++) - { - for (int x = kernelSize / 2; x < inputBitmap.Width - kernelSize / 2; x++) - { - bool flag = true; - - for (int i = -kernelSize / 2; i <= kernelSize / 2; i++) - { - for (int j = -kernelSize / 2; j <= kernelSize / 2; j++) - { - Color pixel = inputBitmap.GetPixel(x + i, y + j); - int gray = (int)(pixel.R * 0.3 + pixel.G * 0.59 + pixel.B * 0.11); - - if (gray >= 128 && kernel[i + kernelSize / 2, j + kernelSize / 2] == 1) - { - flag = false; - break; - } - } - - if (!flag) - { - break; - } - } - - if (flag) - { - outputBitmap.SetPixel(x, y, Color.FromArgb(255, 255, 255)); - } - else - { - outputBitmap.SetPixel(x, y, Color.FromArgb(0, 0, 0)); - } - } - } - - return outputBitmap; - } - /// - /// Method that that use dilatation of the morphology of a bitmap - /// - /// The bitmap you want to use dilatation on - /// The amount of dilatation you want (be carefull its expensive on ressources) - /// The Bitmap after Dilatation - public static Bitmap Dilatation(Bitmap inputBitmap, int kernelSize) - { - Bitmap outputBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height); - - int[,] kernel = new int[kernelSize, kernelSize]; - - for (int i = 0; i < kernelSize; i++) - { - for (int j = 0; j < kernelSize; j++) - { - kernel[i, j] = 1; - } - } - - for (int y = kernelSize / 2; y < inputBitmap.Height - kernelSize / 2; y++) - { - for (int x = kernelSize / 2; x < inputBitmap.Width - kernelSize / 2; x++) - { - bool flag = false; - - for (int i = -kernelSize / 2; i <= kernelSize / 2; i++) - { - for (int j = -kernelSize / 2; j <= kernelSize / 2; j++) - { - Color pixel = inputBitmap.GetPixel(x + i, y + j); - int gray = (int)(pixel.R * 0.3 + pixel.G * 0.59 + pixel.B * 0.11); - - if (gray < 128 && kernel[i + kernelSize / 2, j + kernelSize / 2] == 1) - { - flag = true; - break; - } - } - - if (flag) - { - break; - } - } - - if (flag) - { - outputBitmap.SetPixel(x, y, Color.FromArgb(0, 0, 0)); - } - else - { - outputBitmap.SetPixel(x, y, Color.FromArgb(255, 255, 255)); - } - } - } - - return outputBitmap; - } - } -} - -``` diff --git a/temp_annexes/Code/Program.md b/temp_annexes/Code/Program.md deleted file mode 100644 index e269b1e..0000000 --- a/temp_annexes/Code/Program.md +++ /dev/null @@ -1,27 +0,0 @@ -# Program.cs - -``` cs -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace Test_Merge -{ - internal static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new Form1()); - } - } -} - -``` diff --git a/temp_annexes/Code/Reader.md b/temp_annexes/Code/Reader.md deleted file mode 100644 index 407e0f4..0000000 --- a/temp_annexes/Code/Reader.md +++ /dev/null @@ -1,235 +0,0 @@ -# Reader.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : Reader.cs -/// Brief : Class used to Read the config file for the OCR -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; -using System.Windows.Forms; -using System.IO; -using System.Text.Json; - -namespace Test_Merge -{ - public class Reader - { - const int NUMBER_OF_DRIVERS = 20; - public List Drivers; - public List MainZones; - - public Reader(string configFile, Bitmap image,bool loadOCR = true) - { - MainZones = Load(image,configFile,ref Drivers,loadOCR); - } - /// - /// Method that reads the JSON config file and create all the Zones and Windows - /// - /// The image #id on wich you want to create the zones on - public static List Load(Bitmap image,string configFilePath,ref List driverListToFill,bool LoadOCR) - { - List mainZones = new List(); - Bitmap fullImage = image; - List drivers; - Zone mainZone; - - try - { - using (var streamReader = new StreamReader(configFilePath)) - { - var jsonText = streamReader.ReadToEnd(); - var jsonDocument = JsonDocument.Parse(jsonText); - - var driversNames = jsonDocument.RootElement.GetProperty("Drivers"); - driverListToFill = new List(); - - foreach (var nameElement in driversNames.EnumerateArray()) - { - driverListToFill.Add(nameElement.GetString()); - } - - var mainProperty = jsonDocument.RootElement.GetProperty("Main"); - Point MainPosition = new Point(mainProperty.GetProperty("x").GetInt32(), mainProperty.GetProperty("y").GetInt32()); - Size MainSize = new Size(mainProperty.GetProperty("width").GetInt32(), mainProperty.GetProperty("height").GetInt32()); - Rectangle MainRectangle = new Rectangle(MainPosition, MainSize); - mainZone = new Zone(image, MainRectangle,"Main"); - - var zones = mainProperty.GetProperty("Zones"); - var driverZone = zones[0].GetProperty("DriverZone"); - - Point FirstZonePosition = new Point(driverZone.GetProperty("x").GetInt32(), driverZone.GetProperty("y").GetInt32()); - Size FirstZoneSize = new Size(driverZone.GetProperty("width").GetInt32(), driverZone.GetProperty("height").GetInt32()); - - var windows = driverZone.GetProperty("Windows"); - - var driverPosition = windows[0].GetProperty("Position"); - Size driverPositionArea = new Size(driverPosition.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverPositionPosition = new Point(driverPosition.GetProperty("x").GetInt32(), driverPosition.GetProperty("y").GetInt32()); - - var driverGapToLeader = windows[0].GetProperty("GapToLeader"); - Size driverGapToLeaderArea = new Size(driverGapToLeader.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverGapToLeaderPosition = new Point(driverGapToLeader.GetProperty("x").GetInt32(), driverGapToLeader.GetProperty("y").GetInt32()); - - var driverLapTime = windows[0].GetProperty("LapTime"); - Size driverLapTimeArea = new Size(driverLapTime.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverLapTimePosition = new Point(driverLapTime.GetProperty("x").GetInt32(), driverLapTime.GetProperty("y").GetInt32()); - - - var driverDrs = windows[0].GetProperty("DRS"); - Size driverDrsArea = new Size(driverDrs.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverDrsPosition = new Point(driverDrs.GetProperty("x").GetInt32(), driverDrs.GetProperty("y").GetInt32()); - - var driverTyres = windows[0].GetProperty("Tyres"); - Size driverTyresArea = new Size(driverTyres.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverTyresPosition = new Point(driverTyres.GetProperty("x").GetInt32(), driverTyres.GetProperty("y").GetInt32()); - - var driverName = windows[0].GetProperty("Name"); - Size driverNameArea = new Size(driverName.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverNamePosition = new Point(driverName.GetProperty("x").GetInt32(), driverName.GetProperty("y").GetInt32()); - - var driverSector1 = windows[0].GetProperty("Sector1"); - Size driverSector1Area = new Size(driverSector1.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverSector1Position = new Point(driverSector1.GetProperty("x").GetInt32(), driverSector1.GetProperty("y").GetInt32()); - - var driverSector2 = windows[0].GetProperty("Sector2"); - Size driverSector2Area = new Size(driverSector2.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverSector2Position = new Point(driverSector2.GetProperty("x").GetInt32(), driverSector2.GetProperty("y").GetInt32()); - - var driverSector3 = windows[0].GetProperty("Sector3"); - Size driverSector3Area = new Size(driverSector3.GetProperty("width").GetInt32(), FirstZoneSize.Height); - Point driverSector3Position = new Point(driverSector3.GetProperty("x").GetInt32(), driverSector3.GetProperty("y").GetInt32()); - - float offset = (((float)mainZone.ZoneImage.Height - (float)(driverListToFill.Count * FirstZoneSize.Height)) / (float)driverListToFill.Count); - Bitmap MainZoneImage = mainZone.ZoneImage; - List zonesToAdd = new List(); - List zonesImages = new List(); - - for (int i = 0; i < NUMBER_OF_DRIVERS; i++) - { - Point tmpPos = new Point(0, FirstZonePosition.Y + i * FirstZoneSize.Height - Convert.ToInt32(i * offset)); - Zone newDriverZone = new Zone(MainZoneImage, new Rectangle(tmpPos, FirstZoneSize), "DriverZone"); - zonesToAdd.Add(newDriverZone); - zonesImages.Add(newDriverZone.ZoneImage); - - newDriverZone.ZoneImage.Save("Driver"+i+".png"); - } - - //Parallel.For(0, NUMBER_OF_DRIVERS, i => - for (int i = 0; i < NUMBER_OF_DRIVERS; i++) - { - Zone newDriverZone = zonesToAdd[(int)i]; - Bitmap zoneImg = zonesImages[(int)i]; - - newDriverZone.AddWindow(new DriverPositionWindow(zoneImg, new Rectangle(driverPositionPosition, driverPositionArea),LoadOCR)); - newDriverZone.AddWindow(new DriverGapToLeaderWindow(zoneImg, new Rectangle(driverGapToLeaderPosition, driverGapToLeaderArea), LoadOCR)); - newDriverZone.AddWindow(new DriverLapTimeWindow(zoneImg, new Rectangle(driverLapTimePosition, driverLapTimeArea), LoadOCR)); - newDriverZone.AddWindow(new DriverDrsWindow(zoneImg, new Rectangle(driverDrsPosition, driverDrsArea), LoadOCR)); - newDriverZone.AddWindow(new DriverTyresWindow(zoneImg, new Rectangle(driverTyresPosition, driverTyresArea), LoadOCR)); - newDriverZone.AddWindow(new DriverNameWindow(zoneImg, new Rectangle(driverNamePosition, driverNameArea), LoadOCR)); - newDriverZone.AddWindow(new DriverSectorWindow(zoneImg, new Rectangle(driverSector1Position, driverSector1Area),1, LoadOCR)); - newDriverZone.AddWindow(new DriverSectorWindow(zoneImg, new Rectangle(driverSector2Position, driverSector2Area),2, LoadOCR)); - newDriverZone.AddWindow(new DriverSectorWindow(zoneImg, new Rectangle(driverSector3Position, driverSector3Area),3, LoadOCR)); - - mainZone.AddZone(newDriverZone); - }//); - //MessageBox.Show("We have a main zone with " + MainZone.Zones.Count() + " Driver zones with " + MainZone.Zones[4].Windows.Count() + " windows each and we have " + Drivers.Count + " drivers"); - mainZones.Add(mainZone); - } - } - catch (IOException ex) - { - MessageBox.Show("Error reading JSON file: " + ex.Message); - } - catch (JsonException ex) - { - MessageBox.Show("Invalid JSON format: " + ex.Message); - } - return mainZones; - } - /// - /// Method that calls all the zones and windows to get the content they can find on the image to display them - /// - /// The id of the image we are working with - /// a string representation of all the returns - public async Task Decode(List mainZones,List drivers) - { - string result = ""; - List mainResults = new List(); - - //Decode - for (int mainZoneId = 0; mainZoneId < mainZones.Count; mainZoneId++) - { - switch (mainZoneId) - { - case 0: - //Main Zone - foreach (Zone z in mainZones[mainZoneId].Zones) - { - mainResults.Add(await z.Decode(Drivers)); - } - break; - //Next there could be a Title Zone and TrackInfoZone - } - } - - //Display - foreach (DriverData driver in mainResults) - { - result += driver.ToString(); - result += Environment.NewLine; - } - - return result; - } - /// - /// Method that can be used to convert an amount of miliseconds into a more readable human form - /// - /// The given amount of miliseconds ton convert - /// A human readable string that represents the ms - public static string ConvertMsToTime(int amountOfMs) - { - //Convert.ToInt32 would round upand I dont want that - int minuts = (int)((float)amountOfMs / (1000f * 60f)); - int seconds = (int)((amountOfMs - (minuts * 60f * 1000f)) / 1000); - int ms = amountOfMs - ((minuts * 60 * 1000) + (seconds * 1000)); - - return minuts + ":" + seconds.ToString("00") + ":" + ms.ToString("000"); - } - /// - /// Old method that can draw on an image where the windows and zones are created. mostly used for debugging - /// - /// the #id of the image we are working with - /// the drawed bitmap - public Bitmap Draw(Bitmap image,List mainZones) - { - - Graphics g = Graphics.FromImage(image); - - foreach (Zone z in mainZones) - { - int count = 0; - foreach (Zone zz in z.Zones) - { - g.DrawRectangle(Pens.Red, z.Bounds); - foreach (Window w in zz.Windows) - { - g.DrawRectangle(Pens.Blue, new Rectangle(z.Bounds.X + zz.Bounds.X, z.Bounds.Y + zz.Bounds.Y, zz.Bounds.Width, zz.Bounds.Height)); - } - - count++; - } - } - - return image; - } - } -} - -``` diff --git a/temp_annexes/Code/Settings.md b/temp_annexes/Code/Settings.md deleted file mode 100644 index 38f9dda..0000000 --- a/temp_annexes/Code/Settings.md +++ /dev/null @@ -1,420 +0,0 @@ -# Settings.cs - -``` cs -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.IO; - -namespace Test_Merge -{ - public partial class Settings : Form - { - private string _grandPrixUrl = ""; - private string _grandPrixName = ""; - private int _grandPrixYear = 2000; - private List _driverList = new List(); - - private F1TVEmulator Emulator = null; - private ConfigurationTool Config = null; - - private bool CreatingZone = false; - private Point ZoneP1; - private Point ZoneP2; - - private bool CreatingWindow = false; - private Point WindowP1; - private Point WindowP2; - - List WindowsToAdd = new List(); - - public string GrandPrixUrl { get => _grandPrixUrl; private set => _grandPrixUrl = value; } - public string GrandPrixName { get => _grandPrixName; private set => _grandPrixName = value; } - public int GrandPrixYear { get => _grandPrixYear; private set => _grandPrixYear = value; } - public List DriverList { get => _driverList; private set => _driverList = value; } - - public Settings() - { - InitializeComponent(); - Load(); - } - private void Load() - { - RefreshUI(); - } - private void RefreshUI() - { - - lsbDrivers.DataSource = null; - lsbDrivers.DataSource = DriverList; - - if (Directory.Exists(ConfigurationTool.CONFIGS_FOLDER_NAME)) - { - lsbPresets.DataSource = null; - lsbPresets.DataSource = Directory.GetFiles(ConfigurationTool.CONFIGS_FOLDER_NAME); - } - if (CreatingZone) - { - if (ZoneP1 == new Point(-1, -1)) - { - lblZonePointsRemaning.Text = "2 points Remaining"; - } - else - { - lblZonePointsRemaning.Text = "1 point Remaining"; - } - } - else - { - lblZonePointsRemaning.Text = ""; - } - - if (CreatingWindow) - { - if (WindowP1 == new Point(-1, -1)) - { - lblWindowPointsRemaining.Text = "2 points Remaining"; - } - else - { - lblWindowPointsRemaining.Text = "1 point Remaining"; - } - lblWindowPointsRemaining.Text = ConfigurationTool.NUMBER_OF_ZONES - WindowsToAdd.Count() + " Windows remaining"; - } - else - { - lblWindowPointsRemaining.Text = ""; - lblWindowsRemaining.Text = ""; - } - if (Config != null) - { - pbxMain.Image = Config.MainZone.Draw(); - if(Config.MainZone.Zones.Count > 0) - pbxDriverZone.Image = Config.MainZone.Zones[0].Draw(); - } - } - private void CreateNewZone(Point p1, Point p2) - { - Rectangle dimensions = CreateAbsoluteRectangle(p1, p2); - Config = new ConfigurationTool((Bitmap)pbxMain.Image, dimensions); - RefreshUI(); - } - private void CreateWindows(List dimensions) - { - if (Config != null) - { - Config.AddWindows(dimensions); - } - } - private void tbxGpUrl_TextChanged(object sender, EventArgs e) - { - GrandPrixUrl = tbxGpUrl.Text; - } - - private void tbxGpName_TextChanged(object sender, EventArgs e) - { - GrandPrixName = tbxGpName.Text; - } - - private void tbxGpYear_TextChanged(object sender, EventArgs e) - { - int year; - try - { - year = Convert.ToInt32(tbxGpYear.Text); - } - catch - { - year = 1545; - } - GrandPrixYear = year; - } - - private void btnAddDriver_Click(object sender, EventArgs e) - { - string newDriver = tbxDriverName.Text; - DriverList.Add(newDriver); - tbxDriverName.Text = ""; - RefreshUI(); - } - - private void btnRemoveDriver_Click(object sender, EventArgs e) - { - if (lsbDrivers.SelectedIndex >= 0) - { - DriverList.RemoveAt(lsbDrivers.SelectedIndex); - } - RefreshUI(); - } - private void SwitchZoneCreation() - { - if (CreatingZone) - { - CreatingZone = false; - lblZonePointsRemaning.Text = ""; - } - else - { - CreatingZone = true; - - if (Config != null) - Config.ResetMainZone(); - - if (CreatingWindow) - SwitchWindowCreation(); - - if (Emulator != null && Emulator.Ready) - { - Config = null; - pbxMain.Image = Emulator.Screenshot(); - } - - ZoneP1 = new Point(-1, -1); - ZoneP2 = new Point(-1, -1); - - lblZonePointsRemaning.Text = "2 Points left"; - } - RefreshUI(); - } - private void SwitchWindowCreation() - { - if (CreatingWindow) - { - CreatingWindow = false; - } - else - { - CreatingWindow = true; - - if (Config != null) - Config.ResetWindows(); - - if (CreatingZone) - SwitchZoneCreation(); - - WindowP1 = new Point(-1, -1); - WindowP2 = new Point(-1, -1); - - WindowsToAdd = new List(); - } - RefreshUI(); - } - private void btnCreatZone_Click(object sender, EventArgs e) - { - SwitchZoneCreation(); - } - private void btnCreateWindow_Click(object sender, EventArgs e) - { - SwitchWindowCreation(); - } - private void pbxMain_MouseClick(object sender, MouseEventArgs e) - { - if (CreatingZone && pbxMain.Image != null) - { - //Point coordinates = pbxMain.PointToClient(new Point(MousePosition.X, MousePosition.Y)); - Point coordinates = e.Location; - float xOffset = (float)pbxMain.Image.Width / (float)pbxMain.Width; - float yOffset = (float)pbxMain.Image.Height / (float)pbxMain.Height; - Point newPoint = new Point(Convert.ToInt32((float)coordinates.X * xOffset), Convert.ToInt32((float)coordinates.Y * yOffset)); - - //MessageBox.Show("Coordinates" + Environment.NewLine + "Old : " + coordinates.ToString() + Environment.NewLine + "New : " + newPoint.ToString()); - - if (ZoneP1 == new Point(-1, -1)) - { - ZoneP1 = newPoint; - } - else - { - ZoneP2 = newPoint; - CreateNewZone(ZoneP1, ZoneP2); - SwitchZoneCreation(); - } - RefreshUI(); - } - } - private void pbxMain_Click(object sender, EventArgs e) - { - //Not the right one to use visibly - } - private void pbxDriverZone_MouseClick(object sender, MouseEventArgs e) - { - if (CreatingWindow && pbxDriverZone.Image != null) - { - Point coordinates = e.Location; - - float xOffset = (float)pbxDriverZone.Image.Width / (float)pbxDriverZone.Width; - float yOffset = (float)pbxDriverZone.Image.Height / (float)pbxDriverZone.Height; - - Point newPoint = new Point(Convert.ToInt32((float)coordinates.X * xOffset), Convert.ToInt32((float)coordinates.Y * yOffset)); - - if (WindowP1 == new Point(-1, -1)) - { - WindowP1 = newPoint; - } - else - { - WindowP2 = newPoint; - WindowsToAdd.Add(CreateAbsoluteRectangle(WindowP1, WindowP2)); - - if (WindowsToAdd.Count < ConfigurationTool.NUMBER_OF_ZONES) - { - WindowP1 = new Point(-1, -1); - WindowP2 = new Point(-1, -1); - } - else - { - WindowP1 = new Point(WindowP1.X, 0); - WindowP2 = new Point(WindowP2.X, pbxDriverZone.Image.Height); - CreateWindows(WindowsToAdd); - SwitchWindowCreation(); - } - } - RefreshUI(); - } - } - private void pbxDriverZone_Click(object sender, EventArgs e) - { - //Not the right one to use visibly - } - private Rectangle CreateAbsoluteRectangle(Point p1, Point p2) - { - Point newP1 = new Point(); - Point newP2 = new Point(); - - if (p1.X < p2.X) - { - newP1.X = p1.X; - newP2.X = p2.X; - } - else - { - newP1.X = p2.X; - newP2.X = p1.X; - } - - if (p1.Y < p2.Y) - { - newP1.Y = p1.Y; - newP2.Y = p2.Y; - } - else - { - newP1.Y = p2.Y; - newP2.Y = p1.Y; - } - return new Rectangle(newP1.X, newP1.Y, newP2.X - newP1.X, newP2.Y - newP1.Y); - } - - private async void btnRefresh_Click(object sender, EventArgs e) - { - btnRefresh.Enabled = false; - if (Emulator == null || Emulator.GrandPrixUrl != tbxGpUrl.Text) - { - Emulator = new F1TVEmulator(tbxGpUrl.Text); - } - - if (!Emulator.Ready) - { - Task start = Task.Run(() => Emulator.Start()); - int errorCode = await start; - if (errorCode != 0) - { - string message; - switch (errorCode) - { - case 101: - message = "Error " + errorCode + " Could not start the driver. It could be because an other instance is runnin make sure you closed them all before trying again"; - break; - case 102: - message = "Error " + errorCode + " Could not navigate on the F1TV site. Make sure the correct URL has been given and that you logged from chrome. It can take a few minutes to update"; - break; - case 103: - message = "Error " + errorCode + " The url is not a valid url"; - break; - case 104: - message = "Error " + errorCode + " The url is not a valid url"; - break; - case 105: - message = "Error " + errorCode + " There has been an error trying to emulate button presses. Please try again"; - break; - case 106: - message = "Error " + errorCode + " There has been an error trying to emulate button presses. Please try again"; - break; - default: - message = "Could not start the emulator Error " + errorCode; - break; - } - MessageBox.Show(message); - } - else - { - pbxMain.Image = Emulator.Screenshot(); - } - } - else - { - pbxMain.Image = Emulator.Screenshot(); - } - btnRefresh.Enabled = true; - } - - private void Settings_FormClosing(object sender, FormClosingEventArgs e) - { - if (Emulator != null) - { - Emulator.Stop(); - } - } - - private void btnResetDriver_Click(object sender, EventArgs e) - { - if (Emulator != null) - { - Emulator.ResetDriver(); - } - } - - private void btnSavePreset_Click(object sender, EventArgs e) - { - string presetName = tbxPresetName.Text; - if (Config != null) - { - Config.SaveToJson(DriverList,presetName); - } - RefreshUI(); - } - - private void lsbPresets_SelectedIndexChanged(object sender, EventArgs e) - { - //Nothing - } - - private void btnLoadPreset_Click(object sender, EventArgs e) - { - if (lsbPresets.SelectedIndex >= 0 && pbxMain.Image != null) - { - try - { - Reader reader = new Reader(lsbPresets.Items[lsbPresets.SelectedIndex].ToString(), (Bitmap)pbxMain.Image,false); - //MainZones #0 is the big main zone containing driver zones - Config = new ConfigurationTool((Bitmap)pbxMain.Image, reader.MainZones[0].Bounds); - Config.MainZone = reader.MainZones[0]; - DriverList = reader.Drivers; - } - catch (Exception ex) - { - MessageBox.Show("Could not load the settings error :" + ex); - } - RefreshUI(); - } - } - } -} - -``` diff --git a/temp_annexes/Code/Window.md b/temp_annexes/Code/Window.md deleted file mode 100644 index f453494..0000000 --- a/temp_annexes/Code/Window.md +++ /dev/null @@ -1,322 +0,0 @@ -# Window.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : Window.cs -/// Brief : Default Window object that is mainly expected to be inherited. -/// Version : 0.1 - -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 Test_Merge -{ - public class Window - { - private Rectangle _bounds; - private Bitmap _image; - private string _name; - protected TesseractEngine Engine; - 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); - return sample; - } - } - public Window(Bitmap image, Rectangle bounds, bool generateEngine = true) - { - Image = image; - Bounds = bounds; - if (generateEngine) - { - Engine = new TesseractEngine(TESS_DATA_FOLDER.FullName, "eng", EngineMode.Default); - Engine.DefaultPageSegMode = PageSegMode.SingleLine; - } - } - /// - /// Method that will have to be used by the childrens to let the model make them decode the images they have - /// - /// Returns an object because we dont know what kind of return it will be - public virtual async Task DecodePng() - { - return "NaN"; - } - /// - /// Method that will have to be used by the childrens to let the model make them decode the images they have - /// - /// This is a list of the different possible drivers in the race. It should not be too big but NEVER be too short - /// Returns an object because we dont know what kind of return it will be - public virtual async Task DecodePng(List driverList) - { - return "NaN"; - } - /// - /// This converts an image into a byte[]. It can be usefull when doing unsafe stuff. Use at your own risks - /// - /// The image you want to convert - /// A byte array containing the image informations - public static byte[] ImageToByte(Image inputImage) - { - using (var stream = new MemoryStream()) - { - inputImage.Save(stream, System.Drawing.Imaging.ImageFormat.Png); - return stream.ToArray(); - } - } - /// - /// This method is used to recover a time from a PNG using Tesseract OCR - /// - /// The image where the text is - /// The type of window it is - /// The Tesseract Engine - /// The time in milliseconds - public static async Task 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 (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: - Engine.SetVariable("tessedit_char_whitelist", ""); - break; - } - - - Bitmap enhancedImage = new OcrImage(windowImage).Enhance(windowType); - - 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)); - } - - List rawNumbers; - - //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 - rawNumbers = rawResult.Split('.', ':').ToList(); - //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]); - - if (result > 999999) - { - //We know that we have way too much seconds to make a minut - //Its usually because the ":" have been interpreted as a number - int minuts = (int)(rawNumbers[0][0] - '0'); - // rawNumbers[0][1] should contain the : that has been mistaken - int seconds = Convert.ToInt32(rawNumbers[0][2].ToString() + rawNumbers[0][3].ToString()); - int ms = Convert.ToInt32(rawNumbers[1]); - result = (Convert.ToInt32(minuts) * 1000 * 60) + (Convert.ToInt32(seconds) * 1000) + Convert.ToInt32(ms); - } - } - 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; - } - /// - /// Method that recovers strings from an image using Tesseract OCR - /// - /// The image of the window that contains text - /// The Tesseract engine - /// The list of allowed chars - /// The type of window the text is on. Depending on the context the OCR will behave differently - /// the string it found - public static async Task GetStringFromPng(Bitmap WindowImage, TesseractEngine Engine, string allowedChars = "", OcrImage.WindowType windowType = OcrImage.WindowType.Text) - { - string result = ""; - - Engine.SetVariable("tessedit_char_whitelist", allowedChars); - - Bitmap rawData = WindowImage; - 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)); - } - page.Dispose(); - return result; - } - /// - /// Get a smaller image from a bigger one - /// - /// The big bitmap you want to get a part of - /// The dimensions of the new bitmap - /// The little bitmap - protected Bitmap GetSmallBitmapFromBigOne(Bitmap inputBitmap, Rectangle newBitmapDimensions) - { - Bitmap sample = new Bitmap(newBitmapDimensions.Width, newBitmapDimensions.Height); - Graphics g = Graphics.FromImage(sample); - g.DrawImage(inputBitmap, new Rectangle(0, 0, sample.Width, sample.Height), newBitmapDimensions, GraphicsUnit.Pixel); - return sample; - } - /// - /// Returns the closest string from a list of options - /// - /// an array of all the possibilities - /// the string you want to compare - /// The closest option - protected static string FindClosestMatch(List options, string testString) - { - var closestMatch = ""; - var closestDistance = int.MaxValue; - - foreach (var item in options) - { - var distance = LevenshteinDistance(item, testString); - if (distance < closestDistance) - { - closestMatch = item; - closestDistance = distance; - } - } - return closestMatch; - } - //This method has been generated with the help of ChatGPT - /// - /// Method that computes a score of distance between two strings - /// - /// The first string (order irrelevant) - /// The second string (order irrelevant) - /// The levenshtein distance - protected static int LevenshteinDistance(string string1, string string2) - { - if (string.IsNullOrEmpty(string1)) - { - return string.IsNullOrEmpty(string2) ? 0 : string2.Length; - } - - if (string.IsNullOrEmpty(string2)) - { - return string.IsNullOrEmpty(string1) ? 0 : string1.Length; - } - - 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 <= string2.Length; j++) - { - d[0, j] = j; - } - - for (var i = 1; i <= string1.Length; i++) - { - for (var j = 1; j <= string2.Length; j++) - { - 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[string1.Length, string2.Length]; - } - public virtual string ToJSON() - { - string result = ""; - - result += "\"" + Name + "\"" + ":{" + Environment.NewLine; - result += "\t" + "\"x\":" + Bounds.X + "," + Environment.NewLine; - result += "\t" + "\"y\":" + Bounds.Y + "," + Environment.NewLine; - result += "\t" + "\"width\":" + Bounds.Width + Environment.NewLine; - result += "}"; - - return result; - } - } -} - -``` diff --git a/temp_annexes/Code/Zone.md b/temp_annexes/Code/Zone.md deleted file mode 100644 index 6df76a3..0000000 --- a/temp_annexes/Code/Zone.md +++ /dev/null @@ -1,242 +0,0 @@ -# Zone.cs - -``` cs -/// Author : Maxime Rohmer -/// Date : 08/05/2023 -/// File : Zone.cs -/// Brief : Class that contains all the methods and infos for a zone. This is designed to be potentially be inherited. -/// Version : 0.1 - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Test_Merge -{ - public class Zone - { - private Rectangle _bounds; - private List _zones; - private List _windows; - private Bitmap _image; - private string _name; - - public Bitmap ZoneImage - { - get - { - //This little trickery lets you have the image that the zone 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); - return sample; - } - } - public Bitmap Image - { - get { return _image; } - set - { - //It automatically sets the image for the contained windows and zones - _image = Image; - foreach (Window w in Windows) - { - w.Image = ZoneImage; - } - foreach (Zone z in Zones) - { - z.Image = Image; - } - } - } - - public Rectangle Bounds { get => _bounds; protected set => _bounds = value; } - public List Zones { get => _zones; protected set => _zones = value; } - public List Windows { get => _windows; protected set => _windows = value; } - public string Name { get => _name; protected set => _name = value; } - - public Zone(Bitmap image, Rectangle bounds, string name) - { - Windows = new List(); - Zones = new List(); - Name = name; - - //You cant set the image in the CTOR because the processing is impossible at first initiation - _image = image; - Bounds = bounds; - } - /// - /// Adds a zone to the list of zones - /// - /// The zone you want to add - public virtual void AddZone(Zone zone) - { - Zones.Add(zone); - } - /// - /// Add a window to the list of windows - /// - /// the window you want to add - public virtual void AddWindow(Window window) - { - Windows.Add(window); - } - /// - /// Calls all the windows to do OCR and to give back the results so we can send them to the model - /// - /// A list of all the driver in the race to help with text recognition - /// A driver data object that contains all the infos about a driver - public virtual async Task Decode(List driverList) - { - int sectorCount = 0; - DriverData result = new DriverData(); - Parallel.ForEach(Windows, async w => - { - // A switch would be prettier but I dont think its supported in this C# version - if (w is DriverNameWindow) - result.Name = (string)await (w as DriverNameWindow).DecodePng(driverList); - if (w is DriverDrsWindow) - result.DRS = (bool)await (w as DriverDrsWindow).DecodePng(); - if (w is DriverGapToLeaderWindow) - result.GapToLeader = (int)await (w as DriverGapToLeaderWindow).DecodePng(); - if (w is DriverLapTimeWindow) - result.LapTime = (int)await (w as DriverLapTimeWindow).DecodePng(); - if (w is DriverPositionWindow) - result.Position = (int)await (w as DriverPositionWindow).DecodePng(); - if (w is DriverSectorWindow) - { - sectorCount++; - if (sectorCount == 1) - result.Sector1 = (int)await (w as DriverSectorWindow).DecodePng(); - if (sectorCount == 2) - result.Sector2 = (int)await (w as DriverSectorWindow).DecodePng(); - if (sectorCount == 3) - result.Sector3 = (int)await (w as DriverSectorWindow).DecodePng(); - } - if (w is DriverTyresWindow) - result.CurrentTyre = (Tyre)await (w as DriverTyresWindow).DecodePng(); - }); - return result; - } - public virtual Bitmap Draw() - { - Bitmap img; - - //If its the main zone we want to see everything - if (Zones.Count > 0) - { - img = Image; - } - else - { - img = ZoneImage; - } - - Graphics g = Graphics.FromImage(img); - - //If its the main zone we need to visualize the Zone bounds displayed - if (Zones.Count > 0) - g.DrawRectangle(new Pen(Brushes.Violet, 5), Bounds); - - foreach (Zone z in Zones) - { - Rectangle newBounds = new Rectangle(z.Bounds.X, z.Bounds.Y + Bounds.Y, z.Bounds.Width, z.Bounds.Height); - g.DrawRectangle(Pens.Red, newBounds); - } - foreach (Window w in Windows) - { - g.DrawRectangle(Pens.Blue, w.Bounds); - } - return img; - } - public void ResetZones() - { - Zones.Clear(); - } - public void ResetWindows() - { - foreach (Zone z in Zones) - { - z.ResetWindows(); - } - Windows.Clear(); - } - public virtual string ToJSON() - { - string result = ""; - result += "\"" + Name + "\":{" + Environment.NewLine; - result += "\t" + "\"x\":" + Bounds.X + "," + Environment.NewLine; - result += "\t" + "\"y\":" + Bounds.Y + "," + Environment.NewLine; - result += "\t" + "\"width\":" + Bounds.Width + "," + Environment.NewLine; - result += "\t" + "\"height\":" + Bounds.Height; - - if (Windows.Count != 0) - { - result += "," + Environment.NewLine; - - result += "\t" + "\"Windows\":[" + Environment.NewLine; - result += "\t\t{" + Environment.NewLine; - int Wcount = 0; - foreach (Window w in Windows) - { - result += "\t\t" + w.ToJSON(); - Wcount++; - if (Wcount != Windows.Count) - result += ","; - } - result += "\t\t}" + Environment.NewLine; - result += "\t" + "]" + Environment.NewLine; - } - else - { - result += Environment.NewLine; - } - if (Zones.Count != 0) - { - result += "," + Environment.NewLine; - - result += "\t" + "\"Zones\":[" + Environment.NewLine; - result += "\t\t{" + Environment.NewLine; - int Zcount = 0; - //foreach (Zone z in Zones) - //{ - result += "\t\t" + Zones[0].ToJSON(); - Zcount++; - if (Zcount != Zones.Count) - //result += ","; - //} - result += "\t\t}" + Environment.NewLine; - result += "\t" + "]" + Environment.NewLine; - } - else - { - result += Environment.NewLine; - } - - result += "}"; - - return result; - } - /// - /// Checks if the given Rectangle fits in the current zone - /// - /// The Rectangle you want to check the fittment - /// - protected bool Fits(Rectangle inputRectangle) - { - if (inputRectangle.X + inputRectangle.Width > Bounds.Width || inputRectangle.Y + inputRectangle.Height > Bounds.Height || inputRectangle.X < 0 || inputRectangle.Y < 0) - { - return false; - } - else - { - return true; - } - } - } -} - -``` diff --git a/temp_annexes/Code/recoverCookiesCSV.md b/temp_annexes/Code/recoverCookiesCSV.md deleted file mode 100644 index 33341f6..0000000 --- a/temp_annexes/Code/recoverCookiesCSV.md +++ /dev/null @@ -1,88 +0,0 @@ -# recoverCookiesCSV.py - -``` py -# Rohmer Maxime -# RecoverCookies.py -# Little script that recovers the cookies stored in the chrome sqlite database and then decrypts them using the key stored in the chrome files -# This script has been created to be used by an other programm or for the data to not be used directly. This is why it stores all the decoded cookies in a csv. (Btw could be smart for the end programm to delete the csv after using it) -# Parts of this cript have been created with the help of ChatGPT - -import os -import json -import base64 -import sqlite3 -import win32crypt -from Cryptodome.Cipher import AES -from pathlib import Path -import csv - -def get_master_key(): - with open( - os.getenv("localappdata") + "\\Google\\Chrome\\User Data\\Local State", "r" - ) as f: - local_state = f.read() - local_state = json.loads(local_state) - master_key = base64.b64decode(local_state["os_crypt"]["encrypted_key"]) - master_key = master_key[5:] # removing DPAPI - master_key = win32crypt.CryptUnprotectData(master_key, None, None, None, 0)[1] - print("MASTER KEY :") - print(master_key) - print(len(master_key)) - return master_key - -def decrypt_payload(cipher, payload): - return cipher.decrypt(payload) - -def generate_cipher(aes_key, iv): - return AES.new(aes_key, AES.MODE_GCM, iv) - -def decrypt_password(buff, master_key): - try: - iv = buff[3:15] - payload = buff[15:] - cipher = generate_cipher(master_key, iv) - decrypted_pass = decrypt_payload(cipher, payload) - decrypted_pass = decrypted_pass[:-16].decode() # remove suffix bytes - return decrypted_pass - except Exception: - # print("Probably saved password from Chrome version older than v80\n") - # print(str(e)) - return "Chrome < 80" - - -master_key = get_master_key() - -cookies_path = Path( - os.getenv("localappdata") + "\\Google\\Chrome\\User Data\\Default\\Network\\Cookies" -) - -if not cookies_path.exists(): - raise ValueError("Cookies file not found") - -with sqlite3.connect(cookies_path) as connection: - connection.row_factory = sqlite3.Row - cursor = connection.cursor() - cursor.execute("SELECT * FROM cookies") - - with open('cookies.csv', 'a', newline='') as csvfile: - fieldnames = ['host_key', 'name', 'value', 'path', 'expires_utc', 'is_secure', 'is_httponly'] - writer = csv.DictWriter(csvfile, fieldnames=fieldnames) - - if csvfile.tell() == 0: - writer.writeheader() - - for row in cursor.fetchall(): - decrypted_value = decrypt_password(row["encrypted_value"], master_key) - writer.writerow({ - 'host_key': row["host_key"], - 'name': row["name"], - 'value': decrypted_value, - 'path': row["path"], - 'expires_utc': row["expires_utc"], - 'is_secure': row["is_secure"], - 'is_httponly': row["is_httponly"] - }) - -print("Finished CSV") - -```