图像处理软件设计 基于Qt5/openCV3(未完成)

前言

Qt, 作为软件的UI开发工具, 主要负责的是功能的交互和图片的展示

openCV作为软件的后端数据处理工具,主要用起数据库进行图像处理,相当于把openCV的原装库,在该软件中进行适配,封装成新的类库,供程序调用使用;

Mat和QImage

OpenCV中的图像主要存储在Mat类中,要让其显示在Qt的Label控件上,必须先将其转换为Qt的QImage类

Mat类图像是按照BGR顺序存储的图像,而QImage是按照RGB顺序存储的,在类型转换前需要将通道更改。

Mat::data

        图像矩阵是一个二维数组,不论是灰度图像还是彩色图像,在计算机内存中都是以一维数组的形式存储的。用Mat存储一幅图像时,若图像在内存中是连续存储的(Mat对象的isContinuous == true),则可以将图像的数据看成是一个一维数组,而其data(uchar*)成员就是指向图像数据的第一个字节的,因此可以用data指针访问图像的数据,

        那么问题来了,OpenCV中将data定义为uchar*,而当我们用构造函数创建一个Mat对象的时候,可以指定图像的数据类型有CV_8UC1、CV_8UC3、CV_32FC1、CV_32FC3等多种,那么我们如何通过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中连接了信号和槽,那么槽函数就会执行两次。如果三个都使用了,槽函数就会执行三次
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值