1. 写在前面
在前面的博客中,介绍了有关结构光3D相机的硬件搭建。在这篇博客中,博主将教给大家如何通过 C/C++ 控制我们的2D相机。完成本篇博客的学习内容后,你将收获相机的SDK 使用经验。
本系列博客的完整项目代码皆位于博主的Github项目SLMaster👈中:
https://github.com/Practice3DVision/SLMaster
动动你的小指头给个Star⭐并follow博主吧!你的支持是博主不懈的动力!
2. 相机C/C++控制
博主采用的是华睿相机SDK,海康相机的代码框架与之类似,正所谓万变不离其宗嘛(感兴趣的同学可以尝试使用华睿相机SDK控制海康相机,博主曾经发现它两是都能用这套SDK的)。
在编写代码之前,我们首先得明白我们有什么业务,业务逻辑又是什么。
“相机嘛,很简单,能拍照控制各种参数就行了。”,可别说,还真是!不过为了让我们的代码能够实现高内聚低耦合,我们需要一个虚基类去定义各类接口,然后分别针对华睿相机、海康相机或其它品牌相机实现接口继承。这样的话即使我们在不同的项目中使用了不同的相机,上层代码(就是软件啦)的业务逻辑也是不变的,我们只需要扩展底层相机代码即可。
话说的似乎有点多了,我们直接用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.h和IMVDefines.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视觉