我正在制作一个程序,该程序可以捕获屏幕上的一小块区域,并且如果图像上有与目标颜色匹配的任何颜色,它将运行某些程序。我的程序按以下顺序运行:
从屏幕上的特定区域获取图像
保存到文件夹
使用CountPixel
检测任何target_color
但是,我两次单击button5
(而不是双击)之后,它通过下面一行的异常:
b.Save(@“ C:\ Applications \ CaptureImage000.jpg”,ImageFormat.Jpeg);
例外:
类型的未处理异常 “ System.Runtime.InteropServices.ExternalException”发生在 System.Drawing.dll
其他信息:GDI +中发生了一般错误
我的问题是:
CountPixel()
来提高性能,因为我只需要检测一种目标颜色即可引发事件。步骤2很麻烦。我想知道是否可以跳过它并使用其他方式调用:(@"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);
}
答案 0 :(得分:0)
[EDIT]今晚与OP合作,做了一些改进 现在可以说明机器的字节顺序,并通过使用Color.ToArgb()函数将颜色转换为整数来正确比较颜色
下面的代码将起作用,为清楚起见,我添加了注释,并提供了一些选项。我在没有IDE的情况下编写了代码,但我相信它会很好。
在以下两种情况下,只需保留位图的句柄,无论是否需要复制,都无需保存并重新打开。
选项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中的位,使用编组将数据复制到阵列中并非常非常快地扫描阵列中的数据,您甚至可能不需要删除计数。如果仍然要删除计数,则只需添加“ return 1;”即可。就在将matchs变量递增的下方。
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;
}