2013年3月21日 星期四

EmguCV Image Process: Extracting Lines, Contours, and Components part 4

轉戰新時代廣場
EmguCV Image Process: Extracting Lines, Contours, and Components

參考OpenCV 2 Computer Vision Application Programming Cookbook第七章

介紹內容如下:

Detecting image contours with the Canny operator

Detecting circles in images with the Hough transform

Fitting a line to a set of points

Extracting the components' contours

Computing components' shape descriptors

前幾篇提到如何偵測出影像中的線段及圓

那在一般的應用上

可能不只是要在影像上偵測出線段而已

可能是給定一條線段上很多點的資料

然後透過這許多的點

反求出適合描述此線段的方法

在OpenCV中稱為:FitLine

在EmguCV中則稱為:Line2DFitting


這裡先利用之前提到的方法

把這影像中的線段找出來

然後挑選其中一條線段

畫在一張空白的影像上

然後再把這影像上的所有線段上的點

全部找出來並放在一個點的集合之中

這也就是一開始的需求,一組可組成線段的點

然後利用Line2DFitting把這一組點資料轉成線段資料!

Image<Gray, Byte> image = new Image<Gray, Byte>(@"E:\EmguCVBlogImage\chapter7\image.jpg");
//Hough transform for line detection
LineSegment2D[][] lines = image.HoughLines(
    new Gray(125),  //Canny algorithm low threshold
    new Gray(260),  //Canny algorithm high threshold
    1,              //rho parameter
    Math.PI / 180.0,  //theta parameter 
    130,            //threshold
    100,             //min length for a line
    30);            //max allowed gap along the line
Image<Gray, Byte> blackImage = new Image<Gray, Byte>(image.Size);
//draw one line on image
blackImage.Draw(lines[0][0], new Gray(255), 5);

List<Point> points = new List<Point>();
//Iterate over the pixels to obtain all point positions
for (int w = 0; w < blackImage.Width; w++)
{
    for (int h = 0; h < blackImage.Height; h++)
    {
        //if on a contour
        if (blackImage[h, w].Intensity == 255)
        {
            points.Add(new Point(w,h));
        }
    }
}
這時候就把某一條線段畫到一張全黑的影像上

然後取得所有點的集合 points

此線段的影像如下:
這裡畫線的粗細設定為:5

PointF nVector=PointF.Empty;
PointF point = PointF.Empty;
PointCollection.Line2DFitting(points.ToArray(),  //given set of points
                              Emgu.CV.CvEnum.DIST_TYPE.CV_DIST_L2, //distance type
                              out nVector,       //the normalized direction
                              out point);        //a point on the fitted line
//add a vector of length 200 using the uint vector
PointF point2 = new PointF(point.X - 200 * nVector.X, point.Y - 200 * nVector.Y);
LineSegment2D line = new LineSegment2D(Point.Round(point), Point.Round(point2));
Image<Gray, Byte> blackImage2 = new Image<Gray, Byte>(image.Size);
//draw one line on image
blackImage2.Draw(line, new Gray(255), 5);

接著在把這個點集合轉成點的陣列

傳入PointCollection類別中的靜態方法:Line2DFitting

然後帶入用來計算距離的方法,這裡選擇L2:Euclidean distance

歐基里德距離也就是一般兩端點求距離的算法

而這個算法是最快的

在最後兩組參數是用out傳址(By Reference) 的方式帶入

而這兩個參數在EmguCV上的說明為:

normalizedDirection:The normalized direction of the fitted line

aPointOnLine:A point on the fitted line

也就是說會取得此線段上的某一點 (中間點) point

並提供此線段的方向性 nVector

若是要劃出線段

就必須以point為基準

加或減某個長度乘以nVector

而程式的結果如下:

對照一下如果把point2改成下面這樣:

PointF point2 = new PointF(point.X + 200 * nVector.X, point.Y + 200 * nVector.Y);

則畫出來的結果如下:

可以發現point大概的位置就是原本線段的中間位置

那透過加、減一個長度乘上nVector

就會呈現往下或往上的線段


而在PointCollection類別中

還提供了另一個類似的方法:

EllipseLeastSquareFitting(PointF[] points)

找出可以容納某個圓形的最小方形

這個也可以試試看!!


下一篇將介紹如何擷取輪廓

沒有留言:

張貼留言