C#使用OpenCV剪切圖像中的圓形和矩形

前言html

本文主要介紹如何使用OpenCV剪切圖像中的圓形和矩形。git

準備工做github

首先建立一個Wpf項目——WpfOpenCV,這裏版本使用Framework4.7.2。windows

而後使用Nuget搜索【Emgu.CV】,以下圖。app

這裏的Emgu.CV選擇4.3.0.3890版本,而後安裝Emgu.CV和Emgu.CV.runtime.windows。函數

使用OPenCV剪切矩形測試

如今,咱們進入項目,進行OPenCV的調用。動畫

首先引入命名空間,以下:spa

using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System.Drawing;
using System.Windows.Forms;

而後編寫矩形剪切函數——CutRectangleImage。debug

函數裏,咱們先將圖像進行縮放,這樣能夠有效的減小檢測到的矩形數量。

再將圖片處理成灰度模式,而後再高斯模糊,再邊緣化。

而後,咱們就能夠在圖片裏查找圖形輪廓了,當輪廓有三個頂點,那麼它是三角形,若是有四個頂點,那麼它是四邊形;咱們要截取矩形,因此這裏要加一個角度的判斷,四個角必須都在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

----------------------------------------------------------------------------------------------------

注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的推薦】,很是感謝!

https://www.cnblogs.com/kiba/p/14497894.html

 

相關文章
相關標籤/搜索