在上一篇文章《Linux环境下奥比中光摄像头开发环境搭建(基于Orbbec SDK)》中,我们讲解了如何在Linux下配置奥比中光摄像头的使用环境,下载了Orbbec SDK,配置其编译环境,并编译、测试了附带的深度图像读取例程。本文讲解如何自行编写CMake编译C++程序,借助Orbbec Astra Pro Plus摄像头读取并展示深度图。
工程目录
在上一篇文章《Linux环境下奥比中光摄像头开发环境搭建(基于Orbbec SDK)》,我们已经安装了OpenCV 4,并讲解了Orbbec的SDK的目录结构。当我们自行构建一个工程的时候,我们只需要把SDK文件夹放在我们的目录中,不需要例程。在这篇文章中,目录结构如下所示:
DepthViewerDemo
├── 3rdparty // 第三方库的目录。本文仅用到Orbbec SDK(OpenCV已经被安装到系统路径中)
│ └── SDK // SDK安装包下的SDK目录
│ ├── include // SDK头文件目录
│ │ └── libobsensor
│ │ ├── h
│ │ │ ├── Context.h
│ │ │ ├── ...
│ │ │ └── Version.h
│ │ ├── hpp
│ │ │ ├── Context.hpp
│ │ │ ├── ...
│ │ │ └── Version.hpp
│ │ ├── ObSensor.h
│ │ └── ObSensor.hpp
│ └── lib // SDK的链接库文件目录
│ ├── libOrbbecSDK.so
│ ├── libOrbbecSDK.so.1.4
│ └── libOrbbecSDK.so.1.4.3
├── CMakeLists.txt // CMake脚本
├── include // 自己代码的头文件目录
└── src // 自己代码的源文件目录
└── main.cpp // 主程序脚本
CMake编译脚本
通过在CMakeLists.txt
文件中,写入如下脚本,便可以编译这个项目:
cmake_minimum_required(VERSION 3.13)
# Project basic information
project(DepthViewer LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
set(THIRD_PARTY_DEPENDENCY_DIR ${CMAKE_SOURCE_DIR}/3rdparty)
# Set output options
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Executable
add_executable(${PROJECT_NAME}
"src/main.cpp"
)
target_include_directories(${PROJECT_NAME}
PRIVATE
"include"
)
# Find OpenCV
# set(OpenCV_DIR "3rdparty")
find_package(OpenCV 4 REQUIRED)
# Configurate Orbbec SDK
set(OrbbecSDK_ROOT_DIR ${THIRD_PARTY_DEPENDENCY_DIR}/SDK)
set(OrbbecSDK_INCLUDE_DIR ${OrbbecSDK_ROOT_DIR}/include)
set(OrbbecSDK_LIBRARY_DIRS ${OrbbecSDK_ROOT_DIR}/lib)
# Add dependences
target_include_directories(${PROJECT_NAME} SYSTEM
PRIVATE
${OpenCV_INCLUDE_DIR}
${OrbbecSDK_INCLUDE_DIR}
)
target_link_directories(${PROJECT_NAME}
PRIVATE
${OpenCV_LIBRARY_DIRS}
${OrbbecSDK_LIBRARY_DIRS}
)
# Add all libraries
target_link_libraries(${PROJECT_NAME}
PRIVATE
OrbbecSDK
${OpenCV_LIBS}
$<${UNIX}:pthread>
)
将摄像头的帧转换为OpenCV的矩阵
Orbbec SDK并没有为我们提供图像处理的功能,仅提供了流的功能帮助我们提取相机读取出来的每一帧图像。因此,我们需要将流中提取到的每一帧图像,转换为OpenCV的图像矩阵cv::Mat
,才能进行图像的更进一步处理、展示。Orbbec SDK为我们提供了ob::Pipeline
对象,帮助我们读取摄像头采集到的视频流的帧集合ob::FrameSet
。在帧集合中,我们可以提取出其深度帧、红外线帧等对象。之后,我们就可以将其转为OpenCV的矩阵cv::Mat
。
ob::Frame
的继承关系如下所示:
Frame
|
+------+----------+---------+------------+
| | | | |
VideoFrame PointFrame AccelFrame GyroFrame FrameSet
|
+-----+----+---------+
| | |
ColorFrame DepthFrame IRFrame
当我们在循环中不断读取深度帧,将其转换为OpenCV的矩阵并不断刷新地展示,便可以在屏幕中看到动态的深度图像。彩色摄像头图像和红外摄像头图像也可以通过类似的方法获取。代码如下所示:
#include <cstdlib>
#include <iostream>
#include <libobsensor/hpp/Device.hpp>
#include <libobsensor/hpp/Error.hpp>
#include <libobsensor/hpp/Pipeline.hpp>
#include <libobsensor/hpp/StreamProfile.hpp>
#include <memory>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
int main(void) {
ob::Pipeline pipe;
try {
// 获取深度相机的所有流配置,包括流的分辨率,帧率,以及帧的格式
auto profiles = pipe.getStreamProfileList(OB_SENSOR_DEPTH);
std::shared_ptr<ob::VideoStreamProfile> depth_profile = nullptr;
try {
// 根据指定的格式查找对应的Profile,优先查找Y16格式
depth_profile = profiles->getVideoStreamProfile(640, 0, OB_FORMAT_Y16, 30);
} catch (const ob::Error &) {
// 没找到Y16格式后不匹配格式查找对应的Profile进行开流
depth_profile = profiles->getVideoStreamProfile(640, 0, OB_FORMAT_UNKNOWN, 30);
}
// 通过创建Config来配置Pipeline要启用或者禁用哪些流,这里将启用深度流
std::shared_ptr<ob::Config> config = std::make_shared<ob::Config>();
config->enableStream(depth_profile);
// 启动在Config中配置的流,如果不传参数,将启动默认配置启动流
pipe.start(config);
// 设置镜像模式,先判断设备是否有可读可写的权限,再进行设置
const auto &device = pipe.getDevice();
if (device->isPropertySupported(OB_PROP_DEPTH_MIRROR_BOOL, OB_PERMISSION_WRITE)) {
device->setBoolProperty(OB_PROP_DEPTH_MIRROR_BOOL, true);
}
while (cv::waitKey(1) != 27) { // ESC的ASCII码是27,按ESC键可以终止程序
// 以阻塞的方式等待一帧数据,该帧是一个复合帧,配置里启用的所有流的帧数据都会包含在frameSet内,
// 并设置帧的等待超时时间为100ms
auto frame_set = pipe.waitForFrames(100);
if (frame_set == nullptr) {
continue;
}
cv::Mat img = frame2mat(frame_set->depthFrame());
cv::imshow("Depth Frame", img);
}
pipe.stop();
} catch (const ob::Error &e) {
pipe.stop();
std::cerr << "Function:" << e.getName() << "\nargs:" << e.getArgs()
<< "\nmessage:" << e.getMessage() << "\ntype:" << e.getExceptionType()
<< std::endl;
exit(EXIT_FAILURE);
} catch (const std::exception &e) {
pipe.stop();
std::cerr << e.what() << std::endl;
exit(EXIT_FAILURE);
} catch (...) {
pipe.stop();
std::cerr << "Unexpected Error!" << std::endl;
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
将Orbbec SDK帧转为OpenCV的矩阵
在上面的代码中,用到了frame2mat
这个函数,将将Orbbec SDK的视频帧转为OpenCV的矩阵。这个函数是用户定义而非SDK中自带的。参考Orbbec SDK的例程,这个函数的实现如下:
#include <libobsensor/hpp/Frame.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
cv::Mat frame2mat(const std::shared_ptr<ob::Frame> &raw_frame) {
const int data_size = static_cast<int>(raw_frame->dataSize());
if (raw_frame == nullptr || data_size < 1024) {
return {};
}
const auto frame = raw_frame->as<ob::VideoFrame>();
const OBFrameType frame_type = frame->type();
const OBFormat frame_format = frame->format();
const int frame_height = static_cast<int>(frame->height());
const int frame_width = static_cast<int>(frame->width());
void *const frame_data = frame->data();
cv::Mat result_mat;
if (frame_type == OB_FRAME_COLOR) {
// Color image
if (frame_format == OB_FORMAT_MJPG) {
const cv::Mat raw_mat(1, data_size, CV_8UC1, frame_data);
result_mat = cv::imdecode(raw_mat, 1);
} else if (frame_format == OB_FORMAT_NV21) {
const cv::Mat raw_mat(frame_height * 3 / 2, frame_width, CV_8UC1, frame_data);
cv::cvtColor(raw_mat, result_mat, cv::COLOR_YUV2BGR_NV21);
} else if (frame_format == OB_FORMAT_YUYV || OB_FORMAT_YUY2) {
const cv::Mat raw_mat(frame_height, frame_width, CV_8UC2, frame_data);
cv::cvtColor(raw_mat, result_mat, cv::COLOR_YUV2BGR_YUY2);
} else if (frame_format == OB_FORMAT_RGB888) {
const cv::Mat raw_mat(frame_height, frame_width, CV_8UC3, frame_data);
cv::cvtColor(raw_mat, result_mat, cv::COLOR_RGB2BGR);
} else if (frame_format == OB_FORMAT_UYVY) {
const cv::Mat raw_mat(frame_height, frame_width, CV_8UC2, frame_data);
cv::cvtColor(raw_mat, result_mat, cv::COLOR_YUV2BGR_UYVY);
}
} else if (frame_format == OB_FORMAT_Y16 || frame_format == OB_FORMAT_YUYV ||
frame_format == OB_FORMAT_YUY2) {
// IR or depth image
const cv::Mat raw_mat(frame_height, frame_width, CV_16UC1, frame_data);
const double scale = 1 / pow(2, frame->pixelAvailableBitSize() -
(frame_type == OB_FRAME_DEPTH ? 10 : 8));
cv::convertScaleAbs(raw_mat, result_mat, scale);
} else if (frame_type == OB_FRAME_IR) {
// IR image
if (frame_format == OB_FORMAT_Y8) {
result_mat = cv::Mat(frame_height, frame_width, CV_8UC1, frame_data);
} else if (frame_format == OB_FORMAT_MJPG) {
const cv::Mat raw_mat(1, data_size, CV_8UC1, frame_data);
result_mat = cv::imdecode(raw_mat, 1);
}
}
return result_mat;
}
环境说明
- 操作系统:Ubuntu 22.04 x86_64
- 摄像头:Orbbec Astra Pro Plus
参考文献
https://developer.orbbec.com.cn/technical_library.html?id=42
标签:深度图,mat,为例,frame,Orbbec,OB,include,cv,SDK From: https://www.cnblogs.com/fang-d/p/OrbbecSDK_Image_Reading.html