关于条纹结构光三维重建的多频相移、格雷码、格雷码+相移、互补格雷码等等编码方法,我们在大多数平台上,包括现在使用语言大模型提问,都可以搜到相关的理论,本人重点是想教会你怎么快速用代码实现。
首先说下硬件要求,条纹最终是要烧录到投影仪里,由投影仪打出来,所以需要根据投影的分辨率设计条纹。比如我接下来代码中写到的基于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,精度还是很不错的。