2009-03-13 10 views
0

大きな画像(約2000x2000)をすばやく処理しようとしています。私はTrackBarを使ってイメージの全体的な明るさを変更しています。問題は、あなたが知っているようにTrackBarを非常に素早く動かすことができることです。 の画像を処理する必要がないため、TrackBarのすべてのティックがOKですが、合理的に応答する必要があります。イメージエディタにある明るさトラックバーを考えてみましょう。複数のスレッドを使用してセクション内の画像を処理する方法は?

私は、動作する別のスレッドですべての処理を試みましたが、それでもビットマップでLockBitsを使用しても遅すぎます。 ColorMatrixを使用することはできません。なぜなら、コンポーネントのオーバーフローが発生し、255でクリップするための値が必要なのです。したがって、私はThreadPoolを使用してイメージをセクションに分割することで、良いパフォーマンスを達成できると考えています。

このアプローチの問題は、私は複数のtrheadedアプリケーションで多くの経験を持っていないと私は競争条件(ロックされたイメージなどLockBitsを呼び出すなど)を回避する方法を知りません。 )。誰でも私にこれを行う方法の例を教えてもらえますか?

これまでのコードは次のとおりです。私はそれが良いから遠いことを知っているが、私はそれを働いていると、この時点でさまざまなことをしようとしています。基本的なコンセプトは、ベースイメージをソースとして使用し、各ピクセルで何らかの操作を実行し、処理されたピクセルを表示Bitmapに描画することです。どんな助けも素晴らしいだろう。

public Form1() 
    { 
     InitializeComponent(); 
     testPBox1.Image = Properties.Resources.test; 
     trackBar1.Value = 100; 
     trackBar1.ValueChanged += trackBar1_ValueChanged;   
    }  

    void trackBar1_ValueChanged(object sender, EventArgs e) 
    { 
     testPBox1.IntensityScale = (float) trackBar1.Value/100; 
    } 

class TestPBox : Control 
{ 
    private const int MIN_SAT_WARNING = 240;   
    private Bitmap m_srcBitmap; 
    private Bitmap m_dispBitmap; 
    private float m_scale;   

    public TestPBox() 
    { 
     this.DoubleBuffered = true;            
     IntensityScale = 1.0f; 
    }  

    public Bitmap Image 
    { 
     get 
     { 
      return m_dispBitmap; 
     } 
     set 
     { 
      if (value != null) 
      { 
       m_srcBitmap = value; 
       m_dispBitmap = (Bitmap) value.Clone(); 
       Invalidate(); 
      } 
     } 
    } 

    [DefaultValue(1.0f)] 
    public float IntensityScale 
    { 
     get 
     { 
      return m_scale; 
     } 
     set 
     { 
      if (value == 0.0 || m_scale == value) 
      {     
       return; 
      } 

      if (!this.DesignMode) 
      { 
       m_scale = value; 
       ProcessImage();      
      } 
     } 
    } 

    private void ProcessImage() 
    { 
     if (Image != null) 
     { 
      int sections = 10; 
      int sectionHeight = (int) m_srcBitmap.Height/sections; 
      for (int i = 0; i < sections; ++i) 
      { 
       Rectangle next = new Rectangle(0, i * sectionHeight, m_srcBitmap.Width, sectionHeight); 
       ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { ChangeIntensity(next); })); 
      } 
     } 
    } 

    private unsafe void ChangeIntensity(Rectangle rect) 
    {    
     BitmapData srcData = m_srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb); 
     BitmapData dspData = m_dispBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);    
     byte* pSrc = (byte*) srcData.Scan0; 
     byte* pDsp = (byte*) dspData.Scan0; 

     for (int y = 0; y < rect.Height; ++y) 
     { 
      for (int x = 0; x < rect.Width; ++x) 
      { 
       // we are dealing with a monochrome image, so r = g = b. 
       // We only need to get one component value to use for all r, g, and b. 
       byte b = (byte) CompMinMax(0, 255, (int) (pSrc[0] * m_scale)); 
       Color c = (b > MIN_SAT_WARNING) ? Color.FromArgb(b, Color.Red) : Color.FromArgb(255, b, b, b);      

       // windows stores images internally in 
       // reverse byte order, i.e., Bgra, not Argb. 
       pDsp[3] = (byte) c.A; 
       pDsp[2] = (byte) c.R; 
       pDsp[1] = (byte) c.G; 
       pDsp[0] = (byte) c.B; 

       pSrc += 4; 
       pDsp += 4;       
      } 
     } 

     m_srcBitmap.UnlockBits(srcData); 
     m_dispBitmap.UnlockBits(dspData); 
     this.Invalidate();      
    } 

    private int CompMinMax(int min, int max, int value) 
    { 
     if (value > max) return max; 
     if (value < min) return min; 
     return value; 
    }   

    protected override void OnPaint(PaintEventArgs e) 
    { 
     if (Image != null) 
     {    
      Graphics g = e.Graphics; 
      Rectangle drawingRect = PaintUtils.CenterInRect(ClientRectangle, PaintUtils.ScaleRect(ClientRectangle, Image.Size).Size); 
      g.DrawImage(Image, drawingRect, 0, 0, Image.Width, Image.Height, GraphicsUnit.Pixel);   
     } 

     base.OnPaint(e); 
    } 
} 

答えて

4

ユーザーがトラックバーをドラッグしている間に画像の縮小版を生成し、その上で画像操作を行うと考えましたか?それはあなたに最終的な価値に落ち着くまでユーザーに有用なフィードバックを与えながら応答性を与えるでしょう。

+0

私はそれについて考えている...試してみる... –

+0

優秀な 'アウトオブボックスの'思考! – Jack

1

現在の方法を使用したい場合は、競合状態を取り除くためにいくつか微妙な調整を加えることができます。

変更する必要があるのは、個々のスレッドプール要求ではなく、ProcessImageでLockBits/UnlockBitsを呼び出すように調整することです。しかし、スレッドプールデリゲートに渡されたWaitHandleも追跡して、すべてのスレッドプールスレッドが完了するまでブロックできるようにする必要があります。すべてのデリゲートが終了するまで、ビットのロックを解除して無効にして戻ってください。

ただし、リリースタイムフレームに応じて、ThreadPoolの代わりにタスク並列ライブラリを使用することを検討してください。 C#4にはこれが含まれていますが、それを待つことができない場合は、Monoのバージョンは今日は合理的にうまく動作します。実際には、このタイプの作業は、ThreadPoolを使用するよりもはるかに簡単です。

0

一般に、イメージのサブセットに対するマルチスレッド処理は可能ですが、イメージ全体を処理することでサブセットを処理することで同じ結果をもたらすアルゴリズムには通常意味があります。 AとBは、画像のサブセットである言い換えれば、これは有効な式でなければなりません:

alg(A + B) = alg(A) + alg(B) 

だから、あなたはあなたの何らかの操作は、アルゴリズムのこのクラスに属するかどうかを識別する必要があります。

関連する問題