TiffBitmapEncoder: асинхронное кодирование растрового изображения

Проблема: как я могу сохранять/сжимать растровые изображения (несколько из них сложены в файл tiff) асинхронно по отношению к основному потоку?

Моя настройка: (синхронное и медленное рабочее решение: когда выполняется сжатие, поток зависает на несколько секунд. Я не могу себе этого позволить.)

 ...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;                
    }
 }

Что я пробовал: я хотел бы, чтобы сжатие (вызов encoder.save(fs)) выполнялось в другом потоке, а не в основном.

Я попытался поместить вызов encoder.save(fs) в BackgroundWorker, который превентивно копирует кодировщик в локальную версию (хотя не уверен, что это сработало), а затем выполняет вызов. Я получаю сообщение об ошибке, например 'Вызывающий поток не может получить доступ к этому объекту, поскольку он принадлежит другому потоку'.

Если я использую Dispatcher.Invoke (при условии, что я делаю это правильно), выполнение снова становится очень медленным.

Я делаю какую-то глупую ошибку?


EDIT: (работа в процессе, следуя предложениям @meilke и @user7116)

Теперь я переместил выделение и выполнение компрессора в файл BackgroundWorker. Хотя теперь colorBitmap, который передается, принадлежит другому потоку. Я пытался freeze сделать это, но этого недостаточно; Я по-прежнему получаю сообщение "Вызывающий поток не может получить доступ к этому объекту, поскольку им владеет другой поток".

        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)
                {
                    ..
                }
            }            
        };

person Acorbe    schedule 13.09.2013    source источник
comment
Я с той же проблемой.   -  person 9301293    schedule 22.12.2016


Ответы (2)


Вы должны поместить всю операцию TIFF в фоновый рабочий процесс. А затем передайте копию входного изображения в качестве аргумента RunWorkerAsync. Вот ссылка на одно из множества доступных в Интернете решений о том, как это сделать: BitmapSource в WritableBitmap. Поместите этот код во вспомогательный метод и используйте его для копирования изображения перед его сохранением на диск.

person meilke    schedule 13.09.2013
comment
Ключ в том, что TiffBitmapEncoder/WriteableBitmap необходимо создать в том же потоке, в котором он будет использоваться, и, вероятно, colorBitmap тоже нужно будет заморозить или скопировать. - person user7116; 13.09.2013
comment
@user7116 user7116 Думаю, теперь я реализовал это таким образом, но как теперь я могу глубоко скопировать colorBitmap среди потоков? - person Acorbe; 13.09.2013
comment
@ user7116, я действительно не могу справиться. Не могли бы вы уточнить немного больше? - person Acorbe; 13.09.2013
comment
Я скорректировал свой ответ, включив в него ссылку о том, как глубоко скопировать изображение. - person meilke; 14.09.2013

Я не компьютерщик, но нашел этот пост, в котором говорится:

WriteableBitmap наследует DispatcherObject и должен быть доступен только в потоке диспетчера, которому он принадлежит.

И мне кажется, что вы выходите за пределы разрешений DispatcherObject.

person 9301293    schedule 22.12.2016