写了个类似按键精灵的找图类。方便大家做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);
        }


    }

 代码下载

posted @ 2012-08-20 15:07  dotNET界面-大白  阅读(1615)  评论(2编辑  收藏  举报