这个东西是我接触的第一个非2D方面的算法,到目前为止其实也没有完全搞定,不过可能短时间内也无法突破。先把能搞定的搞定吧。

这个东西也有一大堆参考资料,不过呢,搜来搜去其实也就那些同样的东西,个人觉得就属这个文章最经典,既有说明,也有图片,还有代码:

Photometric Stereo    Chaman Singh Verma and Mon-Ju Wu

       https://pages.cs.wisc.edu/~csverma/CS766_09/Stereo/stereo.html

  另外,github上也应该有一些参考的资料吧,我主要参考的是 https://github.com/chaochaojnu这个中国小哥的博客。

  目前为止,我只实现了提取Albedo、Normal Map和Normal Vectors三个结果。

  从硬件上讲,这算法应该需要一个固定位置的相机(应该是要和目标垂直吧),以及至少3个以上的平行光源,一般实际上可能需要至少4个以上的光源吧,然后每个光源单独打光,单独拍一张图片,共得到N个不同的图片,然后根据这个N个图片,合成一个结果图以及得到额外的梯度和高度信息。

  在Halcon中,有对应的photometric_stereo算子实现该功能,该算子除了要提供N个图片,还需要提供 Slants和Tilts两个参数,你去看他们的英语翻译,其实都是倾斜角,个人理解Tilts就是光源在XY平面投影时和X轴的夹角,而Slants就是光源和XY平面的夹角。

  在我刚刚提供的两个链接里,他们都不是直接提供 Slants和Tilts,而是直接利用标准物体在对应光源下拍照,得到几幅标准图像,然后由标准图像的像素值推算出对应的归一化光源向量,这个方法也是不错了,省去了相机和光源位置的标定。

  有了这些参数,就可以进行算法的执行了,对于Normal Map的获取,在Photometric Stereo这个文章里有一大堆推导,开始看不懂,慢慢的又觉得懂了,然后又有点懵逼,接着折腾又似乎清晰了。

  其实不用管那么多,我们看看Photometric Stereo给出的NormalMap.m代码里的细节吧:

   for i = 1:nrows
for j = 1:ncols
if( maskImage(i,j) )
for im = 1:numImages
I(im) = double(grayimages(i,j,im));
end
[NP,R,fail] = PixelNormal(I, lightMatrix);
surfNormals(i,j,1) = NP(1);
surfNormals(i,j,2) = NP(2);
surfNormals(i,j,3) = NP(3);
albedo(i,j) = R;
end
end
end

  这里的surfNormals就是对应上图中的Normal Vectors,albedo就是反射率图。

  lightMatrix是多个光源的向量,I是多个图对应的像素值,这里的关键在于PixelNormal函数。

unction [N,R, fail] = PixelNormal(I, L)
fail = 0;
I = I';
LT = L';
A = LT*L;
b = LT*I;
g = inv(A)*b;
R = norm(g);
N = g/R;
if( norm(I) < 1.0E-06)
fprintf( ' Warning: Pixel intensity is zero \n' );
N(1) = 0.0;
N(2) = 0.0;
N(3) = 0.0;
R = 0.0;
fail = 1;
end
end

  仔细看这个函数,其实就是上面工时最后一部分的直接实现,什么转置、乘积、求逆、归一化等等。

这个M代码要稍微修改才可以运行,我尝试了下只是运行获取Normal Map这一块,4个500*500的大小的灰度图,需要大概2s的时间,这个时间其实对于工程项目本身来说是没有任何意义的。所以也验证了一句话,matlab只是实验里的工具。

当然,M代码本身也常常只是用来作为验证一个算法的结果是否正确的第一步而已。

  要真正让他变得有意义,像这么大的图,一个合理的处理时间是不大于2ms,这个优化其实也不是很困难,只要你仔细的看看PixelNormal函数里的数据和代码。

  里面最耗时的其实是inv(A),矩阵求逆需要用到LU分解,很少麻烦,但是实际上,我们注意到这里的A值其实是由L一个值决定的,L是什么,是光源的向量矩阵,这意味着什么,就是L是不变的。没有必要每次在循环里计算矩阵的逆的。 这是最重要的一个问题和耗时点所在。

  了解到这个问题后,我们其他的优化手段就是代码层次上的了,比如用C++写算法、把PixelNormal这个小函数直接集成到循环内部、使用SIMD指令加速等等。

  在几个matlab代码里,还要求提供一个mask图,有这个的原因是很多立体光度法拍摄的图片其实有很多黑色的部分或者说可以确定不是目标的部分,这部分如果处理, 是会拖累算法的速度的,因此用个标定好的MASK去删除他,这也无可厚非,不过在halcon里似乎没有这个参数。

  我目前也就只研究到这里,至于后面的深度图或者说是高度图的实现,文章里提供的都是解一个很大的稀疏矩阵,这个已经超出了我所能自行编程的范围。暂时没有能力去解决了。

在Halcon中,利用光度立体法去实现一些检测目标的一个重要应用是通过photometric_stereo算子获取对应的gradient,然后在利用derivate_vector_field 获得梯度的平均曲率场,我目前还不明白这个gradient到底代表了什么值,是上面的M代码里的surfNormals向量吗?有没有哪位朋友知道呢。

  通过和halcon比较,目前获取的反射率图,基本还是差不多正确的,比如下面几个halcon的测试图:

     

 合成后的反射率图为:

再比如:

     

  合成后为:

  下面这个四个图更能合适的看到多光源的合成效果:

      

  合成后为:

  合成后的图各个方向的光线都比较均匀了。

个人觉得这种合成似乎也可以用多图的HDR来做,不过多图HDR还是不能获取一些额外的信息。

  关于光度立体法目前也只能研究这么多了。希望以后有契机再去研究后续的其他细节。

  目前,如果是纯粹的只是获取Normal Map图,我的优化的程序速度非常快,在4个方向 2500*2000像素的灰度图,获取大概只需要25ms,预计比原始的M代码快近2000倍。

  

  提供一个简易的测试DEMO:https://files.cnblogs.com/files/Imageshop/stereo.rar?t=1669368744

研究光度立体法阶段性小结和优化(可20ms获取4个2500*2000灰度图的Normal Map)。的更多相关文章

  1. photometric_stereo halcon光度立体法三维表面重建

    官方文档翻译 名称: photometric_stereo -- 通过光度立体技术重建表面. 签名: photometric_stereo(Images : HeightField, Gradient ...

  2. halcon——缺陷检测常用方法总结(光度立体)

    引言 机器视觉中缺陷检测分为一下几种: blob+特征(官方示例surface_scratch.hdev) blob+差分+特征(官方示例pcb_inspection.hdev) 光度立体 特征训练 ...

  3. 『嗨威说』算法设计与分析 - 回溯法思想小结(USACO-cha1-sec1.5 Checker Challenge 八皇后升级版)

    本文索引目录: 一.回溯算法的基本思想以及个人理解 二.“子集和”问题的解空间结构和约束函数 三.一道经典回溯法题点拨升华回溯法思想 四.结对编程情况 一.回溯算法的基本思想以及个人理解: 1.1 基 ...

  4. Ofbiz项目学习——阶段性小结——删除数据

    一.根据主键进行删除 /** * 按主键进行删除 * @param dctx * @param context * @return */ public static Map<String,Obj ...

  5. Ofbiz项目学习——阶段性小结——更新数据

    一.根据一个字段进行修改 /** * 根据一个字段进行修改(这个条件字段可以是主键, 也可以不是主键) * @param dctx * @param context * @return */ publ ...

  6. Ofbiz项目学习——阶段性小结——插入数据

    一.通用插入操作 /** * * 编写一个服务createUomOneDemo, * 该服务的作用是在表Uom中增加一条记录,其中: * 字段uomId的值为“BaseLineProduct”. * ...

  7. Ofbiz项目学习——阶段性小结——服务返回结果

    一.返回成功 1.在.DispatcherReturnDemoService类中编写服务[returnSuccess],内容如下: /** * 返回成功结果 * @param dctx * @para ...

  8. Ofbiz项目学习——阶段性小结——视图

    一.简要介绍 1.按照SQL的视图概念:在 SQL 中,视图是基于 SQL 语句的结果集的可视化的表.视图包含行和列,就像一个真实的表.视图中的字段就是来自一个或多个数据库中的真实的表中的字段. 2. ...

  9. Ofbiz项目学习——阶段性小结——查询

    一.组装参数的学习 首先是查询条件,对于查询条件,需要判断是否从前端传递空值?——怎么处理查询空值? 当然可以一个一个进行判断,但是这样代码会导致很多,可以统一处理,形成一个公共方法. 1. 单个处理 ...

  10. halcon小结

    持更 应用范围 (罗列自官方帮助文档,以后有空了按照需求展开叙述) 1. 安全系统 2. 表面检测 3. 定位 4. 二维测量比较 5. 二维码识别 6. 二维位置定位 7. 二维物体识别 8. 光学 ...

随机推荐

  1. 快Key:按一下鼠标【滚轮】,帮你自动填写用户名密码,快速登录,可制作U盘随身(开源免费-附安装文件和源代码)

    * 代码以本文所附下载文件包为准,安装文件和源文件包均在本文尾部可下载. * 快Key及本文所有内容仅供交流使用,使用者责任自负,由快Key对使用者及其相关人员或组织造成的任何损失均由使用者自负,与本 ...

  2. 2_Servlet

    一. 引言 1.1 C/S架构和B/S架构 C/S 和B/S是软件发展过程中出现的两种软件架构方式 1.2 C/S架构(Client/Server 客户端/服务器) 特点: 必须在客户端安装特定软件 ...

  3. 从缓存入门到并发编程三要素详解 Java中 volatile 、final 等关键字解析案例

    引入高速缓存概念 在计算机在执行程序时,以指令为单位来执行,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入. 由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这 ...

  4. Springboot 之 Mybatis-plus 多数据源

    简介 Mybatis-puls 多数据源的使用,采用的是官方提供的dynamic-datasource-spring-boot-starter包的 @DS 注解,具体可以参考官网: https://g ...

  5. 关于for循环当中发生强制类型转换的问题

    Map map1 = new HashMap(); Map map2 = new HashMap(); Map map3 = new HashMap(); List<Map> list = ...

  6. 一天五道Java面试题----第八天(怎么处理慢查询--------->简述Myisam和innodb的区别)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.怎么处理慢查询 2.ACID靠什么保证的 3.什么是MVCC 4.mysql主从同步原理 5.简述Myisam和inn ...

  7. [WPF] 抄抄超强的苹果官网滚动文字特效实现

    1. 前言 今天 ChokCoco 大佬发布了一篇博客 超强的苹果官网滚动文字特效实现,iPhone 我是买不起的,但不妨碍我对抄特效感兴趣,正好我这周安排的工作已经完成了,于是有空练练手实现了一个 ...

  8. 【第1篇】人工智能(AI)语音测试原理和实践---宣传

    ​前言 本文主要介绍作者关于人工智能(AI)语音测试的各方面知识点和实战技术. 本书共分为9章,第1.2章详细介绍人工智能(AI)语音测试各种知识点和人工智能(AI)语音交互原理:第3.4章介绍人工智 ...

  9. shell实践

    shell实践 父子shell 父shell:我们在登录某个虚拟机控制器终端的时候(连接某一个linux虚拟机)时,默认启动的交互式shell,然后等待命令输入. ps命令参数,是否有横杠的参数作用是 ...

  10. 5 why 分析法,一种用于归纳抽象出解决方案的好方法

    最近在看了<微信背后的产品观 - 张小龙手抄版>,其中有段话如下: 用户需求是零散的,解决方案是归纳抽象的过程 那如何归纳抽象呢?是否有一定的实践方法论呢?经过一轮探讨和学习,有这些答案: ...