This time I will try to detect objects from image by OpenCvSharp, let me introduce the flow first.
這次我要用OpenCvSharp嘗試偵測圖片中的物體,讓我先介紹流程。
- Load image (I load image as gray scale here). | 讀取圖片(我直接讀成灰階)
- Blur the image (I choose Gaussian Blur). | 模糊化圖片(我用高斯模糊)
- Get edge (use canny) | 取得邊緣(使用Canny)
- Find the Contour. | 找出輪廓
- Get right contour and draw on image. | 取得正確的輪廓並畫到圖片上上
The source image is a picture with random item I shot on my desk.
圖片來源是我拍了張桌上隨機物體的照片。
var gray = new Mat(file, ImreadModes.GrayScale);
Then blur the image first , so we can get better result in next step get edge, you can adjust the parameter to get best result for your picture.
然後先模糊化圖片,這樣在下一步取得邊緣時可以取得更好的成果,你可以針對你的照片來調整參數取得最佳結果。
var blur = new Mat();
Cv2.GaussianBlur(gray, blur, new Size(9,9), 0);
Next step is using Canny get edge, you can tune the threshold value to get better result.
下一步是用Canny取得邊緣,你可以調整閥值來取得更好的結果。
var canny = new Mat();
Cv2.Canny(blur,canny,20,135);
After get edge, we can use it to get Contour, if draw contour on image, it should look like this.
取得邊緣後,我們可以利用它來取得輪廓,如果將輪廓畫到圖片上,他會長得像這樣。
Point[][] contours;
HierarchyIndex[] hierarchyIndexes;
Cv2.FindContours(canny,out contours, out hierarchyIndexes, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
Cv2.DrawContours(image,contours,-1,Scalar.GreenYellow,thickness:1);
Then we can use contour data to draw object's rectangle, if we direct use it without filter, sometimes it will look like this.
然後我們就可以用輪廓的資料來畫出物件的外框,如果我們不先處理就直接使用,有時候會變成這樣。
To avoid overlaping, we need do some extra work to filter it before draw, after tuning, we finally get what we want.
為了避免重疊,我們需要多做一些工來在繪製前過濾資料,經過調整後,我們終於得到了我們想要的結果。
The full code is here.
完整程式碼如下。
using System;
using System.Collections.Generic;
using OpenCvSharp;
namespace OpenCvTest3
{
class Program
{
static void Main(string[] args)
{
//get image
var file = "1.jpg";
var gray = new Mat(file, ImreadModes.GrayScale);
var org = new Mat(file);
//blur first
var blur = new Mat();
Cv2.GaussianBlur(gray, blur, new Size(9,9), 0);
#region get canny
var canny = new Mat();
Cv2.Canny(blur,canny,20,135);
#endregion
#region find contours
Point[][] contours;
HierarchyIndex[] hierarchyIndexes;
Cv2.FindContours(
canny,
out contours,
out hierarchyIndexes,
mode: RetrievalModes.External,
method: ContourApproximationModes.ApproxSimple);
#endregion
Console.WriteLine($"contours={contours.Length}");
//get rect info from contour
var rectList = new List();
var rectDraw = new List();
foreach (var c in contours)
{
//skip too small obj
if(c.Length>80)
rectList.Add(Cv2.BoundingRect(c));
}
for (int i = 0; i < rectList.Count; i++)
{
//only draw bigger one if fully overlap
if(CheckInBound(rectList[i],rectList)) rectDraw.Add(rectList[i]);
}
//draw rectangle on image
var image = org.Clone();
foreach (var rect in rectDraw)
{
Cv2.Rectangle(image, new Point(rect.X, rect.Y), new Point(rect.X + rect.Width, rect.Y + rect.Height), Scalar.Red, 2);
}
//draw Contours if you want
//Cv2.DrawContours(image,contours,-1,Scalar.Yellow,thickness:1);
using (new Window("image", image))
using (new Window("gray blur", blur))
using (new Window("canny", canny))
{
Cv2.WaitKey();
}
}
//A stupid way to avoid fully overlap.
private static bool CheckInBound(Rect rect, List list)
{
foreach (var r in list)
{
if (rect.X > r.X && rect.Y > r.Y &&
(rect.X + rect.Width) < (r.X + r.Width) &&
(rect.Y + rect.Height) < (r.Y + r.Height))
return false;
}
return true;
}
}
}
Here's another test result.
這是另一個測試結果。
Hope you enjoy it.
No comments:
Post a Comment