OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
采集正弦模式教程

目标

在本教程中,您将学习如何使用正弦模式类来

  • 生成正弦模式。
  • 投射生成的模式。
  • 捕获投射的模式。
  • 使用三种不同的算法(傅里叶变换轮廓术、相移轮廓术、傅里叶辅助相移轮廓术)从这些模式计算包裹相位图
  • 解包裹之前的相位图。

代码

/*M///////////////////////////////////////////////////////////////////////////////////////
//
// 重要提示:在下载、复制、安装或使用前请阅读。
//
// 通过下载、复制、安装或使用本软件,即表示您同意本许可证。
// 如果您不同意本许可证,请勿下载、安装,
// 复制或使用本软件。
//
//
// 许可协议
// 适用于开源计算机视觉库
//
// 版权所有 (C) 2015, OpenCV Foundation, 保留所有权利。
// 第三方版权归各自所有者所有。
//
// 以源代码和二进制形式重新分发和使用,无论是否修改,
// 前提是满足以下条件
//
// * 源代码的重新分发必须保留上述版权声明,
// 本条件列表和以下免责声明。
//
// * 二进制形式的重新分发必须在文档中复制上述版权声明、
// 本条件列表和以下免责声明
// 本条件列表和以下免责声明。
//
// * 未经特定书面许可,不得使用版权持有人的名称来认可或推广源自本软件的产品。
// 本软件由版权持有人和贡献者“按原样”提供,
//
// 任何明示或暗示的保证,包括但不限于适销性和特定用途适用性的暗示保证均不予承认。
// 在任何情况下,Intel Corporation 或贡献者均不对任何直接、
// 间接、偶然、特殊、惩戒性或后果性损害负责
// (包括但不限于采购替代商品或服务;
// 使用、数据或利润损失;或业务中断),无论其成因
// 以及基于何种责任理论,无论是合同、严格责任,
// 或侵权(包括疏忽或其他)以任何方式因使用本软件而引起,
// 即使已被告知此类损害的可能性。
#include <opencv2/core.hpp>
//
//M*/
#include <vector>
#include <iostream>
#include <fstream>
using namespace cv;
using namespace std;
static const char* keys =
"{@width | | 投影仪宽度}"
"{@height | | 投影仪高度}"
{
"{@periods | | 周期数}"
"{@setMarkers | | 带或不带标记的模式}"
"{@horizontal | | 模式是水平的}"
"{@methodId | | 要使用的方法}"
"{@outputPatternPath | | 保存模式的路径}"
"{@outputWrappedPhasePath | | 保存包裹相位图的路径}"
"{@outputUnwrappedPhasePath | | 保存解包裹相位图的路径}"
"{@outputCapturePath | | 保存捕获图像的路径}"
"{@reliabilitiesPath | | 保存可靠性的路径}"
static void help()
cout << "\n此示例生成正弦模式" << endl;
};
cout << "调用方法:./example_structured_light_createsinuspattern <宽度> <高度>"
{
" <周期数> <设置标记>(布尔) <水平模式>(布尔) <方法ID>"
" <捕获输出路径> <模式输出路径>(可选) <包裹相位图输出路径> (可选)"
" <解包裹相位图输出路径>" << endl;
int main(int argc, char **argv)
if( argc < 2 )
}
{
{
help();
return -1;
}
// 从命令行检索参数
CommandLineParser parser(argc, argv, keys);
params.width = parser.get<int>(0);
params.height = parser.get<int>(1);
params.nbrOfPeriods = parser.get<int>(2);
params.setMarkers = parser.get<bool>(3);
params.horizontal = parser.get<bool>(4);
params.methodId = parser.get<int>(5);
String outputCapturePath = parser.get<String>(6);
params.shiftValue = static_cast<float>(2 * CV_PI / 3);
params.nbrOfPixelsBetweenMarkers = 70;
String outputPatternPath = parser.get<String>(7);
String outputWrappedPhasePath = parser.get<String>(8);
String outputUnwrappedPhasePath = parser.get<String>(9);
String reliabilitiesPath = parser.get<String>(10);
structured_light::SinusoidalPattern::create(makePtr<structured_light::SinusoidalPattern::Params>(params));
vector<Mat> patterns;
Mat shadowMask;
Mat unwrappedPhaseMap, unwrappedPhaseMap8;
Mat wrappedPhaseMap, wrappedPhaseMap8;
// 生成正弦模式
sinus->generate(patterns);
VideoCapture cap(CAP_PVAPI);
if( !cap.isOpened() )
cout << "无法打开相机" << endl;
cap.set(CAP_PROP_PVAPI_PIXELFORMAT, CAP_PVAPI_PIXELFORMAT_MONO8);
{
namedWindow("pattern", WINDOW_NORMAL);
return -1;
}
setWindowProperty("pattern", WND_PROP_FULLSCREEN, WINDOW_FULLSCREEN);
imshow("pattern", patterns[0]);
cout << "准备就绪后按任意键" << endl;
int nbrOfImages = 30;
int count = 0;
waitKey(0);
vector<Mat> img(nbrOfImages);
Size camSize(-1, -1);
while( count < nbrOfImages )
for(int i = 0; i < (int)patterns.size(); ++i )
imshow("pattern", patterns[i]);
{
cap >> img[count];
{
count += 1;
waitKey(300);
cout << "准备就绪后按回车键" << endl;
bool loop = true;
}
}
while ( loop )
char c = (char) waitKey(0);
if( c == 10 )
{
switch(params.methodId)
case structured_light::FTP
{
loop = false;
}
}
for( int i = 0; i < nbrOfImages; ++i )
{
/*我们需要三张图像来计算阴影掩模,如参考文献中所述
* 即使相位图仅由一个模式计算得出
{
vector<Mat> captures;
if( i == nbrOfImages - 2 )
*/
captures.push_back(img[i]);
captures.push_back(img[i-1]);
{
captures.push_back(img[i+1]);
else if( i == nbrOfImages - 1 )
captures.push_back(img[i-2]);
}
captures.push_back(img[i+2]);
{
captures.push_back(img[i+1]);
else if( i == nbrOfImages - 1 )
sinus->computePhaseMap(captures, wrappedPhaseMap, shadowMask);
}
else
{
captures.push_back(img[i+1]);
captures.push_back(img[i-2]);
if( camSize.height == -1 )
}
camSize.height = img[i].rows;
camSize.width = img[i].cols;
{
paramsUnwrapping.height = camSize.height;
paramsUnwrapping.width = camSize.width;
phaseUnwrapping =
phase_unwrapping::HistogramPhaseUnwrapping::create(paramsUnwrapping);
sinus->unwrapPhaseMap(wrappedPhaseMap, unwrappedPhaseMap, camSize, shadowMask);
phaseUnwrapping->unwrapPhaseMap(wrappedPhaseMap, unwrappedPhaseMap, shadowMask);
}
Mat reliabilities, reliabilities8;
reliabilities.convertTo(reliabilities8, CV_8U, 255,128);
ostringstream tt;
phaseUnwrapping->getInverseReliabilityMap(reliabilities);
tt << i;
imwrite(reliabilitiesPath + tt.str() + ".png", reliabilities8);
unwrappedPhaseMap.convertTo(unwrappedPhaseMap8, CV_8U, 1, 128);
wrappedPhaseMap.convertTo(wrappedPhaseMap8, CV_8U, 255, 128);
if( !outputUnwrappedPhasePath.empty() )
ostringstream name;
name << i;
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
imwrite(outputWrappedPhasePath + "_FTP_" + name.str() + ".png", wrappedPhaseMap8);
}
case structured_light::PSP
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
case structured_light::FAPS
}
}
break;
for( int i = 0; i < nbrOfImages - 2; ++i )
if( params.methodId == structured_light::PSP )
imwrite(outputUnwrappedPhasePath + "_PSP_" + name.str() + ".png", unwrappedPhaseMap8);
{
captures.push_back(img[i]);
captures.push_back(img[i+1]);
captures.push_back(img[i-2]);
if( camSize.height == -1 )
camSize.height = img[i].rows;
camSize.width = img[i].cols;
{
paramsUnwrapping.height = camSize.height;
paramsUnwrapping.width = camSize.width;
phaseUnwrapping =
phase_unwrapping::HistogramPhaseUnwrapping::create(paramsUnwrapping);
sinus->unwrapPhaseMap(wrappedPhaseMap, unwrappedPhaseMap, camSize, shadowMask);
phaseUnwrapping->unwrapPhaseMap(wrappedPhaseMap, unwrappedPhaseMap, shadowMask);
}
Mat reliabilities, reliabilities8;
if( !outputUnwrappedPhasePath.empty() )
ostringstream name;
reliabilities.convertTo(reliabilities8, CV_8U, 255,128);
ostringstream tt;
phaseUnwrapping->getInverseReliabilityMap(reliabilities);
tt << i;
imwrite(reliabilitiesPath + tt.str() + ".png", reliabilities8);
unwrappedPhaseMap.convertTo(unwrappedPhaseMap8, CV_8U, 1, 128);
wrappedPhaseMap.convertTo(wrappedPhaseMap8, CV_8U, 255, 128);
name << i;
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
imwrite(outputUnwrappedPhasePath + "_FAPS_" + name.str() + ".png", unwrappedPhaseMap8);
imwrite(outputWrappedPhasePath + "_PSP_" + name.str() + ".png", wrappedPhaseMap8);
else
imwrite(outputWrappedPhasePath + "_FAPS_" + name.str() + ".png", wrappedPhaseMap8);
}
case structured_light::PSP
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
imwrite(outputUnwrappedPhasePath + "_FAPS_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputCapturePath.empty() )
else
imwrite(outputCapturePath + "_PSP_" + name.str() + ".png", img[i]);
}
imwrite(outputCapturePath + "_FAPS_" + name.str() + ".png", img[i]);
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
imwrite(outputUnwrappedPhasePath + "_FAPS_" + name.str() + ".png", unwrappedPhaseMap8);
if( i == nbrOfImages - 3 )
else
ostringstream nameBis;
nameBis << i+1;
{
imwrite(outputUnwrappedPhasePath + "_FAPS_" + name.str() + ".png", unwrappedPhaseMap8);
{
ostringstream nameTer;
nameTer << i+2;
imwrite(outputCapturePath + "_PSP_" + nameBis.str() + ".png", img[i+1]);
imwrite(outputCapturePath + "_PSP_" + nameTer.str() + ".png", img[i+2]);
imwrite(outputCapturePath + "_FAPS_" + nameBis.str() + ".png", img[i+1]);
imwrite(outputCapturePath + "_FAPS_" + nameTer.str() + ".png", img[i+2]);
}
else
{
ostringstream nameTer;
nameTer << i+2;
imwrite(outputCapturePath + "_PSP_" + nameBis.str() + ".png", img[i+1]);
imwrite(outputCapturePath + "_PSP_" + nameTer.str() + ".png", img[i+2]);
cout << "错误" << endl;
cout << "完成" << endl;
}
}
}
}
break;
default:
if( !outputPatternPath.empty() )
}
for( int i = 0; i < 3; ++ i )
name << i + 1;
{
imwrite(outputPatternPath + name.str() + ".png", patterns[i]);
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
loop = true;
char key = (char) waitKey(0);
}
}
void convertTo(OutputArray m, int rtype, double alpha=1, double beta=0) const
while( loop )
{
用于从视频文件、图像序列或相机捕获视频的类。
if( key == 27 )
{
loop = false;
}
}
return 0;
}
如果数组没有元素,则返回 true。
int64_t int64
n 维密集数组类
定义 mat.hpp:830
void namedWindow(const String &winname, int flags=WINDOW_AUTOSIZE)
使用可选缩放将数组转换为另一种数据类型。
用于指定图像或矩形大小的模板类。
Definition types.hpp:335
cv::setWindowProperty
Definition videoio.hpp:772
std::string String
定义 cvstd.hpp:151
std::shared_ptr< _Tp > Ptr
Definition cvstd_wrapper.hpp:23
CV_8U
#define CV_8U
#define CV_PI
定义 cvdef.h:380
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
void setWindowProperty(const String &winname, int prop_id, double prop_value)
创建窗口。
PyParams params(const std::string &tag, const std::string &model, const std::string &weights, const std::string &device)
动态更改窗口参数。
cv::phase_unwrapping::HistogramPhaseUnwrapping::Params::width
将图像保存到指定文件。
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
定义 core.hpp:107
STL 命名空间。
phaseUnwrapping 构造函数的参数。
Definition histogramphaseunwrapping.hpp:79
cv::phase_unwrapping::HistogramPhaseUnwrapping::Params::height
int height
SinusoidalPattern 构造函数的参数。
定义 sinusoidalpattern.hpp:83
解释
首先,必须生成正弦模式。用户需要设置 SinusoidalPattern 类的参数:

投影仪宽度和高度

模式中的周期数

  • 在模式中设置交叉标记(用于将相对相位图转换为绝对相位图)
  • 模式方向(水平或垂直)
  • 相移值(通常设置为 2pi/3 以启用循环系统)
  • 同一行/列上两个连续标记之间的像素数
  • 用于计算相位图的方法 ID(FTP = 0, PSP = 1, FAPS = 2)
  • 用户还可以选择保存模式和相位图。
  • String outputPatternPath = parser.get<String>(6);

String outputWrappedPhasePath = parser.get<String>(7);

// 从命令行检索参数
params.nbrOfPeriods = parser.get<int>(2);
params.setMarkers = parser.get<bool>(3);
params.horizontal = parser.get<bool>(4);
params.methodId = parser.get<int>(5);
String outputCapturePath = parser.get<String>(6);
params.shiftValue = static_cast<float>(2 * CV_PI / 3);
String outputPatternPath = parser.get<String>(7);
String outputWrappedPhasePath = parser.get<String>(8);
String outputUnwrappedPhasePath = parser.get<String>(8);
Ptr<structured_light::SinusoidalPattern> sinus = structured_light::SinusoidalPattern::create(params);
// 模式存储
无论用于计算相位图的方法如何,模式的数量始终为三。这三个模式以循环方式投射,这很适合循环系统。
生成模式后,将打开相机并以全屏分辨率投射模式。在本教程中,使用 Prosilica 相机捕获灰度图像。当投影仪显示第一个模式时,用户可以按任意键开始投射序列。
Mat unwrappedPhaseMap, unwrappedPhaseMap8;
VideoCapture cap(CAP_PVAPI);
if( !cap.isOpened() )

namedWindow("pattern", WINDOW_NORMAL);

setWindowProperty("pattern", WND_PROP_FULLSCREEN, WINDOW_FULLSCREEN);

cout << "无法打开相机" << endl;
cap.set(CAP_PROP_PVAPI_PIXELFORMAT, CAP_PVAPI_PIXELFORMAT_MONO8);
{
namedWindow("pattern", WINDOW_NORMAL);
return -1;
}
setWindowProperty("pattern", WND_PROP_FULLSCREEN, WINDOW_FULLSCREEN);
imshow("pattern", patterns[0]);
在本教程中,投射了 30 张图像,因此三个模式中的每个模式都投射了十次。“while”循环负责投射过程。捕获的图像存储在 Mat 向量中。两次连续捕获之间有 30 毫秒的延迟。投射完成后,用户必须按“Enter”键开始计算相位图。
imshow("pattern", patterns[i]);
int count = 0;
waitKey(0);

waitKey(30);

vector<Mat> img(nbrOfImages);
Size camSize(-1, -1);
while( count < nbrOfImages )
for(int i = 0; i < (int)patterns.size(); ++i )
imshow("pattern", patterns[i]);
{
cap >> img[count];
{
char c = waitKey(0);
根据所选方法,相位图已准备好进行计算。对于 FTP,每个投射模式都会计算一个相位图,但我们需要从三个连续的模式计算阴影掩模,如 [64] 中所述。因此,在一个名为 captures 的向量中设置了三个模式。我们注意用三个模式填充此向量,尤其是在达到最后捕获时。解包裹算法需要知道捕获图像的大小,因此我们确保将其提供给“unwrapPhaseMap”方法。相位图被转换为 8 位图像,以便将其保存为 png 格式。
cout << "准备就绪后按回车键" << endl;
bool loop = true;
}
}
while ( loop )
char c = (char) waitKey(0);
if( c == 10 )
{
switch(params.methodId)
case structured_light::FTP
{
loop = false;
}
}

对于 PSP 和 FAPS,使用三张投射图像来计算单个相位图。这三张图像设置在“captures”中,这是一个作为 FIFO 工作的向量。同样,相位图被转换为 8 位图像,以便将其保存为 png 格式。

if( params.methodId == structured_light::PSP )
{
/*我们需要三张图像来计算阴影掩模,如参考文献中所述
* 即使相位图仅由一个模式计算得出
{
vector<Mat> captures;
if( i == nbrOfImages - 2 )
*/
captures.push_back(img[i]);
captures.push_back(img[i-1]);
{
captures.push_back(img[i+1]);
else if( i == nbrOfImages - 1 )
captures.push_back(img[i-2]);
}
captures.push_back(img[i+2]);
{
captures.push_back(img[i+1]);
else if( i == nbrOfImages - 1 )
sinus->computePhaseMap(captures, wrappedPhaseMap, shadowMask);
}
else
{
captures.push_back(img[i+1]);
captures.push_back(img[i-2]);
if( camSize.height == -1 )
}
camSize.height = img[i].rows;
camSize.width = img[i].cols;
{
paramsUnwrapping.height = camSize.height;
paramsUnwrapping.width = camSize.width;
}
Mat reliabilities, reliabilities8;
if( !outputUnwrappedPhasePath.empty() )
ostringstream name;
name << i;
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
imwrite(outputWrappedPhasePath + "_FTP_" + name.str() + ".png", wrappedPhaseMap8);
}
case structured_light::PSP
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
case structured_light::FAPS
}
}
break;

imwrite(outputUnwrappedPhasePath + "_PSP_" + name.str() + ".png", unwrappedPhaseMap8);

for( int i = 0; i < nbrOfImages - 2; ++i )
if( params.methodId == structured_light::PSP )
imwrite(outputUnwrappedPhasePath + "_PSP_" + name.str() + ".png", unwrappedPhaseMap8);
{
captures.push_back(img[i]);
captures.push_back(img[i+1]);
captures.push_back(img[i-2]);
if( camSize.height == -1 )
camSize.height = img[i].rows;
camSize.width = img[i].cols;
{
paramsUnwrapping.height = camSize.height;
paramsUnwrapping.width = camSize.width;
}
Mat reliabilities, reliabilities8;
if( !outputUnwrappedPhasePath.empty() )
ostringstream name;
name << i;
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
imwrite(outputUnwrappedPhasePath + "_FAPS_" + name.str() + ".png", unwrappedPhaseMap8);
imwrite(outputWrappedPhasePath + "_PSP_" + name.str() + ".png", wrappedPhaseMap8);
else
imwrite(outputWrappedPhasePath + "_FAPS_" + name.str() + ".png", wrappedPhaseMap8);
}
case structured_light::PSP
{
imwrite(outputUnwrappedPhasePath + "_FTP_" + name.str() + ".png", unwrappedPhaseMap8);
if( !outputWrappedPhasePath.empty() )
imwrite(outputUnwrappedPhasePath + "_FAPS_" + name.str() + ".png", unwrappedPhaseMap8);
生成于 2025 年 7 月 3 日 12:14:36,由 doxygen 1.12.0 为 OpenCV 生成
else
imwrite(outputWrappedPhasePath + "_FAPS_" + name.str() + ".png", wrappedPhaseMap8);
}
}
break;