交了不少智商税之后终于解决了这个问题,为了以后少走弯路,把自己的程序记录下来供大家参考。
一:准备工作
相机:映美精
编译环境:VS2015+QT5.7.1
相机IP分配软件:GigECam IP Configuration
附加库:IC Imaging Control 3.4和OpenCV3.4.1
二:相机控制类简介
主要使用Grabber和FrameHandlerSink,其中Grabber的功能在前文已有解释,FrameHandlerSink用于从图像流中抓取(复制)帧的SinkType,包含一个MemBufferCollection,可选择允许用户指定一个或多个负责将图像数据复制到MemBufferCollection缓冲区的IFrameFilter,可以在复制的过程中执行用户定义的转换,也可以删除不需要的帧。
帧从一个接一个地复制到MemBufferCollection,从集合的第一个缓冲区开始。 将帧复制到最后一个缓冲区时,下一帧将复制到第一个缓冲区,依此类推。类似于堆栈的方式,所以在保存图片的时候,我们只需要得到最后一帧的缓存区数据即可。
头文件:tisudshl.h
命名空间:DShowLib
三:代码
QT界面设计:
四个按钮:LeftButton、RightButton、SaveLButton和SaveRButton
两个label:LeftLabel和RightLabel
label大小是我根据相机成像大小等比例缩放的,我用的相机分辨率是1280×960,label大小为320×240,你也可以设置为自适应大小。
设置包含库文件,包括相机和OpenCV,在QT.pro文件最后加入下面两行代码(将xxx改为你的电脑用户名):
#添加相机库文件
#$$quote关键字是为了加入带空格的路径
INCLUDEPATH += $$quote(C:\Users\asus\Documents\IC Imaging Control 3.4\classlib\include)
LIBS += $$quote(C:\Users\xxx\Documents\IC Imaging Control 3.4\classlib\x64\debug\TIS_UDSHL11d_x64.lib)
//添加OpenCV库
INCLUDEPATH += X:\xxx\opencv\build\include\opencv2 \
X:\xxx\opencv\build\include\opencv \
X:\xxx\opencv\build\include
LIBS += X:\xxx\opencv\build\x64\vc14\lib\opencv_world341d.lib \
为了方便以后其他程序的设计,这里我建立了一个相机类Camera,相机图像的显示和保存均在MyCamera.cpp中完成。
mycamera.h中主要构建了一个相机类,在MyWidget.h中使用该类定义了两个相机对象LeftCamera和RightCamera,具体如下:
#ifndef MYCAMERA_H
#define MYCAMERA_H
#include<tisudshl.h>
#include<opencv2/opencv.hpp>
#include <QLabel>
#include<string>
using namespace std;
class Camera//相机构造类
{
public:
Camera();
~Camera();
void cameraONOFF(); //相机开关
bool getCameraHWND(QLabel *windowName); //获取窗口句柄
void SaveImage(string str); //保存图像
void CreateSaveDir(); //创建文件保存路径
void setpHander(); //初始化操作
public:
DShowLib::Grabber *m_Grabber;
HWND appwnd; //窗口句柄
int m_hWnd,m_wWnd;
smart_ptr<DShowLib::FrameHandlerSink> pHanderSink;
cv::Size Imgsize; //图像的大小//记录相机保存的图片数量
bool m_IsOpen; //相机开启的标志位
};
#endif // MYCAMERA_H
MyWidget.h中定义了两个相机类的对象,四个槽函数均由按钮直接转到槽自动生成。
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include<mycamera.h>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
Camera LeftCamera,RightCamera;//左右相机
private slots:
void on_LeftButton_clicked();//左相机开关
void on_RightButton_clicked();//右相机开关
void on_SaveLButton_clicked();//保存左相机图像
void on_SaveRButton_clicked();//保存右相机图像
private:
Ui::MyWidget *ui;
};
#endif // MYWIDGET_H
main.cpp主程序保持内容不变,MyWidget.cpp主要用来设定保存路径,其他功能通过调用函数完成:
#include "MyWidget.h"
#include "ui_MyWidget.h"
#include <QMessageBox>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_LeftButton_clicked()
{
LeftCamera.getCameraHWND(ui->LeftLabel);
LeftCamera.cameraONOFF();
}
void MyWidget::on_RightButton_clicked()
{
RightCamera.getCameraHWND(ui->RightLabel);
RightCamera.cameraONOFF();
}
void MyWidget::on_SaveLButton_clicked()
{
// 记录采集图像的数量
static int leftCameraImages = 0;
++leftCameraImages;
string
//设置图像保存路径,这里的Save文件夹是在MyCamera.cpp中自动生成的,也可以根据需要自行修改
str=cv::format("F:/QT/CameraControl/Save/LeftCamera/%d.bmp",leftCameraImages);
if(LeftCamera.m_IsOpen)
{
LeftCamera.SaveImage(str);
string info=cv::format("左相机图像已保存%d张",leftCameraImages);
QString qStr=QString(QString::fromLocal8Bit(info.c_str()));//防止中文在转换过程中出现乱码问题
QMessageBox::about (NULL,"inform",qStr);
}
else
{
QMessageBox::about(NULL,"inform","open the camera first");
}
}
void MyWidget::on_SaveRButton_clicked()
{
// 记录采集图像的数量
static int rightCameraImages = 0;
++rightCameraImages;
string str=cv::format("F:/QT/CameraControl/Save/RightCamera/%d.bmp",rightCameraImages);
if(RightCamera.m_IsOpen)
{
RightCamera.SaveImage(str);
string info=cv::format("右相机图像已保存%d张",rightCameraImages);
QString qStr=QString(QString::fromLocal8Bit(info.c_str()));
QMessageBox::about(NULL,"inform",qStr);
}
else
{
QMessageBox::about(NULL,"inform","open the camera first");
}
}
相机的主要设置和保存图片均在MyCamera.cpp中完成,这里为了方便直接使用OpenCV的一些函数来完成图像的格式设置和保存。
#include<mycamera.h>
#include<QMessageBox>
#include<MyWidget.h>
//生成保存文件夹所用头文件
#include <direct.h>
#include<io.h>
#include<QMessageBox>
Camera::Camera()
{
Camera::CreateSaveDir();
Camera::setpHander();
}
Camera::~Camera()
{
delete m_Grabber;
}
void Camera::setpHander()
{
if (!DShowLib::InitLibrary()) //相机控制初始化函数,必须在程序初始化时调用,调用成功返回True否则返回false
{ return ; }
atexit(DShowLib::ExitLibrary); //注册终止函数(即main执行结束后调用的函数
m_Grabber=new DShowLib::Grabber();
assert(m_Grabber); //如果创建失败程序报错
// Create a sink containing a MemBufferCollection of 1 RGB24 buffers.
pHanderSink = DShowLib::FrameHandlerSink::create(DShowLib::eRGB24, 1);//指定缓冲区的图像格式
/*Changes this sink's operating mode between Grab and Snap mode.
* In grab mode, all frames reaching the sink are presented to the frame filter
* or filter chain and then copied into the MemBufferCollection. After that,
* the frameReady event of the GrabberListener is called. In snap mode, snapImages or
* snapImagesAsync has to be called in order to trigger the image acquisition process. */
pHanderSink->setSnapMode(true);//设置图像帧保存时为抓取还是复制,具体解释见上面英语示意
// Set the sink.
m_Grabber->setSinkType(pHanderSink);
m_IsOpen=false;
}
void Camera::cameraONOFF()
{
if (!m_Grabber->isDevOpen())//检查相机是否被打开
{
if(m_Grabber->showDevicePage())//相机选择界面
{
m_Grabber->setHWND(appwnd);//指定窗口句柄
m_Grabber->setDefaultWindowPosition(false);
//Sets the size of the video window.
m_Grabber->setWindowSize(m_wWnd,m_hWnd);
Imgsize=cv::Size(m_Grabber->getAcqSizeMaxX(),m_Grabber->getAcqSizeMaxY());
m_Grabber->startLive();
m_IsOpen=true; //图像成功显示则保存标志位置为true,为下一步运行做准备
}
else
{
QMessageBox::warning(NULL, "warning", "fail to open",
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
m_Grabber->closeDev();
}
}
else//关闭图像
{
m_Grabber->stopLive();
m_Grabber->closeDev();
m_IsOpen=false;
}
}
//获取显示窗口句柄
bool Camera::getCameraHWND(QLabel *windowName)
{
m_hWnd=windowName->height();
m_wWnd=windowName->width();
appwnd=(HWND)windowName->winId();
return true;
}
//保存图像
void Camera::SaveImage(string str)
{
if(!m_Grabber->isLive())
{
QMessageBox::information(NULL, "Title", "open deviceL first",
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes);
return;
}
cv::Mat img=cv::Mat(Imgsize,CV_8UC3,cv::Scalar(0,0,0));
// Snap one image and copy it into the MemBufferCollection.
pHanderSink->snapImages(1, 2000);//把一帧图像复制到缓冲区
img.data=pHanderSink->getLastAcqMemBuffer()->getPtr();//从缓存区中读取图片数据
cv::flip(img, img, 0);//垂直反转
cv::imshow(" ",img);
cv::waitKey(50);
cv::imwrite(str,img); //按指定路径保存图像
return;
}
//设置文件保存路径,自动生成文件夹,可根据需要修改为自己的保存路径
void Camera::CreateSaveDir()
{
string folderPath = "F:/QT/CameraControl/Save";//检查保存路径是否存在,如不存在则在工程文件夹下创建一个
if (_access(folderPath.c_str(), 0))
_mkdir(folderPath.c_str());
folderPath = ("F:/QT/CameraControl/Save/LeftCamera");
if (_access(folderPath.c_str(), 0))
_mkdir(folderPath.c_str());
folderPath = ("F:/QT/CameraControl/Save/RightCamera");
if (_access(folderPath.c_str(), 0))
_mkdir(folderPath.c_str());
}
最终结果如下:
尚未解决的问题:
我在第一次生成文件夹时本想用相对地址进行写入,但不知为什么总是无法生成和写入,所以这里直接简单粗暴地使用了绝对地址,如果有知道为什么的小大神欢迎指教,先谢谢啦。
欢迎大家指正和交流。