问题描述

当我将这个库 https://github.com/stereolabs/zed-open-capture 作为第三方库引用时,我想定义一个类来调用相机,截取部分代码如下:

#pragma once
 
#include "dataflow/utils/camera_interface.h"
#include "videocapture.hpp"
#include <memory>
#include <opencv2/opencv.hpp>
 
namespace dataflow {
 
/**
 * @brief ZED 相机类,实现 CameraInterface 接口
 */
class ZedCamera : public CameraInterface {
public:
    /**
     * @brief 构造函数
     * @param serial_number ZED 相机的序列号,如果为空则使用第一个可用的相机
     * @param resolution 分辨率
     * @param fps 帧率
     * @param dst_width 目标宽度
     * @param dst_height 目标高度
     */
    ZedCamera(const std::string& serial_number = "",
              sl_oc::video::RESOLUTION resolution = sl_oc::video::RESOLUTION::HD720,
              int fps = 30,
              int dst_width = 0,
              int dst_height = 0);

编译时会出现如下的错误:

/workspace/nova_dataflow/dataflow/utils/zed_camera.h:24:22: error: 'sl_oc::video' has not been declared
   24 |               sl_oc::video::RESOLUTION resolution = sl_oc::video::RESOLUTION::HD720,
      |                      ^~~~~
/workspace/nova_dataflow/dataflow/utils/zed_camera.h:54:28: error: 'video' is not a member of 'sl_oc'
   54 |     std::unique_ptr<sl_oc::video::VideoCapture> cap_;
      |                            ^~~~~
/workspace/nova_dataflow/dataflow/utils/zed_camera.h:54:47: error: template argument 1 is invalid
   54 |     std::unique_ptr<sl_oc::video::VideoCapture> cap_;

初步分析

可以肯定的是,我确实正确 include 了 videocapture.hpp 文件(部分代码如下):

 
#ifndef VIDEOCAPTURE_HPP
#define VIDEOCAPTURE_HPP
 
#include "defines.hpp"
#include <thread>
#include <mutex>
#include <fstream>      // std::ofstream
#include <iomanip>
 
#define LOG_SEP ","
 
#ifdef VIDEO_MOD_AVAILABLE
 
#include "videocapture_def.hpp"
 
namespace sl_oc {
 
 
 
#ifdef SENSORS_MOD_AVAILABLE
namespace sensors {
class SensorCapture;
}
#endif
 
namespace video {
 
/*!
 * \brief The Frame struct containing the acquired video frames
 */
struct SL_OC_EXPORT Frame
{
    uint64_t frame_id = 0;          //!< Increasing index of frames
    uint64_t timestamp = 0;         //!< Timestamp in nanoseconds
    uint8_t* data = nullptr;        //!< Frame data in YUV 4:2:2 format
    uint16_t width = 0;             //!< Frame width
    uint16_t height = 0;            //!< Frame height
    uint8_t channels = 0;           //!< Number of channels per pixel
};

唯一值得怀疑的是 VIDEO_MOD_AVAILABLE 这个定义,但是编译随便找一个相关的文件 flags.make 中可以看到:

# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.16
 
# compile CXX with /usr/bin/aarch64-linux-gnu-g++
CXX_FLAGS = -no-pie -fopenmp -O2 -s -flto -fPIE   -O3 -g -fno-omit-frame-pointer -Wall -Wextra -Wpedantic -Werror=return-type -fPIC -mtune=cortex-a72 -mcpu=cortex-a72 -std=gnu++2a
 
CXX_DEFINES = -DEMBEDDED_ARM -DVIDEO_MOD_AVAILABLE
 
CXX_INCLUDES = -I/workspace/nova_dataflow/third-party/zed-open-capture/include -I/workspace/nova_dataflow/third-party/zed-open-capture/examples/include -isystem /rootfs/usr/include/opencv4 

明显是有 -DVIDEO_MOD_AVAILABLE 的,那问题到底出现在哪里呢?

原因

为了描述方便,将被依赖的库称为 sub,将依赖它的项目成为 top。

当您编译 top 库时,编译器可能没有看到 VIDEO_MOD_AVAILABLE 宏的定义,因此ideocapture.hpp 文件中的条件编译指令 #ifdef VIDEO_MOD_AVAILABLE 没有被执行,导致 sl_oc::video 命名空间和相关的类型没有被定义。

也就是 sub 库中添加的宏定义

add_definitions(-DVIDEO_MOD_AVAILABLE)

并不会传递到 top 中。

自然解决办法就有两个:

解决办法

方案一:top增加宏定义

top 项目的 CMakeLists.txt

add_definitions(-DVIDEO_MOD_AVAILABLE)

方案二:top 的文件处理这个宏定义

#include "dataflow/utils/zed_camera.h"
#include <stdexcept>
 
namespace dataflow {
 
#ifdef VIDEO_MOD_AVAILABLE
 
ZedCamera::ZedCamera(const std::string& serial_number,
                     int resolution,
                     int fps,
                     int dst_width,
                     int dst_height)
    : dst_width_(dst_width), dst_height_(dst_height) {
 
    // 获取相机参数
    sl_oc::video::VideoParams params;
    params.res = resolution;
    params.fps = static_cast<sl_oc::video::FPS>(fps);
 
    // 初始化视频捕获
    cap_ = std::make_unique<sl_oc::video::VideoCapture>(params);
    if (!cap_->initializeVideo()) {
        throw std::runtime_error("Failed to initialize ZED camera");
    }
 
    // 获取图像尺寸
    cap_->getFrameSize(width_, height_);
 
    // 如果目标尺寸为0,则使用原始尺寸
    if (dst_width_ == 0) {
        dst_width_ = width_;
    }
    if (dst_height_ == 0) {
        dst_height_ = height_;
    }
}
 
cv::Mat ZedCamera::GetImage() const {
    const sl_oc::video::Frame frame = cap_->getLastFrame();
    if (frame.data == nullptr) {
        return cv::Mat();
    }
 
    // 将 YUV 格式转换为 BGR 格式
    cv::Mat yuv(height_, width_, CV_8UC2, frame.data);
    cv::Mat bgr;
    cv::cvtColor(yuv, bgr, cv::COLOR_YUV2BGR_YUYV);
 
    return bgr;
}
 
std::vector<cv::Mat> ZedCamera::GetImages() const {
    std::vector<cv::Mat> images;
    images.push_back(GetImage());
    return images;
}
 
cv::Mat ZedCamera::GetResizedImage() const {
    cv::Mat image = GetImage();
    if (image.empty()) {
        return cv::Mat();
    }
 
    cv::Mat resized;
    cv::resize(image, resized, cv::Size(dst_width_, dst_height_));
    return resized;
}
 
std::vector<cv::Mat> ZedCamera::GetResizedImages() const {
    std::vector<cv::Mat> images;
    images.push_back(GetResizedImage());
    return images;
}
 
#else // VIDEO_MOD_AVAILABLE
 
ZedCamera::ZedCamera(const std::string& serial_number,
                     int resolution,
                     int fps,
                     int dst_width,
                     int dst_height) {
    throw std::runtime_error("ZED 相机模块未启用");
}
 
cv::Mat ZedCamera::GetImage() const {
    throw std::runtime_error("ZED 相机模块未启用");
}
 
std::vector<cv::Mat> ZedCamera::GetImages() const {
    throw std::runtime_error("ZED 相机模块未启用");
}
 
cv::Mat ZedCamera::GetResizedImage() const {
    throw std::runtime_error("ZED 相机模块未启用");
}
 
std::vector<cv::Mat> ZedCamera::GetResizedImages() const {
    throw std::runtime_error("ZED 相机模块未启用");
}
 
#endif // VIDEO_MOD_AVAILABLE
 
} // namespace dataflow