This repository has been archived on 2024-06-04. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
thesis-src/Implementation/ReportGenerator/ReportGenerator.cs
T
2024-01-08 16:45:35 +01:00

265 lines
6.4 KiB
C#

using Ocr.Report.Models;
using ReportGeneration.Abstract.Model;
using ReportGeneration.Interface;
namespace Ocr.Report;
public class ReportGenerator : IDisposable
{
private IDocumentGenerator Document { get; }
private ICollection<ImageStats> Images { get; }
public ReportGenerator(
IDocumentGenerator document,
IEnumerable<ImageStats> data
)
{
Images = data.ToArray();
Document = document;
document.Open();
}
public ReportGenerator(
string title,
IDocumentGenerator document,
IEnumerable<ImageStats> data
) : this(document, data) => AddTitle(title);
#region Writing
public ReportGenerator AddTitle(string text)
{
Document.AppendHeading(1, text);
return this;
}
public ReportGenerator AddProcessorStats(string title)
{
Document.AppendHeading(2, title);
var processors = new Dictionary<string, ICollection<ImageStats>>();
foreach (var image in Images)
{
foreach (var processor in image.Processors)
{
if (processors.TryGetValue(processor.Name, out var images))
{
images.Add(image);
}
else
{
processors.Add(processor.Name, new List<ImageStats> { image });
}
}
}
foreach (var (processor, images) in processors)
{
var ordered = images
.Select(i => (Stats: i, Distance: i
.Processors
.Where(p => p.Name.Equals(processor))
.Select(p => p.Distance)
.Average()
))
.OrderBy(i => i.Distance)
.ToArray();
Document
.AppendHeading(3, processor)
.AppendTable(2, table =>
{
table.AppendHeader(new[] { "Image", "Preview", "Distance" });
foreach (var (stats, distance) in ordered)
{
var imgPath = Path.Combine("results", $"{processor}.00.{stats.ImageName}.png");
table.AppendRow(new[]
{
$"<a href=\"#{stats.ImageName}\">{stats.ImageName}</a>",
Document.FormatImage(imgPath, new Bounds(0, 150)),
distance.ToString("F2")
});
}
}
);
}
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("Perfect matches")
.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.Count(w => w.Distance == 0)} / {processor.Words.Count}")
.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<IEnumerable<double>, (double, double)> evaluationFunc
)
{
var lookup = Images
.SelectMany(s => s.Processors)
.ToLookup(p => p.Name);
Document.AppendHeading(2, title);
var byWer = lookup
.Select(g =>
{
var (value, deviation) = evaluationFunc(g.Select(p => p.Distance * 100));
return (
Name: g.Key,
Value: value,
Deviation: deviation
);
})
.OrderBy(g => g.Value);
Document.AppendHeading(3, "WER");
AppendComparison(("Error", "%"), byWer);
var byCer = lookup
.Select(g =>
{
var (value, deviation) =
evaluationFunc(g.SelectMany(p => p.Words, (_, word) => word.Distance));
return (
Name: g.Key,
Value: value,
Deviation: deviation
);
})
.OrderBy(g => g.Value);
Document.AppendHeading(3, "CER");
AppendComparison(("Changes", string.Empty), byCer);
var byTime = lookup
.Select(g =>
{
var (value, deviation) = evaluationFunc(g.Select(p => p.ProcessingTime * 1000));
return (
Name: g.Key,
Value: value,
Deviation: deviation
);
})
.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, double)> values
)
{
const int context = 5;
var tValues = values.ToArray();
var tContext = Math.Min(tValues.Length / 2, context);
Document.AppendTable(3, table =>
{
table
.AppendHeader(new[]
{
"Processor",
valueInfo?.name ?? "Value",
"Deviation"
})
.AppendRows(tValues
.Take(tContext)
.Select(MakeRow))
.AppendRow("...")
.AppendRows(
tValues
.TakeLast(tContext)
.Select(MakeRow)
);
return;
string[] MakeRow((string, double, double) v) =>
new[]
{
v.Item1,
v.Item2.ToString("F2") + valueInfo?.unit,
v.Item3.ToString("F2") + valueInfo?.unit
};
});
}
#endregion
#region IDisposable
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Document.Dispose();
}
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}