欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于生长的棋盘格角点检测方法 代码介绍

程序员文章站 2022-03-27 10:27:09
...

本文主要讲述代码同时丰富参考博客中的一些小点和问题

参考文章:

https://blog.csdn.net/sunshine_zoe/article/details/78303658

参考资料

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.

2、代码网站(我改了其中一些的顺序,增加了一些用于适应自己的情况)
http://www.cvlibs.net/software/libcbdetect/

目录

直接上干货

代码块 MATLAB

增加地址(论文的代码里面没有的,适用于小白,地址需要改成自己的,增加的原因是有时候图片不能识别)

C:\Users\Administrator\Desktop\libcbdetect (1)\libcbdetect\matching

addpath('C:\Users\Administrator\Desktop\libcbdetect (1)\libcbdetect\matching');
addpath('C:\Users\Administrator\Desktop\libcbdetect (1)\libcbdetect\data');
I = imread('09.jpg');

demo.m分块:主要分为三大块。1.角点检测;2.棋盘格检测;3.画出来

corners =  findCorners(I,0.01,1);
chessboards = chessboardsFromCorners(corners);
plotChessboards(chessboards,corners);

findcorners.m

灰度转换
img = im2double(img);
if length(size(img))==3
  img = rgb2gray(img);
end
findCorners ::计算梯度的角度,归一化到【0,pi】;梯度强度,并归一化到【0,1】
% sobel masks  卷积核
du = [-1 0 1; -1 0 1; -1 0 1];
dv = du';

% compute image derivatives (for principal axes estimation)
img_du     = conv2(double(img),du,'same');%卷积:对img进行du卷积,返回和img相同尺寸的卷积结果
img_dv     = conv2(double(img),dv,'same');%两个方向的梯度
img_angle  = atan2(img_dv,img_du);        %梯度方向
img_weight = sqrt(img_du.^2+img_dv.^2);   %梯度值

figure(1); imshow(img_weight); hold on;%%%%%%%%%%%%%%%1

% correct angle to lie in between [0,pi]
img_angle(img_angle<0)  = img_angle(img_angle<0)+pi;
img_angle(img_angle>pi) = img_angle(img_angle>pi)-pi;

% scale input image
img     = double(img);
img_min = min(img(:));
img_max = max(img(:));
img     = (img-img_min)/(img_max-img_min);
findCorners::滤波(其实就是参考博客中写到的8个角点模型,卷积)

这里我增加了一个radius(4) = 30;使他可以检测到中心点附近的(广角相机)角点
createCorrelationPatch这个函数就是其中4个模型,后面会通过case1和case2来增加对称性但是颜色相反的模型。
原程序:
基于生长的棋盘格角点检测方法 代码介绍
修改之后:
基于生长的棋盘格角点检测方法 代码介绍

% 3 scales 后面会用到
radius(1) = 4;
radius(2) = 8;
radius(3) = 12;
radius(4) = 30;
% 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
findCorners::nonMaximumSuppression 非极大值抑制

原文给的非极大值抑制算法的详细原理参考:
http://www.cnblogs.com/liekkas0626/p/5219244.html
中第一个图片不知道是什么语言,我修改了一下,可以参考着看:
基于生长的棋盘格角点检测方法 代码介绍
本文用了一个技巧,它先检测右上角部分,找到极大值点;
然后寻找该点的n*n的区域(处于右边沿的按照实际大小检测相应的大小);
如果n*n区域中还有更大的点就跳出,跳出的点作为新的检测点,继续循环;如果没有则判断它是否大于tau,得到极大值点,然后循环。
直到检测完整张图片。

 function maxima = nonMaximumSuppression(img,n,tau,margin)%抑制范围0-n;极大值需要大于tau

% extract parameters
width  = size(img,2);
height = size(img,1);

% init maxima list 初始化
maxima = [];

%% 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];%将得到的极大值点放到maxima后面,初始的maxima为空
    end

  end
end
findCorners::refineCorners 亚像素化 没有看直接贴出来
%% subpixel refinement  亚像素
if refine_corners
  corners = refineCorners(img_du,img_dv,img_angle,img_weight,corners,10);
end

findCorners::去除杂点:1.不是边沿点;2.为角点打分,低于tau的删去

%% remove corners without edges
idx = corners.v1(:,1)==0 & corners.v1(:,2)==0;
corners.p(idx,:)  = [];
corners.v1(idx,:) = [];
corners.v2(idx,:) = [];

figure(4); imshow(uint8(I2));hold on;%%%%%%%%%%%%%%%%4
alpha=0:pi/20:2*pi;    %角度[0,2*pi] 
 R=4;                   %半径 
 x=R*cos(alpha); 
 y=R*sin(alpha); 
for i=1:size(corners.p,1)
 plot(x+corners.p(i,1),y+corners.p(i,2),'-') 
 fill(x+corners.p(i,1),y+corners.p(i,2),'r');
end

%%
disp('Scoring ...');
% score corners 角点得分
corners = scoreCorners(img,img_angle,img_weight,corners,radius);

% remove low scoring corners
idx = corners.score<tau;
corners.p(idx,:)     = [];
corners.v1(idx,:)    = [];
corners.v2(idx,:)    = [];
corners.score(idx) = [];

figure(5); imshow(uint8(I2));hold on;%%%%%%%%%%%%%%%%5
alpha=0:pi/20:2*pi;    %角度[0,2*pi] 
 R=4;                   %半径 
 x=R*cos(alpha); 
 y=R*sin(alpha); 
for i=1:size(corners.p,1)
 plot(x+corners.p(i,1),y+corners.p(i,2),'-') 
 fill(x+corners.p(i,1),y+corners.p(i,2),'r');
end
findCorners::最后,没看…还是写在这里

% make v1(:,1)+v1(:,2) positive (=> comparable to c++ code)
idx = corners.v1(:,1)+corners.v1(:,2)<0;
corners.v1(idx,:) = -corners.v1(idx,:);

% make all coordinate systems right-handed (reduces matching ambiguities from 8 to 4)
%使所有坐标系统向右(减少匹配模糊从8到4)
corners_n1 = [corners.v1(:,2) -corners.v1(:,1)];
flip       = -sign(corners_n1(:,1).*corners.v2(:,1)+corners_n1(:,2).*corners.v2(:,2));
corners.v2 = corners.v2.*(flip*ones(1,2));

% convert to 0-based index
corners.p = corners.p-1;

根据角点找边沿

chessboards = chessboardsFromCorners(corners);

脚注

生成一个脚注1.

UML 图:

可以渲染序列图:

Created with Raphaël 2.1.2张三张三李四李四嘿,小四儿, 写博客了没?李四愣了一下,说:忙得吐血,哪有时间写。

或者流程图:

Created with Raphaël 2.1.2开始我的操作确认?结束yesno
  • 关于 序列图 语法,参考 这儿,
  • 关于 流程图 语法,参考 这儿.

离线写博客

即使用户在没有网络的情况下,也可以通过本编辑器离线写博客(直接在曾经使用过的浏览器中输入write.blog.csdn.net/mdeditor即可。Markdown编辑器使用浏览器离线存储将内容保存在本地。

用户写博客的过程中,内容实时保存在浏览器缓存中,在用户关闭浏览器或者其它异常情况下,内容不会丢失。用户再次打开浏览器时,会显示上次用户正在编辑的没有发表的内容。

博客发表后,本地缓存将被删除。 

用户可以选择 把正在写的博客保存到服务器草稿箱,即使换浏览器或者清除缓存,内容也不会丢失。

注意:虽然浏览器存储大部分时候都比较可靠,但为了您的数据安全,在联网后,请务必及时发表或者保存到服务器草稿箱

浏览器兼容

  1. 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
  2. IE9以下不支持
  3. IE9,10,11存在以下问题
    1. 不支持离线功能
    2. IE9不支持文件导入导出
    3. IE10不支持拖拽文件导入


  1. 这里是 脚注内容.