Added a lot to the doc and modified the pdf generation
This commit is contained in:
+148
-14
@@ -1,8 +1,8 @@
|
||||
/// Author : Maxime Rohmer
|
||||
/// Date : 08/05/2023
|
||||
/// Date : 30/05/2023
|
||||
/// File : OcrImage.cs
|
||||
/// Brief : Class containing all the methods used to enhance images for OCR
|
||||
/// Version : 0.1
|
||||
/// Version : Alpha 1.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -11,7 +11,7 @@ using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace Test_Merge
|
||||
namespace TrackTrends
|
||||
{
|
||||
public class OcrImage
|
||||
{
|
||||
@@ -44,28 +44,57 @@ namespace Test_Merge
|
||||
public Bitmap Enhance(WindowType type = WindowType.Text)
|
||||
{
|
||||
Bitmap outputBitmap = (Bitmap)InputBitmap.Clone();
|
||||
//Note : If you plan to activate all the comments that I used to debug the OCR I would advise to make sure that the debug folder exists
|
||||
switch (type)
|
||||
{
|
||||
case WindowType.LapTime:
|
||||
outputBitmap = Tresholding(outputBitmap, 185);
|
||||
case WindowType.Gap:
|
||||
//outputBitmap.Save(Window.GAPTOLEADER_DEBUG_FOLDER + @"\raw_" + id + ".png");
|
||||
|
||||
outputBitmap = Tresholding(outputBitmap, 165);
|
||||
//outputBitmap.Save(Window.GAPTOLEADER_DEBUG_FOLDER + @"\treshold_" + id + ".png");
|
||||
|
||||
outputBitmap = Resize(outputBitmap, 2);
|
||||
//outputBitmap.Save(Window.GAPTOLEADER_DEBUG_FOLDER + @"\resize_" + id + ".png");
|
||||
|
||||
outputBitmap = Dilatation(outputBitmap, 1);
|
||||
outputBitmap = Erode(outputBitmap, 1);
|
||||
//outputBitmap.Save(Window.GAPTOLEADER_DEBUG_FOLDER + @"\Final_dilatation_" + id + ".png");
|
||||
break;
|
||||
case WindowType.Sector:
|
||||
//outputBitmap.Save(Window.SECTOR1_DEBUG_FOLDER + @"\raw_" + id + ".png");
|
||||
|
||||
outputBitmap = VanishOxyAction(outputBitmap);
|
||||
//outputBitmap.Save(Window.SECTOR1_DEBUG_FOLDER + @"\vanish_" + id + ".png");
|
||||
|
||||
outputBitmap = Tresholding(outputBitmap, 150);
|
||||
//outputBitmap.Save(Window.SECTOR1_DEBUG_FOLDER + @"\Final_treshold_" + id + ".png");
|
||||
break;
|
||||
case WindowType.LapTime:
|
||||
//outputBitmap.Save(Window.LAPTIME_DEBUG_FOLDER + @"\raw_" + id + ".png");
|
||||
|
||||
outputBitmap = Tresholding(outputBitmap,185);
|
||||
//outputBitmap.Save(Window.LAPTIME_DEBUG_FOLDER + @"\Treshold_" + id + ".png");
|
||||
|
||||
outputBitmap = SobelEdgeDetection(outputBitmap);
|
||||
//outputBitmap.Save(Window.LAPTIME_DEBUG_FOLDER + @"\SobelDetection_" + id + ".png");
|
||||
break;
|
||||
case WindowType.Text:
|
||||
outputBitmap = InvertColors(outputBitmap);
|
||||
//outputBitmap.Save(Window.STRING_DEBUG_FOLDER + @"\raw_" + id + ".png");
|
||||
|
||||
outputBitmap = Tresholding(outputBitmap, 165);
|
||||
outputBitmap = Resize(outputBitmap, 2);
|
||||
outputBitmap = Dilatation(outputBitmap, 1);
|
||||
//outputBitmap.Save(Window.STRING_DEBUG_FOLDER + @"\Final_treshold_" + id + ".png");
|
||||
break;
|
||||
case WindowType.Tyre:
|
||||
//outputBitmap.Save(Window.TYRE_DEBUG_FOLDER + @"\raw_" + id + ".png");
|
||||
|
||||
outputBitmap = RemoveUseless(outputBitmap);
|
||||
outputBitmap = Resize(outputBitmap, 4);
|
||||
//outputBitmap.Save(Window.TYRE_DEBUG_FOLDER + @"\uselessRemoved_" + id + ".png");
|
||||
|
||||
outputBitmap = Dilatation(outputBitmap, 1);
|
||||
//outputBitmap.Save(Window.TYRE_DEBUG_FOLDER + @"\Final_dilatation_" + id + ".png");
|
||||
break;
|
||||
default:
|
||||
outputBitmap = Tresholding(outputBitmap, 165);
|
||||
outputBitmap = Resize(outputBitmap, 4);
|
||||
outputBitmap = Resize(outputBitmap, 2);
|
||||
outputBitmap = Erode(outputBitmap, 1);
|
||||
break;
|
||||
}
|
||||
@@ -99,7 +128,112 @@ namespace Test_Merge
|
||||
//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;
|
||||
//This is not a proper treshold method but it is helping the sobel edge detection
|
||||
if(gray <= F1TV_BACKGROUND_TRESHOLD.R)
|
||||
{
|
||||
pixel[0] = pixel[1] = pixel[2] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel[0] = pixel[1] = pixel[2] = (byte)gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inputBitmap.UnlockBits(bmpData);
|
||||
|
||||
return inputBitmap;
|
||||
}
|
||||
/// <summary>
|
||||
/// Method that uses the Sobel Edge detection to outline the edges of the characters to help with the OCR
|
||||
/// </summary>
|
||||
/// <param name="grayscaleImage">The image with the sobel edge detection used</param>
|
||||
/// <returns></returns>
|
||||
private Bitmap SobelEdgeDetection(Bitmap grayscaleImage)
|
||||
{
|
||||
// Create a new bitmap for the edges
|
||||
Bitmap edgesImage = new Bitmap(grayscaleImage.Width, grayscaleImage.Height);
|
||||
|
||||
// Define the Sobel operators
|
||||
// Its just a matrix that we will use on the all image
|
||||
int[,] sobelX = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
|
||||
int[,] sobelY = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };
|
||||
|
||||
// Apply the Sobel operators and normalize the gradients
|
||||
// NOTE: I dont know how easy or hard it would be to make this paralel but it could be a good idea to do so if possible.
|
||||
for (int y = 1; y < grayscaleImage.Height - 1; y++)
|
||||
{
|
||||
for (int x = 1; x < grayscaleImage.Width - 1; x++)
|
||||
{
|
||||
int gradientX = CalculateGradient(grayscaleImage, sobelX, x, y);
|
||||
int gradientY = CalculateGradient(grayscaleImage, sobelY, x, y);
|
||||
int gradient = (int)Math.Sqrt(gradientX * gradientX + gradientY * gradientY);
|
||||
|
||||
// Normalize the gradient value
|
||||
// In some rare cases the value can exceed 255 so we limit it with the Math.Min method
|
||||
gradient = Math.Min(255, Math.Max(0, gradient));
|
||||
|
||||
edgesImage.SetPixel(x, y, Color.FromArgb(gradient, gradient, gradient));
|
||||
}
|
||||
}
|
||||
|
||||
return edgesImage;
|
||||
}
|
||||
/// <summary>
|
||||
/// Method that's here to be used by the sobel edge detection method (Chat GPT has been used for parts of this method)
|
||||
/// </summary>
|
||||
/// <param name="grayscaleImage">The input image with the grayscale processing already done</param>
|
||||
/// <param name="sobelOperator">The matrix to apply</param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns>Returns the processed gradient</returns>
|
||||
private int CalculateGradient(Bitmap grayscaleImage, int[,] sobelOperator, int x, int y)
|
||||
{
|
||||
int gradient = 0;
|
||||
|
||||
for (int j = -1; j <= 1; j++)
|
||||
{
|
||||
for (int i = -1; i <= 1; i++)
|
||||
{
|
||||
int pixelX = grayscaleImage.GetPixel(x + i, y + j).R;
|
||||
gradient += sobelOperator[j + 1, i + 1] * pixelX;
|
||||
}
|
||||
}
|
||||
|
||||
return gradient;
|
||||
}
|
||||
/// <summary>
|
||||
/// Method that is used to whiten an image. Ignore the funny name. Its used to prevent colored text to trouble the OCR when it uses grayscaling
|
||||
/// </summary>
|
||||
/// <param name="inputBitmap">The bitmap to vanish</param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap VanishOxyAction(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
|
||||
{
|
||||
//Note : MAKE THIS PARALELL OMG WY DID I LEFT IT LIKE THAT
|
||||
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 blue = (int)pixel[0];
|
||||
int green = (int)pixel[1];
|
||||
int red = (int)pixel[2];
|
||||
|
||||
int max = Math.Max(Math.Max(blue, green), red);
|
||||
|
||||
if (max > 255 / 3)
|
||||
max = 255;
|
||||
|
||||
pixel[0] = pixel[1] = pixel[2] = (byte)max;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,7 +401,7 @@ namespace Test_Merge
|
||||
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)
|
||||
if (R >= F1TV_BACKGROUND_TRESHOLD.R +25|| G >= F1TV_BACKGROUND_TRESHOLD.G +25|| B >= F1TV_BACKGROUND_TRESHOLD.B +25)
|
||||
{
|
||||
pixel[0] = 0xFF;
|
||||
pixel[1] = 0xFF;
|
||||
@@ -324,7 +458,7 @@ namespace Test_Merge
|
||||
}
|
||||
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));
|
||||
return Color.FromArgb(255,Math.Min(Convert.ToInt32((float)totR / (float)totPixels),255), Math.Min(Convert.ToInt32((float)totG / (float)totPixels),255), Math.Min(Convert.ToInt32((float)totB / (float)totPixels),255));
|
||||
}
|
||||
/// <summary>
|
||||
/// This method simply inverts all the colors in a Bitmap
|
||||
|
||||
Reference in New Issue
Block a user