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?
Antwort
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");
Graphics.CopyFromScreen ist wahrscheinlich schneller (keine zusätzlichen Kopien) und viel einfacher zu verwenden. – lexx9999
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.
- 1. sehr schnell Gesamtgröße des Ordners
- 2. C# -Arrays Minimalwert in einem bestimmten Bereich
- 3. RMagick: Wie überprüft man, ob alle Pixel in einem bestimmten Bereich des Bildes transparent sind?
- 4. Wie kann ich den aktuellen Sitzungsnamen des Bildschirms erhalten?
- 5. C++ SDL - Render in einem bestimmten Bereich
- 6. OpenCV: Wie man jedes Pixel in einem bestimmten Bereich eines Bildes iteriert
- 7. Wie kann ein Schlüsselcode vorübergehend gelöscht werden, wenn sich ein Objekt in einem bestimmten Bereich des Bildschirms befindet?
- 8. Detect neu gestrichenen Bereich des Bildschirms in Java
- 9. Anzahl der Zeilenfrequenz in einem bestimmten Bereich
- 10. Abrufen des Namens meines Bildschirms in C#
- 11. Wie kann ich die Länge der Zeichenfolge für eine Seite des Bildschirms erhalten?
- 12. Get native Auflösung des Bildschirms
- 13. Kann ich Zeitstempel in einem bestimmten Format erhalten
- 14. Den nächsten nicht schwarzen Pixel in einem Bild schnell finden
- 15. Wie sicher sind Informationen außerhalb des Bildschirms?
- 16. Erzeuge zufällige Punkte in einem bestimmten Bereich
- 17. Crop inkCanvas Zeichnung aus einem bestimmten Bereich, Windows Universal App
- 18. Div Gesamtfläche des Bildschirms
- 19. Wie kann ich TableView-Elemente in einem Wörterbuch schnell gruppieren?
- 20. Nehmen Sie Screenshot von bestimmten Teil des Bildschirms
- 21. Wie kann ich alles nach einem bestimmten Index in Zeichenfolge erhalten C#
- 22. Wie testen, ob die Variable in einem bestimmten Bereich ist?
- 23. Video auf einem Teil des Bildschirms wiedergeben
- 24. Wie kann ich 4 maximalen Wert aus einem Array erhalten
- 25. Pixel des Bildes in C++ lesen
- 26. Wie kann ich ein Div die Höhe des Bildschirms anpassen?
- 27. So erhalten Sie die letzte Zeile aus der Spalte in einem bestimmten Bereich
- 28. Wie kann ich jedes Pixel in einem GIF-Bild durchlaufen?
- 29. Wie kann ich etwas in Bildschirm-Pixel-Koordinaten in einem OnDrawGizmos in Unity3D zeichnen?
- 30. Geplante Linie in einem bestimmten Bereich R
https://www.opengl.org/wiki/Pixel_Buffer_Object –
'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