前言

本文主要介绍如何使用opencv剪切图像中的圆形和矩形。

准备工作

首先创建一个wpf项目——wpfopencv,这里版本使用framework4.7.2。

然后使用nuget搜索【emgu.cv】,如下图。

这里的emgu.cv选择4.3.0.3890版本,然后安装emgu.cv和emgu.cv.runtime.windows。

使用opencv剪切矩形

现在,我们进入项目,进行opencv的调用。

首先引入命名空间,如下:

using emgu.cv;
using emgu.cv.cvenum;
using emgu.cv.structure;
using system.drawing;
using system.windows.forms;

然后编写矩形剪切函数——cutrectangleimage。

函数里,我们先将图像进行缩放,这样可以有效的减少检测到的矩形数量。

再将图片处理成灰度模式,然后再高斯模糊,再边缘化。

然后,我们就可以在图片里查找图形轮廓了,当轮廓有三个顶点,那么它是三角形,如果有四个顶点,那么它是四边形;我们要截取矩形,所以这里要加一个角度的判断,四个角必须都在80-100度之间。

取到了顶点后,在依据顶点剪切图片就可以了。

下面是截取矩形的代码,代码中只截取了宽度最大的那个矩形。

public void cutrectangleimage(string imagepath)
{
  image<bgr, byte> src = new image<bgr, byte>(imagepath);
  int scale = 1;
  if (src.width > 500)
  {
    scale = 2;
  }
  if (src.width > 1000)
  {
    scale = 10;
  }
  if (src.width > 10000)
  {
    scale = 100;
  }
  var size = new size(src.width / scale, src.height / scale);
  image<bgr, byte> srcnewsize = new image<bgr, byte>(size);
  cvinvoke.resize(src, srcnewsize, size);
  //将图像转换为灰度
  umat grayimage = new umat();
  cvinvoke.cvtcolor(srcnewsize, grayimage, colorconversion.bgr2gray);
  //使用高斯滤波去除噪声
  cvinvoke.gaussianblur(grayimage, grayimage, new size(3, 3), 3);
  umat cannyedges = new umat();
  cvinvoke.canny(grayimage, cannyedges, 60, 180);//通过边缘化,然后取出轮廓
   
  #region 取三角形和矩形的顶点坐标
  list<triangle2df> trianglelist = new list<triangle2df>();
  list<rotatedrect> boxlist = new list<rotatedrect>(); //旋转的矩形框
​
  using (vectorofvectorofpoint contours = new vectorofvectorofpoint())
  {
    cvinvoke.findcontours(cannyedges, contours, null, retrtype.list, chainapproxmethod.chainapproxsimple);
    int count = contours.size;
    for (int i = 0; i < count; i++)
    {
      using (vectorofpoint contour = contours[i])
      using (vectorofpoint approxcontour = new vectorofpoint())
      {
        cvinvoke.approxpolydp(contour, approxcontour, cvinvoke.arclength(contour, true) * 0.08, true);
        //仅考虑面积大于50的轮廓
        if (cvinvoke.contourarea(approxcontour, false) > 50)
        {
          if (approxcontour.size == 3) //轮廓有3个顶点:三角形
          {
            system.drawing.point[] pts = approxcontour.toarray();
            trianglelist.add(new triangle2df(pts[0], pts[1], pts[2]));
          }
          else if (approxcontour.size == 4) //轮廓有4个顶点
          {
            #region 检测角度,如果角度都在 [80, 100] 之间,则为矩形
            bool isrectangle = true;
            system.drawing.point[] pts = approxcontour.toarray();
            linesegment2d[] edges = emgu.cv.pointcollection.polyline(pts, true);
​
            for (int j = 0; j < edges.length; j++)
            {
              double angle = math.abs(edges[(j + 1) % edges.length].getexteriorangledegree(edges[j]));
              if (angle < 80 || angle > 100)
              {
                isrectangle = false;
                break;
              }
            }
            #endregion
            if (isrectangle) boxlist.add(cvinvoke.minarearect(approxcontour));
          }
        }
      }
    }
  }
  #endregion
     
  #region 保存剪切的最大的矩形图片 
  rectangle rectangle = new rectangle(0, 0, src.width, src.height);
  int maxwidth = 0;
  //boxlist = boxlist.where(p => p.size.width > 300).tolist();
  for (int i = 0; i < boxlist.count(); i++)
  {
    rotatedrect box = boxlist[i];
    rectangle rectangletemp = box.minarearect();
    //这里对取到的顶点坐标进行了加宽,因为矩形可能存在角度,这里没有进行角度旋转,所以加宽了取值范围就可以取到完整的图了
    rectangletemp = new rectangle(rectangletemp.x * scale, rectangletemp.y * scale, rectangletemp.width * scale + scale, rectangletemp.height * scale + scale);
    
    //取最大的矩形图片
    if (rectangletemp.width > maxwidth)
    {
      maxwidth = rectangletemp.width;
      rectangle = rectangletemp;
    }
  }
  src.draw(rectangle, new bgr(system.drawing.color.red), 4);//在图片中画线
  cvinvoke.imwrite("原始图片.bmp", src); //保存原始图片
  cvinvoke.cvsetimageroi(src.ptr, rectangle);//设置兴趣点—roi(region of interest )
  var clone = src.clone();
  cvinvoke.imwrite("剪切的矩形图片.bmp", clone); //保存结果图 
  #endregion
  src.dispose();
  srcnewsize.dispose();
  grayimage.dispose();
}

然后编写一个打开文件的函数,在成功打开文件后调用cutrectangleimage。

private void btnrectangle_click(object sender, routedeventargs e)
{
  system.windows.forms.openfiledialog frm = new system.windows.forms.openfiledialog();
  frm.filter = "(*.jpg,*.png,*.jpeg,*.bmp,*.gif)|*.jgp;*.png;*.jpeg;*.bmp;*.gif|all files(*.*)|*.*";
  if (frm.showdialog() == system.windows.forms.dialogresult.ok)
  {
    cutrectangleimage(frm.filename);
  }
}

然后运行项目,点击剪切矩形文件。

然后到debug文件夹下,查看结果。

测试结果如下图所示:

图中红线为检测到矩形后,手动画上去的矩形轮廓。

使用opencv剪切圆形

编写矩形剪切函数——cutcircleimage。

函数里,我们依然先将图像进行缩放,为了有效的减少检测到的圆形数量。

再将图片处理成灰度模式,然后再高斯模糊。

然后再使用霍夫圆检测函数,获取圆的圆心和半径。

最后再根据圆心和半径计算出最小矩形,然后将圆剪切并保存。

代码如下:

public void cutcircleimage(string imagepath)
{
  image<bgr, byte> src = new image<bgr, byte>(imagepath);
  
  int scale = 1;
  if (src.width > 500)
  {
    scale = 2;
  }
  if (src.width > 1000)
  {
    scale = 10;
  }
  if (src.width > 10000)
  {
    scale = 100;
  }
  var size = new size(src.width / scale, src.height / scale);
  image<bgr, byte> srcnewsize = new image<bgr, byte>(size);
  cvinvoke.resize(src, srcnewsize, size);
  //将图像转换为灰度
  umat grayimage = new umat();
  cvinvoke.cvtcolor(srcnewsize, grayimage, colorconversion.bgr2gray);
  //使用高斯滤波去除噪声
  cvinvoke.gaussianblur(grayimage, grayimage, new size(3, 3), 3);
  //霍夫圆检测
  circlef[] circles = cvinvoke.houghcircles(grayimage, houghmodes.gradient, 2.0, 200.0, 100.0, 180.0, 5);
  
  rectangle rectangle = new rectangle();
  float maxradius = 0;
  foreach (circlef circle in circles)
  {
    var center = circle.center;//圆心
    var radius = circle.radius;//半径
    if (radius > maxradius)
    {
      maxradius = radius;
      rectangle = new rectangle((int)(center.x - radius) * scale,
        (int)(center.y - radius) * scale,
        (int)radius * 2 * scale + scale,
        (int)radius * 2 * scale + scale);
    }
    srcnewsize.draw(circle, new bgr(system.drawing.color.blue), 4);
​
  }
  cvinvoke.imwrite("原始图片.bmp", srcnewsize); //保存原始图片
  if (maxradius == 0)
  {
    messagebox.show("没有圆形");
  }
  cvinvoke.cvsetimageroi(srcnewsize.ptr, rectangle);//设置兴趣点—roi(region of interest )
  var clone = srcnewsize.clone();
  cvinvoke.imwrite("剪切的圆形图片.bmp", clone); //保存结果图 
  src.dispose();
  srcnewsize.dispose();
  grayimage.dispose();
}

运行项目进行测试,结果如下:

—————————————————————————————————-

到此,c#使用opencv剪切图像中的圆形和矩形就已经介绍完了。

代码已经传到github上了,欢迎大家下载。

github地址:https://github.com/kiba518/opencv_cutimage

到此这篇关于c#使用opencv剪切图像中的圆形和矩形的文章就介绍到这了,更多相关c#剪切图像中的圆形和矩形内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!