Renamed Examples to Implementation
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="13.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Lookup\Lookup.Memory\Lookup.Memory.csproj" />
|
||||
<ProjectReference Include="..\..\Ocr\Ocr.Tesseract.Screenshots\Ocr.Tesseract.Screenshots.csproj" />
|
||||
<ProjectReference Include="..\..\Ocr\Ocr.Tesseract\Ocr.Tesseract.csproj" />
|
||||
<ProjectReference Include="..\..\Process\Process.Interface\Process.Interface.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Common.Distance;
|
||||
|
||||
public static class Calculator
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the levenshtein distance between two enumerables
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="reference"></param>
|
||||
/// <param name="hypothesis"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetDistance<T>(T reference, T? hypothesis)
|
||||
where T : IEnumerable
|
||||
{
|
||||
// Setup
|
||||
var refArr = reference.Cast<object>().ToArray();
|
||||
var hypArr = hypothesis?.Cast<object>().ToArray() ?? Array.Empty<object>();
|
||||
|
||||
var distance = new int[refArr.Length + 1, hypArr.Length + 1];
|
||||
|
||||
// Fill matrix
|
||||
for (var x = 0; x <= refArr.Length; x++)
|
||||
{
|
||||
// Reference on X axis
|
||||
distance[x, 0] = x;
|
||||
}
|
||||
|
||||
for (var y = 0; y <= hypArr.Length; y++)
|
||||
{
|
||||
// Hypothesis on Y axis
|
||||
distance[0, y] = y;
|
||||
}
|
||||
|
||||
// Calculate distance
|
||||
for (var x = 0; x < refArr.Length; x++)
|
||||
{
|
||||
for (var y = 0; y < hypArr.Length; y++)
|
||||
{
|
||||
// BL Cost depends on whether the two elements are equal
|
||||
var cost = Equals(refArr[x], hypArr[y]) ? 0 : 1;
|
||||
|
||||
// Apply distance mask
|
||||
var c1 = distance[x, y] + cost; // Bottom left
|
||||
|
||||
var c2 = distance[x, y + 1] + 1; // Top left
|
||||
var c3 = distance[x + 1, y] + 1; // Bottom right
|
||||
|
||||
distance[x + 1, y + 1] = Min(c1, c2, c3); // Top right
|
||||
}
|
||||
}
|
||||
|
||||
return distance[refArr.Length, hypArr.Length];
|
||||
}
|
||||
|
||||
private static T Min<T>(params T[] values)
|
||||
{
|
||||
if (!values.Any())
|
||||
{
|
||||
throw new ArgumentException("Array cannot be empty", nameof(values));
|
||||
}
|
||||
|
||||
return values.Min()!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Common.Distance;
|
||||
|
||||
public readonly struct DistanceComparer<T> : IDistanceComparer<T>
|
||||
where T : IEnumerable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public T Reference { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public T? Hypothesis { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public double Distance { get; }
|
||||
|
||||
public DistanceComparer(T reference) : this(reference, default)
|
||||
{
|
||||
}
|
||||
|
||||
public DistanceComparer(T reference, T? hypothesis)
|
||||
{
|
||||
Reference = reference;
|
||||
Hypothesis = hypothesis;
|
||||
|
||||
Distance = Calculator.GetDistance(Reference, Hypothesis);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
var str = Hypothesis?.ToString();
|
||||
|
||||
if (Hypothesis is var hyp && Equals(hyp, Reference))
|
||||
{
|
||||
return str ?? string.Empty;
|
||||
}
|
||||
|
||||
return
|
||||
$"<strong style='color: orange;' title='REf: {Reference}, CER: {Distance}'>{str ?? "-"}</strong>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Common.Distance;
|
||||
|
||||
public interface IDistanceComparer
|
||||
{
|
||||
/// <summary>
|
||||
/// The calculated absolute distance between
|
||||
/// <see cref="IDistanceComparer{T}.Reference"/> and
|
||||
/// <see cref="IDistanceComparer{T}.Hypothesis"/>
|
||||
/// </summary>
|
||||
public double Distance { get; }
|
||||
}
|
||||
|
||||
public interface IDistanceComparer<out T> : IDistanceComparer
|
||||
where T : IEnumerable
|
||||
{
|
||||
/// <summary>
|
||||
/// The comparison reference, meaning the "known to be correct" value
|
||||
/// </summary>
|
||||
public T Reference { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The value hypothesis, whose correctness is checked against <see cref="Reference"/>
|
||||
/// </summary>
|
||||
public T? Hypothesis { get; }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace Common.Extensions;
|
||||
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static double Median(this IEnumerable<double> values)
|
||||
{
|
||||
var tValues = values.OrderBy(v => v).ToArray();
|
||||
if (!tValues.Any())
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The list must contain at least one value for calculating the median");
|
||||
}
|
||||
|
||||
if (tValues.Length % 2 != 0)
|
||||
{
|
||||
return tValues[tValues.Length / 2];
|
||||
}
|
||||
|
||||
var center = tValues.Length / 2;
|
||||
return (tValues[center - 1] + tValues[center]) / 2.0;
|
||||
}
|
||||
|
||||
public static double Median(this IEnumerable<double> values, out double deviation)
|
||||
{
|
||||
var tValues = values.ToArray();
|
||||
var median = tValues.Median();
|
||||
deviation = tValues.Select(value => Math.Abs(value - median)).Median();
|
||||
return median;
|
||||
}
|
||||
|
||||
public static double Average(this IEnumerable<double> values, out double deviation)
|
||||
{
|
||||
var tValues = values?.ToArray();
|
||||
if (tValues is null || tValues.Length < 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The list must contain at least two values for calculating standard deviation");
|
||||
}
|
||||
|
||||
var average = tValues.Average();
|
||||
|
||||
var diffSquaredSum = tValues.Sum(value => Math.Pow(value - average, 2));
|
||||
deviation = Math.Sqrt(diffSquaredSum / (tValues.Length - 1));
|
||||
|
||||
return average;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Common.Extensions;
|
||||
|
||||
public static class StringBuilderExtensions
|
||||
{
|
||||
public static StringBuilder AppendHeading(this StringBuilder sb, int level, string text) => sb
|
||||
.Append(new string('#', level))
|
||||
.Append(' ')
|
||||
.AppendParagraph(text);
|
||||
|
||||
public static StringBuilder AppendParagraph(this StringBuilder sb, string text) => sb
|
||||
.AppendLine(text)
|
||||
.AppendLine();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace Common.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for the string object type
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether this string contains the specified string. Not case sensitive.
|
||||
/// </summary>
|
||||
/// <param name="source"> The source.</param>
|
||||
/// <param name="contained">The contained.</param>
|
||||
public static bool ContainsIgnoreCase(this string source, string contained)
|
||||
{
|
||||
return source?.IndexOf(contained, StringComparison.InvariantCultureIgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands a path containing a wildcard pattern
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> ExpandPath(this string self)
|
||||
{
|
||||
var parts = self.Split(Path.DirectorySeparatorChar);
|
||||
|
||||
var fileName = parts.Last();
|
||||
if (fileName.Contains('*') || fileName.Contains('?'))
|
||||
{
|
||||
// Path contains file pattern
|
||||
|
||||
var path = Path.Combine(parts.SkipLast(1).ToArray());
|
||||
return Directory.EnumerateFiles(path, fileName);
|
||||
}
|
||||
|
||||
// Path contains no pattern
|
||||
return new[] { self };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using ImageMagick;
|
||||
using Lookup.Memory;
|
||||
using Ocr.Tesseract.Models;
|
||||
using Process.Interface;
|
||||
|
||||
namespace Common;
|
||||
|
||||
/// <summary>
|
||||
/// Scanner class, scanning <see cref="MagickImage"/>s for <see cref="Word"/>s
|
||||
/// via optical character recognition. Optimized for digital Screenshots.
|
||||
/// </summary>
|
||||
public class ScreenshotScanner
|
||||
{
|
||||
/// <summary>
|
||||
/// The screenshot processor
|
||||
/// </summary>
|
||||
protected IProcessor<MagickImage, ScanResult> Processor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Data storage
|
||||
/// </summary>
|
||||
public Lookup.Interface.ILookup<Word, MagickImage> Lookup { get; } =
|
||||
new MemoryLookup<Word, MagickImage>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public ScreenshotScanner(IProcessor<MagickImage, ScanResult> processor)
|
||||
{
|
||||
Processor = processor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the provided <paramref name="images"/> and add the results to
|
||||
/// the <see cref="Lookup"/>
|
||||
/// </summary>
|
||||
/// <param name="images">The <see cref="MagickImage"/>s to process</param>
|
||||
public void Process(IEnumerable<MagickImage> images)
|
||||
{
|
||||
foreach (var kv in Processor.Process(images))
|
||||
{
|
||||
Lookup.Add(kv.Word, kv.Image);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
Lookup.Clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user