基于网络上很多都是C++ 、Python实现的图像拼接,或者直接调用的Stitch函数进行的拼接,想着手动实现一个C#版本,具体思路如下:
实际拼接效果如下:
实现起来不是很难,但还是有许多可以优化的点,这些放在以后把。
下面展示一些 内联代码片
。
///
/// 基于SIFT算法特征提取进行拼接
///
///
///
private void btnSift_Click(object sender, EventArgs e)
{
#region 读取图片
//这一部分相对而言较为简单,读者按自己需求编写就可
#endregion
//从这开始是正式流程
/*
* 图像二值化,加快特征点检测速度
*/
Mat srcImgBgr2Gray = new Mat();
Mat testImgBgr2Gray = new Mat();
Cv2.CvtColor(srcImg, srcImgBgr2Gray,ColorConversionCodes.BGRA2GRAY);
Cv2.CvtColor(testImg, testImgBgr2Gray, ColorConversionCodes.BGR2GRAY);
#region 创建 SIFT
//var MySift = OpenCvSharp.Features2D.SIFT.Create(400);
var MySift = OpenCvSharp.XFeatures2D.SIFT.Create(2000);
/*
* 特征点
*/
KeyPoint[] srcKeyPoints = MySift.Detect(srcImgBgr2Gray);
KeyPoint[] testKeyPoints = MySift.Detect(testImgBgr2Gray);
/*
* 描述子
*/
Mat srcDescriptors = new Mat();
Mat testDescriptors = new Mat();
/*
* 计算特征向量
*/
srcKeyPoints = MySift.Detect(srcImg);
testKeyPoints = MySift.Detect(testImg);
MySift.Compute(srcImgBgr2Gray, ref srcKeyPoints, srcDescriptors);
MySift.Compute(testImgBgr2Gray, ref testKeyPoints, testDescriptors);
#endregion
#region 创建FLANN匹配器
FlannBasedMatcher matcher = new FlannBasedMatcher();
DMatch[] matches = matcher.Match(srcDescriptors, testDescriptors);
#endregion
#region 比率测试
double max_dist = 0;
double min_dist = 100;
for (int i = 0; i < srcDescriptors.Rows; i++)
{
double dist = matches[i].Distance;
if (dist < 0.9 * min_dist)
{
min_dist = dist;
}
if (dist > 0.9 * max_dist)
{
max_dist = dist;
}
}
List<DMatch> good_matches = new List<DMatch>();
for (int i = 0; i < srcDescriptors.Rows; i++)
{
if (matches[i].Distance < 3 * min_dist)
{
good_matches.Add(matches[i]);
}
}
if (good_matches.Count <= 0)
{
MessageBox.Show("未匹配到特征点,无法进行拼接!\n请尝试手动拼接","提醒");
return;
}
/*
* 绘制匹配点
*/
Mat img_matches = new Mat();
Cv2.DrawMatches(srcImg, srcKeyPoints, testImg, testKeyPoints, good_matches, img_matches);
/*
* 创建模板
*/
Mat srcImgTemp = new Mat();
Cv2.CopyMakeBorder(srcImg, srcImgTemp, top, bottom, left, right, BorderTypes.Constant, 0);
Mat testImgTemp = new Mat();
Cv2.CopyMakeBorder(testImg, testImgTemp, top, bottom, left, right, BorderTypes.Constant, 0);
#endregion
#region 透视变换
List<Point2f> srcImgTransf = new List<Point2f>();
List<Point2f> testImgTransf = new List<Point2f>();
for (int i = 0; i < good_matches.Count; i++)
{
srcImgTransf.Add(srcKeyPoints[good_matches[i].QueryIdx].Pt);
testImgTransf.Add(testKeyPoints[good_matches[i].TrainIdx].Pt);
}
List<Point2d> srcImgPoints = srcImgTransf.ConvertAll(Point2fToPoint2d);
List<Point2d> testImgPoints = testImgTransf.ConvertAll(Point2fToPoint2d);
/*
* 单应性矩阵
*/
Mat testImgWarp = new Mat();
Mat M = Cv2.FindHomography(testImgPoints, srcImgPoints, HomographyMethods.Ransac, 5.0);
OpenCvSharp.Size sizeTest = new OpenCvSharp.Size(testImg.Cols, testImg.Rows + 250);
//CalcCorners(M, testImg);
/*
* 透视变换
*/
Cv2.WarpPerspective(testImgTemp, testImgWarp, M, sizeTest, InterpolationFlags.WarpFillOutliers, BorderTypes.Constant, 0);
#endregion
#region 图像融合
/*
* ①图像融合
*/
int dstHeight = testImgWarp.Rows;
int dstWidth = srcImg.Cols;
Mat dst = new Mat(dstHeight, dstWidth, MatType.CV_8UC3);
dst.SetTo(0);
Rect rect = new Rect(0, 0, testImgWarp.Cols, testImgWarp.Rows);
Mat dstTestImgWarp = new Mat(dst, rect);
testImgWarp.CopyTo(dstTestImgWarp);
Rect rectTestImg = new Rect(0, 0, srcImg.Cols, srcImg.Rows);
Mat dstTestImg = new Mat(dst, rectTestImg);
srcImg.CopyTo(dstTestImg);
/*
* ②黑边处理
*/
//OptimizeSeam(srcImg, dstTestImgWarp,dst);
#endregion
/*
* 输出图像
*/
Bitmap map = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(dst);
pbStitchImg.Image = map;
}
以上便是本次实现过程demo,期待和大家共同探讨。