Initial implementation
@@ -0,0 +1,42 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Ocr\Ocr.Processors\Ocr.Processors.csproj" />
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="img\command-processing_screentypes_controlgroup_005.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\editor_startpage_project-exist_001.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\editor_windows_position_006.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\historian_assistent_001.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\zrs_MetadataEditor_variables_001.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\zrs_REPORTS_EfficencyClass_009.png">
|
||||
<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.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,16 @@
|
||||
using Common;
|
||||
using Common.Extensions;
|
||||
using ImageMagick;
|
||||
|
||||
var scanner = new ScreenshotScanner();
|
||||
|
||||
Console.WriteLine($"# Scanning: {string.Join(',', args)}...");
|
||||
scanner.Process(GetImages(args));
|
||||
|
||||
Console.WriteLine($"# Results ({scanner.Lookup.Keys.Count}):");
|
||||
Console.WriteLine(string.Join(' ', scanner.Lookup.Keys));
|
||||
|
||||
static IEnumerable<MagickImage> GetImages(IEnumerable<string> paths) => paths
|
||||
.SelectMany(p => p.ExpandPath())
|
||||
.Select(p => new MagickImage(p))
|
||||
.ToArray();
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"profiles": {
|
||||
"WSL": {
|
||||
"commandName": "WSL2",
|
||||
"distributionName": ""
|
||||
},
|
||||
"Test all img": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "\"img/*\""
|
||||
},
|
||||
"Test single img": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "\"img/historian_assistent_001.png\""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,33 @@
|
||||
<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.Processors\Ocr.Processors.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>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="tessdata\deu.traineddata">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="tessdata\eng.traineddata">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="tessdata\osd.traineddata">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Common.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for the string object type
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
private static readonly Regex patternRegex = new Regex(@"^\*$");
|
||||
|
||||
/// <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 ICollection<string> ExpandPath(this string self)
|
||||
{
|
||||
string pattern = Path.GetFileName(self);
|
||||
if (patternRegex.IsMatch(pattern))
|
||||
{
|
||||
return Directory.GetFiles(
|
||||
self.Substring(0, self.Length - pattern.Length),
|
||||
pattern,
|
||||
SearchOption.TopDirectoryOnly
|
||||
);
|
||||
}
|
||||
|
||||
return new[] { self };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using ImageMagick;
|
||||
using Lookup.Memory;
|
||||
using Ocr.Tesseract;
|
||||
using Ocr.Tesseract.Configuration;
|
||||
using Ocr.Tesseract.Models;
|
||||
using Ocr.Tesseract.Screenshots;
|
||||
using Ocr.Tesseract.Screenshots.Configuration;
|
||||
using Process.Abstract.Configuration;
|
||||
using Process.Interface;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
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
|
||||
{
|
||||
private readonly IProcessor<MagickImage, ScanResult> _processor;
|
||||
|
||||
/// <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
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Data storage
|
||||
/// </summary>
|
||||
public Lookup.Interface.ILookup<Word, MagickImage> Lookup { get; } =
|
||||
new MemoryLookup<Word, MagickImage>();
|
||||
|
||||
/// <summary>
|
||||
/// Configuration of the <see cref="ImageProcessor"/>
|
||||
/// </summary>
|
||||
public ScreenshotProcessorConfiguration ImageProcessorConfiguration { get; set; } = new();
|
||||
|
||||
public ITesseractConfiguration TesseractConfiguration { get; set; } =
|
||||
new TesseractScreenshotConfiguration();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public ScreenshotScanner()
|
||||
{
|
||||
_processor = MakeProcessor();
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
private IProcessor<MagickImage, ScanResult> MakeProcessor()
|
||||
{
|
||||
return new ProcessorChainConfiguration<MagickImage, ScanResult>()
|
||||
.Use(new ScreenshotProcessor(ImageProcessorConfiguration)) // Preprocess input data
|
||||
.Use(new ProcessingEvent<MagickImage>(OnProcessing)) // Scan
|
||||
.Use(new TesseractProcessor(TesseractConfiguration)) // Scan
|
||||
.Use(new ProcessingEvent<ScanResult>(OnProcessed)) // Scan
|
||||
.Use(new ConfidenceFilter(50)) // Process output data
|
||||
.Use(new DuplicateFilter())
|
||||
.Use(new ToLowerProcessor())
|
||||
.Complete(new RegexFilter(wordRegex));
|
||||
}
|
||||
|
||||
protected virtual void OnProcessing(IProcessor sender, ICollection<MagickImage> inputs)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnProcessed(IProcessor sender, ICollection<ScanResult> inputs)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33424.131
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{CF65AA6A-2F25-4FEE-BDC1-AD96E1FFFA49}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Process", "Process", "{E55C5AE2-39DF-4AC6-B7AC-3100B0ACFD77}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Process.Interface", "..\Process\Process.Interface\Process.Interface.csproj", "{249ECD4B-B160-4DFF-B4A5-888D702182B1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Process.Abstract", "..\Process\Process.Abstract\Process.Abstract.csproj", "{369DB407-CF9A-4DC4-83BF-391894B2D25E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lookup", "Lookup", "{25AA2201-7BB4-4D00-A979-74FA04EB225B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Memory", "..\Lookup\Lookup.Memory\Lookup.Memory.csproj", "{061EDDA9-0A15-452C-8AEA-C15B5532AD90}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Interface", "..\Lookup\Lookup.Interface\Lookup.Interface.csproj", "{D6281061-E3CB-4F32-ABBB-4B41CE6189CE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.IO", "..\Lookup\Lookup.File\Lookup.IO.csproj", "{3944B59F-CB56-4A43-A2CE-7CE9C00BBB6C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Database", "..\Lookup\Lookup.Database\Lookup.Database.csproj", "{EF39CBC7-E961-4CE8-ABC0-4FC1F3F8C91B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Abstract", "..\Lookup\Lookup.Abstract\Lookup.Abstract.csproj", "{D14DA0B8-5EAE-4C77-992E-3527DC84CE6D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CLI", "CLI\CLI.csproj", "{2856493F-EF1C-42A1-8EE5-6C0387D08F95}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GUI", "GUI\GUI.csproj", "{DA447F14-1B1D-4733-99F3-6EF8225DCBAB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csproj", "{A6C738AC-DCD7-4024-A92D-3FC3CDCD7229}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ocr", "Ocr", "{E55F78E4-09F1-4D79-A9A2-460562C96DAB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocr.Tesseract", "..\Ocr\Ocr.Tesseract\Ocr.Tesseract.csproj", "{D9B70035-0159-4D75-8ED6-2461F060F683}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocr.Tesseract.Screenshots", "..\Ocr\Ocr.Tesseract.Screenshots\Ocr.Tesseract.Screenshots.csproj", "{251F9AC9-3765-498C-83FD-DB3539A19CB3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{249ECD4B-B160-4DFF-B4A5-888D702182B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{249ECD4B-B160-4DFF-B4A5-888D702182B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{249ECD4B-B160-4DFF-B4A5-888D702182B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{249ECD4B-B160-4DFF-B4A5-888D702182B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{369DB407-CF9A-4DC4-83BF-391894B2D25E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{369DB407-CF9A-4DC4-83BF-391894B2D25E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{369DB407-CF9A-4DC4-83BF-391894B2D25E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{369DB407-CF9A-4DC4-83BF-391894B2D25E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{061EDDA9-0A15-452C-8AEA-C15B5532AD90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{061EDDA9-0A15-452C-8AEA-C15B5532AD90}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{061EDDA9-0A15-452C-8AEA-C15B5532AD90}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{061EDDA9-0A15-452C-8AEA-C15B5532AD90}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D6281061-E3CB-4F32-ABBB-4B41CE6189CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D6281061-E3CB-4F32-ABBB-4B41CE6189CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D6281061-E3CB-4F32-ABBB-4B41CE6189CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D6281061-E3CB-4F32-ABBB-4B41CE6189CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3944B59F-CB56-4A43-A2CE-7CE9C00BBB6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3944B59F-CB56-4A43-A2CE-7CE9C00BBB6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3944B59F-CB56-4A43-A2CE-7CE9C00BBB6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3944B59F-CB56-4A43-A2CE-7CE9C00BBB6C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF39CBC7-E961-4CE8-ABC0-4FC1F3F8C91B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF39CBC7-E961-4CE8-ABC0-4FC1F3F8C91B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF39CBC7-E961-4CE8-ABC0-4FC1F3F8C91B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF39CBC7-E961-4CE8-ABC0-4FC1F3F8C91B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D14DA0B8-5EAE-4C77-992E-3527DC84CE6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D14DA0B8-5EAE-4C77-992E-3527DC84CE6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D14DA0B8-5EAE-4C77-992E-3527DC84CE6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D14DA0B8-5EAE-4C77-992E-3527DC84CE6D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2856493F-EF1C-42A1-8EE5-6C0387D08F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2856493F-EF1C-42A1-8EE5-6C0387D08F95}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2856493F-EF1C-42A1-8EE5-6C0387D08F95}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2856493F-EF1C-42A1-8EE5-6C0387D08F95}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DA447F14-1B1D-4733-99F3-6EF8225DCBAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA447F14-1B1D-4733-99F3-6EF8225DCBAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA447F14-1B1D-4733-99F3-6EF8225DCBAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA447F14-1B1D-4733-99F3-6EF8225DCBAB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A6C738AC-DCD7-4024-A92D-3FC3CDCD7229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A6C738AC-DCD7-4024-A92D-3FC3CDCD7229}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A6C738AC-DCD7-4024-A92D-3FC3CDCD7229}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A6C738AC-DCD7-4024-A92D-3FC3CDCD7229}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D9B70035-0159-4D75-8ED6-2461F060F683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D9B70035-0159-4D75-8ED6-2461F060F683}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D9B70035-0159-4D75-8ED6-2461F060F683}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D9B70035-0159-4D75-8ED6-2461F060F683}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{251F9AC9-3765-498C-83FD-DB3539A19CB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{251F9AC9-3765-498C-83FD-DB3539A19CB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{251F9AC9-3765-498C-83FD-DB3539A19CB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{251F9AC9-3765-498C-83FD-DB3539A19CB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{E55C5AE2-39DF-4AC6-B7AC-3100B0ACFD77} = {CF65AA6A-2F25-4FEE-BDC1-AD96E1FFFA49}
|
||||
{249ECD4B-B160-4DFF-B4A5-888D702182B1} = {E55C5AE2-39DF-4AC6-B7AC-3100B0ACFD77}
|
||||
{369DB407-CF9A-4DC4-83BF-391894B2D25E} = {E55C5AE2-39DF-4AC6-B7AC-3100B0ACFD77}
|
||||
{25AA2201-7BB4-4D00-A979-74FA04EB225B} = {CF65AA6A-2F25-4FEE-BDC1-AD96E1FFFA49}
|
||||
{061EDDA9-0A15-452C-8AEA-C15B5532AD90} = {25AA2201-7BB4-4D00-A979-74FA04EB225B}
|
||||
{D6281061-E3CB-4F32-ABBB-4B41CE6189CE} = {25AA2201-7BB4-4D00-A979-74FA04EB225B}
|
||||
{3944B59F-CB56-4A43-A2CE-7CE9C00BBB6C} = {25AA2201-7BB4-4D00-A979-74FA04EB225B}
|
||||
{EF39CBC7-E961-4CE8-ABC0-4FC1F3F8C91B} = {25AA2201-7BB4-4D00-A979-74FA04EB225B}
|
||||
{D14DA0B8-5EAE-4C77-992E-3527DC84CE6D} = {25AA2201-7BB4-4D00-A979-74FA04EB225B}
|
||||
{E55F78E4-09F1-4D79-A9A2-460562C96DAB} = {CF65AA6A-2F25-4FEE-BDC1-AD96E1FFFA49}
|
||||
{D9B70035-0159-4D75-8ED6-2461F060F683} = {E55F78E4-09F1-4D79-A9A2-460562C96DAB}
|
||||
{251F9AC9-3765-498C-83FD-DB3539A19CB3} = {E55F78E4-09F1-4D79-A9A2-460562C96DAB}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DFA659EE-FE78-4BD9-888B-78984354093E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,5 @@
|
||||
<Application x:Class="GUI.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Application.Resources/>
|
||||
</Application>
|
||||
@@ -0,0 +1,27 @@
|
||||
using GUI.ViewModels;
|
||||
using GUI.Views;
|
||||
using Serilog;
|
||||
using System.Windows;
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
var loggingCollection = new LoggingCollection(100);
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Sink(loggingCollection)
|
||||
.CreateLogger();
|
||||
|
||||
new LogView(loggingCollection).Show();
|
||||
new ImageView().Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
@@ -0,0 +1,34 @@
|
||||
<UserControl
|
||||
x:Class="GUI.Controls.ImageControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:converters="clr-namespace:GUI.Converters"
|
||||
xmlns:controls="clr-namespace:GUI.Controls"
|
||||
d:DataContext="{d:DesignInstance controls:ImageControl}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<converters:ImageConverter x:Key="Converter.CImage" />
|
||||
|
||||
<ControlTemplate x:Key="ControlTemplate.ImageControl.Default" TargetType="controls:ImageControl">
|
||||
<Border DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
|
||||
Padding="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Image RenderOptions.BitmapScalingMode="NearestNeighbor"
|
||||
Stretch="Uniform"
|
||||
Source="{Binding Image, Converter={StaticResource Converter.CImage}}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
|
||||
<Style x:Key="Style.ImageControl.Default" TargetType="controls:ImageControl">
|
||||
<Setter Property="Template" Value="{StaticResource ControlTemplate.ImageControl.Default}" />
|
||||
</Style>
|
||||
|
||||
<Style BasedOn="{StaticResource Style.ImageControl.Default}" TargetType="controls:ImageControl" />
|
||||
</UserControl.Resources>
|
||||
<Grid />
|
||||
</UserControl>
|
||||
@@ -0,0 +1,45 @@
|
||||
using ImageMagick;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ImageControl.xaml
|
||||
/// </summary>
|
||||
public partial class ImageControl : UserControl
|
||||
{
|
||||
/// <inheritdoc cref="Image"/>
|
||||
public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(
|
||||
nameof(Image), typeof(MagickImage), typeof(ImageControl), new PropertyMetadata(default(MagickImage)));
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MagickImage"/> displayed in this <see cref="ImageControl"/>
|
||||
/// </summary>
|
||||
public MagickImage Image
|
||||
{
|
||||
get => (MagickImage)GetValue(ImageProperty);
|
||||
set => SetValue(ImageProperty, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ImageName"/>
|
||||
public static readonly DependencyProperty ImageNameProperty = DependencyProperty.Register(
|
||||
nameof(ImageName), typeof(object), typeof(ImageControl),
|
||||
new PropertyMetadata(default(object)));
|
||||
|
||||
/// <summary>
|
||||
/// The name of the loaded image
|
||||
/// </summary>
|
||||
public object ImageName
|
||||
{
|
||||
get => (object)GetValue(ImageNameProperty);
|
||||
set => SetValue(ImageNameProperty, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ImageControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using ImageMagick;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GUI.Converters
|
||||
{
|
||||
internal class ImageConverter : IValueConverter
|
||||
{
|
||||
#region Implementation of IValueConverter
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not MagickImage image)
|
||||
{
|
||||
return Binding.DoNothing;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
|
||||
// Save image to stream
|
||||
image.Write(stream, MagickFormat.Png);
|
||||
|
||||
// Build Bitmap from stream
|
||||
var imageSource = new BitmapImage();
|
||||
imageSource.BeginInit();
|
||||
imageSource.StreamSource = stream;
|
||||
imageSource.CacheOption = BitmapCacheOption.OnLoad;
|
||||
imageSource.EndInit();
|
||||
|
||||
return imageSource;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{e.Message}");
|
||||
return Binding.DoNothing;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="img\command-processing_screentypes_controlgroup_005.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\editor_startpage_project-exist_001.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\editor_windows_position_006.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\historian_assistent_001.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\zrs_MetadataEditor_variables_001.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="img\zrs_REPORTS_EfficencyClass_009.png">
|
||||
<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.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace GUI.Model;
|
||||
|
||||
public class Command : ICommand
|
||||
{
|
||||
public Action Action { get; set; }
|
||||
|
||||
public Command(Action action)
|
||||
{
|
||||
Action = action;
|
||||
}
|
||||
|
||||
#region Implementation of ICommand
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanExecute(object? parameter)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
Action?.Invoke();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
using Common;
|
||||
using GUI.Model;
|
||||
using ImageMagick;
|
||||
using Microsoft.Win32;
|
||||
using Ocr.Tesseract.Models;
|
||||
using Process.Interface;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace GUI.ViewModels;
|
||||
|
||||
internal class ImageViewModel : ScreenshotScanner, INotifyPropertyChanged
|
||||
{
|
||||
public ImageViewModel()
|
||||
{
|
||||
// Scanner.ImageOperationSettings.PropertyChanged += (sender, args) => Task.Run(UpdateImage);
|
||||
|
||||
OpenFileCommand = new Command(OpenFile);
|
||||
SaveEditedImageCommand = new Command(SaveEditedImage);
|
||||
}
|
||||
|
||||
#region Overrides of Scanner
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnProcessing(IProcessor sender, ICollection<MagickImage> inputs)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
foreach (var image in inputs)
|
||||
{
|
||||
Edited.Add(image);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnProcessed(IProcessor sender, ICollection<ScanResult> inputs)
|
||||
{
|
||||
ScannedText = $"[{inputs.Count} words] " + string.Join(' ', inputs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ImageViewModel(MagickImage image) : this()
|
||||
{
|
||||
Image = image;
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
ScannedText = string.Empty;
|
||||
Words.Clear();
|
||||
Lookup.Clear();
|
||||
Edited.Clear();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void OpenFile()
|
||||
{
|
||||
var dialog = new OpenFileDialog()
|
||||
{
|
||||
InitialDirectory = Directory.GetCurrentDirectory()
|
||||
};
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
Image = new MagickImage(dialog.FileName);
|
||||
UpdateImage();
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveEditedImage()
|
||||
{
|
||||
var basePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
for (var i = 0; i < Edited.Count; i++)
|
||||
{
|
||||
Edited[i].Write(Path.Combine(basePath, $"edited_{i}.png"));
|
||||
}
|
||||
|
||||
Log.Information($"Saved image to '{basePath}'");
|
||||
System.Diagnostics.Process.Start(
|
||||
"explorer.exe",
|
||||
Path.GetDirectoryName(basePath) ?? string.Empty
|
||||
);
|
||||
}
|
||||
|
||||
private void UpdateConfidence()
|
||||
{
|
||||
Confidence = Lookup.Keys.Any()
|
||||
? Lookup.Keys.Sum(key => key.Confidence) / Lookup.Keys.Count
|
||||
: 0;
|
||||
}
|
||||
|
||||
private void UpdateImage()
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
IsIdle = false;
|
||||
|
||||
Clear();
|
||||
if (Image != null)
|
||||
{
|
||||
Process(new[] { Image });
|
||||
}
|
||||
|
||||
UpdateWords();
|
||||
UpdateConfidence();
|
||||
|
||||
IsIdle = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateWords()
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
foreach (var word in Lookup.Keys)
|
||||
{
|
||||
Words.Add(word);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
private float _confidence;
|
||||
private MagickImage? _image;
|
||||
private bool _isIdle;
|
||||
|
||||
private string _scannedText = string.Empty;
|
||||
|
||||
public string ScannedText
|
||||
{
|
||||
get => _scannedText;
|
||||
set
|
||||
{
|
||||
if (value == _scannedText)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_scannedText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsIdle
|
||||
{
|
||||
get => _isIdle;
|
||||
set
|
||||
{
|
||||
if (value == _isIdle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isIdle = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public float Confidence
|
||||
{
|
||||
get => _confidence;
|
||||
set
|
||||
{
|
||||
_confidence = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<MagickImage> Edited { get; } = new();
|
||||
|
||||
public MagickImage? Image
|
||||
{
|
||||
get => _image;
|
||||
set
|
||||
{
|
||||
_image = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<Word> Words { get; } = new();
|
||||
|
||||
#endregion Properties
|
||||
|
||||
#region Commands
|
||||
|
||||
public ICommand OpenFileCommand { get; private set; }
|
||||
public ICommand SaveEditedImageCommand { get; private set; }
|
||||
|
||||
#endregion Commands
|
||||
|
||||
#region INotifyPropertyChanged
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion INotifyPropertyChanged
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace GUI.ViewModels;
|
||||
|
||||
public class LogMessage
|
||||
{
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace GUI.ViewModels
|
||||
{
|
||||
public class LogViewModel
|
||||
{
|
||||
public LoggingCollection LoggingCollection { get; }
|
||||
|
||||
public LogViewModel(LoggingCollection loggingCollection)
|
||||
{
|
||||
LoggingCollection = loggingCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace GUI.ViewModels;
|
||||
|
||||
public class LoggingCollection : ILogEventSink
|
||||
{
|
||||
public int Capacity { get; }
|
||||
|
||||
public ObservableCollection<LogMessage> Items { get; }
|
||||
|
||||
public LoggingCollection(int capacity)
|
||||
{
|
||||
Capacity = capacity;
|
||||
Items = new ObservableCollection<LogMessage>(new List<LogMessage>(capacity));
|
||||
}
|
||||
|
||||
public void Trim(int offset = 0)
|
||||
{
|
||||
for (int i = Items.Count - Capacity - offset; i >= 0; i--)
|
||||
{
|
||||
Items.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
#region Implementation of ILogEventSink
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Emit(LogEvent logEvent)
|
||||
{
|
||||
Trim(1);
|
||||
|
||||
Items.Add(new LogMessage
|
||||
{
|
||||
Timestamp = logEvent.Timestamp.DateTime,
|
||||
Message = logEvent.RenderMessage()
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
<Window
|
||||
x:Class="GUI.Views.ImageView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:GUI.Controls"
|
||||
xmlns:viewModels="clr-namespace:GUI.ViewModels"
|
||||
Title="OcrView"
|
||||
Width="800"
|
||||
Height="450"
|
||||
d:DataContext="{d:DesignInstance viewModels:ImageViewModel}"
|
||||
mc:Ignorable="d">
|
||||
<Window.Resources>
|
||||
<CollectionViewSource
|
||||
x:Key="View.Words"
|
||||
Source="{Binding Words}">
|
||||
<CollectionViewSource.SortDescriptions>
|
||||
<componentModel:SortDescription
|
||||
Direction="Descending"
|
||||
PropertyName="Confidence" />
|
||||
</CollectionViewSource.SortDescriptions>
|
||||
</CollectionViewSource>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="*"
|
||||
MinHeight="300" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Menu Grid.Row="0">
|
||||
<MenuItem Header="File">
|
||||
<MenuItem
|
||||
Command="{Binding OpenFileCommand}"
|
||||
Header="Open image..." />
|
||||
<MenuItem
|
||||
Command="{Binding SaveEditedImageCommand}"
|
||||
Header="Save edited image" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition
|
||||
Height="*"
|
||||
MinHeight="120" />
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition
|
||||
Height="2*"
|
||||
MinHeight="120" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition
|
||||
Width="*"
|
||||
MinWidth="120" />
|
||||
<ColumnDefinition Width="4" />
|
||||
<ColumnDefinition
|
||||
Width="2*"
|
||||
MinWidth="120" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollViewer
|
||||
Grid.Column="0"
|
||||
HorizontalScrollBarVisibility="Visible"
|
||||
VerticalScrollBarVisibility="Disabled">
|
||||
<controls:ImageControl Image="{Binding Image}" />
|
||||
</ScrollViewer>
|
||||
<GridSplitter
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ResizeBehavior="PreviousAndNext"
|
||||
ResizeDirection="Columns" />
|
||||
<ListBox
|
||||
Grid.Column="2"
|
||||
ItemsSource="{Binding Edited}"
|
||||
ScrollViewer.CanContentScroll="False"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Visible"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel
|
||||
IsItemsHost="True"
|
||||
Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
BorderBrush="CornflowerBlue"
|
||||
BorderThickness="2">
|
||||
<controls:ImageControl
|
||||
VerticalAlignment="Stretch"
|
||||
Image="{Binding NotifyOnSourceUpdated=True}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
<GridSplitter
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ResizeBehavior="PreviousAndNext"
|
||||
ResizeDirection="Rows" />
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*" MinWidth="60"></ColumnDefinition>
|
||||
<ColumnDefinition Width="4"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*" MinWidth="60"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.Column="2" VerticalContentAlignment="Top" HorizontalContentAlignment="Left"
|
||||
TextWrapping="Wrap" Text="{Binding ScannedText}">
|
||||
</TextBox>
|
||||
<GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ResizeBehavior="PreviousAndNext"
|
||||
ResizeDirection="Columns">
|
||||
</GridSplitter>
|
||||
<ListBox Grid.Column="0"
|
||||
d:ItemsSource="{d:SampleData ItemCount=25}"
|
||||
ItemsSource="{Binding Source={StaticResource View.Words}}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
IsItemsHost="True" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid ToolTip="{Binding Text}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="36" />
|
||||
<ColumnDefinition Width="80" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Text="{Binding Confidence,
|
||||
StringFormat=0.00}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding Text}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<UniformGrid
|
||||
IsEnabled="{Binding IsIdle}"
|
||||
Grid.Row="2"
|
||||
Margin="16"
|
||||
Columns="3">
|
||||
<CheckBox
|
||||
x:Name="EnableThreshold"
|
||||
Content="Apply Threshold"
|
||||
IsChecked="{Binding ImageProcessorConfiguration.EnableThresholding}" />
|
||||
|
||||
<Grid
|
||||
Margin="4"
|
||||
IsEnabled="{Binding ElementName=EnableThreshold,
|
||||
Path=IsChecked}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0">Block Width:</TextBlock>
|
||||
<Slider
|
||||
x:Name="SldThreshold1"
|
||||
Grid.Column="1"
|
||||
Margin="4,0"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Thumb.DragCompleted="SldThreshold1_OnDragCompleted"
|
||||
Value="15" />
|
||||
<TextBlock Grid.Column="2">
|
||||
<Run Text="{Binding Value, ElementName=SldThreshold1, FallbackValue=0, StringFormat=0.00}" />
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<Grid
|
||||
Margin="4"
|
||||
IsEnabled="{Binding ElementName=EnableThreshold,
|
||||
Path=IsChecked}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0">Block Height:</TextBlock>
|
||||
<Slider
|
||||
x:Name="SldThreshold2"
|
||||
Grid.Column="1"
|
||||
Margin="4,0"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Thumb.DragCompleted="SldThreshold2_OnDragCompleted"
|
||||
Value="17" />
|
||||
<TextBlock Grid.Column="2">
|
||||
<Run Text="{Binding Value, ElementName=SldThreshold2, FallbackValue=0, StringFormat=0.00}" />
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<CheckBox
|
||||
Content="Resize"
|
||||
IsChecked="{Binding ImageProcessorConfiguration.EnableResizing}" />
|
||||
<Grid Margin="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0">Border:</TextBlock>
|
||||
<Slider
|
||||
x:Name="SldBorder"
|
||||
Grid.Column="1"
|
||||
Margin="4,0"
|
||||
Maximum="25"
|
||||
Minimum="0"
|
||||
Thumb.DragCompleted="SldBorder_OnDragCompleted"
|
||||
Value="10" />
|
||||
<TextBlock Grid.Column="2">
|
||||
<Run Text="{Binding Value, ElementName=SldBorder, FallbackValue=0, StringFormat=0.00}" />
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<Rectangle />
|
||||
|
||||
|
||||
<CheckBox
|
||||
Content="Filter connected components"
|
||||
IsChecked="{Binding ImageProcessorConfiguration.FilterConnectedComponents}" />
|
||||
</UniformGrid>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,43 @@
|
||||
using GUI.ViewModels;
|
||||
using ImageMagick;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace GUI.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class ImageView : Window
|
||||
{
|
||||
public ImageView()
|
||||
{
|
||||
DataContext = new ImageViewModel();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ImageView(MagickImage image)
|
||||
{
|
||||
DataContext = new ImageViewModel(image);
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void SldThreshold1_OnDragCompleted(object sender, DragCompletedEventArgs args)
|
||||
{
|
||||
((ImageViewModel)DataContext).ImageProcessorConfiguration.ThresholdWidth = (int)Math.Round(((Slider)sender).Value);
|
||||
}
|
||||
|
||||
private void SldThreshold2_OnDragCompleted(object sender, DragCompletedEventArgs args)
|
||||
{
|
||||
((ImageViewModel)DataContext).ImageProcessorConfiguration.ThresholdHeight = (int)Math.Round(((Slider)sender).Value);
|
||||
}
|
||||
|
||||
|
||||
private void SldBorder_OnDragCompleted(object sender, DragCompletedEventArgs e)
|
||||
{
|
||||
((ImageViewModel)DataContext).ImageProcessorConfiguration.Border = (int)Math.Round(((Slider)sender).Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<Window
|
||||
x:Class="GUI.Views.LogView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:GUI.ViewModels"
|
||||
Title="LogView"
|
||||
Width="800"
|
||||
Height="450"
|
||||
d:DataContext="{d:DesignInstance viewModels:LogViewModel}"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<DataGrid
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding LoggingCollection.Items}"
|
||||
VirtualizingPanel.ScrollUnit="Pixel">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="Auto"
|
||||
MinWidth="120"
|
||||
Binding="{Binding Timestamp}"
|
||||
Header="Timestamp" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
MinWidth="120"
|
||||
MaxWidth="300"
|
||||
Binding="{Binding Message}"
|
||||
Header="Message">
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Left" />
|
||||
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</DataGridTextColumn.ElementStyle>
|
||||
</DataGridTextColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,17 @@
|
||||
using GUI.ViewModels;
|
||||
using System.Windows;
|
||||
|
||||
namespace GUI.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LogView.xaml
|
||||
/// </summary>
|
||||
public partial class LogView : Window
|
||||
{
|
||||
public LogView(LoggingCollection loggingCollection)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new LogViewModel(loggingCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.8 KiB |