从0到1搭建一套属于你自己的高精度实时结构光3D相机(3):相机控制(C/C++)

1. 写在前面

在前面的博客中,介绍了有关结构光3D相机的硬件搭建。在这篇博客中,博主将教给大家如何通过 C/C++ 控制我们的2D相机。完成本篇博客的学习内容后,你将收获相机的SDK 使用经验。

本系列博客的完整项目代码皆位于博主的Github项目SLMaster👈

https://github.com/Practice3DVision/SLMaster

动动你的小指头给个Star⭐并follow博主吧!你的支持是博主不懈的动力!

2. 相机C/C++控制

博主采用的是华睿相机SDK,海康相机的代码框架与之类似,正所谓万变不离其宗嘛(感兴趣的同学可以尝试使用华睿相机SDK控制海康相机,博主曾经发现它两是都能用这套SDK的)。

在编写代码之前,我们首先得明白我们有什么业务,业务逻辑又是什么。

“相机嘛,很简单,能拍照控制各种参数就行了。”,可别说,还真是!不过为了让我们的代码能够实现高内聚低耦合,我们需要一个虚基类去定义各类接口,然后分别针对华睿相机、海康相机或其它品牌相机实现接口继承。这样的话即使我们在不同的项目中使用了不同的相机,上层代码(就是软件啦)的业务逻辑也是不变的,我们只需要扩展底层相机代码即可。

话说的似乎有点多了,我们直接用UML图来说明一下吧!

相机UML
多看几遍有没有更明白呢?

我们用代码来实现一下吧!

首先是我们定义一些宏用于控制静态库或者动态库的生成,并定义一个线程安全队列类SafeQueue(当然是直接Github搜一搜就有啦,自己知道原理就好了),这个类将用于后续图片在线程安全下的存入和读取。

//typeDef.h
#ifndef IN
#define IN //输入
#endif

#ifndef OUT
#define OUT //输出
#endif

# ifdef BUILD_SHARED_LIBS
#   ifdef _WIN32
#       ifdef DLL_EXPORTS
#           define DEVICE_API _declspec(dllexport)
#       else
#           define DEVICE_API _declspec(dllimport)
#       endif
#   else
#       define DEVICE_API
#   endif
# else
#   define DEVICE_API
# endif
/*
 * SafeQueue.hpp
 * Copyright (C) 2019 Alfredo Pons Menargues <apons@linucleus.com>
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef SAFEQUEUE_HPP_
#define SAFEQUEUE_HPP_

#include <queue>
#include <list>
#include <mutex>
#include <thread>
#include <cstdint>
#include <condition_variable>
 
 
 
/** A thread-safe asynchronous queue */
template <class T, class Container = std::list<T>>
class SafeQueue
{
 
    typedef typename Container::value_type value_type;
    typedef typename Container::size_type size_type;
    typedef Container container_type;
 
  public:
 
    /*! Create safe queue. */
    SafeQueue() = default;
    SafeQueue (SafeQueue&& sq)
    {
      m_queue = std::move (sq.m_queue);
    }
    SafeQueue (const SafeQueue& sq)
    {
      std::lock_guard<std::mutex> lock (sq.m_mutex);
      m_queue = sq.m_queue;
    }
 
    /*! Destroy safe queue. */
    ~SafeQueue()
    {
      std::lock_guard<std::mutex> lock (m_mutex);
    }
 
    /**
     * Sets the maximum number of items in the queue. Defaults is 0: No limit
     * \param[in] item An item.
     */
    void set_max_num_items (unsigned int max_num_items)
    {
      m_max_num_items = max_num_items;
    }
 
    /**
     *  Pushes the item into the queue.
     * \param[in] item An item.
     * \return true if an item was pushed into the queue
     */
    bool push (const value_type& item)
    {
      std::lock_guard<std::mutex> lock (m_mutex);
 
      if (m_max_num_items > 0 && m_queue.size() > m_max_num_items)
        return false;
 
      m_queue.push (item);
      m_condition.notify_one();
      return true;
    }
 
    /**
     *  Pushes the item into the queue.
     * \param[in] item An item.
     * \return true if an item was pushed into the queue
     */
    bool push (const value_type&& item)
    {
      std::lock_guard<std::mutex> lock (m_mutex);
 
      if (m_max_num_items > 0 && m_queue.size() > m_max_num_items)
        return false;
 
      m_queue.push (item);
      m_condition.notify_one();
      return true;
    }

    value_type& back() {
      std::unique_lock<std::mutex> lock (m_mutex);

      return m_queue.back();
    }
 
    /**
     *  Pops item from the queue.
     * \param[out] item The item.
     */
    void pop (value_type& item)
    {
      std::unique_lock<std::mutex> lock (m_mutex);
      item = m_queue.front();
      m_queue.pop();
    }
 
    /**
     *  Pops item from the queue using the contained type's move assignment operator, if it has one..
     *  This method is identical to the pop() method if that type has no move assignment operator.
     *
     * \param[out] item The item.
     */
    void move_pop (value_type& item)
    {
      std::unique_lock<std::mutex> lock (m_mutex);
      item = std::move (m_queue.front());
      m_queue.pop();
    }
 
    /**
     *  Tries to pop item from the queue.
     * \param[out] item The item.
     * \return False is returned if no item is available.
     */
    bool try_pop (value_type& item)
    {
      std::unique_lock<std::mutex> lock (m_mutex);
 
      if (m_queue.empty())
        return false;
 
      item = m_queue.front();
      m_queue.pop();
      return true;
    }
 
    /**
     *  Tries to pop item from the queue using the contained type's move assignment operator, if it has one..
     *  This method is identical to the try_pop() method if that type has no move assignment operator.
     * \param[out] item The item.
     * \return False is returned if no item is available.
     */
    bool try_move_pop (value_type& item)
    {
      std::unique_lock<std::mutex> lock (m_mutex);
 
      if (m_queue.empty())
        return false;
 
      item = std::move (m_queue.front());
      m_queue.pop();
      return true;
    }
 
    /**
     *  Pops item from the queue. If the queue is empty, blocks for timeout microseconds, or until item becomes available.
     * \param[out] t An item.
     * \param[in] timeout The number of microseconds to wait.
     * \return true if get an item from the queue, false if no item is received before the timeout.
     */
    bool timeout_pop (value_type& item, std::uint64_t timeout)
    {
      std::unique_lock<std::mutex> lock (m_mutex);
 
      if (m_queue.empty())
        {
          if (timeout == 0)
            return false;
 
          if (m_condition.wait_for (lock, std::chrono::microseconds (timeout)) == std::cv_status::timeout)
            return false;
        }
 
      item = m_queue.front();
      m_queue.pop();
      return true;
    }
 
    /**
     *  Pops item from the queue using the contained type's move assignment operator, if it has one..
     *  If the queue is empty, blocks for timeout microseconds, or until item becomes available.
     *  This method is identical to the try_pop() method if that type has no move assignment operator.
     * \param[out] t An item.
     * \param[in] timeout The number of microseconds to wait.
     * \return true if get an item from the queue, false if no item is received before the timeout.
     */
    bool timeout_move_pop (value_type& item, std::uint64_t timeout)
    {
      std::unique_lock<std::mutex> lock (m_mutex);
 
      if (m_queue.empty())
        {
          if (timeout == 0)
            return false;
 
          if (m_condition.wait_for (lock, std::chrono::microseconds (timeout)) == std::cv_status::timeout)
            return false;
        }
 
      item = std::move (m_queue.front());
      m_queue.pop();
      return true;
    }
 
    /**
     *  Gets the number of items in the queue.
     * \return Number of items in the queue.
     */
    size_type size() const
    {
      std::lock_guard<std::mutex> lock (m_mutex);
      return m_queue.size();
    }
 
    /**
     *  Check if the queue is empty.
     * \return true if queue is empty.
     */
    bool empty() const
    {
      std::lock_guard<std::mutex> lock (m_mutex);
      return m_queue.empty();
    }
 
    /**
     *  Swaps the contents.
     * \param[out] sq The SafeQueue to swap with 'this'.
     */
    void swap (SafeQueue& sq)
    {
      if (this != &sq)
        {
          std::lock_guard<std::mutex> lock1 (m_mutex);
          std::lock_guard<std::mutex> lock2 (sq.m_mutex);
          m_queue.swap (sq.m_queue);
 
          if (!m_queue.empty())
            m_condition.notify_all();
 
          if (!sq.m_queue.empty())
            sq.m_condition.notify_all();
        }
    }
 
    /*! The copy assignment operator */
    SafeQueue& operator= (const SafeQueue& sq)
    {
      if (this != &sq)
        {
          std::lock_guard<std::mutex> lock1 (m_mutex);
          std::lock_guard<std::mutex> lock2 (sq.m_mutex);
          std::queue<T, Container> temp {sq.m_queue};
          m_queue.swap (temp);
 
          if (!m_queue.empty())
            m_condition.notify_all();
        }
 
      return *this;
    }
 
    /*! The move assignment operator */
    SafeQueue& operator= (SafeQueue && sq)
    {
      std::lock_guard<std::mutex> lock (m_mutex);
      m_queue = std::move (sq.m_queue);
 
      if (!m_queue.empty())  m_condition.notify_all();
 
      return *this;
    }
 
 
  private:
 
    std::queue<T, Container> m_queue;
    mutable std::mutex m_mutex;
    std::condition_variable m_condition;
    unsigned int m_max_num_items = 0;
};
 
/*! Swaps the contents of two SafeQueue objects. */
template <class T, class Container>
void swap (SafeQueue<T, Container>& q1, SafeQueue<T, Container>& q2)
{
  q1.swap (q2);
}
#endif /* SAFEQUEUE_HPP_ */

接着我们就可以开始着手实现我们上面UML图的业务逻辑了。首先是实现一个纯虚基类Camera,它定义了我们相机所有的业务(接口方法)。

#ifndef __CAMERA_H_
#define __CAMERA_H_

#include <string>
#include <queue>

#include <opencv2/opencv.hpp>

#include "typeDef.h"
#include "safeQueue.hpp"

/** @brief 结构光库 **/
namespace device {
    /** @brief 相机库 **/
    namespace camera {
        /** @brief 相机信息 **/
        struct DEVICE_API CameraInfo {
            std::string cameraKey_;      // 相机序列号
            std::string cameraUserId_;   // 相机用户名
            std::string deviceType_;     // 相机传输数据类型
            bool isFind_;
        };

        /** @brief 触发方式 **/
        enum TrigMode {
            trigContinous = 0, // 连续拉流
            trigSoftware = 1,  // 软件触发
            trigLine = 2,      // 外部触发
        };

        /** @brief 相机控制类 **/
        class DEVICE_API Camera {
            public:
                /**
                 * @brief 析构函数
                 * 
                 */
                virtual ~Camera() { }
                /**
                 * @brief 获取相机信息
                 * 
                 * @return CameraInfo 相机信息
                 */
                virtual CameraInfo getCameraInfo() = 0;
                /**
                 * @brief 连接相机
                 * 
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool connect() = 0;
                /**
                 * @brief 断开相机
                 * 
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool disConnect() = 0;
                /**
                 * @brief 获取已捕获图片
                 * 
                 * @return SafeQueue<cv::Mat>& 捕获的图片
                 */
                virtual SafeQueue<cv::Mat>& getImgs() = 0;
                /**
                 * @brief 存入图片
                 * 
                 * @param img 图片
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool pushImg(IN const cv::Mat& img) = 0;
                /**
                 * @brief 丢弃最早的一张图片
                 * 
                 * @return cv::Mat 被丢弃的图片
                 */
                virtual cv::Mat popImg() = 0;
                /**
                 * @brief 清空所有捕获的图片
                 * 
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool clearImgs() = 0;
                /**
                 * @brief 是否已连接
                 * 
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool isConnect() = 0;
                /**
                 * @brief 捕获一帧图片
                 * 
                 * @return cv::Mat 捕获到的图片
                 */
                virtual cv::Mat capture() = 0;
                /**
                 * @brief 打开相机并持续捕获
                 * 
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool start() = 0;
                /**
                 * @brief 暂停相机持续捕获
                 * 
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool pause() = 0;
                /**
                 * @brief 设置触发模式
                 * 
                 * @param mode 触发模式
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool setTrigMode(IN TrigMode mode) = 0;
                /**
                 * @brief 设置枚举属性值
                 * 
                 * @param attributeName 枚举属性名称
                 * @param val 需要设置的值
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool setEnumAttribute(IN const std::string attributeName, IN const std::string val) = 0;
                /**
                 * @brief 设置字符属性值
                 * 
                 * @param attributeName 字符属性名称
                 * @param val 需要设置的值
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool setStringAttribute(IN const std::string attributeName, IN const std::string val) = 0;
                /**
                 * @brief 设置数字属性值
                 * 
                 * @param attributeName 数字属性名称
                 * @param val 需要设置的值
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool setNumberAttribute(IN const std::string attributeName, IN const double val) = 0;
                /**
                 * @brief 设置布尔属性值
                 * 
                 * @param attributeName 布尔属性名称
                 * @param val 需要设置的值
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool setBooleanAttribute(IN const std::string attributeName, IN const bool val) = 0;
                /**
                 * @brief 获取枚举属性值
                 * 
                 * @param attributeName 枚举属性名称
                 * @param val 当前枚举量
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool getEnumAttribute(IN const std::string attributeName, OUT std::string& val) = 0;
                /**
                 * @brief 获取字符属性值
                 * 
                 * @param attributeName 字符属性名称
                 * @param val 当前字符属性值
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool getStringAttribute(IN const std::string attributeName, OUT std::string& val) = 0;
                /**
                 * @brief 获取数字属性值
                 * 
                 * @param attributeName 数字属性名称
                 * @param val 当前数字属性值
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool getNumbericalAttribute(IN const std::string attributeName, OUT double& val) = 0;
                /**
                 * @brief 获取布尔属性值
                 * 
                 * @param attributeName 布尔属性名称
                 * @param val 当前布尔属性值
                 * @return true 成功
                 * @return false 失败
                 */
                virtual bool getBooleanAttribute(IN const std::string attributeName, OUT bool& val) = 0;
                /**
                 * @brief getFps 获取帧率
                 * @return 帧率
                 */
                virtual int getFps() = 0;
            private:
        };
    }
}

#endif //__CAMERA_H_

紧接着,我们就可以创建华睿相机类HuarayCamera并继承上述Camera类,在HuarayCamera类中具体实现上面Camera类的接口方法。(需要用到华睿相机的SDK,包括IMVApi.hIMVDefines.h,以及MVSDKmd.lib,上述文件在安装MV Viewer安装文件夹下的Development文件夹内)

/**
 * @file huarayCamera.h
 * @author Evans Liu(1369215984@qq.com)
 * @brief  相机工具类:值得一提的是大华和海康相机皆可使用,采用同一标准。
 * @version 0.1
 * @date 2021-12-10
 *
 * @copyright Copyright (c) 2021
 *
 */
#ifndef __HUARAY_CAMERA_H_
#define __HUARAY_CAMERA_H_

#include "IMVApi.h"
#include "camera.h"
#include "safeQueue.hpp"

#include <opencv2/opencv.hpp>

/** @brief 相机库 */
namespace device {
/** @brief 设备控制库 */
namespace camera {
/** @brief 华睿相机控制类 **/
class DEVICE_API HuarayCammera : public Camera{
  public:
    explicit HuarayCammera(IN const std::string cameraUserId);
    ~HuarayCammera();
    CameraInfo getCameraInfo() override;
    bool connect() override;
    bool disConnect() override;
    SafeQueue<cv::Mat> &getImgs() override;
    bool pushImg(IN const cv::Mat &img) override;
    cv::Mat popImg() override;
    bool clearImgs() override;
    bool isConnect() override;
    cv::Mat capture() override;
    bool start() override;
    bool pause() override;
    bool setTrigMode(IN const TrigMode trigMode) override;
    bool setEnumAttribute(IN const std::string attributeName,
                          IN const std::string val) override;
    bool setStringAttribute(IN const std::string attributeName,
                            IN const std::string val) override;
    bool setNumberAttribute(IN const std::string attributeName,
                            IN const double val) override;
    bool setBooleanAttribute(IN const std::string attributeName,
                             IN const bool val) override;
    bool getEnumAttribute(IN const std::string attributeName,
                          OUT std::string &val) override;
    bool getStringAttribute(IN const std::string attributeName,
                            OUT std::string &val) override;
    bool getNumbericalAttribute(IN const std::string attributeName,
                                OUT double &val) override;
    bool getBooleanAttribute(IN const std::string attributeName,
                             OUT bool &val) override;
    int getFps() override;
    IMV_HANDLE* getHandle() { return pCamera_; }
  private:
    //相机ID
    const std::string cameraUserId_;
    //相机指针
    IMV_HANDLE *pCamera_;
    //相机捕获到的图片
    SafeQueue<cv::Mat> imgs_;
};
} // namespace camera
} // namespace device
#endif // __HUARAY_CAMERA_H_

//huarayCamera.cpp

#include "huarayCamera.h"

#include <chrono>

namespace device {
namespace camera {

//相机取流回调函数
static void frameCallback(IMV_Frame *pFrame, void *pUser) {
    HuarayCammera *pCamera = reinterpret_cast<HuarayCammera *>(pUser);
    if (pFrame->pData != NULL) {
        //TODO@Evans Liu:增加各种格式的pack支持

        cv::Mat img;
        if(_IMV_EPixelType::gvspPixelMono8 == pFrame->frameInfo.pixelFormat) {
            img = cv::Mat(pFrame->frameInfo.height, pFrame->frameInfo.width, CV_8U, pFrame->pData).clone();
        }
        else if(_IMV_EPixelType::gvspPixelBayRG8 == pFrame->frameInfo.pixelFormat) {
            //TODO@Evans Liu:如果直接在这进行转码,将导致延迟
            img = cv::Mat(pFrame->frameInfo.height, pFrame->frameInfo.width, CV_8U, pFrame->pData).clone();
        }
        else if(_IMV_EPixelType::gvspPixelMono16 == pFrame->frameInfo.pixelFormat) {
            img = cv::Mat(pFrame->frameInfo.height, pFrame->frameInfo.width,
                CV_16U, pFrame->pData)
                .clone();
        }

        pCamera->getImgs().push(std::move(img));
    }
}

HuarayCammera::HuarayCammera(const std::string cameraUserId)
    : cameraUserId_(cameraUserId), pCamera_(nullptr) {}

HuarayCammera::~HuarayCammera() {}

CameraInfo HuarayCammera::getCameraInfo() {
    CameraInfo info;
    info.isFind_ = false;
    IMV_DeviceList deviceList;
    IMV_EnumDevices(&deviceList, IMV_EInterfaceType::interfaceTypeAll);
    for (size_t i = 0; i < deviceList.nDevNum; ++i) {
        if(cameraUserId_ == deviceList.pDevInfo[i].cameraName) {
            info.isFind_ = true;
            info.cameraKey_ = deviceList.pDevInfo[i].cameraKey;
            info.cameraUserId_ = deviceList.pDevInfo[i].cameraName;
            info.deviceType_ = deviceList.pDevInfo[i].nInterfaceType;
        }
    }
    return info;
}

bool HuarayCammera::connect() {
    auto ret =
        IMV_CreateHandle((void**)&pCamera_, IMV_ECreateHandleMode::modeByDeviceUserID,
                        (void*)cameraUserId_.data());

    if (IMV_OK != ret) {
        printf("create devHandle failed! userId[%s], ErrorCode[%d]\n",
               cameraUserId_.data(), ret);
        return false;
    }

    ret = IMV_Open(pCamera_);
    if (IMV_OK != ret) {
        printf("open camera failed! ErrorCode[%d]\n", ret);
        return false;
    }

    return true;
}

bool HuarayCammera::disConnect() {
    if (!pCamera_) {
        printf("close camera fail. No camera.\n");
        return false;
    }

    if (false == IMV_IsOpen(pCamera_)) {
        printf("camera is already close.\n");
        return false;
    }

    auto ret = IMV_Close(pCamera_);
    if (IMV_OK != ret) {
        printf("close camera failed! ErrorCode[%d]\n", ret);
        return false;
    }

    ret = IMV_DestroyHandle(pCamera_);
    if (IMV_OK != ret) {
        printf("destroy devHandle failed! ErrorCode[%d]\n", ret);
        return false;
    }

    pCamera_ = nullptr;

    return true;
}

SafeQueue<cv::Mat>& HuarayCammera::getImgs() {
    return imgs_;
}

bool HuarayCammera::pushImg(const cv::Mat &img) {
    imgs_.push(std::move(img));

    return true;
}

cv::Mat HuarayCammera::popImg() {
    cv::Mat img;
    bool isSucess = imgs_.try_move_pop(img);

    //std::cout << cameraUserId_ << " Size: " << imgs_.size() << " fps:  " << getFps() << " is sucess: " << isSucess << std::endl;

    return img;
}

bool HuarayCammera::clearImgs() {
    SafeQueue<cv::Mat> emptyQueue;
    imgs_.swap(emptyQueue);

    return true;
}

bool HuarayCammera::isConnect() { return IMV_IsOpen(pCamera_); }

cv::Mat HuarayCammera::capture() {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return cv::Mat();
    }

    const int preNums = imgs_.size();

    setTrigMode(TrigMode::trigSoftware);

    auto ret = IMV_ExecuteCommandFeature(pCamera_, "TriggerSoftware");
    if (IMV_OK != ret) {
        printf("ExecuteSoftTrig fail, ErrorCode[%d]\n", ret);
        return cv::Mat();
    }

    double exposureTime;
    getNumbericalAttribute("ExposureTime", exposureTime);
    auto timeBegin = std::chrono::system_clock::now();
    while(preNums == imgs_.size()) {
        auto timeEnd = std::chrono::system_clock::now();
        auto timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(timeEnd - timeBegin).count()* (double)std::chrono::milliseconds::period::num / std::chrono::milliseconds::period::den;
        if(timeElapsed > (exposureTime / 1000000.0 * 2))  {
            break;
        }
    };

    cv::Mat softWareCapturedImg = imgs_.back();

    setTrigMode(TrigMode::trigLine);

    return softWareCapturedImg;
}

bool HuarayCammera::start() {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    if (IMV_IsGrabbing(pCamera_)) {
        printf("camera is already grebbing.\n");
        return false;
    }

    auto ret = IMV_AttachGrabbing(pCamera_, frameCallback, this);

    if (IMV_OK != ret) {
        printf("Attach grabbing failed! ErrorCode[%d]\n", ret);
        return false;
    }

    ret = IMV_StartGrabbing(pCamera_);
    if (IMV_OK != ret) {
        printf("start grabbing failed! ErrorCode[%d]\n", ret);
        return false;
    }

    return true;
}

bool HuarayCammera::pause() {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    if (!IMV_IsGrabbing(pCamera_)) {
        printf("camera is already stop grubbing.\n");
        return false;
    }

    auto ret = IMV_StopGrabbing(pCamera_);
    if (IMV_OK != ret) {
        printf("Stop grubbing failed! ErrorCode[%d]\n", ret);
        return false;
    }

    return true;
}

bool HuarayCammera::setTrigMode(const TrigMode trigMode) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    int ret = IMV_OK;
    if (trigContinous == trigMode) {
        ret = IMV_SetEnumFeatureSymbol(pCamera_, "TriggerMode", "Off");
        if (IMV_OK != ret) {
            printf("set TriggerMode value = Off fail, ErrorCode[%d]\n", ret);
            return false;
        }
    } else if (trigSoftware == trigMode) {
        ret = IMV_SetEnumFeatureSymbol(pCamera_, "TriggerMode", "On");
        if (IMV_OK != ret) {
            printf("set TriggerMode value = On fail, ErrorCode[%d]\n", ret);
            return false;
        }

        ret = IMV_SetEnumFeatureSymbol(pCamera_, "TriggerSource", "Software");
        if (IMV_OK != ret) {
            printf("set TriggerSource value = Software fail, ErrorCode[%d]\n",
                   ret);
            return false;
        }
    } else if (trigLine == trigMode) {
        ret = IMV_SetEnumFeatureSymbol(pCamera_, "TriggerMode", "On");
        if (IMV_OK != ret) {
            printf("set TriggerMode value = On fail, ErrorCode[%d]\n", ret);
            return false;
        }

        ret = IMV_SetEnumFeatureSymbol(pCamera_, "TriggerSource", "Line2");
        if (IMV_OK != ret) {
            printf("set TriggerSource value = Line1 fail, ErrorCode[%d]\n",
                   ret);
            return false;
        }
    }
    return true;
}

bool HuarayCammera::setEnumAttribute(const std::string attributeName,
                                     const std::string val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    return IMV_SetEnumFeatureSymbol(pCamera_, attributeName.data(),
                                    val.data()) == IMV_OK;
}

bool HuarayCammera::setStringAttribute(const std::string attributeName,
                                       const std::string val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    return IMV_SetStringFeatureValue(pCamera_, attributeName.data(),
                                     val.data()) == IMV_OK;
}

bool HuarayCammera::setNumberAttribute(const std::string attributeName,
                                           const double val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    return IMV_SetDoubleFeatureValue(pCamera_, attributeName.data(), val) == IMV_OK;
}

bool HuarayCammera::setBooleanAttribute(const std::string attributeName,
                                        const bool val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    return IMV_SetBoolFeatureValue(pCamera_, attributeName.data(), val) == IMV_OK;
}

int HuarayCammera::getFps() {
    IMV_StreamStatisticsInfo info;
    IMV_GetStatisticsInfo(pCamera_, &info);

    return info.u3vStatisticsInfo.fps;
}

bool HuarayCammera::getEnumAttribute(const std::string attributeName,
                                     std::string &val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    IMV_String data;
    IMV_GetEnumFeatureSymbol(pCamera_, attributeName.data(), &data);
    val = data.str;

    return true;
}

bool HuarayCammera::getStringAttribute(const std::string attributeName,
                                       std::string &val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    IMV_String data;
    IMV_GetStringFeatureValue(pCamera_, attributeName.data(), &data);
    val = data.str;

    return true;
}

bool HuarayCammera::getNumbericalAttribute(const std::string attributeName,
                                           double &val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    IMV_GetDoubleFeatureValue(pCamera_, attributeName.data(), &val);

    return true;
}

bool HuarayCammera::getBooleanAttribute(const std::string attributeName,
                                        bool &val) {
    if (!pCamera_) {
        printf("Error, camera dosn't open! \n");
        return false;
    }

    IMV_GetBoolFeatureValue(pCamera_, attributeName.data(), &val);

    return true;
}
} // namespace camera
} // namespace device

最后,我们定义一个相机工厂类,通过该类方便得获取到我们的相机指针Camera*。

//cameraFactory.h

#ifndef __CAMERA_FACTORY_H_
#define __CAMERA_FACTORY_H_

#include <string>
#include <unordered_map>

#include "huarayCamera.h"

/** @brief 结构光库 **/
namespace device {
/** @brief 相机库 **/
namespace camera {
/** @brief 相机工厂 **/
class DEVICE_API CameraFactory {
  public:
    CameraFactory() { };
    /**@brief 制造商*/
    enum CameraManufactor {
        Huaray = 0, //华睿科技
        Halcon      //海康机器人
    };

    Camera *getCamera(std::string cameraUserId,
                            CameraManufactor manufactor) {
        Camera *camera = nullptr;

        if (cameras_.count(cameraUserId)) {
            return cameras_[cameraUserId];
        } else {
            if (Huaray == manufactor) {
                camera = new HuarayCammera(cameraUserId);
                cameras_[cameraUserId] = camera;
            }
            // TODO@LiuYunhuang:增加海康相机支持
            else if (Halcon == manufactor) {
                camera = new HuarayCammera(cameraUserId);
                cameras_[cameraUserId] = camera;
            }
        }

        return camera;
    }
  private:
    std::unordered_map<std::string, Camera *> cameras_;
}; // class CameraFactory
} // namespace camera
} // namespace sl

#endif //__CAMERA_FACTORY_H_

大功告成了!接下来的问题是,我们则么去测试我们写的代码对不对呢?博主推荐大家养成写Precise Test(准确度测试)的习惯,也就是单元测试,博主喜欢使用google_test框架进行单元测试。写单元测试的好处是,可以测试自己的代码是否正确,更为重要的是在你每次改动之后执行一次单元测试,能够发现有无因为这一次改动而导致的其他bug

在这里给出博主写的单元测试文件。

//testHuarayCamera.cpp

#include "cameraFactory.h"

#include <gtest/gtest.h>
/*
TEST(HuarayCameraLib, getCamera) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    ASSERT_NE(pCamera, nullptr);
}

TEST(HuarayCameraLib, getCameraInfo) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    auto info = pCamera->getCameraInfo();
    ASSERT_EQ(info.isFind_, true);
}

TEST(HuarayCameraLib, connect) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    bool isSucess = pCamera->connect();
    ASSERT_EQ(isSucess, true);
    pCamera->disConnect();
}

TEST(HuarayCameraLib, disConnect) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    bool isSucess = pCamera->connect();
    isSucess = pCamera->start();
    isSucess = pCamera->disConnect();
    ASSERT_EQ(pCamera->isConnect(), false);
}

TEST(HuarayCameraLib, start) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    bool isSucess = pCamera->connect();
    isSucess = pCamera->start();
    ASSERT_EQ(isSucess, true);
    pCamera->disConnect();
}

TEST(HuarayCameraLib, capture) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    bool isSucess = pCamera->connect();
    isSucess = pCamera->start();
    cv::Mat img = pCamera->capture();
    ASSERT_NE(img.rows, 0);
    pCamera->disConnect();
}

TEST(HuarayCameraLib, popImg) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    bool isSucess = pCamera->connect();
    isSucess = pCamera->start();
    cv::Mat img = pCamera->capture();
    cv::Mat popImg = pCamera->popImg();
    ASSERT_EQ(pCamera->getImgs().size(), 0);
    pCamera->disConnect();
}
*/
TEST(HuarayCameraLib, setNumberAttribute) {
    auto pCameraFactory = new device::camera::CameraFactory();
    device::camera::Camera* pCamera = pCameraFactory->getCamera("Left", device::camera::CameraFactory::CameraManufactor::Huaray);
    bool isSucess = pCamera->connect();
    pCamera->setTrigMode(device::camera::trigLine);
    pCamera->start();
    isSucess = pCamera->setNumberAttribute("ExposureTime", 4000);
    ASSERT_EQ(isSucess, true);
    pCamera->disConnect();
}

终于大功告成了!你只要愿意写,慢慢的代码水平就上来了!

3. 总结

在这篇博客中,博主介绍了如何编写 C/C++ 用于控制我们的 2D相机 。在下一篇博客中,博主将介绍如何编写 C/C++ 用于控制我们的 投影仪

本系列文章将持续更新,如果有等不及想要获得代码的小伙伴,可访问博主Github中的SLMaster项目,动动你的小手指,follow and star⭐!你们的关注是博主持续的动力!

Github:https://github.com/Practice3DVision
QQ群:229441078

公众号:实战3D视觉

在这里插入图片描述

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值