C# 从屏幕捕获图像并获取颜色
我正在制作一个程序,它可以捕捉屏幕上的一个小区域,如果图像上有与目标颜色匹配的颜色,它就会运行。我的程序按以下顺序运行:C# 从屏幕捕获图像并获取颜色,c#,C#,我正在制作一个程序,它可以捕捉屏幕上的一个小区域,如果图像上有与目标颜色匹配的颜色,它就会运行。我的程序按以下顺序运行: 从屏幕上的特定区域获取图像 保存到文件夹 使用CountPixel检测任何目标颜色 但是,在我单击按钮5两次(不是双击)后,它会通过下面一行的异常: b、 保存(@“C:\Applications\CaptureImage000.jpg”,ImageFormat.Jpeg) 例外情况: 类型的未处理异常 中出现“System.Runtime.InteropServices.E
CountPixel
检测任何目标颜色
但是,在我单击按钮5
两次(不是双击)后,它会通过下面一行的异常:
b、 保存(@“C:\Applications\CaptureImage000.jpg”,ImageFormat.Jpeg)CountPixel()
,以提高性能,因为我只需要检测一种目标颜色即可升高事件(@“C:\Applications\CaptureImage000.jpg”,ImageFormat.Jpeg)
,因为使用这个长字符串不舒服,并且在尝试使用GetPixel
时会出现错误,。。。或者将其添加到互联网上的“价值示例”代码中进行改进
private int CountPixels(Bitmap bm, Color target_color)
{
// Loop through the pixels.
int matches = 0;
for (int y = 0; y < bm.Height; y++)
{
for (int x = 0; x < bm.Width; x++)
{
if (bm.GetPixel(x, y) == target_color) matches++;
}
}
return matches;
}
private Bitmap CapturedImage(int x, int y)
{
Bitmap b = new Bitmap(XX, YY);
Graphics g = Graphics.FromImage(b);
g.CopyFromScreen(x, y, 0, 0, new Size(XX, YY));
b.Save(@"C:\Applications\CaptureImage000.jpg", ImageFormat.Jpeg);
/* Run 3 line below will lead to question 1 - through exception
Bitmap bm = new Bitmap(@"C:\Applications\CaptureImage000.jpg");
int black_pixels = CountPixels(b, Color.FromArgb(255, 0, 0, 0));
textBox3.Text = black_pixels + " black pixels";
*/
return b;
}
private void button5_Click(object sender, EventArgs e)// Do screen cap
{
Bitmap bmp = null;
bmp = CapturedImage(X0, Y0);
}
private int CountPixels(位图bm、颜色目标\u颜色)
{
//循环通过像素。
int匹配=0;
对于(int y=0;y
[编辑]今晚与OP合作完成了这项工作,做了一些改进 现在,这可以解释机器的端点,并通过使用Color.ToArgb()函数将颜色转换为整数来正确比较颜色 下面的代码将起作用,为了清晰起见,我添加了注释,并为您提供了一些选项。我在没有IDE的情况下编写了代码,但我相信它很好 在以下两种情况下,只需保留位图的句柄,无需保存并重新打开,无论是否需要副本 CapturedImage函数的异常问题和改进 选项A(推荐) 不要保存位图,您已经有了一个句柄,图形对象只是修改了BMP。只需保留此函数的以下代码,它就可以正常工作,而无需对其他选项之一进行注释 代码和其他选项:
private Bitmap CapturedImage(Bitmap bm, int x, int y)
{
Bitmap b = new Bitmap(XX, YY);
Graphics g = Graphics.FromImage(b);
g.CopyFromScreen(x, y, 0, 0, new Size(XX, YY));
//option B - If you DO need to keep a copy of the image use PNG and delete the old image
/*
try
{
if(System.IO.File.Exists(@"C:\Applications\CaptureImage.png"))
{
System.IO.File.Delete(@"C:\Applications\CaptureImage.png");
}
b.Save(@"C:\Applications\CaptureImage.png", ImageFormat.Png);
}
catch (System.Exception ex)
{
MessageBox.Show("There was a problem trying to save the image, is the file in open in another program?\r\nError:\r\n\r\n" + ex.Message);
}
*/
//option C - If you DO need to keep a copy of the image AND keep all copies of all images when you click the button use PNG and generate unique filename
/*
int id = 0;
while(System.IO.File.Exists(@"C:\Applications\CaptureImage" + id.ToString().PadLeft('0',4) + ".png"))
{
//increment the id until a unique file name is found
id++;
}
b.Save(@"C:\Applications\CaptureImage" + id.ToString().PadLeft('0',4) + ".png", ImageFormat.Png);
*/
int black_pixels = CountPixels(b, Color.FromArgb(255, 0, 0, 0));
textBox3.Text = black_pixels + " black pixels";
return b;
}
现在对于CountPixels函数,您有3个选项,但实际上,您有一个非常实心的选项,所以我省略了其他选项
这将锁定BMP中的位,使用编组将数据复制到数组中,并以非常非常快的速度扫描数组中的数据,您甚至可能不需要删除计数。如果仍然要删除计数,只需在其递增matches变量的正下方添加“return 1;”
CountPixels函数的速度问题和改进
private int CountPixels(位图bm、颜色目标\u颜色)
{
int匹配=0;
位图bmp=(位图)bm.Clone();
BitmapData bmpDat=bmp.LockBits(新矩形(0,0,bmp.Width,bmp.Height),ImageLockMode.ReadWrite,bmp.PixelFormat);
int size=bmpDat.Stride*bmpDat.Height;
字节[]子字节=新字节[大小];
System.Runtime.InteropServices.Marshal.Copy(bmpDat.Scan0,subPx,0,size);
//如果没有alpha通道,请将4(ARGB)更改为3(RGB),这适用于32bpp图像
//三值运算符检查机器的端部,并将像素颜色组织为A、R、G、B或B、G、R、A(小端部反转);
Color temp=BitConverter.IsLittleEndian?Color.FromArgb(subPx[i+2],subPx[i+1],subPx[i]):Color.FromArgb(subPx[i+1],subPx[i+2],subPx[i+3]);
对于(inti=0;i
最后,使用相同的函数,但允许公差百分比
private int CountPixels(位图bm、颜色目标\u颜色、浮点公差百分比)
{
int匹配=0;
位图bmp=(位图)bm.Clone();
BitmapData bmpDat=bmp.LockBits(新矩形(0,0,bmp.Width,bmp.Height),ImageLockMode.ReadWrite,bmp.PixelFormat);
int size=bmpDat.Stride*bmpDat.Height;
字节[]子字节=新字节[大小];
System.Runtime.InteropServices.Marshal.Copy(bmpDat.Scan0,subPx,0,size);
对于(int i=0;i private int CountPixels(Bitmap bm, Color target_color)
{
int matches = 0;
Bitmap bmp = (Bitmap)bm.Clone();
BitmapData bmpDat = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
int size = bmpDat.Stride * bmpDat.Height;
byte[] subPx = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(bmpDat.Scan0, subPx, 0, size);
//change the 4 (ARGB) to a 3 (RGB) if you don't have an alpha channel, this is for 32bpp images
//ternary operator to check endianess of machine and organise pixel colors as A,R,G,B or B,G,R,A (little endian is reversed);
Color temp = BitConverter.IsLittleEndian ? Color.FromArgb(subPx[i + 2], subPx[i + 1], subPx[i]) : Color.FromArgb(subPx[i + 1], subPx[i + 2], subPx[i + 3]);
for (int i = 0; i < size; i += 4 ) //4 bytes per pixel A, R, G, B
{
if(temp.ToArgb() == target_color.ToArgb())
{
matches++;
}
}
System.Runtime.InteropServices.Marshal.Copy(subPx, 0, bmpDat.Scan0, subPx.Length);
bmp.UnlockBits(bmpDat);
return matches;
}
private int CountPixels(Bitmap bm, Color target_color, float tolerancePercent)
{
int matches = 0;
Bitmap bmp = (Bitmap)bm.Clone();
BitmapData bmpDat = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
int size = bmpDat.Stride * bmpDat.Height;
byte[] subPx = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(bmpDat.Scan0, subPx, 0, size);
for (int i = 0; i < size; i += 4 )
{
byte r = BitConverter.IsLittleEndian ? subPx[i+2] : subPx[i+3];
byte g = BitConverter.IsLittleEndian ? subPx[i+1] : subPx[i+2];
byte b = BitConverter.IsLittleEndian ? subPx[i] : subPx[i+1];
float distancePercent = (float)Math.Sqrt(
Math.Abs(target_color.R-r) +
Math.Abs(target_color.G-g) +
Math.Abs(target_color.B-b)
) / 7.65f;
if(distancePercent < tolerancePercent)
{
matches++;
}
}
System.Runtime.InteropServices.Marshal.Copy(subPx, 0, bmpDat.Scan0, subPx.Length);
bmp.UnlockBits(bmpDat);
return matches;
}