C#在大图查找小图位置

2023 年 10 月 23 日 星期一
10

C#在大图查找小图位置

因需要做一个工具,需要使用到大图查找小图功能,在网上找了下,可以使用AForge包,然后找到一个现在这个代码,感觉还可以,速度也不错,就拿来用了,代码如下。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;

namespace TestPictureFinder
{
    public class PictureFinder
    {
        /// <summary>
        /// 查找局部图片的方法
        /// </summary>
        /// <param name="FindResulte">返回查找的结果,相似度大于设定的FoundRate比例</param>
        /// <param name="Source">原始大图片</param>
        /// <param name="Smallpic">原始大图片的局部小图片</param>
        /// <param name="OriginalFindResulte">返回原始的查找结果</param>
        /// <param name="FindAll">是否相同所有相同的多个子图片</param>
        /// <param name="FoundRate">查找到相同像素的百分比条件,1表示100%完全相同,默认值为0.90f表示90%的相似度</param>
        /// <param name="ExcludeColor">默认排除查找的颜色为黑色0xFF和白色0xFFFFFFFF(BGRA)</param>
        /// <param name="error">颜色的容错值,0~255,0表示没有色差,255表示最大色差</param>
        /// <param name="step">查找像素的步长,默认为1</param>
        /// <param name="deeppercent">取出多少百分比相同颜色进行比较,以提高速度,0~1f</param>
        /// <returns>是否找到了局部小图片</returns>
        public static bool FindSmallPic(out Dictionary<Point, float> FindResulte, Bitmap Source, Bitmap Smallpic, out Dictionary<Point, float> OriginalFindResulte, bool FindAll = false, float FoundRate = 0.90f, uint[] ExcludeColor = default, byte error = 20, int step = 1, float deeppercent = 0.1f)
        {
            int w1 = Source.Width;
            int h1 = Source.Height;
            int w = Smallpic.Width;
            int h = Smallpic.Height;
            if (step == default || step < 1)
                step = 1;

            if (ExcludeColor == default)
                ExcludeColor = new uint[] { 0xFF, 0xFFFFFFFF };//黑色和白色,最后一个字节是255表示是透明度
            Dictionary<uint, uint> colorcountsmall;
            Dictionary<uint, uint> colorcountbig;
            Dictionary<uint, List<Point>> ColorPointListSmall = GetBitmapColorPointList(Smallpic, out colorcountsmall);
            Dictionary<uint, List<Point>> ColorPointListBig = GetBitmapColorPointList(Source, out colorcountbig);
            ColorPointListBig = ColorPointListBig.Where(x => ColorPointListSmall.ContainsKey(x.Key) && !ExcludeColor.Contains(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            //有时小图不一定是原大图中的,要进行颜色的相互筛选
            ColorPointListSmall = ColorPointListSmall.Where(x => ColorPointListBig.ContainsKey(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            Dictionary<uint, double> count = new Dictionary<uint, double>();
            foreach (var v in ColorPointListSmall.Keys)
            {
                double vsmall = Math.Sqrt(colorcountsmall[v]);
                double vbig = Math.Sqrt(colorcountbig[v]);
                double cunt = vsmall * vbig;
                count.Add(v, cunt);
            }
            if (count.Count == 0)
            {
                FindResulte = null;
                OriginalFindResulte = null;
                //GC.Collect();
                return false;
            }
            colorcountsmall = null;
            colorcountbig = null;
            count = count.OrderBy(x => x.Value).ToDictionary(p => p.Key, p => p.Value);
            int deep = (int)(count.Count* deeppercent);// (int)(count.Last().Value * deeppercent);
            deep=deep<1 ? 1 : deep;
            uint[] corlortofind = count.OrderBy(x => x.Value).Take(deep).Select(x => x.Key).ToArray();

            count = null;
            ColorPointListBig = ColorPointListBig.Where(x => corlortofind.Contains(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            ColorPointListSmall = ColorPointListSmall.Where(x => corlortofind.Contains(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            corlortofind = null;
            Dictionary<Point, List<Point>> PointsFound = new Dictionary<Point, List<Point>>();
            foreach (var vv in ColorPointListSmall)
            {
                foreach (var v in vv.Value)
                {
                    List<Point> points = ColorPointListBig.Where(x => x.Key == vv.Key).Select(x => x.Value).First();
                    PointsFound.Add(v, points);
                }
            }
            ColorPointListBig = null;
            ColorPointListSmall = null;
            FindResulte = new Dictionary<Point, float>();

            foreach (var vp in PointsFound)
            {
                foreach (var p in vp.Value)
                {
                    float percent = 0;
                    float BeFind = 0;
                    int startY = p.Y - vp.Key.Y;
                    int startX = p.X - vp.Key.X;
                    if (startX < 0 || startY < 0 || startX > w1 - w || startY > h1 - h)
                        continue;
                    Point start = new Point(startX, startY);
                    if (FindResulte.ContainsKey(start))
                    {
                        continue;
                    }
                    float AllDot = 0;
                    for (int j = 0; j < h; j += step)
                    {
                        for (int i = 0; i < w; i += step)
                        {
                            AllDot++;
                            Color vsmal = Smallpic.GetPixel(i, j);
                            Color vsource = Source.GetPixel(i + startX, j + startY);
                            if (ColorSimilarity(vsmal, vsource) <= error)
                            {
                                BeFind++;
                            }
                        }
                    }
                    percent = BeFind / AllDot;
                    FindResulte.Add(start, percent);
                }
            }

            OriginalFindResulte = FindResulte.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
            FindResulte = OriginalFindResulte.Where(x => x.Value >= FoundRate).ToDictionary(x => x.Key, x => x.Value);
            bool ret = FindResulte.Count > 0;
            PointsFound = null;
            //GC.Collect();
            return ret;
        }
        /// <summary>
        /// 两颜色的RGB值的差的平均值
        /// </summary>
        /// <param name="a">颜色a</param>
        /// <param name="b">颜色b</param>
        /// <returns>两颜色的RGB值的差平均值</returns>
        private static byte ColorSimilarity2(Color a, Color b)
        {
            //int Ea = (a.A - b.A);
            byte Er = (byte)Math.Abs(a.R - b.R);
            byte Eg = (byte)Math.Abs(a.G - b.G);
            byte Eb = (byte)Math.Abs(a.B - b.B);
            byte e = (byte)((Er + Eg + Eb) / 3);
            return e;
        }
        private static byte ColorSimilarity(Color a, Color b)
        {
            const double Max= 2167.470702927d;
            //int Ea = (a.A - b.A);
            long Er = a.R - b.R;
            long Eg = a.G - b.G;
            long Eb = a.B - b.B;
            long DR = (a.R + b.R)/2;
            double e = Math.Sqrt((((512+DR)*Er*Er)>>8)+4*Eg*Eg+(((767-DR)*Eb*Eb)>>8));
            byte ret = (byte)(e/Max*255);
            return ret;
        }

        /// <summary>
        /// 查找局部图片的方法.
        /// </summary>
        /// <param name="FindResulte">返回查找的结果,相似度大于设定的FoundRate比例</param>
        /// <param name="Source">原始大图片</param>
        /// <param name="Smallpic">原始大图片的局部小图片</param>
        /// <param name="FoundRate">查找到相同像素的百分比条件,1表示100%完全相同,默认值为0.90f表示90%的相似度</param>
        /// <param name="ExcludeColor">默认排除查找的颜色为白色0xFFFFFFFF</param>
        /// <returns>是否找到了局部小图片</returns>
        public static bool FindSmallPic(out Dictionary<Point, float> FindResulte, Bitmap Source, Bitmap Smallpic, float FoundRate = 0.90f, uint[] ExcludeColor = default)
        {
            int WidthBig = Source.Width;
            int HeightBig = Source.Height;
            int WidthSmall = Smallpic.Width;
            int HeightSmall = Smallpic.Height;
            //查找的对比像素的步长,越大越快,但会降低精度,速度提升并不明显。
            int Step = 1;
            //比对多少种颜色,越少越快
            int ColorDeep = 1;
            if (ExcludeColor == default)
                ExcludeColor = new uint[] { 0xFFFFFFFF };
            Dictionary<uint, uint> colorcountsmall;
            Dictionary<uint, uint> colorcountbig;
            Dictionary<uint, List<Point>> ColorPointListSmall = GetBitmapColorPointList(Smallpic, out colorcountsmall);
            Dictionary<uint, List<Point>> ColorPointListBig = GetBitmapColorPointList(Source, out colorcountbig);
            ColorPointListBig = ColorPointListBig.Where(x => ColorPointListSmall.ContainsKey(x.Key) && !ExcludeColor.Contains(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            //有时小图不一定是原大图中的,要进行颜色的相互筛选
            ColorPointListSmall = ColorPointListSmall.Where(x => ColorPointListBig.ContainsKey(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            Dictionary<uint, double> count = new Dictionary<uint, double>();
            foreach (var v in ColorPointListSmall.Keys)
            {
                double vsmall = Math.Sqrt(colorcountsmall[v]);
                double vbig = Math.Sqrt(colorcountbig[v]);
                double allcount = vsmall * vbig;
                count.Add(v, allcount);
            }
            if (count.Count == 0)
            {
                FindResulte = null;
                return false;
            }

            colorcountsmall = null;
            colorcountbig = null;
            count = count.OrderBy(x => x.Value).ToDictionary(p => p.Key, p => p.Value);

            uint[] corlortofind = count.OrderBy(x => x.Value).Take(ColorDeep).Select(x => x.Key).ToArray();
            count = null;
            ColorPointListBig = ColorPointListBig.Where(x => corlortofind.Contains(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            ColorPointListSmall = ColorPointListSmall.Where(x => corlortofind.Contains(x.Key)).ToDictionary(p => p.Key, p => p.Value);
            Dictionary<Point, List<Point>> PointsFound = new Dictionary<Point, List<Point>>();
            foreach (var vv in ColorPointListSmall)
            {
                foreach (var v in vv.Value)
                {
                    List<Point> points = ColorPointListBig.Where(x => x.Key == vv.Key).Select(x => x.Value).First();
                    PointsFound.Add(v, points);
                }
            }
            ColorPointListBig = null;
            ColorPointListSmall = null;
            FindResulte = new Dictionary<Point, float>();
            Dictionary<Point, Rectangle> RectanglesFound = new Dictionary<Point, Rectangle>();
            foreach (var vp in PointsFound)
            {
                foreach (var p in vp.Value)
                {
                    float percent = 0;
                    float BeFind = 0;
                    int startY = p.Y - vp.Key.Y;
                    int startX = p.X - vp.Key.X;
                    if (startX < 0 || startY < 0 || startX > WidthBig - WidthSmall || startY > HeightBig - HeightSmall)
                        continue;
                    Point start = new Point(startX, startY);
                    if (FindResulte.ContainsKey(start))
                    {
                        continue;
                    }
                    float AllDot = 0;
                    for (int j = 0; j < HeightSmall; j += Step)
                    {
                        for (int i = 0; i < WidthSmall; i += Step)
                        {
                            AllDot++;
                            int vsmal = Smallpic.GetPixel(i, j).ToArgb();
                            int vsource = Source.GetPixel(i + startX, j + startY).ToArgb();
                            if (vsmal == vsource)
                            {
                                BeFind++;
                            }
                        }
                    }
                    percent = BeFind / AllDot;
                    FindResulte.Add(start, percent);
                }
            }
            Dictionary<Point, float> vlist, vlist2;
            vlist = FindResulte.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
            vlist2 = vlist.Where(x => x.Value >= FoundRate).ToDictionary(x => x.Key, x => x.Value);
            FindResulte = vlist2;
            vlist=null; vlist2=null;
            //GC.Collect();
            return FindResulte.Count > 0;
        }
        public static Dictionary<uint, List<Point>> GetBitmapColorPointList(Bitmap obitmap, out Dictionary<uint, uint> ColorCount)
        {
            Bitmap bitmap = (Bitmap)obitmap.Clone();
            int parWidth = bitmap.Width;
            int parHeight = bitmap.Height;
            var parData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            var byteArraryPar = new byte[parData.Stride * parData.Height];
            Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height);
            var iMax = parData.Height;//行
            var jMax = parData.Width;//列
            int Depth = 4;//Format32bppArgb的颜色深度
            Dictionary<uint, List<Point>> dir = new Dictionary<uint, List<Point>>();
            ColorCount = new Dictionary<uint, uint>();
            for (int i = 0; i < iMax - 1; i++)
            {
                for (int j = 0; j < jMax - 1; j++)
                {
                    //大图x,y坐标处的颜色值
                    //int x = j, y = i;
                    int parIndex = i * parWidth * Depth + j * Depth;
                    uint c = 0;
                    byte a = byteArraryPar[parIndex + 3];
                    byte r = byteArraryPar[parIndex + 2];
                    byte g = byteArraryPar[parIndex + 1];
                    byte b = byteArraryPar[parIndex + 0];
                    c = (uint)((b << 24) | (g << 16) | (r << 8) | a);
                    if (dir.ContainsKey(c))
                    {
                        dir[c].Add(new Point(j, i));
                        ColorCount[c]++;
                    }
                    else
                    {
                        List<Point> list = new List<Point>
                        {
                            new Point(j, i)
                        };
                        dir.Add(c, list);
                        ColorCount.Add(c, 1);
                    }
                }
            }
            bitmap.UnlockBits(parData);
            bitmap = null;
            //GC.Collect();
            return dir;
        }

        public static Bitmap DrawRectangle(Bitmap d, List<Rectangle> recs, Color color = default, float penwidth = 1f)
        {
            if (color == default) color = Color.Red;
            Bitmap vb = (Bitmap)d.Clone();
            Graphics g = Graphics.FromImage(vb);
            Pen pen = new Pen(color, penwidth);
            pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
            foreach (var v in recs)
            {
                g.DrawRectangle(pen, v);
            }
            g.Dispose();
            return vb;
        }
        public static Bitmap DrawRectangle(Bitmap d, Rectangle rec, Color color = default)
        {
            if (color == default) color = Color.Red;
            Bitmap vb = (Bitmap)d.Clone();
            Pen pen = new Pen(color, 0.1f);
            Graphics g = Graphics.FromImage(vb);
            pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
            g.DrawRectangle(pen, rec);
            g.Dispose();
            return vb;
        }
    }
}

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...