AoiMarkMethod.cs 6.8 KB
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using OpenCvSharp.XFeatures2D;

namespace AOI
{
    /// <summary>
    /// 从搜索区域中查找Mark区域,对图片进行校准
    /// </summary>
    public class AoiMarkMethod : AoiMethod
    {
        /// <summary>
        /// 放大RoiPath作为SearchPath
        /// </summary>
        public float SearchPathZoom = 2.0f;

        /// <summary>
        /// 放大RoiPath作为SearchPath
        /// </summary>
        /// <param name="zoom">放大倍率</param>
        /// <returns></returns>
        public GraphicsPath GetSearchPath()
        {
            if(RoiPath != null && SearchPathZoom > 0)
            {
                GraphicsPath SearchPath = new GraphicsPath(RoiPath.PathPoints, RoiPath.PathTypes);
                Matrix matrix = new Matrix();
                matrix.Scale(SearchPathZoom, SearchPathZoom);
                SearchPath.Transform(matrix);

                var oldBounds = this.RoiPath.GetBounds();
                var newBounds = SearchPath.GetBounds();
                var oldCenterX = oldBounds.X + oldBounds.Width / 2;
                var oldCenterY = oldBounds.Y + oldBounds.Height / 2;

                var newCenterX = newBounds.X + newBounds.Width / 2;
                var newCenterY = newBounds.Y + newBounds.Height / 2;

                matrix.Reset();
                matrix.Translate(oldCenterX - newCenterX, oldCenterY - newCenterY);
                SearchPath.Transform(matrix);

                return SearchPath;
            }
            return RoiPath;
        }

        /// <summary>
        /// 根据Mark点校正相机获取的图片
        /// </summary>
        /// <param name="standardImage">标准图片</param>
        /// <param name="imageToCheck">相机获取的图片</param>
        /// <returns></returns>
        public override ResultBean Check(Image standardImage, Image imageToCheck)
        {
            ResultBean resultBean = new ResultBean();
            resultBean.standardRoiImage = standardImage;
            Image resultImage = FixImage(standardImage, imageToCheck);
            if(resultImage != null)
            {
                resultBean.result = true;
            }
            return resultBean;
        }

        public Image FixImage(Image standardImage, Image imageToCheck)
        {
            bool needCut = false;
            //标准图中的Mart区域
            Image markImage = GetRoiImage(standardImage, RoiPath, needCut);
            //搜索区域
            var SearchPath = GetSearchPath();
            Image searchImage = GetRoiImage(imageToCheck, SearchPath, needCut);
            if (markImage != null && searchImage != null)
            {
                var affine = GetAffineMat(markImage, searchImage);
                if (affine != null)
                {
                    var matToCheck = ImageUtil.ToMat(imageToCheck);
                    var fixedMat = FixImage(affine, matToCheck);
                    return ImageUtil.ToImage(fixedMat);
                }
            }
            return null;
        }

        /// <summary>
        /// 校准图片
        /// </summary>
        /// <param name="affineMat"></param>
        /// <param name="srcMat"></param>
        /// <returns></returns>
        private Mat FixImage(Mat affineMat, Mat srcMat)
        {
            Mat resultMat = new Mat();
            Cv2.WarpAffine(srcMat, resultMat, affineMat, srcMat.Size());
            return resultMat;
        }
        /// <summary>
        /// 获取映射距阵
        /// </summary>
        /// <param name="markImage"></param>
        /// <param name="srcImage"></param>
        /// <returns></returns>
        private Mat GetAffineMat(Image markImage, Image srcImage)
        {
            Mat markMat = ImageUtil.ToMat(new Bitmap(markImage));
            Mat originalMat = ImageUtil.ToMat(new Bitmap(srcImage));
            Mat srcMat = new Mat();
            //灰度图转换
            Cv2.CvtColor(markMat, markMat, ColorConversionCodes.RGB2GRAY);
            Cv2.CvtColor(originalMat, srcMat, ColorConversionCodes.RGB2GRAY);

            //提取特征点
            SIFT sift = SIFT.Create(200);
            KeyPoint[] markKeyPoints, srcKeyPoints;
            MatOfFloat roiDescriptors = new MatOfFloat();
            MatOfFloat srcDescriptors = new MatOfFloat();
            sift.DetectAndCompute(markMat, null, out markKeyPoints, roiDescriptors);
            sift.DetectAndCompute(srcMat, null, out srcKeyPoints, srcDescriptors);

            var flannMatcher = new FlannBasedMatcher();
            DMatch[] matchePoints = flannMatcher.Match(srcDescriptors, roiDescriptors);
            //提取强特征点
            double minMatch = 1;
            double maxMatch = 0;
            for (int i = 0; i < matchePoints.Length; i++)
            {
                double distance = matchePoints[i].Distance;
                //匹配值最大最小值获取
                if (distance < minMatch)
                {
                    minMatch = distance;
                }
                if (distance > maxMatch)
                {
                    maxMatch = distance;
                }
            }

            List<DMatch> goodMatchePoints = new List<DMatch>();
            for (int i = 0; i < matchePoints.Length; i++)
            {
                if (matchePoints[i].Distance < minMatch + (maxMatch - minMatch) / 4)
                {
                    goodMatchePoints.Add(matchePoints[i]);
                }
            }
            //获取排在前N个的最优匹配特征点
            int num = goodMatchePoints.Count;
            if (num >= 3)
            {
                num = 3;
            }
            else
            {
                //不匹配
                return null;
            }
            List<Point2f> markPoints = new List<Point2f>();
            List<Point2f> srcPoints = new List<Point2f>();
            goodMatchePoints.Sort((left, right) =>
            {
                if (left.Distance > right.Distance)
                    return 1;
                else if (left.Distance == right.Distance)
                    return 0;
                else
                    return -1;
            });

            //Mat matchMat = new Mat();
            //Cv2.DrawMatches(srcMat, srcKeyPoints, markMat, roiKeyPoints, goodMatchePoints.Take(num), matchMat);
            //Cv2.ImShow("Match", matchMat);

            for (int i = 0; i < num; i++)
            {
                srcPoints.Add(srcKeyPoints[goodMatchePoints[i].QueryIdx].Pt);
                markPoints.Add(markKeyPoints[goodMatchePoints[i].TrainIdx].Pt);
            }

            //获取图像1到图像2的投影映射矩阵 尺寸为3*3
            Mat affineMat = Cv2.GetAffineTransform(srcPoints, markPoints);
            return affineMat;
        }
    }
}