考文獻:
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.
代碼網站:http://www.cvlibs.net/software/libcbdetect/
棋盤格角點定位: 棋盤格角點有很明顯的特征, 黑白交叉,常用的方案是使用角點檢測算法, opencv有提供對應的算法, 但是這種算法對於模糊、噪點多的
圖像穩定太差, 雖然能夠對角點進行檢測,但是同時會引入其他不需要的點,需要配合篩查算法來進行過濾;
文章開頭給出的論文提供了一種角點檢測算法, 它是基於生長的檢測方案, 通過研究作者代碼發現該算法的穩定性非常好, 能夠定位亞像素級的角點;
下面按照代碼流程看看它是怎么實現的:
1. 角點粗檢測(濾波 + 非極大抑制);
針對棋盤格角點的特征, 黑白交叉, 構建高斯濾波核(選取3個sigma, 兩種布局);
上面是同一個sigma對應的四個濾波核, 分別為a1, a2, b1, b2;
通過灰度圖像與濾波核的卷積運算,得到四個處理后的圖像imga1,imga2,imgb1, imgb2,顯出角點位置, 抹平黑白平坦區;
平均圖像: imgmu = (imga1 + imga2 + imgb1 + imgb2)/4;
在平坦區域, 經過四個卷積運算后, 值基本沒什么變化, 但是在角點、邊緣地方由於不同的構造濾波核,將使得邊緣不同
方向的值不同,譬如a1核,將導致角點左上角邊緣區明顯大於其他核后的值,將該值減去平均圖像, 就可以得到差值圖;
通過不同sigma尺寸濾波核處理后的差值圖即可以得到粗略的角點位置, 當然當前的角點還是一個斑點;
% template properties 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)]; disp('Filtering ...'); % filter image img_corners = zeros(size(img,1),size(img,2)); for template_class=1:size(template_props,1) % create correlation template template = createCorrelationPatch(template_props(template_class,1),template_props(template_class,2),template_props(template_class,3)); % filter image according with current template 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'); % compute mean img_corners_mu = (img_corners_a1+img_corners_a2+img_corners_b1+img_corners_b2)/4; % case 1: a=white, b=black img_corners_a = min(img_corners_a1-img_corners_mu,img_corners_a2-img_corners_mu); img_corners_b = min(img_corners_mu-img_corners_b1,img_corners_mu-img_corners_b2); img_corners_1 = min(img_corners_a,img_corners_b); % case 2: b=white, a=black img_corners_a = min(img_corners_mu-img_corners_a1,img_corners_mu-img_corners_a2); img_corners_b = min(img_corners_b1-img_corners_mu,img_corners_b2-img_corners_mu); img_corners_2 = min(img_corners_a,img_corners_b); % update corner map img_corners = max(img_corners,img_corners_1); img_corners = max(img_corners,img_corners_2); end
通過非極大抑制(NMS),我們可以在斑點中找到准確的位置(像素級);NMS一般用於搜索局部極大值;
2、亞像素級角點檢測;
經過1處理后可以找到像素級的角點位置, 但是棋盤格一般用來標定, 最好是采用亞像素精度更好;
首先, 計算角點周圍的邊,得到邊的方向v1, v2, 然后對3 * 3范圍內的邊緣點進行擬合計算求實際位置;
% non maximum suppression for i=n+1+margin:n+1:width-n-margin for j=n+1+margin:n+1:height-n-margin maxi = i; maxj = j; maxval = img(j,i); for i2=i:i+n //先找到一個象限一定區域內的極大值; for j2=j:j+n currval = img(j2,i2); if currval>maxval maxi = i2; maxj = j2; maxval = currval; end end end failed = 0; for i2=maxi-n:min(maxi+n,width-margin) // 然后,在這個極大值的鄰域內找到比該極大值更大的; for j2=maxj-n:min(maxj+n,height-margin) currval = img(j2,i2); if currval>maxval && (i2<i || i2>i+n || j2<j || j2>j+n) failed = 1; break; end end if failed break; end end if maxval>=tau && ~failed maxima = [maxima; maxi maxj]; end end end
3、基於生長的檢測;
經過1,2處理后, 角點的位置已經明確了, 下面就需要對所有的角點進行排布, 這就有問題了: 可能存在角點檢測遺漏的情況。
首先, 通過一個焦點找到一個3 * 3的鄰居角點陣列, 然后在通過外推(距離、方向),得到四個方向的理論位置,將該理論位置在剩下的
角點中找最近的點,同時計算當前的能量(判定函數)。理論上, 隨着點的增多, 能 % for all seed corners do
for i=1:size(corners.p,1) % output if mod(i-1,100)==0 fprintf('%d/%d\n',i,size(corners.p,1)); end % init 3x3 chessboard from seed i chessboard = initChessboard(corners,i); // 找到3 * 3 的鄰居; % check if this is a useful initial guess if isempty(chessboard) || chessboardEnergy(chessboard,corners)>0 continue; end % try growing chessboard while 1 % compute current energy energy = chessboardEnergy(chessboard,corners); // 計算目前的能量; % compute proposals and energies for j=1:4 // 四個方向擴展; proposal{j} = growChessboard(chessboard,corners,j); //擴展, 生長; p_energy(j) = chessboardEnergy(proposal{j},corners); // 擴展后的能量; end % find best proposal [min_val,min_idx] = min(p_energy); % accept best proposal, if energy is reduced if p_energy(min_idx)<energy chessboard = proposal{min_idx}; if 0 figure, hold on, axis equal; chessboards{1} = chessboard; plotChessboards(chessboards,corners); keyboard; end % otherwise exit loop else break; end end % if chessboard has low energy (corresponding to high quality) if chessboardEnergy(chessboard,corners)<-10 //當能量小於-10; 因為最大-9 ( 3 * 3) % check if new chessboard proposal overlaps with existing chessboards overlap = zeros(length(chessboards),2); for j=1:length(chessboards) // 判斷當前找的陣列是否與先前找的陣列有重疊區; for k=1:length(chessboards{j}(:)) if any(chessboards{j}(k)==chessboard(:)) overlap(j,1) = 1; overlap(j,2) = chessboardEnergy(chessboards{j},corners); break; end end end % add chessboard (and replace overlapping if neccessary) if ~any(overlap(:,1)) chessboards{end+1} = chessboard; else idx = find(overlap(:,1)==1); if ~any(overlap(idx,2)<=chessboardEnergy(chessboard,corners)) // 如果重疊區的能量比現有的陣列小, 那么久置換先前的; chessboards(idx) = []; chessboards{end+1} = chessboard; end end end end