我想計算屏幕特定區域的平均像素顏色。我爲我的電視製作LED背光,因此它需要非常快。至少30fps。 Bitmap.GetPixel()對它來說太慢了。我發現了一個OpenGL方法Gl.ReadPixels(),但我不知道它是如何工作的。它接收int []作爲要返回的數據。但是在調用這個方法之後,我只能得到一個滿數爲0的數組。我如何使用這種方法?或者也許還有其他的方式來完成這項任務?如何從C#中的屏幕特定區域獲得像素非常快?
0
A
回答
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);
}
允許不安全的代碼(小心),以避免額外的內存拷貝。
相關問題
- 1. ASP.NET - 獲取屏幕特定區域的屏幕截圖?
- 2. 屏幕捕獲OCR屏幕的特定區域
- 3. 如何從python的屏幕給定區域拍攝快照?
- 4. 如何在當前屏幕的ios獲得特定區域的UIImage
- 5. 非常快的像素在屏幕上繪圖
- 6. 放大屏幕的特定區域
- 7. (C#)如何獲得我的屏幕點到像素關係?
- 8. 如何在特定區域找到非零像素Mat
- 9. 如何在C中清除屏幕區域或重繪屏幕#
- 10. iPhone屏幕截圖特定區域
- 11. 如何從Javascript中的屏幕區域創建圖像?
- 12. 如何獲得特定區域的時區中的Android
- 13. C#打印屏幕預定義區域
- 14. 如何讓相機出現在android的特定屏幕區域?
- 15. 如何創建特定區域的屏幕截圖?
- 16. 如何從陣列緩衝區畫布中的像素非常快?
- 17. 如何在Xcode屏幕上捕獲指定的區域屏幕截圖
- 18. 如何獲得墊圖像的像素區域
- 19. AS3:如何清除特定像素/區域中的圖形
- 20. 如何獲得橢圓內像素的座標? (屏幕截圖)
- 21. 如何獲得XNA屏幕的每英寸像素值?
- 22. 如何獲得谷歌地圖中的特定區域座標
- 23. 從圖像中選擇特定區域
- 24. 如何獲得android的這個特定的屏幕布局?
- 25. 在SDL中升級屏幕以獲得像素化外觀的更快方法?
- 26. 如何從屏幕上讀取像素?
- 27. 如何讓屏幕上的顏色更好,然後獲得像素? (C++)
- 28. 如何在Android中的屏幕的特定區域打開PDF文件
- 29. 如何從陣列中刪除元素的特定區域
- 30. 如何從圖像中獲取區域?
https://www.opengl.org/wiki/Pixel_Buffer_Object –
'glReadPixels'僅適用於使用OpenGL渲染上下文繪製的圖像'glReadPixels'被調用。您不能使用(可靠地)使用'glReadPixels'來讀取任意屏幕內容。 – datenwolf