手把手教你实现条纹结构光三维重建(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.1415926

void 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,精度还是很不错的。

最近更新

  1. TCP协议是安全的吗?

    2024-06-09 01:32:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-09 01:32:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-09 01:32:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-09 01:32:02       18 阅读

热门阅读

  1. 一千题,No.0049(跟奥巴马一起编程)

    2024-06-09 01:32:02       11 阅读
  2. 一些关于机器学习的思路和猜测

    2024-06-09 01:32:02       8 阅读
  3. Python代码——压缩整个文件夹

    2024-06-09 01:32:02       10 阅读
  4. rust结构体

    2024-06-09 01:32:02       7 阅读
  5. 最小二乘法-拟合平面方程

    2024-06-09 01:32:02       9 阅读
  6. 内网中redis无法连接访问问题

    2024-06-09 01:32:02       10 阅读