红外标定图

一、开发原因

项目需要调用高德红外相机拍图,需要有一个采图软件,为了方便操作并且有可视化见面,我选择了基于**“Qt + Opencv + 高德SDK”的组合,使用“VS + QT”** 实现相机的采图。

QQ交流(记得备注哦):2514251475
代码:GitHub

原始的想法:

  1. 点击按钮/按键拍图功能
  2. 按F键或者点击“拍摄",存图
  3. 模仿高德的例程写一个就行

二、Qt学习笔记

2.1 信号与槽

信号是动作开关,是执行函数;信号和槽函数之间用 connect 函数连接。个人理解应该是类似于中断机制,当触发中断后,进入中断函数执行相关操作。

添加槽函数的方式:
方法一
使用VS插件中的Qt Designer,转到槽函数;步骤如下:

  • 在VS中需要 “VS解决方案中选中方案,右键->Qt->Refresh intelliSense进行刷新”;
  • 然后在对应的.h和.cpp文件中分别添加 信号类函数声明 和 槽函数具体功能;
  • 该方法在会自动在 ui_xxxxx.h 文件中定义connect函数

方法二
构造函数里写connect函数,(纯手动添加connect函数)
connect函数两种写法:

// 连接信号与槽 谁发出信号 发出什么信号 谁处理信号 怎么处理信号
// 方法一:用宏定义
connect(ui.cmd_Edit, SIGNAL(returnPressed()), this, SLOT(commit_BtnClicked()));
// 方法二:取地址
connect(ui.exit_Btn, &QPushButton::clicked, this, &QCamera::on_exit_BtnClicked);

然后在对应的.h和.cpp文件中分别添加 信号类函数声明 和 槽函数具体功能(一般先声明好槽函数);

当槽函数的功能比较少时,可以用QMessageBox的方法

connect(ui.browse_Btn, &QPushButton::clicked, [this]()
{
	QMessageBox::information(this, "信息", "点击浏览");
});

2.2 定时器

两种方式开启定时器
方式一:使用QObject类

// 相关函数
// startTimer   开启
// timerEvent   事件
// killTimer    关闭

// 槽函数里打开计时器
myTimerId = this->startTimer(TIMEOUT); //TIMEOUT
// 然后重写虚函数(类继承)
virtual void timerEvent(QTimerEvent *event);

timerEvent(QTimerEvent *event){
	if(event->timerId() != myTimerId)
	return;
	// 实现定时功能
}

// 槽函数关闭定时器
this->killTimer(myTimerId);

方式二:使用QTimer类

// 头文件定义
QTimer *timer;

// 构造函数中
timer = new QTimer;
// 定时器槽函数
connect(timer,&QTimer::timerout,this,&Widget::timeoutSlot)

// 开启定时器
timer->start(TIMEOUT);

void Widget::timeoutSlot(){
	//定时器操作
}

// 结束定时器
timer->stop();

// 只执行单次定时
QTimer::singleShot(1000, this, SLOT(timerout));

三、高德红外SDK梳理

这里可以跳过,我自己梳理手册记录的。

数据类型说明:
enum guide_usb_code_e

typedef enum
{
	ERROR_NO = 1,
	//1:无错误
	ERROR_DEVICE_NOT_FOUND = -1,
	//-1:找不到设备
	ERROR_POINT_NULL = -2,
	//-2:数据指针为空
	ERROR_POINTS_TOO_LARGE = -3,
	//-3:数据指针过大
	ERROR_POINTS_TOO_SMALL = -4,
	//-4:数据指针过小
	ERROR_MALLOC_FAILED = -5,
	//-5:申请缓存失败
	ERROR_RESOLUTION = -6,
	//-6:分辨率设置错误
	ERROR_UNKNOW = -999
	//-999:未知错误
} guide_usb_code_e;

struct guide_usb_device_info_t 高德设备信息,打开设备时需要的信息

typedef struct
{
	int width;							//图像宽度
	int height;							//图像高度
	guide_usb_video_mode_e video_mode;	//视频模式
} guide_usb_device_info_t;

enum guide_usb_video_mode_e 根据机芯配置好的视频模式,传入对应的类型

typedef enum
{
	X16 = 0,						//X16
	X16_PARAM = 1,					//X16+参数行
	Y16 = 2,						//Y16
	Y16_PARAM = 3,					//Y16+参数行
	YUV = 4,						//YUV
	YUV_PARAM = 5,					//YUV+参数行
	Y16_YUV = 6,					//Y16+YUV
	Y16_PARAM_YUV = 7				//Y16+参数行+YUV
} guide_usb_video_mode_e;

struct guide_usb_frame_data_t 图像数据X16/Y16 模式从 frame_src_data 传出数据 YUV 模式从 frame_yuv_data 传出数据

typedef struct
{
	int frame_width;							//图像宽度
	int frame_height;							//图像高度
	unsigned char *frame_rgb_data;				//rgb 数据
	int frame_rgb_data_length;					//rgb 数据长度
	short *frame_src_data;						//原始数据,X16/Y16
	int frame_src_data_length;					//原始数据长度
	short *frame_yuv_data;						//yuv 数据
	int frame_yuv_data_length;					//yuv 数据长度
	short *paraLine;							//参数行
	int paraLine_length;						//参数行长度
} guide_usb_frame_data_t;

OnFrameDataReceivedCB 这段代码定义了一个函数指针类型 OnFrameDataReceivedCB,该函数指向一个没有返回值的函数,该函数接受一个 guide_usb_frame_data_t 类型的常量参数 frame_data。

typedef void(__stdcall *OnFrameDataReceivedCB)(const guide_usb_frame_data_t frame_data);

enum guide_usb_device_status_e 机芯连接状态

typedef enum
{
	DEVICE_CONNECT_OK = 1,
	//连接正常
	DEVICE_DISCONNECT_OK = -1,
	//断开连接
} guide_usb_device_status_e;

OnDeviceConnectStatusCB 连接状态回调方法,自动读取相机连接状态

typedef void(__stdcall *OnDeviceConnectStatusCB)(const guide_usb_device_status_e device_status);

struct device_info

typedef struct
{
	int devID;							//设备 ID
	char devName[128];					//设备名称
} device_info;

struct device_info_list

typedef struct
{
	int devCount;						//设备数量
	device_info devs[32];				//设备信息
} device_info_list;

四、问题、小技巧

问题(数据类型需要对应,指针也是一样)

报错:在Qt的类中申明了OnFrameDataReceivedCB pFrameProc;
并且在主函数中将pFrameProc = FrameData_callback;
不能将"void (CameraControl:😗)(quide usb frame data tframe data)“类型的值分配到"OnFrameDataReceivedCB” 类型的实体。

解决方法
问题出在函数指针类型的匹配上。
OnFrameDataReceivedCB 定义的函数指针类型不兼容 void (CameraControl::)(guide_usb_frame_data_t) 这种成员函数指针类型。
原因是 CameraControl::
部分表示成员函数指针,而OnFrameDataReceivedCB 是一个全局函数指针类型。

想要使用成员函数指针,需要修改 OnFrameDataReceivedCB 的定义来匹配成员函数指针的类型。
可以将 OnFrameDataReceivedCB 定义为一个成员函数指针类型,例如:
typedef void (CameraControl::OnFrameDataReceivedCB)(guide_usb_frame_data_t frame_data);
这样,OnFrameDataReceivedCB 就可以匹配 void (CameraControl::
)(guide_usb_frame_data_t) 这种成员函数指针类型了。

代码规范:

命名:驼峰命名
类名,结构体名,函数名:大驼峰	class Student
类对象,变量:		小驼峰	int countNum
{ 前面加空格
;  , 后面加空格

Qt出现中文乱码

在头文件中添加 #pragma execution_character_set(“UTF-8”)

关于Qt图像处理

问题一:

QImage((const unsigned char*)(myImg.data), myImg.cols, myImg.rows, QImage::Format_RGB888).copy()

QImage((const unsigned char*)(myImg.data), myImg.cols, myImg.rows, QImage::Format_RGB888)

为什么用后者会有程序卡退和报错:(Qt5Gui.dll)处(位于 xxx.exe 中)引发的异常: 0xC0000005: 读取位置 XXXXXXXX 时发生访问冲突。这里copy()作用是啥?

解释:

QImage((const unsigned char*)(myImg.data), myImg.cols, myImg.rows, QImage::Format_RGB888).copy():

这行代码创建了一个 QImage 对象,并使用给定的参数构造了这个对象。然后调用了 copy() 方法,该方法返回了一个 QImage 对象的深拷贝,也就是说,它会复制原始图像的像素数据,而不仅仅是共享相同的数据。因此,这个操作会在内存中复制一份图像数据。

QImage((const unsigned char*)(myImg.data), myImg.cols, myImg.rows, QImage::Format_RGB888);

这行代码创建了一个 QImage 对象,但没有调用 copy() 方法。这意味着它只是使用了给定的参数直接构造了一个 QImage 对象,但没有额外的深拷贝操作。

因此,它与原始图像共享相同的像素数据,而不是创建了一个完全独立的副本。所以,主要区别在于第一行代码通过 copy() 方法创建了一个图像数据的深拷贝,而第二行代码则没有进行额外的拷贝操作,只是直接使用了原始图像数据。当你使用 QImage 构造函数直接传递 myImg.data 时,并且没有使用 copy() 方法,QImage 对象会尝试直接访问 myImg.data 指向的内存。如果 myImg 对象在 QImage 对象使用之后被释放或者销毁,那么 QImage 对象就会尝试访问无效的内存地址,从而导致程序崩溃或异常。

这种报错通常是由于内存访问错误引起的。在这种情况下,可能是因为 myImg 对象的生命周期问题导致了内存访问错误。

解决这个问题的一种方法是确保在 QImage 对象的生命周期内保持 myImg 对象有效(保证图像处理完之前不会无效)。
另一种方法是使用 copy() 方法创建 QImage 对象的深拷贝,以避免与原始图像数据的共享,从而减少潜在的生命周期问题(先缓存下来)。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐