AoiEyemTemplateMethod.cs 9.8 KB
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using OpenCvSharp.Blob;
using OpenCvSharp.ImgHash;
using OpenCvSharp.XFeatures2D;
using static AOI.Eyemlib;

namespace AOI
{
    /// <summary>
    /// 模板匹配
    /// </summary>
    public class AoiEyemTemplateMethod : AoiMethod
    {
        /// <summary>
        /// 相似度百分比
        /// </summary>
        public double SamePercent = 80;
        public double AnglePercent = 180;

        public override ResultBean Check(Image standardImage, Image imageToCheck)
        {
            ResultBean resultBean = new ResultBean(MethodName,4,SamePercent,SamePercent);
           
            bool needCut = true;
            Image standardRoiImg = GetRoiImage(standardImage, needCut);
            resultBean.standardRoiImage = standardRoiImg;
            double percent=0d, angle=0d;
            Image cutImg = null;
            //将基准图切割成小块
            if (CuttingAOIArea(standardImage, out Bitmap standardAOIImage, out RectangleF rect))
            {
                (percent, angle) = GetTemplateMatchPercent(standardAOIImage, imageToCheck,rect ,out cutImg);
            }           
            bool result = false;
            if(SamePercent > 100)
            {
                SamePercent = 100;
            }

            if(percent >= SamePercent && angle<=AnglePercent)
            {
                result = true;
            }
            if (!resultBean.result)
            {
                if (string.IsNullOrEmpty(AoiProject.FilePath))
                {
                    AoiProject.FilePath = "logs\\temp";
                }

                string p = getSaveImagePath(AoiProject.FilePath, this.RoiPath);
                string aoipath = AoiProject.FilePath;
                string xypath= Path.GetFileNameWithoutExtension(aoipath);
                PointF[] points = this.RoiPath.PathData.Points;
                //if (Directory.Exists(xypath))
                //{
                //    string pst = null;
                //    PointF[] points = this.RoiPath.PathData.Points;
                //    if (points.Length >= 4)
                //    {
                //        for (int i = 0; i < points.Length; i++)
                //        {
                //            pst += (int)points[i].X + "-" + (int)points[i].Y + "_";
                //        }
                //        string path = Path.Combine(xypath,pst);
                if (Directory.Exists(p))
                {
                    string[] files = Directory.GetFiles(p);

                    foreach (string file in files)
                    {
                        try
                        {

                            if (file.EndsWith(".jpg") || file.EndsWith(".png") || file.EndsWith(".bmp") || file.EndsWith(".gif"))
                            {
                                Bitmap bmp = new Bitmap(file);
                                RectangleF rects = new RectangleF(points[0], new System.Drawing.Size(bmp.Width, bmp.Height));
                                (percent, angle) = GetTemplateMatchPercent(bmp, imageToCheck, rects, out cutImg);
                                if (percent >= SamePercent && angle <= AnglePercent)
                                {
                                    result = true;
                                    break;
                                }
                            }
                        }
                        catch(Exception ex)
                        { 
                            Console.WriteLine( MethodName+"  补充检测["+ file + "]出错:" + ex.ToString());
                        }
                    }
                }
                    //}
                //}               
            }
            resultBean.currentRoiImage = cutImg;
            resultBean.result = result;
            resultBean.percentValue = Math.Round(percent, 3);
            resultBean.checkData = angle;
            return resultBean;
        }
        private string getSaveImagePath(string aoiFileName, GraphicsPath gPath)
        {
            string path = Path.GetDirectoryName(aoiFileName);
            string fileName = Path.GetFileNameWithoutExtension(aoiFileName);
            System.Drawing.PointF[] pts = gPath.PathPoints;
            string pPath = "";
            for (int i = 0; i < pts.Length; i++)
            {
                pPath += (int)pts[i].X + "-" + (int)pts[i].Y + "_";
            }

            return Path.Combine(path, fileName + @"\" + pPath + @"\");
        }
        /// <summary>
        /// 获取模板相似度
        /// </summary>
        /// <param name="standardImage"></param>
        /// <param name="imageToCheck"></param>
        /// <param name="cutImg"></param>
        /// <returns></returns>
        public (double,double) GetTemplateMatchPercent(Image standardAOIImage, Image imageToCheck, RectangleF rect, out Image cutImg)
        {
            cutImg = null;
            //if (RoiPath == null)
            //    return (0d, 0d);
            //var rect = RoiPath.GetBounds();
            //if (rect == RectangleF.Empty)
            //    return (0d, 0d);
            //float MatchRoi_Inflate = ConfigHelper.Config.Get("SURF_MatchRect_Inflate", 0.3f);
            //var newroi = new RectangleF(rect.Location,rect.Size);
            //newroi.Inflate(rect.Width * MatchRoi_Inflate, rect.Height * MatchRoi_Inflate);
            //var bitmap1 = AoiProject.CropBitmap((Bitmap)standardImage, newroi);
            var template = Eyemlib.eyemCvtToEyemImage((Bitmap)standardAOIImage);
            var search = Eyemlib.eyemCvtToEyemImage((Bitmap)imageToCheck);

            int iNumMatches = 5;//目标个数
            double dToleranceAngle = 0.0;//要检测目标容忍角度
            double dMaxOverlap = 0.0;//重叠比例
            double dScore = 0.8;//最小分数
            
            //模板匹配
            EyemTargetMatch[] tpResults = new EyemTargetMatch[iNumMatches];
            IntPtr ResultHandle = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(EyemTargetMatch)) * tpResults.Length);

            rect.X -= CenterOffsetX;
            rect.Y -= CenterOffsetY;
            //框选模板匹配位置(通常左上角位置选一块)
            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);
            if(templ.iWidth==0)
                return (0, 0);
            cutImg = eyemCvtToBitmap(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 = Eyemlib.eyemFindNCCModel(template, dToleranceAngle, iNumMatches, dMaxOverlap, dScore, true, ResultHandle, out EyemImage tpDstImg);
            if (flag != 0)
                return (0,0);

            eyemImageFree(ref templ);
            eyemImageFree(ref template);
            eyemImageFree(ref search);

            tpResults = Eyemlib.eyemIntPtr2StructArray<EyemTargetMatch>(ResultHandle, tpResults.Length).ToArray();

            int matchscore = 0;
            double anglescore = 0;
            double maxscore = 0;
            //检测到目标
            for (int i = 0; i < tpResults.Length; i++)
            {
                if (tpResults[i].fMatchScore > 0 && tpResults[i].fMatchScore>maxscore)// && rect.Contains(tpResults[i].fCenterX, tpResults[i].fCenterY))
                {
                    
                    matchscore = (int)(tpResults[i].fMatchScore*100);
                    anglescore = Math.Abs(tpResults[i].fMatchAngle);
                    maxscore = matchscore;
                    Console.WriteLine(string.Format("目标{0},位置({1},{2}),匹配分数{3}",
                            i, tpResults[i].fCenterX.ToString("F3"), tpResults[i].fCenterY.ToString("F3"), tpResults[i].fMatchScore.ToString("F3")));


                }
            }
            if (matchscore == 0) {
                Bitmap bitmap = Eyemlib.eyemCvtToBitmap(tpDstImg);
                bitmap.Save("\\match.png");
            }
            eyemImageFree(ref tpDstImg);
            Marshal.FreeHGlobal(ResultHandle);
            return (matchscore,anglescore);
        }

        public bool CuttingAOIArea(Image standardImage,out Bitmap standardAOIImage,out RectangleF rect) 
        {
            standardAOIImage = null;
            rect = new RectangleF();
            try
            {
                if (RoiPath == null)
                    return false;
                rect = RoiPath.GetBounds();
                if (rect == RectangleF.Empty)
                    return false;
                float MatchRoi_Inflate = ConfigHelper.Config.Get("SURF_MatchRect_Inflate", 0.3f);
                var newroi = new RectangleF(rect.Location, rect.Size);
                newroi.Inflate(rect.Width * MatchRoi_Inflate, rect.Height * MatchRoi_Inflate);
                standardAOIImage= AoiProject.CropBitmap((Bitmap)standardImage, newroi);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }
    }
}