前言
圖像特征提取是計算機視覺和圖像處理中的一個概念。它指的是使用計算機提取圖像信 息,決定每個圖像的點是否屬於一個圖像特征。本文主要探討如何提取圖像中的“角點”這 一特征,及其相關的內容。而諸如直方圖、邊緣、區域等內容在前文中有所提及,請查看相 關文章。OpenCv(EmguCv)中實現了多種角點特征的提取方法,包括:Harris角點、 ShiTomasi角點、亞像素級角點、SURF角點、Star關鍵點、FAST關鍵點、Lepetit關鍵點等等 ,本文將逐一介紹如何檢測這些角點。在此之前將會先介紹跟角點檢測密切相關的一些變換 ,包括Sobel算子、拉普拉斯算子、Canny算子、霍夫變換。另外,還會介紹一種廣泛使用而 OpenCv中並未實現的SIFT角點檢測,以及最近在OpenCv中實現的MSER區域檢測。所要講述的 內容會很多,我這裡盡量寫一些需要注意的地方及實現代碼,而參考手冊及書本中有的內容 將一筆帶過或者不會提及。
Sobel算子
Sobel算子用多項式計算來擬合導數計算,可以用OpenCv中的cvSobel函數或者EmguCv中 的 Image<TColor,TDepth>.Sobel方法來進行計算。需要注意的是,xorder和yorder 中必須且只能有一個為非零值,即只能計算x方向或者y反向的導數;如果將方形濾波器的寬 度設置為特殊值CV_SCHARR(-1),將使用Scharr濾波器代替Sobel濾波器。
使用Sobel濾波器的示例代碼如下:
Sobel算子
private string SobelFeatureDetect()
{
//獲取參數
int xOrder = int.Parse((string) cmbSobelXOrder.SelectedItem);
int yOrder = int.Parse((string) cmbSobelYOrder.SelectedItem);
int apertureSize = int.Parse((string) cmbSobelApertureSize.SelectedItem);
if ((xOrder == 0 && yOrder == 0) || (xOrder != 0 && yOrder != 0))
return "Sobel算子,參數錯誤:xOrder和yOrder 中必須且只能有一個非零。\r\n";
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
Image<Gray, Single> imageDest = imageSourceGrayscale.Sobel(xOrder, yOrder, apertureSize);
sw.Stop();
//顯示
pbResult.Image = imageDest.Bitmap;
//釋放資源
imageDest.Dispose();
//返回
return string.Format("·Sobel算子,用時{0:F05}毫秒, 參數(x方向求導階數:{1},y方向求導階數:{2},方形濾波器寬度:{3})\r\n", sw.Elapsed.TotalMilliseconds, xOrder, yOrder, apertureSize);
}
拉普拉斯算子
拉普拉斯算子可以用作邊緣檢測;可以用OpenCv中的cvLaplace函數或者EmguCv中的 Image<TColor,TDepth>.Laplace方法來進行拉普拉斯變換。需要注意的是:OpenCv的 文檔有點小錯誤,apertureSize參數值不能為CV_SCHARR(-1)。
使用拉普拉斯變換的示例代碼如下:
拉普拉斯算子
private string LaplaceFeatureDetect()
{
//獲取參數
int apertureSize = int.Parse((string) cmbLaplaceApertureSize.SelectedItem);
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
Image<Gray, Single> imageDest = imageSourceGrayscale.Laplace(apertureSize);
sw.Stop();
//顯示
pbResult.Image = imageDest.Bitmap;
//釋放資源
imageDest.Dispose();
//返回
return string.Format("·拉普拉斯變換,用時{0:F05}毫 秒,參數(方形濾波器寬度:{1})\r\n", sw.Elapsed.TotalMilliseconds, apertureSize);
}
Canny算子
Canny算子也可以用作邊緣檢測;可以用OpenCv中的cvCanny函數或者EmguCv中的 Image<TColor,TDepth>.Canny方法來進行Canny邊緣檢測。所不同的是, Image<TColor,TDepth>.Canny方法可以用於檢測彩色圖像的邊緣,但是它只能使用 apertureSize參數的默認值3;
而cvCanny只能處理灰度圖像,不過可以自定義apertureSize。cvCanny和Canny的方法參 數名有點點不同,下面是參數對照表。
Image<TColor,TDepth>.Canny CvInvoke.cvCanny
thresh lowThresh
threshLinking highThresh
3 apertureSize
值得注意的是,apertureSize只能取3,5或者7,這可以在cvcanny.cpp第87行看到:
aperture_size &= INT_MAX; if( (aperture_size & 1) == 0 || aperture_size < 3 || aperture_size > 7 ) CV_ERROR( CV_StsBadFlag, "" );
使用Canny算子的示例代碼如下:
Canny算子
private string CannyFeatureDetect()
{
//獲取參數
double lowThresh = double.Parse (txtCannyLowThresh.Text);
double highThresh = double.Parse (txtCannyHighThresh.Text);
int apertureSize = int.Parse((string) cmbCannyApertureSize.SelectedItem);
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
Image<Gray, Byte> imageDest = null;
Image<Bgr, Byte> imageDest2 = null;
if (rbCannyUseCvCanny.Checked)
{
imageDest = new Image<Gray, byte> (imageSourceGrayscale.Size);
CvInvoke.cvCanny(imageSourceGrayscale.Ptr, imageDest.Ptr, lowThresh, highThresh, apertureSize);
}
else
imageDest2 = imageSource.Canny(new Bgr (lowThresh, lowThresh, lowThresh), new Bgr(highThresh, highThresh, highThresh));
sw.Stop();
//顯示
pbResult.Image = rbCannyUseCvCanny.Checked ? imageDest.Bitmap : imageDest2.Bitmap;
//釋放資源
if (imageDest != null)
imageDest.Dispose();
if (imageDest2 != null)
imageDest2.Dispose();
//返回
return string.Format("·Canny算子,用時{0:F05}毫秒, 參數(方式:{1},閥值下限:{2},閥值上限:{3},方形濾波器寬度:{4})\r\n", sw.Elapsed.TotalMilliseconds, rbCannyUseCvCanny.Checked ? "cvCanny" : "Image<TColor, TDepth>.Canny", lowThresh, highThresh, apertureSize);
}
另外,在http://www.china-vision.net/blog/user2/15975/archives/2007/804.html有 一種自動獲取Canny算子高低閥值的方法,作者提供了用C語言實現的代碼。我將其改寫成了 C#版本,代碼如下:
計算圖像的自適應Canny算子閥值
/// <summary>
/// 計算圖像的自適應Canny算子閥值
/// </summary>
/// <param name="imageSrc">源圖像,只能是256級灰度圖 像</param>
/// <param name="apertureSize">方形濾波器的寬度 </param>
/// <param name="lowThresh">閥值下限 </param>
/// <param name="highThresh">閥值上限 </param>
unsafe void AdaptiveFindCannyThreshold(Image<Gray, Byte> imageSrc, int apertureSize, out double lowThresh, out double highThresh)
{
//計算源圖像x方向和y方向的1階Sobel算子
Size size = imageSrc.Size;
Image<Gray, Int16> imageDx = new Image<Gray, short>(size);
Image<Gray, Int16> imageDy = new Image<Gray, short>(size);
CvInvoke.cvSobel(imageSrc.Ptr, imageDx.Ptr, 1, 0, apertureSize);
CvInvoke.cvSobel(imageSrc.Ptr, imageDy.Ptr, 0, 1, apertureSize);
Image<Gray, Single> image = new Image<Gray, float>(size);
int i, j;
DenseHistogram hist = null;
int hist_size = 255;
float[] range_0 = new float[] { 0, 256 };
double PercentOfPixelsNotEdges = 0.7;
//計算邊緣的強度,並保存於圖像中
float maxv = 0;
float temp;
byte* imageDataDx = (byte*) imageDx.MIplImage.imageData.ToPointer();
byte* imageDataDy = (byte*) imageDy.MIplImage.imageData.ToPointer();
byte* imageData = (byte*) image.MIplImage.imageData.ToPointer();
int widthStepDx = imageDx.MIplImage.widthStep;
int widthStepDy = widthStepDx;
int widthStep = image.MIplImage.widthStep;
for (i = 0; i < size.Height; i++)
{
short* _dx = (short*)(imageDataDx + widthStepDx * i);
short* _dy = (short*)(imageDataDy + widthStepDy * i);
float* _image = (float*)(imageData + widthStep * i);
for (j = 0; j < size.Width; j++)
{
temp = (float)(Math.Abs(*(_dx + j)) + Math.Abs(*(_dy + j)));
*(_image + j) = temp;
if (maxv < temp)
maxv = temp;
}
}
//計算直方圖
range_0[1] = maxv;
hist_size = hist_size > maxv ? (int)maxv : hist_size;
hist = new DenseHistogram(hist_size, new RangeF (range_0[0], range_0[1]));
hist.Calculate<Single>(new Image<Gray, Single>[] { image }, false, null);
int total = (int)(size.Height * size.Width * PercentOfPixelsNotEdges);
double sum = 0;
int icount = hist.BinDimension[0].Size;
for (i = 0; i < icount; i++)
{
sum += hist[i];
if (sum > total)
break;
}
//計算閥值
highThresh = (i + 1) * maxv / hist_size;
lowThresh = highThresh * 0.4;
//釋放資源
imageDx.Dispose();
imageDy.Dispose(); image.Dispose();
hist.Dispose();
}
霍夫變換
霍夫變換是一種在圖像中尋找直線、圓及其他簡單形狀的方法,在OpenCv中實現了霍夫 線變換和霍夫圓變換。值得注意的地方有以下幾點:(1)HoughLines2需要先計算Canny邊 緣,然後再檢測直線;(2)HoughLines2計算結果的獲取隨獲取方式的不同而不同;(3) HoughCircles檢測結果似乎不正確。
使用霍夫變換的示例代碼如下所示:
霍夫變換
private string HoughLinesFeatureDetect()
{
//獲取參數
HOUGH_TYPE method = rbHoughLinesSHT.Checked ? HOUGH_TYPE.CV_HOUGH_STANDARD : (rbHoughLinesPPHT.Checked ? HOUGH_TYPE.CV_HOUGH_PROBABILISTIC : HOUGH_TYPE.CV_HOUGH_MULTI_SCALE);
double rho = double.Parse (txtHoughLinesRho.Text);
double theta = double.Parse (txtHoughLinesTheta.Text);
int threshold = int.Parse (txtHoughLinesThreshold.Text);
double param1 = double.Parse (txtHoughLinesParam1.Text);
double param2 = double.Parse (txtHoughLinesParam2.Text);
MemStorage storage = new MemStorage();
int linesCount = 0;
StringBuilder sbResult = new StringBuilder();
//計算,先運行Canny邊緣檢測(參數來自Canny算子屬性頁 ),然後再用計算霍夫線變換
double lowThresh = double.Parse (txtCannyLowThresh.Text);
double highThresh = double.Parse (txtCannyHighThresh.Text);
int apertureSize = int.Parse((string) cmbCannyApertureSize.SelectedItem);
Image<Gray, Byte> imageCanny = new Image<Gray, byte>(imageSourceGrayscale.Size);
CvInvoke.cvCanny(imageSourceGrayscale.Ptr, imageCanny.Ptr, lowThresh, highThresh, apertureSize);
Stopwatch sw = new Stopwatch();
sw.Start();
IntPtr ptrLines = CvInvoke.cvHoughLines2 (imageCanny.Ptr, storage.Ptr, method, rho, theta, threshold, param1, param2);
Seq<LineSegment2D> linesSeq = null;
Seq<PointF> linesSeq2 = null;
if (method == HOUGH_TYPE.CV_HOUGH_PROBABILISTIC)
linesSeq = new Seq<LineSegment2D> (ptrLines, storage);
else
linesSeq2 = new Seq<PointF> (ptrLines, storage);
sw.Stop();
//顯示
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
if (linesSeq != null)
{
linesCount = linesSeq.Total;
foreach (LineSegment2D line in linesSeq)
{
imageResult.Draw(line, new Bgr (255d, 0d, 0d), 4);
sbResult.AppendFormat("{0}-{1},", line.P1, line.P2);
}
}
else
{
linesCount = linesSeq2.Total;
foreach (PointF line in linesSeq2)
{
float r = line.X;
float t = line.Y;
double a = Math.Cos(t), b = Math.Sin(t);
double x0 = a * r, y0 = b * r;
int x1 = (int)(x0 + 1000 * (- b));
int y1 = (int)(y0 + 1000 * (a));
int x2 = (int)(x0 - 1000 * (- b));
int y2 = (int)(y0 - 1000 * (a));
Point pt1 = new Point(x1, y1);
Point pt2 = new Point(x2, y2);
imageResult.Draw(new LineSegment2D (pt1, pt2), new Bgr(255d, 0d, 0d), 4);
sbResult.AppendFormat("{0}-{1},", pt1, pt2);
}
}
pbResult.Image = imageResult.Bitmap;
//釋放資源
imageCanny.Dispose();
imageResult.Dispose();
storage.Dispose();
//返回
return string.Format("·霍夫線變換,用時{0:F05}毫秒 ,參數(變換方式:{1},距離精度:{2},弧度精度:{3},閥值:{4},參數1:{5},參數 2:{6}),找到{7}條直線\r\n{8}",
sw.Elapsed.TotalMilliseconds, method.ToString("G"), rho, theta, threshold, param1, param2, linesCount, linesCount != 0 ? (sbResult.ToString() + "\r\n") : "");
}
//霍夫圓變換
private string HoughCirclesFeatureDetect()
{
//獲取參數
double dp = double.Parse (txtHoughCirclesDp.Text);
double minDist = double.Parse (txtHoughCirclesMinDist.Text);
double param1 = double.Parse (txtHoughCirclesParam1.Text);
double param2 = double.Parse (txtHoughCirclesParam2.Text);
int minRadius = int.Parse (txtHoughCirclesMinRadius.Text);
int maxRadius = int.Parse (txtHoughCirclesMaxRadius.Text);
StringBuilder sbResult = new StringBuilder();
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
CircleF[][] circles = imageSourceGrayscale.HoughCircles(new Gray(param1), new Gray(param2), dp, minDist, minRadius, maxRadius);
sw.Stop();
//顯示
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
int circlesCount = 0;
foreach (CircleF[] cs in circles)
{
foreach (CircleF circle in cs)
{
imageResult.Draw(circle, new Bgr (255d, 0d, 0d), 4);
sbResult.AppendFormat("圓心{0}半徑{1} ,", circle.Center, circle.Radius);
circlesCount++;
}
}
pbResult.Image = imageResult.Bitmap;
//釋放資源
imageResult.Dispose();
//返回
return string.Format("·霍夫圓變換,用時{0:F05}毫秒 ,參數(累加器圖像的最小分辨率:{1},不同圓之間的最小距離:{2},邊緣閥值:{3}, 累加器閥值:{4},最小圓半徑:{5},最大圓半徑:{6}),找到{7}個圓\r\n{8}",
sw.Elapsed.TotalMilliseconds, dp, minDist, param1, param2, minRadius, maxRadius, circlesCount, sbResult.Length > 0 ? (sbResult.ToString() + "\r\n") : "");
}
Harris角點
cvCornerHarris函數檢測的結果實際上是一幅包含Harris角點的浮點型單通道圖像,可 以使用類似下面的代碼來計算包含Harris角點的圖像:
Harris角點
private string CornerHarrisFeatureDetect()
{
//獲取參數
int blockSize = int.Parse (txtCornerHarrisBlockSize.Text);
int apertureSize = int.Parse (txtCornerHarrisApertureSize.Text);
double k = double.Parse (txtCornerHarrisK.Text);
//計算
Image<Gray, Single> imageDest = new Image<Gray, float>(imageSourceGrayscale.Size);
Stopwatch sw = new Stopwatch();
sw.Start();
CvInvoke.cvCornerHarris(imageSourceGrayscale.Ptr, imageDest.Ptr, blockSize, apertureSize, k);
sw.Stop();
//顯示
pbResult.Image = imageDest.Bitmap;
//釋放資源
imageDest.Dispose();
//返回
return string.Format("·Harris角點,用時{0:F05}毫秒 ,參數(鄰域大小:{1},方形濾波器寬度:{2},權重系數:{3})\r\n", sw.Elapsed.TotalMilliseconds, blockSize, apertureSize, k);
}
如果要計算Harris角點列表,需要使用cvGoodFeatureToTrack函數,並傳遞適當的參數 。
ShiTomasi角點
在默認情況下,cvGoodFeatureToTrack函數計算ShiTomasi角點;不過如果將參數 use_harris設置為非0值,那麼它會計算harris角點。
使用cvGoodFeatureToTrack函數的示例代碼如下:
ShiTomasi角點
private string CornerShiTomasiFeatureDetect()
{
//獲取參數
int cornerCount = int.Parse (txtGoodFeaturesCornerCount.Text);
double qualityLevel = double.Parse (txtGoodFeaturesQualityLevel.Text);
double minDistance = double.Parse (txtGoodFeaturesMinDistance.Text);
int blockSize = int.Parse (txtGoodFeaturesBlockSize.Text);
bool useHarris = cbGoodFeaturesUseHarris.Checked;
double k = double.Parse (txtGoodFeaturesK.Text);
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
PointF[][] corners = imageSourceGrayscale.GoodFeaturesToTrack(cornerCount, qualityLevel, minDistance, blockSize, useHarris, k);
sw.Stop();
//顯示
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
int cornerCount2 = 0;
StringBuilder sbResult = new StringBuilder();
int radius = (int)(minDistance / 2) + 1;
int thickness = (int)(minDistance / 4) + 1;
foreach (PointF[] cs in corners)
{
foreach (PointF p in cs)
{
imageResult.Draw(new CircleF(p, radius), new Bgr(255d, 0d, 0d), thickness);
cornerCount2++;
sbResult.AppendFormat("{0},", p);
}
}
pbResult.Image = imageResult.Bitmap;
//釋放資源
imageResult.Dispose();
//返回
return string.Format("·ShiTomasi角點,用時{0:F05}毫 秒,參數(最大角點數目:{1},最小特征值:{2},角點間的最小距離:{3},鄰域大小: {4},角點類型:{5},權重系數:{6}),檢測到{7}個角點\r\n{8}",
sw.Elapsed.TotalMilliseconds, cornerCount, qualityLevel, minDistance, blockSize, useHarris ? "Harris" : "ShiTomasi", k, cornerCount2, cornerCount2 > 0 ? (sbResult.ToString() + "\r\n") : "");
}
亞像素級角點
在檢測亞像素級角點前,需要提供角點的初始為止,這些初始位置可以用本文給出的其 他的角點檢測方式來獲取,不過使用GoodFeaturesToTrack得到的結果最方便直接使用。
亞像素級角點檢測的示例代碼如下:
亞像素級角點
private string CornerSubPixFeatureDetect()
{
//獲取參數
int winWidth = int.Parse (txtCornerSubPixWinWidth.Text);
int winHeight = int.Parse (txtCornerSubPixWinHeight.Text);
Size win = new Size(winWidth, winHeight);
int zeroZoneWidth = int.Parse (txtCornerSubPixZeroZoneWidth.Text);
int zeroZoneHeight = int.Parse (txtCornerSubPixZeroZoneHeight.Text);
Size zeroZone = new Size(zeroZoneWidth, zeroZoneHeight);
int maxIter=int.Parse (txtCornerSubPixMaxIter.Text);
double epsilon=double.Parse (txtCornerSubPixEpsilon.Text);
MCvTermCriteria criteria = new MCvTermCriteria (maxIter, epsilon);
//先計算得到易於跟蹤的點(ShiTomasi角點)
int cornerCount = int.Parse (txtGoodFeaturesCornerCount.Text);
double qualityLevel = double.Parse (txtGoodFeaturesQualityLevel.Text);
double minDistance = double.Parse (txtGoodFeaturesMinDistance.Text);
int blockSize = int.Parse (txtGoodFeaturesBlockSize.Text);
bool useHarris = cbGoodFeaturesUseHarris.Checked;
double k = double.Parse (txtGoodFeaturesK.Text);
PointF[][] corners = imageSourceGrayscale.GoodFeaturesToTrack(cornerCount, qualityLevel, minDistance, blockSize, useHarris, k);
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
imageSourceGrayscale.FindCornerSubPix(corners, win, zeroZone, criteria);
sw.Stop();
//顯示
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
int cornerCount2 = 0;
StringBuilder sbResult = new StringBuilder();
int radius = (int)(minDistance / 2) + 1;
int thickness = (int)(minDistance / 4) + 1;
foreach (PointF[] cs in corners)
{
foreach (PointF p in cs)
{
imageResult.Draw(new CircleF(p, radius), new Bgr(255d, 0d, 0d), thickness);
cornerCount2++;
sbResult.AppendFormat("{0},", p);
}
}
pbResult.Image = imageResult.Bitmap;
//釋放資源
imageResult.Dispose();
//返回
return string.Format("·亞像素級角點,用時{0:F05}毫 秒,參數(搜索窗口:{1},死區:{2},最大迭代次數:{3},亞像素值的精度:{4}),檢 測到{5}個角點\r\n{6}",
sw.Elapsed.TotalMilliseconds, win, zeroZone, maxIter, epsilon, cornerCount2, cornerCount2 > 0 ? (sbResult.ToString() + "\r\n") : "");
}
SURF角點
OpenCv中的cvExtractSURF函數和EmguCv中的Image<TColor,TDepth>.ExtractSURF 方法用於檢測SURF角點。
SURF角點檢測的示例代碼如下:
SURF角點
private string SurfFeatureDetect()
{
//獲取參數
bool getDescriptors = cbSurfGetDescriptors.Checked;
MCvSURFParams surfParam = new MCvSURFParams ();
surfParam.extended=rbSurfBasicDescriptor.Checked ? 0 : 1;
surfParam.hessianThreshold=double.Parse (txtSurfHessianThreshold.Text);
surfParam.nOctaves=int.Parse (txtSurfNumberOfOctaves.Text);
surfParam.nOctaveLayers=int.Parse (txtSurfNumberOfOctaveLayers.Text);
//計算
SURFFeature[] features = null;
MKeyPoint[] keyPoints = null;
Stopwatch sw = new Stopwatch();
sw.Start();
if (getDescriptors)
features = imageSourceGrayscale.ExtractSURF(ref surfParam);
else
keyPoints = surfParam.DetectKeyPoints (imageSourceGrayscale, null);
sw.Stop();
//顯示
bool showDetail = cbSurfShowDetail.Checked;
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
StringBuilder sbResult = new StringBuilder();
int idx = 0;
if (getDescriptors)
{
foreach (SURFFeature feature in features)
{
imageResult.Draw(new CircleF (feature.Point.pt, 5), new Bgr(255d, 0d, 0d), 2);
if (showDetail)
{
sbResult.AppendFormat("第{0} 點(坐標:{1},尺寸:{2},方向:{3}°,hessian值:{4},拉普拉斯標志:{5},描述: [",
idx, feature.Point.pt, feature.Point.size, feature.Point.dir, feature.Point.hessian, feature.Point.laplacian);
foreach (float d in feature.Descriptor)
sbResult.AppendFormat("{0},", d);
sbResult.Append("]),");
}
idx++;
}
}
else
{
foreach (MKeyPoint keypoint in keyPoints)
{
imageResult.Draw(new CircleF (keypoint.Point, 5), new Bgr(255d, 0d, 0d), 2);
if (showDetail)
sbResult.AppendFormat("第{0} 點(坐標:{1},尺寸:{2},方向:{3}°,響應:{4},octave:{5}),",
idx, keypoint.Point, keypoint.Size, keypoint.Angle, keypoint.Response, keypoint.Octave);
idx++;
}
}
pbResult.Image = imageResult.Bitmap;
//釋放資源
imageResult.Dispose();
//返回
return string.Format("·SURF角點,用時{0:F05}毫秒, 參數(描述:{1},hessian閥值:{2},octave數目:{3},每個octave的層數:{4},檢測 到{5}個角點\r\n{6}",
sw.Elapsed.TotalMilliseconds, getDescriptors ? (surfParam.extended == 0 ? "獲取基本描述" : "獲取擴展描述") : "不獲 取描述", surfParam.hessianThreshold,
surfParam.nOctaves, surfParam.nOctaveLayers, getDescriptors ? features.Length : keyPoints.Length, showDetail ? sbResult.ToString() + "\r\n" : "");
}
Star關鍵點
OpenCv中的cvGetStarKeypoints函數和EmguCv中的 Image<TColor,TDepth>.GetStarKeypoints方法用於檢測“星型”附近的點。
Star關鍵點檢測的示例代碼如下:
Star關鍵點
private string StarKeyPointFeatureDetect()
{
//獲取參數
StarDetector starParam = new StarDetector();
starParam.MaxSize = int.Parse((string) cmbStarMaxSize.SelectedItem);
starParam.ResponseThreshold = int.Parse (txtStarResponseThreshold.Text);
starParam.LineThresholdProjected = int.Parse (txtStarLineThresholdProjected.Text);
starParam.LineThresholdBinarized = int.Parse (txtStarLineThresholdBinarized.Text);
starParam.SuppressNonmaxSize = int.Parse (txtStarSuppressNonmaxSize.Text);
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
MCvStarKeypoint[] keyPoints = imageSourceGrayscale.GetStarKeypoints(ref starParam);
sw.Stop();
//顯示
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
StringBuilder sbResult = new StringBuilder();
int idx = 0;
foreach (MCvStarKeypoint keypoint in keyPoints)
{
imageResult.Draw(new CircleF(new PointF (keypoint.pt.X, keypoint.pt.Y), keypoint.size / 2), new Bgr(255d, 0d, 0d), keypoint.size / 4);
sbResult.AppendFormat("第{0}點(坐標:{1},尺 寸:{2},強度:{3}),", idx, keypoint.pt, keypoint.size, keypoint.response);
idx++;
}
pbResult.Image = imageResult.Bitmap;
//釋放資源
imageResult.Dispose();
//返回
return string.Format("·Star關鍵點,用時{0:F05}毫秒 ,參數(MaxSize:{1},ResponseThreshold:{2},LineThresholdProjected:{3}, LineThresholdBinarized:{4},SuppressNonmaxSize:{5}),檢測到{6}個關鍵點\r\n {7}",
sw.Elapsed.TotalMilliseconds, starParam.MaxSize, starParam.ResponseThreshold, starParam.LineThresholdProjected, starParam.LineThresholdBinarized, starParam.SuppressNonmaxSize, keyPoints.Length, keyPoints.Length > 0 ? (sbResult.ToString() + "\r\n") : "");
}
源碼:http://files.cnblogs.com/xrwang/特征檢測 .rar