feat: Update package references and enhance AI extraction service with CSV output functionality
This commit is contained in:
parent
55e8f0face
commit
af74c90ce7
12 changed files with 400 additions and 153 deletions
|
|
@ -17,6 +17,7 @@ public partial class AvaloniaMainWindow : Window
|
|||
private readonly DataModel _model;
|
||||
private readonly PickerPreferenceService _pickerPreferenceService;
|
||||
private bool _isDarkTheme;
|
||||
private bool _startupSettingsRestoreAttempted;
|
||||
|
||||
public AvaloniaMainWindow(DataModel model)
|
||||
{
|
||||
|
|
@ -26,7 +27,11 @@ public partial class AvaloniaMainWindow : Window
|
|||
_pickerPreferenceService = Program.ServiceProvider.GetRequiredService<PickerPreferenceService>();
|
||||
DataContext = _model;
|
||||
|
||||
Opened += (_, _) => SyncThemeStateFromCurrentTheme();
|
||||
Opened += async (_, _) =>
|
||||
{
|
||||
SyncThemeStateFromCurrentTheme();
|
||||
await TryLoadLastSettingsOnStartupAsync();
|
||||
};
|
||||
Closing += AvaloniaMainWindow_Closing;
|
||||
|
||||
// Let DataModel marshal callbacks onto Avalonia UI thread.
|
||||
|
|
@ -120,30 +125,38 @@ public partial class AvaloniaMainWindow : Window
|
|||
|
||||
_model.SaveSettingsRequested += async (_, _) =>
|
||||
{
|
||||
var suggestedStartLocation = await _pickerPreferenceService.TryGetStartFolderAsync(StorageProvider, PickerPreferenceKeys.SettingsFile);
|
||||
var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
|
||||
{
|
||||
Title = "Salva impostazioni",
|
||||
DefaultExtension = "xml",
|
||||
FileTypeChoices = [new FilePickerFileType("Setup") { Patterns = ["*.xml"] }]
|
||||
FileTypeChoices = [new FilePickerFileType("Setup") { Patterns = ["*.xml"] }],
|
||||
SuggestedStartLocation = suggestedStartLocation
|
||||
});
|
||||
|
||||
if (file is not null)
|
||||
{
|
||||
await _model.SaveSettingsToFileAsync(file.Path.LocalPath);
|
||||
_pickerPreferenceService.RememberPath(PickerPreferenceKeys.SettingsFile, file.Path.LocalPath);
|
||||
_pickerPreferenceService.RememberValue(PickerPreferenceKeys.LastSettingsFile, file.Path.LocalPath);
|
||||
}
|
||||
};
|
||||
|
||||
_model.LoadSettingsRequested += async (_, _) =>
|
||||
{
|
||||
var suggestedStartLocation = await _pickerPreferenceService.TryGetStartFolderAsync(StorageProvider, PickerPreferenceKeys.SettingsFile);
|
||||
var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
Title = "Carica impostazioni",
|
||||
FileTypeFilter = [new FilePickerFileType("Setup") { Patterns = ["*.xml"] }]
|
||||
FileTypeFilter = [new FilePickerFileType("Setup") { Patterns = ["*.xml"] }],
|
||||
SuggestedStartLocation = suggestedStartLocation
|
||||
});
|
||||
|
||||
if (files.Count > 0)
|
||||
{
|
||||
await _model.LoadSettingsFromFileAsync(files[0].Path.LocalPath);
|
||||
_pickerPreferenceService.RememberPath(PickerPreferenceKeys.SettingsFile, files[0].Path.LocalPath);
|
||||
_pickerPreferenceService.RememberValue(PickerPreferenceKeys.LastSettingsFile, files[0].Path.LocalPath);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -165,6 +178,38 @@ public partial class AvaloniaMainWindow : Window
|
|||
|
||||
private bool _isStoppingFaceEncoderForClose;
|
||||
|
||||
private async Task TryLoadLastSettingsOnStartupAsync()
|
||||
{
|
||||
if (_startupSettingsRestoreAttempted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_startupSettingsRestoreAttempted = true;
|
||||
|
||||
var lastSettingsFile = _pickerPreferenceService.GetRememberedValue(PickerPreferenceKeys.LastSettingsFile);
|
||||
if (string.IsNullOrWhiteSpace(lastSettingsFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(lastSettingsFile))
|
||||
{
|
||||
_pickerPreferenceService.ForgetValue(PickerPreferenceKeys.LastSettingsFile);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _model.LoadSettingsFromFileAsync(lastSettingsFile);
|
||||
_pickerPreferenceService.RememberPath(PickerPreferenceKeys.SettingsFile, lastSettingsFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ShowMessageDialogAsync("Impostazioni", $"Impossibile caricare il file impostazioni automatico:\n{ex.GetBaseException().Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async void AvaloniaMainWindow_Closing(object? sender, CancelEventArgs e)
|
||||
{
|
||||
if (_isStoppingFaceEncoderForClose || (!_model.IsFaceEncoderRunning && !_model.IsFaceMatcherRunning))
|
||||
|
|
|
|||
|
|
@ -3,112 +3,128 @@
|
|||
xmlns:avaloniaDataGrid="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls.DataGrid"
|
||||
xmlns:iconPacks="https://github.com/MahApps/IconPacks.Avalonia"
|
||||
x:Class="ImageCatalog_2.AvaloniaViews.AiTabView">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TabControl Margin="4">
|
||||
<TabItem Header="Esecuzione">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="4" Spacing="8">
|
||||
<TextBlock Text="AI / OCR" FontWeight="Bold" />
|
||||
|
||||
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="4">
|
||||
<TextBlock Text="AI / OCR" FontWeight="Bold" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="12" Margin="0,6,0,0">
|
||||
<CheckBox Content="Usa GPU"
|
||||
IsChecked="{Binding UseNumberAiGpu, Mode=TwoWay}"
|
||||
IsEnabled="{Binding NumberAiGpuOptionEnabled}" />
|
||||
<CheckBox Content="Includi thumbnail" IsChecked="{Binding IncludeNumberAiThumbnails, Mode=TwoWay}" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<CheckBox Content="Usa GPU"
|
||||
IsChecked="{Binding UseNumberAiGpu, Mode=TwoWay}"
|
||||
IsEnabled="{Binding NumberAiGpuOptionEnabled}" />
|
||||
<CheckBox Content="Includi thumbnail" IsChecked="{Binding IncludeNumberAiThumbnails, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="Auto,Auto,*" ColumnSpacing="8">
|
||||
<TextBlock Grid.Column="0" Text="Carico OCR:" VerticalAlignment="Center" />
|
||||
<ComboBox Grid.Column="1"
|
||||
Width="84"
|
||||
ItemsSource="{Binding NumberAiWorkloadOptions}"
|
||||
SelectedItem="{Binding NumberAiWorkloadLevel, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding NumberAiStatsSummary}"
|
||||
VerticalAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
FontWeight="SemiBold" />
|
||||
</Grid>
|
||||
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="6">
|
||||
<TextBlock Grid.Column="0" Text="Sorgente:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="1"
|
||||
Text="{Binding DestinationPath, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
VerticalAlignment="Center" />
|
||||
<Button Grid.Column="2" Width="104" Command="{Binding SelectDestinationFolderCommand}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="FolderOpenOutline" Width="14" Height="14" />
|
||||
<TextBlock Text="Scegli..." />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Grid.Column="3" Width="72" Click="OpenAiSourceFolder_Click">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Folder" Width="14" Height="14" />
|
||||
<TextBlock Text="Apri" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Spacing="8">
|
||||
<Button Command="{Binding StartAiCommand}" Width="132">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="PlayCircle" Width="16" Height="16" Foreground="#2E7D32" />
|
||||
<TextBlock Text="Avvia AI" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding AsyncCancelOperationCommand}" Width="132">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Cancel" Width="16" Height="16" Foreground="#C62828" />
|
||||
<TextBlock Text="Annulla" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="Output CSV" FontWeight="Bold" />
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="6">
|
||||
<TextBlock Text="Percorso CSV:" VerticalAlignment="Center" Margin="0,0,8,0" Grid.Column="0" />
|
||||
<TextBox Text="{Binding CsvOutputPath, Mode=TwoWay}" Grid.Column="1" VerticalAlignment="Center" />
|
||||
<Button Width="104" Command="{Binding SelectCsvOutputCommand}" Grid.Column="2">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="FileOutline" Width="14" Height="14" />
|
||||
<TextBlock Text="Scegli..." />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Width="72" Grid.Column="3" Click="OpenCsvOutputFolder_Click">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Folder" Width="14" Height="14" />
|
||||
<TextBlock Text="Apri" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="Anteprima risultati" FontWeight="Bold" />
|
||||
<ProgressBar Minimum="0" Maximum="100" Value="{Binding AiProgress}" Height="16" Margin="0,0,0,4" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<avaloniaDataGrid:DataGrid Grid.Row="1"
|
||||
ItemsSource="{Binding PreviewResults}"
|
||||
IsReadOnly="True"
|
||||
AutoGenerateColumns="False"
|
||||
Margin="4,4,4,4"
|
||||
CanUserResizeColumns="True"
|
||||
VerticalAlignment="Stretch">
|
||||
<avaloniaDataGrid:DataGrid.Columns>
|
||||
<avaloniaDataGrid:DataGridTextColumn Header="Path" Binding="{Binding Path}" Width="*" />
|
||||
<avaloniaDataGrid:DataGridTextColumn Header="Text" Binding="{Binding Text}" Width="2*" />
|
||||
</avaloniaDataGrid:DataGrid.Columns>
|
||||
</avaloniaDataGrid:DataGrid>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Impostazioni">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="8" Spacing="8">
|
||||
<TextBlock Text="Modelli" FontWeight="Bold" />
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="6">
|
||||
<TextBlock Text="Cartella modelli:" VerticalAlignment="Center" Margin="0,0,8,0" Grid.Column="0" />
|
||||
<TextBox Text="{Binding ModelsFolderPath, Mode=TwoWay}" Grid.Column="1" VerticalAlignment="Center" />
|
||||
<Button Width="104" Command="{Binding SelectModelsFolderCommand}" Grid.Column="2">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="FolderOpenOutline" Width="14" Height="14" />
|
||||
<TextBlock Text="Scegli..." />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Width="72" Grid.Column="3" Click="OpenModelsFolder_Click">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Folder" Width="14" Height="14" />
|
||||
<TextBlock Text="Apri" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Margin="0,8,0,0" ColumnDefinitions="Auto,Auto,*" ColumnSpacing="8">
|
||||
<TextBlock Grid.Column="0" Text="Carico OCR:" VerticalAlignment="Center" />
|
||||
<ComboBox Grid.Column="1"
|
||||
Width="84"
|
||||
ItemsSource="{Binding NumberAiWorkloadOptions}"
|
||||
SelectedItem="{Binding NumberAiWorkloadLevel, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding NumberAiStatsSummary}"
|
||||
VerticalAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
FontWeight="SemiBold" />
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="0,8,0,0" ColumnDefinitions="Auto,*,Auto" ColumnSpacing="6">
|
||||
<TextBlock Grid.Column="0" Text="Sorgente:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="1" Text="{Binding DestinationPath, Mode=OneWay}" IsReadOnly="True" VerticalAlignment="Center" />
|
||||
<Button Grid.Column="2" Width="72" Click="OpenAiDestinationFolder_Click">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Folder" Width="14" Height="14" />
|
||||
<TextBlock Text="Apri" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="Modelli" FontWeight="Bold" Margin="0,8,0,0" />
|
||||
<Grid Margin="0,4,0,0" ColumnDefinitions="Auto,*,Auto,Auto">
|
||||
<TextBlock Text="Cartella modelli:" VerticalAlignment="Center" Margin="0,0,8,0" Grid.Column="0" />
|
||||
<TextBox Text="{Binding ModelsFolderPath, Mode=TwoWay}" Grid.Column="1" VerticalAlignment="Center" />
|
||||
<Button Width="104" Margin="6,0,0,0" Command="{Binding SelectModelsFolderCommand}"
|
||||
Grid.Column="2">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="FolderOpenOutline" Width="14" Height="14" />
|
||||
<TextBlock Text="Scegli..." />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Width="72" Margin="6,0,0,0" Grid.Column="3"
|
||||
Click="OpenModelsFolder_Click">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Folder" Width="14" Height="14" />
|
||||
<TextBlock Text="Apri" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0" Spacing="8">
|
||||
<Button Command="{Binding StartAiCommand}" Width="132">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="PlayCircle" Width="16" Height="16" Foreground="#2E7D32" />
|
||||
<TextBlock Text="Avvia AI" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding AsyncCancelOperationCommand}" Width="132">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Cancel" Width="16" Height="16" Foreground="#C62828" />
|
||||
<TextBlock Text="Annulla" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="Output CSV" FontWeight="Bold" Margin="0,8,0,0" />
|
||||
<Grid Margin="0,4,0,0" ColumnDefinitions="Auto,*,Auto,Auto">
|
||||
<TextBlock Text="Percorso CSV:" VerticalAlignment="Center" Margin="0,0,8,0" Grid.Column="0" />
|
||||
<TextBox Text="{Binding CsvOutputPath, Mode=TwoWay}" Grid.Column="1" VerticalAlignment="Center" />
|
||||
<Button Width="104" Margin="6,0,0,0" Command="{Binding SelectCsvOutputCommand}"
|
||||
Grid.Column="2">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="FileOutline" Width="14" Height="14" />
|
||||
<TextBlock Text="Scegli..." />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Width="72" Margin="6,0,0,0" Grid.Column="3"
|
||||
Click="OpenCsvOutputFolder_Click">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
|
||||
<iconPacks:PackIconMaterial Kind="Folder" Width="14" Height="14" />
|
||||
<TextBlock Text="Apri" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="Anteprima risultati" FontWeight="Bold" Margin="0,8,0,0" />
|
||||
<ProgressBar Minimum="0" Maximum="100" Value="{Binding AiProgress}" Height="16" Margin="0,4,0,4" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<avaloniaDataGrid:DataGrid Grid.Row="1" ItemsSource="{Binding PreviewResults}" IsReadOnly="True"
|
||||
AutoGenerateColumns="False" Margin="4,4,4,4" CanUserResizeColumns="True" VerticalAlignment="Stretch">
|
||||
<avaloniaDataGrid:DataGrid.Columns>
|
||||
<avaloniaDataGrid:DataGridTextColumn Header="Path" Binding="{Binding Path}" Width="*" />
|
||||
<avaloniaDataGrid:DataGridTextColumn Header="Text" Binding="{Binding Text}" Width="2*" />
|
||||
</avaloniaDataGrid:DataGrid.Columns>
|
||||
</avaloniaDataGrid:DataGrid>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public partial class AiTabView : Avalonia.Controls.UserControl
|
|||
OpenInExplorer(string.IsNullOrWhiteSpace(directory) ? model.CsvOutputPath : directory);
|
||||
}
|
||||
|
||||
private void OpenAiDestinationFolder_Click(object? sender, RoutedEventArgs e)
|
||||
private void OpenAiSourceFolder_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is DataModel model)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ namespace ImageCatalog_2
|
|||
private Task? _faceMatcherLogWatcherTask;
|
||||
private bool _hasStartedFaceEncoderInSession;
|
||||
private bool _hasStartedFaceMatcherInSession;
|
||||
private int _numberAiGpuRefreshVersion;
|
||||
private volatile bool _numberAiGpuValidationPending;
|
||||
|
||||
private sealed record ParsedFaceMatcherRow(string PhotoId, double? Score, string RawRow, string DebugSummary);
|
||||
|
||||
|
|
@ -137,7 +139,7 @@ namespace ImageCatalog_2
|
|||
|
||||
// Load available fonts
|
||||
AvailableFonts = LoadAvailableFonts();
|
||||
RefreshNumberAiGpuCapabilities();
|
||||
QueueRefreshNumberAiGpuCapabilities();
|
||||
RefreshFaceExecutableCapabilities();
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +159,7 @@ namespace ImageCatalog_2
|
|||
_logger.LogError(ex, "AI extraction failed");
|
||||
if (UseNumberAiGpu)
|
||||
{
|
||||
RefreshNumberAiGpuCapabilities();
|
||||
QueueRefreshNumberAiGpuCapabilities();
|
||||
}
|
||||
|
||||
await InvokeOnUiThreadAsync(() => NumberAiStatsSummary = $"Errore OCR: {ex.GetBaseException().Message}").ConfigureAwait(false);
|
||||
|
|
@ -255,7 +257,7 @@ namespace ImageCatalog_2
|
|||
set
|
||||
{
|
||||
_ai.ModelsFolderPath = value;
|
||||
RefreshNumberAiGpuCapabilities();
|
||||
QueueRefreshNumberAiGpuCapabilities();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2281,31 +2283,86 @@ namespace ImageCatalog_2
|
|||
}
|
||||
}
|
||||
|
||||
private void RefreshNumberAiGpuCapabilities()
|
||||
private void QueueRefreshNumberAiGpuCapabilities()
|
||||
{
|
||||
if (!TryBuildNumberAiModelConfiguration(out var configuration))
|
||||
{
|
||||
_numberAiGpuValidationPending = false;
|
||||
NumberAiGpuOptionEnabled = false;
|
||||
_ai.UseNumberAiGpu = false;
|
||||
return;
|
||||
}
|
||||
|
||||
NumberAiGpuOptionEnabled = NumberRecognitionEngine.TryValidateGpuRuntime(configuration, _logger, out _);
|
||||
if (!NumberAiGpuOptionEnabled)
|
||||
_numberAiGpuValidationPending = true;
|
||||
var requestVersion = Interlocked.Increment(ref _numberAiGpuRefreshVersion);
|
||||
_ = RefreshNumberAiGpuCapabilitiesAsync(configuration, requestVersion);
|
||||
}
|
||||
|
||||
private async Task RefreshNumberAiGpuCapabilitiesAsync(ModelConfiguration configuration, int requestVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ai.UseNumberAiGpu = false;
|
||||
var gpuAvailable = await Task.Run(() =>
|
||||
NumberRecognitionEngine.TryValidateGpuRuntime(configuration, _logger, out _)).ConfigureAwait(false);
|
||||
|
||||
if (requestVersion != Volatile.Read(ref _numberAiGpuRefreshVersion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await InvokeOnUiThreadAsync(() =>
|
||||
{
|
||||
if (requestVersion != Volatile.Read(ref _numberAiGpuRefreshVersion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_numberAiGpuValidationPending = false;
|
||||
NumberAiGpuOptionEnabled = gpuAvailable;
|
||||
if (!gpuAvailable)
|
||||
{
|
||||
_ai.UseNumberAiGpu = false;
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (requestVersion != Volatile.Read(ref _numberAiGpuRefreshVersion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogWarning(ex, "Failed to refresh OCR GPU capabilities.");
|
||||
|
||||
await InvokeOnUiThreadAsync(() =>
|
||||
{
|
||||
if (requestVersion != Volatile.Read(ref _numberAiGpuRefreshVersion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_numberAiGpuValidationPending = false;
|
||||
NumberAiGpuOptionEnabled = false;
|
||||
_ai.UseNumberAiGpu = false;
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetUseNumberAiGpu(bool value)
|
||||
{
|
||||
if (!NumberAiGpuOptionEnabled)
|
||||
if (!value)
|
||||
{
|
||||
_ai.UseNumberAiGpu = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_ai.UseNumberAiGpu = value;
|
||||
if (NumberAiGpuOptionEnabled || _numberAiGpuValidationPending)
|
||||
{
|
||||
_ai.UseNumberAiGpu = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_ai.UseNumberAiGpu = false;
|
||||
}
|
||||
|
||||
private bool TryBuildNumberAiModelConfiguration(out ModelConfiguration configuration)
|
||||
|
|
|
|||
|
|
@ -65,16 +65,16 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AIFotoONLUS.Core" Version="0.1.2" Condition="'$(UseLocalAIFotoONLUS)' != 'true'" />
|
||||
<PackageReference Include="AutoMapper" Version="16.1.0" />
|
||||
<PackageReference Include="IconPacks.Avalonia" Version="1.3.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.5" />
|
||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
||||
<PackageReference Include="IconPacks.Avalonia" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.8" />
|
||||
<PackageReference Include="MinVer" Version="7.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia" Version="12.0.3" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="12.0.3" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.3" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="12.0.0" />
|
||||
<PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.421302">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
|
|
|||
|
|
@ -198,20 +198,7 @@ public class AiExtractionService : IAiExtractionService
|
|||
{
|
||||
try
|
||||
{
|
||||
var dir = Path.GetDirectoryName(request.CsvOutputPath) ?? string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
using var sw = new StreamWriter(request.CsvOutputPath, false, Encoding.UTF8);
|
||||
sw.WriteLine("Path,Text");
|
||||
foreach (var r in extractedResults)
|
||||
{
|
||||
var csvFileName = Path.GetFileName(r.Path ?? string.Empty);
|
||||
var safeText = (r.Text ?? string.Empty).Replace("\"", "\"\"");
|
||||
sw.WriteLine($"\"{csvFileName}\",\"{safeText}\"");
|
||||
}
|
||||
WriteCsvOutput(request.CsvOutputPath, extractedResults);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -222,6 +209,24 @@ public class AiExtractionService : IAiExtractionService
|
|||
return summary;
|
||||
}
|
||||
|
||||
internal static void WriteCsvOutput(string csvOutputPath, IEnumerable<AiResultItem> extractedResults)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(csvOutputPath) ?? string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
using var sw = new StreamWriter(csvOutputPath, false, Encoding.UTF8);
|
||||
sw.WriteLine("filename,text");
|
||||
foreach (var result in extractedResults)
|
||||
{
|
||||
var csvFileName = Path.GetFileName(result.Path ?? string.Empty);
|
||||
var safeText = (result.Text ?? string.Empty).Replace("\"", "\"\"");
|
||||
sw.WriteLine($"\"{csvFileName}\",\"{safeText}\"");
|
||||
}
|
||||
}
|
||||
|
||||
private static double CalculateAverageImagesPerSecond(int processed, TimeSpan elapsed)
|
||||
{
|
||||
return elapsed.TotalSeconds > 0 ? processed / elapsed.TotalSeconds : 0;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ public static class PickerPreferenceKeys
|
|||
public const string LogoFile = "Picker.LogoFile.LastPath";
|
||||
public const string ModelsFolder = "Picker.ModelsFolder.LastPath";
|
||||
public const string CsvOutput = "Picker.CsvOutput.LastPath";
|
||||
public const string SettingsFile = "Picker.SettingsFile.LastPath";
|
||||
public const string LastSettingsFile = "Settings.LastFilePath";
|
||||
public const string FaceExecutableFolder = "Picker.FaceExecutableFolder.LastPath";
|
||||
public const string FaceOutputFolder = "Picker.FaceOutputFolder.LastPath";
|
||||
public const string FaceMatcherExecutable = "Picker.FaceMatcherExecutable.LastPath";
|
||||
|
|
@ -61,6 +63,33 @@ public sealed class PickerPreferenceService
|
|||
_userPreferences.SalvaParametriSetup();
|
||||
}
|
||||
|
||||
public string? GetRememberedValue(string preferenceKey)
|
||||
{
|
||||
var value = _userPreferences.LeggiParametroString(preferenceKey);
|
||||
return string.IsNullOrWhiteSpace(value)
|
||||
? null
|
||||
: value.Trim().Trim('"');
|
||||
}
|
||||
|
||||
public void RememberValue(string preferenceKey, string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_userPreferences.AggiornaParametro(preferenceKey, value.Trim().Trim('"'));
|
||||
_userPreferences.SalvaParametriSetup();
|
||||
}
|
||||
|
||||
public void ForgetValue(string preferenceKey)
|
||||
{
|
||||
if (_userPreferences.RimuoviParametro(preferenceKey))
|
||||
{
|
||||
_userPreferences.SalvaParametriSetup();
|
||||
}
|
||||
}
|
||||
|
||||
private string? GetPreferredStartDirectory(string preferenceKey, string? currentPath)
|
||||
{
|
||||
var storedPath = _userPreferences.LeggiParametroString(preferenceKey);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue