Compare commits

..

2 Commits

Author SHA1 Message Date
Maxluli 5b8b7b6c0b Updated doc 2023-04-03 12:52:56 +02:00
Maxluli a7009dcc21 End of the day push 2023-03-31 15:43:41 +02:00
10 changed files with 265 additions and 1 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

+264
View File
@@ -506,3 +506,267 @@ Maintenant le programme peut créer des zones pour chaque pilote
!["Images pilotes"](./Images/Screens/WindowsPilotes.png)
!["Zone d'un pilote"](./Images/Screens/driver2.png)
Maintenant il faut que j'implémente un système un peu similaire pour créer des windows.
Voici la methode que j'ai créé pour l'autocalibration :
```Csharp
public void AutoCalibrate()
{
List<Rectangle> detectedText = new List<Rectangle>();
Zones = new List<Zone>();
TesseractEngine engine = new TesseractEngine(Window.tessDataFolder.FullName, "eng", EngineMode.Default);
Image image = 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));
}
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);
Zones.Add(new Zone(ZoneImage, windowRectangle));
}
}
```
Ca peut paraitre pas énorme comme code mais pour tout mettre en place ca demande quand même pas mal de reflexion.
J'ai du clean un peu le code que j'avais fait pour permettre la selection de zones et ajouter la possibilité d'ajouter des windows sur une zone.
J'ai juste quelques difficultées à les ajouter correctement, j'ai un offset tout pourri qui se met tout le temps
!["Sainz coupé"](./Images/Screens/data0Sainz.png)
!["Perez coupé"](./Images/Screens/data0Perez.png)
Cela doit être un soucis lors de la detection de clic qui met un offset en trop. C'est vraiment pénible en tout cas.
Certes c'est moins fun de devoir manuellement indiquer ou sont les windows sur une ligne de pilote, mais je ne vois vraiment pas comment faire cela automatiquement. Le but c'est de faire une configuration qui puisse être sauvegardée comme ca pas besoin d'à chaques fois le refaire.
C'est bon ! J'avais juste oublié de changer le calcul d'offset entre le code de la zone et de la window. Note pour plus tard, il serait peut-être judicieux de faire quelque chose pour la vue, les windows et les Zones ont le même exact comportement pour la vue ce qui fait dupliquer du code.
Mais au moins maintenant ca fonctionne :
!["Ocr tester screenshot"](./Images/Screens/OCRTestingScreen.png)
Et le programme va directement créer un dossier par pilote avec toutes les images de chaque Data le concernant :
!["Dossier Perez"](./Images/Screens/PerezFolder.png);
Et c'est tout pour aujourd'hui je pense. Ce qui serait cool demain c'est que je puisse stocker d'une manière ou d'une autre ces fichiers de calibration et que je puisse les transfèrer vers le programme qui va s'occuper de décoder et commencer gentillement à décoder les différents types de data.
Note pour quand je ferai les tests. Je pense que la meilleure idée serait que je prenne pleins de photos du style et que je les mette dans un fichier CSV ou JSON avec leur contenu. Et ensuite je le fais passer en tests pour calculer la prescision de mon algo de décodage.
Pour le moment on est plutôt dans les clouts niveau planning.
## Mardi 4 Avril
Aujourd'hui je suis scensé plutôt bosser sur l'interpretation des données, mais une idée m'a taraudé l'esprit toute la nuit. Est-ce que je ne pourrais pas quand même essayer de décomposer la zone de pilote directment comme pour la Main zone.
Pour ce faire j'ai tenté de faire comme pour la main zone c'est à dire lancer la reconnaissance pour savoir ou étaient tous les champs de données mais malheureusement je ne pense pas que cela va être possible.
En effet non seulement ici les champs sont de tailles très variées, mais en plus la reconnaissance n'arrive pas à en récupèrer le même nombre sur chaque ligne ce qui risque d'être complexe à utiliser ensuite.
La preuve :
!["Tentative d'auto calibration"](./Images/Screens/AutoCalibration6.png);
Cependant tout n'est pas perdu ! Il y a peut-être un moyen qui serait mieux en tous points. Le soucis avec ce type de reconnaissance c'est qu'on utilise beaucoup de ressources inutiles. On peut peut-être hard coder la valeur des diviseurs et les utiliser pour créer des zones.
Ok alors visiblement c'est un problème car il semble y avoir d'autres pixels de cette couleur dans l'image (Qui l'aurait cru lol)
!["Tentative 2"](./Images/Screens/AutoCalibration7.png)
J'a tenté de réduire la tolérance mais le soucis c'est que c'est soit trop soit pas assez
Dernière tentative, j'ai essayé de prendre plusieurs pixels en hauteur pour chaque incrément de X et en faire la moyenne, et même comme ca, impossible de trouver de manière efficace les zones. Je pense que je vais donc revert tous mes changements pour revenir à la version ou on les choisissait manuellement.
Pas mal de temps perdu mais bon c'est comme ca ca arrive
Bon j'ai fait un revert mais j'ai ajouté une feature importante. Les zones font la largeur indiquée par l'utilisateur mais elles font la hauteur max comme ca toutes les window font la même hauteur et ca permet à l'utilisateur de ne pas forcément être ultra précis dans sa selection.
Ce qui nous donne :
!["Resultat final"](./Images/Screens/AutoCalibration8.png)
Maintenant je dirais que les deux prochaines choses à faire seraient de stocker ces zones dans un fichier JSON ou autre pour que la calibration puisse être envoyée directement dans le logiciel de reconnaissance et ensuite de faire une calibration sur des images qui font la taille qu'on aura pendant les Grands Prix. Pour le moment elles sont au format 16:10 qui est le format d'écrant de mon laptop.
Pour le stockage j'imagine un fichier qui donne des indications assez simples qui permettent de reconstruire le total des zones quand il est importé plutot que d'écrire les coordonnées en dur pour chacunes.
Chaque Grande zone va implémenter une methode qui s'occupe de mettre tous ses enfants dans un fichier.
```JSON
{
"MainZone":{
"x": 10,
"y": 20,
"width": 1450,
"height": 1340,
"DriverZone":{
"x": 0,
"y": 23,
"height": 25,
"Windows":[
{
"DriverPositionWindow":{
"x": 0,
"y": 0,
"width": 35
}
},
{
"DriverPositionChangesWindow":{
"x": 0,
"y": 0,
"width":45
}
}
]
}
}
}
```
C'est le résultat auquel j'aimerais arriver. Mais pour y arriver il faut encore que je crée les différents types de window.
Cela veut dire que je dois décider quelles informations je vais récupèrer de la page.
Par exemple je vais conserver la position du pilote mais au final les changements de positions sont difficiles à lire et sont redondants. Si je garde un historique des positions des pilotes je peux calculer moi même les changements.
Pareil pour gap avec la voiture devant. Je pense que je vais juste garder l'information des écarts absolus et ensuite je pourrai toujours calculer la différence entre les pilotes.
Ca peut paraître bête car cela rajoute du calcul mais en réalité le calcul de l'OCR est extrêmement gourmand alors il faut que j'évite le plus possible d'y faire recours. Il est bien plus rapide de calculer les écarts que d'essayer de reconnaitre le texte et le convertir en chiffre.
J'ai visiblement ajouté un bug dans mon code. Maintenant tous les pilotes ont la même image quand on les selectionne. Mais visiblement ca n'était pas le cas avant car j'avais pu prendres des images de chaque pilote.
J'ai passé 3 minutes à fixer un bug stupide j'ai un peu envie de brûler ma place de travail... Mais bon au moins maintenant cela fonctionne !
Toutes les images sont récupèrées et ont un format correct avec le bon nom :
!["Verstappen folder"](./Images/Screens/VerstappenFolder.png)
Avec un peu de code très moche j'ai pu créer un fichier JSON qui contient les différentes infos. Cependant en exportant TOUT on se retrouve avec un fichier de 1200 lignes ce qui n'est pas optimal.
Mais quand on regarde, il devrait être possible de faire un fichier qui ne contient que les infos d'un seul pilote car ensuite il y a simplement un offset à appliquer sur la zone et les windows.
Je vais donc pouvoir commencer enfin le logiciel de décodage qui prend en entrée un fichier JSON comme celui ci qui a été génèré avec le programme de calibration.
```Json
{
"Main": {
"x": 40,
"y": 230,
"width": 1845,
"height": 719,
"Zones": [
{
"DriverZone": {
"x": 0,
"y": 3,
"width": 1845,
"height": 35,
"Windows": [
{
"Position": {
"x": 2,
"y": 0,
"width": 32
},
"GapToLeader": {
"x": 204,
"y": 0,
"width": 96
},
"LapTime": {
"x": 413,
"y": 0,
"width": 105
},
"Drs": {
"x": 526,
"y": 0,
"width": 81
},
etc...
}
]
}
}
]
}
}
```
Dans le futur il faudrait ajouter d'autres choses comme par exemple les différents pilotes présents sur le Grand Prix et ce genre d'infos.
Quoique je vais l'ajouter déja maintenant et plus tard je mettrai en place la feature acessible depuis l'interface. Mais le hardcoder maintenant me permet déja de mieux coder l'autre côté.
Ce programme n'est en aucun cas terminé et je vais devoir travailler encore un peu dessus pour qu'il soit utilisable correctement mais au moins il fonctionne à peu près.
Exemple du json avec les noms de pilotes:
```Json
{
"Main": {
"x": 37,
"y": 238,
"width": 1851,
"height": 713,
"Zones": [
{
"DriverZone": {
"x": 0,
"y": -5,
"width": 1851,
"height": 35
}
}
]
},
"Drivers": [
"Leclerc",
"Verstappen",
etc...
]
}
```
Maintenant je vais m'attaquer au décodage.