C#使用OpenCV剪下影象中的圓形和矩形
前言
本文主要介紹如何使用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
----------------------------------------------------------------------------------------------------
注:此文章為原創,任何形式的轉載都請聯絡作者獲得授權並註明出處!
若您覺得這篇文章還不錯,請點選下方的【推薦】,非常感謝!
https://www.cnblogs.com/kiba/p/14497894.html
&n