1. 程式人生 > >基於生長的棋盤格角點檢測方法--(2)程式碼詳解(上)

基於生長的棋盤格角點檢測方法--(2)程式碼詳解(上)

上一篇介紹了基於生長的棋盤格角點檢測方法的大概原理,詳見:基於生長的棋盤格角點檢測方法–(1)原理介紹
本文進一步從程式碼解讀角度出發,更深入地理解工程中是如何實現的。
本文中用到的程式碼可以從以下連結下載
http://www.cvlibs.net/software/libcbdetect/
這裡我把程式碼中主要的函式提取出來作為演算法骨架,這樣比較好和論文對應,可以幫助讀者在茫茫程式碼中抓住重點。
程式碼框架結構如下,包括了主要的函式。其中縮排表示包含從屬關係。

程式碼框架

I = imread('image.jpg');
corners = findCorners(I,0.01,1
); function template = createCorrelationPatch(angle_1,angle_2,radius) corners.p = nonMaximumSuppression(img_corners,3,0.025,5); corners = refineCorners(img_du,img_dv,img_angle,img_weight,corners,10); [v1,v2] = edgeOrientations(img_angle_sub,img_weight_sub); corners = scoreCorners(img,img_angle,img_weight,corners,radius); chessboards = chessboardsFromCorners(corners); chessboard = initChessboard(corners,i
); energy = chessboardEnergy(chessboard,corners) proposal{j} = growChessboard(chessboard,corners,j); pred = predictCorners(p1,p2,p3) idx = assignClosestCorners(cand,pred); plotChessboards(chessboards,corners);

本篇先介紹第一個重要函式:

findCorners

該函式的目的是從一幅包含棋盤(可以是多個)自然影象中找到棋盤中每個角點的位置。首先利用自定義的模板來突出角點,效果類似於顯著性檢測。然後用非極大值抑制演算法來獲得極大值候選點。然後對這些候選點進行亞畫素級精細化(refinement),最後根據一定的規則對每個角點進行評分,最後得到較為純淨的角點。

createCorrelationPatch

首先就是建立模板(即論文中所說的prototypes),實際使用中考慮了影象中棋盤尺度的不同尺寸,所以分別建立了3個不同尺度的模板原型,實現程式碼如下

template_props = [0 pi/2 radius(1); pi/4 -pi/4 radius(1); 0 pi/2 radius(2); pi/4 -pi/4 radius(2); 0 pi/2 radius(3); pi/4 -pi/4 radius(3)];
function template = createCorrelationPatch(angle_1,angle_2,radius)

得到的3個不同尺度下的模板原型如下:

尺度1
這裡寫圖片描述
這裡寫圖片描述
尺度2
這裡寫圖片描述
這裡寫圖片描述
尺度3
這裡寫圖片描述
這裡寫圖片描述
以上三種尺度基本可以處理實際影象中所有尺度的棋盤。然後,如下程式碼

  img_corners_a1 = conv2(img,template.a1,'same');
  img_corners_a2 = conv2(img,template.a2,'same');
  img_corners_b1 = conv2(img,template.b1,'same');
  img_corners_b2 = conv2(img,template.b2,'same');

分別對應這裡寫圖片描述 ,根據論文中公式(1)可以計算出這裡寫圖片描述 (即case 1: a=white, b=black)
公式(1)如下,具體解釋見上一篇部落格。
這裡寫圖片描述
之所以分case1,case2(對應這裡寫圖片描述 )是由於棋盤格根據黑白分佈順序不同有兩種,分別是
b w w b
w b b w
其中,b=black, w=white
按照上面的公式(1)即可計算每個畫素的Corner likelihood。

nonMaximumSuppression

非極大值抑制(NMS)採用的視窗範圍是(n+1)*(n+1), n=3。經過非極大值抑制後得到的候選點位置:
這裡寫圖片描述

Img_weight是x,y方向梯度的2範數,後面用來作為方向直方圖的加權。其結果如下圖
這裡寫圖片描述

refineCorner

對於每個Corner,以該畫素點座標為中心,取21*21視窗,程式碼如下

img_angle_sub  = img_angle(max(cv-r,1):min(cv+r,height),max(cu-r,1):min(cu+r,width));
img_weight_sub = img_weight(max(cv-r,1):min(cv+r,height),max(cu-r,1):min(cu+r,width));

img_angle,img_weight分別是畫素點的梯度方向角和幅值。

edgeOrientations

函式 edgeOrientations 首先將視窗內所有的梯度方向對映到一個32bin的直方圖裡,用梯度幅值作為加權值,程式碼如下:

    bin = max(min(floor(vec_angle(i)/(pi/bin_num)),bin_num-1),0)+1;
    angle_hist(bin) = angle_hist(bin)+vec_weight(i);

然後用meanshift方法來尋找該直方圖的區域性極大值。首先要先對直方圖做一個高斯平滑。

findModesMeanShift函式目的就是尋找直方圖中兩個最大的峰值位置,對應最大的兩個梯度。正常角點的直方圖應該是下圖左,有兩個幅值相當的峰值。若兩個峰值差別太大(下圖右),則認為該點不是角點。
這裡寫圖片描述這裡寫圖片描述

我們把這兩個最大峰值對應的位置稱為該直方圖的modes。然後計算這兩個modes直接對應的梯度角的差值,這個差值要大於一定的閾值才認為該角點有效。
然後就是corner location refinement

        w  = [u v]-[cu cv];
        d1 = norm(w-w*v1'*v1);
        d2 = norm(w-w*v2'*v

其中w*v1’*v1表示向量 w在單位向量v1方向的投影向量。

refineCorners函式結束後,濾掉不合格的Corner。如下圖,綠色表示refine後剩餘的Corner,紅色表示濾掉的偽Corner
這裡寫圖片描述

ScoreCorners

取出角點的radius* radius鄰域(仍然分3個不同尺度),根據當前角點的兩個主方向向量建立模板。
score_intensity的計算參考論文(見最後的參考文獻)中公式(1),和前面計算Corner likelihood的方法一樣。
最後的score是梯度得分和likelihood得分的乘積。

template=reateCorrelationPatch(atan2(v1(2),v1(1)),atan2(v2(2),v2(1)),c(1)-1);
score_gradient = max(sum(vec_weight.*vec_filter)/(length(vec_weight)-1),0);
score = score_gradient*score_intensity;

去掉低score的Corner後,最後剩餘的Corner就比較乾淨了,如下圖。

這裡寫圖片描述
這些角點可能還會有干擾點,這在下一步生成棋盤中會慢慢剔除。下一篇介紹另外一個重要的函式:chessboardsFromCorners。

參考資料

1、Geiger A, Moosmann F, Car Ö, et al. Automatic camera and range sensor calibration using a single shot[C]//Robotics and Automation (ICRA), 2012 IEEE International Conference on. IEEE, 2012: 3936-3943.