2016-06-25 39 views
0

我想計算屏幕特定區域的平均像素顏色。我爲我的電視製作LED背光,因此它需要非常快。至少30fps。 Bitmap.GetPixel()對它來說太慢了。我發現了一個OpenGL方法Gl.ReadPixels(),但我不知道它是如何工作的。它接收int []作爲要返回的數據。但是在調用這個方法之後,我只能得到一個滿數爲0的數組。我如何使用這種方法?或者也許還有其他的方式來完成這項任務?如何從C#中的屏幕特定區域獲得像素非常快?

+0

https://www.opengl.org/wiki/Pixel_Buffer_Object –

+0

'glReadPixels'僅適用於使用OpenGL渲染上下文繪製的圖像'glReadPixels'被調用。您不能使用(可靠地)使用'glReadPixels'來讀取任意屏幕內容。 – datenwolf

回答

0

到目前爲止,在C#中處理圖像的最快方式是通過使用Win32 Gdi32和User32功能的pinvoke

這是一些捕獲當前桌面的C#代碼。或者,您可以傳遞可用於隔離桌面捕獲區域的'mask'圖像的文件路徑。在面具中,黑色(帶有alpha 0)的紋理將從屏幕抓取中丟棄。這是相當原始的,因爲它只支持32位圖像,並要求掩碼和屏幕捕獲大小相同。您可以用自己的方式對其進行改進,或者您可以通過使用pinvoke(請參閱構造函數)直接掛接到Win32函數來了解如何通過texel進行圖像處理。

public class ScreenCapture 
{ 
    private System.Drawing.Bitmap capture; 

    /// <summary> 
    /// Constructor 
    /// </summary> 
    /// <param name="maskFile">Optional mask file, use "" for no mask</param> 
    public ScreenCapture(string maskFile) 
    { 
     // capture the screen 
     capture = CaptureWindow(User32.GetDesktopWindow()); 

     // if there is a mask file, load it and apply it to the capture 
     if (maskFile != "") 
     { 
      string maskfilename = maskFile + ".png"; 
      System.Drawing.Bitmap maskImage = System.Drawing.Image.FromFile(maskfilename) as Bitmap; 

      // ensure mask is same dim as capture... 
      if ((capture.Width != maskImage.Width) || (capture.Height != maskImage.Height)) 
       throw new System.Exception("Mask image is required to be " + capture.Width + " x " + capture.Height + " RGBA for this screen capture"); 

      Rectangle rectCapture = new Rectangle(0, 0, capture.Width, capture.Height); 
      Rectangle rectMask = new Rectangle(0, 0, maskImage.Width, maskImage.Height); 

      System.Drawing.Imaging.BitmapData dataCapture = capture.LockBits(rectCapture, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); 
      System.Drawing.Imaging.BitmapData dataMask = maskImage.LockBits(rectCapture, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); 

      IntPtr ptrCapture = dataCapture.Scan0; 
      IntPtr ptrMask = dataMask.Scan0; 

      int size = Math.Abs(dataCapture.Stride) * capture.Height; 

      byte[] bitsCapture = new byte[size]; 
      byte[] bitsMask = new byte[size]; 

      System.Runtime.InteropServices.Marshal.Copy(ptrCapture, bitsCapture, 0, size); 
      System.Runtime.InteropServices.Marshal.Copy(ptrMask, bitsMask, 0, size); 

      // check each pixel of the image... if the mask is 0 for each channel, set the capture pixel to 0. 
      for (int n = 0; n < size; n += 4) 
      { 
       bool clearPixel = true; 
       for (int c = 0; c < 4; ++c) 
       { 
        // if any pixel of the mask is not black, do not clear the capture pixel 
        if (bitsMask[n + c] != 0) 
        { 
         clearPixel = false; 
         break; 
        }  
       } 
       if (clearPixel) 
       { 
        bitsCapture[n] = 0; 
        bitsCapture[n + 1] = 0; 
        bitsCapture[n + 2] = 0; 
        bitsCapture[n + 3] = 0; 
       } 
      } 

      System.Runtime.InteropServices.Marshal.Copy(bitsCapture, 0, ptrCapture, size); 
      System.Runtime.InteropServices.Marshal.Copy(bitsMask, 0, ptrMask, size); 
      capture_.UnlockBits(dataCapture); 
      maskImage.UnlockBits(dataMask); 
      } 
     } 

     /// <summary> 
     /// Creates an Image object containing a screen shot of a specific window 
     /// </summary> 
     /// <param name="handle">The handle to the window. 
     /// (In windows forms, this is obtained by the Handle property)</param> 
     /// <returns>Screen grab image</returns> 
     private System.Drawing.Bitmap CaptureWindow(IntPtr handle) 
     { 
      // get te hDC of the target window 
      IntPtr hdcSrc = User32.GetWindowDC(handle); 

      // get the size 
      User32.RECT windowRect = new User32.RECT(); 
      User32.GetWindowRect(handle, ref windowRect); 
      int width = windowRect.right - windowRect.left; 
      int height = windowRect.bottom - windowRect.top; 

      // create a device context we can copy to 
      IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc); 

      // create a bitmap we can copy it to, 
      // using GetDeviceCaps to get the width/height 
      IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height); 

      // select the bitmap object 
      IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap); 

      // bitblt over 
      GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY); 

      // restore selection 
      GDI32.SelectObject(hdcDest, hOld); 

      // clean up 
      GDI32.DeleteDC(hdcDest); 
      User32.ReleaseDC(handle, hdcSrc); 

      // get a .NET image object for it 
      System.Drawing.Bitmap img = System.Drawing.Image.FromHbitmap(hBitmap); 

      // free up the Bitmap object 
      GDI32.DeleteObject(hBitmap); 
      return img; 
     } 

     /// <summary> 
     /// Save this capture as a Png image. 
     /// </summary> 
     /// <param name="filename">File path and name not including extension</param> 
     public void SaveCapture(string filename) 
     { 
      capture.Save(filename + ".png", System.Drawing.Imaging.ImageFormat.Png); 
     } 

     /// <summary> 
     /// Helper class containing Gdi32 API functions 
     /// </summary> 
     private class GDI32 
     { 
      public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter 
      [DllImport("gdi32.dll")] 
      public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, 
       int nWidth, int nHeight, IntPtr hObjectSource, 
       int nXSrc, int nYSrc, int dwRop); 
      [DllImport("gdi32.dll")] 
      public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, 
       int nHeight); 
      [DllImport("gdi32.dll")] 
      public static extern IntPtr CreateCompatibleDC(IntPtr hDC); 
      [DllImport("gdi32.dll")] 
      public static extern bool DeleteDC(IntPtr hDC); 
      [DllImport("gdi32.dll")] 
      public static extern bool DeleteObject(IntPtr hObject); 
      [DllImport("gdi32.dll")] 
      public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); 
     } 

     /// <summary> 
     /// Helper class containing User32 API functions 
     /// </summary> 
     private class User32 
     { 
      [StructLayout(LayoutKind.Sequential)] 
      public struct RECT 
      { 
       public int left; 
       public int top; 
       public int right; 
       public int bottom; 
      } 
      [DllImport("user32.dll")] 
      public static extern IntPtr GetDesktopWindow(); 
      [DllImport("user32.dll")] 
      public static extern IntPtr GetActiveWindow(); 
      [DllImport("user32.dll")] 
      public static extern IntPtr GetWindowDC(IntPtr hWnd); 
      [DllImport("user32.dll")] 
      public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC); 
      [DllImport("user32.dll")] 
      public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect); 
     } 
    } 

它拋出異常,所以一定要抓住它們。要使用...

ScreenCapture grab = new ScreenCapture("screenGrabMask.png"); 
grab.SaveCapture("screenGrab.png"); 
+0

Graphics.CopyFromScreen可能會更快(不需要額外的副本)並且使用起來更容易。 – lexx9999

0

看看Bitmap.LockBits https://msdn.microsoft.com/de-de/library/5ey6h79d(v=vs.110).aspx

提供一些基本的想法

private unsafe void demo(Bitmap bm) 
    { 
     System.Drawing.Imaging.BitmapData D = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); 
     int stride = Math.Abs(D.Stride); 
     byte* pImg = (byte*)D.Scan0.ToPointer(); 
     for(int y = 0; y < bm.Height; y++) 
     { 
      byte* pRow = (byte*)(pImg + y * stride); 
      for(int x = 0; x < bm.Width; x++) 
      { 
       uint b = *pRow++; 
       uint g = *pRow++; 
       uint r = *pRow++; 
       pRow++; // skip alpha 
       // do something with r g b 
      } 
     } 
     bm.UnlockBits(D); 
    } 

允許不安全的代碼(小心),以避免額外的內存拷貝。

相關問題