程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 圖像特征檢測(Image Feature Detection)(二)

圖像特征檢測(Image Feature Detection)(二)

編輯:關於.NET

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);
     }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved