当前位置: 代码网 > 服务器>服务器>Linux > OpenCv案例(十三):基于OpenCVSharp-模板匹之旋转角度匹配

OpenCv案例(十三):基于OpenCVSharp-模板匹之旋转角度匹配

2024年08月01日 Linux 我要评论
OpenCV 模板匹配 旋转匹配 图像识别 OpenCvSharp

原模板匹配方法中,是无法进行任意角度和旋转匹配的,但我们在实际的使用中,模板图像的位置和方向都是不确定的,因此,就需要我们对模板图像进行预处理,处理后在进行模板匹配。

基本处理方法如下:

  • 模板图像读取
  • 进行模糊
  • 绘制轮廓
  • 形态学处理
  • 提取轮廓
  • 获取当前位置角度
  • 图像旋转。
  • 原图读取
  • 从4个角度进行模板匹配(0°,90°,180°,270°)
  • 记录每个角度的匹配值
  • 进行后续操作。

1:原图如下所示:

2:模板图像如下所示:

3:思路:在模板图像中,抠出图像,并且找到当前的旋转角度,将其恢复为正向0°,在进行对其模板匹配,给出匹配结果。处理过程图像如下:

分别是将其调整为正向后,0°,90°,180°,270°的旋转匹配,同时记录匹配度的值。

 

源码图下:

 public list<point2d> getresult(mat tempimage, string srcimgpath, out double robotrotateangel, out double arcval)
 {
     list<point2d> point2ds = new list<point2d>();
     robotrotateangel = 999;
     arcval = 0;
     try
     {
         double rotangel;
         point2f centerpoint;
         rect rect;
         mat tempmat = settemplateimage1(tempimage, out rotangel, out rect, out centerpoint);
         bool matchret = getmatchret1(srcimgpath, tempmat, rotangel, out robotrotateangel, out arcval);

         if (!matchret)
         {
             //图像识别失败
             return null;
         }
     }
     catch (exception ex)
     {
         
     }
     return point2ds;
 }

public mat settemplateimage1(mat image, out double rotangel, out rect rect, out point2f centerpoint)
{
    rotangel = -1;
    rect = new rect();
    centerpoint = new point2f();
    mat blurimg = gaussblurimage(image);//ip.medblurimage(image);// 
    mat cannyimg = new mat();
    cv2.canny(blurimg, cannyimg, 30, 90);
    mat morphimg = morphimage(cannyimg);
    //cv2.imshow("morphimg1", morphimg);
    mat roiimage = getriooutline(morphimg, image, out rotangel, out rect, out centerpoint);
    //cv2.imshow("roiimage", roiimage);
    mat rotimg = imagerotate(roiimage, rotangel, rect);
    //cv2.imshow("rotimg", rotimg);
    mat mat = getroi(rotimg, rect);
    cv2.imshow("mat", mat);
    return mat;
}

        /// <summary>
        /// 高斯 模糊
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        public mat gaussblurimage(mat image)
        {
            mat gaussianimage = new mat();
            cv2.gaussianblur(image, gaussianimage, new opencvsharp.size(3, 3), 3, 3);//尽可能多的去除杂质
            return gaussianimage;
        }

        /// <summary>
        /// 图像形态学操作
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public mat morphimage(mat image)
        {
            mat morphimage = new mat();
            mat kernel = cv2.getstructuringelement(morphshapes.rect, new opencvsharp.size(3, 3), new point(-1, -1));
            cv2.morphologyex(image, morphimage, morphtypes.gradient, kernel, new point(-1, -1), 2);
            return morphimage;
        }

        /// <summary>
        /// 提取图像轮廓
        /// </summary>
        /// <param name="morphimage">形态学图片</param>
        /// <param name="srcimage">原图</param>
        /// <param name="rotateangel">返回旋转角度</param>
        /// <param name="rect">返回获取的矩形边框</param>
        /// <returns></returns>
        public mat getriooutline(mat morphimage, mat srcimage, out double rotateangel, out rect rect, out point2f minrectcenterpoint)
        {
            opencvsharp.point[][] contours;
            hierarchyindex[] hierarchies;
            //cv2.imshow(",mmmm", morphimage);
            cv2.findcontours(morphimage, out contours, out hierarchies, retrievalmodes.external, contourapproximationmodes.approxtc89kcos, new point());
            mat connimg = mat.zeros(morphimage.size(), mattype.cv_8uc3);
            rotateangel = 0;
            //point2f[] vertices = new point2f[4];
            minrectcenterpoint = new point2f();
            rect = new rect();
            int index = 0;
            int count = -1;
            //寻找最大轮廓
            for (int i = 0; i < contours.length; i++)
            {
                for (int j = 0; j < contours[i].length; j++)
                {
                    if (count < contours[i].length)
                    {
                        count = contours[i].length;
                        index = i;
                    }
                }
            }
            //获取轮廓点的矩形区域
            rect = cv2.boundingrect(contours[index]);
            //minarearect():以x轴正方形为起点,顺时针为正,逆时针为负
            //绘制rio区域最小矩形
            #region 绘制rio区域最小矩形
            rotatedrect minrect = cv2.minarearect(contours[index]);
            rotateangel = minrect.angle;
            minrectcenterpoint = minrect.center;
            //vertices = minrect.points(); 
            #endregion
            //绘制最小矩形
            #region 绘制最小矩形
            //cv2.line(srcimage, convert.toint32(vertices[0].x), convert.toint32(vertices[0].y), convert.toint32(vertices[1].x), convert.toint32(vertices[1].y), new scalar(0, 255, 255));
            //cv2.line(srcimage, convert.toint32(vertices[0].x), convert.toint32(vertices[0].y), convert.toint32(vertices[3].x), convert.toint32(vertices[3].y), new scalar(0, 255, 255));
            //cv2.line(srcimage, convert.toint32(vertices[1].x), convert.toint32(vertices[1].y), convert.toint32(vertices[2].x), convert.toint32(vertices[2].y), new scalar(0, 255, 255));
            //cv2.line(srcimage, convert.toint32(vertices[2].x), convert.toint32(vertices[2].y), convert.toint32(vertices[3].x), convert.toint32(vertices[3].y), new scalar(0, 255, 255));
            //console.writeline("anglerect_angle: {0}", minrect.angle); 
            #endregion

            //cv2.imshow("srcimage", srcimage);

            return srcimage;
        }

        /// <summary>
        /// 图像旋转
        /// </summary>
        /// <param name="image">旋转图像</param>
        /// <param name="rotateangel">旋转角度</param>
        /// <returns></returns>
        public mat imagerotate(mat image, double rotateangel, rect rect)
        {
            if (rotateangel > -90 && rotateangel <= -45)
            {
                rotateangel += 90;
            }
            else if (!(rotateangel > -45 && rotateangel < 0))
            {
                //degree = degree;
                rotateangel = 0;
            }
            //opencvsharp.point center = new opencvsharp.point(image.cols / 2, image.rows / 2);
            //以被截取的区域中心位置旋转
            //边缘放大截取
            opencvsharp.point center = new opencvsharp.point(rect.topleft.x + (rect.width / 2), rect.topleft.y + (rect.height / 2));
            //截取最小矩形
            //opencvsharp.point center = new opencvsharp.point(image.cols / 2, image.rows / 2);
            double angle = rotateangel;
            double scale = 1;
            mat rotate = new mat(2, 3, mattype.cv_32fc1);
            rotate = cv2.getrotationmatrix2d(center, angle, scale);//getrotationmatrix2d():以x轴正方形为起点,顺时针为负,逆时针为正
            mat rotimage = new mat();
            cv2.warpaffine(image, rotimage, rotate, image.size(), interpolationflags.linear, bordertypes.wrap);
            int winsize = 0;
            if (image.cols > image.rows)
            {
                winsize = image.cols;
            }
            else
            {
                winsize = image.rows;
            }
            return rotimage;
        }


        /// <summary>
        /// 获取感兴趣区域
        /// </summary>
        /// <param name="image">图像</param>
        /// <param name="rect">面积</param>
        /// <returns></returns>
        public mat getroi(mat image, rect rect)
        {
            //mat imageroi = new mat(image, new rect(new point(rect.topleft.x - rect.width / 2, rect.topleft.y), new size(rect.height, rect.height)));
            mat imageroi = new mat(image, new rect(new point(rect.topleft.x, rect.topleft.y), new size(rect.width, rect.height)));
            return imageroi;
        }

        dictionary<int, double> angeldic1;
        /// <summary>
        /// 模板匹配是否成功
        /// </summary>
        /// <param name="srcbmp">原图</param>
        /// <param name="templatemat">模板图像</param>
        /// <param name="rotangel">初始旋转角度</param>
        /// <param name="robotrotateangel">机器人需要旋转的角度</param>
        /// <returns></returns>
        public bool getmatchret1(string srcimgfilepath, mat templatemat, double rotangel, out double robotrotateangel, out double arcvalue)
        {
            double matchval = 0;
            //最大旋转角度360
            robotrotateangel = 999;
            arcvalue = 0;
            try
            {
                mat src = cv2.imread(@"e:\softwarepackage\codepackage\imgpro\img\t1.bmp");
                //src.convertto(src, mattype.cv_8uc4);
                if (src.empty())
                {
                    //console.writeline("src could not load image...\n");
                    return false;
                }
                cv2.imshow("src", src);
                //mat templatemat = cv2.imread(@"e:\softwarepackage\codepackage\imgpro\img\t21.bmp");
                if (templatemat.empty())
                {
                    //console.writeline("src could not load image...\n");
                    return false;
                }

                angeldic1 = new dictionary<int, double>(); ;
                //记录角度
                int rotateangel = 0;
                //第一张调正之后的图像
                //cv2.imshow(0.tostring(), mat);
                point p;
                bitmap rotatebmp;
                //旋转图像4个角度
                for (int i = 0; i < 4; i++)
                {
                    if (templatemat.rows > src.rows || templatemat.cols > src.cols)
                    {
                        continue;
                    }
                    bitmap bmp = matchtemplate(templatemat, src, out p, out matchval);
                    if (matchval > templatematchthreshold)
                    {
                        angeldic1.add(rotateangel, matchval);
                    }
                    //mat rotimg1 = ip.imagerotate(mat, 90);
                    rotatebmp = bitmapconverter.tobitmap(templatemat);
                    rotatebmp.rotateflip(rotatefliptype.rotate90flipxy);
                    rotateangel += 90;
                    templatemat = bitmapconverter.tomat(rotatebmp);
                    if (1 + i > 3)
                    {
                        break;
                    }
                    cv2.imshow((i + 1).tostring(), templatemat);
                }
                //若匹配值没有符合条件
                if (angeldic1.count < 1)
                {
                    return false;
                }

                list<double> matchret = new list<double>();
                foreach (keyvaluepair<int, double> kvp in angeldic1)
                {
                    matchret.add(kvp.value);
                }
                //判断匹配最大值是否大于设定阈值 否则 匹配失败
                //if (matchret.max() < templatematchthreshold)
                //{
                //    return false;
                //}
                //抠出模板图像 与原图进行模板匹配,并且记住最大匹配度的角度
                var ag = angeldic1.where(q => q.value == matchret.max()).select(q => q.key);
                robotrotateangel = calcrotateangel(rotangel, ag.max());
                //角度转弧度
                arcvalue = robotrotateangel * arc;
            }
            catch (exception ex)
            {
                //loghelper.writelog(ex.message, ex);
            }
            return true;
        }

        private double arc1 = math.pi / 180;

        /// <summary>
        /// 模板匹配
        /// </summary>
        /// <param name="tempalte">模板图片像</param>
        /// <param name="srcpic">被匹配图像</param>
        /// <returns>标注匹配区域的图像</returns>
        public bitmap matchtemplate(mat tempalte, mat srcpic, out opencvsharp.point temppoint, out double matchvalue)
        {

            using (mat result = new mat()) //匹配结果
            {                             //模板匹配
                double minvul;
                double maxvul;
                opencvsharp.point minloc = new opencvsharp.point(0, 0);
                opencvsharp.point maxloc = new opencvsharp.point(0, 0);
                opencvsharp.point matchloc = new opencvsharp.point(0, 0);
                cv2.matchtemplate(srcpic, tempalte, result, templatematchmodes.ccoeffnormed);//ccoeffnormed  最好匹配为1,值越小匹配越差 xxxnormed的算法无需归一化
                                                                                             //cv2.normalize(result, result, 0, 1, normtypes.minmax, -1);//归一化
                cv2.minmaxloc(result, out minvul, out maxvul, out minloc, out maxloc);//查找极值
                                                                                      //cv2.minmaxloc(result, out minvul, out maxvul, out minloc, out maxloc, null);//查找极值
                matchloc = maxloc;//最大值坐标
                                  //result.set(matchloc.y, matchloc.x, 0);//改变最大值为最小值  
                mat mask = srcpic.clone();//复制整个矩阵
                                          //console.writeline("最大值:{0},x:{1},y:{2}", maxvul, matchloc.y, matchloc.x);
                matchvalue = maxvul;
                temppoint.x = matchloc.x;
                temppoint.y = matchloc.y;
                //画框显示 :对角线画框,起点和终点,都用point,线宽
                if (matchvalue > templatematchthreshold)
                {
                    cv2.rectangle(mask, matchloc, new opencvsharp.point(matchloc.x + tempalte.cols, matchloc.y + tempalte.rows), scalar.green, 2);//2代表画的线条的宽细程度
                }
                return opencvsharp.extensions.bitmapconverter.tobitmap(mask);
            }
        }

        /// <summary>
        /// 计算角度
        /// </summary>
        /// <param name="srcangel"></param>
        /// <param name="rotateangle"></param>
        /// <returns></returns>
        public double calcrotateangel(double srcangel, int rotateangle)
        {
            //逆时针旋转为负 顺时针旋转为正
            double tempsrcangel = math.abs(srcangel);
            double resultangel = -1;
            if (tempsrcangel >= 45)
            {
                switch (rotateangle)
                {
                    case 0:
                        resultangel = 90 - tempsrcangel;
                        break;
                    case 90:
                        resultangel = 180 - tempsrcangel;
                        break;
                    case 180:
                        //逆时针
                        resultangel = -(90 + tempsrcangel);
                        break;
                    case 270:
                        //逆时针
                        resultangel = -tempsrcangel;
                        break;
                }
            }
            else if (tempsrcangel < 45)
            {
                switch (rotateangle)
                {
                    case 0:
                        //逆时针
                        resultangel = -tempsrcangel;
                        break;
                    case 90:
                        resultangel = 90 - tempsrcangel;
                        break;
                    case 180:
                        resultangel = 90 + tempsrcangel;
                        break;
                    case 270:
                        //逆时针
                        resultangel = -(90 + tempsrcangel);
                        break;
                }
            }
            return resultangel;
        }

完整代码都在上面了,感兴趣的小伙伴可以在优化优化。下个案例见。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com