how to preload images in a background thread? - c#

In my WPF app i need to load some images. I only need to display one image at a time. If i load the image when it's needed, there is a slightly delay. So i thought to myself: "Hey, why not do some preloading in a background thread? Can't be that hard." I have some experience with threads, but not enough to know that this thought was wrong. I started programming and run into some problems. I fixed some of the problems and i probably could fix the other problems too, but that would result in spaghetti code. So, I think starting from the scratch would be the best. What initial planing is needed to build a nice and little preloading thread? Is there a pattern or something like that?
Here's my current setup:
LinkedList<string> to stores pathes to the pictures and navigate to the next picture
Dictionary<string, BitmapImage> to store the preloaded images

I'd use something like this:
class ImageManager
{
private Dictionary<string, Image> images=
new Dictionary<string,Image>();
public Image get(string s) { // blocking call, returns the image
return load(s);
}
private Image load(string s) { // internal, thread-safe helper
lock(images) {
if(!images.ContainsKey(s)) {
Image img=// load the image s
images.Add(s,img);
return img;
}
return images[s];
}
}
public void preload(params string[] imgs) { // non-blocking preloading call
foreach(string img in imgs) {
BackgroundWorker bw=new BackgroundWorker();
bw.DoWork+=(s,e)=>{ load(img); } // discard the actual image return
bw.RunWorkerAsync();
}
}
}
// in your main function
{
ImageManager im=new ImageManager();
im.preload("path1", "path2", "path3", "path4"); // non-blocking call
// then you just request images based on their path
// they'll become available as they are loaded
// or if you request an image before it's queued to be loaded asynchronously
// it will get loaded synchronously instead, thus with priority because it's needed
}

That certainly sounds like a good use for a background thread. Also, as your unit of work is quite large, there shouldn't be too much contention for the synchronization on your collections. You might find examples of similar algorithms, but I think you'll have to roll your own implementation - it's not that complicated.
One thing springs to mind, though: you will either have to keep a record of which images are currently in the process of being loaded, or tolerate multiple loads of the same image.
For example, if your UI requires an image that has not yet been loaded, you will probably want to load that image as a priority. If you know that the background thread is in the process of loading that image, you could just wait for it to become available. If instead you decide to just do the load on the UI thread, there is a possibility that the background thread will try to add a loaded image that is already present.
So, there will have to be some synchronization, but it shouldn't be too complicated.
Nick

Marcel,
WPF provides us already with great mechanisms of BackgroundWorker and Dispatcher to make you forget about writing your own thread mechanisms. However your problem doesn't seem to be that obvious for me. How many images do you need/where do you get these from? Please give us more info.

I was looking in to this yesterday and couldn't find much on the subject. There's actually quite a simple solution to the problem. Use WebClient to load the images asynchronously into a stream and then add that stream to a BitmapImage. Bellow is an example of how I implemented the preloading of a list of images. the example uses the Reactive Extensions Library(Rx) but it can easily be implemented in a non Rx way (Rx makes the code a lot more succinct and hides a lot of state).
public IEnumerable<BitmapImage> BitmapImages { get; private set }
private void PreloadImages(IEnumerbale<Uri> uriCollection)
{
var bitmapImages= new List<BitmapImage>();
uriCollection.ToObservable()
.SelectMany(LoadImageAsync)
.Catch(Observable.Empty<BitmapImage>())
.Subscribe(bitmapImages.Add,
() =>
{
BitmapImages = bitmapImages;
});
}
private IObservable<BitmapImage> LoadImageAsync(Uri uri)
{
return Observable.CreateWithDisposable<BitmapImage>(observer =>
{
var downloader = new WebClient();
downloader.OpenReadCompleted += (s, e) =>
{
if (e.Error != null)
{
observer.OnError(e.Error);
}
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = e.Result;
bitmapImage.EndInit();
observer.OnNext(bitmapImage);
observer.OnCompleted();
};
downloader.OpenReadAsync(uri);
return downloader;
});
}

Related

How to Capture Multiple Images Using Direct Show [duplicate]

I have a C# desktop application in which one thread that I create continously gets an image from a source(it's a digital camera actually) and puts it on a panel(panel.Image = img) in the GUI(which must be another thread as it is the code-behind of a control.
The application works but on some machines I get the following error at random time intervals(unpredictable)
************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere.
Then the panel turns into a red cross, red X - i think this is the invalid picture icon that is editable from the properties. The application keeps working but the panel is never updated.
From what I can tell this error comes from the control's onpaint event where I draw something else on the picture.
I tried using a lock there but no luck :(
The way I call the function that puts the image on the panel is as follows:
if (this.ReceivedFrame != null)
{
Delegate[] clients = this.ReceivedFrame.GetInvocationList();
foreach (Delegate del in clients)
{
try
{
del.DynamicInvoke(new object[] { this,
new StreamEventArgs(frame)} );
}
catch { }
}
}
this is the delegate:
public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
public event ReceivedFrameEventHandler ReceivedFrame;
and this is how the function inside the control code-behind registers to it:
Camera.ReceivedFrame +=
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);
I also tried
del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });
instead of
del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });
but no luck
Does anyone know how I could fix this error or at least catch the error somehow and make the thread put the images on the panel once again?
This is because Gdi+ Image class is not thread safe. Hovewer you can avoid InvalidOperationException by using lock every time when you need to Image access, for example for painting or getting image size:
Image DummyImage;
// Paint
lock (DummyImage)
e.Graphics.DrawImage(DummyImage, 10, 10);
// Access Image properties
Size ImageSize;
lock (DummyImage)
ImageSize = DummyImage.Size;
BTW, invocation is not needed, if you will use the above pattern.
I had a similar problem with the same error message but try as I might, locking the bitmap didn't fix anything for me. Then I realized I was drawing a shape using a static brush. Sure enough, it was the brush that was causing the thread contention.
var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
e.Graphics.FillRectangle(brush, location);
This worked for my case and lesson learned: Check all the reference types being used at the point where thread contention is occurring.
Seems to me, that the same Camera object is used several times.
E.g. try to use a new buffer for each received frame. It seems to me, that while the picture box is drawing the new frame, your capture library fills that buffer again. Therefore on faster machines this might not be an issue, with slower machines it might be an issue.
I've programmed something similar once, after each received frame, we had to request to receive the next frame and set the NEW frame receive buffer in that request.
If you can not do that, copy the received frame from the camera first to a new buffer and append that buffer to a queue, or just use 2 alternating buffers and check for overruns. Either use myOutPutPanel.BeginInvoke to call the camera_ReceivedFrame method, or better have a thread running, which checks the queue, when it has a new entry it calls mnyOutPutPanel.BeginInvoke to invoke your method to set the new buffer as image on the panel.
Furthermore, once you received the buffer, use the Panel Invoke Method to invoke the setting of the image (guarantee that it runs in the window thread and not the thread from your capture library).
The example below can be called from any thread (capture library or other separate thread):
void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
if(myOutputPanel.InvokeRequired)
{
myOutPutPanel.BeginInvoke(
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame),
sender,
e);
}
else
{
myOutPutPanel.Image = e.Image;
}
}
I think this is multithreading problem
Use windows golden rule and update the panel in the main thread use panel.Invoke
This should overcome cross threading exception

Xamarin: Load images asyn in view

I m developing a wallpaper app. Therefore I am using Xamarin.Forms. There is a scroll view with all available wallpapers and on top of it, the user can see the selected one bigger.
The problem is that when I start the App I can see that it is a bit lagging. (Images are loading in view) And I don t like that. It looks not good. So I started a new Task in that I am getting the images from the embedded resources. But that doesn't t coast so much time. The time-consuming thing is to display the images in the view. And I can not do that in the task because it has to be an STA thread to access the view.
I want it like this:
The app starts You see a running activity indicator
The images are loaded from the resource without blocking the UI thread
Images are loaded into view without blocking the UI thread
Activity indicator disappears and main grid comes in front
So here is my code:
Check this pastebin for code.
Thank you.
Ok I didn't t fixed the problem but I have a solution:
Task.Factory.StartNew(() =>
{
return InitializeImages();
}).ContinueWith(r =>
{
Device.BeginInvokeOnMainThread(() =>
{
if (r.Result == GlobalSettings.ImageCount)
ButtonsGrid.ColumnDefinitions[1].Width = 0;
else
ButtonsGrid.ColumnDefinitions[1].Width = GridLength.Star;
for (int i = 0; i < r.Result; i++)
{
ImagesGrid.Children.Add(_previewImages[i]);
ImagesStackLayout.Children.Add(_images[i]);
}
});
Thread.Sleep(2000);
Device.BeginInvokeOnMainThread(async () =>
{
await ShowWallpaperInPreview();
await Loading(false);
});
});
I let it lagging in the background. and after a short time, I change from loading to normal view. This way it is lagging in the background (who cares=)
A very good candidate to keep your app responsive and cover cases that are not mentioned in your question is an open source project named FFImageLoading. Here is the list of supported features:
Configurable disk and memory caching
Multiple image views using the same image source (url, path, resource) will use only one bitmap which is cached in memory (less memory usage)
Error and loading placeholders support
Images can be automatically downsampled to specified size (less memory usage)
Can retry image downloads (RetryCount, RetryDelay)
and much more!
Check the Advanced Usage doc and Wiki for more details.

A proper way to read image from other thread

I've got a class that runs a Thread with some data processing inside:
void thread_function()
{
while (client != null)
{
....some stuff...
wrBitmap.WritePixels(rect, rgbch, rowLen, 0); // wrBitmap is WriteableBitmap
OnFrameRdy(this, MyEventsArgs);
}
}
How I can read wrBitmap from this Thread process without memory copying? I need to bind wrBitmap to my Image wpf control. Is that possible?
Or the only way of doing it - is to copy wrBitmap for some other variable in class and read it in my control?
I dont want to copy variable, cause its going to give me a huge overhead in memory.
I thought I could use events and created my OnFrameRdy event, but is still doesnot let me use data.
It will be really cool if I could just type Image.source = MyClass.wrBitmap
I am guessing you are getting InvalidOperationException from attempt to set the Source property from different thread or using Bitmap in different thread than created (you seriously should be more explicit about what is the issue you are trying to solve).
If I am right, you should Freeze your bitmap (this disallows changes to it) and invoke assignment to Image.Source on the UI thread. This should work for you:
void thread_function()
{
while (client != null)
{
....some stuff...
wrBitmap.WritePixels(rect, rgbch, rowLen, 0); // wrBitmap is WriteableBitmap
wrBitmap.Freeze();
Dispatcher.BeginInvoke((Action)(() => Image.Source = wrBitmap));
}
}

UWP - How to use WriteableBimap in a background task? Alternative?

The following code reads an image, and centers (cuts and copies) that image into a resulting image with a black frame, returns the new image.
Can i do it without using a writeablebitmap class?
private async Task<WriteableBitmap> FrameToCenter(StorageFile image, Size newSize)
{
WriteableBitmap result = new WriteableBitmap((int) newSize.Width,(int) newSize.Height);
WriteableBitmap bitmapToFrame = await StorageFileToSoftwareBitmap(image);
Rect sourceRect = new Rect(0, 0, bitmapToFrame.PixelWidth, bitmapToFrame.PixelHeight);
int a = (int) Math.Min(newSize.Height, newSize.Width);
Rect destRect = new Rect((int) ((_screenResolution.Width - _screenResolution.Height)/2), 0, a, a);
result.Blit(destRect, bitmapToFrame, sourceRect);
return result;
}
Everything worked fine, but then I tried to put that code into a BackgroundTask in UWP, that results in an exception due to the fact that I can not use WriteableBitmap on a different thread then the UI thread.
Is there anything else I can do? I need to run it in a backgroundtask though.
As well the line:
result.Blit(destRect, bitmapToFrame, sourceRect);
is actually using a WriteableBitmapEx extension which I won't be able to use. Any other way? I really dont see a way how to manipulate a bitmap on a background task thread. I am quiet lost here. I've been even thinking about using openCV library and just do it in C++, would that solve the problem?
There is as well this SO Q&A, which mentions I should derive my background task from XamlRenderingBackgroundTask class. How can I do that?
You use the XamlBackgroundTask the same way you would a regular BackgroundTask
public sealed class MyBackgroundTask : XamlRenderingBackgroundTask
{
protected override async void OnRun(IBackgroundTaskInstance taskInstance)
{
var deferral = taskInstance.GetDeferral();
//your code here
deferral.Complete();
}
}
The difference is that this will have access to the UI thread so you can use your WriteableBitmap.
I would just keep an eye on the important recommendation in this article.
Important To keep the memory footprint of the background task as low as possible, this task should be implemented in a C++ Windows Runtime Component for Windows Phone. The memory footprint will be higher if written in C# and will cause out of memory exceptions on low-memory devices which will terminate the background task. For more information on memory constraints, see Support your app with background tasks.

Accessing System.Drawing.Bitmap from GTK# Thread throws Object Currently in use elsewhere exception

Im trying to manipulate an image using system.drawing in GTk#.I want the UI to update the image on screen as soon as the user updates a textbox.To implement this i tried using the background worker from winforms,it worked but when the textbox is updated at a higher speed the application becomes stuck with no error.
So i took a look at multithreading in GTK here http://www.mono-project.com/docs/gui/gtksharp/responsive-applications/ and created a thread .
void textboxchanged()
{
Thread thr = new Thread (new ThreadStart (ThreadRoutine));
thr.Start ();
}
static void ThreadRoutine ()
{
LargeComputation ();
}
static void LargeComputation ()
{
image=new Bitmap(backupimage);
//Long image processing
}
It works poorly than the background worker throws up object currently in use elsewhere error here image=new Bitmap(backupimage); when the speed of entry in textbox is even a little fast.What im i doing wrong ?
Update 1 :
Im not processing the same image using 2 different threads that does 2 different operations at the same time.Im calling the thread that does the same operation before the old thread is complete.As in background worker i need a way to check if the old thread has completed working before launching the new one.So basically what im looking for is a way to check if an instance of the same thread
is running.In winforms i used to do if(backgroundworker.isbusy==false) then do stuff
Update 2
Solution with performance degradation
As suggested by #voo Replacing the global bitmap helped solve the issue.What i did was instead of using global bitmap.I created a global string(filename).Now i use img=new Bitmap(filename).Tried executing fast as i can no error came up.So inorder to update the GUI i used the invoke as suggested here mono-project.com/docs/gui/gtksharp/responsive-applications/.The thing is no error comes up and image gets updated,but when the typing operation is fast enough there a wait involved. Performance got degraded.This was not the case with background worker.Is there a way to improve performance.
At end of the large image processing operation method i added this to update GUI
Gtk.Application.Invoke (delegate {
MemoryStream istream=new MemoryStream();
img.Save (istream, System.Drawing.Imaging.ImageFormat.Png);
istream.Position = 0;
workimagepixbuff = new Gdk.Pixbuf (istream);
image1.Pixbuf = workimagepixbuff.ScaleSimple (400, 300, Gdk.InterpType.Bilinear);
});
// cannot directly convert Bitmap to Pixbuff,so doing this
The problem here is that you are processing an image at two places (two threads) on the same time and the image operation in .Net (GDI speaking) does not allow this.
Because you did not provided very much information I'm just guessing here.
When manipulating bitmap images in GDI, there are BitmapData behind the scene that needs to be locked and unlocked. This mechanism just make the picture available in memory for read/write. But AFAIK when you lock a BitmapData that is already locked you get a similar exception : System.InvalidOperationException, Bitmap region is already locked.
To me it sounds like you are getting this kind of error, but with other words because you are not explicitly locking bitmap data bits. GDI just tell you : "I would have to lock bitmap data bits but I can't because the object is already used (locked) elsewhere."
The solution here may be to try to synchronize bitmap usage (where bits locking may be involved) between thread whenever they may occur. So you may want to use the lock keyword or a similar mechanism:
So try something that looks like the following:
private static object _imageLock = new object();
static void LargeComputation ()
{
lock(_imageLock)
{
image=new Bitmap(backupimage);
//Long image processing ...
}
}
static void AnotherImageOperationSomewhereElse()
{
lock(_imageLock)
{
//Another image processing on backupImage or something derived from it...
}
}

Categories

Resources