-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathMainViewModel.AI.cs
More file actions
184 lines (152 loc) · 5.93 KB
/
Copy pathMainViewModel.AI.cs
File metadata and controls
184 lines (152 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
using Microsoft.Graphics.Imaging;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Windows.AI;
using Microsoft.Windows.AI.Imaging;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using XkcdViewer.Windows.Utils;
namespace XkcdViewer.Windows;
#pragma warning disable CA1416
public partial class MainViewModel
{
private void InitializeCopilotCapabilities()
{
// Light up Windows Foundry Gen AI capabilities if the system supports it
if (AppUtils.HasNpu())
{
CopilotCapVisibility = Visibility.Visible;
DescriptionLevels.AddRange(Enum.GetValues<ImageDescriptionKind>());
PreferredDescriptionLevel = DescriptionLevels.FirstOrDefault(n => n == ImageDescriptionKind.DetailedDescription);
}
}
public async Task AnalyzeCurrentComicAsync()
{
if (IsBusy || CurrentComic is null || string.IsNullOrEmpty(CurrentComic.Img))
return;
try
{
IsBusy = true;
IsBusyMessage = "Analyzing...";
// - STEP 1 -
// Download image and save to temp working file
IsBusyMessage = "Downloading image...";
await AppUtils.DownloadImageAsync(CurrentComic.Num, CurrentComic.Img);
// - STEP 2 -
// Load png file into a SoftwareBitmap
IsBusyMessage = "Analyzing image...";
var languageModelResponse = await GetImageDescriptionAsync(CurrentComic.Num);
if (languageModelResponse == null)
return;
// - STEP 3 -
// Audio playback using SpeechSynthesizer.
IsBusyMessage = "Playing back audio...";
await AppUtils.ReadAloudAsync(languageModelResponse.Description);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
IsBusyMessage = "";
IsBusy = false;
}
}
private static void DeleteCachedComicImage(int comicId)
{
var filePath = AppUtils.IsPackagedApp
? Path.Combine(ApplicationData.Current.TemporaryFolder.Path, $"{comicId}.png")
: Path.Combine(AppContext.BaseDirectory, $"{comicId}.png");
if (!File.Exists(filePath))
return;
File.Delete(filePath);
}
private async Task<ImageDescriptionResult?> GetImageDescriptionAsync(int comicId)
{
var filePath = AppUtils.IsPackagedApp
? Path.Combine(ApplicationData.Current.TemporaryFolder.Path, $"{comicId}.png")
: Path.Combine(AppContext.BaseDirectory, $"{comicId}.png");
if (!File.Exists(filePath))
{
await ShowAnalyzerMessageAsync("Local image file does not exist, cannot proceed.");
return null;
}
IRandomAccessStream stream;
if (AppUtils.IsPackagedApp)
{
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(filePath));
stream = await file.OpenAsync(FileAccessMode.Read);
}
else
{
stream = File.OpenRead(filePath).AsRandomAccessStream();
}
if (stream is null)
{
await ShowAnalyzerMessageAsync("There was a problem loading the image file.");
return null;
}
// - STEP 1 -
// Make sure Gen AI capabilities are onboard
IsBusyMessage = "Checking Windows AI capabilities...";
if (ImageDescriptionGenerator.GetReadyState() == AIFeatureReadyState.NotReady)
{
var wProg = ImageDescriptionGenerator.EnsureReadyAsync();
wProg.Progress = (result, progress) =>
{
DispatcherQueue.GetForCurrentThread().TryEnqueue(() => IsBusyMessage = $"Downloading model... {progress * 100}% complete.");
};
AIFeatureReadyResult? result = await wProg;
if (result.Status != AIFeatureReadyResultState.Failure)
{
await ShowAnalyzerMessageAsync($"There was a problem installing the required packages: {result.ExtendedError.Message}");
return null;
}
}
else if (ImageDescriptionGenerator.GetReadyState() == AIFeatureReadyState.NotSupportedOnCurrentSystem)
{
await ShowAnalyzerMessageAsync("This device does not support the required Gen AI capabilities.");
return null;
}
// - STEP 2 -
// Request an ImageDescriptionGenerator session from Windows AI Foundry
IsBusyMessage = "Requesting Windows AI Foundry session...";
var imageDescriptionGenerator = await ImageDescriptionGenerator.CreateAsync();
// - STEP 3 -
// Describe the image
IsBusyMessage = "Preparing image...";
var decoder = await BitmapDecoder.CreateAsync(stream);
var sBitmap = await decoder.GetSoftwareBitmapAsync();
IsBusyMessage = "Preparing AI...";
var describer = imageDescriptionGenerator.DescribeAsync(
ImageBuffer.CreateForSoftwareBitmap(sBitmap),
ImageDescriptionKind.DetailedDescription,
AppUtils.GetContentFilterOptions());
describer.Progress = (info, streamingProgress) =>
{
((App)Application.Current).MainDispatcherQueue.TryEnqueue(() => IsBusyMessage = $"Generating: {streamingProgress}");
};
IsBusyMessage = "Analyzing image...";
return await describer;
}
private async Task ShowAnalyzerMessageAsync(string message)
{
var dialog = new ContentDialog
{
Title = "Alert",
Content = message,
PrimaryButtonText = "OK",
DefaultButton = ContentDialogButton.Primary
};
await this.DialogService.ShowDialogAsync(dialog);
}
}