using ReportGenerator.Generator.Abstract; using ReportGenerator.Generator.Interface; using ReportGenerator.Generator.Model; using ReportGenerator.Models; namespace ReportGenerator; public class ReportGenerator : FileSerializableBase { /// public override string FileExtension => Document.FileExtension; private IDocumentGenerator Document { get; } private ICollection Images { get; } public ReportGenerator( IDocumentGenerator document, IEnumerable data ) { Document = document; Images = data.ToArray(); } public ReportGenerator( string title, IDocumentGenerator document, IEnumerable data ) : this(document, data) { WithTitle(title); } /// public override string ToString() => Document.ToString(); #region Report content generation public ReportGenerator WithTitle(string text) { Document.AppendHeading(1, text); return this; } public ReportGenerator AddImageStatsFull(string title) { Document.AppendHeading(2, title); foreach (var stat in Images) { Document .AppendHeading(3, stat.ImageName) .AppendParagraph( Document.FormatImage(Path.Combine("img", stat.ImageName), new Bounds(0, 350)) ) .AppendTable( stat.Reference.Count + 5, table => { table.AppendHeader(stat .Reference .Prepend("Image") .Prepend("CER (avg)") .Prepend("WER") .Prepend("Elapsed") .Prepend("Processor") ); var processors = stat.Processors .OrderBy(s => s.Distance) .ThenBy(s => s.ProcessingTime); foreach (var processor in processors) { var imgPath = Path.Combine("results", $"{processor.Name}.00.{stat.ImageName}.png"); table.AppendRow(processor.Words .Select(s => s.ToString() ?? string.Empty) .Prepend(Document.FormatImage(imgPath, new Bounds(0, 150))) .Prepend(processor.Words.Average(s => s.Distance).ToString("F2")) .Prepend($"{processor.Distance * 100:F1}%") .Prepend($"{processor.ProcessingTime * 1000:F1}ms") .Prepend(processor.Name) ); } }) .AppendParagraph( $"*Comparison data generated based on {stat.Reference.Count} tagged words.*" ); } return this; } public ReportGenerator AddComparison( string title, Func, double> evaluationFunc ) { var lookup = Images .SelectMany(s => s.Processors) .ToLookup(p => p.Name); Document.AppendHeading(2, title); var byWer = lookup .Select(g => ( Name: g.Key, Value: evaluationFunc(g.Select(p => p.Distance)) * 100 )) .OrderBy(g => g.Value); Document.AppendHeading(3, "WER"); AppendComparison(("Error", "%"), byWer); var byCer = lookup .Select(g => ( Name: g.Key, Value: evaluationFunc(g.SelectMany(p => p.Words, (_, word) => word.Distance)) )) .OrderBy(g => g.Value); Document.AppendHeading(3, "CER"); AppendComparison(("Changes", string.Empty), byCer); var byTime = lookup .Select(g => ( Name: g.Key, Value: evaluationFunc(g.Select(p => p.ProcessingTime)) * 1000 )) .OrderBy(g => g.Value); Document.AppendHeading(3, "Time"); AppendComparison(("Time", "ms"), byTime); return this; } private void AppendComparison( (string name, string unit)? valueInfo, IEnumerable<(string, double)> values ) { const int context = 5; var tValues = values.ToArray(); var tContext = Math.Min(tValues.Length / 2, context); Document.AppendTable(2, table => { table .AppendHeader(new[] { "Processor", valueInfo?.name ?? "Value" }) .AppendRows(tValues .Take(tContext) .Select(MakeRow)) .AppendRow("...") .AppendRows( tValues .TakeLast(tContext) .Select(MakeRow) ); return; string[] MakeRow((string, double) v) => new[] { v.Item1, v.Item2.ToString("F2") + valueInfo?.unit }; }); } #endregion }