除去NARF這種和特征檢測聯系比較緊密的方法外,一般來說特征檢測都會對曲率變化比較劇烈的點更敏感。Harris算法是圖像檢測識別算法中非常重要的一個算法,其對物體姿態變化魯棒性好,對旋轉不敏感,可以很好的檢測出物體的角點。甚至對於標定算法而言,HARRIS角點檢測是使之能成功進行的基礎。
HARRIS算法的思想還是很有意思的。很聰明也很trick.
1.Harris 算法
其思想及數學推導大致如下:
1.在圖像中取一個窗 w (矩形窗,高斯窗,XX窗,各種窗,某師姐要改標定算法不就可以從選Harris的窗開始做起么。。。。。)
2.獲得在該窗下的灰度 I
3.移動該窗,則灰度會發生變化,平坦區域灰度變化不大,邊緣區域沿邊緣方向灰度變化劇烈,角點處各個方向灰度變化均劇烈
4.依據3中條件選出角點
當然啦,如果Harris算子的實現也和它的思想這么平淡那我就不表揚他聰明了,Harris算子的具體實現方法,利用的是圖像偏微分方程的思想。
先給出抽象數學表達式(不要問我為什么這么淡,我也不知道):
其中 w 代表窗函數,某個x,y為圖像坐標,u,v是一個移動向量(既反應移動方向,也反應移動大小)。
Ix表示圖像沿x方向的差分,Iy表示圖像沿y方向的差分。圖像差分算子有什么Sober~PXX~總之很多就對了,一階差分還是很好求的。
顯然,E(u,v)可以用另外一種形式來表示了。最終可以表達為協方差矩陣的形式。
OK,在這里我們有了數學中最優雅的表達——Matrix,especially symmetric Matrix. Nothing is better than that.
2.矩陣的方向性
顯然,E(u,v)的值和u,v有關。。。很有關。。
1.可以取一組u,v,讓E(u,v)的值最小。
2.還可以取一組u,v,讓E(u,v)的值最大。
這些u,v怎么取,顯然就和矩陣M的方向有關了。OK,讓我們換一個思路來看這個矩陣。
平面內的一個矩陣乘以一個向量v,大概簡單的寫成 Mv
它會使得這個向量發生一個作用:旋轉,拉伸,平移.....總之,這種作用叫做 線性變換
矩陣的左邊好像也是一個向量,只不過是橫着寫的([u v]),換而言之,那就是 vT(v的轉置)。
vT(Mv)......這是啥?
意思好像是。。。。v先旋轉+拉伸一下,然后再在它自己身上投影,最終的 E(u,v)本質上來說,就是這個投影的長度。。。嗯,對,投影的長度
好了。我們現在明確了 E(u,v) 的數學幾何意義,再回過頭來想想,要怎樣才能讓這個投影的長度達到最大或者最小呢?
顯然,答案就是矩陣的特征值與特征向量,當[u v]T 取特征向量方向的時候,矩陣M只有拉伸作用,而沒有旋轉作用,這時的投影長度是最長的(如果反向投則是負的最長)。
到此為止,我們已經知道了 E(u,v)的最大和最小值了(笨辦法是求出特征向量方向再帶進去,聰明的方法是直接看矩陣特征值,特征值就是放大倍數)。並且,分析可以知道,特征值越大,那么說明 E(u,v)越大。
1.兩個特征值都很大==========>角點(兩個響應方向)
2.一個特征值很大,一個很小=====>邊緣(只有一個響應方向)
3.兩個特征值都小============>平原地區(響應都很微弱)
基於上述特征,有很多人設計了角點的快速判據。
有 det(M) - trace(M)^2
有 det(M)/trace(M)
.....等等很多,但是這不重要,思想都是一樣的。(某師姐這里又有一個標定算法的創新點哦。。。。我會告訴你換換判據又是個新思路?)
3. 3DHarris
在2DHarris里,我們使用了 圖像梯度構成的 協方差矩陣。 圖像梯度。。。嗯。。。。每個像素點都有一個梯度,在一階信息量的情況下描述了兩個相鄰像素的關系。顯然這個思想可以輕易的移植到點雲上來。
OOPS,糟糕,點雲木有灰度的概念啊,一般的點雲也木有強度的概念啊。。。這可如何是好??????
別緊張,pcl 說這樣能行,那就肯定能行咯,先定性的分析一下Harris3D的理念。
想象一下,如果在 點雲中存在一點p
1、在p上建立一個局部坐標系:z方向是法線方向,x,y方向和z垂直。
2、在p上建立一個小正方體,不要太大,大概像材料力學分析應力那種就行
3、假設點雲的密度是相同的,點雲是一層蒙皮,不是實心的。
a、如果小正方體沿z方向移動,那小正方體里的點雲數量應該不變
b、如果小正方體位於邊緣上,則沿邊緣移動,點雲數量幾乎不變,沿垂直邊緣方向移動,點雲數量改變
c、如果小正方體位於角點上,則有兩個方向都會大幅改變點雲數量
OK,我們已經有了Harris3D的基本准則,接下來要思考的是怎樣優雅的解決這個問題
兩個和z相互垂直的方向。。。。嗯。。。。perpendicular。。。。
如果由法向量x,y,z構成協方差矩陣,那么它應該是一個對稱矩陣。而且特征向量有一個方向是法線方向,另外兩個方向和法線垂直。
那么直接用協方差矩陣替換掉圖像里的M矩陣,就得到了點雲的Harris算法。
其中,半徑r可以用來控制角點的規模
r小,則對應的角點越尖銳(對噪聲更敏感)
r大,則可能在平緩的區域也檢測出角點
r怎么取? 我也不知道。。。。。試吧。。。。。
4.PCL對Harris算法的實現
根據以上分析,在PCL的API文檔的幫助下,我嘗試了一下 Harris3D 算法。感謝山大的畢同學提供的點雲,該點雲是場景點雲而不是一般的物體點雲。總體感覺是慢,因為針對每個點雲,需要計算它的法線,算完之后又要針對每個點進行協方差矩陣的計算,總而言之,整個過程還是非常耗時的。並且說實話。。。算法的效果一般般。
#include <iostream> #include <pcl\io\pcd_io.h> #include <pcl/point_cloud.h> #include <pcl/visualization/pcl_visualizer.h> #include <pcl/io/io.h> #include <pcl/keypoints/harris_keypoint3D.h> #include <cstdlib> #include <vector> using namespace std; int main() { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile ("F:\\PCL\\PCD\\both.pcd", *cloud); boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer); viewer->addPointCloud(cloud,"all_cloud"); //注意Harris的輸出點雲必須是有強度(I)信息的,因為評估值保存在I分量里 pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_out (new pcl::PointCloud<pcl::PointXYZI>); pcl::HarrisKeypoint3D<pcl::PointXYZ,pcl::PointXYZI,pcl::Normal> harris; harris.setInputCloud(cloud); cout<<"input successful"<<endl; harris.setNonMaxSupression(true); harris.setRadius(0.04f); harris.setThreshold(0.02f); cout<<"parameter set successful"<<endl; //新建的點雲必須初始化,清零,否則指針會越界 cloud_out->height=1; cloud_out->width =100; cloud_out->resize(cloud_out->height*cloud->width); cloud_out->clear(); harris.compute(*cloud_out); int size = cloud_out->size(); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_harris (new pcl::PointCloud<pcl::PointXYZ>); cloud_harris->height=1; cloud_harris->width =100; cloud_harris->resize(cloud_out->height*cloud->width); cloud_harris->clear(); pcl::PointXYZ point; //可視化結果不支持XYZI格式點雲,所有又要導回XYZ格式。。。。 for (int i = 0;i<size;i++) { point.x = cloud_out->at(i).x; point.y = cloud_out->at(i).y; point.z = cloud_out->at(i).z; cloud_harris->push_back(point); } pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> harris_color_handler (cloud_harris, 0, 255, 0); viewer->addPointCloud(cloud_harris,harris_color_handler,"harris"); viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "harris"); while (!viewer->wasStopped()) { viewer->spinOnce(100); } system("pause"); }
由於我選擇的搜索半徑比較大,所以找到的角點都不是太"角”,關於參數設置大家可以多多探索,但我認為,特征點檢測算法實在太慢,對實時機器人系統來說是遠遠達不到要求的。這種先算法線,再算協方差的形式真心上不起。。。。實際上這種基於領域法線的特征點檢測算法有點類似基於 CRF的語義識別算法,都只使用了相鄰信息而忽略了全局信息。也可能相鄰信息包含的相關性比較大,是通往高層次感知的唯一路徑吧,誰又知道呢?
任重而道遠。。。。。