作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
开发环境
操作系统:Windows 10 1903 18362.778
相机型号:IMAGING SOURCE DMK 33G274
相机软件:ic_setup_3.4.0.2744,gigecam_setup_3.7.1.4512
软件版本:Qt 5.12.8, vs2017 Community
通信方式:GigE - 千兆以太网通信协议
驱动与SDK
开发包和驱动下载地址
提取码:34mt
安装驱动和开发包完成之后,可以找到以下2个目录:
-
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\IC Imaging Control 3.4
- 1 - 开发文档;
- 2 - 示例程序源代码;
- 其他 - 可运行的示例程序;
-
C:\Users\60455\Documents\IC Imaging Control 3.4
- classlib - 程序开发的头文件和库文件;
- redist - 发行包;
- samples - 程序源代码(和刚才目录下的源代码是相同的);
相机原理介绍
映美精相机实时显示图像比较简单,因为SDK中提供了封装好的函数可以直接调用,如下所示:
bool startLive( bool show_videowindow = true );
show_videowindow 设置为true就是启用实时视频窗口,设置为false仅用于图片抓取。
我们只需要提供一个窗口的句柄就可以实时显示图像了。
我们主要来看一下如何使用映美精相机来处理每一帧的数据。
当我们需要处理每一帧数据时,我们就要继承 DShowLib::GrabberListener 类,重新实现 frameReady 这个函数。frameReady 函数就是用来处理图像的,每当相机内存中有图片时,相机就会调用 GrabberListener 中的 frameReady 函数。
这里还要介绍一个叫 FrameHandlerSink 的东西,这是设计采集图像的模式,一共有两种模式:
-
snap采图模式
在snap模式下,到达接收器的所有帧都显示给 IFrameFilter,然后复制到 MemBufferCollection 中。之后调用 GrabberListener 的 frameReady 事件。 -
grab采集模式
在grab模式下,所有帧都呈现给 IFrameFilter。如果没有挂起任何快照作业,则 IFrameFilter::transform 中的目标帧为0,并且不会调用 frameReady 事件。
当快照作业挂起时,会在 MemBuffer 中传递缓冲区,当 IFrameFilter::transform 返回true时,MemBuffer 将在 GrabberListener 中的 frameReady 事件中显示。
步骤如下:
-
添加新的侦听器对象
m_Grabber.addListener( this, GrabberListener::eALL );
-
设置采集模式
m_Sink->setSnapMode( true ); // true为snap采图模式,false为grab采集模式
-
重写 overlayCallback 和 frameReady 函数
virtual void overlayCallback(Grabber &caller, smart_ptr<OverlayBitmap> pBitmap, const tsMediaSampleDesc &desc) override;
virtual void frameReady(Grabber &caller, smart_ptr<MemBuffer> pBuffer, DWORD FrameNumber) override;
-
调用 startLive 开启实时模式
m_Grabber.startLive(true);
-
调用 snapImages 抓取图片
m_Sink->snapImages( 1, 2000 ); // 抓取一张图片,每次调用 snapImages() 之后,就会触发 Listener 中的 frameReady()
我的代码
这里需要说明一下,博主自定义的相机控制类 BICCameraControl 使用了多继承的方式,这个方法不是很好。我们在开发项目中还是要倡导多用组合,少用继承,博主在这里偷懒了。
实现功能:相机图像的实时显示,并且可以在某一时刻拍照保存图片到本地。
首先需要在pro中配置头文件和库文件
CONFIG += debug_and_release
INCLUDEPATH += $$PWD/classlib/include/
windows {
CONFIG(debug, debug|release) {
CONFIG += console
contains(DEFINES, WIN64) {
LIBS += -L$$PWD/classlib/x64/debug/ -lTIS_UDSHL11d_x64
} else {
LIBS += -L$$PWD/classlib/win32/debug/ -lTIS_UDSHL11d_x64
}
} else {
contains(DEFINES, WIN64) {
LIBS += -L$$PWD/classlib/x64/release/ -lTIS_UDSHL11d_x64
} else {
LIBS += -L$$PWD/classlib/win32/release/ -lTIS_UDSHL11d_x64
}
}
}
自定义 BICCameraControl 相机控制类
#ifndef BICCAMERACONTROL_H
#define BICCAMERACONTROL_H
#include <QObject>
#include <tisudshl.h>
#include "Grabber.h"
using namespace _DSHOWLIB_NAMESPACE;
#define NUM_BUFFERS 10
class BICCameraControl : public QObject, public DShowLib::GrabberListener
{
Q_OBJECT
public:
explicit BICCameraControl(QObject *parent = nullptr);
void initCamera(); // 初始化相机
void destroyCamera(); // 销毁相机
void setHWND(HWND hwnd); // 设置窗口句柄
void setWindowSize(long width, long height); // 设置窗口大小
void openCamera(); // 打开相机
void closeCamera(); // 关闭相机
void startWork(); // 开始工作
void stopWork(); // 停止工作
// Overwrite the GrabberListener methods we need.
virtual void overlayCallback(Grabber &caller, smart_ptr<OverlayBitmap> pBitmap, const tsMediaSampleDesc &desc) override;
virtual void frameReady(Grabber &caller, smart_ptr<MemBuffer> pBuffer, DWORD FrameNumber) override;
void takeAPicture(); // 拍照
protected:
DShowLib::Grabber m_Grabber;
DShowLib::FrameHandlerSink::tFHSPtr m_Sink;
private:
HWND m_hwnd;
long m_width;
long m_height;
};
#endif // BICCAMERACONTROL_H
#include "biccameracontrol.h"
#include <QMessageBox>
#include <QDebug>
BICCameraControl::BICCameraControl(QObject *parent) : QObject(parent)
{
DShowLib::InitLibrary();
atexit(DShowLib::ExitLibrary);
}
void BICCameraControl::initCamera()
{
// Enable the overlay bitmap to display the frame counter in the live video.
m_Grabber.setOverlayBitmapPathPosition( ePP_DEVICE );
m_Grabber.getOverlay( ePP_DEVICE )->setEnable( true );
// Register the pListener object event.
m_Grabber.addListener( this, GrabberListener::eALL );
m_Sink = DShowLib::FrameHandlerSink::create( FrameTypeInfoArray::createRGBArray(), NUM_BUFFERS );
m_Sink->setSnapMode( true );
// Apply the sink to the grabber.
m_Grabber.setSinkType( m_Sink );
}
void BICCameraControl::setHWND(HWND hwnd)
{
m_hwnd = hwnd;
}
void BICCameraControl::setWindowSize(long width, long height)
{
m_width = width;
m_height = height;
}
void BICCameraControl::openCamera()
{
// Show the device page.
m_Grabber.showDevicePage();
// Check if there is a valid device.
if ( m_Grabber.isDevValid() ) {
qDebug() << QString::fromStdString(m_Grabber.getDev().toString());
// Set the window that should display the live video.
m_Grabber.setHWND(m_hwnd);
// Enables or disables the default window size lock of the video window.
m_Grabber.setDefaultWindowPosition(false);
// Sets the size of the video window.
m_Grabber.setWindowSize(m_width, m_height);
}
}
void BICCameraControl::closeCamera()
{
// Closes the currently active video capture device.
m_Grabber.closeDev();
}
void BICCameraControl::startWork()
{
if ( m_Grabber.isLive() ) {
QMessageBox::warning(NULL, "Warning", "Grabber already in live-mode !", QMessageBox::Ok);
return;
}
// Check if there is a valid device.
if ( m_Grabber.isDevValid() ) {
// Start the live video.
// Set true to enable the live video window, false to grab only.
m_Grabber.startLive(true);
if ( m_Grabber.getLastError().isError() )
{
QMessageBox::warning(NULL, "Warning", QString::fromStdWString(m_Grabber.getLastError().toStringW()), QMessageBox::Ok);
}
}
}
void BICCameraControl::stopWork()
{
if ( !m_Grabber.isLive() ) {
QMessageBox::warning(NULL, "Warning", "Grabber already in unlive-mode !", QMessageBox::Ok);
return;
}
m_Grabber.stopLive();
if ( m_Grabber.getLastError().isError() )
{
QMessageBox::warning(NULL, "Warning", QString::fromStdWString(m_Grabber.getLastError().toStringW()), QMessageBox::Ok);
}
}
void BICCameraControl::overlayCallback(Grabber& caller, smart_ptr<OverlayBitmap> pBitmap, const tsMediaSampleDesc& MediaSampleDesc)
{
UNREFERENCED_PARAMETER(caller);
char szText[25];
if( pBitmap->getEnable() == true ) // Draw only, if the overlay bitmap is enabled.
{
sprintf_s( szText, "%04d ", MediaSampleDesc.FrameNumber );
pBitmap->drawText( RGB(255,0,0), 0, 0, szText );
}
}
// frameReady() 方法用于处理图像
void BICCameraControl::frameReady(Grabber& caller, smart_ptr<MemBuffer> pBuffer, DWORD currFrame)
{
UNREFERENCED_PARAMETER(caller);
qDebug() << "Buffer " << currFrame << " processed in CListener::frameReady().";
char filename[MAX_PATH];
sprintf_s( filename, "image%02i.bmp", currFrame );
saveToFileBMP( *pBuffer, filename );
}
void BICCameraControl::takeAPicture()
{
if ( !m_Grabber.isLive() ) {
QMessageBox::warning(NULL, "Warning", "Grabber already in unlive-mode !", QMessageBox::Ok);
return;
}
m_Sink->snapImages( 1, 2000 ); // 抓取一张图片,每次调用 snapImages() 之后,就会触发 Listener 中的 frameReady()
}
使用比较简单,直接调用就行
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "biccameracontrol.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_openBtn_clicked();
void on_closeBtn_clicked();
void on_startBtn_clicked();
void on_endBtn_clicked();
void on_picBtn_clicked();
private:
Ui::MainWindow *ui;
BICCameraControl *m_BICCameraControl;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_BICCameraControl = new BICCameraControl(this);
m_BICCameraControl->initCamera();
//QT中获取label控件的窗口句柄
m_BICCameraControl->setHWND((HWND)ui->label->winId());
m_BICCameraControl->setWindowSize(ui->label->width(), ui->label->height());
};
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_openBtn_clicked()
{
m_BICCameraControl->openCamera();
}
void MainWindow::on_closeBtn_clicked()
{
m_BICCameraControl->closeCamera();
}
void MainWindow::on_startBtn_clicked()
{
m_BICCameraControl->startWork();
}
void MainWindow::on_endBtn_clicked()
{
m_BICCameraControl->stopWork();
}
void MainWindow::on_picBtn_clicked()
{
m_BICCameraControl->takeAPicture();
}