在做一个关于相机库SDK的二次开发demo,记录此过程

工业相机都配有自己的相机软件,在软件中会集成这家相机的各种型号下的硬件配置,所以这部分不需要我们去关心,仅需要知道它干了些什么就可以,关于工业相机的编程模型和流程在:


工业相机的编程原理及流程我们知道了,接下来就是找到这些工业相机为我们准备的SDK;

以Basler为例,在Basler的官方网站中找到它的Software,下载对应电脑系统的版本

多个Basler相机调用python python basler相机sdk开发_工业相机


多个Basler相机调用python python basler相机sdk开发_多个Basler相机调用python_02

下载完毕之后是安装,安装过程有一项选择开发者(Development)安装模式,其余按照提示即可;
以开发者模式安装后的pylon会比非开发者模式多出一个文件夹,在安装路径(Normal:C:\Program Files\Basler\pylon 6\Development)下找到它,文件夹名为Development。

该文件夹下有Basler为我们提供的include、lib及对应的Documentation,此外还提供了一些sample供我们玩耍。

打开sample文件夹,可以看到这是一个Vs工程,使用VS加载,通常不会成功。

这是因为你的VS里缺少了支持Basler开发者们写的工程的组件,按照提示去安装这些组件:

Tips:在快要安装完成时,会非常慢,查了一下,据说是在配置系统原点,慢就慢点吧,总比安装个组件搞坏了原有的系统好。

多个Basler相机调用python python basler相机sdk开发_加载_03


。。。Thousand Years Later。。。

以pylon的相机流程为例
总体流程:
1、加载相机对象
2、加载数据流抓取对象
3、连续或单帧采图
4、卸载数据流抓取对象
5、卸载相机对象

通常来说在编程上步骤1和步骤5是通用的,对应的事件对象也有这种机制,加载事件对象–完成功能–卸载事件对象。
下面对每个过程进行分析:
1、加载相机对象

1.1 初始化动态系统资源
	PylonInitialize  //建立内在的结构体,为一些全局参数提供空间
1.2 检测当前相机数目
	PylonEnumerateDevices  //枚举相机
1.3 创建相机对象
	PylonCreateDeviceByIndex  //找到对应相机index并创建对象
1.4 打开相机对象
	PylonDeviceOpen

2、加载数据流抓取对象

2.1 检测Stream 采集通道数目
	PylonDeviceGetNumStreamGrabberChannels
2.2 获取Stream Grabber抓取对象
	PylonDeviceGetStreamGrabber
2.3 打开对象
	PylonStreamGrabberOpen
2.4 获取同步对象
	PylonStreamWaitObjectWait
2.5 设置抓图参数
	PylonStreamGrabberSetMaxNumBuffer  //Buffer数目 				
	PylonStreamGrabberSetMaxBuffersize  //Buff大小
2.6 分配每个Buffer内存
	new
2.7 分配抓图要的资源
	PylonStreamGrabberPrepareGrab  //分配资源包括以下四个部分:         	
	//1、动态内存分配         
	//2、驱动数据内存分配         
	//3、xxx相机同步通道         
	//4、xxx相机同步带宽  
	//(此函数调用后,影响资源分配的参数均为只读型)
2.8 将Buffer 注册到抓取对象
	PylonStreamGrabberRegisterBuffer
2.9 Buffer依次放入抓图的输入对象中
	PylonStreamGrabberQueueBuffer

步骤2中,2.1-2.4为打开数据流抓取对象部分;2.5-2.9为分配抓图所需资源部分;

3、单帧或者连续抓图过程

3.1 设置抓图模式为连续,并开始抓图
	PylonDeviceFeatureFromString  //设置连续模式 		
	PylonDeviceExcuteCommandFeature  //开始抓图命令
3.2 等待下一个要抓取的Buffer被填充
	PylonWaitObjectWait
3.3 抓取Buffer结果并显示
	PylonStreamGrabberRetriveResult
3.4 将Buffer再放入抓图的输入队列中
	PylonStreamGrabberQueueBuffer

步骤3.2–3.3重复一次或者多次可实现单帧或者连续抓图;

4、卸载数据流抓取对象

4.1 将所有Buffer放到数据流抓取对象输出队列上
	PylonStreamGrabberCancelGrab
4.2 将输出队列上的数据全部取出
	PylonStreamGrabberRetriveResult
4.3 取消注册分配的Buffer
	PylonStreamGrabberDeregisterBuffer
4.4 释放Buffer内存
	delete
4.5 释放相关的分配资源
	PylonStreamGrabberFinishGrab
4.6 关闭数据流抓取对象
	PylonStreamGrabberClose

步骤4.1 - 4.5 均为释放抓图资源

5、卸载相机对象

5.1 关闭相机对象
	PylonDeviceClose
5.2 销毁相机对象
	PylonDestrouDevice
5.3 卸载动态系统资源
	PylonTerminate  //释放内在结构体


Recommend sample

Description

ParametrizeCamera_LoadAndSave.cpp

This sample application demonstrates how to save or load the features of a camera to or from a file.

GUI_ImageWindow.cpp

This sample illustrates how to show images using the CPylonImageWindow class. Here, images are grabbed, split into multiple tiles and and each tile is shown in a separate image windows.

Grab_CameraEvents.cpp

It is shown in this sample how to register event handlers indicating the arrival of events sent by the camera. For demonstration purposes, several different handlers are registered for the same event.

Grab_Strategies.cpp

This sample shows the use of the Instant Camera grab strategies.

Grab.cpp

Grab flow.

以上为Basler相机的采图流程,下面将其封装成动态库,并使用QT写一个Ui进行调用。

可能是由于电脑被公司的加密软件加密了,导致Pylon的相机库编译报错,问题是出现在枚举设备参数设置头文件EnumParameterT.h中

virtual CEnumParameterT<EnumT>& operator=(EnumT value){}

多个Basler相机调用python python basler相机sdk开发_取对象_04


多方求解未果,遂弃Basler投HiK:

对于相机的操作原理都是一样的,海康的SDK提供两种方式采图:

一是先注册图像采集回调函数,然后操作相机 StartGrabbing,采集的图像在回调函数中返回;
二是不注册图像采集回调函数,然后在应用层循环调用GetOneFrame获取指定像素格式的帧数据,但是获取帧数据时,上层应用程序需要根据帧率控制好调用接口的频率;

我采用的时第二种方式,因为第一种方式,我尝试失败了,错误出在注册图像采集回调函数后,不能获取到曝光时间、曝光频率、帧率等信息,也可能与公司的加密软件有关,所以就直接采用了第二种方式:
这里未单独分一个新的线程去处理读取图像,所以效率来说不是很高,仅供了解原理使用,在图像的处理上采用了cv的Mat类型,此外还可以使用halcon的Hobject进行图像的显示、处理、存储等.
直接贴上槽函数,有注释:

QtGrabImage::QtGrabImage(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
	nRet = MV_OK;
	handle = NULL;
	pData = NULL;
	pDataForRGB = NULL;
	g_bExit = false;
	g_nPayloadSize = 0;
	nIndex = 0;
	nPacketSize = 0;

	//connect(ui.EnumCam, &QPushButton::clicked, this, &QtGrabImage::on_EnumCam_clicked);
	//connect(ui.OpenCam, &QPushButton::clicked, this, &QtGrabImage::on_OpenCam_clicked);
	//connect(ui.StartCam, &QPushButton::clicked, this, &QtGrabImage::on_StartCam_clicked);

	//connect(ui.StopCam, &QPushButton::clicked, this, &QtGrabImage::on_StopCam_clicked);
	//connect(ui.CloseCam, &QPushButton::clicked, this, &QtGrabImage::on_CloseCam_clicked);
	//connect(ui.SaveImg, &QPushButton::clicked, this, &QtGrabImage::on_SaveImg_clicked);
	//connect(ui.FrameModel, &QRadioButton::toggled, this, &QtGrabImage::on_FrameModel_clicked);//使用一个新的pushbutton去采集的话就不需要这两个状态了
	//connect(ui.ConstantModel, &QRadioButton::toggled, this, &QtGrabImage::on_ConstantModel_clicked);//直接用ui.RadioButton->isChecked()去判断
	connect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(comboxSlots()));
}

bool QtGrabImage::PrintDeviceInfo (MV_CC_DEVICE_INFO* pstMVDevInfo)
{
	if (NULL == pstMVDevInfo)
	{
		QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("The Pointer of pstMVDevInfo is NULL!"));
		//printf("The Pointer of pstMVDevInfo is NULL!\n");
		return false;
	}
	if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
	{
		int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
		int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
		int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
		int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);

		//打印当前相机ip和用户自定义名字
		QString strIp = QString::number(nIp1) + "." + QString::number(nIp2) + "." + QString::number(nIp3) + "." +
			QString::number(nIp4);
		unsigned char* cusrname = pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName; //相机用户名是存储成unsigned char 类型
		std::string cstr = std::string((char *)cusrname);
		QString strName = QString::fromStdString(cstr);

		QString strMesg = "Current User: " + strName + " , Current Ip: " + strIp;
		ui.statusBar->showMessage(strMesg);
		ui.comboBox->addItem(strMesg);//调用之前先声明一下 disconnected
	}
	else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
	{
		QMessageBox::information(this, tr("Sorry : "), QString::fromLocal8Bit("Not USB Device!"));
	}
	else
	{
		QMessageBox::information(this, tr("Error : "), QString::fromLocal8Bit("Not support!"));
		//printf("Not support.\n");
	}

	return true;
}

//WorkThread线程函数,用于读取相机的图像流
//unsigned int _stdcall QtGrabImage::WorkThread(void *pUser)
//{
//	int nRet = MV_OK;
//	stImageInfo = { 0 };
//	memset(&stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
//	unsigned char * pData = (unsigned char *)malloc(sizeof(unsigned char) * (g_nPayloadSize));
//	if (pData == NULL)
//	{
//		return 0;
//	}
//	unsigned int nDataSize = g_nPayloadSize;
//	while (1)
//	{
//		nRet = MV_CC_GetOneFrameTimeout(pUser, pData, nDataSize, &stImageInfo, 1000);
//		if (nRet == MV_OK)
//		{
//			printf("Get one frame : Width:[%d], Height[%d], nFrameNum[%d]\n",
//				stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.nFrameNum);
//		}
//		else
//		{
//			printf("Get frame : No data. nRet = [0x%x]\n", nRet);
//		}
//		if (g_bExit)
//		{
//			break;
//		}
//	}
//	free(pData);
//	return 0;
//}

void QtGrabImage::on_EnumCam_clicked()
{
	memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
	int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
	if (MV_OK != nRet)
	{
		QString qstrerror = "Enum Device Faile! nRet:" + QString(nRet, 16);

		QMessageBox::information(this, tr("Error : "), qstrerror);
		//printf("Enum Devices fail! nRet [0x%x]\n", nRet);
		return;
	}
	QObject::disconnect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(comboxSlots()));
	if (stDeviceList.nDeviceNum > 0)
	{
		for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++)
		{
			//printf("[device %d]:\n", i);
			MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];
			if (NULL == pDeviceInfo)
			{
				break;
			}
			PrintDeviceInfo(pDeviceInfo);
		}
	}
	else
	{
		QMessageBox::information(this, tr("Error: "), QString::fromLocal8Bit("Find No Device!"));
		//printf("Find No Devices!\n");
		return;
	}

	//printf("Please Intput camera index:");

	//scanf_s("%d", &nIndex);

	//if (nIndex >= stDeviceList.nDeviceNum)
	//{
	//	printf("Intput error!\n");
	//	return;
	//}
}

void QtGrabImage::comboxSlots()
{
	nIndex = ui.comboBox->currentIndex();
}

void QtGrabImage::on_OpenCam_clicked()
{
	int nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);
	if (MV_OK != nRet)
	{
		QString strerr = "Create Handle Fail! nRet :" + QString(nRet, 16);
		QMessageBox::information(this, tr("Error: "), strerr);
		//printf("Creat Handle Failed! nRet = [0x%x]\n", nRet);
		return;
	}
	nRet = MV_CC_OpenDevice(handle);
	if (MV_OK != nRet)
	{
		QString strerr = "Open Device Fail! nRet:" + QString::number(nRet, 16);
		QMessageBox::information(this, tr("Error "), strerr);
		//printf("Open Device Failed! nRet = [0x%x]\n", nRet);
		return;
	}
	//探测网络最佳包大小(只对GigE相机有效)
	if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE)
	{
		nPacketSize = MV_CC_GetOptimalPacketSize(handle);
		if (nPacketSize > 0)
		{
			nRet = MV_CC_SetIntValue(handle, "GevSCPSPacketSize", nPacketSize);
			if (nRet != MV_OK)
			{
				QString strerr = "Set Packet Size fail nRet:" + QString(nRet, 16);
				QMessageBox::information(this, tr("Warning:"), strerr);
				//printf("Warning: Set Packet Size fail nRet [0x%x]!", nRet);
			}
		}
		else
		{
			QString strerr = "Get Packet Size fail nRet:" + QString(nRet, 16);
			QMessageBox::information(this, tr("Warning:"), strerr);
			//printf("Warning: Get Packet Size fail nRet [0x%x]!", nRet);
		}
	}
	//获取数据包大小
	memset(&stParam, 0, sizeof(MVCC_INTVALUE));
	nRet = MV_CC_GetIntValue(handle, "PayloadSize", &stParam);
	if (MV_OK != nRet)
	{
		QString strerr = "Get PayloadSize fail! nRet:" + QString(nRet, 16);
		QMessageBox::information(this, tr("Warning:"), strerr);
		//printf("Get PayloadSize fail! nRet [0x%x]\n", nRet);
		return;
	}
	g_nPayloadSize = stParam.nCurValue;
}

void QtGrabImage::on_StartCam_clicked()
{
	//设置触发模式为off
	int nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 0);
	if (MV_OK != nRet)
	{
		QString strerr = "Set Trigger Model Fail! nRet:" + QString(nRet, 16);
		QMessageBox::information(this, tr("Error"), strerr);
		//printf("Set Trigger Mode fail! nRet [0x%x]\n", nRet);
		return;
	}
	//开始取流
	nRet = MV_CC_StartGrabbing(handle);
	if (nRet != MV_OK)
	{
		QString strerr = "Start Grabbing Fail! nRet:" + QString(nRet, 16);
		QMessageBox::information(this, tr("Error"), strerr);
		//printf("Start Grabbing Failed. nRet = [0x%x]\n", nRet);
		return;
	}
	//创建新线程去操作取流
	//unsigned int nThreadID = 0;
	//void *hThreadHandle = (void *)_beginthreadex(NULL, 0, WorkThread, handle, 0, &nThreadID);
	//if (NULL == hThreadHandle)
	//{
	//	return;
	//}
	stImageInfo = { 0 };
	memset(&stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
	pData = (unsigned char *)malloc(sizeof(unsigned char) * (g_nPayloadSize));
	if (pData == NULL)
	{
		QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("malloc pData fail!"));
		return;
	}
	unsigned nDataSize = g_nPayloadSize;
//	
//	nRet = MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, &stImageInfo, 1000);
//	if (nRet == MV_OK)
//	{
//		QString strInfo = "Get one frame: Width:" + QString(stImageInfo.nWidth, 10) +
//			" Height:" + QString(stImageInfo.nHeight, 10) + " nFrameNum:" + QString(stImageInfo.nFrameNum);
//		
//		pDataForRGB = (unsigned char *)malloc(stImageInfo.nWidth * stImageInfo.nHeight * 3);
//		if (pDataForRGB == NULL)
//		{
//			QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("pDataForRGB malloc fail!"));
//			return;
//		}
//		unsigned nDataSizeForRGB = stImageInfo.nWidth * stImageInfo.nHeight * 3;
//		//像素格式转换
//		MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
//		memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));
//
//		stConvertParam.nWidth = stImageInfo.nWidth;		//图像的宽
//		stConvertParam.nHeight = stImageInfo.nHeight;	//图像的高
//		stConvertParam.pSrcData = pData;					//输入数据缓存
//		stConvertParam.nSrcDataLen = stImageInfo.nFrameLen;	//输入数据大小
//		stConvertParam.enSrcPixelType = stImageInfo.enPixelType;		//源图像像素类型
//		stConvertParam.enDstPixelType = PixelType_Gvsp_RGB8_Packed;	//目标像素类型
//		stConvertParam.pDstBuffer = pDataForRGB;						//输出数据缓存地址
//		stConvertParam.nDstBufferSize = nDataSizeForRGB;				//输出数据大小
//		nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
//		if (nRet != MV_OK)
//		{
//			QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("Image Pixel convert fail!"));
//			return;
//		}
//		/*bool Convert2Mat(MV_FRAME_OUT_INFO_EX* pstImageInfo, unsigned char * pData, int i)*/
//		int cvheight = stImageInfo.nHeight;
//		int cvwidth = stImageInfo.nWidth;
//		cv::Mat cvImg(cvheight, cvwidth, CV_8UC3, pDataForRGB);
//		//cvImg 转换到QImage
//		cv::cvtColor(cvImg, cvImg, CV_BGR2RGB);
//		QImage qImg = QImage((const unsigned char*)(cvImg.data), cvImg.cols, cvImg.rows,
//			cvImg.cols*cvImg.channels(), QImage::Format_RGB888);
//		//显示
//		ui.label->clear();
//		ui.label->setPixmap(QPixmap::fromImage(qImg.scaled(ui.label->size())));//设置图像大小和label大小一致
		ui.label->resize(ui.label->pixmap()->size());//设置label的大小和图像的大小一致
	DispGrabImgFrame();
	return;
	//}
}

void QtGrabImage::on_CamGrab_clicked()
{
	//判断Radio 的状态 及运行接下来的事情
	if (ui.FrameModel->isChecked())
	{
		DispGrabImgFrame();
		return;
	}
	if (ui.ConstantModel->isChecked())
	{
		DispGrabImgConstant();
		return;
	}
	else
	{
		QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("Please choose a Model first!"));
		return;
	}
}

void QtGrabImage::on_StopCam_clicked()
{
	nRet = MV_CC_StopGrabbing(handle);
	if (MV_OK != nRet)
	{
		QString strerr = "Stop Grabbing fail! \n nRet:" + QString::number(nRet, 16);
		QMessageBox::information(this, tr("Error"), strerr);
		return;
	}
}

void QtGrabImage::on_SaveImg_clicked()
{
	QString saveImgname = QFileDialog::getSaveFileName(this, tr("Open Config"), " ", tr("config files (*.jpg)"));
	if (!saveImgname.isNull())
	{
		QImage qimg = ui.label->pixmap()->toImage();
		if (qimg.isNull())
		{
			QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("There is no Image to save!"));
			return;
		}
		else
		{
			qimg.save(saveImgname, "JPG", 100);
			QMessageBox::information(this, tr(" "), QString::fromLocal8Bit("Image saved Successed!"));
		}
	}
	else
	{
		QMessageBox::information(this, tr(" "), QString::fromLocal8Bit("Save Canceled!"));
		return;
	}
}

void QtGrabImage::on_CloseCam_clicked()
{
	//关闭设备
	nRet = MV_CC_CloseDevice(handle);
	if (MV_OK != nRet)
	{
		QString strerr = "CloseDevice fail! \n nRet:" + QString::number(nRet, 16);
		QMessageBox::information(this, tr("Error"), strerr);
		return;
	}

	//销毁句柄
	nRet = MV_CC_DestroyHandle(handle);
	if (MV_OK != nRet)
	{
		QString strerr = "Destroy handle fail! \n nRet:" + QString::number(nRet, 16);
		QMessageBox::information(this, tr("Error"), strerr);
		return;
	}
}

void QtGrabImage::DispGrabImgFrame()
{
	unsigned nDataSize = g_nPayloadSize;
	nRet = MV_CC_GetOneFrameTimeout(handle, pData, nDataSize, &stImageInfo, 1000);
	if (nRet == MV_OK)
	{
		QString strInfo = "Get one frame: Width:" + QString(stImageInfo.nWidth, 10) +
			" Height:" + QString(stImageInfo.nHeight, 10) + " nFrameNum:" + QString(stImageInfo.nFrameNum);
		pDataForRGB = (unsigned char *)malloc(stImageInfo.nWidth * stImageInfo.nHeight * 3);
		if (pDataForRGB == NULL)
		{
			QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("pDataForRGB malloc fail!"));
			return;
		}
		unsigned nDataSizeForRGB = stImageInfo.nWidth * stImageInfo.nHeight * 3;
		//像素格式转换
		MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
		memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));

		stConvertParam.nWidth = stImageInfo.nWidth;		//图像的宽
		stConvertParam.nHeight = stImageInfo.nHeight;	//图像的高
		stConvertParam.pSrcData = pData;					//输入数据缓存
		stConvertParam.nSrcDataLen = stImageInfo.nFrameLen;	//输入数据大小
		stConvertParam.enSrcPixelType = stImageInfo.enPixelType;		//源图像像素类型
		stConvertParam.enDstPixelType = PixelType_Gvsp_RGB8_Packed;	//目标像素类型
		stConvertParam.pDstBuffer = pDataForRGB;						//输出数据缓存地址
		stConvertParam.nDstBufferSize = nDataSizeForRGB;				//输出数据大小
		nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
		if (nRet != MV_OK)
		{
			QMessageBox::information(this, tr("Error"), QString::fromLocal8Bit("Image Pixel convert fail!"));
			return;
		}
		/*bool Convert2Mat(MV_FRAME_OUT_INFO_EX* pstImageInfo, unsigned char * pData, int i)*/
		int cvheight = stImageInfo.nHeight;
		int cvwidth = stImageInfo.nWidth;
		cv::Mat cvImg(cvheight, cvwidth, CV_8UC3, pDataForRGB);
		//cvImg 转换到QImage
		cv::cvtColor(cvImg, cvImg, CV_BGR2RGB);
		QImage qImg = QImage((const unsigned char*)(cvImg.data), cvImg.cols, cvImg.rows,
			cvImg.cols*cvImg.channels(), QImage::Format_RGB888);
		//显示
		ui.label->clear();
		ui.label->setPixmap(QPixmap::fromImage(qImg.scaled(ui.label->size())));
		Sleep(1000);
		//ui.label->resize(ui.label->size());
	}
}

void QtGrabImage::DispGrabImgConstant()
{
	for (int i = 0; i < 1000; i++)
	{
		++i;
		DispGrabImgFrame();
		while (waitKey())
		{
			return;
		}
	}
	return;
}

头文件也贴一下:

#pragma once
#include <Windows.h>
#include <stdio.h>
#include <string>
#include <conio.h>
#include <process.h>

#include "MvCameraControl.h"
//#include "TlFactory.h"
//#include "MvGigEDevice.h"

#include <qfiledialog.h>
#include <QtWidgets/QMainWindow>
#include <qmessagebox.h>
#include "ui_qtgrabimage.h"
#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
//using namespace MvCamCtrl;

class QtGrabImage : public QMainWindow
{
	Q_OBJECT

public:
	QtGrabImage(QWidget *parent = Q_NULLPTR);

	bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo);
	//WorkThread线程函数,用于读取相机的图像
	unsigned int _stdcall QtGrabImage::WorkThread(void *pUser);

	int nRet;
	void *handle;
	unsigned char* pData;
	unsigned char* pDataForRGB;
	bool g_bExit;
	unsigned int g_nPayloadSize;
									
	MV_CC_DEVICE_INFO_LIST stDeviceList;
	unsigned int nIndex; //设备列表信息index
	int nPacketSize;  //网络包大小
	MVCC_INTVALUE stParam;  //数据包大小设置参数
	MV_FRAME_OUT_INFO_EX stImageInfo;  //用于接收单帧图像流

	void DispGrabImgFrame();
	void DispGrabImgConstant();
	//MvCamCtrl::CTlFactory& tlFactory;  //使用MvCamCtrl 下的功能,用于设置单帧/连续模式

private:
	Ui::QtGrabImageClass ui;

signals:
	void currentIndexChanged(int i);

private slots:
	
	void on_EnumCam_clicked();//在此函数中实现下拉列表选取枚举设备
	void on_OpenCam_clicked();
	void on_StartCam_clicked();
	//void on_FrameModel_clicked(bool);
	//void on_ConstantModel_clicked(bool);
	void on_CamGrab_clicked();
	void on_StopCam_clicked();
	void on_CloseCam_clicked();
	void on_SaveImg_clicked();
	void comboxSlots();
};

效果图如下(因为我用的是工业相机和FA镜头,正常拍摄到的图像就是这样,图中亮的地方是灯):

多个Basler相机调用python python basler相机sdk开发_多个Basler相机调用python_05