这个东西是我接触的第一个非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. 【Vue学习笔记】—— vuex的语法 { }

    学习笔记 作者:o_Ming vuex Vuex ++ state ++ (用于存储全局数据) 组件访问 state 中的全局数据的方式1: this.$store.state.全局数据 组件访问 s ...

  2. JS数据结构之 Map

    JS数据结构之 Map Map介绍 Map(映射)是ES6引入的一种数据结构.这是一种存储键值对列表很方便的方法,类似于其他编程语言的哈希表. HashMap(哈希表),也叫做散列表.是根据关键码值 ...

  3. Gitea 与 Drone 集成实践:完全基于 Docker 搭建的轻量级 CI/CD 系统

    Drone 是一个使用 Go 语言编写的自助式的持续集成平台,和 Gitea 一样可以完全基于容器部署,轻松扩展流水线规模.开发者只需要将持续集成过程通过简单的 YAML 语法写入 Gitea 仓库目 ...

  4. ProxySQL Cluster 概述

    文章转载自:https://blog.csdn.net/n88Lpo/article/details/79608639 前言 在ProxySQL 1.4.2 之前,ProxySQL 单点的解决方法有配 ...

  5. kubeadm 使用 Calico CNI 以及外部 etcd 部署 kubernetes v1.23.1 高可用集群

    文章转载自:https://mp.weixin.qq.com/s/2sWHt6SeCf7GGam0LJEkkA 一.环境准备 使用服务器 Centos 8.4 镜像,默认操作系统版本 4.18.0-3 ...

  6. 使用Prometheus和Grafana监控RabbitMQ集群 (使用RabbitMQ自带插件)

    配置RabbitMQ集群 官方文档:https://www.rabbitmq.com/prometheus.html#quick-start 官方github地址:https://github.com ...

  7. 第二章:视图层 - 7:HttpResponse对象

    类定义:class HttpResponse[source] HttpResponse类定义在django.http模块中. HttpRequest对象由Django自动创建,而HttpRespons ...

  8. 几篇关于MySQL数据同步到Elasticsearch的文章---第三篇:logstash_output_kafka:Mysql同步Kafka深入详解

    文章转载自: https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247484411&idx=1&sn=1f5a371 ...

  9. Redis的web管理界面redis-manager

    下载 下载地址:https://github.com/ngbdf/redis-manager/releases 配置 tar -zxv -f redis-manager-2.3.2.2-RELEASE ...

  10. Redis一键安装脚本

    #! /usr/bin/env bash # redis 6.0.3 源码安装 # 用法: bash -x install-redis-single.sh 6.0.3 version=$1 usage ...