2012-12-21 4 views
3

이미지의 포커스 값을 계산하는 코드를 작성했습니다. 하지만 5 초 이상 걸립니다.루프를 병렬 루프로 변환

public double GetFValue(Image image) 
     { 
      Bitmap source = new Bitmap(image); 
      int count = 0; 
      double total = 0; 
      double totalVariance = 0; 
      double FM = 0; 
      Bitmap bm = new Bitmap(source.Width, source.Height); 
      Rectangle rect = new Rectangle(0,0,source.Width,source.Height); 
       Bitmap targetRect = new Bitmap(rect.Width, rect.Height); 

      // converting to grayscale 
      for (int y = 0; y < source.Height; y++) 
      { 
       for (int x = 0; x < source.Width; x++) 
       { 
        count++; 
        Color c = source.GetPixel(x, y); 
        int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11); 
        source.SetPixel(x, y, Color.FromArgb(luma, luma, luma)); // the image is now gray scaled 
        var pixelval = source.GetPixel(x, y); 
        // targetRect.Save(@"C:\Users\payam\Desktop\frame-42-rectangle.png", System.Drawing.Imaging.ImageFormat.Png); 
        int pixelValue = pixelval.G; 
        total += pixelValue; 
        double avg = total/count; 
        totalVariance += Math.Pow(pixelValue - avg, 2); 
        double stDV = Math.Sqrt(totalVariance/count); // the standard deviation, which is also the focus value 
        FM = Math.Round(stDV, 2); 
       } 
      } 
      return FM; 
     } 

이 코드를 병렬 계산으로 변환하려고합니다. 나는 그들 주위에 내 머리를 얻을 수없는 버그로 끝납니다. 어떠한 제안?

public double CalculateFvalue (Image image) 
    { 
     Bitmap myimage = new Bitmap(image); 
     int count = 0; 
     int total = 0; 
     double totalVariance = 0; 
     double FM = 0; 
     Parallel.For(0, image.Height, y => 
      { 

      for (int x = 0; x < myimage.Width; x++) 
        { 
          count++; 
          Color c = myimage.GetPixel(x, y); 
          int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11); 
          myimage.SetPixel(x, y, Color.FromArgb(luma, luma, luma)); // the image is now gray scaled 
          var pixelval = myimage.GetPixel(x, y); 
          int pixelValue = pixelval.G; 
          total += pixelValue; 
          double avg = total/count; 
          totalVariance += Math.Pow(pixelValue - avg, 2); 
          double stDV = Math.Sqrt(totalVariance/count); // the standard deviation, which is also the focus value 
          FM = Math.Round(stDV, 2); 
        } 


    }); 

     return Math.Round(FM,2); 
    } 
+0

보고있는 버그는 무엇입니까? –

+5

예, 전체 스레드 안전 문제가 있습니다. 하지만 어쨌든, getpixel은 매우 느리며, 대신 lockbits를 조사합니다. – Steve

+2

다른 스레드에서'count','total' 및'FM'을 동시에 업데이트하고 있습니다. 각 루프 반복에서 이들에 대한 지역 값을 계산 한 다음 끝에 결합해야합니다. – Lee

답변

1

내 의견을 확장하려면 GetPixel을 병렬로 실행하지 말고 lockBits를 대신 사용하십시오.

에 코드를 사용하여 lockbits : WIN7 샘플 사진에서 1024 X 768 JPG를 사용하여 빠른 테스트에서

public double GetFValue(Image image) 
    { 
     Bitmap source = new Bitmap(image); 
     int count = 0; 
     double total = 0; 
     double totalVariance = 0; 
     double FM = 0; 
     Bitmap bm = new Bitmap(source.Width, source.Height); 
     Rectangle rect = new Rectangle(0, 0, source.Width, source.Height); 
     //Bitmap targetRect = new Bitmap(rect.Width, rect.Height); 

     //new 
     ///* 
     BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadWrite, source.PixelFormat); 
     int[] pixelData = new int[(rect.Height * rect.Width) -1]; 
     System.Runtime.InteropServices.Marshal.Copy(bmd.Scan0, pixelData, 0, pixelData.Length); 

     for (int i = 0; i < pixelData.Length; i++) 
     { 
      count++; 
      Color c = Color.FromArgb(pixelData[i]); 
      int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11); 
      //Probably a formula for this 
      pixelData[i] = Color.FromArgb(luma, luma, luma).ToArgb(); 
      total += luma; 
      double avg = total/count; 
      totalVariance += Math.Pow(luma - avg, 2); 
      double stDV = Math.Sqrt(totalVariance/count); 
      FM = Math.Round(stDV, 2); 
     } 
     source.UnlockBits(bmd); 
     return FM; 
    } 

(Chrysanthemum.jpg) :

lockbits : 241 밀리 초

getPixel와 : 2208 밀리 초

코드를 변환 할 때 (getpixel, setpixel, getpixel 같은 픽셀에 대한 것 같은) 몇 가지 이상한 점을주의하십시오.하지만 당신은 k 이제 달성하고자하는 것은 무엇이며,이 코드는 귀하와 정확히 동일합니다.

+0

이것은 확실히 좋은 조언이지만 순차적 인 구현에도 똑같이 적용됩니다. 이브의 대답은 실제로 원래의 질문과 더 관련이 있습니다. 공유 상태는 코드를 병렬 처리 할 때 항상 나쁜 생각입니다. –

+0

당신은 바위 사람, 이것은 완벽하게 일했습니다. for 루프 부분을 병렬 코드로 변환하고 여기에 최종 결과를 전달합니다. 마지막으로이 코드는 이미지를 회색조로 변환 한 다음 비트 맵 행렬에 표준 편차를 사용하여 이미지의 초점을 측정합니다. 한 가지 방법은 이미지의 초점을 측정하는 다른 많은 방법이 있습니다. 이것은 내가 작업하고있는 흥미로운 프로젝트이며 나중에 더 자세하게 게시 할 것입니다. – user843681

7

Parallel.For의 범위를 벗어난 것으로 선언 한 변수 때문에 발생합니다. 액세스 및 기록이 비 결정적이기 때문에 잘못된 데이터로 값을 덮어 쓸 수 있습니다 (예 : FM).

모든 반복을 결과에 대한 수익률 정보로 만든 다음 수집 된 데이터를 사용하여 외부 변수를 스레드로부터 안전하게 조작 할 것을 제안합니다. 몇 가지 lock 문을 사용하여 문제를 해결할 수도 있지만 개인적으로이를 피할 수 있습니다.

+0

감사합니다. 귀하의 제안에 따라 노력할 것입니다. 어떻게 진행되는지 알려 드리겠습니다. – user843681

+0

+1 : 또한 * 동기화하지 않고 * 비트 맵을 수정하지 않도록하십시오. 현재 코드는'SetPixel'을 비 threadsafe 방식으로 사용합니다. (성능에 문제가있는 경우 앞서 언급 한 Get/Set 픽셀 호출을 사용하면 안됩니다.) –

관련 문제