FAST角點檢測
FAST角點由E. Rosten教授提出,相比其他檢測手段,這種方法的速度正如其名,相當的 快。值得關注的是他所研究的理論都是屬於實用類的,都很快。Rosten教授實現了FAST角點 檢測,並將其提供給了OpenCv,相當的有愛呀;不過OpenCv中的函數和Rosten教授的實現似 乎有點點不太一樣。遺憾的是,OpenCv中目前還沒有FAST角點檢測的文檔。下面是我從 Rosten的代碼中找到的函數聲明,可以看到粗略的參數說明。
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
void cvCornerFast( const CvArr* image, int threshold, int N,
int nonmax_suppression, int* ret_number_of_corners,
CvPoint** ret_corners);
image: OpenCV image in which to detect corners. Must be 8 bit unsigned.
threshold: Threshold for detection (higher is fewer corners). 0-- 255
N: Arc length of detector, 9, 10, 11 or 12. 9 is usually best.
nonmax_suppression: Whether to perform nonmaximal suppression.
ret_number_of_corners: The number of detected corners is returned here.
ret_corners: The corners are returned here.
EmguCv中的 Image<TColor,TDepth>.GetFASTKeypoints方法也實現了FAST角點檢 測,不過參數少了一些,只有 threshold和nonmaxSupression,其中N我估計取的默認值9, 但是返回的角點數目我不知道是怎麼設置的。
使用FAST角點檢測的示例代碼如下:
FAST關鍵點
private string FASTKeyPointFeatureDetect()
{
//獲取參數
int threshold = int.Parse (txtFASTThreshold.Text);
bool nonmaxSuppression = cbFASTNonmaxSuppression.Checked;
bool showDetail = cbFASTShowDetail.Checked;
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
MKeyPoint[] keyPoints = imageSourceGrayscale.GetFASTKeypoints(threshold, nonmaxSuppression);
sw.Stop();
//顯示
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
StringBuilder sbResult = new StringBuilder();
int idx = 0;
foreach (MKeyPoint keypoint in keyPoints)
{
imageResult.Draw(new CircleF(keypoint.Point, (int)(keypoint.Size / 2)), new Bgr(255d, 0d, 0d), (int)(keypoint.Size / 4));
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("·FAST關鍵點,用時{0:F05}毫秒 ,參數(閥值:{1},nonmaxSupression:{2}),檢測到{3}個關鍵點\r\n{4}",
sw.Elapsed.TotalMilliseconds, threshold, nonmaxSuppression, keyPoints.Length, showDetail ? (sbResult.ToString() + "\r\n") : "");
}
Lepetit關鍵點
Lepetit關鍵點由Vincent Lepetit提出,可以在他的網站 (http://cvlab.epfl.ch/~vlepetit/)上看到相關的論文等資料。EmguCv中的類LDetector 實現了Lepetit關鍵點的檢測。
使用Lepetit關鍵點檢測的示例代碼如下:
Lepetit關鍵點
private string LepetitKeyPointFeatureDetect()
{
//獲取參數
LDetector lepetitDetector = new LDetector();
lepetitDetector.BaseFeatureSize = double.Parse (txtLepetitBaseFeatureSize.Text);
lepetitDetector.ClusteringDistance = double.Parse (txtLepetitClasteringDistance.Text);
lepetitDetector.NOctaves = int.Parse (txtLepetitNumberOfOctaves.Text);
lepetitDetector.NViews = int.Parse (txtLepetitNumberOfViews.Text);
lepetitDetector.Radius = int.Parse (txtLepetitRadius.Text);
lepetitDetector.Threshold = int.Parse (txtLepetitThreshold.Text);
lepetitDetector.Verbose = cbLepetitVerbose.Checked;
int maxCount = int.Parse (txtLepetitMaxCount.Text);
bool scaleCoords = cbLepetitScaleCoords.Checked;
bool showDetail = cbLepetitShowDetail.Checked;
//計算
Stopwatch sw = new Stopwatch();
sw.Start();
MKeyPoint[] keyPoints = lepetitDetector.DetectKeyPoints(imageSourceGrayscale, maxCount, scaleCoords);
sw.Stop();
//顯示
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
StringBuilder sbResult = new StringBuilder();
int idx = 0;
foreach (MKeyPoint keypoint in keyPoints)
{
//imageResult.Draw(new CircleF (keypoint.Point, (int)(keypoint.Size / 2)), new Bgr(255d, 0d, 0d), (int)(keypoint.Size / 4));
imageResult.Draw(new CircleF(keypoint.Point, 4), 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("·Lepetit關鍵點,用時{0:F05}毫 秒,參數(基礎特征尺寸:{1},集群距離:{2},階數:{3},視圖數:{4},半徑:{5}, 閥值:{6},計算詳細結果:{7},最大關鍵點數目:{8},縮放坐標:{9}),檢測到{10}個 關鍵點\r\n{11}",
sw.Elapsed.TotalMilliseconds, lepetitDetector.BaseFeatureSize, lepetitDetector.ClusteringDistance, lepetitDetector.NOctaves, lepetitDetector.NViews,
lepetitDetector.Radius, lepetitDetector.Threshold, lepetitDetector.Verbose, maxCount, scaleCoords, keyPoints.Length, showDetail ? (sbResult.ToString() + "\r\n") : "");
}
SIFT角點
SIFT角點是一種廣泛使用的圖像特征,可用於物體跟蹤、圖像匹配、圖像拼接等領域, 然而奇怪的是它並未被OpenCv實現。提出SIFT角點的 David Lowe教授已經用C和matlab實現 了SIFT角點的檢測,並開放了源代碼,不過他的實現不方便直接使用。您可以在 http://www.cs.ubc.ca/~lowe/keypoints/看到SIFT的介紹、相關論文及David Lowe教授的 實現代碼。下面我要介紹由Andrea Vedaldi和Brian Fulkerson先生創建的vlfeat開源圖像 處理庫,vlfeat庫有C和matlab兩種實現,其中包含了SIFT檢測。您可以在 http://www.vlfeat.org/下載到vlfeat庫的代碼、文檔及可執行文件。
使用vlfeat檢測SIFT角點需要以下步驟:
(1)用函數vl_sift_new()初始化SIFT過濾器對象,該過濾器對象可以反復用於多幅尺 寸相同的圖像;
(2)用函數vl_sift_first_octave()及vl_sift_process_next()遍歷縮放空間的每一階 ,直到返回VL_ERR_EOF為止;
(3)對於縮放空間的每一階,用函數vl_sift_detect()來獲取關鍵點;
(4)對每個關鍵點,用函數vl_sift_calc_keypoint_orientations()來獲取該點的方向 ;
(5)對關鍵點的每個方向,用函數vl_sift_calc_keypoint_descriptor()來獲取該方向 的描述;
(6)使用完之後,用函數vl_sift_delete()來釋放資源;
(7)如果要計算某個自定義關鍵點的描述,可以使用函數 vl_sift_calc_raw_descriptor()。
直接使用vlfeat中的SIFT角點檢測示例代碼如下:
通過P/Invoke調用vlfeat函數來進行SIFT檢測
unsafe private string SiftFeatureDetectByPinvoke(int noctaves, int nlevels, int o_min, bool showDetail)
{
StringBuilder sbResult = new StringBuilder();
//初始化
IntPtr ptrSiftFilt = VlFeatInvoke.vl_sift_new (imageSource.Width, imageSource.Height, noctaves, nlevels, o_min);
if (ptrSiftFilt == IntPtr.Zero)
return "Sift特征檢測:初始化失敗。";
//處理
Image<Gray, Single> imageSourceSingle = imageSourceGrayscale.ConvertScale<Single>(1d, 0d);
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
int pointCount = 0;
int idx = 0;
//依次遍歷每一組
if (VlFeatInvoke.vl_sift_process_first_octave (ptrSiftFilt, imageSourceSingle.MIplImage.imageData) != VlFeatInvoke.VL_ERR_EOF)
{
while (true)
{
//計算每組中的關鍵點
VlFeatInvoke.vl_sift_detect (ptrSiftFilt);
//遍歷並繪制每個點
VlSiftFilt siftFilt = (VlSiftFilt)Marshal.PtrToStructure(ptrSiftFilt, typeof(VlSiftFilt));
pointCount += siftFilt.nkeys;
VlSiftKeypoint* pKeyPoints = (VlSiftKeypoint*)siftFilt.keys.ToPointer();
for (int i = 0; i < siftFilt.nkeys; i++)
{
VlSiftKeypoint keyPoint = *pKeyPoints;
pKeyPoints++;
imageResult.Draw(new CircleF(new PointF(keyPoint.x, keyPoint.y), keyPoint.sigma / 2), new Bgr (255d, 0d, 0d), 2);
if (showDetail)
sbResult.AppendFormat("第{0}點,坐標:({1},{2}),階:{3},縮放:{4},s:{5},", idx, keyPoint.x, keyPoint.y, keyPoint.o, keyPoint.sigma, keyPoint.s);
idx++;
//計算並遍歷每個點的方向
double[] angles = new double[4];
int angleCount = VlFeatInvoke.vl_sift_calc_keypoint_orientations(ptrSiftFilt, angles, ref keyPoint);
if (showDetail)
sbResult.AppendFormat("共{0}個方向,", angleCount);
for (int j = 0; j < angleCount; j++)
{
double angle = angles[j];
if (showDetail)
sbResult.AppendFormat("【方向:{0},描述:", angle);
//計算每個方向的描述
IntPtr ptrDescriptors = Marshal.AllocHGlobal(128 * sizeof(float));
VlFeatInvoke.vl_sift_calc_keypoint_descriptor(ptrSiftFilt, ptrDescriptors, ref keyPoint, angle);
float* pDescriptors = (float*)ptrDescriptors.ToPointer();
for (int k = 0; k < 128; k++)
{
float descriptor = *pDescriptors;
pDescriptors++;
if (showDetail)
sbResult.AppendFormat("{0},", descriptor);
}
sbResult.Append("】, ");
Marshal.FreeHGlobal (ptrDescriptors);
}
}
//下一階
if (VlFeatInvoke.vl_sift_process_next_octave(ptrSiftFilt) == VlFeatInvoke.VL_ERR_EOF)
break;
}
}
//顯示
pbResult.Image = imageResult.Bitmap;
//釋放資源
VlFeatInvoke.vl_sift_delete(ptrSiftFilt);
imageSourceSingle.Dispose();
imageResult.Dispose();
//返回
return string.Format("·SIFT特征檢測(P/Invoke),用時 :未統計,參數(階數:{0},每階層數:{1},最小階索引:{2}),{3}個關鍵點\r\n {4}",
noctaves, nlevels, o_min, pointCount, showDetail ? (sbResult.ToString() + "\r\n") : "");
}
要在.net中使用vlfeat還是不夠方便,為此我對vlfeat中的SIFT角點檢測部分進行了封 裝,將相關操作放到了類SiftDetector中。
使用SiftDetector需要兩至三步:
(1)用構造函數初始化SiftDetector對象;
(2)用Process方法計算特征;
(3)視需要調用Dispose方法釋放資源,或者等待垃圾回收器來自動釋放資源。
使用SiftDetector的示例代碼如下:
通過dotnet封裝的SiftDetector類來進行SIFT檢測
private string SiftFeatureDetectByDotNet(int noctaves, int nlevels, int o_min, bool showDetail)
{
//初始化對象
SiftDetector siftDetector = new SiftDetector (imageSource.Size, noctaves, nlevels, o_min);
//計算
Image<Gray, Single> imageSourceSingle = imageSourceGrayscale.Convert<Gray, Single>();
Stopwatch sw = new Stopwatch();
sw.Start();
List<SiftFeature> features = siftDetector.Process(imageSourceSingle, showDetail ? SiftDetectorResultType.Extended : SiftDetectorResultType.Basic);
sw.Stop();
//顯示結果
Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>();
StringBuilder sbResult = new StringBuilder();
int idx=0;
foreach (SiftFeature feature in features)
{
imageResult.Draw(new CircleF(new PointF (feature.keypoint.x, feature.keypoint.y), feature.keypoint.sigma / 2), new Bgr(255d, 0d, 0d), 2);
if (showDetail)
{
sbResult.AppendFormat("第{0}點,坐標 :({1},{2}),階:{3},縮放:{4},s:{5},",
idx, feature.keypoint.x, feature.keypoint.y, feature.keypoint.o, feature.keypoint.sigma, feature.keypoint.s);
sbResult.AppendFormat("共{0}個方 向,", feature.keypointOrientations != null ? feature.keypointOrientations.Length : 0);
if (feature.keypointOrientations != null)
{
foreach (SiftKeyPointOrientation orientation in feature.keypointOrientations)
{
if (orientation.descriptors != null)
{
sbResult.AppendFormat("【方向:{0},描述:", orientation.angle);
foreach (float descriptor in orientation.descriptors)
sbResult.AppendFormat("{0},", descriptor);
}
else
sbResult.AppendFormat("【方向:{0},", orientation.angle);
sbResult.Append("】, ");
}
}
}
}
pbResult.Image = imageResult.Bitmap;
//釋放資源
siftDetector.Dispose();
imageSourceSingle.Dispose();
imageResult.Dispose();
//返回
return string.Format("·SIFT特征檢測(.net),用時: {0:F05}毫秒,參數(階數:{1},每階層數:{2},最小階索引:{3}),{4}個關鍵點\r\n {5}",
sw.Elapsed.TotalMilliseconds, noctaves, nlevels, o_min, features.Count, showDetail ? (sbResult.ToString() + "\r\n") : "");
}
對vlfeat庫中的SIFT部分封裝代碼如下所示:
定義SiftDetector類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ImageProcessLearn
{
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct VlSiftKeypoint
{
/// int
public int o;
/// int
public int ix;
/// int
public int iy;
/// int
public int @is;
/// float
public float x;
/// float
public float y;
/// float
public float s;
/// float
public float sigma;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct VlSiftFilt
{
/// double
public double sigman;
/// double
public double sigma0;
/// double
public double sigmak;
/// double
public double dsigma0;
/// int
public int width;
/// int
public int height;
/// int
public int O;
/// int
public int S;
/// int
public int o_min;
/// int
public int s_min;
/// int
public int s_max;
/// int
public int o_cur;
/// vl_sift_pix*
public System.IntPtr temp;
/// vl_sift_pix*
public System.IntPtr octave;
/// vl_sift_pix*
public System.IntPtr dog;
/// int
public int octave_width;
/// int
public int octave_height;
/// VlSiftKeypoint*
public System.IntPtr keys;
/// int
public int nkeys;
/// int
public int keys_res;
/// double
public double peak_thresh;
/// double
public double edge_thresh;
/// double
public double norm_thresh;
/// double
public double magnif;
/// double
public double windowSize;
/// vl_sift_pix*
public System.IntPtr grad;
/// int
public int grad_o;
/// <summary>
/// 獲取SiftFilt指針;
/// 注意在使用完指針之後,需要用Marshal.FreeHGlobal釋放內存 。
/// </summary>
/// <returns></returns>
unsafe public IntPtr GetPtrOfVlSiftFilt()
{
IntPtr ptrSiftFilt = Marshal.AllocHGlobal(sizeof (VlSiftFilt));
Marshal.StructureToPtr(this, ptrSiftFilt, true);
return ptrSiftFilt;
}
}
public class VlFeatInvoke
{
/// VL_ERR_MSG_LEN -> 1024
public const int VL_ERR_MSG_LEN = 1024;
/// VL_ERR_OK -> 0
public const int VL_ERR_OK = 0;
/// VL_ERR_OVERFLOW -> 1
public const int VL_ERR_OVERFLOW = 1;
/// VL_ERR_ALLOC -> 2
public const int VL_ERR_ALLOC = 2;
/// VL_ERR_BAD_ARG -> 3
public const int VL_ERR_BAD_ARG = 3;
/// VL_ERR_IO -> 4
public const int VL_ERR_IO = 4;
/// VL_ERR_EOF -> 5
public const int VL_ERR_EOF = 5;
/// VL_ERR_NO_MORE -> 5
public const int VL_ERR_NO_MORE = 5;
/// Return Type: VlSiftFilt*
///width: int
///height: int
///noctaves: int
///nlevels: int
///o_min: int
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_new")]
public static extern System.IntPtr vl_sift_new(int width, int height, int noctaves, int nlevels, int o_min);
/// Return Type: void
///f: VlSiftFilt*
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_delete")]
public static extern void vl_sift_delete(IntPtr f);
/// Return Type: int
///f: VlSiftFilt*
///im: vl_sift_pix*
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_process_first_octave")]
public static extern int vl_sift_process_first_octave (IntPtr f, IntPtr im);
/// Return Type: int
///f: VlSiftFilt*
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_process_next_octave")]
public static extern int vl_sift_process_next_octave (IntPtr f);
/// Return Type: void
///f: VlSiftFilt*
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_detect")]
public static extern void vl_sift_detect(IntPtr f);
/// Return Type: int
///f: VlSiftFilt*
///angles: double*
///k: VlSiftKeypoint*
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_calc_keypoint_orientations")]
public static extern int vl_sift_calc_keypoint_orientations(IntPtr f, double[] angles, ref VlSiftKeypoint k);
/// Return Type: void
///f: VlSiftFilt*
///descr: vl_sift_pix*
///k: VlSiftKeypoint*
///angle: double
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_calc_keypoint_descriptor")]
public static extern void vl_sift_calc_keypoint_descriptor(IntPtr f, IntPtr descr, ref VlSiftKeypoint k, double angle);
/// Return Type: void
///f: VlSiftFilt*
///image: vl_sift_pix*
///descr: vl_sift_pix*
///widht: int
///height: int
///x: double
///y: double
///s: double
///angle0: double
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_calc_raw_descriptor")]
public static extern void vl_sift_calc_raw_descriptor (IntPtr f, IntPtr image, IntPtr descr, int widht, int height, double x, double y, double s, double angle0);
/// Return Type: void
///f: VlSiftFilt*
///k: VlSiftKeypoint*
///x: double
///y: double
///sigma: double
[DllImportAttribute("vl.dll", EntryPoint = "vl_sift_keypoint_init")]
public static extern void vl_sift_keypoint_init(IntPtr f, ref VlSiftKeypoint k, double x, double y, double sigma);
}
}