目次
SURF.Create() は SURF 検出器を作成します
detecter.DetectAndCompute() はキーポイントを検出し、特徴記述ベクトルを計算します
序文:
SURF
(Speeded Up Robust features) は、特徴検出および画像マッチングのためのアルゴリズムであり、その主な特徴は、優れた特徴マッチング性能を提供しながら、画像変換に対する高速性と堅牢性です。
コア機能:
SURF.Create() は SURF 検出器を作成します
SURF.Create(hessianThreshold, extended, upright)
hessianThreshold
: ヘシアンしきい値。キー ポイントの検出を制御するために使用されます。一般に、値が大きいほど、キーポイントは少なくなりますが、重要なキーポイントは多くなります。extended
:true
の場合、拡張 SURF 記述子が使用されます。拡張記述子にはより多くの情報が含まれますが、サイズも大きくなります。upright
:true
の場合、直立した (回転していない) SURF フィーチャのみが検出されます。これにより、場合によってはパフォーマンスが向上することがあります。
detecter.DetectAndCompute() はキーポイントを検出し、特徴記述ベクトルを計算します
DetectAndCompute(Mat image, Mat mask, out KeyPoint[] keypoints, Mat descriptors)
DetectAndCompute(Mat image, Mat mask, out KeyPoint[] keypoints, Mat descriptors)
キーポイントを検出し、特徴記述ベクトルを計算するために使用されます。image
:キーポイントを検出する画像。mask
: キーポイントの検出領域を制限するために使用できるオプションのマスク。keypoints
: 検出されたキーポイントを保存するための出力パラメータ。descriptors
: 計算された特徴記述ベクトルを保存する出力パラメーター。
Cv2.DrawMatches() 特徴一致を描画します
void Cv2.DrawMatches(
Mat img1, KeyPoint[] keypoints1,
Mat img2, KeyPoint[] keypoints2,
DMatch[] matches1to2,
Mat outImg,
Scalar matchColor, Scalar singlePointColor,
MatOfByte mask,
DrawMatchesFlags flags = DrawMatchesFlags.Default );
img1
およびimg2
: 特徴一致を描画するために使用される 2 つの入力イメージ。keypoints1
とkeypoints2
: それぞれ 2 つの画像のキー ポイント配列です。matches1to2
: 特徴点のマッチング関係を含む配列。通常、特徴マッチング アルゴリズム (SIFT、SURF、ORB など) を使用して取得されます。outImg
: 出力結果 (通常は空のイメージ) を格納する Mat オブジェクト。このメソッドはこのオブジェクト上にフィーチャ マッチを描画します。matchColor
: 一致する線を描画するために使用される色。singlePointColor
: 単一の特徴点を描画するために使用される色。mask
: オプションのパラメータ。どの一致が有効であるかを示すマスク イメージ。マスクを使用しない場合は、null
を渡すことができます。flags
: フィーチャの一致を描画する方法を制御するオプションのパラメーター。DrawMatchesFlags.Default
またはその他のオプションを指定できます。
KeyPoint
親切:
- 位置、スケール、方向、その他の情報を含む、検出されたキーポイントを表すために使用されるクラス。
一致するアイデア:
1. 検出器を作成する
2. キーポイントを検出し、特徴記述ベクトルを計算します
3. 特徴点マッチャーを作成し、マッチングを実行する
4. 2 つの画像を照合して得られたキー ポイントを描画します。
5. 一致する画像を表示する
コードフロー:
注意事項が詳しく記載されていますので、よくお読みください。 SURFマッチングは優良なマッチングポイントを選別するロジックを追加し、SIFTマッチングがマッチングのメインプロセスとなります。
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Point = OpenCvSharp.Point;
namespace SURF_and_SIFT特征匹配
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Mat srcImage1;
Mat srcImage2;
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Image Files(*.jpg;*.png*;*.bmp*)|*.jpg;*.png*;*.bmp";
if (ofd.ShowDialog() != DialogResult.OK)
return;
string imagePath = ofd.FileName;
srcImage1 = Cv2.ImRead(imagePath, ImreadModes.AnyColor);
Cv2.ImShow("src image1", srcImage1);
}
private void button2_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Image Files(*.jpg;*.png*;*.bmp*)|*.jpg;*.png*;*.bmp";
if (ofd.ShowDialog() != DialogResult.OK)
return;
string imagePath = ofd.FileName;
srcImage2 = Cv2.ImRead(imagePath, ImreadModes.AnyColor);
Cv2.ImShow("src image2", srcImage2);
}
private void button3_Click(object sender, EventArgs e)
{
// 定义SURF中的hessian阈值特征点检测算子
int minHessian = 400;
// 定义一个特征检测类对象
var MySurf = OpenCvSharp.XFeatures2D.SURF.Create(minHessian, 4, 3, true, true);
Mat descriptors1 = new Mat();
Mat descriptors2 = new Mat();
// 模板类是能够存放任意类型的动态数组,能够增加和压缩数据
// 方法1:计算描述符(特征向量),将Detect和Compute操作分开
//KeyPoint[] keyPoint1 = MySurf.Detect(srcImage1);
//KeyPoint[] keyPoint2 = MySurf.Detect(srcImage2);
//MySurf.Compute(srcImage1, ref keyPoint1, descriptors1);
//MySurf.Compute(srcImage2, ref keyPoint2, descriptors2);
// 方法2:计算描述符(特征向量),将Detect和Compute操作合并
KeyPoint[] keyPoint1, keyPoint2;
MySurf.DetectAndCompute(srcImage1, null, out keyPoint1, descriptors1);
MySurf.DetectAndCompute(srcImage2, null, out keyPoint2, descriptors2);
// 使用BruteForce进行匹配 暴力匹配
// 创建特征点匹配器
BFMatcher matcher = new BFMatcher(NormTypes.L2, crossCheck: false);
// 匹配两幅图中的描述子(descriptors)
DMatch[] matches = matcher.Match(descriptors1, descriptors2);
// 设置比率阈值
double ratio_thresh = 0.2;
List<DMatch> good_matches = new List<DMatch>();
for (int i = 0; i < matches.Length; i++)
{
if (matches[i].Distance < ratio_thresh)
{
good_matches.Add(matches[i]);
}
}
if (good_matches.Count <= 4)
{
// 匹配点不足,无法进行透视变换
MessageBox.Show("合格匹配点数量不足" + good_matches.Count);
}
// 创建一个新的图像以绘制匹配结果
Mat imgMatches = new Mat();
// 绘制匹配关键点
Cv2.DrawMatches(srcImage1, keyPoint1, srcImage2, keyPoint2, good_matches, imgMatches, null, null, null, DrawMatchesFlags.NotDrawSinglePoints);
// -------锚定物体------------
// 创建两个数组来存储匹配成功的特征点,一个用于物体图像(obj),另一个用于场景图像(scene)
Point2d[] obj = new Point2d[good_matches.Count()], scene = new Point2d[good_matches.Count()];
// 遍历匹配成功的特征点
for (int i = 0; i < good_matches.Count(); i++)
{
// 获取查询图像中特征点的坐标,通过good_matches[i].QueryIdx找到对应特征点的索引
obj[i] = keyPoint1[good_matches[i].QueryIdx].Pt.ToPoint();
// 获取模板图像中对应的特征点坐标,通过good_matches[i].TrainIdx找到对应特征点的索引
scene[i] = keyPoint2[good_matches[i].TrainIdx].Pt.ToPoint();
}
if (obj.Length < 4)
{
MessageBox.Show("obj优秀匹配点不足,数量为"+ obj.Length);
}
if (scene.Length < 4)
{
MessageBox.Show("匹配点不足,数量为"+ scene.Length);
return;
}
// 使用Cv2.FindHomography方法计算透视变换矩阵H,它可以将物体图像映射到场景图像上
// HomographyMethods.Ransac表示使用RANSAC算法来估计透视变换矩阵,3表示RANSAC算法的最大迭代次数,null表示不使用掩码
Mat H = Cv2.FindHomography(obj, scene, HomographyMethods.Ransac, 3, null);
// 创建两点,初始值设为最小和最大的浮点数,用于存储最左上角和最右下角的点
Point2f topLeft = new Point2f(float.MaxValue, float.MaxValue);
Point2f bottomRight = new Point2f(float.MinValue, float.MinValue);
// 遍历所有匹配点
foreach (DMatch match in good_matches)
{
// 获取当前匹配对中源图像和目标图像的点坐标
Point2f srcPt = keyPoint1[match.QueryIdx].Pt;
Point2f dstPt = keyPoint2[match.TrainIdx].Pt;
// 寻找最左上角的点
topLeft.X = Math.Min(topLeft.X, srcPt.X);
topLeft.Y = Math.Min(topLeft.Y, srcPt.Y);
// 寻找最右下角的点
bottomRight.X = Math.Max(bottomRight.X, srcPt.X);
bottomRight.Y = Math.Max(bottomRight.Y, srcPt.Y);
}
// 将浮点数坐标转换为整数坐标
Point topLeftPoint = new Point((int)topLeft.X, (int)topLeft.Y);
Point bottomRightPoint = new Point((int)bottomRight.X, (int)bottomRight.Y);
// 绘制一个矩形框,框住匹配的区域
Cv2.Rectangle(imgMatches, topLeftPoint, bottomRightPoint, new Scalar(0, 255, 0), 2);
// 显示最终的匹配图像
Cv2.ImShow("匹配图", imgMatches);
}
// SIFT特征匹配
private void button4_Click(object sender, EventArgs e)
{
// 创建检测器
int minHessian = 400;
var MySift = OpenCvSharp.Features2D.SIFT.Create(minHessian);
Mat descriptors1 = new Mat();
Mat descriptors2 = new Mat();
// 计算描述符(特征向量)
KeyPoint[] keyPoint1, keyPoint2;
MySift.DetectAndCompute(srcImage1, null, out keyPoint1, descriptors1);
MySift.DetectAndCompute(srcImage2, null, out keyPoint2, descriptors2);
// 使用BruteForce进行匹配
// 创建特征点匹配器
BFMatcher matcher = new BFMatcher();
// 匹配两幅图中的描述子(descriptors)
DMatch[] matches = matcher.Match(descriptors1, descriptors2);
//【6】绘制从两个图像中匹配出的关键点
Mat imgMatches = new Mat();
Cv2.DrawMatches(srcImage1, keyPoint1, srcImage2, keyPoint2, matches, imgMatches);//进行绘制
// 显示最终的匹配图像
Cv2.ImShow("SIFT匹配", imgMatches);
}
}
}