2016-06-25 5 views
0

Ich möchte durchschnittliche Pixelfarbe aus einem bestimmten Bereich des Bildschirms berechnen. Ich mache eine LED-Hintergrundbeleuchtung für mein Fernsehgerät, also muss es sehr schnell sein. Mindestens 30 Bilder pro Sekunde. Bitmap.GetPixel() ist zu langsam dafür. Ich habe eine OpenGL-Methode Gl.ReadPixels() gefunden, aber ich weiß nicht, wie es funktioniert. Es empfängt int [] als zurückzusendende Daten. Aber nach dem Aufruf dieser Methode bekomme ich nur ein Array mit 0. Wie kann ich diese Methode verwenden? Oder gibt es vielleicht einen anderen Weg für diese Aufgabe?Wie kann ich Pixel aus einem bestimmten Bereich des Bildschirms in C# sehr schnell erhalten?

+0

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

+0

'glReadPixels' funktioniert nur für Bilder, die mit OpenGL-Renderkontext gezeichnet wurden, mit dem 'glReadPixels' aufgerufen wird. Sie können (zuverlässig) 'glReadPixels' nicht verwenden, um beliebige Bildschirminhalte zu lesen. – datenwolf

Antwort

0

Bei weitem der schnellste Weg, Bilder in C# zu verarbeiten, ist pinvoke mit Win32 Gdi32 und User32-Funktionalität.

Hier ist ein C# -Code, der den aktuellen Desktop erfasst. Optional können Sie den Dateipfad eines "Masken" -Bildes übergeben, mit dem eine Region des Desktop-Captures isoliert werden kann. In der Maske wird Texel, das schwarz ist (mit Alpha 0), von der Bildschirmaufnahme verworfen. Es ist ziemlich rudimentär, da es nur 32-Bit-Bilder unterstützt und die Maske und die Bildschirmaufnahme dieselbe Größe haben müssen. Sie können es auf Ihre eigene Weise verbessern, oder Sie können sehen, wie die Bildverarbeitung auf einer Texel-by-Texelbasis erfolgt, indem Sie direkt in die Win32-Funktionen mit pinvoke einhaken (siehe Konstruktor).

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); 
     } 
    } 

Es wirft Ausnahmen, also achten Sie darauf, sie zu fangen. Zur Nutzung ...

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

Graphics.CopyFromScreen ist wahrscheinlich schneller (keine zusätzlichen Kopien) und viel einfacher zu verwenden. – lexx9999

0

Werfen Sie einen Blick auf Bitmap.LockBits https://msdn.microsoft.com/de-de/library/5ey6h79d(v=vs.110).aspx

einige grundlegende Idee

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); 
    } 

erlauben unsicheren Code zu liefern (vorsichtig), um zusätzliche Speicherkopien zu vermeiden.

Verwandte Themen