1

Problem: how can I save/compress bitmap images (several of them stacked in tiff file) asynchronously with respect to the main thread?

My setting: (synchronous & slow working solution: when the compression is performed the thread remains stuck for a few seconds. I cannot afford this.)

 ...in the body of a window class 

private void afunction(){ 
     //called with a given frequency and updating this.colorBitmap
     ...
     this.colorBitmap = something;          
     savePictureStackTiff();
}

private int stack_pict_count = 0;
private int PICT_PER_FILE = 45;        
private TiffBitmapEncoder encoder;        

private string savePictureStackTiff()
{
      initializeTiffEncoder();
      //make a local copy of the image I want to put in the tiff binder
      WriteableBitmap localCopy = new WriteableBitmap(this.colorBitmap);    

      encoder.Frames.Add(BitmapFrame.Create(localCopy));  

      stack_pict_count++;
      if (stack_pict_count % PICT_PER_FILE == 0) 
      //Once I have enough files stacked I ask for compression
      {              
          stack_pict_count = 0;
          pict_metadata = "";            
          try
          {
              using (FileStream fs = new FileStream(path, FileMode.Create))
              {
                  encoder.Save(fs); //<<<== LINE WHICH I'D LIKE TO BE RUN ASYNC
              }                    
           }
           catch (IOException)
           {}               
        }
 }

 private void initializeTiffEncoder()
 {
    if (stack_pict_count == 0)
    {                
        encoder = new TiffBitmapEncoder();
        encoder.Compression = TiffCompressOption.Zip;                
    }
 }

What I've been trying: I would like the compression (the call encoder.save(fs)) to be performed in another thread than the main one.

I tried to put the call encoder.save(fs) in a BackgroundWorker which preventively copies the encoder to a local version (not sure if it worked though) and then makes the call. I receive an error like 'The calling thread cannot access this object because a different thread owns it'.

If I use a Dispatcher.Invoke (provided I do it correctly) the execution becomes again very slow.

Am I make some stupid mistake?


EDIT: (work in progress following the suggestions of @meilke and @user7116)

I now moved the allocation and the execution of the compressor in a BackgroundWorker. Although now colorBitmap which is passed, owns to another thread. I tried to freeze it, but it doesn't look enough; I still get a 'The calling thread cannot access this object because a different thread owns it'.

        tiffCompressorWorker = new BackgroundWorker();
        tiffCompressorWorker.DoWork += (s, a) =>
        {

            initializeTiffEncoder();
            WriteableBitmap localCopy = new WriteableBitmap((WriteableBitmap)a.Argument);
            localCopy.Freeze();
            encoder.Frames.Add(BitmapFrame.Create(localCopy));

            stack_pict_count++;
            if (stack_pict_count % PICT_PER_FILE == 0)
            {
                stack_pict_count = 0;                    
                try
                {
                    using (FileStream fs = new FileStream(path, FileMode.Create))
                    {
                        saving_encoder.Save(fs);
                    }
                }
                catch (IOException)
                {
                    ..
                }
            }            
        };
Acorbe
  • 7,956
  • 3
  • 33
  • 59

2 Answers2

2

You have to put the whole TIFF operation into the background worker. And then pass a copy of the input image as an argument to RunWorkerAsync. Here is a link to one of the many solutions available on the web on how to do it: Copying from BitmapSource to WritableBitmap. Put that code into a helper method and use it to copy the image before saving it to disk.

Community
  • 1
  • 1
meilke
  • 3,192
  • 1
  • 12
  • 30
  • The key is the `TiffBitmapEncoder`/`WriteableBitmap` needs to be *created* in the same thread it will be used in, and likely the `colorBitmap` will need to be frozen or copied too. – user7116 Sep 13 '13 at 18:53
  • @user7116 I guess I now implemented it this way, but now how can I deepcopy `colorBitmap` among the threads? – Acorbe Sep 13 '13 at 18:58
  • @user7116, I really cannot manage. Could you please elaborate a bit more? – Acorbe Sep 13 '13 at 19:52
  • I adjusted my answer to include a link on how to deep copy the image. – meilke Sep 14 '13 at 12:28
0

I am no computer scientist, but I found this post, which states:

WriteableBitmap inherits DispatcherObject, and must be accessed only within the dispatcher thread that owns it.

And, it looks to me like you are steping outside of the DispatcherObject's permissions.

Community
  • 1
  • 1
9301293
  • 484
  • 3
  • 16