267 lines
12 KiB
Markdown
267 lines
12 KiB
Markdown
# ConfigurationTool.cs
|
|
|
|
``` cs
|
|
/// Author : Maxime Rohmer
|
|
/// Date : 30/05/2023
|
|
/// File : ConfigurationTool.cs
|
|
/// Brief : Class that contains all the methods used to create config files for the main programm
|
|
/// Version : Alpha 1.0
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Tesseract;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
|
|
namespace TrackTrends
|
|
{
|
|
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/";
|
|
/// <summary>
|
|
/// Creates the configuration tool. It can only be created if you already have the dimensions of the main zone
|
|
/// </summary>
|
|
/// <param name="fullImage">The full image coming from the F1TV Data Channel</param>
|
|
/// <param name="mainZoneDimensions">The dimensions of the zone where all the drivers data are situated</param>
|
|
public ConfigurationTool(Bitmap fullImage, Rectangle mainZoneDimensions)
|
|
{
|
|
MainZone = new Zone(fullImage, mainZoneDimensions,"Main");
|
|
AutoCalibrate();
|
|
}
|
|
/// <summary>
|
|
/// Resets the main zone
|
|
/// </summary>
|
|
public void ResetMainZone()
|
|
{
|
|
MainZone.ResetZones();
|
|
}
|
|
/// <summary>
|
|
/// Reset the windows
|
|
/// </summary>
|
|
public void ResetWindows()
|
|
{
|
|
MainZone.ResetWindows();
|
|
}
|
|
/// <summary>
|
|
/// Save the current config in a JSON file stored in /Presets/
|
|
/// </summary>
|
|
/// <param name="drivers">A list of all the drivers in the GP. IMPORTANT, they need to ALL be mentionned or the program wont be able to detect the missing ones and will F up everything</param>
|
|
/// <param name="configName">The name the config should have</param>
|
|
public void SaveToJson(List<string> drivers, string configName)
|
|
{
|
|
string JSON = "";
|
|
|
|
JsonObject jsonFileObject = new JsonObject();
|
|
|
|
//Creates the mainZone object
|
|
JsonObject mainZoneObject = new JsonObject();
|
|
|
|
mainZoneObject.Add("x",MainZone.Bounds.X);
|
|
mainZoneObject.Add("y",MainZone.Bounds.Y);
|
|
mainZoneObject.Add("width",MainZone.Bounds.Width);
|
|
mainZoneObject.Add("height",MainZone.Bounds.Height);
|
|
|
|
JsonArray driverZonesArray = new JsonArray();
|
|
|
|
//Creates all the subzones that contain driver infos
|
|
int DriverID = 0;
|
|
foreach (Zone driverZone in MainZone.Zones)
|
|
{
|
|
DriverID++;
|
|
JsonObject driverZoneObject = new JsonObject();
|
|
driverZoneObject.Add("name","Driver"+DriverID);
|
|
driverZoneObject.Add("x", driverZone.Bounds.X);
|
|
driverZoneObject.Add("y", driverZone.Bounds.Y);
|
|
driverZoneObject.Add("width", driverZone.Bounds.Width);
|
|
driverZoneObject.Add("height", driverZone.Bounds.Height);
|
|
|
|
JsonArray windowsArray = new JsonArray();
|
|
JsonObject windowObject = new JsonObject();
|
|
|
|
//Creates all the windows of the current driver zone
|
|
//Note : We store ALL the windows and zones in the JSON because they are not spaced exactly the same on the main zone
|
|
foreach (Window window in driverZone.Windows)
|
|
{
|
|
windowObject.Add(window.Name, new JsonObject {
|
|
{ "x", window.Bounds.X },
|
|
{ "y", window.Bounds.Y },
|
|
{ "width", window.Bounds.Width },
|
|
{ "height", window.Bounds.Height }
|
|
});
|
|
}
|
|
windowsArray.Add(windowObject);
|
|
|
|
driverZoneObject.Add("Windows",windowsArray);
|
|
|
|
driverZonesArray.Add(driverZoneObject);
|
|
}
|
|
|
|
mainZoneObject.Add("DriverZones",driverZonesArray);
|
|
|
|
JsonArray driversArray = new JsonArray();
|
|
|
|
foreach (string driver in drivers)
|
|
{
|
|
driversArray.Add(driver);
|
|
}
|
|
|
|
mainZoneObject.Add("Drivers",driversArray);
|
|
|
|
jsonFileObject.Add("Main",mainZoneObject);
|
|
|
|
JSON = jsonFileObject.ToString();
|
|
|
|
//Saving the file
|
|
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);
|
|
}
|
|
/// <summary>
|
|
/// Adds a window in the windows list
|
|
/// Be carefull of the order. It cant be random or it will crash. The programm expect the first to be position, second Gap to leader etc...
|
|
/// </summary>
|
|
/// <param name="rectangles">The bounds of the window</param>
|
|
public void AddWindows(List<Rectangle> 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:
|
|
//Second zone should be the Gap to leader
|
|
driverZone.AddWindow(new DriverGapToLeaderWindow(driverZone.ZoneImage, rectangles[i - 1], false));
|
|
break;
|
|
case 3:
|
|
//Third zone should be the driver's Lap Time
|
|
driverZone.AddWindow(new DriverLapTimeWindow(driverZone.ZoneImage, rectangles[i - 1], false));
|
|
break;
|
|
case 4:
|
|
//Fourth zone should be the driver's DRS status
|
|
driverZone.AddWindow(new DriverDrsWindow(driverZone.ZoneImage, rectangles[i - 1], false));
|
|
break;
|
|
case 5:
|
|
//Fifth zone should be the driver's Tyre's informations
|
|
driverZone.AddWindow(new DriverTyresWindow(driverZone.ZoneImage, rectangles[i - 1], false));
|
|
break;
|
|
case 6:
|
|
//Sixth zone should be the driver's Name
|
|
driverZone.AddWindow(new DriverNameWindow(driverZone.ZoneImage, rectangles[i - 1], false));
|
|
break;
|
|
case 7:
|
|
//Seventh zone should be the driver's First Sector
|
|
driverZone.AddWindow(new DriverSectorWindow(driverZone.ZoneImage, rectangles[i - 1], 1, false));
|
|
break;
|
|
case 8:
|
|
//Zone number eight should be the driver's Second Sector
|
|
driverZone.AddWindow(new DriverSectorWindow(driverZone.ZoneImage, rectangles[i - 1], 2, false));
|
|
break;
|
|
case 9:
|
|
//Zone number nine should be the driver's Position Sector
|
|
driverZone.AddWindow(new DriverSectorWindow(driverZone.ZoneImage, rectangles[i - 1], 3, false));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// This will automatically create all the driver zones at the correct places if the main zone has been weel positionned
|
|
/// You cant just divide the image by the number of pilots or it will be messy and inconsistent at the end (Garbage in Garbage Out)
|
|
/// </summary>
|
|
public void AutoCalibrate()
|
|
{
|
|
List<Rectangle> detectedText = new List<Rectangle>();
|
|
List<Zone> zones = new List<Zone>();
|
|
|
|
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);
|
|
//Runs a quick OCR detection. Not to detect any content but just to detect where is all the text positionned.
|
|
//For each row it decides the best Zone location and adds it to the Driver zone list
|
|
using (var iter = page.GetIterator())
|
|
{
|
|
iter.Begin();
|
|
do
|
|
{
|
|
Rect boundingBox;
|
|
if (iter.TryGetBoundingBox(PageIteratorLevel.Word, out boundingBox))
|
|
{
|
|
//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++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
```
|