Add AI/OCR extraction feature with UI and CSV export

Integrates optional AI/OCR (AIFotoONLUS.Core) support to extract numbers from images after processing. Adds new "AI" tab in the UI for enabling extraction, selecting models folder, specifying CSV output, and previewing results. Results can be exported to CSV. Uses reflection for AI library invocation, with fallback simulation if unavailable. Persists new AI settings. Updates related NuGet packages and adds theme resources.
This commit is contained in:
MaddoScientisto 2026-02-16 18:32:04 +01:00
commit 6a5173a20d
8 changed files with 392 additions and 7 deletions

View file

@ -16,6 +16,8 @@ namespace ImageCatalog_2
InitializeComponent();
_model = model;
DataContext = _model;
// Apply theme based on user preference or system setting (default to light)
ApplyTheme(isDark: false);
// Subscribe to DataModel events that require UI dialogs
_model.SelectSourceFolderRequested += Model_SelectSourceFolderRequested;
_model.SelectDestinationFolderRequested += Model_SelectDestinationFolderRequested;
@ -24,11 +26,95 @@ namespace ImageCatalog_2
_model.LoadSettingsRequested += Model_LoadSettingsRequested;
_model.SelectColorRequested += Model_SelectColorRequested;
_model.SelectTransparentColorRequested += Model_SelectTransparentColorRequested;
_model.SelectModelsFolderRequested += Model_SelectModelsFolderRequested;
_model.SelectCsvOutputRequested += Model_SelectCsvOutputRequested;
// Watch for logo changes to update preview
_model.PropertyChanged += Model_PropertyChanged;
}
private void ApplyTheme(bool isDark)
{
try
{
var rd = isDark ? (ResourceDictionary)Resources["DarkTheme"] : (ResourceDictionary)Resources["LightTheme"];
foreach (var key in rd.Keys)
{
Resources[key] = rd[key];
}
}
catch
{
// ignore theme failures
}
}
private void Model_SelectModelsFolderRequested(object? sender, EventArgs e)
{
var dlg = new System.Windows.Forms.FolderBrowserDialog();
var starting = string.IsNullOrWhiteSpace(_model.ModelsFolderPath) ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) : _model.ModelsFolderPath;
dlg.SelectedPath = starting;
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
_model.ModelsFolderPath = dlg.SelectedPath + Path.DirectorySeparatorChar;
}
}
private void OpenModelsFolder_Click(object sender, RoutedEventArgs e)
{
try
{
var path = _model.ModelsFolderPath;
if (string.IsNullOrWhiteSpace(path)) return;
path = path.Trim().Trim('"');
if (File.Exists(path))
{
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{path}\"");
return;
}
if (Directory.Exists(path))
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = path, UseShellExecute = true });
return;
}
}
catch { }
}
private void Model_SelectCsvOutputRequested(object? sender, EventArgs e)
{
var dlg = new Microsoft.Win32.SaveFileDialog();
dlg.Filter = "CSV file (*.csv)|*.csv|All files (*.*)|*.*";
if (!string.IsNullOrWhiteSpace(_model.CsvOutputPath)) dlg.FileName = _model.CsvOutputPath;
var result = dlg.ShowDialog(this);
if (result == true)
{
_model.CsvOutputPath = dlg.FileName;
}
}
private void OpenCsvOutputFolder_Click(object sender, RoutedEventArgs e)
{
try
{
var path = _model.CsvOutputPath;
if (string.IsNullOrWhiteSpace(path)) return;
path = path.Trim().Trim('"');
if (File.Exists(path))
{
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{path}\"");
return;
}
var dir = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(dir) && Directory.Exists(dir))
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = dir, UseShellExecute = true });
return;
}
}
catch { }
}
private void Model_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e is null || string.IsNullOrWhiteSpace(e.PropertyName)) return;