余暇には、コンピューター ビジョン技術を使ってさまざまなゲームを自動化するのが好きです。通常、フィルターとピクセル検出を使用したテンプレート マッチングは問題なく機能します。しかし、私は最近、機能マッチングを使用してレベル内をナビゲートしようと決心しました。私が意図したのは、探索したマップ全体のフィルター処理された画像を保存することでした。
次に、数秒ごとに画面からミニマップをコピーし、同じ方法でフィルター処理し、Surf を使用してそれを完全なマップに一致させます。これにより、プレイヤーの現在の場所が得られることを願っています (試合の中心は、プレイヤーがいる場所になります)。地図)。これが意図したとおりに機能する良い例を以下に示します (左側に一致が見つかった完全なマップ、右側がミニ マップ イメージです。
私が問題を抱えているのは、EMGU ライブラリの Surf Matching が、多くの場合、間違った一致を検出するように見えることです。
以下のように、完全に悪くない場合もあります。
サーフはスケール不変であるはずなので、マップ上のさまざまな場所のキーポイントにより適した一致を見つけることが、何が起こっているのかを見ることができます。私は EMGU ライブラリまたは Surf について十分に理解していないため、最初の適切な一致のみを受け入れ、これらの不適切な一致を破棄するか、それらの不安定な一致が代わりに適切な一致になるように調整する必要があります。
私は新しい 2.4 EMGU コード ベースを使用しており、SURF マッチングのコードは以下のとおりです。常に同じサイズ(通常のミニマップサイズとフルマップのサイズの比率)の一致のみを返すようにして、クレイジーな形の一致が得られないようにすることを本当に望んでいます.
public Point MinimapMatch(Bitmap Minimap, Bitmap FullMap)
{
Image<Gray, Byte> modelImage = new Image<Gray, byte>(Minimap);
Image<Gray, Byte> observedImage = new Image<Gray, byte>(FullMap);
HomographyMatrix homography = null;
SURFDetector surfCPU = new SURFDetector(100, false);
VectorOfKeyPoint modelKeyPoints;
VectorOfKeyPoint observedKeyPoints;
Matrix<int> indices;
Matrix<byte> mask;
int k = 6;
double uniquenessThreshold = 0.9;
try
{
//extract features from the object image
modelKeyPoints = surfCPU.DetectKeyPointsRaw(modelImage, null);
Matrix<float> modelDescriptors = surfCPU.ComputeDescriptorsRaw(modelImage, null, modelKeyPoints);
// extract features from the observed image
observedKeyPoints = surfCPU.DetectKeyPointsRaw(observedImage, null);
Matrix<float> observedDescriptors = surfCPU.ComputeDescriptorsRaw(observedImage, null, observedKeyPoints);
BruteForceMatcher<float> matcher = new BruteForceMatcher<float>(DistanceType.L2);
matcher.Add(modelDescriptors);
indices = new Matrix<int>(observedDescriptors.Rows, k);
using (Matrix<float> dist = new Matrix<float>(observedDescriptors.Rows, k))
{
matcher.KnnMatch(observedDescriptors, indices, dist, k, null);
mask = new Matrix<byte>(dist.Rows, 1);
mask.SetValue(255);
Features2DToolbox.VoteForUniqueness(dist, uniquenessThreshold, mask);
}
int nonZeroCount = CvInvoke.cvCountNonZero(mask);
if (nonZeroCount >= 4)
{
nonZeroCount = Features2DToolbox.VoteForSizeAndOrientation(modelKeyPoints, observedKeyPoints, indices, mask, 1.5, 20);
if (nonZeroCount >= 4)
homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(modelKeyPoints, observedKeyPoints, indices, mask, 2);
}
if (homography != null)
{ //draw a rectangle along the projected model
Rectangle rect = modelImage.ROI;
PointF[] pts = new PointF[] {
new PointF(rect.Left, rect.Bottom),
new PointF(rect.Right, rect.Bottom),
new PointF(rect.Right, rect.Top),
new PointF(rect.Left, rect.Top)};
homography.ProjectPoints(pts);
Array.ConvertAll<PointF, Point>(pts, Point.Round);
Image<Bgr, Byte> result = Features2DToolbox.DrawMatches(modelImage, modelKeyPoints, observedImage, observedKeyPoints, indices, new Bgr(255, 255, 255), new Bgr(255, 255, 255), mask, Features2DToolbox.KeypointDrawType.DEFAULT);
result.DrawPolyline(Array.ConvertAll<PointF, Point>(pts, Point.Round), true, new Bgr(Color.Red), 5);
return new Point(Convert.ToInt32((pts[0].X + pts[1].X) / 2), Convert.ToInt32((pts[0].Y + pts[3].Y) / 2));
}
}
catch (Exception e)
{
return new Point(0, 0);
}
return new Point(0,0);
}