I detect faces in image files inside a folder using Emgu CV. I'm using a foreach loop for this. However, the form does not respond until the whole process is finished.
I use threads to avoid this. However, the thread is not working as I expected. The loop ends before the rendering of the images is finished.
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
Thread th = new Thread(() => ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath));
th.Start();
}
Even if I do it this way, it works like it was before using threads.
th.Start();
th.Join();
The simplest thing is to change from Thread to Task (and correctly dispose of your image) like this:
foreach (var item in files)
{
using (Image img = Image.FromFile(item))
{
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
await Task.Run(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath));
}
}
That MUST be done in a async Task method (or async void event handler) to allow the use of the await keyword.
This approach will run each imageprocessmulti one after the other without blocking the UI.
If you want to run all of the imageprocessmulti in parallel, then you're best off making a list of tasks and awaiting them all at once. Like this:
List<Task> tasks =
(
from item in files
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
select Task.Run(() =>
{
using (Image img = Image.FromFile(item))
{
ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath);
}
})
).ToList();
await Task.WhenAll(tasks);
My preferred approach is to use Microsoft's Reactive Framework - NuGet System.Reactive - and then do this:
IObservable<Unit> query =
from item in files.ToObservable(Scheduler.Default)
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
from unit in Observable.Using(
() => Image.FromFile(item),
img => Observable.Start(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath)))
select unit;
await query.ToArray();
All of these approaches should work for you.
Here's one way to approach it using async/await, for example, with a button click event:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => {
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath);
}
});
// ...more code here to run after all images have been processed...
MessageBox.Show("Done!");
}
Here's an alternate version to show progress, not sure what you tried:
private async void button1_Click_1(object sender, EventArgs e)
{
progressBar1.Visible = true;
progressBar1.Value = 0;
int counter = 0;
int total = files.Length;
await Task.Run(() => {
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath);
counter++;
int p = (int)((double)counter / total * 100);
progressBar1.Invoke((MethodInvoker) delegate {
progressBar1.Value = p;
});
}
});
// ...more code here to run after all images have been processed...
progressBar1.Visible = false;
MessageBox.Show("Done!");
}
Related
In a Blazor app I have a file uploader that can take multiple files. When the user clicked 'upload' I used something like this:
private async Task HandleFileUploadClicked()
{
_isUploading = true;
StateHasChanged();
foreach (var file in _files)
{
await using var stream = file.OpenReadStream(file.Size);
await _myService.AddFile(
stream
);
}
_isUploading = false;
_files = new List<IBrowserFile>();
await LoadData();
StateHasChaged();
}
But I discovered this doesn't work correct. Because I am not using the result of _myService.Addfile the call is just fired and the code continues at _isUploading = false;
Then I thought maybe I should keep an array of the AddFile Tasks and use Task.WaitAll to wait for all of them. But the problem it that this waits on the UI thread, so _isUploading = true; and StateHasChanged have no effect.
My current (working) solution is something like this:
private async Task HandleFileUploadClicked()
{
_isUploading = true;
StateHasChanged();
var allOk = true;
foreach (var file in _files)
{
await using var stream = file.OpenReadStream(file.Size);
var result = await _myService.AddFile(
stream
);
allOk = result != null && allOk;
}
_isUploading = false;
_files = new List<IBrowserFile>();
await LoadData();
StateHasChaged();
}
But this feels like a hack.
Is there a better way to wait for multiple tasks but without blocking the UI thread?
You can make use of Task Parller Library method Task.WhenAll(). Which will make sure that the tasks are processed in parallel.
code snippet should be as follows:
bool allOk = true;
try
{
var uploadtasks = new List<Task>();
foreach(var file in filesToUpload)
{
uploadtasks.Add(_myService.AddFile(file));
}
await Task.WhenAll(uploadtasks);
}
catch(Exception ex)
{
allOk = false;
}
I'm trying to build an app that pulls back all the directories and files and builds a Treeview. I have some code below that does this, however it seams to cause my UI to freeze as it is building.
I'm unsure if this a threading issue? I think that the code that builds the tree view is on a separate thread to the UI although this is one bit i'm unsure of.
I've also read that i may need to look into UI Virtualisation / data Virtualisation. If anyone could point me into the correct direction that would be very helpful.
private void btnSearch_Click(object sender, System.EventArgs e)
{
btnSearch.Text = "Searching...";
this.Cursor = Cursors.WaitCursor;
Application.DoEvents();
Thread treeThread = new Thread(() => ListDirectory(lstTreeView, "C:\\"));
treeThread.Start();
btnSearch.Text = "Search";
this.Cursor = Cursors.Default;
}
private void ListDirectory(TreeView treeView, string path)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
this.Invoke((MethodInvoker)(() => treeView.Nodes.Clear()));
var rootDirectoryInfo = new DirectoryInfo(path);
this.Invoke((MethodInvoker)(() => treeView.Nodes.Add(CreatedirNode(rootDirectoryInfo))));
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
this.Invoke((MethodInvoker)(() => lblCount.Items.Add("Time taken in Sec " + (elapsedMs)/1000)));
}
I think this maybe the reason as I think the UI is freezing as directoryNode.Nodes.Add(new TreeNode(file.Name)); looks to be writing on the UI thread?
private static TreeNode CreatedirNode(DirectoryInfo DirI) {
var directoryNode = new TreeNode(DirI.Name);
try
{
foreach (var directory in DirI.GetDirectories())
directoryNode.Nodes.Add(CreatedirNode(directory));
foreach (var file in DirI.GetFiles())
directoryNode.Nodes.Add(new TreeNode(file.Name));
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
return directoryNode;
}
First of all, sorry because I am so new at C# and I decided to make this question because I have been choked in this for hours.
I have an GUI that works with Google Cloud Speech services and make a Speech-to-Text operation. I share with you the whole method that runs when a button is clicked:
private async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
GoogleCredential googleCredential;
using (Stream m = new FileStream(#"..\..\credentials.json", FileMode.Open))
googleCredential = GoogleCredential.FromStream(m);
var channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host,
googleCredential.ToChannelCredentials());
var speech = SpeechClient.Create(channel);
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 48000,
LanguageCode = "es-ES",
},
InterimResults = true,
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(48000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Console.WriteLine(alternative.Transcript);
//Textbox1.Text = alternative.Transcript;
}
}
}
});
waveIn.StartRecording();
Console.WriteLine("Speak now.");
Result_Tone.Text = "Speak now:\n\n";
await Task.Delay(TimeSpan.FromSeconds(seconds));
// Stop recording and shut down.
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
My problem is that I want to update the content of the Textbox1control but it doesn´t work. It writes perfectly the output into the console with the line Console.WriteLine(alternative.Transcript); but not into my textbox.
If someone could help I would appreciate so much his help.
The problem is that you're using Task.Run, which means your code will be running on a thread-pool thread.
Instead of calling Task.Run(), just move that code into a separate async method:
async Task DisplayResponses(IAsyncEnumerator<StreamingRecognizeResponse> responses)
{
while (await responses.MoveNext(default(CancellationToken)))
{
foreach (var result in responses.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}
}
Then call that method directly (without Task.Run) from code that's already on the UI thread (e.g. an event handler).
The async machinery will make sure that after the await expression, you're back on the UI thread (the same synchronization context). So the assignment to the Text property will occur on the UI thread, and all should be well.
For example:
// This would be registered as the event handler for a button
void HandleButtonClick(object sender, EventArgs e)
{
var stream = client.StreamingRecognize();
// Send the initial config request
await stream.WriteAsync(...);
// Presumably you want to send audio data...
StartSendingAudioData(stream);
await DisplayResponses(stream.ResponseStream);
}
Tasks run on seperate threads, so you must Invoke an action that will be performed on the control's thread
Textbox1.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
Edit: For WPF, I believe the equivalent is
Textbox1.Dispatcher.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
have you tried using Dispatcher.InvokeASync()?
await Dispatcher.InvokeAsync(() => {while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}});
I am trying to read the contents of a directory that has over 100k+ files. Each file will be parsed eventually. Since this is a long time consuming task I want to provide feedback to the end user that the program has not "frozen", This is an MVP pattern project setup.
I am using async Task and IProgress to provide feedback to the UI, but either methods below lock up the UI and I can't figure out why..
private async void MainFormViewOnStartProcessingFiles(object sender, EventArgs eventArgs)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var asyncProgress = new Progress<int>(percent =>
{
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarMessage($"Async found {percent} files..."));
});
// GetDirectoryFiles is run in the thread pool
await Task.Run(() => GetDirectoryFilesAsync(asyncProgress));
var syncProgress = new Progress<int>(percent =>
{
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarMessage($"Sync found {percent} files..."));
});
GetDirectoryFiles(syncProgress);
}
This updates the UI
private void DisplayWaitingBarMessage(DisplayMainFormWaitBarMessage obj)
{
_mainFormView.DisplayWaitBarMessage(obj.DisplayText);
}
This is my Async and Non Async code to read the files into a queue
private async Task GetDirectoryFilesAsync(IProgress<int> taskProgress)
{
await Task.Run(() =>
{
try
{
var directoryPath = Directory.GetCurrentDirectory() + #"\..\..\..\FUG_2017\";
var _listOfDirectoryFiles = Directory.GetFiles(directoryPath).Take(100);
var fileCount = 0;
foreach (var filePath in _listOfDirectoryFiles)
{
_filesToProcessQueue.Enqueue(filePath);
fileCount++;
taskProgress?.Report(fileCount);
}
}
catch (Exception exc)
{
FlushAndExit(exc);
}
});
}
private void GetDirectoryFiles(IProgress<int> taskProgress)
{
try
{
var directoryPath = Directory.GetCurrentDirectory() + #"\..\..\..\FUG_2017\";
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarWithMessage(ElementVisibility.Visible, $"Inspecting path {directoryPath}"));
var _listOfDirectoryFiles = Directory.GetFiles(directoryPath).Take(1000);
var fileCount = 0;
foreach (var filePath in _listOfDirectoryFiles)
{
_filesToProcessQueue.Enqueue(filePath);
fileCount++;
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarWithMessage(ElementVisibility.Visible, $"Loaded file {fileCount} of {_listOfDirectoryFiles.Count()} to Queue..."));
taskProgress?.Report(fileCount);
}
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarWithMessage(ElementVisibility.Visible, $"Loaded {_listOfDirectoryFiles.Count()} files to Queue..."));
}
catch (Exception exc)
{
FlushAndExit(exc);
}
}
I have a code which should take all pictures from folder, put them into object named "PhotoInspection", add some informations and put this object into list. See the code below
private async void btnSend_Click(object sender, RoutedEventArgs e)
{
bool isOn = tsOnOff.IsOn;
TextBlock tbChosen = new TextBlock();
tbChosen = lbInspections.SelectedItem as TextBlock;
string chosen = tbChosen.Text;
AllInspectionPhotos aip = new AllInspectionPhotos();
var folders = await ApplicationData.Current.LocalFolder.GetFoldersAsync();
if (isOn)
{
foreach (var item in folders)
{
if (item.Name == "ONLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photos = await inspectionFolder.GetFilesAsync();
foreach (var photo in photos)
{
using (Stream stream = await photo.OpenStreamForReadAsync())
{
PhotoInspection phtInsp = new PhotoInspection();
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
aip.Photos.Add(phtInsp);
}
}
}
}
}
}
}
else
{
foreach (var item in folders)
{
if (item.Name == "OFFLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photoset = await inspectionFolder.GetFilesAsync();
foreach (var photo in photoset)
{
PhotoInspection phtInsp = new PhotoInspection();
using (Stream stream = await photo.OpenStreamForReadAsync())
{
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
}
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//await App._client.SendPhotoAsync(phtInsp);
aip.Photos.Add(phtInsp); //fires error here
});
}
}
}
}
}
}
await App._client.SendAllPhotosAsync(aip);
}
However when I try to add object into list, I get "attempted to read write protected memory" error. PhotoInspection object is filled with relevant data and everything looks good before adding to list. Thanks for any help
The problem is due to the Dispatcher.RunAsync call, which returns control to the calling routine as soon as the aip.Photos.Add() lambda is queued, and not when it has completed. This results in the aip object being disposed before the lambda has actually run.
The MSDN page for CoreDispatcher.RunAsync says:
[...] it schedules the work on the UI thread and returns control to the caller immediately.
and:
To spin off a worker thread from the UI thread, do not use this method (CoreDispatcher::RunAsync). Instead, use one of the Windows::System::Threading::ThreadPool::RunAsync method overloads.
It also provides the following code example, and the comment is from Microsoft!
//DO NOT USE THIS CODE.
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await signInDialog.ShowAsync();
});
// Execution continues here before the call to ShowAsync completes.