快速的多线程图像处理,不是那么快

时间:2013-12-13 21:10:47

标签: .net wpf multithreading image-processing c++-cli

我正在创建一个函数,对每个像素进行一些强烈的计算。使用此功能时,小型笔记本电脑可能无法正常工作。我从来没有使用线程来提高处理速度,但我认为如果没有它们我就无法管理。

我认为让一个线程执行所有奇数行并且一个线程执行偶数行可能是个好主意。我创建了一个小的WPF应用程序来测试它。这是一个窗口,背景图像从黑色变为绿色。一段时间后,它又重新开始。在每次像素更新期间,都会执行数学函数。

尽管我希望速度大幅提升但是非常令人失望,有人能告诉我如何优化多线程图像处理吗?

这是代码。

#define MULTITHREADED
#include <cmath>
#include "bitmap.h"//Based on WriteableBitmap, used this thread:http://stackoverflow.com/questions/1855308/asynchronous-operations-on-writeablebitmap/5292315#5292315

using namespace System;
using namespace System::ComponentModel;
using namespace System::Threading;
using namespace System::Windows;
using namespace System::Windows::Threading;

public ref class TestWindow : Window
{
public:
    TestWindow(void) : _isActive(true)
    {
        _bm = gcnew Bitmap(1000,1000);
        ImageBrush^ ib = gcnew ImageBrush(_bm->bitmap);
        ib->Stretch = Stretch::Fill;
        this->BeginInit();
        this->Background = ib;
        this->Height = 300;
        this->Width = 300;
        this->EndInit();
        this->Loaded += gcnew RoutedEventHandler(this, &TestWindow::onLoaded);
        this->Closing += gcnew CancelEventHandler(this, &TestWindow::onClosing);
    }

    void onClosing(Object^ sender,CancelEventArgs^ e)
    {
        //Let the threads know the window is being closed.
        _isActive = false;
#ifdef MULTITHREADED
        //Give the threads a chance to terminate properly.
        _t0->Resume();
        _t1->Resume();
#endif
    }

    void onLoaded(Object^ sender, RoutedEventArgs^ e)
    {
#ifndef MULTITHREADED
        _d = gcnew DispatcherTimer(TimeSpan(1),DispatcherPriority::Background,gcnew EventHandler(this,&TestWindow::sDispatchFunc),Dispatcher);
        _d->Start();
#else
        _t0 = gcnew Thread(gcnew ParameterizedThreadStart(this,&TestWindow::threadFunc));
        _t1 = gcnew Thread(gcnew ParameterizedThreadStart(this,&TestWindow::threadFunc));
        _d = gcnew DispatcherTimer(TimeSpan(1),DispatcherPriority::Background,gcnew EventHandler(this,&TestWindow::mDispatchFunc),Dispatcher);
        _d->Start();
#endif
    }

#ifdef MULTITHREADED
    void mDispatchFunc(Object^ sender, EventArgs^ e)
    {
        //Prepare the image for pixel access.
        _bm->lock();
        //Run the threads.
        if(_t0->ThreadState == ThreadState::Unstarted){//If one of the threads is unstarted, start them both.
            _t0->Start(_t0);
            _t1->Start(_t1);
        }else if(_t0->ThreadState == ThreadState::Suspended){//If one of the threads is suspended, restart them both.
            _t0->Resume();
            _t1->Resume();
        }
        //Wait for the threads.
        while(_t0->ThreadState != ThreadState::Suspended || _t1->ThreadState != ThreadState::Suspended){}
        //Place the new image.
        _bm->unlock();
        dynamic_cast<ImageBrush^>(this->Background)->ImageSource = _bm->bitmap;
        //Do everything over again if the window is still active.
        if(_isActive)
            _d->Start();
    }

    void threadFunc(Object^ sender)
    {
        //Determine which thread this is.
        uint8_t tid;
        if(dynamic_cast<Thread^>(sender) == _t0)
            tid = 0;
        else
            tid = 1;
        //Prepare the variables to avoid calculations in the loop.
        int w = _bm->width;
        int h = _bm->height;
        uint8_t* p;
        //This loop will stay active as long as the window is opened.
        while(true){
            //Determine where the thread should start.
            p = _bm->backBuffer + w * 4 * tid;
            //Change the image color.
            for(int y = tid; y < h; y += 2){
                for(int x = 0; x < w; x++){
                    float y = cos(12.34) + sin(56.78) * sqrt(tan(0.9f));//Different calculations each test.
                    (*(p + 1))++;
                    (*(p + 3))++;
                    p += 4;
                }
                //Skip a row.
                p += w * 4;
            }
            //Give the image time to render.
            Thread::CurrentThread->Suspend();
            //If the window has been closed, stop the thread.
            if(!_isActive)
                return;
        }
    }

#else
    void sDispatchFunc(Object^ sender,EventArgs^ e)
    {
        //Prepare the image for pixel access.
        int w = _bm->width;
        int h = _bm->height;
        _bm->lock();
        uint8_t* p = _bm->backBuffer;
        //Change the image color.
        for(int y = 0; y < h; y++){
            for(int x = 0; x < w; x++){
                float y = cos(12.34f);//Different calculations each test.
                (*(p + 1))++;
                (*(p + 3))++;
                p += 4;
            }
        }
        //Place the new image.
        _bm->unlock();
        dynamic_cast<ImageBrush^>(this->Background)->ImageSource = _bm->bitmap;
        //Do everything over again if the window is still active.
        if(_isActive)
            _d->Start();
    }
#endif

private:
    Bitmap^ _bm;
    DispatcherTimer^ _d;
#ifdef MULTITHREADED
    Thread^ _t0;
    Thread^ _t1;
    Thread^ _t2;
#endif
    bool _isActive;
};

[STAThread]
int main(void)
{
    System::Windows::Application app;
    app.Run(gcnew TestWindow);
}

这些是我在调试模式下使用Visual Studio在四种不同情况下得到的结果:

  1. 每个像素无计算。
  2. cos(12.34f)/像素。
  3. cos(12.34f)+ sin(56.78f)/像素。
  4. cos(12.34f)+ sin(56.78f)* sqrt(tan(0.9f))

                Thread count  |  1        2
    Calculation               |
    __________________________|___________________
    1.                        |  2        17
    2.                        |  35       34
    3.                        |  67       62
    4.                        |  131      73
    
  5. 有没有办法让多线程版本在功能较少的情况下也能更快地运行?

    谢谢。

0 个答案:

没有答案