Skip to content

Instantly share code, notes, and snippets.

@corstian
Created November 12, 2019 16:32
Show Gist options
  • Save corstian/51f4a709c80f4f463b22eea4d3217dd0 to your computer and use it in GitHub Desktop.
Save corstian/51f4a709c80f4f463b22eea4d3217dd0 to your computer and use it in GitHub Desktop.
The scripts which have been used to try and figure out a license plate from some IR over-illuminated shots. Full blog post on https://corstianboerman.com.
void Main()
{
StreamReader csvreader = new StreamReader(@"C:\Documents\subset.csv");
var font = new Font("kenteken", 300, FontStyle.Regular, GraphicsUnit.Pixel, 0x00);
Dictionary<String, double[]> results = new Dictionary<String, double[]>();
var reference = new Bitmap(@"C:\Documents\reference.bmp");
var normalizedRef = Normalize(reference);
var lines = csvreader.ReadToEnd().Split('\r', '\n').Where(q => !String.IsNullOrWhiteSpace(q));
foreach (var line in lines)
{
var license = line.Split(',')[0];
var image = DrawText(license, font, Color.Black, Color.FromArgb(209, 178, 36));
results.Add(license, Normalize(ResizeImage(image, 13, 3)));
}
List<double[]> nextCluster = new List<double[]>(results.Select(q => q.Value));
var resultingClusterSize = 5000;
KMeans cluster = null;
int labelToLookFor = 0;
while (resultingClusterSize > 10)
{
var clusterData = nextCluster.ToArray();
cluster = new KMeans(2)
{
// MaxIterations = resultingClusterSize
};
KMeansClusterCollection clusters = cluster.Learn(clusterData);
int[] labels = clusters.Decide(clusterData);
var d1 = Distance.SquareEuclidean(cluster.Centroids[0], normalizedRef);
var d2 = Distance.SquareEuclidean(cluster.Centroids[1], normalizedRef);
labelToLookFor = labels[d1 > d2 ? 0 : 1];
nextCluster.Clear();
for (var label = 0; label < labels.Length; label++)
{
if (labels[label] == labelToLookFor)
{
nextCluster.Add(clusterData[label]);
}
}
resultingClusterSize = nextCluster.Count();
}
nextCluster.Dump();
foreach (var c in nextCluster) {
var dictItem = results.FirstOrDefault(q => q.Value == c);
var distance = Distance.SquareEuclidean(dictItem.Value, cluster.Centroids[labelToLookFor]);
var origin = Distance.SquareEuclidean(dictItem.Value, normalizedRef);
$"lic: {dictItem.Key}, dstC: {distance}, dstN: {origin}".Dump();
dictItem.Value.Dump();
}
}
public double[] Normalize(Bitmap bmp)
{
var array = new byte[bmp.Width * bmp.Height];
for (var y = 0; y < bmp.Height; y++)
{
for (var x = 0; x < bmp.Width; x++)
{
array[(y * bmp.Width) + x] = bmp.GetPixel(x, y).B;
}
}
var min = array.Min();
var max = array.Max();
double[] result = new double[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = MinMax(array[i], max, min, 0x00, 0xFF);
}
return result;
}
public double MinMax(byte x, byte max, byte min, byte newMax, byte newMin)
{
return ((x - min) * (newMax - newMin) / (max - min) + newMin);
}
public static byte[] ImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
private Image DrawText(String text, Font font, Color textColor, Color backColor)
{
//first, create a dummy bitmap just to get a graphics object
Image img = new Bitmap(1, 1);
Graphics drawing = Graphics.FromImage(img);
//measure the string to see how big the image needs to be
SizeF textSize = drawing.MeasureString(text, font);
//free up the dummy image and old graphics object
img.Dispose();
drawing.Dispose();
//create a new image of the right size
img = new Bitmap((int)textSize.Width, (int)textSize.Height);
drawing = Graphics.FromImage(img);
//paint the background
drawing.Clear(backColor);
//create a brush for the text
Brush textBrush = new SolidBrush(textColor);
drawing.DrawString(text, font, textBrush, 0, 0);
drawing.Save();
textBrush.Dispose();
drawing.Dispose();
return img;
}
/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
// First a script to filter the list containing all license plates
StreamReader csvreader = new StreamReader(@"C:\Downloads\Open_Data_RDW__Gekentekende_voertuigen.csv");
string inputLine = "";
List<string> results = new List<string>();
var lowerDate = DateTime.Parse("01-01-2010");
var upperDate = DateTime.Parse("01-01-2016");
csvreader.ReadLine();
while ((inputLine = csvreader.ReadLine()) != null)
{
try
{
string[] row = inputLine.Split(new char[] { ',' });
var license = row[0];
var carType = row[1];
if (carType != "Personenauto") continue;
var brand = row[2];
if (brand != "CITROEN") continue;
var brandType = row[3];
if (brandType != "C4") continue;
var APK = DateTime.Parse(row[4]);
if (APK < DateTime.UtcNow) continue;
var doorCount = Int32.Parse(row[32]);
if (doorCount != 4) continue;
var firstDateAccepted = DateTime.Parse(row[20]);
if (firstDateAccepted > lowerDate && firstDateAccepted < upperDate)
{
results.Add(inputLine);
}
} catch {
}
}
results.Dump();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment