手把手教你实现条纹结构光三维重建(1)——多频条纹生成

关于条纹结构光三维重建的多频相移、格雷码、格雷码+相移、互补格雷码等等编码方法,我们在大多数平台上,包括现在使用语言大模型提问,都可以搜到相关的理论,本人重点是想教会你怎么快速用代码实现。

首先说下硬件要求,条纹最终是要烧录到投影仪里,由投影仪打出来,所以需要根据投影的分辨率设计条纹。比如我接下来代码中写到的基于TI 3010 的分辨率,其为1280*720,如果是4710,则是1920*1080,如果是2010,那么就是854*480了,那当然TI还有一款.45,我们也叫做4500,其分辨率是912*1140,根据自己的投影自行设置就好了。

直接附上代码如下(已经包含大多数注释),理论上配置好opencv就可以使用:

#include <iostream>
#include <vector>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc_c.h"#define PROJECTOR_WIDTH    1280          //这里定义的是投影仪的分辨率
#define PROJECTOR_HEIGHT   720
#define PI 3.1415926void generate_freqs(std::vector <float> &freq_array,int length,int min_T)
{freq_array[4] = (double)length / min_T;     //我们需要生成五个频率,第五个频率为[投影宽度/周期]double x = sqrtf(sqrtf(freq_array[4]));    //第二个频率定义为第五个频率的开四次根号freq_array[3] = x * x * x; //第四个频率  freq_array[2] = x * x;     //第三个频率freq_array[1] = x;         //第二个频率freq_array[0] = 1;         //第一个频率
}void calc_phaseVal(float D, float A, float S, float F, int lens, cv::Mat econde_data)
{double W = 2 * PI * F / (double)(lens);for (int x = 0; x < lens; x++){econde_data.data[x] = (uchar)(D + A * cos((x)*W + S) + 0.5);}
}void generate_pattern()
{std::vector<float> h_freq_array, v_freq_array;v_freq_array.resize(5);generate_freqs(v_freq_array, PROJECTOR_HEIGHT, 10);        //图像垂直方向——横条纹频率h_freq_array.resize(5);generate_freqs(h_freq_array, PROJECTOR_WIDTH, 10);         //图像水平方向——竖条纹频率cv::Mat econde_H(1, PROJECTOR_WIDTH, CV_8UC1, cv::Scalar(255));   //图像水平方向编码,生成一个行,其他行一样cv::Mat econde_V(PROJECTOR_HEIGHT,1, CV_8UC1, cv::Scalar(255));    //图像垂直方向编码,生成一个列,其他列一样int m_light = 250;        //图像亮度的最大值float A = (m_light) / 2;float D = (m_light) / 2;char fileNameBmp[60];float phase_shift[4] = { 0.0, PI / 2, PI, 3 * PI / 2 };for (int index = 0; index < 20; index++)   //生成竖条纹(图像水平方向){sprintf_s(fileNameBmp, ".//pattern_H//imagecode_H%d.bmp", index + 1);int phase = index % 4;int freq = index / 4;calc_phaseVal(D, A, -phase_shift[phase], h_freq_array[freq],PROJECTOR_WIDTH, econde_H);cv::Mat econde_show(PROJECTOR_HEIGHT, PROJECTOR_WIDTH, CV_8UC1, cv::Scalar(255));for (int y = 1; y < PROJECTOR_HEIGHT; y++){memcpy(&econde_show.data[y * PROJECTOR_WIDTH], econde_H.data, PROJECTOR_WIDTH * sizeof(uchar));}/*imshow("Econde", Econde_show);cvWaitKey(0);*/imwrite(fileNameBmp, econde_H);}for (int index = 0; index < 20; index++)   //生成横条纹(图像垂直方向){sprintf_s(fileNameBmp, ".//pattern_V//imagecode_V%d.bmp", index + 1);int phase = index % 4;int freq = index / 4;calc_phaseVal(D, A, -phase_shift[phase], v_freq_array[freq], PROJECTOR_HEIGHT, econde_V);cv::Mat econde_show(PROJECTOR_HEIGHT, PROJECTOR_WIDTH, CV_8UC1, cv::Scalar(255));for (int y = 0; y < PROJECTOR_WIDTH; y++)econde_V.copyTo(econde_show.col(y));/*imshow("Econde", econde_show);cvWaitKey(0);*/imwrite(fileNameBmp, econde_V);}//最后生成一张白色的图,用于投影纯色光cv::Mat econde_white(1, PROJECTOR_WIDTH, CV_8UC1, cv::Scalar(255));imwrite(".//pattern_H//white.bmp",econde_white);
}int main()
{generate_pattern();return 0;
}

这时候我们可以用imshow显示,看到不同频率不同相位的条纹图

 

对于有的人,可能刚刚接触C++,或者opencv,或者还只是用matlab,在运行代码遇到问题时,可以直接私信我。

在下一章,我将介绍怎么做单目+投影的标定,先看一个三维重建效果

也可以显示伪彩色

我们测量其平面度,某个黑白区域,其平面度为0.088mm,精度还是很不错的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/24809.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从0到1:企业办公审批小程序开发笔记

可行性分析 企业办公审批小程序&#xff0c;适合各大公司&#xff0c;企业&#xff0c;机关部门办公审批流程&#xff0c;适用于请假审批&#xff0c;报销审批&#xff0c;外出审批&#xff0c;合同审批&#xff0c;采购审批&#xff0c;入职审批&#xff0c;其他审批等规划化…

云计算期末复习(3)

Amazon云计算 习题 私有IP、公有IP和弹性IP的区别在哪里? EC2的实例一旦被创建就会动态地分配公共IP地址和私有IP地址。私有IP地址由动态主机配置协议(DHCP)分配产生。 私有IP、公有IP和弹性IP的主要区别在于它们的使用场景、可达性和管理方式&#xff1a; 私有IP&#xff1a…

46-1 护网溯源 - 钓鱼邮件溯源

一、客户提供钓鱼邮件样本 二、行为分析 三、样本分析 对钓鱼邮件中的木马程序1111.exe文件进行了分析,提交了360安全大脑沙箱云和微步在线云沙箱。 360安全大脑沙箱云显示,该1111.exe文件存在危险,因此在解压时需要谨慎操作,以免触发木马程序。 建议使用360压缩软件进行…

面试(02)————Java集合篇

目录 一、为什么数组索引是从0开始&#xff1f;如果从1开始不行吗&#xff1f; 二、ArrayList底层的实现原理是什么&#xff1f; ​编辑三、ArrayList list new ArrayList(10)中的list扩容几次&#xff1f; 四、如何实现数组与List之间的转换&#xff1f; 五、ArrayList…

Swift 序列(Sequence)排序面面俱到 - 从过去到现在(三)

概述 在上一篇 Swift 序列(Sequence)排序面面俱到 - 从过去到现在(二) 博文中,我们介绍了如何构建一个自定义类型中“多属性”排序的通用实现。 而在本课中我们将再接再厉介绍 iOS 15+ 中新的排序机制,并简要剖析就地排序(In-place sorting)对运行性能有着怎样的显著影…

基础乐理入门

基础概念 乐音&#xff1a;音高&#xff08;频率&#xff09;固定&#xff0c;振动规则的音。钢琴等乐器发出的是乐音&#xff0c;听起来悦耳、柔和。噪音&#xff1a;振动不规则&#xff0c;音高也不明显的音。风声、雨声、机器轰鸣声是噪音&#xff0c;大多数打击乐器&#…

【RK3568】制作Android11开机动画

Android 开机 logo 分为两种&#xff1a;静态显示和动态显示。静态显示就是循环显示一张图片&#xff1b;动态显示就是以特定帧率顺序显示多张图片 1.准备 android logo 图片 Android logo最好是png格式的&#xff0c;因为同一张图片的情况下&#xff0c;png 格式的比 jpg和b…

线性表和链表

一&#xff0c;线性结构 1.Array Array文档&#xff1a;可以自行阅读相关文档来了解Array class array.array(typecode[, initializer]) array.append(x)&#xff1a;添加元素到数组末尾 array.count(x)&#xff1a;计算元素出现次数 array.extend(iterable)&#xff1a;将迭代…

shell编程(二)——字符串与数组

本文为shell 编程的第二篇&#xff0c;介绍shell中的字符串和数组相关内容。 一、字符串 shell 字符串可以用单引号 ‘’&#xff0c;也可以用双引号 “”&#xff0c;也可以不用引号。 单引号的特点 单引号里不识别变量单引号里不能出现单独的单引号&#xff08;使用转义符…

ChatTTS增强版V2,批量导出srt,语速控制,情感控制,支持朗读数字,问题修复

ChatTTS增强版最新版本已经发布&#xff0c;本次更新我主要增加了多文本批量、SRT导出、语速控制、情感控制、停顿控制等新功能&#xff0c;并针对上一版本中存在的数字读音异常、随机uv_break等问题进行了修复。 视频版本 【ChatTTS增强版V2&#xff0c;批量导出srt&#xff…

Android AAudio——C API控制音频流(四)

上一篇文章我们介绍了 C API 中音频流的创建流程,以及打开音频流操作,这里我们再来看一下音频流的其他操作流程 一、音频流操作介绍 1、操作流程图 下图是状态变化流程图,虚线框表示瞬时状态,实线框表示稳定状态。 2、操作函数 上图中主要包含下面几个操作函数: aaudio…

2022 hnust 湖科大 javaweb课设 数据库课设 报告+源代码+流程图文件+课设指导书+附赠数据库课堂实验指导书

2022 hnust 湖科大 javaweb课设 数据库课设 报告源代码流程图文件课设指导书附赠数据库课堂实验指导书 描述 湖南科技大学大二下学期先后开展java web和数据库课程设计&#xff0c;两个课设项目可以通用&#xff0c;老师一般会允许自拟选题&#xff0c;所以在此统一打包&…

批量高效调整图片像素:自定义缩小bmp图片,画质优先,一键实现高效优化

图片已经成为我们生活中不可或缺的一部分。无论是社交媒体分享&#xff0c;还是工作文件传输&#xff0c;图片总是扮演着重要的角色。然而&#xff0c;有时候&#xff0c;我们可能会面临一个问题&#xff1a;图片像素过大&#xff0c;不仅占用过多的存储空间&#xff0c;还可能…

Linux编译器-gcc或g++的使用

一.安装gcc/g 在linux中是不会自带gcc/g的&#xff0c;我们需要编译程序就自己需要安装gcc/g。 很简单我们使用简单的命令安装gcc&#xff1a;sudo yum install -y gcc。 g安装&#xff1a;sudo yum install -y gcc-c。 我们知道Windows上区分文件&#xff0c;都是使用文件…

如何使用Python的Turtle模块绘制小猪

一、前置条件 在开始学习如何使用Python的Turtle模块进行绘画之前&#xff0c;请确保你的电脑已安装Python环境。如果尚未安装Python&#xff0c;你可以从Python官网下载并安装最新版本。 Turtle模块是Python内置的一个用于绘图的库&#xff0c;通常不需要额外安装。如果你发…

反转链表 (oj题)

一、题目链接 https://leetcode.cn/problems/reverse-linked-list/submissions/538124207 二、题目思路 1.定义三个指针&#xff0c;p1先指向NULL p2指向头结点 p3指向第二个结点 2.p2的next指向p1。然后移动指针&#xff0c;p1来到p2的位置&#xff0c;p2来到p3的位置&…

中缀表达式和前缀后缀

在中缀表达式中&#xff0c;操作数可能与两个操作符相结合 但是&#xff0c;想要不带括号无歧义&#xff0c;且不需要考虑运算符优先级和结合性 所以考虑 前缀表达式&#xff0c;波兰表达式 后缀表达式 逆波兰表达式 对于人来说&#xff0c;中缀表达式是最容易读懂的。但是对于…

基于JSP技术的网络视频播放器

你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 管理员界面 用户界…

Live800:客户服务团队的力量,塑造企业的服务之魂

在数字化时代&#xff0c;企业的竞争已经不仅仅是产品和价格的竞争&#xff0c;更是服务质量的竞争。这里将探讨客户服务团队的力量如何塑造企业的服务之魂&#xff0c;以及这一团队如何成为企业不可或缺的一部分。 一、客户服务团队的重要性 客户服务团队是企业与客户之间的重…

毫米波SDK使用2

5.5 毫米波SDK-TI组件 毫米波SDK功能分解成组件将在接下来的几小节中解释。有关这些模块的详细文档&#xff0c;请参阅位于mmwave_mcuplus_sdk_<ver>/docs/mmwave_sdk_module_document .html的顶层文档。 5.5.1 演示 5.5.1.1 毫米波演示 这个演示位于mmwave_mcuplus_sd…