写了个类似按键精灵的找图类。方便大家做UI测试的时候可以用
在Delphi源码有个叫BitmapData的一个找图找色组件性能非常好效果也很给力.一直想用C#实现但是最初实现的性能实在太糟糕了. 找个图片基本都是6 秒以上. 后来经过自己改进开unsafe 用指针后速度有了比较大的提升基本满足了做模拟外挂或者做UI测试的时候图像判断。现在公布出来给大家参考下 如果有更好的性能提升优化请在讨论下面留言 不胜感激。
目前只支持 24位的bmp 其他的就不支持了 源码在下面有时间可以自己改下 我机器配置如下:e31230 8G 找图片 在 1980 1080的图找 20*20的图片没设置背景透明色的情况下 100ms以内 算是满足做找图需求了。
使用如下:
//定义色差范围值
BGR bgr = new BGR();
bgr.B = 1;
BitmapDataFinder big = new BitmapDataFinder("C:\\b.bmp");
big.Name = "母图";
BitmapDataFinder targ = new BitmapDataFinder("C:\\a1.bmp");
targ.Name = "子图";
可以对图片设置背景色.有时候子图 是有透明的地方的,这里用到 BackColor 设置一个颜色为透明色 颜色类型为BGR
targ.BackColor=;
Stopwatch watch = new Stopwatch();
int x = 0, y = 0;
watch.Start();
bool b = big.Find(targ, bgr, ref x, ref y);
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString() + " " + x.ToString() + "," + y.ToString());
/// <summary>
/// 三色分量
/// </summary>
public struct BGR
{
public byte B { get; set; }
public byte G { get; set; }
public byte R { get; set; }
public static bool operator ==(BGR c1, BGR c2)
{
return c1.R == c2.R && c1.B == c1.B && c1.G == c2.G;
}
public static bool operator !=(BGR c1, BGR c2)
{
return !(c1.R == c2.R && c1.B == c1.B && c1.G == c2.G);
}
}
/// <summary>
/// 图片快速搜索类(大图找小图)
/// </summary>
public class BitmapDataFinder
{
const int BD_BYTECOUNT = 3;
/// <summary>
/// 名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 位图宽度(象素)
/// </summary>
public int Width { get; private set; }
/// <summary>
/// 位图高度(象素)
/// </summary>
public int Height { get; private set; }
private BGR _backColor;
/// <summary>
/// 背景颜色(BGR格式)
/// </summary>
public BGR BackColor { get { return _backColor; } set { _backColor = value; IsEnableBackColor = true; } }
/// <summary>
/// 对齐后每行数据宽度(字节)
/// </summary>
public int LineWidth { get; private set; }
/// <summary>
/// 对齐后每行数据多余宽度(字节)
/// </summary>
public int OffsetWidth { get; private set; }
/// <summary>
/// 位图数据长度
/// </summary>
public int Length { get; private set; }
/// <summary>
/// 缓冲区实际长度(目前未用到,和位图数据长度一致)
/// </summary>
public int BufSize { get; private set; }
private byte[] _bits;
/// <summary>
/// 位图数据缓冲区
/// </summary>
public byte[] Bits { get { return _bits; } private set { _bits = value; } }
/// <summary>
/// 是否开启了背景色
/// </summary>
public bool IsEnableBackColor { get; set; }
public BitmapDataFinder(string path)
{
if (!File.Exists(path))
throw new Exception(path + " 文件无法找到!");
IniteData(new Bitmap(path));
}
public BitmapDataFinder(Bitmap bmp)
{
IniteData(bmp);
}
/// <summary>
/// 初始化数据
/// </summary>
/// <param name="bmp"></param>
private void IniteData(Bitmap bmp)
{
if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
{
throw new Exception("错误颜色格式只支持24位bmp");
}
BitmapData bitmapdata = bmp.LockBits(new Rectangle(new System.Drawing.Point(), bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
try
{
Width = bitmapdata.Width;
Height = bitmapdata.Height;
LineWidth = bitmapdata.Stride;
OffsetWidth = bitmapdata.Stride - bitmapdata.Width * BD_BYTECOUNT; //对齐后每行数据多余宽度(字节)
Length = bitmapdata.Stride * Height; //位图数据长度
BufSize = Length; //缓冲区实际长度
int length = Length;
Bits = new byte[length];
Marshal.Copy(bitmapdata.Scan0, Bits, 0, length);
}
finally
{
bmp.UnlockBits(bitmapdata);
bmp.Dispose();
}
}
/// <summary>
/// 获取指定坐标点颜色
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public BGR this[int x, int y]
{
get
{
BGR c = new BGR();
if (x < 0 || (x >= this.Width) ||
(y < 0) || (y >= this.Height))
{
throw new Exception("索引越界错误");
}
else
{
// c.B = Bits[(((this.Height - y - 1) * this.LineWidth) + x * BD_BYTECOUNT)];
int offset = y * this.LineWidth + x * BD_BYTECOUNT;
c.B = Bits[offset];
c.G = Bits[offset + 1];
c.R = Bits[offset + 2];
}
return c;
}
}
/// <summary>
/// 比对大图中某坐标是否存在小图
/// </summary>
/// <param name="bmp"></param>
/// <param name="range"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public unsafe bool Compare(BitmapDataFinder bmp, BGR range, byte* b1, byte* b2, int Left, int Top)
{
bool result;
if (((Left + bmp.Width) > this.Width) || ((Top + bmp.Height) > this.Height))
{
return false;
}
result = true;
int offset1 = 0, offset2 = 0;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
offset1 = (Top + y) * this.LineWidth + (Left + x) * BD_BYTECOUNT;
offset2 = y * bmp.LineWidth + x * BD_BYTECOUNT;
//如果背景颜色启用而且当前颜色等于背景色,则继续看下一个点
if (bmp.IsEnableBackColor && (*(b2 + offset2) == bmp.BackColor.B && *(b2 + offset2 + 1) == bmp.BackColor.G && *(b2 + offset2 + 2) == bmp.BackColor.R))
continue;
//颜色比较
if (!BGRCompareColor(*(b1 + offset1), *(b1 + offset1 + 1), *(b1 + offset1 + 2), *(b2 + offset2), *(b2 + offset2 + 1), *(b2 + offset2 + 2), range))
{
result = false;
break;
}
}
if (!result) break;
}
return result;
}
public unsafe bool Compare(BitmapDataFinder bmp, byte* b1, byte* b2, int Left, int Top)
{
bool result;
if (((Left + bmp.Width) > this.Width) || ((Top + bmp.Height) > this.Height))
{
return false;
}
result = true;
int offset1 = 0, offset2 = 0;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
offset1 = (Top + y) * this.LineWidth + (Left + x) * BD_BYTECOUNT;
offset2 = y * bmp.LineWidth + x * BD_BYTECOUNT;
//如果背景颜色启用而且当前颜色等于背景色,则继续看下一个点
if (bmp.IsEnableBackColor && (*(b2 + offset2) == bmp.BackColor.B && *(b2 + offset2 + 1) == bmp.BackColor.G && *(b2 + offset2 + 2) == bmp.BackColor.R))
continue;
//颜色比较
if (*(b2 + offset2) != *(b1 + offset1) || *(b2 + offset2 + 1) != *(b1 + offset1 + 1) || *(b2 + offset2 + 2) != *(b1 + offset1 + 2))
{
result = false;
break;
}
}
if (!result) break;
}
return result;
}
/// <summary>
/// 颜色比较
/// </summary>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <param name="range"></param>
/// <returns></returns>
public bool BGRCompareColor(BGR c1, BGR c2, BGR range)
{
return ((Math.Abs(c1.R - c2.R) <= range.R) && (Math.Abs(c1.G - c2.G) <= range.G) && (Math.Abs(c1.B - c2.B) <= range.B));
}
public bool BGRCompareColor(byte c1B, byte c1G, byte c1R, byte c2B, byte c2G, byte c2R, BGR range)
{
//B
int C = c1B - c2B;
if ((C > range.B) || (C < -range.B)) return false;
//G
C = c1G - c2G;
if ((C > range.G) || (C < -range.G)) return false; ;
//R
C = c1R - c2R;
if ((C > range.R) || (C < -range.R)) return false; ; //
return true;
}
/// <summary>
/// 找图
/// </summary>
/// <param name="bmp"></param>
/// <param name="range"></param>
/// <param name="Left"></param>
/// <param name="Top"></param>
/// <returns></returns>
public unsafe bool Find(BitmapDataFinder bmp, BGR range, ref int Left, ref int Top)
{
bool result = false;
int x = 0;
int y = 0;
fixed (byte* b1 = &Bits[0])
{
fixed (byte* b2 = &bmp.Bits[0])
{
for (y = 0; y + bmp.Height < this.Height; y++)
{
for (x = 0; x + bmp.Width < this.Width; x++)
{
if (Compare(bmp, range, b1, b2, x, y))
{
result = true;
break;
}
}
if (result)
{
break;
}
}
}
}
if (result)
{
Left = x;
Top = y;
}
else
{
Left = -1;
Top = -1;
}
return result;
}
public bool Find(BitmapDataFinder bmp, byte range)
{
int Left = 0, Top = 0;
BGR b = new BGR();
b.B = range;
b.G = range;
b.R = range;
return Find(bmp, b, ref Left, ref Top);
}
public bool Find(BitmapDataFinder bmp, byte range, ref Point p)
{
int Left = 0, Top = 0;
BGR b = new BGR();
b.B = range;
b.G = range;
b.R = range;
bool re = Find(bmp, b, ref Left, ref Top);
p.X = Left;
p.Y = Top;
return re;
}
public bool Find(BitmapDataFinder bmp, BGR range)
{
int Left = 0, Top = 0;
return Find(bmp, range, ref Left, ref Top);
}
public bool Find(BitmapDataFinder bmp, byte r, byte g, byte b)
{
int Left = 0, Top = 0;
BGR range = new BGR();
range.B = b;
range.G = g;
range.R = r;
return Find(bmp, range, ref Left, ref Top);
}
public unsafe bool Find(BitmapDataFinder bmp, ref int Left, ref int Top)
{
bool result = false;
int x = 0;
int y = 0;
fixed (byte* b1 = &Bits[0])
{
fixed (byte* b2 = &bmp.Bits[0])
{
for (y = 0; y + bmp.Height < this.Height; y++)
{
for (x = 0; x + bmp.Width < this.Width; x++)
{
if (Compare(bmp, b1, b2, x, y))
{
result = true;
break;
}
}
if (result)
{
break;
}
}
}
}
if (result)
{
Left = x;
Top = y;
}
else
{
Left = -1;
Top = -1;
}
return result;
}
/// <summary>
/// 枚举子图所有可能点
/// </summary>
/// <param name="bmp"></param>
/// <param name="range"></param>
/// <returns></returns>
public unsafe List<Point> EnumImage(BitmapDataFinder bmp, BGR range)
{
List<Point> list = new List<Point>();
int x = 0;
int y = 0;
fixed (byte* b1 = &Bits[0])
{
fixed (byte* b2 = &bmp.Bits[0])
{
for (y = 0; y + bmp.Height < this.Height; y++)
{
for (x = 0; x + bmp.Width < this.Width; x++)
{
if (Compare(bmp, range, b1, b2, x, y))
{
list.Add(new Point(x, y));
}
}
}
}
}
return list;
}
/// <summary>
/// 保存为图片文件
/// </summary>
/// <param name="path"></param>
public void SaveToBmp(string path)
{
SaveToBmp().Save(path);
}
/// <summary>
/// 保存图片
/// </summary>
/// <returns></returns>
public Bitmap SaveToBmp()
{
Bitmap bmp = new Bitmap(this.Width, this.Height);
BitmapData bitmapdata = bmp.LockBits(new Rectangle(new System.Drawing.Point(), bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
try
{
Marshal.Copy(Bits, 0, bitmapdata.Scan0, Length);
}
finally
{
bmp.UnlockBits(bitmapdata);
}
return bmp;
}
/// <summary>
/// 屏幕截图
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public static BitmapDataFinder CopyScreen(int x, int y, int width, int height)
{
//根据屏幕大小建立位图
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(x, y, 0, 0, new Size(width, height));
}
return new BitmapDataFinder(bitmap);
}
}

浙公网安备 33010602011771号