前言
Qt, 作为软件的UI开发工具, 主要负责的是功能的交互和图片的展示
openCV作为软件的后端数据处理工具,主要用起数据库进行图像处理,相当于把openCV的原装库,在该软件中进行适配,封装成新的类库,供程序调用使用;
Mat和QImage
OpenCV中的图像主要存储在Mat类中,要让其显示在Qt的Label控件上,必须先将其转换为Qt的QImage类。
Mat类图像是按照BGR顺序存储的图像,而QImage是按照RGB顺序存储的,在类型转换前需要将通道更改。
Mat::data
data(uchar*)成员就是指向图像数据的第一个字节的,因此可以用data指针访问图像的数据,
如何通过data指针去访问和修改图像的某一个像素值呢,
对于数据为uchar类型的Mat对象,可以直接用data访问和修改,
对于数据为float或double类型的Mat对象,我们同样可以用data对图像的某个像素值进行访问和修改操作,方法就是将data强制转换成指向Mat对象对应数据类型的指针。
例如我们创建一个10x10的CV_8UC1的Mat对象,将其每个元素都赋值为10(int),然后输出每个元素的值,这里我们用data进行相关操作,代码和运行结果如下:
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat img(10,10,CV_8UC1);
for (int i=0;i<img.rows;i++)
{
for (int j=0;j<img.cols;j++)
{
int* data = (int*)img.data;
data[i] = 10;
}
}
if (img.isContinuous()) {
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
int* data = (int*)img.data;
cout << data[i] << " ";
}
}
}
system("pause");
return 0;
}
//simple
int main()
{
using namespace cv;
using namespace std;
Mat dst = (Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cout << dst << endl;
int* a =(int*)dst.data;
cout <<a[2]<< endl;
return 0;
}
Mat::step
step的几个类别区分:
- step:矩阵第一行元素的字节数
- step[0]:矩阵第一行元素的字节数
- step[1]:矩阵中一个元素的字节数
- step1(0):矩阵中一行有几个通道数
- step1(1):一个元素有几个通道数(channel())
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
using namespace cv;
using namespace std;
Mat dst = (Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cout << dst << endl;
int* a =(int*)dst.data;
cout <<a[2]<< endl;
cout <<"矩阵第一行元素的字节数: "<< dst.step << endl; //矩阵第一行元素的字节数
cout << "矩阵第一行元素的字节数: "<<dst.step[0] << endl; //矩阵第一行元素的字节数
cout << "矩阵第一个元素的字节数: "<<dst.step[1] << endl; //矩阵第一元素的字节数
cout <<"矩阵中一行有几个通道数: "<< dst.step1(0)<< endl;
cout <<"一个元素有几个通道数(channel()): "<< dst.step1(1) << endl;
return 0;
}
QImage
Qt中利用QImage来存储图片
Mat和QImage都给出了相关参数的获取和设置方法,上述的Mat::data对应此处的data, Mat::step对应此处的bytersPerLine
QImage::QImage(const uchar *data,
int width, int height,
int bytesPerLine,
QImage::Format format,
QImageCleanupFunction cleanupFunction = nullptr,
void *cleanupInfo = nullptr);
QImage和Mat对象转换的简单例子
该例子之所以说简单,是因为:
一般来说,还应该考虑Mat的元素类型,比如有CV_8UC1,CV_16FC1等,对于不同的类型应进行不同的转换操作
//e.g.
void MatdisplayinQt::on_pushButton_clicked()
{
srcImg = imread("00.jpg");
cvtColor(srcImg, grayImg, CV_BGR2GRAY);
Mat temp;
QImage Qtemp;
if (!isGray)
{
cvtColor(srcImg, temp, CV_BGR2RGB);//BGR convert to RGB
Qtemp = QImage((const unsigned char*)(temp.data),
temp.cols, temp.rows,
temp.step,
QImage::Format_RGB888);
}
else
{
cvtColor(grayImg, temp, CV_GRAY2RGB);//GRAY convert to RGB
Qtemp = QImage((const unsigned char*)(temp.data), temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
}
ui.label->setPixmap(QPixmap::fromImage(Qtemp));
ui.label->resize(Qtemp.size());
ui.label->show();
}
其实,格式转换无非就是找到格式间参数的对应关系:
QImage Mat
数据指针 uchar* bits() uchar* data
宽度 int width() int cols
高度 int height() int rows
步长 int bytesPerLine() cols * elemeSize()
格式 Format_Indexed8 8UC1, GRAY,灰度图
Format_RGB888 8UC3, BGR, 需要通过mixChannels进行顺序调换
Format_ARGB32 8UC4, BGRA,需要通过mixChannels进行顺序调换
以此类推,只要保证通道数以及排列顺序一致即可。
在转换的过程中一定要先判断图像的格式,QImage用QImage::format(), Mat中用Mat::channels(),然后再设置相应的转换参数
namespace QtOcv {
/* Convert QImage to/from cv::Mat without data copy
*
* - Supported QImage formats and cv::Mat types are:
* - QImage::Format_Indexed8 <==> CV_8UC1
* - QImage::Format_Alpha8 <==> CV_8UC1
* - QImage::Format_Grayscale8 <==> CV_8UC1
* - QImage::Format_RGB888 <==> CV_8UC3 (R G B)
* - QImage::Format_RGB32 <==> CV_8UC4 (A R G B or B G R A)
* - QImage::Format_ARGB32 <==> CV_8UC4 (A R G B or B G R A)
* - QImage::Format_ARGB32_Premultiplied <==> CV_8UC4 (A R G B or B G R A)
* - QImage::Format_RGBX8888 <==> CV_8UC4 (R G B A)
* - QImage::Format_RGBA8888 <==> CV_8UC4 (R G B A)
* - QImage::Format_RGBA8888_Premultiplied <==> CV_8UC4 (R G B A)
*
* - For QImage::Format_RGB32 and QImage::Format_ARGB32, the
* color channel order of cv::Mat will be (B G R A) in little
* endian system or (A R G B) in big endian system.
*
* - User must make sure that the color channels order is the same as
* the color channels order required by QImage.
*/
cv::Mat image2Mat_shared(const QImage &img, MatColorOrder *order=0);
QImage mat2Image_shared(const cv::Mat &mat, QImage::Format formatHint = QImage::Format_Invalid);
} //namespace QtOcv
QImage::bits()
bits() 结果的结构布局跟它的 format() 是相关的,但是跟图片文件的格式基本是无关的,所以和bmp文件的数据不一样是正常的。也就是说 QImage 在从图片文件加载后,会根据它的 format 转换成特定的内部格式。 如果你关心的是像素值的话,就不应该直接使用 bits(),或者转换成 Format_RGB32 再利用 qRed(), qGreen(), qBlue(), qRgb() 之类的来访问像素。 如果你不关心像素的具体值(比如我以前做的一个图像加密算法,只是打乱数据的顺序),直接用 bits()倒也无所谓。 |
槽函数调用两次问题:
关联方法:
1.Qt翻译机制
用官方的槽函数写法“ on_对象名称_信号类型 ”可以不用写connect函数,可以直接触发槽函数。
例如:
on_btn_pressed()
on_photoGrid_triggered()
2.Connect连接方法
利用Connect函数连接对象和槽
形如:
connect(ui->btn,signal(pressed()),this,slot(on_btn_pressed()),qt::uniqueconnection);
connect(ui.photoGrid, SIGNAL(triggered()), this, SLOT(on_photoGrid_triggered()));
3.Qt designer中编辑信号和槽
在Qt designer中添加槽函数,需要在界面上右击,弹出菜单,选择“改变信号/槽”,然后弹出下图。点击加号,添加自定义的信号和槽函数。再到CPP文件中去声明和实现同名槽函数。
注意给槽函数起名时,不要按照Qt的翻译机制起名,下图就是一个错误的示范。
然后在信号/槽编辑器中连接信号和槽,如果是自定义的槽函数,接受者要选当前类
4.注意
以上三种方法都能实现信号和槽的连接,但不要同时使用,只要选择一种即可。同时使用就会出现槽函数执行多次的错误,例如自定义槽函数的名称是按照Qt翻译机制起名的,然后又Connect()了,或者在Designer中连接了信号和槽,那么槽函数就会执行两次。如果三个都使用了,槽函数就会执行三次