2011-01-22 6 views
3

私はアプリケーションを作成していますが、基本的にイメージのすべてのピクセルをピクセルの色(単純なもの)で検索するためにループを使用しています。決して正常に戻りません。私は実際の問題を見ずにコードの時間と時間をもう一度ループしました。Forループ、アプリケーションぶら下げ

私が気づいた唯一の事は、ScanPixelsLaterのforループですが早く終了することがあります。私はあなたのアプリケーションは、あなたがWinフォームアプリケーションがどのように機能するかについて少し知っておく必要がぶら下がっている理由を理解することは、だから、できるだけ多くのコード、

private Point topLeftc, bottomLeftc, topRightc, bottomRightc; 

/// <summary> 
/// Starts the initial looping process, designed only to loop through ONCE, ScanPixelsLater takes over 
/// </summary> 
/// <param name="img">Image to scan</param> 
public void ScanPixels(Bitmap img) 
{ 
    int whitePixel = 0; 

    for (int y = 100; y < img.Height; y++) 
    { 

     for (int x = 100; x < img.Width; x++) 
     { 

      if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255)) 
      { 
       // img.SetPixel(x, y, Color.Green); 
       whitePixel++; 

      } 
      else { whitePixel = 0; } 


      if (whitePixel == 18) 
      { 
       whitePixel = 0; 
       topLeftc = new Point(x - 18, y); 
       DetectNextWhiteLine(topLeftc, img); 

      } 
     } 
    } 


} 


/// <summary> 
/// First creates the TopRight value via using the last pixel in x range, and using the current Y value 
/// Then a Y loop is started 10 pixels down, within this loop is another X loop which scans the X range 
/// If 10 consecutive white pixels are found, the TopLeft X value and the current Y value are used to map the 
/// BottomLeft and BottomRight coordinates. Finally a new Point is created which starts (x = 1) and (y = currentYValue + 2) 
/// The ScanPixelsLater method is then called, passing the new Point (newLocation). 
/// 
/// </summary> 
/// <param name="p">The x and y value of where the pixels were found</param> 
/// <param name="img">Image being used</param> 
private void DetectNextWhiteLine(Point p, Bitmap img) 
{ 

    int whitePixel = 0; 
    topRightc = new Point(img.Width, topLeftc.Y); 

    for (int y = p.Y + 10; y < img.Height; y++) 
    { 

     for (int x = p.X - 5; x < img.Width; x++) 
     { 
      if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255)) 
      { 
       whitePixel++; 
      } 
      else 
      { 
       whitePixel = 0; 
      } 

      if (whitePixel == 10) 
      { 
       bottomLeftc = new Point(topLeftc.X, y); 
       bottomRightc = new Point(img.Width, y); 
       Cords.Add(new Coordinates(topLeftc, topRightc, bottomLeftc, bottomRightc)); 

       Point newLocation = new Point(1, y + 2); 
       calls++; 
       ScanPixelsLater(newLocation, img); //rescan the image from new y axis 
      } 
     } 
    } 

} 



/// <summary> 
/// Loops through the pixels based on the p parameter, if 15 white pixels are found, the location (x & y) is 
/// passed to the DetectNextWhiteLine method which fixes the next line with a similar number of white pixels. 
/// </summary> 
/// <param name="p">The Point(x,y) at which to start scanning</param> 
/// <param name="img"></param> 
private void ScanPixelsLater(Point p, Bitmap img) 
{ 

    int whitePixel = 0; 

    for (int y = p.Y; y < img.Height; y++) 
    { 

     for (int x = p.X; x < img.Width; x++) 
     { 
      if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255)) 
      { 
       whitePixel++; 
      } 
      else 
      { 
       whitePixel = 0; 
      } 

      if (whitePixel == 15) 
      { 
       bottomLeftc = new Point(topLeftc.X, y); 
       topLeftc = new Point(x - 15, y); 
       calls++; 
       DetectNextWhiteLine(topLeftc, img); 
      } 

     } 
    } 

    // only want this to execute after all the pixels within the entire img have been read 
    // possibly executing early. 

    DrawWhiteLines(img); 
    AppArgs aa = new AppArgs(true); 
    Change(this, aa); // custom event handler, fired behind form to update GUI 

} 

答えて

2

をコメントしました。

UIが実行されているスレッドには、メッセージポンプと呼ばれるものもあります。このメッセージポンプには、オペレーティングシステム(および他のソース)からすべてのUI要素に渡されるメッセージが含まれています。彼らは、状態を変更する時期、自分自身を再描画する時期などを教えます。あなたのような長いループがあるときは、メッセージポンプはメッセージを処理できません。待ち行列に入れられますが、決して処理されません。これが、アプリケーションが「ハングする」ことを意味します。

アプリケーションが回復しないことはほとんどありません。あなたのループは最終的に終了し、あなたのUIは再び応答するようになります(私は無限ループをどこかで見逃していないと仮定していますが、私はそうは思わない)。しかし、GDI + GetPixelメソッドは実際には非常に遅く、イメージが大規模であれば、一連のループは完了するまでに長い時間がかかります。おそらく、安全でないコンテキストを掘り下げ、LockBitsを使ってイメージのメモリへのポインタを取得する必要があります。ここに浮かべる方法の例がたくさんあります。

編集:あなたのコードをもう少し詳しく見ると、それは比較的非効率的であることも明らかです。少なくとも6レベルのネストされたforループがあるので、基本的に1回のスキャンだけが必要な場合は、イメージを複数回スキャンします。

イメージ処理はリソース集約的なプロセスです。できるだけ効率的にパフォーマンス集中型の作業をすべて行うように注意する必要があります。私のコメントから移行

+0

私は本当に上記の方法を考えています。これは、私が達成しようとしていることがうまくいくなら、「最も効果的な」方法です。私はすべてのループを嫌い、私は離れて考え続けます:) – Ash

2

(現在削除)

それが通常に来ることはありませんので、あなたにトラブルを起こしていますがループ内でGetPixelを呼び出すことはありませんどの適切にこれではありません。それを使用することは信じられないほど遅いです。代わりにポインタやピクセル配列を使うことができますgoogleやstackoverflowで "getpixel slow"を検索し、多数のソリューションが登場します。更新

私もforループのネストされた、最終的には、このある今... forループネストされているメインコード(ScanPixels)では、あなたはDetectNextWhiteLineを呼び出すコードを少し見てきましたScanPixelsLaterもネストされたfor-loopです。今度は、比較的高価なメソッド(GetPixel)を呼び出す、6レベルのネストされたforループO(n^6)を取得する可能性があります。ピクセルを何度か反復する必要があります。これは1000 x 6 *〜100の指示のような潜在的なものなので、決して止まらない理由かもしれません。

+0

@lasseespeholt:C#はポインタ操作をサポートしており、Bitmap/Imageクラスで公開されています。 – leppie

+0

@leppie私は自分自身を不明瞭にしていると思う(私の英語力のおかげで)。私はそれがポインタをサポートしていないことを意味しませんでした(それは、stackoverflowとGoogleのソリューションを探します)しかし、私がチェックした最後に、WPF WriteableBitmapのようなピクセル配列を公開していませんでした。だから、それは文の最後の部分だけでした。 –

+2

@lasseespeholt:まだ分かりません。機能は 'System.Drawing'にあります。 'Bitmap.LockBits'メソッドを見てください。 http://msdn2.microsoft.com/en-us/library/system.drawing.bitmap.lockbitsしかし、コードをMSDN上の例として使用することは決してありません。それはちょっとばかりのオーバーヘッドです。 – leppie

関連する問題