Created
November 12, 2019 16:32
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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