/* 拼接的方法封装到 MatchPicRealtime(Mat matNew, Mat matBig)函数中。 函数执行流程: 1、采用OpenCvSharp.Feature2D.ORB的DetectAndCompute算法,检测和计算特征点。 2、采用OpenCvSharp.DescriptorMatcher.BFMatcher的KnnMatch算法,对检测出来的特征点进行匹配,得到DMatch对象的集合。 3、筛选DMatch对象的集合,计算得到横向距离差和纵向距离差。 4、对匹配的结果做过滤,将拼接好的图像返回。 */ using OpenCvSharp; using System; using System.Collections.Generic; using System.Linq; namespace PaintDotNet.Adjust { /// /// 图像处理 /// public unsafe class AdjustMethods { public static Mat LastMat; public static double LastX; public static double LastY; private static Mat BackupMat; private static Rect LastRect; /// /// 图像拼接方法,返回拼接好的mat /// /// 需要拼接的mat1 /// 需要拼接的mat2 /// 拼接成功/失败 /// 拼接的矩形边框 /// public static Mat MatchPicRealtime(Mat matSrc, Mat matTo, out Boolean errorFlag, out Rect rect) { errorFlag = false; Mat mat = matSrc.Clone(); try { BackupMat = LastMat; mat = MatchPicRealtime(matSrc, matTo); } catch (Exception) { LastMat = BackupMat; errorFlag = true; } rect = LastRect; return mat; } public static Mat MatchPicRealtime(Mat matNew, Mat matBig) { if (LastMat == null) { LastMat = matNew; return matNew.Clone(); } using (Mat matSrcRet = new Mat()) using (Mat matToRet = new Mat()) { KeyPoint[] keyPointsSrc, keyPointsTo; using (var surf = OpenCvSharp.ORB.Create(800)) { surf.DetectAndCompute(LastMat, null, out keyPointsSrc, matSrcRet); surf.DetectAndCompute(matNew, null, out keyPointsTo, matToRet); LastMat = matNew; } using (var bfMatcher = new OpenCvSharp.BFMatcher())//FlannBasedMatcher { var matches = bfMatcher.KnnMatch(matSrcRet, matToRet, k: 2); var pointsSrc = new List(); var pointsDst = new List(); var goodMatches = new List(); //数组按照元素个数由多到少排序 var arrXMatches = new List(); var arrYMatches = new List(); float XCompute = 0; float YCompute = 0; int coutPt = 0; foreach (DMatch[] items in matches.Where(x => x.Length > 1)) { if (items[0].Distance < 0.5 * items[1].Distance) { var X_dis = (keyPointsSrc[items[0].QueryIdx].Pt.X - keyPointsTo[items[0].TrainIdx].Pt.X); var Y_dis = (keyPointsSrc[items[0].QueryIdx].Pt.Y - keyPointsTo[items[0].TrainIdx].Pt.Y); arrXMatches.Add((int)X_dis); arrYMatches.Add((int)Y_dis); } } int[] arrX = arrXMatches.ToArray(); int[] arrY = arrYMatches.ToArray(); Array.Sort(arrX); Array.Sort(arrY); int valX1 = arrX[0];//当前 int valX2 = 0;//上一个 int timeX1 = 1;//当前 int timeX2 = 0;//上一个 for (int sortI = 1; sortI < arrX.Length; sortI++) { if (arrX[sortI] == valX1) { timeX1++; } else { if (timeX1 > timeX2) { valX2 = valX1; timeX2 = timeX1; } valX1 = arrX[sortI]; timeX1 = 1; } } if (timeX1 > timeX2) { valX2 = valX1; timeX2 = timeX1; } int valY1 = arrY[0];//当前 int valY2 = 0;//上一个 int timeY1 = 1;//当前 int timeY2 = 0;//上一个 for (int sortI = 1; sortI < arrY.Length; sortI++) { if (arrY[sortI] == valY1) { timeY1++; } else { if (timeY1 > timeY2) { valY2 = valY1; timeY2 = timeY1; } valY1 = arrY[sortI]; timeY1 = 1; } } if (timeY1 > timeY2) { valY2 = valY1; timeY2 = timeY1; } 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]); var X_dis = (keyPointsSrc[items[0].QueryIdx].Pt.X - keyPointsTo[items[0].TrainIdx].Pt.X); var Y_dis = (keyPointsSrc[items[0].QueryIdx].Pt.Y - keyPointsTo[items[0].TrainIdx].Pt.Y); if ((valX2 >= X_dis - 5 && valX2 <= X_dis + 5) && (valY2 >= Y_dis - 5 && valY2 <= Y_dis + 5)) { Console.WriteLine("横向距离差:{0} 纵向距离差:{1}", X_dis, Y_dis); pointsSrc.Add(keyPointsSrc[items[0].QueryIdx].Pt); pointsDst.Add(keyPointsTo[items[0].TrainIdx].Pt); goodMatches.Add(items[0]); //arrXMatches.Add((int)X_dis); ////arrYMatches.Add((int)Y_dis); coutPt++; XCompute += X_dis; YCompute += Y_dis; } else { Console.WriteLine("横向距离差:{0} 纵向距离差:{1} 舍弃", X_dis, Y_dis); } } } LastX += XCompute / coutPt; LastY += YCompute / coutPt;// 600;// //Console.WriteLine("平均横向距离差:{0} 平均纵向距离差:{1}", currentX, currentY);//计算原图像相对于新图像的(距离差)、-->变换矩阵 var outMat = new Mat(); // 算法RANSAC对匹配的结果做过滤 var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d); var pDst = pointsDst.ConvertAll(Point2fToPoint2d); var outMask = new Mat(); var result = new Mat(); // 如果原始的匹配结果为空, 则跳过过滤步骤 if (pSrc.Count > 0 && pDst.Count > 0) { int srcOriX = 0; int srcOriY = 0; int newOriX = 0; int newOriY = 0; // Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask); //得到变换矩阵后拼接图像 int viewWidth = matBig.Width;// 2448; int viewHeight = matBig.Height;// 2040; if (LastX <= 0) { viewWidth = (int)(viewWidth - LastX); srcOriX = (int)(-LastX); newOriX = 0; LastX = 0; } else { viewWidth = (int)Math.Max(LastX + matNew.Width, matBig.Width); newOriX = (int)LastX; //for (int i = 0; i < pDst.Count; i++) //{ // pDst[i] = new Point2d(pSrc[i].X, pDst[i].Y); //} } if (LastY <= 0) { viewHeight = (int)(viewHeight - LastY); srcOriY = (int)(-LastY); newOriY = 0; LastY = 0; } else { //for (int i = 0; i < pDst.Count; i++) //{ // pDst[i] = new Point2d(pDst[i].X, pSrc[i].Y); //} newOriY = (int)LastY; viewHeight = (int)Math.Max(matBig.Height, matNew.Height + LastY); } result = new Mat(new OpenCvSharp.Size(viewWidth, viewHeight), matNew.Type()); //Rect rectSrc = new Rect(0, 0, matNew.Width, matNew.Height); //Mat tempMatSrc = matNew.Clone(rectSrc); Mat tempMatSrc = matNew.Clone(); //复制原有区域part1(<--用户选中的区域)到新图像(<--原图像) Mat mask = tempMatSrc.CvtColor(ColorConversionCodes.RGBA2GRAY); Mat posSrc = new Mat(result, new Rect(newOriX, newOriY, matNew.Width, matNew.Height)); tempMatSrc.CopyTo(posSrc, mask);//原图像复制到新图像 //Rect rect = new Rect(0, 0, matBig.Width, matBig.Height); //Mat tempMat = matBig.Clone(rect); Mat tempMat = matBig.Clone(); //复制原有区域part2(<--用户选中的区域)到新图像(<--原图像) mask = tempMat.CvtColor(ColorConversionCodes.RGBA2GRAY); Mat pos = new Mat(result, new Rect(srcOriX, srcOriY, matBig.Width, matBig.Height)); tempMat.CopyTo(pos, mask);//原图像复制到新图像 LastRect = new Rect(newOriX, newOriY, matNew.Width, matNew.Height); //Bitmap part2 = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(result); ////、、R3a //part2.Save(@"ResSurfA" + indexRes + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg); return result; } result = matNew.Clone(); return result; } } } private static Point2d Point2fToPoint2d(Point2f input) { Point2d p2 = new Point2d(input.X, input.Y); return p2; } } }