I need to create a thumbnail of an image on WP8, and currently I'm facing difficulties. In a nutshell, I know the only one way of doing this, that is using classes System.Windows.Controls.Image, System.Windows.Media.Imaging.BitmapImage and System.Windows.Media.Imaging.WritableBitmap. I'm also trying to perform thumbnail creation on the threadpool, because it's a part of other bigger operation, which is running on a threadpool.
As you have probably already understood, I'm failing with invalid cross-thread access, even when I'm trying to create an instance of the above classes. That's a shame, really, because this thumbnail even is not going to be used in the UI, only saved to a file, and displayed from the file later on. My work has nothing to do with UI thread, and I'm still facing this limitations.
So is there some other way of creating the thumbnail from an image stream (I'm getting it from PhotoChooser task)? Maybe some other API, which doesn't require those UI-bound classes? Tried to bing it, even to google it, but with no luck.
Okay, I think I'll put my own answer here as well, since it shows things from a bit of different perspective. The answer by Justin Angel is okay, but there's a couple of issues with it:
It's not possible to have a reference to Dispatcher, when the code is deep in the Model layer and running on the background thread.
I need to return the thumbnail image from the method and use it later in the same synchronization context. Otherwise I'll have to change a lot of code around this method of creating thumbnail.
With this requirements in mind, here's my solution:
private WriteableBitmap CreateThumbnail(Stream stream, int width, int height, SynchronizationContext uiThread)
{
// This hack comes from the problem that classes like BitmapImage, WritableBitmap, Image used here could
// only be created or accessed from the UI thread. And now this code called from the threadpool. To avoid
// cross-thread access exceptions, I dispatch the code back to the UI thread, waiting for it to complete
// using the Monitor and a lock object, and then return the value from the method. Quite hacky, but the only
// way to make this work currently. It's quite stupid that MS didn't provide any classes to do image
// processing on the non-UI threads.
WriteableBitmap result = null;
var waitHandle = new object();
lock (waitHandle)
{
uiThread.Post(_ =>
{
lock (waitHandle)
{
var bi = new BitmapImage();
bi.SetSource(stream);
int w, h;
double ws = (double)width / bi.PixelWidth;
double hs = (double)height / bi.PixelHeight;
double scale = (ws > hs) ? ws : hs;
w = (int)(bi.PixelWidth * scale);
h = (int)(bi.PixelHeight * scale);
var im = new Image();
im.Stretch = Stretch.UniformToFill;
im.Source = bi;
result = new WriteableBitmap(width, height);
var tr = new CompositeTransform();
tr.CenterX = (ws > hs) ? 0 : (width - w) / 2;
tr.CenterY = (ws < hs) ? 0 : (height - h) / 2;
tr.ScaleX = scale;
tr.ScaleY = scale;
result.Render(im, tr);
result.Invalidate();
Monitor.Pulse(waitHandle);
}
}, null);
Monitor.Wait(waitHandle);
}
return result;
}
I'm capturing UI thread's SynchronizationContext while I'm still in the UI thread (in View Model), and passing it further, and then I'm using closures to capture local variables, so that they're available for the callback which runs on UI thread. I'm also using lock and Monitor to synchronize these two threads and wait until the image is ready.
I'll accept my or Justin Angel's answer based on votes, if any. :)
EDIT: You can get an instance of Dispatcher's SynchronizationContext through System.Threading.SynchronizationContext.Current while you're on UI thread (in an button click handler, for example). Like this:
private async void CreateThumbnailButton_Clicked(object sender, EventArgs args)
{
SynchronizationContext uiThread = SynchronizationContext.Current;
var result = await Task.Factory.StartNew<WriteableBitmap>(() =>
{
Stream img = GetOriginalImage();// get the original image through a long synchronous operation
return CreateThumbnail(img, 163, 163, uiThread);
});
await SaveThumbnailAsync(result);
}
Yeah, using WriteableBitmap requires access to the UI string. You might have to schedule the work on the UI thread as part of your workflow using the Dispatcher class.
The only other thing I can think off is saving the image to the Phone's MediaLibrary and using the Picture.GetThumbnail() method to get a very low-res thumbnail. It might or might not work without access to the UI thread. Also, once you add pictures to the user's MediaLibrary, you can't delete those so be careful not to spam those folders.
Related
I would like to create a worker class that can retrieve laggy resources (eg from a database or over a network), and prepare them for later, zero-lag display on the UI. That worker class should not be bound to the UI or to the UI thread i.e. it should be able to run anywhere. When done, it's output will be a populated array of some image container class that the UI can work with easily.
My first thought was to use the BitmapImage class as the image container; however that inherits from the DependencyObject class, which will restrict access from any non-UI thread. As #Filip pointed out in my earlier form of this question, this is not inherently a problem, but it would restrict the generality of my worker class.
Is there a better Windows class that I can use as the basis for the image container, and then convert to BitmapImage on the UI thread at bind time?
Or a better way altogether?
Here is some pseudo code to indicate what I'm trying to do, using an array of URIs for the source images.
In the worker class
ImageContainerClass[] prepImages(Uri[] uriSet)
{
ImageContainerClass[] iccSet = new ImageContainerClass[uriSet.Length];
for (int i = 0; i < iccSet.Length; i++)
iccSet[i] = new ImageContainerClass.retrieveImage(uriSet[i]);
return iccSet;
}
On the UI thread (at some later time):
BitmapImage bmi = new BitmapImage();
var image = iccSet[i].getImage();
<<some operation to set the source of bmi to the image>>
someImage.Source = bmi;
I think loading images into BitmapImage happens on a background thread in the platform, so there shouldn't be a need for you to do it. You could probably use BitmapDecoder on a background thread if you needed to and then maybe push a pixel buffer into a WriteableBitmap, but you wouldn't gain much from it. If you really want to force all the code to a background thread then you can use DirectX interop and have a completely separate background render thread for your stuff.
OK, so here's a solution that appears to work.
The following code will run happily on a non-UI thread:
internal async Task<InMemoryRandomAccessStream> getImage(Uri uri)
{
try {
var httpClient = new HttpClient();
IBuffer iBuffer = await httpClient.GetBufferAsync(uri);
byte[] bytes = iBuffer.ToArray();
InMemoryRandomAccessStream ims = new InMemoryRandomAccessStream();
DataWriter dataWriter = new DataWriter(ims);
dataWriter.WriteBytes(bytes);
await dataWriter.StoreAsync();
ims.Seek(0);
return ims;
} catch (Exception e) { return null; }
}
Then, the following code may be executed on the UI thread when the image needs to be displayed:
BitmapImage bmi = new BitmapImage();
bmi.SetSource(ims);
someImage.Source = bmi;
I'm sure this can be cleaned up by people more familiar with the relevant frameworks, but hopefully this will help others. My thanks to #Filip for putting me on the right track.
I have a Winform with 4 PictureBox controls, each control will contain a diferent image. The process is:
An event x is raised, the eventargs from this event, contain the filenames of each image (4), an so on (file exists etc..). Then, I have to update the UI.
Commonly I use Invoke:
Invoke((ThreadStart)delegate()
{
picBig.Image = new Bitmap(strImageBig);
picLittle1.Image = new Bitmap(saLittle[0]);
picLittle2.Image = new Bitmap(saLittle[1]);
picLittle3.Image = new Bitmap(saLittle[2]);
});
// saLittle[] is a string array, contains, filenames: "image1.jpg"
But when this executes, the form freezes for a little time, about 500ms, I know it's a small interval but it's noticeable, then the app continues normally.
I was trying to find out the reason of that 'UI freeze', then, after research, I found BeginInvoke. Now my code looks like this:
BeginInvoke((MethodInvoker)delegate
{
picBig.Image = new Bitmap(strImageBig);
picLittle1.Image = new Bitmap(saLittle[0]);
picLittle2.Image = new Bitmap(saLittle[1]);
picLittle3.Image = new Bitmap(saLittle[2]);
});
This is a little faster. But the UI is still freezing for 200~300ms.
In the articles I've read, they say that BeginInvoke is a better way than Invoke.
The code is working OK, there is no problem with logic or anything else. I just want to know why this happens. I don't want to leave this doubt unclear. The project is already finished. Hope this be useful for someone else.
Maybe this is not the correct approach. I know there are many ways to update the UI from a background thread, but is there another way to make an update faster?
Or, do you think, the image loading is the reason? Is there another way to do a faster loading of images?
Thanks in advance.
This is because you're actually loading your images from disk on the UI thread along with setting the contents of the control. Calling the Bitmap constructor with a file path will go to the hard drive and load the image into memory.
Invoke and BeginInvoke will run the delegate that you provide on the thread that the control was created on, which is most likely going to be the UI thread.
...but is there another way to make an update faster?
Load the images on your background thread and, when they're actually loaded, invoke and set the images into the controls.
var big = new Bitmap(strImageBig);
var little1 = new Bitmap(saLittle[0]);
var little2 = new Bitmap(saLittle[1]);
var little3 = new Bitmap(saLittle[2]);
Invoke((ThreadStart)delegate()
{
picBig.Image = big;
picLittle1.Image = little1;
picLittle2.Image = little2;
picLittle3.Image = little3;
});
But when this executes, the form freezes for a little time, about 500ms, I know it's a small interval but it's noticeable, then the app continues normally.
Eventually, the UI thread needs to actually update the images. When the images are generated and updated on the UI thread, this will cause a (short) delay.
Currently my program reads images and text in a record from an xml file, displays them on the screen, and then the click of the previous/next buttons moves to the next record. However, it seems to need a few seconds loading time between each photo and I'd like it to be instant, like how Windows Photo Gallery would...or Facebook photos (bear in mind this is not a web app).
I searched found a few similar situations to mine but none seemed to fit my situation. I tried making a class, based on my search, to deal with background loading and calling it in my program, but it's fraught with error and probably won't even do what I want it do:
//ImageManager.cs
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 = images.Add(s, img); //load the image s - ERROR cannot implicitly convert type void to image. Void??
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();
}
}
}
//MainWindow.cs
ImageManager im = new ImageManager();
im.preload("Data/Images"); // Errors - im is a field but used like a type/token '('
Many thanks in advance
Your ImageManager should work with ImageSources, not Images. Even if you get your current code to work you'll find that your UI still hangs because you have no choice but to perform the work on the UI thread. If you instead deal with ImageSources, you can load them on a background thread and then freeze them in order to use them from the UI thread. This frees you to pre-emptively load images, or to show a loading animation whilst they load.
BitmapFrame.Create is likely the method you want to be using to load the images.
Consider caching scaled down images - 1:1 of what you want to show, or even smaller. This way loading of preview will be much faster and if user looks at the image long enough you can load full image.
With modern photos original size of the image is usually way bigger than can be normally diaplayed. So if you always read original images you spend large amount of disk IO on something that will never be shown.
Usual note: it may not be case in your program. As with any performance issues measure, than optimize.
In a previous question I asked how to improve a bit of code. It was said that I should move it to a new thread. I'd never thought about it before so it seems like a great idea to me. So this morning I went ahead and reused a bit of code I already have for processing emails and updated the way I handle image uploads into my site.
So is this a good way to start a new thread and process the images? Is there even a need to lock it like I am?
private static object dummy = new object();
public static void Save(int nProjId, byte[] bData)
{
var worker = new ThreadStart(() => ProcessImage(nProjId,bData));
var thread = new Thread(worker);
thread.Start();
}
private static void ProcessImage(int nProjId, byte[] bData)
{
lock (dummy)
{
try
{
byte[] xlargeImage = Thumbs.ResizeImageFile(bData, 700);
byte[] largeImage = Thumbs.ResizeImageFile(bData, 500);
//improved based on previous question to use the already reduced image
byte[] mediumImage = Thumbs.ResizeImageFile(xlargeImage, 200);
byte[] smallImage = Thumbs.ResizeImageFile(xlargeImage, 100);
//existing code to actually save the images
MyGlobals.GetDataAccessComponent().File_Save(
ConfigurationManager.ConnectionStrings["ImgStore"],
nProjId,
xlargeImage,
largeImage,
mediumImage,
smallImage);
}
catch (Exception)
{
//ToDo: add error handleing
{ }
throw;
}
}
}
Oh and the images now upload and process nearly instantly (locally) so it's a HUGE help so far. I just want to make sure it's the best way to do it. Oh and I'm using a dual core machine running Server 2008 with 6gb or ram, so I have a little wiggle room to make it faster or use more threads.
I would suggest using a ThreadPool class, specifically because it will re-use a thread for you rather than you creating a new thread each time, which is a little bit more intensive.
Check out the QueueUserWorkItem method.
Also if you are not using a static resource to write to (I am not sure what exactly File_Save does) I dont think there is a need for your lock. However if you are using a static resource then you should lock just the code that is using it.
Is this for any production code? Or just a sample ? If it is not for production code, apart from using ThreadPool, you can use TPL from .NET4.0 . MS recommends using TPL instead of ThreadPool.
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