本文将梳理一个不借助外部视觉库(如 OpenCV/Halcon)的海康相机图像采集和显示 Demo。该程序直接使用 Qt GUI 来显示图像。通过海康 MVS SDK 实现相机的连接、参数设置、图像采集和异常处理等功能,并通过 Qt 界面展示操作结果。
1. 功能概述
该程序通过 Qt 的 GUI 作为界面,通过海康 MVS SDK 控制相机并显示实时图像。主要功能包括:
- 相机连接和设置
- 参数调整(如曝光时间和触发模式)
- 图像采集与显示
- 异常处理(如设备断开)
2. 初始化与 GUI 设置
在 MainWindow
的构造函数中进行界面初始化,包括设置按钮的可见性、启用状态等。 initStyle()
函数用于加载并应用界面的样式表。
cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
initStyle();
// 设置初始 UI 状态
ui->btn_Start->setEnabled(false);
ui->btn_Stop->setEnabled(false);
ui->btn_close->setEnabled(false);
ui->btn_Grab->setVisible(false);
ui->groupBox->setEnabled(false);
}
3. 相机连接与操作
3.1 枚举和连接相机
通过点击"搜索相机"按钮,程序调用 MV_CC_EnumDevices
枚举连接的相机设备,将可用的相机序列号显示在列表中供用户选择。
cpp
void MainWindow::on_btnSeachCamera_clicked() {
ui->listWidget->clear();
int nRet = MV_OK;
bool isGige = ui->radioGIGE->isChecked();
// 枚举连接的相机
if (isGige) {
nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE, &cameraList);
} else {
nRet = MV_CC_EnumDevices(MV_USB_DEVICE, &cameraList);
}
if (nRet != MV_OK || cameraList.nDeviceNum == 0) {
setLastErrorMsg(tc("未找到任何可用相机, 错误码: %1").arg(nRet));
return;
}
// 添加相机序列号到列表中
for (int i = 0; i < cameraList.nDeviceNum; i++) {
const char *serial = isGige
? reinterpret_cast<char *>(cameraList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.chSerialNumber)
: reinterpret_cast<char *>(cameraList.pDeviceInfo[i]->SpecialInfo.stUsb3VInfo.chSerialNumber);
ui->listWidget->addItem(serial);
}
}
3.2 相机连接及初始化
点击"连接相机"按钮后,程序通过创建相机句柄并打开设备。设置默认曝光时间和注册图像采集、异常处理回调函数。
cpp
void MainWindow::on_btnconnect_clicked() {
MV_CC_DEVICE_INFO cameraInfo;
int nRet = MV_OK;
// 创建句柄并打开相机
memcpy(&cameraInfo, cameraList.pDeviceInfo[0], sizeof(MV_CC_DEVICE_INFO));
if ((nRet = MV_CC_CreateHandle(&m_handle, &cameraInfo)) != MV_OK) {
setLastErrorMsg(tc("相机初始化失败, 错误码: %1").arg(nRet));
return;
}
if ((nRet = MV_CC_OpenDevice(m_handle, MV_ACCESS_Exclusive)) != MV_OK) {
setLastErrorMsg(tc("相机打开失败, 错误码: %1").arg(nRet));
return;
}
// 设置曝光时间并注册回调函数
MV_CC_SetExposureTime(m_handle, ui->spinBox->value());
MV_CC_RegisterImageCallBackEx(m_handle, ImageCallBack, this);
MV_CC_RegisterExceptionCallBack(m_handle, ExceptionCallBack, this);
// 更新 UI 状态
ui->btn_Start->setEnabled(true);
ui->btn_Stop->setEnabled(true);
ui->btn_close->setEnabled(true);
ui->btnconnect->setEnabled(false);
ui->groupBox->setEnabled(true);
}
4. 图像采集与显示
4.1 图像采集回调
通过注册图像回调函数 ImageCallBack
,将采集到的图像数据转换为 QImage
格式,以便在 Qt 的 QLabel
中显示。
cpp
void ImageCallBack(unsigned char *pData, MV_FRAME_OUT_INFO_EX *pFrameInfo, void *pUser) {
MainWindow *camera = static_cast<MainWindow *>(pUser);
QImage img;
switch (pFrameInfo->enPixelType) {
case PixelType_Gvsp_Mono8:
// 单通道灰度图像转换
img = QImage(pData, pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nWidth, QImage::Format_Grayscale8);
break;
case PixelType_Gvsp_RGB8_Packed:
// RGB8 数据转换
img = QImage(pData, pFrameInfo->nWidth, pFrameInfo->nHeight, QImage::Format_RGB888);
break;
case PixelType_Gvsp_BayerRG8:
case PixelType_Gvsp_BayerBG8:
// Bayer 格式转换
img = QImage(pFrameInfo->nWidth, pFrameInfo->nHeight, QImage::Format_RGB888);
for (int y = 0; y < pFrameInfo->nHeight - 1; y++) {
for (int x = 0; x < pFrameInfo->nWidth - 1; x++) {
int index = y * pFrameInfo->nWidth + x;
uchar r, g, b;
if (pFrameInfo->enPixelType == PixelType_Gvsp_BayerRG8) {
r = pData[index];
g = (pData[index + 1] + pData[index + pFrameInfo->nWidth]) / 2;
b = pData[index + pFrameInfo->nWidth + 1];
} else {
b = pData[index];
g = (pData[index + 1] + pData[index + pFrameInfo->nWidth]) / 2;
r = pData[index + pFrameInfo->nWidth + 1];
}
img.setPixel(x, y, qRgb(r, g, b));
}
}
break;
default:
qWarning("Unsupported pixel type");
return;
}
// 显示图像
camera->detect(img);
}
void MainWindow::detect(const QImage &image) {
ui->lab_image->setPixmap(QPixmap::fromImage(image).scaled(ui->lab_image->size()));
}
4.2 触发模式和曝光时间调整
根据用户选择的触发模式和曝光时间设置相机参数。Qt 的信号和槽机制使得切换触发模式和调整曝光时间变得直观。
cpp
void MainWindow::on_comboBox_activated(int index) {
int nRet = MV_OK;
switch (index) {
case OpenContinue:
nRet = MV_CC_SetEnumValue(m_handle, "TriggerMode", 0);
break;
case OpenSoftWare:
nRet = MV_CC_SetEnumValue(m_handle, "TriggerMode", 1);
nRet = MV_CC_SetEnumValue(m_handle, "TriggerSource", 7);
break;
case OpenHardWare:
nRet = MV_CC_SetEnumValue(m_handle, "TriggerMode", 1);
nRet = MV_CC_SetEnumValue(m_handle, "TriggerSource", 0);
break;
}
if (nRet != MV_OK) setLastErrorMsg(tc("设置触发模式失败, 错误码: %1").arg(nRet));
}
5. 异常处理与资源释放
5.1 异常处理
程序注册了异常回调函数 ExceptionCallBack
,用于处理如设备断开连接等异常情况,并将错误信息显示在界面上。
cpp
void ExceptionCallBack(unsigned int nMsgType, void *pUser) {
if (nMsgType == MV_EXCEPTION_DEV_DISCONNECT) {
MainWindow *camera = static_cast<MainWindow *>(pUser);
camera->setLastErrorMsg(tc("相机连接断开"));
}
}
5.2 资源释放
程序退出时,调用相机停止、关闭函数,释放资源以避免内存泄漏。
cpp
void MainWindow::on_btn_close_clicked() {
on_btn_Stop_clicked();
int nRet = MV_CC_CloseDevice(m_handle);
if (nRet != MV_OK) {
setLastErrorMsg(tc("关闭相机失败, 错误码: %1").arg(nRet));
}
}
6. 总结
本代码实现了
基于 Qt 的海康相机图像采集和显示系统,直接通过 MVS SDK 进行图像数据处理和显示。Qt 的信号槽机制、MVS SDK 的相机控制 API,以及图像格式转换,使得整个系统操作简单高效。