Renamed Examples to Implementation

This commit is contained in:
Simon Gruber
2024-01-08 16:23:24 +01:00
parent b17044f959
commit 6c554e444f
1240 changed files with 0 additions and 0 deletions
@@ -0,0 +1,46 @@
using System.Text;
namespace Ocr.Cli.Monitor;
public class CliTaskMonitor : TaskMonitor
{
/// <inheritdoc />
public CliTaskMonitor(IEnumerable<(string? Name, Task Task)> tasks) : base(tasks) { }
/// <inheritdoc />
public CliTaskMonitor(IEnumerable<Task> tasks) : base(tasks) { }
#region Overrides of TaskMonitor
/// <inheritdoc />
protected override void OnUpdate(ICollection<(string? Name, Task Task)> tasks)
{
var sb = new StringBuilder(tasks.Count * 30);
foreach (var (name, task) in tasks)
{
sb.Append($"{StatusMap[task.Status],-5}");
if (name is not null)
{
sb.Append(": ")
.Append(name);
}
sb.AppendLine();
if (task.Exception is not null)
{
sb.Append("> EX: ")
.AppendLine(task.Exception.Message);
}
}
sb.AppendLine();
Console.Clear();
Console.Write(sb.ToString());
}
#endregion
}
@@ -0,0 +1,42 @@
using System.Text;
namespace Ocr.Cli.Monitor;
public class CompactCliTaskMonitor : TaskMonitor
{
/// <inheritdoc />
public CompactCliTaskMonitor(IEnumerable<(string? Name, Task Task)> tasks) : base(tasks) { }
/// <inheritdoc />
public CompactCliTaskMonitor(IEnumerable<Task> tasks) : base(tasks) { }
#region Overrides of TaskMonitor
/// <inheritdoc />
protected override void OnUpdate(ICollection<(string? Name, Task Task)> tasks)
{
int completed = 0;
int total = tasks.Count;
var sb = new StringBuilder(total * 30);
foreach (var (_, task) in tasks)
{
var status = task.Status;
if (status > TaskStatus.WaitingForChildrenToComplete)
{
completed++;
}
sb.Append(task.Exception is not null ? 'X' : StatusMap[status].First());
}
sb.AppendLine($" ({completed}/{total})");
Console.Clear();
Console.Write(sb.ToString());
}
#endregion
}
@@ -0,0 +1,8 @@
namespace Ocr.Cli.Monitor;
public interface ITaskMonitor
{
TimeSpan Interval { get; init; }
Task Run();
}
+42
View File
@@ -0,0 +1,42 @@
namespace Ocr.Cli.Monitor;
public abstract class TaskMonitor : ITaskMonitor
{
/// <inheritdoc />
public TimeSpan Interval { get; init; } = TimeSpan.FromSeconds(1);
private readonly ICollection<(string? Name, Task Task)> _tasks;
protected static IReadOnlyDictionary<TaskStatus, string> StatusMap { get; } =
new Dictionary<TaskStatus, string>
{
{ TaskStatus.RanToCompletion, "DONE" },
{ TaskStatus.Faulted, "FAULT" },
{ TaskStatus.Canceled, "CANCL" },
{ TaskStatus.Created, "WAIT" },
{ TaskStatus.WaitingToRun, "WAIT" },
{ TaskStatus.WaitingForActivation, "WAIT" },
{ TaskStatus.Running, "RUN" },
{ TaskStatus.WaitingForChildrenToComplete, "RUN" },
};
protected TaskMonitor(IEnumerable<(string? Name, Task Task)> tasks) => _tasks = tasks.ToArray();
protected TaskMonitor(IEnumerable<Task> tasks) :
this(tasks.Select(t => (t.Id.ToString(), t))!)
{ }
public Task Run()
{
var waitTask = Task.WhenAll(_tasks.Select(i => i.Task));
while (!waitTask.Wait(Interval))
{
OnUpdate(_tasks);
}
return waitTask;
}
protected abstract void OnUpdate(ICollection<(string? Name, Task Task)> tasks);
}
+65
View File
@@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="img\command-processing_screentypes_controlgroup_005.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\command-processing_screentypes_controlgroup_005.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\editor_startpage_project-exist_001.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\editor_startpage_project-exist_001.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\editor_windows_position_006.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\editor_windows_position_006.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\historian_assistent_001.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\historian_assistent_001.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_MetadataEditor_variables_001.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_MetadataEditor_variables_001.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_REPORTS_EfficencyClass_009.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_REPORTS_EfficencyClass_009.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_ZAMS_3rd-connector_014.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_ZAMS_3rd-connector_014.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_ZAMS_filter-alarmgroup_001.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="img\zrs_ZAMS_filter-alarmgroup_001.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,110 @@
using ImageMagick;
using Ocr.Tesseract;
using Ocr.Tesseract.Configuration;
using Ocr.Tesseract.Extensions;
using Ocr.Tesseract.Models;
using Ocr.Tesseract.Screenshots;
using Ocr.Tesseract.Screenshots.Configuration;
using Process.Abstract.Configuration;
using Process.Interface;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace Ocr.Cli.Processor;
internal class EvaluationProcessor
{
#region Configuration
/// <summary>
/// <see cref="Regex"/> expression for extracting whole words from scan results
/// </summary>
private static readonly Regex wordRegex = new(
@"[\w'\-äöüÄÖÜß]{2,}",
RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase
);
private static readonly ITesseractConfiguration tesseractConfig =
new TesseractScreenshotConfiguration
{
DataPath = "tessdata",
Languages = new[] { "eng", "deu" }
};
#endregion
#region Processors
private static readonly IProcessorChain<ScanResult, ScanResult> postProcessor =
new ProcessorChainConfiguration<ScanResult, ScanResult>()
.Use(new ConfidenceFilter(50))
.Use(new ToLowerProcessor())
.Use(new DuplicateFilter())
.Complete(new RegexFilter(wordRegex));
private IProcessorChain<MagickImage, ScanResult> MakeProcessor()
{
var preprocessing = new ProcessorChainConfiguration<MagickImage, MagickImage>()
.Use(new CloneImageProcessor())
.Use(new ResizeProcessor(FilterType.Lanczos2Sharp, PixelInterpolateMethod.Mesh))
.Use(new NormalizeProcessor())
.Use(_thresholdProcessor)
.Use(new AddBorderProcessor(10))
.Use(new BinarizeProcessor())
.Use(new NegateCloneProcessor())
.Complete(OnPreprocessed);
return new ProcessorChainConfiguration<MagickImage, ScanResult>()
.Use(preprocessing)
.Use(tesseractProcessor)
.Complete(postProcessor);
}
private static readonly TesseractProcessor tesseractProcessor = new(tesseractConfig);
private readonly StopwatchProcessor<MagickImage, MagickImage> _thresholdProcessor;
#endregion
public string OutputFolder { get; init; } = "results";
public EvaluationProcessor(
IProcessor<MagickImage, MagickImage> thresholdProcessor
) => _thresholdProcessor = new StopwatchProcessor<MagickImage, MagickImage>(thresholdProcessor);
/// <inheritdoc />
public Task Process(MagickImage image) => Task.Run(async () =>
{
var words = MakeProcessor()
.Process(new[] { image })
.Select(r => r.Word)
.ToArray();
var result = new
{
Words = words.ToArray(),
Elapsed = _thresholdProcessor.Elapsed?.TotalMilliseconds,
};
var name = Path.GetFileNameWithoutExtension(image.FileName);
var path = Path.Combine(OutputFolder, $"{name}.{_thresholdProcessor}.json");
await using var file = File.OpenWrite(path);
await JsonSerializer.SerializeAsync(file, result);
});
private IEnumerable<MagickImage> OnPreprocessed(IEnumerable<MagickImage> images)
{
var tImages = images.ToArray();
for (var i = 0; i < tImages.Length; i++)
{
var image = tImages[i].CloneImage();
var name = Path.GetFileName(image.FileName);
var path = Path.Combine(OutputFolder, $"{_thresholdProcessor}.{i:D2}.{name}");
image.Write(path);
}
return tImages;
}
}
@@ -0,0 +1,29 @@
using Process.Abstract;
using Process.Interface;
using System.Diagnostics;
namespace Ocr.Cli.Processor;
public class StopwatchProcessor<TInput, TOutput> : Processor<TInput, TOutput>
{
private readonly IProcessor<TInput, TOutput> _processor;
/// <summary>
/// Execution time of the last processing action
/// </summary>
public TimeSpan? Elapsed { get; private set; }
public StopwatchProcessor(IProcessor<TInput, TOutput> processor) => _processor = processor;
public override IEnumerable<TOutput> Process(IEnumerable<TInput> inputs)
{
var stopWatch = Stopwatch.StartNew();
var results = _processor.Process(inputs);
stopWatch.Stop();
Elapsed = stopWatch.Elapsed;
return results;
}
/// <inheritdoc />
public override string? ToString() => _processor.ToString();
}
+71
View File
@@ -0,0 +1,71 @@
using Common.Extensions;
using ImageMagick;
using Ocr.Cli.Monitor;
using Ocr.Cli.Processor;
using Ocr.Tesseract.Screenshots.Threshold;
using System.Diagnostics.CodeAnalysis;
namespace Ocr.Cli;
public class Program
{
public Task Run(string[] args)
{
Directory.Delete("results", true);
Directory.CreateDirectory("results");
var scans = (
from processor in MakeThresholdVariations()
from path in ExpandPaths(args)
select (Key: path, Task: processor.Process(new MagickImage(path)))
).ToArray();
// return new CliTaskMonitor(scans) { Interval = TimeSpan.FromMilliseconds(500) }.Run();
return new CompactCliTaskMonitor(scans) { Interval = TimeSpan.FromMilliseconds(500) }.Run();
}
[SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")]
private static IEnumerable<EvaluationProcessor> MakeThresholdVariations()
{
for (int i = 4; i <= 24; i += 4)
{
yield return new(new ThresholdAdaptiveProcessor(i));
}
for (int i = 20; i <= 80; i += 10)
{
yield return new(new ThresholdProcessor(i));
}
yield return new(new AutoThresholdProcessor(AutoThresholdMethod.Kapur));
yield return new(new AutoThresholdProcessor(AutoThresholdMethod.OTSU));
yield return new(new AutoThresholdProcessor(AutoThresholdMethod.Triangle));
}
private static IEnumerable<string> ExpandPaths(params string[] paths) =>
paths.SelectMany(p => p.ExpandPath());
#region Main
public static int Main(string[] args)
{
Console.WriteLine("Starting up");
try
{
new Program()
.Run(args)
.Wait();
Console.WriteLine("Completed");
return 0;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return 1;
}
}
#endregion
}
@@ -0,0 +1,17 @@
{
"profiles": {
"WSL": {
"commandName": "WSL2",
"distributionName": ""
},
"Test all img": {
"commandName": "Project",
"commandLineArgs": "\"img/*.png\"",
"workingDirectory": "D:\\git\\BA\\Examples\\testdata"
},
"Test single img": {
"commandName": "Project",
"commandLineArgs": "\"img/historian_assistent_001.png\""
}
}
}