C#中OpenCvSharp 通過(guò)特征點(diǎn)匹配圖片的方法
現(xiàn)在的手游基本都是重復(fù)操作,一個(gè)動(dòng)作要等好久,結(jié)束之后繼續(xù)另一個(gè)動(dòng)作.很麻煩,所以動(dòng)起了自己寫一個(gè)游戲輔助的心思.
這個(gè)輔助本身沒什么難度,就是通過(guò)不斷的截圖,然后從這個(gè)截圖中找出預(yù)先截好的能代表相應(yīng)動(dòng)作的按鈕或者觸發(fā)條件的小圖.
找到之后獲取該子區(qū)域的左上角坐標(biāo),然后通過(guò)windows API調(diào)用鼠標(biāo)或者鍵盤做操作就行了.
這里面最難的也就是找圖了,因?yàn)橐珳?zhǔn)找圖,而且最好能適應(yīng)不同的分辨率下找圖,所以在模板匹配的基礎(chǔ)上,就有了SIFT和SURF的特征點(diǎn)找圖方式.
在寫的過(guò)程中查找資料,大都是C++ 或者python的, 很少有原生的C#實(shí)現(xiàn), 所以我就直接拿來(lái)翻譯過(guò)來(lái)了(稍作改動(dòng)).
SIFT算法
public static Bitmap MatchPicBySift(Bitmap imgSrc, Bitmap imgSub)
{
using (Mat matSrc = imgSrc.ToMat())
using (Mat matTo = imgSub.ToMat())
using (Mat matSrcRet = new Mat())
using (Mat matToRet = new Mat())
{
KeyPoint[] keyPointsSrc, keyPointsTo;
using (var sift = OpenCvSharp.XFeatures2D.SIFT.Create())
{
sift.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);
sift.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);
}
using (var bfMatcher = new OpenCvSharp.BFMatcher())
{
var matches = bfMatcher.KnnMatch(matSrcRet, matToRet, k: 2);
var pointsSrc = new List<Point2f>();
var pointsDst = new List<Point2f>();
var goodMatches = new List<DMatch>();
foreach (DMatch[] items in matches.Where(x => x.Length > 1))
{
if (items[0].Distance < 0.5 * items[1].Distance)
{
pointsSrc.Add(keyPointsSrc[items[0].QueryIdx].Pt);
pointsDst.Add(keyPointsTo[items[0].TrainIdx].Pt);
goodMatches.Add(items[0]);
Console.WriteLine($"{keyPointsSrc[items[0].QueryIdx].Pt.X}, {keyPointsSrc[items[0].QueryIdx].Pt.Y}");
}
}
var outMat = new Mat();
// 算法RANSAC對(duì)匹配的結(jié)果做過(guò)濾
var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d);
var pDst = pointsDst.ConvertAll(Point2fToPoint2d);
var outMask = new Mat();
// 如果原始的匹配結(jié)果為空, 則跳過(guò)過(guò)濾步驟
if (pSrc.Count > 0 && pDst.Count > 0)
Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
// 如果通過(guò)RANSAC處理后的匹配點(diǎn)大于10個(gè),才應(yīng)用過(guò)濾. 否則使用原始的匹配點(diǎn)結(jié)果(匹配點(diǎn)過(guò)少的時(shí)候通過(guò)RANSAC處理后,可能會(huì)得到0個(gè)匹配點(diǎn)的結(jié)果).
if (outMask.Rows > 10)
{
byte[] maskBytes = new byte[outMask.Rows * outMask.Cols];
outMask.GetArray(0, 0, maskBytes);
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints);
}
else
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints);
return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outMat);
}
}
}
SURF算法
public static Bitmap MatchPicBySurf(Bitmap imgSrc, Bitmap imgSub, double threshold = 400)
{
using (Mat matSrc = imgSrc.ToMat())
using (Mat matTo = imgSub.ToMat())
using (Mat matSrcRet = new Mat())
using (Mat matToRet = new Mat())
{
KeyPoint[] keyPointsSrc, keyPointsTo;
using (var surf = OpenCvSharp.XFeatures2D.SURF.Create(threshold,4,3,true,true))
{
surf.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);
surf.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);
}
using (var flnMatcher = new OpenCvSharp.FlannBasedMatcher())
{
var matches = flnMatcher.Match(matSrcRet, matToRet);
//求最小最大距離
double minDistance = 1000;//反向逼近
double maxDistance = 0;
for (int i = 0; i < matSrcRet.Rows; i++)
{
double distance = matches[i].Distance;
if (distance > maxDistance)
{
maxDistance = distance;
}
if (distance < minDistance)
{
minDistance = distance;
}
}
Console.WriteLine($"max distance : {maxDistance}");
Console.WriteLine($"min distance : {minDistance}");
var pointsSrc = new List<Point2f>();
var pointsDst = new List<Point2f>();
//篩選較好的匹配點(diǎn)
var goodMatches = new List<DMatch>();
for (int i = 0; i < matSrcRet.Rows; i++)
{
double distance = matches[i].Distance;
if (distance < Math.Max(minDistance * 2, 0.02))
{
pointsSrc.Add(keyPointsSrc[matches[i].QueryIdx].Pt);
pointsDst.Add(keyPointsTo[matches[i].TrainIdx].Pt);
//距離小于范圍的壓入新的DMatch
goodMatches.Add(matches[i]);
}
}
var outMat = new Mat();
// 算法RANSAC對(duì)匹配的結(jié)果做過(guò)濾
var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d);
var pDst = pointsDst.ConvertAll(Point2fToPoint2d);
var outMask = new Mat();
// 如果原始的匹配結(jié)果為空, 則跳過(guò)過(guò)濾步驟
if (pSrc.Count > 0 && pDst.Count > 0)
Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
// 如果通過(guò)RANSAC處理后的匹配點(diǎn)大于10個(gè),才應(yīng)用過(guò)濾. 否則使用原始的匹配點(diǎn)結(jié)果(匹配點(diǎn)過(guò)少的時(shí)候通過(guò)RANSAC處理后,可能會(huì)得到0個(gè)匹配點(diǎn)的結(jié)果).
if (outMask.Rows > 10)
{
byte[] maskBytes = new byte[outMask.Rows * outMask.Cols];
outMask.GetArray(0, 0, maskBytes);
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints);
}
else
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints);
return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outMat);
}
}
}

模板匹配
public static System.Drawing.Point FindPicFromImage(Bitmap imgSrc, Bitmap imgSub, double threshold = 0.9)
{
OpenCvSharp.Mat srcMat = null;
OpenCvSharp.Mat dstMat = null;
OpenCvSharp.OutputArray outArray = null;
try
{
srcMat = imgSrc.ToMat();
dstMat = imgSub.ToMat();
outArray = OpenCvSharp.OutputArray.Create(srcMat);
OpenCvSharp.Cv2.MatchTemplate(srcMat, dstMat, outArray, Common.templateMatchModes);
double minValue, maxValue;
OpenCvSharp.Point location, point;
OpenCvSharp.Cv2.MinMaxLoc(OpenCvSharp.InputArray.Create(outArray.GetMat()), out minValue, out maxValue, out location, out point);
Console.WriteLine(maxValue);
if (maxValue >= threshold)
return new System.Drawing.Point(point.X, point.Y);
return System.Drawing.Point.Empty;
}
catch(Exception ex)
{
return System.Drawing.Point.Empty;
}
finally
{
if (srcMat != null)
srcMat.Dispose();
if (dstMat != null)
dstMat.Dispose();
if (outArray != null)
outArray.Dispose();
}
}

總結(jié)
以上所述是小編給大家介紹的C#中OpenCvSharp 通過(guò)特征點(diǎn)匹配圖片,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
Unity3D網(wǎng)格功能生成球體網(wǎng)格模型
這篇文章主要為大家詳細(xì)介紹了Unity3D網(wǎng)格功能生成球體網(wǎng)格模型,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
C#動(dòng)態(tài)生成PictureBox并指定圖片的方法
這篇文章主要介紹了C#動(dòng)態(tài)生成PictureBox并指定圖片的方法,實(shí)例分析了C#圖形控件的動(dòng)態(tài)生成及使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
c# textbox的滾動(dòng)條總是指向最底端的簡(jiǎn)單解決方法
這篇文章主要介紹了c# textbox的滾動(dòng)條總是指向最底端的簡(jiǎn)單解決方法,需要的朋友可以參考下2017-11-11
C#獲取HTML文本的第一張圖片與截取內(nèi)容摘要示例代碼
在日常web開發(fā)的時(shí)候,經(jīng)常會(huì)遇到需要獲取保存的HTML文本中的第一張圖片,并且截取內(nèi)容摘要的效果,例如織夢(mèng)的后臺(tái)添加完詳細(xì)內(nèi)容后就是自動(dòng)讀取內(nèi)容摘要,并保存第一張圖片為縮略圖,那么這篇文章跟大家分享下利用C#如何實(shí)現(xiàn),感興趣的朋友們下面來(lái)一起看看吧。2016-10-10
C# winform點(diǎn)擊生成二維碼實(shí)例代碼
這篇文章主要介紹了 C# winform點(diǎn)擊生成二維碼實(shí)例代碼,需要的朋友可以參考下2017-04-04
ADO.NET實(shí)體數(shù)據(jù)模型詳細(xì)介紹
本文將詳細(xì)介紹ADO.NET實(shí)體數(shù)據(jù)模型,下面先看看簡(jiǎn)單的單表的增刪改查操作,然后再看多表的關(guān)聯(lián)查詢,帶參數(shù)查詢等2012-11-11

