利用Steger算法提取激光中心线的亚像素坐标(Matlab)

标签: 激光中心线提取  算法  计算机视觉  c语言

最近一直在做线结构光扫描三维成像方面的内容,采用结构光进行扫描检测时,需要提取激光条纹的中心线,我利用Steger算法提取激光中心线的亚像素坐标,在Matlab 2018b 软件上运行。
联系方式:QQ:936874728
邮箱:[email protected]
[email protected]
欢迎读者指正错误,希望和大家一起交流线结构光扫描三维成像相关的内容

下面先介绍一下steger算法的运行思路:(借鉴的别人的总结,直接截图白嫖过来了)
在这里插入图片描述
根据算法思路,我们
①输入图像
②图像处理
③利用高斯模板与图像卷积构造hessian矩阵
④求解hessian的特征值得到光条的法线方向
⑤法线方向泰勒展开获得亚像素位置
⑥选择合适的像素点,此时获得的是像素坐标(x0,y0),和对应的(tnx,tny)相加获得亚像素坐标。
根据上述思路编写matlab代码:(代码主题是嫖的网上的开源码,然后结合自己需要进行的修改)。

第一部分先是主函数啦
代码解释:
1.bmp是我采集的激光线的图片,会附在下面。
然后选取感兴趣的区域
最后plot函数绘图
保存激光中心线的亚像素坐标

img = imread('1.bmp');
 laserPixel = findLaserCenter(img,1,960,301,957,0);
 plot(laserPixel(:,1),laserPixel(:,2),'o');
 axis equal
 save C:\Users\17806\Desktop\Steger算法\1.txt -ascii laserPixel

第二部分是调用函数部分
①对感兴趣区域的图像处理
这一部分我就不解释了,都注释在代码里面了

function realPixel = findLaserCenter(frame, startRow, endRow, startColumn, endColumn, plotStatue)
if nargin < 2, plotStatue = 0;    %plotStatue,默认为0
end
frame_gray = rgb2gray(frame);%将文件的第i帧转换为灰度图像
frame_gray_roi = frame_gray(startRow:endRow, startColumn:endColumn);%感兴趣范围
[row, column]=size(frame_gray);     % 图像行列数
keral = fspecial('gaussian',[3,3],0.5);%fspecial函数用于创建预定义的滤波算子,格式:h=fspecial(type,parameters,sigma)
%gaussian,高斯低通滤波器,参数有两个,n表示模板尺寸,默认值为[33],sigma为滤波器的标准差,单位为像素,默认值为0.5
frame_gray_gauss = imfilter(frame_gray_roi,keral,'replicate');%imfilter对任意类型数组或多维图像进行滤波,imfilter(区域,类型,边界)
%replicate图像大小通过复制外边界的值来扩展。
Pixel=steger(frame_gray_gauss);%对滤波之后的图像运用steger算法求得像素坐标。
realPixel = [ Pixel(:, 2) + startColumn - 1,  Pixel(:, 1) + startRow - 1];%真实像素坐标
%Generat a binary image of equal size to show the middle line of laser
%生成大小相同的二值图像,以显示激光器的中心线
if plotStatue ~= 0
    image_1=zeros(row,column);      % 等大的全黑背景
    [max, t] = size(Pixel);
    for index = 1 : max
        image_1(Pixel(index, 1) + startRow - 1, Pixel(index, 2) + startColumn - 1) = 255;
    end
    figure;
    imshow(image_1);
end
end

②调用steger函数
完全根据steger算法思路进行编写的,简单也不用解释了,看代码注释

function linePixel = steger(EinRDR)
%采用steger算法提取光条中心
 im = double(EinRDR);
 im = im/max(im(:)); %%归一化
 bw=im2bw(im, 0.4);  %%二值化
 bw = bwmorph(bw, 'clean', 1);    % 去孤立点
[x0,y0]=find(bw==1);  %%找到白点坐标
sigma=25/sqrt(3);%求Hessian矩阵之前需要对图像进行高斯滤波,高斯滤波时,设置高斯方差σ<(w/3),w是光条宽度
[Dx,Dy,Dxx,Dxy,Dyy] = Hessian2D(im,sigma);
[eigenvalue1, eigenvalue2, eigenvectorx, eigenvectory]=eig2image(Dxx, Dxy, Dyy); 
%eigenvectorx,eigenvectory:Hessian矩阵最大特征值对应的特征向量对应于光条的法线方向 
t = -(Dx.*eigenvectorx + Dy .* eigenvectory) ./...  
    (Dxx .* eigenvectorx.^2 + 2*Dxy.*eigenvectorx.*eigenvectory + Dyy.*eigenvectory.^2 );   
px = t.*eigenvectorx;  
py = t.*eigenvectory;
[candidateX1, candidateY1] = find(px >= -0.5 & px <= 0.5 & py >= -0.5 & py <= 0.5 & bw==1);  
%判断:如果(px,py)[?0.5,0.5]×[?0.5,0.5]
%即一阶导数为零的点位于当前像素内,
%(nx,ny)方向的二阶导数大于指定的阈值,
%则该点(candidataX1,candidataY1)为光条的中心点,candidataX1+px,candidataY1+py 则为亚像素坐标。
linePixel_t = [candidateX1, candidateY1];
for i=1:size(candidateX1,1)
    m1=candidateX1(i,1);
    n1=candidateY1(i,1);
    px1(i,1)=px(m1,n1);
    py1(i,1)=py(m1,n1);
    x1(i,1)=m1+px(m1,n1);
    x2(i,1)=py(m1,n1)+n1;
end
linePixel = [x1,x2];
%获得激光线像素数点坐标
end

③构造hessian矩阵
利用高斯模板和图像卷积获得图像沿x,y方向上的二阶导数

function [Dx,Dy,Dxx,Dxy,Dyy] = Hessian2D(I,Sigma)
%构造高斯模板
if nargin < 2, Sigma = 1; end
[X,Y]   = ndgrid(-round(3*Sigma):round(3*Sigma));
DGaussx  = 1/(2*pi*Sigma^4)*(-X).* exp(-(X.^2 + Y.^2)/(2*Sigma^2));
DGaussy  = 1/(2*pi*Sigma^4)*(-Y).* exp(-(X.^2 + Y.^2)/(2*Sigma^2));
DGaussxx = 1/(2*pi*Sigma^4) * (X.^2/Sigma^2 - 1) .* exp(-(X.^2 + Y.^2)/(2*Sigma^2));
DGaussxy = 1/(2*pi*Sigma^6) * (X .* Y)           .* exp(-(X.^2 + Y.^2)/(2*Sigma^2));
DGaussyy = DGaussxx';
%卷积
Dx  = imfilter(I,DGaussx,'conv');
Dy  = imfilter(I,DGaussy,'conv');
Dxx = imfilter(I,DGaussxx,'conv');
Dxy = imfilter(I,DGaussxy,'conv');
Dyy = imfilter(I,DGaussyy,'conv');
end

④计算黑森矩阵的特征值

function [Lambda1,Lambda2,Ix,Iy]=eig2image(Dxx,Dxy,Dyy)
tmp = sqrt((Dxx - Dyy).^2 + 4*Dxy.^2);
v2x = 2*Dxy; v2y = Dyy - Dxx + tmp;
% Normalize
%标准化
mag = sqrt(v2x.^2 + v2y.^2); i = (mag ~= 0);
v2x(i) = v2x(i)./mag(i);
v2y(i) = v2y(i)./mag(i);
% The eigenvectors are orthogonal
%实对称矩阵性质:不同特征值对应的特征向量是正交的
v1x = -v2y; 
v1y = v2x;
% Compute the eigenvalues
%计算特征值
mu1 = 0.5*(Dxx + Dyy + tmp);
mu2 = 0.5*(Dxx + Dyy - tmp);
% Sort eigen values by absolute value abs(Lambda1)<abs(Lambda2)
%按绝对值abs(Lambda1)<abs(Lambda2)对特征值排序
check=abs(mu1)>abs(mu2);
Lambda1=mu1; Lambda1(check)=mu2(check);
Lambda2=mu2; Lambda2(check)=mu1(check);
Ix=v1x; Ix(check)=v2x(check);
Iy=v1y; Iy(check)=v2y(check);
end

附上程序所需的图片:
我也不知道什么原因,可能网页原因或者我的网络原因吧,原图没传上去。。。。下面截图,大小发生了变化,读者还原过程时需要重新选择感兴趣区域,,,
在这里插入图片描述

试验结果:
在这里插入图片描述
在这里插入图片描述
部分坐标值:
在这里插入图片描述
可能叙述和代码还有些许问题,请大家加以指正,目前在做线结构光扫描三维成像相关的内容,有相关课题的可以加好友讨论。
QQ:936874728
邮箱:[email protected]
[email protected]

版权声明:本文为kangjielearning原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/kangjielearning/article/details/104636867

智能推荐

GDB随笔(一)

在编译的时候必须加上-g,生成的目标文件才能够进行调试。(我们调试的是目标文件) -g选项的作用是在目标文件中加入源代码的信息,保证gdb能找到源文件。 -o选项,相当于指定一个文件作为目标文件。 可以做一个实验:由main.c生成main(-g),然后将m**在目标文件中加入源代码的信息,保证gdb能找到源文件。ain.c改成其他名字,然后调用gdb main,就会发现对gdb使用命令(list...

tensorboard报No dashboards are active for the current data set

遇到这个错误,通过两步可以解决这个问题 一、检查所指定的目录下是否存在event文件 类似于上面箭头所指的文件,并不需要所指定的目录为event的上一级目录,比如像下面这种情况 在启动tensorboard的时候,指定到log目录就行了,命令如下 二、确定logdir的路径是否正确 我们在确定event文件确实存在之后,还需要确定logdir的路径是否正确,因为logdir的路径中不能包含中文、空...

struts2--动态方法调用的三种方式

一般情况下,我们是通过实现action中execute方法来实现请求处理,这样子一个action中就只能写一个方法,当我需要实现很多方法的时候写多个action显然是很不合理的,因此就需要使用动态调用来实现。 方式一:指定method属性 也就是说通过在struts.xml文件中通过配置action标签的method属性来设置即可。 但是这个方法有一个缺陷,当一个action中有很多方法的时候就需...

深度剖析HashMap(一)——基于JDK7

HashMap是每个Java/Android程序员必须掌握的一种容器。在这个专题下将分若干篇文章对其进行深度剖析。由于JDK版本的不同,HashMap的底层实现也有些许差别。本文先对基于JDK7的HashMap进行分析,之后会奉上JDK8中对HashMap实现改动的分析。 一、HashMap结构概览 在JDK7中,HashMap说白了就是用到两种数据结构——数组与链表。 数...

Faster R-CNN基于pytorch的原理

  相关资料以及下载 代码地址:https://github.com/ruotianluo/pytorch-faster-rcnn.git 原理参考:https://blog.csdn.net/zm147451753/article/details/88218619 代码编译和运行 代码使用方法,暂时没找到win系统下的方法,为此,我安装了unbuntu,还是蛮好用的。 代码的编译运行准...

猜你喜欢

welpwn(RCTF-2015)--write up

文件下载地址: 链接:https://pan.baidu.com/s/1MG2z9r4wz_WTEz1vIikqJQ 提取码:3tbc  0x01.分析 checksec: 源码分析: 流程非常简单,首先输入一个1024大小字符串,然后进入函数echo,这个函数会将buf的数据一个字节一个字节的复制到s2中,当遇到x00时停止,退出,并打印s2。 寻找漏洞: 第一个read处没有漏洞,但...

mongodb+java实现日志的日活与月活查询

业务介绍 前段时间有个日志统计的需求,是规范的登陆日志,估计一个月有几十万,放入hadoop太麻烦了,放数据库又怕后续数据量增加较快,于是尝试用mongodb来存储,后续进行统计。 mongodb是采用3.4, 2017年12月最新的是3.6 中文官网的文档(英文官网的文档访问太坑爹了) http://www.mongoing.com/docs/crud.html 先查看下自己的系统,这里是选择用...

HQChart使用教程60-新版k线训练使用教程

HQChart使用教程60-新版k线训练使用教程 样例页面 K线训练重构 创建K线图 操作接口 下一个K线 自动/停止自动移动K线 买卖股票 K线移动监听事件 买卖数据计算 K线背景色设置功能 完整代码 HQChart代码地址 如果教程或hqchart对你有帮助, 请在git上star,教程点下赞 。谢谢~~ 样例页面 手机端页面 https://opensource2.zealink.com/h...

yolo数据集标注软件安装+使用流程

目录 一、数据集标注软件 1.LabelImg  2.Make-sense 二、软件使用流程 一、数据集标注软件 1.LabelImg         LabelImg这个标注软件算是比较主流的数据集标注软件了,我也是查询了大多数软件推荐以及课程学习时up主所推荐基本都有这个,所以本人使用也是这个软件,下面是...

2020-10-25

1024力扣打卡题(动态规划) 解题思路: 1、如果要覆盖所有区间,那么先从视频开始的 0 开始考虑 2、找到所有包含 0 的片段,因为不会有比 0 小开始的片段,所以很好找 3、假如有 [0,1] 和 [0,2] 两个片段,当然会选择 [0,2] 毕竟多了可以剪掉,少了就得多找一个片段了 4、当选择了 [0,2] 为一个肯定要选的片段之后,接下来可以找 [1,x] 和 [2,x] 的片段 5、同...