SIFT演算法大家都比較熟悉,網上的版本很多,剛開始接觸時我主要研究的是C++,因為相對於C#,基於OPEN CV C++的SIFT演算法資料更多,但是由於想要實現較為理想的介面效果,最終還是放棄了使用C++轉而使用C#。
C#中SIFT演算法主要分為三種:
1)脫離Emgu cv平臺,完全手動實現所有SIFT演算法函式,這樣的程式雖然實現有些困難,但是完全可藉助已有的C++程式做更改,而且這樣做最大的好處就是對SIFT演算法的原理有更深的理解。
2)實現時使用少量Emgu cv函式(例如影像的讀取,灰度值獲得等),但是大部分工作還是依賴於.net平臺自行完成。
3)基本上程式完全藉助於Emgu cv提供的介面,核心函式完全由Emgu cv提供。
前兩類程式資源較多,大家也很容易下載,第三類程式資源相對較少,因此我今天簡單為大家介紹第三類演算法的實現方法,首先回顧一下SIFT演算法計算步驟:
1. 尺度空間極值檢測:搜尋所有尺度上的影象位置。通過高斯微分函式來識別潛在的對於尺度和旋轉不變的興趣點。
2. 關鍵點定位:在每個候選的位置上,通過一個擬合精細的模型來確定位置和尺度。關鍵點的選擇依據於它們的穩定程度。
3. 方向確定:基於影象區域性的梯度方向,分配給每個關鍵點位置一個或多個方向。所有後面的對影象資料的操作都相對於關鍵點的方向、尺度和位置進行變換,從而提供對於這些變換的不變性。
4. 關鍵點描述:在每個關鍵點周圍的鄰域內,在選定的尺度上測量影象區域性的梯度。這些梯度被變換成一種表示,這種表示允許比較大的區域性形狀的變形和光照變化。
使用Emgu cv實現上述步驟非常簡單,程式如下:
//確定匹配影象
Bitmap bt1 = new Bitmap(@"C:UsersGAOXIANGDesktopI.jpg");
Bitmap bt2 = new Bitmap(@"C:UsersGAOXIANGDesktop.jpg");
//將影象轉為Emgu cv的處理格式
Image
Image
//使用Emgu cv探測圖片特徵點
SIFTDetector sift = new SIFTDetector();
var f1 = sift.DetectFeatures(img1, null);
var f2 = sift.DetectFeatures(img2, null);
到此已經獲得了兩張相片的特徵點f1,f2,接下來就是將相互匹配的特徵點相連:
for (int i = 0; i < f1.Length; i++)
{ double[] dist = new double[f2.Length];
int n = 0;
int index = 0;
for (int j = 0; j < f2.Length; j++)
{
//計算待比較特徵點描述子的歐式距離
dist[n++] = diedai(f1[i].Descriptor, f2[j].Descriptor);
}
//排序,獲得歐式距離最近點以及次近點
for (int k = 0; k < 2; k++)
{ for (int k1 = k+1; k1 < dist.Length; k1++)
{ if (dist[k] > dist[k1])
{ double temp = dist[k];
dist[k] = dist[k1];
dist[k1] = temp;
//記錄最近點在相片2中的序列
if (k == 0)
{ index = k1; } } } }
//若最近點與次近點比值小於閾值0.49,繪製特徵點連線
if (dist[0]/dist[1] < 0.49)
{ PointF point1 = f1[i].KeyPoint.Point;
PointF point2 = f2[index].KeyPoint.Point;
Graphics g = this.CreateGraphics();
Pen p1 = new Pen(Color.Blue, 3);
g.DrawLine(p1, point1, point2);
//注意:point1,point2位置與具體控制元件,影象佈局有關,請自行調整 }}
//描述子距離計算函式
private double diedai(float[] p1, float[] p2)
{ double sub=0; double sqrt=0;for (int i = 0; i < p1.Length; i++)
{ sub += Math.Pow(p1[i] - p2[i], 2); }
sqrt = Math.Sqrt(sub);
return sqrt;
}