QT图像处理:QImage与QPixmap

ops/2024/12/22 20:54:50/

图像表示和图像处理概述

颜色数据格式

图像数据可以看作是二维数组,数组每个元素就是1 像素的颜色数据,在绘图设备上显示图像就是设置每个像素的颜色。任何颜色在显像时都是红色、绿色、蓝色三原色的组合。

 颜色数据表示格式有以下几种:

RGB32:用 32 位无符号整数表示颜色,数据格式为 0xffRRGGBB,其中最高字节的 ff 是 无意义的,实际是用 24 位有效数据表示颜色。因为 32 位无符号整数(quint32)是标准的整数格式,所以在计算机上存储的图片文件一般采用这种格式表示像素的颜色。

RGB888:即红色、绿色、蓝色各用 1 字节表示,数据格式为 0xRRGGBB,1 像素的颜色占用 3 字节。但是 3 字节数据不是标准类型的整数,所以即使在内存中表示颜色使用了 RGB888 格式,在计算机上保存图片文件时也会使用 RGB32 格式。

ARGB32:在 RGB32 的基础上用 1 字节表示 Alpha 值,数据格式为 0xAARRGGBB。Alpha 值一般作为不透明度参数,Alpha 为 0 表示完全透明,Alpha 为 100 表示完全不透明。

RGBA32:与 ARGB32 类似,只是存储格式是 0xRRGGBBAA。

RGB565:用 16 位无符号整数(quint16)表示 1 像素的颜色,其中红色占 5 位,绿色占 6 位,蓝色占 5 位,1 像素只需用 2 字节就可以表示。一些嵌入式设备的 TFT-LCD 会用 RGB565 表示像素的颜色,这样可以节省存储空间,提高处理效率。

RGB565格式

Grayscale8:用 8 位无符号整数(quint8)表示 1 像素的灰度颜色。

Grayscale16:用 16 位无符号整数(quint16)表示 1 像素的灰度颜色。

图像文件格式

图像数据可以保存为不同格式的图片文件,常见的图片文件格式有 BMP、JPG、PNG、SVG 等。这些文件格式是在像素数据的基础上附加一些头部控制信息(如图像高度、宽度、颜色格式等),根据遵循的协议,封装成一个带后缀的文件。显示时根据图像的拆包协议可以提取压缩/非压缩的图像数据。

BMP 是位图文件格式,其文件头存储图像的一些信息,如图像宽度、高度、颜色数据格式等,图像中所有像素的颜色数据被无修改地保存在文件里,例如每个像素的颜色是一个RGB32 的数据。BMP 是一种无损图片文件格式,保留了图像的原始颜色数据。

JPG 是使用了联合图像专家组(joint photographic experts group,JPEG)图像压缩算法的图片文件格式,是一种有损压缩格式,可以在保持较高图像质量的情况下使文件大小减小很多,从而节省存储空间。

PNG便携式网络图形(portable network graphics)是一种无损压缩图片文件格式,它具有一定的压缩率,文件解压后就是真实的原图。PNG 图像的颜色数据可以有 Alpha 通道,例如颜色数据格式可以是 ARGB32。

SVG 是基于 XML,描述图像绘制方法的图片文件格式。SVG 文件存储的是绘制图像的过程,而不是图像的像素颜色数据。

BMP、JPG 和 PNG 都是基于图像中所有像素颜色数据的文件格式,BMP 是无压缩的位图文件格式,JPG 是有损压缩文件格式,PNG 是无损压缩文件格式。使用 QImage 和 QPixmap 类可以直接加载这 3 种格式的文件。

SVG 是基于 XML 的矢量图文件格式,不能用 QImage 和 QPixmap 类处理。要读取和显示 SVG 图片文件,需要使用 QSvgRenderer 和 QSvgWidget 类。

QImage 类与QPixmap类

QImage 是一种绘图设备类它可以读取 BMP、JPG、PNG 等格式的图片文件,存储图像中所有像素的颜色数据。QImage 的接口函数可以实现图像的缩放、旋转、镜像翻转等简单处理,可以转换颜色数据格式。因为 QImage 可以读写图像中每个像素的颜色数据,所以结合图像处理算法,我们可以对图像进行各种处理,例如调整亮度、调整对比度、模糊化处理等。

QPixmap是为实现在屏幕上显示图像而优化设计的类。QPixmap主要用于在界面上显示图像,它可以对图像 进行缩放,但是不能像 QImage 那样对图像进 行像素级的处理。

QImage

加载与保存图像数据

QImage(const QString &fileName, const char *format = nullptr) //指定图片文件名
QImage(int width, int height, QImage::Format format) //设置图像大小
QImage() //不设置任何参数

第一种指定图片文件名,创建的 QImage 对象会加载图片文件内的图像数据。参数 format 是图片文件的格式,用“BMP”“JPG”等字符串表示。如果不设置参数 format,程序会根据文件名的后缀自动判断图片文件格式。

第二种创建指定宽度和高度的图像,宽度和高度的单位是像素,参数 format 是像素的颜色数据的格式,是枚举类型 QImage::Format。创建后可以用来绘图(结合QPainter)和图像处理,类似于OpenCV中的Mat。

第三种创建 QImage 对象,之后一般会用函数 load()加载图片文件。可以从文件或其他 I/O 设备加载图像数据,也可以将图像数据保存为文件或保存到其他 I/O 设备中。

bool QImage::load(const QString &fileName, const char *format = nullptr) 
bool QImage::save(const QString &fileName, const char *format = nullptr, int quality = -1)

其中,参数 fileName 是图片文件名。参数 format 是图片文件格式,参数 quality 是保存为有损压缩图片文件(如 JPG 图片文件)时的品质参数,取值为 0 表示最低品质,压缩后文件最小;取值为 100 表示最高品质,图像无压缩;取值为-1 表示使用默认的品质参数。

另一组 load()和 save()函数可以从其他 I/O 设备加载数据和保存数据:

bool QImage::load(QIODevice *device, const char *format) 
bool QImage::save(QIODevice *device, const char *format = nullptr, int quality = -1)

其中,参数 device 是 I/O 设备,参数 format 是图片文件格式,参数 quality 是保存图片的品质参数。device 可以使用 QBuffer 类对象,QBuffer 是为 QByteArray 数据提供读写接口的 I/O 设备类。

QImage image("Save_as.png"); 
QByteArray ba; //字节数组
QBuffer buffer(&ba); //缓冲区对象
buffer.open(QIODevice::WriteOnly); //以只读模式打开缓冲区
image.save(&buffer, "PNG"); //将图像以 PNG 格式写入缓冲区,也就是写到字节数组 ba 里

函数 loadFromData()可以从字节数组中加载图像数据,其中一种定义如下:

bool QImage::loadFromData(const QByteArray &data, const char *format = nullptr)

其中,参数 data 是字节数组;format 是图片文件格式,这个函数中的参数 data 可以是用QFile::readAll()函数读取的图片文件的全部内容,也可以是用 QImage::save()保存到缓冲区中的数据。如果是在内存中复制 QImage 对象的图像数据,使用缓冲区是最高效的。

图像信息

QImage 提供的一些接口函数可以获取图像的一些信息:

图像格式:返回值是枚举类型 QImage::Format。详细查看qimage.h中关于Format的定义。

图像深度和位平面数:图像深度就是指 1 像素的颜色数据的位数,它与图像格式有关,例如RGB32 格式是 32 位,RGB565 格式是 16 位。位平面数就是指 1 像素的颜色和透明度数据的有效位数,它的值小于或等于图像深度值。例如对于 RGB32 格式,数据是 0xffRRGGBB,其图像深度是 32 位,但是位平面数是 24 位,因为只有 24 位数据有效。

图像的大小:QImage 的函数 width()和 height()分别返回图像的宽度和高度,单位是像素。函数 sizeInBytes()返回图像中所有像素的颜色数据所占用的字节数。其值等于width()×height()×depth()/8。

图像的分辨率:函数 dotsPerMeterX()和 dotsPerMeterY()分别返回图像在水平和垂直方向上的 DPM 分辨率。(1 DPI = 0.0254 DPM)通过设置图像的水平和垂直方向上的分辨率,可以调整图像大小,也可以改变长宽比。

图像数据访问

通过 QImage 的接口函数可以读写图像中每个像素的颜色数据,可以进行像素级的图像处理。像素颜色数据表示有直接和间接两种方式:8 位格式图像有颜色表,是间接方式;其他格式图像中每个像素直接用一个 QRgb 数据表示颜色。

读取像素颜色

使用函数 pixel()

QRgb QImage::pixel(int x, int y) //返回像素(x, y)的颜色数据

返回的颜色数据是 QRgb 类型的,QRgb 就是无符号 32 位整数,它以 0xAARRGGBB 的格式表示颜色的 Alpha 通道以及红色、绿色、蓝色对应的数值,我们用(a, r, g, b)表示这 4 种成分的数值。对于QRgb 类型的处理可以使用以下接口:

使用函数 pixelColor()

QColor QImage::pixelColor(int x, int y) //返回像素(x, y)的颜色数据

返回数据类型是 QColor,QColor 是表示颜色的类,它有一些接口函数可以获取颜色成分数值,也可以将 QColor 表示的颜色转换为 QRgb 类型的颜色:

QRgb QColor::rgb() //返回颜色的 QRgb 类型数据,Alpha 成分值是 255 
QRgb QColor::rgba() //返回颜色的 QRgb 类型数据,包括 Alpha 通道的值
int QColor::red() //返回颜色中红色成分的数值
int QColor::green() //返回颜色中绿色成分的数值
int QColor::blue() //返回颜色中蓝色成分的数值
int QColor::alpha() //返回颜色中 Alpha 通道的数值

QColor 有丰富的接口函数用于颜色数据处理,它可以用 RGB、HSV、CMYK 等模式表示颜色数据,可以修改颜色的饱和度和亮度。

设置像素颜色

使用函数 setPixel()和 setPixelColor()可以设置某个像素的颜色

void QImage::setPixel(int x, int y, uint index_or_rgb) 
void QImage::setPixelColor(int x, int y, const QColor &color)

函数 setPixel()设置像素(x, y)的颜色索引或 QRgb 颜色数据。对于带颜色表的图像,参数 index_or_rgb 是颜色索引;对于不带颜色表的图像,参数 index_or_rgb 是 QRgb 颜色数据值。 函数 setPixelColor()设置的颜色是 QColor 类型的,所以这个函数不适用于 8 位图像。

图像处理

QImage 提供了一些接口函数来对图像进行处理,例如镜像翻转、缩放、图像格式转换等,这

些接口函数如下表所示,表中省略了函数的输入参数。

示例程序解读

主窗口头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include    <QImage>
#include    <QPrinter>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTprivate:QString m_filename;  //当前图片文件名QImage  m_image;          //原始图像void  showImageFeatures(bool formatChanged=true);  //显示图像属性void  imageModified(bool modified=true);    //图像被修改了,改变actions状态void  printImage(QPainter *painter, QPrinter *printer);   //打印图像void  printRGB565Data(QPainter *painter, QPrinter *printer);  //打印RGB565数据public:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void do_paintRequestedImage(QPrinter *printer); //用于打印图片void do_paintRequestedText(QPrinter *printer);  //用于打印文本void on_actFile_Open_triggered();              //打开图像文件void on_actImg_RotateLeft_triggered();         //左旋void on_actImg_FlipUD_triggered();             //上下翻转void on_actImg_FlipLR_triggered();             //左右翻转void on_actImg_RotateRight_triggered();        //右旋void on_actFile_Reload_triggered();            //重新加载void on_btnFormatConvert_clicked();            //格式转换void on_btnGetRGB565_clicked();                //RGB565格式数据void on_btnSaveDataFile_clicked();             //保存RGB565void on_actFile_Save_triggered();              //保存图像void on_actFile_SaveAs_triggered();            //另存为void on_actFile_Print_triggered();             //打印void on_actFile_Preview_triggered();           //打印预览void on_actImg_ZoomIn_triggered();             //放大void on_actImg_ZoomOut_triggered();            //缩小private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

变量 m_filename 用于保存当前打开的图片文件名,变量 m_image 是当前图像对象。函数 printImage()用于打印图片,函数 printRGB565Data()用于打印文本数据。这里注意用到了打印功能,需要在项目配置文件(.pro 文件)中加入语句:QT += printsupport

打开文件

通过对话框选择一个图片文件后, 使用 QImage::load()函数加载图片文件。QImage 会自动解析图片文件格式,读取文件内的数据,在内存中存储图像中所有像素的颜色数据。为了在界面上的QLabel 组件上显示图像,程序还创建了一个 QPixmap 对象。QPixmap 是适合在界面上显示图片的绘图设备类。

void MainWindow::on_actFile_Open_triggered()
{//”打开”按钮QString  curPath= QDir::currentPath();   //应用程序当前目录QString  filter="图片文件(*.bmp *.jpg *.png);;""BMP文件(*.bmp);;JPG文件(*.jpg);;PNG文件(*.png)";QString  fileName=QFileDialog::getOpenFileName(this,"选择图片文件",curPath,filter);if (fileName.isEmpty())return;ui->statusbar->showMessage(fileName);m_filename=fileName;     //保存当前图片文件名QFileInfo  fileInfo(fileName);QDir::setCurrent(fileInfo.absolutePath());  //设置应用程序当前目录m_image.load(fileName);   //载入图片文件QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);      //在QLabel组件上显示图片ui->tabWidget->setCurrentIndex(0);showImageFeatures();    //显示图片属性ui->frameLeft->setEnabled(true);ui->actFile_SaveAs->setEnabled(true);ui->actImg_ZoomIn->setEnabled(true);ui->actImg_ZoomOut->setEnabled(true);ui->actImg_FlipLR->setEnabled(true);ui->actImg_FlipUD->setEnabled(true);ui->actImg_RotateLeft->setEnabled(true);ui->actImg_RotateRight->setEnabled(true);ui->actFile_Print->setEnabled(true);ui->actFile_Preview->setEnabled(true);
}

打开文件后,通过QImage类的format()函数获取图像的格式,然后读取深度(depth()函数)、位平面(bitPlaneCount()函数)、图像字节数(sizeInBytes()函数)、长宽(height()与width()函数)、DPM(dotsPerMeterX()与dotsPerMeterY()函数)等信息。并显示在视图组件中。

输入参数 formatChanged,其默认值为 true。当这个参数值为true 时,表示需要获取与图像格式相关的信息,包括图像格式、图像深度、是不是灰度图等信息,在图像格式变化后才需要获取这些信息。当参数 formatChanged 值为 false 时,只获取图像的宽度、高度、数据字节数等信息,在对图像进行翻转、缩放等处理后只需刷新显示这些信息。

//formatChanged, 改变了图像格式
void MainWindow::showImageFeatures(bool formatChanged)
{if (formatChanged)    //格式转换后需要显示全部信息{QImage::Format fmt=m_image.format();  //图像格式if (fmt == QImage::Format_RGB32)ui->editImg_Format->setText("32-bit RGB(0xffRRGGBB)");else if (fmt == QImage::Format_RGB16)ui->editImg_Format->setText("16-bit RGB565");else if (fmt == QImage::Format_RGB888)ui->editImg_Format->setText("24-bit RGB888");else if (fmt == QImage::Format_Grayscale8)ui->editImg_Format->setText("8-bit grayscale");else if (fmt == QImage::Format_Grayscale16)ui->editImg_Format->setText("16-bit grayscale");else if (fmt == QImage::Format_ARGB32)ui->editImg_Format->setText("32-bit ARGB(0xAARRGGBB)");else if (fmt == QImage::Format_Indexed8)ui->editImg_Format->setText("8-bit indexes into a colormap");elseui->editImg_Format->setText(QString("Format= %1,其他格式").arg(fmt));ui->editImg_Depth->setText(QString("%1 bits/pixel").arg(m_image.depth()));ui->editImg_BitPlane->setText(QString("%1 bits").arg(m_image.bitPlaneCount()));ui->chkBox_Alpha->setChecked(m_image.hasAlphaChannel());ui->chkBox_GrayScale->setChecked(m_image.isGrayscale());}//缩放,或旋转之后显示大小信息ui->editImg_Height->setText(QString("%1 像素").arg(m_image.height()));ui->editImg_Width->setText(QString("%1 像素").arg(m_image.width()));qsizetype sz =m_image.sizeInBytes();      //图像数据字节数if (sz<1024*9)ui->editImg_SizeByte->setText(QString("%1 Bytes").arg(sz));elseui->editImg_SizeByte->setText(QString("%1 KB").arg(sz/1024));QString dpi=QString::asprintf("DPI_X=%.0f, DPI_Y=%.0f",m_image.dotsPerMeterX()*0.0254,m_image.dotsPerMeterY()*0.0254);ui->editImg_DPM->setText(dpi);  //DPI分辨率}

图像保存、另存为、重载

保存选项是将当前处理的图像保存到源文件中(之前的m_filename保存了打开的文件路径名),会覆盖保存;另存为会打开一个文件会话框,可以设置与原文件名后缀不同的文件名,例如原来是BMP 文件,可以另存为 JPG 文件,QImage::save()函数会自动进行文件格式转换。重载图像就是放弃当前的图像处理结果,重新载入图像文件(通过m_filename文件路径名);

void MainWindow::on_actFile_Save_triggered()
{m_image.save(m_filename);  //保存到当前文件imageModified(false);
//    ui->actFile_Save->setEnabled(false);
//    ui->actFile_Reload->setEnabled(false);
}void MainWindow::on_actFile_SaveAs_triggered()
{
//    QString  filter="图片文件(*.bmp *.jpg *.png);;BMP文件(*.bmp);;JPG文件(*.jpg);;PNG文件(*.png)";QString  filter= "图片文件(*.bmp *.jpg *.png);;""BMP文件(*.bmp);;JPG文件(*.jpg);;PNG文件(*.png)";QString  fileName=QFileDialog::getSaveFileName(this,"保存文件",m_filename,filter);if (fileName.isEmpty())return;m_image.save(fileName);  //保存到新的文件m_filename= fileName;    //重新设置当前文件名ui->statusbar->showMessage(fileName);imageModified(false);
}void MainWindow::on_actFile_Reload_triggered()
{//重新载入QString fileName =m_filename;m_image.load(fileName);       //从当前文件重新载入QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);  //刷新图像显示ui->tabWidget->setCurrentIndex(0);showImageFeatures(true);    //显示全部属性imageModified(false);       //设置按钮状态
}

图像格式转换

对图像进行图像格式转换,也就是改变像素颜色数据的表示格式。从下拉列表框中选择目标格式,然后点击“图像格式转换”按钮就可以进行图像格式转换。对应的槽函数如下:

void MainWindow::on_btnFormatConvert_clicked()
{//图像格式转换
//    QImage newImage;int index=ui->comboFormat->currentIndex();if (index ==0)m_image.convertTo(QImage::Format_RGB16);      //RGB565else if (index ==1)m_image.convertTo(QImage::Format_RGB888);     //RGB888else if (index ==2)m_image.convertTo(QImage::Format_RGB32);      //RGBx888else if (index ==3)
//        newImage = image.convertToFormat(QImage::Format_Grayscale8);        //不改变原图
//    newImage = image.convertedTo(QImage::Format_Grayscale8);        //不改变原图像m_image.convertTo(QImage::Format_Grayscale8); //8位灰度else if (index ==4)m_image.convertTo(QImage::Format_Grayscale16);//16位灰度else if (index ==5)m_image.convertTo(QImage::Format_Indexed8);   //8位索引elsereturn;QPixmap  pixmap=QPixmap::fromImage(m_image);      //刷新界面的图像显示ui->labPic->setPixmap(pixmap);showImageFeatures(true);    //显示全部信息imageModified(true);        //图像被修改了
}

程序里使用 QImage::convertTo()函数进行图像格式转换,这个函数的原型定义如下:

void QImage::convertTo(QImage::Format format, Qt::ImageConversionFlags flags = Qt::AutoColor)

其中,参数 format 是需要转换的目标格式,参数 flags 控制格式转换的处理方法,一般使用默认值 即可。函数 convertTo()没有返回值,转换后的图像会覆盖原来的图像。

图像处理

工具栏上有一些按钮可以对图像进行缩放、旋转或翻转处理。

图像缩放:图像缩放可以使用函数 scaled()来实现,该函数原型定义如下:

QImage QImage::scaled(int width, int height,Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation)

其中,参数 width 和 height 分别是缩放后的新图像的宽度和高度,单位是像素;参数aspectRatioMode 控制是否保持图像的长宽比,默认值 Qt::IgnoreAspectRatio 表示忽略长宽比,也可以设置为保持长宽比, 也就是设置值为 Qt::KeepAspectRatio;参数 transformMode 表示变换模式,默认值 Qt::FastTransformation表示快速转换,不做平滑处理,也可以设置值为 Qt::SmoothTransformation,表示进行平滑处理。

void MainWindow::on_actImg_ZoomIn_triggered()
{//放大int W=m_image.width();int H=m_image.height();m_image=m_image.scaled(1.1*W, 1.1*H,Qt::KeepAspectRatio);   //放大QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);      //重新设置pixmap,将清除之前的内容showImageFeatures(false);imageModified(true);
}void MainWindow::on_actImg_ZoomOut_triggered()
{//缩小int W=m_image.width();int H=m_image.height();m_image=m_image.scaled(0.9*W, 0.9*H,Qt::KeepAspectRatio);   //缩小QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);      //刷新界面的图像显示showImageFeatures(false);imageModified(true);
}

函数 scaled()返回缩放后的图像副本,原图像不变。示例程序中将缩放后的图像又保存到原图

像,所以会改变图像的物理尺寸。

图像缩放还可以使用函数 scaledToHeight()和 scaledToWidth()来实现,它们分别用于指定高度

或宽度进行缩放,函数原型定义如下:

QImage QImage::scaledToHeight(int height, Qt::TransformationMode mode = Qt::FastTransformation)

QImage QImage::scaledToWidth(int width, Qt::TransformationMode mode = Qt::FastTransformation)

图像旋转

函数 transformed()可以通过一个变换矩阵对图像进行任意的变换,其函数原 型定义如下:

QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation)

参数 matrix 是 QTransform 类型的变换矩阵。变换矩阵是一个 3×3 的矩阵,可以表示坐标系的平移、缩放、旋转等坐标变换运算关系。


void MainWindow::on_actImg_RotateLeft_triggered()
{//左旋90度QTransform matrix;matrix.reset();     //单位矩阵matrix.rotate(-90); //默认Qt::ZAxism_image=m_image.transformed(matrix);    //使用变换矩阵matrix进行图像变换QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);  //刷新界面的图像显示showImageFeatures(false);       //只刷新显示图像尺寸相关信息imageModified(true);
}void MainWindow::on_actImg_RotateRight_triggered()
{//右旋90度QTransform matrix;matrix.reset();     //单位矩阵matrix.rotate(90);  //默认Qt::ZAxism_image=m_image.transformed(matrix);    //使用变换矩阵matrix进行图像变换QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);      //刷新界面的图像显示showImageFeatures(false);           //只刷新显示图像尺寸相关信息imageModified(true);
}
void MainWindow::on_actImg_RotateLeft_triggered()
{//左旋90度QTransform matrix;matrix.reset();     //单位矩阵matrix.rotate(-90); //默认Qt::ZAxism_image=m_image.transformed(matrix);    //使用变换矩阵matrix进行图像变换QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);  //刷新界面的图像显示showImageFeatures(false);       //只刷新显示图像尺寸相关信息imageModified(true);
}void MainWindow::on_actImg_RotateRight_triggered()
{//右旋90度QTransform matrix;matrix.reset();     //单位矩阵matrix.rotate(90);  //默认Qt::ZAxism_image=m_image.transformed(matrix);    //使用变换矩阵matrix进行图像变换QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);      //刷新界面的图像显示showImageFeatures(false);           //只刷新显示图像尺寸相关信息imageModified(true);
}

图像镜像

函数 mirror()可以对图像进行镜像处理,该函数定义如下:

void QImage::mirror(bool horizontal = false, bool vertical = true)

其中,参数 horizontal 表示是否进行水平镜像,参数 vertical 表示是否进行垂直镜像。函数没有返 回值,直接修改原图像。

还有一个函数 mirrored()可以对图像进行镜像处理,它返回处理后的图像副本,不修改原图像。

QImage QImage::mirrored(bool horizontal = false, bool vertical = true)

void MainWindow::on_actImg_FlipUD_triggered()
{//上下翻转bool horizontal=false;bool vertical=true;m_image.mirror(horizontal,vertical);  //图像镜像处理QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);imageModified(true);
}void MainWindow::on_actImg_FlipLR_triggered()
{//左右翻转bool horizontal=true;bool vertical=false;m_image.mirror(horizontal,vertical);  //图像镜像处理QPixmap  pixmap=QPixmap::fromImage(m_image);ui->labPic->setPixmap(pixmap);imageModified(true);
}

生成 与保存RGB565 数据

对图像从上到下、从左到右进行处理,读取每个像素的颜色数据,然后将其转换成十六进制字符串。通过 QImage::pixel()函数获取某个像素的颜色,无论图像是什么格式,函数 pixel() 返回的数据类型 QRgb 的格式都是 0xAARRGGBB。通过 qRed()、qGreen()和 qBlue()函数分别获取 QRgb 数据中的红色、绿色和蓝色成分,并分别取其有效的高 5 位、高 6 位、高 5 位。最后提取RGB565 数据的低字节和高字节数据。

在将 byteLSB 和 byteMSB 组合成字符串时,还会根据界面上单选按钮的选择,设置为低字节在前或高字节在前。可以点击保存位C语言头文件。

void MainWindow::on_btnGetRGB565_clicked()
{ui->plainText->clear();int W=m_image.width();int H=m_image.height();int total=2*W*H;        //总数据字节数QFileInfo  fileInfo(m_filename);QString arrayName=fileInfo.baseName();  //不带后缀的文件名QString aLine=QString("const unsigned char RGB565_%1[%2] = {").arg(arrayName).arg(total);ui->plainText->appendPlainText(aLine);QString   onePixel; //一个像素的2字节16进制数据字符串QChar   ch0('0');   //用于填充的字符int base=16;        //16进制int count=0;        //单行像素个数计数for (int y=0; y<H; y++)     //从上到下逐行处理{QApplication::processEvents();for (int x=0; x<W; x++) //从左到右逐个像素处理{QRgb rgb=m_image.pixel(x,y);   //一个像素的RGB颜色, 格式 0xAARRGGBB/自己处理,有效
//            quint32 tmp32 = rgb & 0x00F80000;   //取red高5位
//            rgb565 = tmp32>>8;          //Red5
//            tmp32 = rgb & 0x0000FC00;       //取green高6位
//            rgb565 = rgb565 | (tmp32>>5);   //R5G6
//            tmp32 = rgb & 0x000000F8;   //取blue高5位
//            rgb565 =rgb565 | (tmp32>>3);    //RGB565
//            quint8 byteLSB = rgb565 & 0x00FF;
//            quint8 byteMSB = rgb565>>8;//使用qRed()等函数,有效quint16 red  =qRed(rgb)   & 0x00F8; //取高5位quint16 green=qGreen(rgb) & 0x00FC; //取高6位quint16 blue =qBlue(rgb)  & 0x00F8; //取高5位quint16 rgb565=(red<<8) | (green <<3) | (blue>>3);  //RGB565数据quint8 byteLSB = rgb565 & 0x00FF;   //低字节quint8 byteMSB = rgb565>>8;         //高字节//if (ui->radioLSB->isChecked())      //低字节在前//2为字宽,例如0xff字宽为2,0x0ff字宽为3,base为基数(进制),ch0为填充的字符onePixel += QString("0x%1,0x%2,").arg(byteLSB, 2, base,ch0).arg(byteMSB, 2, base,ch0);
//            onePixel=onePixel+QString("0x%1,0x%2,").arg(byteLSB, 2, base,ch0).arg(byteMSB, 2, base,ch0);elseonePixel += QString("0x%1,0x%2,").arg(byteMSB, 2, base,ch0).arg(byteLSB, 2, base,ch0);
//            onePixel=onePixel+QString("0x%1,0x%2,").arg(byteMSB, 2, base,ch0).arg(byteLSB, 2, base,ch0);count++;if (count==8)   //每行只填8个像素的数据{onePixel = onePixel.toUpper();onePixel = onePixel.replace(QChar('X'),"x");//使用小写的0x为16进制前缀ui->plainText->appendPlainText(onePixel);onePixel="";count=0;}}}if (count>0)    //最后不足8个像素的数据{onePixel = onePixel.toUpper();onePixel = onePixel.replace(QChar('X'),"x");ui->plainText->appendPlainText(onePixel);}ui->plainText->appendPlainText("};");   //数组结尾ui->tabWidget->setCurrentIndex(1);      //切换tabui->btnSaveDataFile->setEnabled(true);QMessageBox::information(this,"提示","RGB565数据生成已完成");
}void MainWindow::on_btnSaveDataFile_clicked()
{QFileInfo fileInfo(m_filename);QString  newName=fileInfo.baseName()+".h";       //更改文件后缀QString  filter="C语言头文件(*.h);;C语言程序文件(*.c);;文本文件(*.txt)";QString  fileName=QFileDialog::getSaveFileName(this,"保存文件",newName,filter);if (fileName.isEmpty())return;//    QFileInfo fileInfo(imageFilename);
//    QString  newName=fileInfo.fileName();   //去除路径的文件名
//    fileInfo.setFile(newName);
//    newName=fileInfo.baseName()+".h";       //更改文件后缀
//    newName=QDir::currentPath()+"\\"+newName;   //应用程序当前目录
//    QString  filter="C语言头文件(*.h);;C语言程序文件(*.c);;文本文件(*.txt)";
//    QString  fileName=QFileDialog::getSaveFileName(this,"保存文件",newName,filter);//    if (fileName.isEmpty())
//        return;
//    fileInfo.setFile(fileName);
//    QDir::setCurrent(fileInfo.absolutePath());  //设置应用程序当前目录QFile   aFile(fileName);if (aFile.open(QIODevice::WriteOnly | QIODevice::Text)){QString str=ui->plainText->toPlainText();	//整个内容作为字符串QByteArray  strBytes=str.toUtf8();          //转换为字节数组, UTF-8编码aFile.write(strBytes,strBytes.length());    //写入文件aFile.close();}
}

打印功能

打印相关的类

QPrinter (父类为QPagedPaintDevice)是实现打印功能的绘图设备类,打印输出实际上就是在 QPrinter 上用 QPainter 绘制各种图形和文字。QPagedPaintDevice 还有另一个子类QPdfWriter,使用 QPdfWriter 类可以创建 PDF 文件,将内容打印到 PDF 文件里。

QPrintDialog 是打印设置对话框类,使用这个对话框类可以对 QPrinter 对象的各种主要属性进行设置,包括选择打印机,设置纸张大小、纸张方向、打印页面范围、打印份数、是否双面打印等。还有一个打印预览对话框类 QPrintPreviewDialog,它可以实现打印预览功能。

QPagedPaintDevice 类定义了多页打印输出的一些基本函数如下(表中省略了函数的输入参数):

这些接口函数用于设置页面布局,包括纸张大小、纸张方向、页边距等,还可以设置打印页面范围,在新建页面之前需要设置好页面布局。

使用 QPrintDialog 对话框类可以对一个 QPrinter 对象进行设置,包括打印机和打印页面的设置。在打印对话框确认后,就可以为 QPrinter 对象设置 QPainter 画笔,实际上打印就是在页面上用 QPainter 输出文字或绘制图像。

打印预览功能

Qt 中有一个打印预览对话框类 QPrintPreviewDialog,它可以实现打印预览功能。它的父类是

QDialog,所以它是一个独立的对话框。要实现打印预览,关键是要为 QPrintPreviewDialog 的

paintRequested()信号关联一个槽函数,信号函数的输入参数 printer 是当前使用的打印机。在实现槽函数时,可以为这个 printer 创建QPainter 对象,然后在 printer 上输出打印内容。


void MainWindow::on_actFile_Preview_triggered()
{//”打印预览”按钮QPrintPreviewDialog  previewDlg(this);    //打印预览对话框previewDlg.setWindowFlag(Qt::WindowMaximizeButtonHint); //具有最大化按钮if (ui->tabWidget->currentIndex() == 0)connect(&previewDlg, SIGNAL(paintRequested(QPrinter *)),this, SLOT(do_paintRequestedImage(QPrinter *)));elseconnect(&previewDlg, SIGNAL(paintRequested(QPrinter *)),this, SLOT(do_paintRequestedText(QPrinter *)));previewDlg.exec();  //以模态方式显示对话框
}void MainWindow::do_paintRequestedImage(QPrinter *printer)
{QPainter painter(printer);    //打印机的画笔printImage(&painter, printer);
}void MainWindow::do_paintRequestedText(QPrinter *printer)
{QPainter painter(printer);    //打印机的画笔printRGB565Data(&painter, printer);
}

上述代码中分别为打印图片和打印RGB565数据设置了预览对话框,并根据当前的tab页面分别连接槽函数,由于这里QPrintPreviewDialog对话框是局部创建的。所以每次点击Action时,才会连接创建的对话框中信号与槽函数,执行exec()函数后,该对话框会同时发送paintRequested信号,槽函数便会运行。


void MainWindow::printImage(QPainter *painter, QPrinter *printer)
{//打印图像QMargins margin(20,40,20,40);   //上下左右4个边距,单位:像素QRectF pageRect=printer->pageRect(QPrinter::DevicePixel);   //单位:设备像素int pageW=pageRect.width();     //打印页面的宽度int pageH=pageRect.height();const int lineInc=20;   //一行文字所占的行高度,单位:像素int curX=margin.left(); //当前X坐标int curY=margin.top();  //当前Y坐标painter->drawText(curX,curY,m_filename);   //打印图片文件名curY += lineInc;    //移到下一行painter->drawText(curX,curY,QString("Page width =%1 像素").arg(pageW));painter->drawText(200,curY,QString("Image width =%1 像素").arg(m_image.width()));curY += lineInc;painter->drawText(curX,curY,QString("Page height=%1 像素").arg(pageH));painter->drawText(200,curY,QString("Image height=%1 像素").arg(m_image.height()));curY += lineInc;int spaceH= pageH-curY;  //页面剩余的高度//图像未超过页面范围,居中显示实际大小的图片if ((pageW >m_image.width()) && (spaceH >m_image.height())){curX =(pageW-m_image.width())/2;          //使水平居中painter->drawImage(curX,curY,m_image);    //打印图像return;}//否则图像高度或宽度超过了页面剩余空间,缩放后打印QImage newImg;if (m_image.height() > m_image.width())newImg =m_image.scaledToHeight(spaceH);   //按高度缩放elsenewImg =m_image.scaledToWidth(pageW);     //按宽度缩放curX =(pageW-newImg.width())/2;             //使水平居中painter->drawImage(curX,curY,newImg);       //打印图像
}void MainWindow::printRGB565Data(QPainter *painter, QPrinter *printer)
{//打印文档QMargins margin(20,40,20,40);   //上下左右4个边距,单位:像素QRectF pageRect=printer->pageRect(QPrinter::DevicePixel); //单位:设备像素int pageW=pageRect.width();     //打印页面的宽度,像素int pageH=pageRect.height();const int lineInc=25;   //一行文字所占的行高度,单位:像素int curX=margin.left(); //当前X坐标int curY=margin.top();  //当前Y坐标QFont font=ui->plainText->font();painter->setFont(font);       //设置打印字体int pageNum=1;      //打印页面编号painter->drawLine(margin.left(), pageH- margin.bottom()+1,            //页脚划线pageW-margin.right(), pageH- margin.bottom()+1);painter->drawText(pageW-5*margin.right(),pageH-margin.bottom()+20,    //页脚页面编号QString("第 %1 页").arg(pageNum));QTextDocument* doc=ui->plainText->document();       //文本对象int cnt=doc->blockCount();      //回车符是一个blockfor (int i=0; i<cnt; i++)       //逐行读取文字,逐行打印{QTextBlock  textLine =doc->findBlockByNumber(i);   // 文本中的一段QString str=textLine.text();           //一行文字painter->drawText(curX,curY,str);      //打印文字curY += lineInc;           //换到下一行if (curY>= (pageH-margin.bottom()))    //需要换页{printer->newPage();    //新建一个打印页curY=margin.top();     //一页的首行位置pageNum++;             //页面编号++painter->drawLine(margin.left(), pageH- margin.bottom()+1,   //页脚划线pageW-margin.right(), pageH- margin.bottom()+1);painter->drawText(pageW-5*margin.right(),pageH-margin.bottom()+20,QString("第 %1 页").arg(pageNum));            //页脚页面编号}}
}

打印图像关键点是要设置好图像的宽高以适配打印纸张的大小,打印文本关键点是设置好合适的换行和换页。

打印

打印与打印预览的区别主要是对话框的区别,打印预览使用的是QPrintPreviewDialog对话框,而打印使用的是QPrintDialog对话框;最终的打印函数与打印预览部分是共用的。

void MainWindow::on_actFile_Print_triggered()
{//"打印"按钮QPrinter printer;QPrintDialog pritnDialog(&printer,this);    //打印设置对话框if (pritnDialog.exec()==QDialog::Accepted){QPainter painter(&printer);     //打印机的画笔,并传入打印机对象if (ui->tabWidget->currentIndex() == 0)printImage(&painter, &printer);         //打印图像elseprintRGB565Data(&painter, &printer);    //打印文本}
}

打印相关的内容仅是基本演示,实际的设计还是比较复杂的,更专业的设计软件才需要。

参考

Qt 6 C++开发指南


http://www.ppmy.cn/ops/144120.html

相关文章

【工具变量】中国数字经济发展水平面板数据DID(2012-2022)

数据来源&#xff1a;《中国统计年鉴》、国家统计局 时间跨度&#xff1a;2012-2022年 数据范围&#xff1a;中国各省 包含指标&#xff1a; 1. 地区 2. id 3. 年份 4. 互联网域名数 5. 互联网接入端口数 6. 互联网宽带接入用户数 7. 移动基站密度 8. 移动电…

基于ceres优化的3d激光雷达开源算法

以下是一些基于CERES优化的开源激光雷达SLAM或相关算法&#xff1a; (1) LOAM (Lidar Odometry And Mapping) 简介: LOAM是一种经典的激光雷达里程计和建图算法&#xff0c;它通过提取特征点&#xff08;角点和平面点&#xff09;&#xff0c;利用ICP&#xff08;Iterative Cl…

金智塔科技喜获CCF中国数字金融大会 GraphRAG竞赛二等奖

12月7日&#xff0c;CCF 首届中国数字金融大会GraphRAG竞赛在上海落下帷幕&#xff0c;金智塔科技&#xff08;团队名称&#xff1a;塔塔向前冲&#xff09;从众多参赛队伍中脱颖而出&#xff0c;喜获二等奖。 CCF 首届中国数字金融大会由中国计算机学会主办&#xff0c;中国计…

ParrotOS,一个与kali类似的渗透测试操作系统

介绍 Parrot Security&#xff08;ParrotOS&#xff0c;Parrot&#xff09;是一个基于 Debian Stable 的免费开源 GNU/Linux 发行版&#xff0c;专为安全专家、开发人员和注重隐私的人设计。 它包括一个完整的便携式武器库&#xff0c;用于 IT 安全和数字取证操作。它还包括开…

《 QT 5.14.1 类库模块列表详述》

《 QT 5.14.1 类库模块列表详述》 一、引言&#xff08;一&#xff09;简述 QT 5.14.1 类库的重要性&#xff08;二&#xff09; QT 5.14.1 基本模块列表 二、QT 5.14.1 基本模块&#xff08;一&#xff09;Qt Core 模块&#xff08;二&#xff09;Qt GUI 模块&#xff08;三&a…

SAGA 软件阴影计算功能介绍

自动化地球科学分析系统&#xff08;SAGA GIS&#xff09;是一种地理信息系统&#xff08;GIS&#xff09;计算机程序&#xff0c;用于编辑空间数据。它是免费的开源软件&#xff0c;最初由德国哥廷根大学自然地理系的一个小团队开发&#xff0c;现在由国际开发人员社区进行维护…

Spark优化----Spark 性能调优

目录 常规性能调优 常规性能调优一&#xff1a;最优资源配置 常规性能调优二&#xff1a;RDD 优化 RDD 复用 RDD 持久化 RDD 尽可能早的 filter 操作 常规性能调优三&#xff1a;并行度调节 常规性能调优四&#xff1a;广播大变量 常规性能调优五&#xff1a;Kryo 序列化 常规性…

文心一言对接FreeSWITCH实现大模型呼叫中心

文心一言对接FreeSWITCH实现大模型呼叫中心 作者&#xff1a;开源大模型智能呼叫中心FreeIPCC&#xff0c;Github&#xff1a;https://github.com/lihaiya/freeipcc 随着人工智能技术的快速发展&#xff0c;特别是大规模语言模型&#xff08;LLM&#xff09;的应用&#xff0…