AoiEyemMarkMethod.cs 10.0 KB
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using OpenCvSharp.XFeatures2D;
using static AOI.Eyemlib;

namespace AOI
{
    /// <summary>
    /// 从搜索区域中查找Mark区域,对图片进行校准
    /// </summary>
    public class AoiEyemMarkMethod : AoiMethod
    {
        /// <summary>
        /// 放大RoiPath作为SearchPath
        /// </summary>
        public float SearchPathZoom = 1.0f;
        /// <summary>
        /// 相似度百分比
        /// </summary>
        public float SamePercent = 50;

        /// <summary>
        /// 放大RoiPath作为SearchPath
        /// </summary>
        /// <param name="zoom">放大倍率</param>
        /// <returns></returns>
        public GraphicsPath GetSearchPath()
        {
            try
            {
                if (RoiPath != null && RoiPath.PointCount>0 && 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;
            }catch (Exception ex)
            {
                Console.WriteLine(ex.ToString()); 
            }return null;
        }

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

        public Bitmap Fix(Image standardImage, Image imageToCheck, out double sameValue)
        {
            var template = Eyemlib.eyemCvtToEyemImage((Bitmap)standardImage);
            var search = Eyemlib.eyemCvtToEyemImage((Bitmap)imageToCheck);
            
            


            sameValue = 0;
            int iNumMatches = 1;//目标个数
            double dToleranceAngle = 0.0;//要检测目标容忍角度
            double dMaxOverlap = 0.0;//重叠比例
            double dScore = 0.45;//最小分数
            //模板匹配
            EyemTargetMatch[] tpResults = new EyemTargetMatch[iNumMatches];
            IntPtr ResultHandle = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(EyemTargetMatch)) * tpResults.Length);
            var rect  = RoiPath.GetBounds();
            //框选模板匹配位置(通常左上角位置选一块)
            EyemRect tpRoiTemplate = new EyemRect();
            tpRoiTemplate.iXs = (int)rect.X;
            tpRoiTemplate.iYs = (int)rect.Y;
            tpRoiTemplate.iWidth = (int)rect.Width;
            tpRoiTemplate.iHeight = (int)rect.Height;
            //var search = eyemCvtToEyemImage((Bitmap)imageToCheck);
            Eyemlib.eyemCopyRegion(search, tpRoiTemplate, out Eyemlib.EyemImage templ);

            //可以用来调试查看图像
            //Eyemlib.eyemNamedWindow("templ");
            //Eyemlib.eyemImshow("templ", templ);
            //Eyemlib.eyemWaitkey();
            //Eyemlib.eyemImshow("templ", template);
            //Eyemlib.eyemWaitkey();
            Eyemlib.eyemMakeNCCModel(templ, 256);
            //额外用法包括确定电容正负极,比如模板为0方向,搜寻到目标角度|angle|>45°判断极性错误等等
            var flag = eyemFindNCCModel(template, dToleranceAngle, iNumMatches, dMaxOverlap, dScore, true, ResultHandle, out EyemImage tpDstImg);
            if (flag != 0)
                return null;
            eyemImageFree(ref template);
            eyemImageFree(ref search);
            tpResults = eyemIntPtr2StructArray<EyemTargetMatch>(ResultHandle, tpResults.Length).ToArray();

            Bitmap bitmap = eyemCvtToBitmap(tpDstImg);
            eyemImageFree(ref tpDstImg);
            //检测到目标
            for (int i = 0; i < tpResults.Length; i++)
            {
                if (tpResults[i].fMatchScore > 0)
                {
                    sameValue = tpResults[i].fMatchScore*100;
                    CenterOffsetX = tpResults[i].fCenterX - (rect.X + rect.Width / 2);
                    CenterOffsetY = tpResults[i].fCenterY - (rect.Y + rect.Height / 2);
                    Console.WriteLine(string.Format("目标{0},位置({1},{2}),位置({3},{4}),匹配分数{5}",
                            i, tpResults[i].fCenterX.ToString("F3"), tpResults[i].fCenterY.ToString("F3"), CenterOffsetX, CenterOffsetY, tpResults[i].fMatchScore.ToString("F3")));

                    
                }
            }

            Marshal.FreeHGlobal(ResultHandle);
            return bitmap;
            
        }

        public Image FixImage(Image standardImage, Image imageToCheck,out double sameValue)
        {
            var affine = Fix(standardImage, imageToCheck, out sameValue);
            if (affine != null)
            {
                return affine;
            }

            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;
        }
    }
}