找色:
/// <summary>
/// 找顏色
/// </summary>
/// <param name="parPic">查找的圖片的絕對路徑</param>
/// <param name="searchColor">查找的16進制顏色值,如#0C5FAB</param>
/// <param name="searchRect">查找的矩形區域范圍內</param>
/// <param name="errorRange">容錯</param>
/// <returns></returns>
System.Drawing.Point FindColor(string parPic, string searchColor, System.Drawing.Rectangle searchRect, byte errorRange = 10)
{
var colorX = System.Drawing.ColorTranslator.FromHtml(searchColor);
var parBitmap = new Bitmap(parPic);
var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.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);
if (searchRect.IsEmpty)
{
searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height);
}
var searchLeftTop = searchRect.Location;
var searchSize = searchRect.Size;
var iMax = searchLeftTop.Y + searchSize.Height;//行
var jMax = searchLeftTop.X + searchSize.Width;//列
int pointX = -1; int pointY = -1;
for (int m = searchRect.Y; m < iMax; m++)
{
for (int n = searchRect.X; n < jMax; n++)
{
int index = m * parBitmap.Width * 4 + n * 4;
var color = System.Drawing.Color.FromArgb(byteArraryPar[index + 3], byteArraryPar[index + 2], byteArraryPar[index + 1], byteArraryPar[index]);
if (ColorAEqualColorB(color, colorX, errorRange))
{
pointX = n;
pointY = m;
goto END;
}
}
}
END:
parBitmap.UnlockBits(parData);
return new System.Drawing.Point(pointX, pointY);
}
#endregion
方法中的容錯范圍,默認設置為10。R、G、B三者的范圍都是0~255,容錯為10,就表示每個范圍都可以在10上下波動,下面還會有容錯的概念
找圖:
在一張大圖中截取一個矩形小圖,然后在任意包含該小圖的圖片中找到該小圖的坐標位置
#region 找圖
/// <summary>
/// 查找圖片,不能鏤空
/// </summary>
/// <param name="subPic"></param>
/// <param name="parPic"></param>
/// <param name="searchRect">如果為empty,則默認查找整個圖像</param>
/// <param name="errorRange">容錯,單個色值范圍內視為正確0~255</param>
/// <param name="matchRate">圖片匹配度,默認90%</param>
/// <param name="isFindAll">是否查找所有相似的圖片</param>
/// <returns>返回查找到的圖片的中心點坐標</returns>
List<System.Drawing.Point> FindPicture(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false)
{
List<System.Drawing.Point> ListPoint = new List<System.Drawing.Point>();
var subBitmap = new Bitmap(subPic);
var parBitmap = new Bitmap(parPic);
int subWidth = subBitmap.Width;
int subHeight = subBitmap.Height;
int parWidth = parBitmap.Width;
int parHeight = parBitmap.Height;
if (searchRect.IsEmpty)
{
searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height);
}
var searchLeftTop = searchRect.Location;
var searchSize = searchRect.Size;
System.Drawing.Color startPixelColor = subBitmap.GetPixel(0, 0);
var subData = subBitmap.LockBits(new System.Drawing.Rectangle(0, 0, subBitmap.Width, subBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var byteArrarySub = new byte[subData.Stride * subData.Height];
var byteArraryPar = new byte[parData.Stride * parData.Height];
Marshal.Copy(subData.Scan0, byteArrarySub, 0, subData.Stride * subData.Height);
Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height);
var iMax = searchLeftTop.Y + searchSize.Height - subData.Height;//行
var jMax = searchLeftTop.X + searchSize.Width - subData.Width;//列
int smallOffsetX = 0, smallOffsetY = 0;
int smallStartX = 0, smallStartY = 0;
int pointX = -1; int pointY = -1;
for (int i = searchLeftTop.Y; i < iMax; i++)
{
for (int j = searchLeftTop.X; j < jMax; j++)
{
//大圖x,y坐標處的顏色值
int x = j, y = i;
int parIndex = i * parWidth * 4 + j * 4;
var colorBig = System.Drawing.Color.FromArgb(byteArraryPar[parIndex + 3], byteArraryPar[parIndex + 2], byteArraryPar[parIndex + 1], byteArraryPar[parIndex]);
;
if (ColorAEqualColorB(colorBig, startPixelColor, errorRange))
{
smallStartX = x - smallOffsetX;//待找的圖X坐標
smallStartY = y - smallOffsetY;//待找的圖Y坐標
int sum = 0;//所有需要比對的有效點
int matchNum = 0;//成功匹配的點
for (int m = 0; m < subHeight; m++)
{
for (int n = 0; n < subWidth; n++)
{
int x1 = n, y1 = m;
int subIndex = m * subWidth * 4 + n * 4;
var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]);
sum++;
int x2 = smallStartX + x1, y2 = smallStartY + y1;
int parReleativeIndex = y2 * parWidth * 4 + x2 * 4;//比對大圖對應的像素點的顏色
var colorPixel = System.Drawing.Color.FromArgb(byteArraryPar[parReleativeIndex + 3], byteArraryPar[parReleativeIndex + 2], byteArraryPar[parReleativeIndex + 1], byteArraryPar[parReleativeIndex]);
if (ColorAEqualColorB(colorPixel, color, errorRange))
{
matchNum++;
}
}
}
if ((double)matchNum / sum >= matchRate)
{
Console.WriteLine((double)matchNum / sum);
pointX = smallStartX + (int)(subWidth / 2.0);
pointY = smallStartY + (int)(subHeight / 2.0);
var point = new System.Drawing.Point(pointX, pointY);
if (!ListContainsPoint(ListPoint, point, 10))
{
ListPoint.Add(point);
}
if (!isFindAll)
{
goto FIND_END;
}
}
}
//小圖x1,y1坐標處的顏色值
}
}
FIND_END:
subBitmap.UnlockBits(subData);
parBitmap.UnlockBits(parData);
subBitmap.Dispose();
parBitmap.Dispose();
GC.Collect();
return ListPoint;
}
#endregion
private void Button_Click_Pic(object sender,RoutedEventArgs e)
{
string subPic = @"E:\2.png";
string parPic = @"E:\1.png";
;
List<System.Drawing.Point> List =FindPicture(subPic, parPic, System.Drawing.Rectangle.Empty, 100);
foreach(System.Drawing.Point p in List)
{
Console.WriteLine(p.X.ToString());
Console.WriteLine(p.Y.ToString());
}
Console.WriteLine("finish");
}
找字:
找字比較困難了呢,因為文字是一種鏤空的圖像,不像上述找的是非鏤空圖像,代碼:
定義結構體:
struct NumBody
{
public int num;//數字
public int matchNum;//匹配的個數
public int matchSum;
public double matchRate;//匹配度
public System.Drawing.Point point;
public List<System.Drawing.Point> bodyCollectionPoint;//該數字所有像素在大圖中的坐標
}
#region 找字
/// <summary>
/// 找文字,鏤空的圖片文字
/// </summary>
/// <param name="subPic"></param>
/// <param name="parPic"></param>
/// <param name="searchRect"></param>
/// <param name="errorRange"></param>
/// <param name="matchRate"></param>
/// <param name="isFindAll"></param>
/// <returns></returns>
List<NumBody> FindText(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false)
{
List<NumBody> ListPoint = new List<NumBody>();
var subBitmap = new Bitmap(subPic);
var parBitmap = new Bitmap(parPic);
int subWidth = subBitmap.Width;
int subHeight = subBitmap.Height;
int parWidth = parBitmap.Width;
int parHeight = parBitmap.Height;
var bgColor = subBitmap.GetPixel(0, 0);//背景紅色
if (searchRect.IsEmpty)
{
searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height);
}
var searchLeftTop = searchRect.Location;
var searchSize = searchRect.Size;
var subData = subBitmap.LockBits(new System.Drawing.Rectangle(0, 0, subBitmap.Width, subBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var byteArrarySub = new byte[subData.Stride * subData.Height];
var byteArraryPar = new byte[parData.Stride * parData.Height];
Marshal.Copy(subData.Scan0, byteArrarySub, 0, subData.Stride * subData.Height);
Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height);
var iMax = searchLeftTop.Y + searchSize.Height - subData.Height;//行
var jMax = searchLeftTop.X + searchSize.Width - subData.Width;//列
System.Drawing.Color startPixelColor = System.Drawing.Color.FromArgb(0, 0, 0);
int smallOffsetX = 0, smallOffsetY = 0;
int smallStartX = 0, smallStartY = 0;
int pointX = -1; int pointY = -1;
for (int m = 0; m < subHeight; m++)
{
for (int n = 0; n < subWidth; n++)
{
smallOffsetX = n;
smallOffsetY = m;
int subIndex = m * subWidth * 4 + n * 4;
var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]);
if (!ColorAEqualColorB(color, bgColor, errorRange))
{
startPixelColor = color;
goto END;
}
}
}
END:
for (int i = searchLeftTop.Y; i < iMax; i++)
{
for (int j = searchLeftTop.X; j < jMax; j++)
{
//大圖x,y坐標處的顏色值
int x = j, y = i;
int parIndex = i * parWidth * 4 + j * 4;
var colorBig = System.Drawing.Color.FromArgb(byteArraryPar[parIndex + 3], byteArraryPar[parIndex + 2], byteArraryPar[parIndex + 1], byteArraryPar[parIndex]);
;
List<System.Drawing.Point> myListPoint = new List<System.Drawing.Point>();
if (ColorAEqualColorB(colorBig, startPixelColor, errorRange))
{
smallStartX = x - smallOffsetX;//待找的圖X坐標
smallStartY = y - smallOffsetY;//待找的圖Y坐標
int sum = 0;//所有需要比對的有效點
int matchNum = 0;//成功匹配的點
for (int m = 0; m < subHeight; m++)
{
for (int n = 0; n < subWidth; n++)
{
int x1 = n, y1 = m;
int subIndex = m * subWidth * 4 + n * 4;
var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]);
if (color != bgColor)
{
sum++;
int x2 = smallStartX + x1, y2 = smallStartY + y1;
int parReleativeIndex = y2 * parWidth * 4 + x2 * 4;//比對大圖對應的像素點的顏色
var colorPixel = System.Drawing.Color.FromArgb(byteArraryPar[parReleativeIndex + 3], byteArraryPar[parReleativeIndex + 2], byteArraryPar[parReleativeIndex + 1], byteArraryPar[parReleativeIndex]);
if (ColorAEqualColorB(colorPixel, color, errorRange))
{
matchNum++;
}
myListPoint.Add(new System.Drawing.Point(x2, y2));
}
}
}
double rate = (double)matchNum / sum;
if (rate>= matchRate)
{
Console.WriteLine((double)matchNum / sum);
pointX = smallStartX + (int)(subWidth / 2.0);
pointY = smallStartY + (int)(subHeight / 2.0);
var point = new System.Drawing.Point(pointX, pointY);
if (!ListTextBodyContainsPoint(ListPoint, point, 1))
{
ListPoint.Add(new NumBody() { point = point, matchNum = matchNum,matchSum=sum, matchRate = rate, bodyCollectionPoint = myListPoint });
}
SearchNumbersByMatchNum(ref ListPoint);
if (!isFindAll)
{
goto FIND_END;
}
}
}
//小圖x1,y1坐標處的顏色值
}
}
FIND_END:
subBitmap.UnlockBits(subData);
parBitmap.UnlockBits(parData);
subBitmap.Dispose();
parBitmap.Dispose();
GC.Collect();
return ListPoint;
}
特別注意:有了這個方法還是不能找到你要的文字的。要先處理文字,下面舉例:
例如在這張圖片上找到朋友的朋字的坐標位置:

1:打開你的PS,先將圖片放大,看到像素方塊為止,然后將朋字的范圍圈選住,注意稍微比字圈選的大一點,像這樣:

2:按住CTRL+C,然后CTRL+N,出現對話框:(教教大家使用PS^_^)

3:將背景內容選擇透明,按確定,再按CTRL+V復制圖像

4:將這個圖片放大到看到像素為止,將所有非字體的位置全部用鉛筆工具塗上同一種顏色,

5:塗完了之后將這張圖片保存下來,這張圖片就是我們要查找的“朋”字,圖片是這樣的

6:我們需要的就是第五步的圖片和第一張底圖,下面見證奇跡的時刻到了。
1 string str1 = @"C:\Users\JimmyBright\Desktop\1.png"; 2 string str2 = @"C:\Users\JimmyBright\Desktop\2.png"; 3 var xx = FindText(str2,str1,new System.Drawing.Rectangle(0, 0, 400, 600),10);
str1是我們的底圖,str2是第五步的那張處理后的文字圖片,xx就是我們最后需要的文字的位置坐標,我們運行看看。下面截圖運行結果:

顯然最后我們查找的文字在圖片中的坐標為(224,286),大家可以下載那張圖片驗證
找數:
你以為找到文字就算完了嗎?No,找數字才是最困難的,為什么呢?有人會問,數字難道不也是文字嗎,不也可以通過PS處理數字達到查找其位置的目的嗎?對的,數字也是文字,我們將需要查找的數字0~9全部PS處理,就能查到它們的位置了。但是有一個問題啊,游戲中用數字表示的地方通常是一連串的數字,這些數字里面包含0~9的任意組合。所以我們需要這樣處理:
我們從0~9依次查找指定區域,記錄每次查找的結果,沒查到的數字不必記錄,對查到結果的數字再按照X坐標排序,因為在X坐標越小,數字越靠左邊。
還有一個嚴重的問題,例如38,14,這樣的數字會很討厭,為什么呢,我們會再8當中查找3,在4當中查找到1,這會對我們的數字識別產生重大誤差,所以下面我也寫了一個方法對這個問題做了處理,代碼:
1 #region 查找數字
2
3 /// <summary>
4 /// 在指定區域里面查找數字
5 /// </summary>
6 /// <param name="numDic"></param>
7 /// <param name="parPic"></param>
8 /// <param name="searchRect"></param>
9 /// <param name="errorRange"></param>
10 /// <returns></returns>
11 int FindNumbers(Dictionary<int, string> numDic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange=8, double matchRate = 0.9)
12 {
13 //同一個區域找到多個相同的圖片
14 List<NumBody> ListBody = new List<NumBody>();
15 foreach (var item in numDic)
16 {
17 var listPoint = FindText(item.Value, parPic, searchRect, errorRange, matchRate, true);
18 foreach (var point in listPoint)
19 {
20 ListBody.Add(new NumBody() { num = item.Key,matchNum=point.matchNum,matchSum=point.matchSum, matchRate=point.matchRate, point = point.point, bodyCollectionPoint = point.bodyCollectionPoint });
21 }
22 }
23
24 SearchNumbersByMatchNum(ref ListBody);
25 var myList = from body in ListBody orderby body.point.X ascending select body;
26 string number = "0";
27 foreach (var item in myList)
28 {
29 number += item.num;
30 }
31 int num = Int32.Parse(number);
32 return num;
33 }
34 /// <summary>
35 /// 搜索同一個數字的時候,出現重疊的地方,用匹配度去過濾掉匹配度低的
36 /// 比如同樣是1,在控制匹配度允許下,一個(83,95)和(84,95)這兩個點明顯是同一個數字
37 /// 此時誰的匹配度低過濾掉誰
38 /// </summary>
39 /// <param name="ListBody"></param>
40 void SearchNumbersByMatchNum(ref List<NumBody> ListBody)
41 {
42 bool isValid = true;
43 for (int i = 0; i < ListBody.Count; i++)
44 {
45 var body = ListBody[i];
46
47 for (int j = i; j < ListBody.Count; j++)
48 {
49
50 var bodyX = ListBody[j];
51 if (!bodyX.Equals(body))
52 {
53 int sameNum = 0;
54 foreach (var item in body.bodyCollectionPoint)
55 {
56 if (bodyX.bodyCollectionPoint.Contains(item))
57 {
58 sameNum++;
59 }
60 }
61 if (sameNum >= 1)//有1個以上點重合,表面圖像重疊,刪除像素點數少的圖像
62 {
63 isValid = false;
64
65 //如果某個數字100%匹配,那就不用比較了,這個數字肯定是對的
66 double maxRate = 1;
67 if (bodyX.matchRate >= maxRate)
68 {
69 ListBody.Remove(body);
70 }
71 else if (body.matchRate>=maxRate)
72 {
73 ListBody.Remove(bodyX);
74 }
75 else
76 {
77 if (bodyX.matchNum >= body.matchNum)//圖像包含的所有像素個數
78 {
79 ListBody.Remove(body);
80 }
81 else
82 {
83 ListBody.Remove(bodyX);
84 }
85 }
86 SearchNumbersByMatchNum(ref ListBody);
87 }
88 }
89 }
90 }
91 if (isValid)
92 {
93 return;
94 }
95 }
96
97 #endregion
其他方法:
1 bool ColorAEqualColorB(System.Drawing.Color colorA, System.Drawing.Color colorB, byte errorRange = 10)
2 {
3 return colorA.A <= colorB.A + errorRange && colorA.A >= colorB.A - errorRange &&
4 colorA.R <= colorB.R + errorRange && colorA.R >= colorB.R - errorRange &&
5 colorA.G <= colorB.G + errorRange && colorA.G >= colorB.G - errorRange &&
6 colorA.B <= colorB.B + errorRange && colorA.B >= colorB.B - errorRange;
7
8 }
9 bool ListContainsPoint(List<System.Drawing.Point> listPoint, System.Drawing.Point point, double errorRange = 10)
10 {
11 bool isExist = false;
12 foreach (var item in listPoint)
13 {
14 if (item.X <= point.X + errorRange && item.X >= point.X - errorRange && item.Y <= point.Y + errorRange && item.Y >= point.Y - errorRange)
15 {
16 isExist = true;
17 }
18 }
19 return isExist;
20 }
21 bool ListTextBodyContainsPoint(List<NumBody> listPoint, System.Drawing.Point point, double errorRange = 10)
22 {
23 bool isExist = false;
24 foreach (var item in listPoint)
25 {
26
27 if (item.point.X <= point.X + errorRange && item.point.X >= point.X - errorRange && item.point.Y <= point.Y + errorRange && item.point.Y >= point.Y - errorRange)
28 {
29 isExist = true;
30 }
31 }
32 return isExist;
33 }
結束語:以上代碼本人實現了找顏色,找圖片,找文字,找數字的所有功能,希望對朋友們能有所幫助。
最近找到了以前的代碼:
想看源碼的去這里
https://github.com/ahuchjm/BtnPress.git

