Qt图像处理程序,实现灰度化、锐化、反色、旋转、缩放等功能,包含完整的UI界面和图像处理算法。
一、系统架构
Qt图像处理系统架构:
├── 图像加载模块
│ ├── 支持JPG/PNG/BMP/GIF等格式
│ ├── 批量加载
│ └── 图像预览
├── 图像处理模块
│ ├── 灰度化(加权/平均/最大值)
│ ├── 锐化(拉普拉斯/索贝尔/自定义卷积)
│ ├── 反色(负片效果)
│ ├── 旋转(任意角度)
│ ├── 缩放(多种插值算法)
│ └── 其他滤镜(模糊、边缘检测等)
├── 图像显示模块
│ ├── 自适应显示
│ ├── 缩放和平移
│ ├── 直方图显示
│ └── 对比度增强
└── 文件操作模块
├── 打开/保存
├── 批量处理
└── 历史记录
二、核心代码实现
2.1 图像处理类 (ImageProcessor.h)
cpp
#ifndef IMAGEPROCESSOR_H
#define IMAGEPROCESSOR_H
#include <QObject>
#include <QImage>
#include <QColor>
#include <QVector>
#include <QPoint>
#include <QSize>
#include <QDebug>
// 图像处理类
class ImageProcessor : public QObject
{
Q_OBJECT
public:
explicit ImageProcessor(QObject *parent = nullptr);
~ImageProcessor();
// 图像加载与保存
bool loadImage(const QString &filePath);
bool saveImage(const QString &filePath, const QString &format = "PNG");
// 获取图像信息
QImage getOriginalImage() const { return originalImage; }
QImage getCurrentImage() const { return currentImage; }
QSize getImageSize() const { return currentImage.size(); }
QString getImageInfo() const;
// 灰度化处理
enum GrayMode {
GRAY_AVERAGE = 0, // 平均值法
GRAY_WEIGHTED, // 加权平均法
GRAY_MAX, // 最大值法
GRAY_MIN, // 最小值法
GRAY_RED_CHANNEL, // 红色通道
GRAY_GREEN_CHANNEL, // 绿色通道
GRAY_BLUE_CHANNEL // 蓝色通道
};
QImage toGray(GrayMode mode = GRAY_WEIGHTED);
// 锐化处理
enum SharpenMode {
SHARPEN_LAPLACIAN = 0, // 拉普拉斯算子
SHARPEN_SOBEL, // 索贝尔算子
SHARPEN_PREWITT, // Prewitt算子
SHARPEN_CUSTOM // 自定义卷积核
};
QImage sharpen(SharpenMode mode = SHARPEN_LAPLACIAN, int strength = 1);
// 反色处理
QImage invertColors();
// 旋转处理
enum RotationMode {
ROTATE_90_CW = 90, // 顺时针90度
ROTATE_180 = 180, // 180度
ROTATE_270_CW = 270, // 顺时针270度
ROTATE_CUSTOM = -1 // 自定义角度
};
QImage rotate(int angle = ROTATE_90_CW);
QImage rotateCustom(double angle);
// 缩放处理
enum ScaleMode {
SCALE_NEAREST = 0, // 最近邻插值
SCALE_BILINEAR, // 双线性插值
SCALE_BICUBIC, // 双三次插值
SCALE_LANCZOS // Lanczos插值
};
QImage scale(double factor, ScaleMode mode = SCALE_BILINEAR);
QImage scaleToSize(const QSize &newSize, ScaleMode mode = SCALE_BILINEAR);
// 其他图像处理
QImage blur(int radius = 3); // 模糊
QImage edgeDetection(); // 边缘检测
QImage emboss(); // 浮雕效果
QImage oilPainting(int radius = 5); // 油画效果
QImage adjustBrightnessContrast(int brightness, int contrast); // 亮度对比度调整
QImage adjustGamma(double gamma); // Gamma校正
// 直方图均衡化
QImage histogramEqualization();
// 自定义卷积核
QImage applyConvolutionKernel(const QVector<QVector<float>> &kernel);
// 重置图像
void resetImage();
// 撤销/重做
void undo();
void redo();
bool canUndo() const { return !undoStack.isEmpty(); }
bool canRedo() const { return !redoStack.isEmpty(); }
signals:
void imageProcessed(const QImage &image);
void processingStarted();
void processingFinished();
void errorOccurred(const QString &error);
private:
QImage originalImage; // 原始图像
QImage currentImage; // 当前图像
// 撤销/重做栈
QVector<QImage> undoStack;
QVector<QImage> redoStack;
static const int MAX_STACK_SIZE = 20;
// 辅助函数
QRgb getPixelSafe(const QImage &image, int x, int y);
void pushUndoStack();
void clearRedoStack();
// 卷积运算
QImage convolution(const QImage &src, const QVector<QVector<float>> &kernel);
// 插值函数
QRgb bilinearInterpolation(const QImage &image, double x, double y);
QRgb bicubicInterpolation(const QImage &image, double x, double y);
};
#endif // IMAGEPROCESSOR_H
2.2 图像处理实现 (ImageProcessor.cpp)
cpp
#include "ImageProcessor.h"
#include <QFile>
#include <QFileInfo>
#include <QImageReader>
#include <QImageWriter>
#include <QTransform>
#include <QElapsedTimer>
#include <cmath>
ImageProcessor::ImageProcessor(QObject *parent) : QObject(parent)
{
}
ImageProcessor::~ImageProcessor()
{
}
bool ImageProcessor::loadImage(const QString &filePath)
{
QImageReader reader(filePath);
QImage image = reader.read();
if (image.isNull()) {
emit errorOccurred(QString("无法加载图像: %1").arg(reader.errorString()));
return false;
}
// 转换为ARGB32格式以便处理
originalImage = image.convertToFormat(QImage::Format_ARGB32);
currentImage = originalImage;
// 清空撤销/重做栈
undoStack.clear();
redoStack.clear();
emit imageProcessed(currentImage);
return true;
}
bool ImageProcessor::saveImage(const QString &filePath, const QString &format)
{
if (currentImage.isNull()) {
emit errorOccurred("没有图像可保存");
return false;
}
QImageWriter writer(filePath, format.toLatin1());
if (!writer.write(currentImage)) {
emit errorOccurred(QString("保存失败: %1").arg(writer.errorString()));
return false;
}
return true;
}
QString ImageProcessor::getImageInfo() const
{
if (currentImage.isNull()) {
return "无图像";
}
QString info;
info += QString("尺寸: %1 × %2\n").arg(currentImage.width()).arg(currentImage.height());
info += QString("格式: %1\n").arg(currentImage.format() == QImage::Format_ARGB32 ? "ARGB32" : "其他");
info += QString("大小: %1 KB\n").arg(currentImage.sizeInBytes() / 1024.0, 0, 'f', 1);
// 计算颜色统计
int rSum = 0, gSum = 0, bSum = 0;
int pixelCount = 0;
for (int y = 0; y < currentImage.height(); y += 10) {
for (int x = 0; x < currentImage.width(); x += 10) {
QRgb pixel = currentImage.pixel(x, y);
rSum += qRed(pixel);
gSum += qGreen(pixel);
bSum += qBlue(pixel);
pixelCount++;
}
}
if (pixelCount > 0) {
info += QString("平均颜色: R=%1, G=%2, B=%3")
.arg(rSum / pixelCount)
.arg(gSum / pixelCount)
.arg(bSum / pixelCount);
}
return info;
}
// 灰度化处理
QImage ImageProcessor::toGray(GrayMode mode)
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QImage grayImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = grayImage.width();
int height = grayImage.height();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb pixel = grayImage.pixel(x, y);
int gray;
switch (mode) {
case GRAY_AVERAGE:
gray = (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3;
break;
case GRAY_WEIGHTED:
gray = qRound(0.299 * qRed(pixel) + 0.587 * qGreen(pixel) + 0.114 * qBlue(pixel));
break;
case GRAY_MAX:
gray = qMax(qRed(pixel), qMax(qGreen(pixel), qBlue(pixel)));
break;
case GRAY_MIN:
gray = qMin(qRed(pixel), qMin(qGreen(pixel), qBlue(pixel)));
break;
case GRAY_RED_CHANNEL:
gray = qRed(pixel);
break;
case GRAY_GREEN_CHANNEL:
gray = qGreen(pixel);
break;
case GRAY_BLUE_CHANNEL:
gray = qBlue(pixel);
break;
default:
gray = qGray(pixel);
break;
}
grayImage.setPixel(x, y, qRgba(gray, gray, gray, qAlpha(pixel)));
}
}
currentImage = grayImage;
emit processingFinished();
emit imageProcessed(currentImage);
return grayImage;
}
// 锐化处理
QImage ImageProcessor::sharpen(SharpenMode mode, int strength)
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QVector<QVector<float>> kernel;
switch (mode) {
case SHARPEN_LAPLACIAN: {
// 拉普拉斯算子
kernel = {
{0, -1, 0},
{-1, 5, -1},
{0, -1, 0}
};
break;
}
case SHARPEN_SOBEL: {
// Sobel算子(水平方向)
kernel = {
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}
};
break;
}
case SHARPEN_PREWITT: {
// Prewitt算子
kernel = {
{-1, 0, 1},
{-1, 0, 1},
{-1, 0, 1}
};
break;
}
case SHARPEN_CUSTOM: {
// 自定义锐化核
kernel = {
{0, -strength, 0},
{-strength, 1 + 4*strength, -strength},
{0, -strength, 0}
};
break;
}
}
// 应用卷积
QImage result = applyConvolutionKernel(kernel);
currentImage = result;
emit processingFinished();
emit imageProcessed(currentImage);
return result;
}
// 反色处理
QImage ImageProcessor::invertColors()
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QImage invertedImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = invertedImage.width();
int height = invertedImage.height();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb pixel = invertedImage.pixel(x, y);
int r = 255 - qRed(pixel);
int g = 255 - qGreen(pixel);
int b = 255 - qBlue(pixel);
int a = qAlpha(pixel);
invertedImage.setPixel(x, y, qRgba(r, g, b, a));
}
}
currentImage = invertedImage;
emit processingFinished();
emit imageProcessed(currentImage);
return invertedImage;
}
// 旋转处理
QImage ImageProcessor::rotate(int angle)
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QTransform transform;
transform.rotate(angle);
QImage rotatedImage = currentImage.transformed(transform, Qt::SmoothTransformation);
currentImage = rotatedImage;
emit processingFinished();
emit imageProcessed(currentImage);
return rotatedImage;
}
QImage ImageProcessor::rotateCustom(double angle)
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QTransform transform;
transform.rotate(angle);
QImage rotatedImage = currentImage.transformed(transform, Qt::SmoothTransformation);
currentImage = rotatedImage;
emit processingFinished();
emit imageProcessed(currentImage);
return rotatedImage;
}
// 缩放处理
QImage ImageProcessor::scale(double factor, ScaleMode mode)
{
if (currentImage.isNull() || factor <= 0) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QSize newSize = currentImage.size() * factor;
QImage scaledImage = scaleToSize(newSize, mode);
currentImage = scaledImage;
emit processingFinished();
emit imageProcessed(currentImage);
return scaledImage;
}
QImage ImageProcessor::scaleToSize(const QSize &newSize, ScaleMode mode)
{
if (currentImage.isNull() || newSize.isEmpty()) {
return QImage();
}
QImage scaledImage(newSize, QImage::Format_ARGB32);
scaledImage.fill(Qt::white);
double scaleX = (double)currentImage.width() / newSize.width();
double scaleY = (double)currentImage.height() / newSize.height();
for (int y = 0; y < newSize.height(); y++) {
for (int x = 0; x < newSize.width(); x++) {
double srcX = x * scaleX;
double srcY = y * scaleY;
QRgb pixel;
switch (mode) {
case SCALE_NEAREST:
pixel = getPixelSafe(currentImage, qRound(srcX), qRound(srcY));
break;
case SCALE_BILINEAR:
pixel = bilinearInterpolation(currentImage, srcX, srcY);
break;
case SCALE_BICUBIC:
pixel = bicubicInterpolation(currentImage, srcX, srcY);
break;
default:
pixel = getPixelSafe(currentImage, qRound(srcX), qRound(srcY));
break;
}
scaledImage.setPixel(x, y, pixel);
}
}
return scaledImage;
}
// 模糊处理
QImage ImageProcessor::blur(int radius)
{
if (currentImage.isNull() || radius <= 0) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
// 创建均值模糊核
int size = 2 * radius + 1;
QVector<QVector<float>> kernel(size, QVector<float>(size, 1.0f / (size * size)));
QImage blurredImage = applyConvolutionKernel(kernel);
currentImage = blurredImage;
emit processingFinished();
emit imageProcessed(currentImage);
return blurredImage;
}
// 边缘检测
QImage ImageProcessor::edgeDetection()
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
// 使用Sobel算子进行边缘检测
QVector<QVector<float>> kernelX = {
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}
};
QVector<QVector<float>> kernelY = {
{-1, -2, -1},
{0, 0, 0},
{1, 2, 1}
};
QImage imageX = applyConvolutionKernel(kernelX);
QImage imageY = applyConvolutionKernel(kernelY);
// 合并两个方向的梯度
QImage edgeImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = edgeImage.width();
int height = edgeImage.height();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb pixelX = imageX.pixel(x, y);
QRgb pixelY = imageY.pixel(x, y);
int grayX = qGray(pixelX);
int grayY = qGray(pixelY);
int magnitude = qMin(255, qRound(sqrt(grayX*grayX + grayY*grayY)));
edgeImage.setPixel(x, y, qRgba(magnitude, magnitude, magnitude, 255));
}
}
currentImage = edgeImage;
emit processingFinished();
emit imageProcessed(currentImage);
return edgeImage;
}
// 浮雕效果
QImage ImageProcessor::emboss()
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
// 浮雕卷积核
QVector<QVector<float>> kernel = {
{-2, -1, 0},
{-1, 1, 1},
{0, 1, 2}
};
QImage embossedImage = applyConvolutionKernel(kernel);
// 调整亮度
for (int y = 0; y < embossedImage.height(); y++) {
for (int x = 0; x < embossedImage.width(); x++) {
QRgb pixel = embossedImage.pixel(x, y);
int gray = qGray(pixel);
gray = qBound(0, gray + 128, 255);
embossedImage.setPixel(x, y, qRgba(gray, gray, gray, 255));
}
}
currentImage = embossedImage;
emit processingFinished();
emit imageProcessed(currentImage);
return embossedImage;
}
// 油画效果
QImage ImageProcessor::oilPainting(int radius)
{
if (currentImage.isNull() || radius <= 0) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QImage oilImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = oilImage.width();
int height = oilImage.height();
// 创建颜色直方图
const int levels = 8;
const int intensityLevels = 256 / levels;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 统计周围像素的颜色分布
int histogram[levels][levels][levels] = {0};
int maxCount = 0;
QRgb dominantColor = qRgba(0, 0, 0, 255);
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
int nx = qBound(0, x + dx, width - 1);
int ny = qBound(0, y + dy, height - 1);
QRgb pixel = oilImage.pixel(nx, ny);
int r = qRed(pixel) / intensityLevels;
int g = qGreen(pixel) / intensityLevels;
int b = qBlue(pixel) / intensityLevels;
histogram[r][g][b]++;
if (histogram[r][g][b] > maxCount) {
maxCount = histogram[r][g][b];
dominantColor = pixel;
}
}
}
oilImage.setPixel(x, y, dominantColor);
}
}
currentImage = oilImage;
emit processingFinished();
emit imageProcessed(currentImage);
return oilImage;
}
// 亮度对比度调整
QImage ImageProcessor::adjustBrightnessContrast(int brightness, int contrast)
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QImage adjustedImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = adjustedImage.width();
int height = adjustedImage.height();
// 对比度因子
double contrastFactor = (259.0 * (contrast + 255.0)) / (255.0 * (259.0 - contrast));
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb pixel = adjustedImage.pixel(x, y);
int r = qRed(pixel);
int g = qGreen(pixel);
int b = qBlue(pixel);
// 调整亮度
r = qBound(0, r + brightness, 255);
g = qBound(0, g + brightness, 255);
b = qBound(0, b + brightness, 255);
// 调整对比度
r = qBound(0, (int)(contrastFactor * (r - 128) + 128), 255);
g = qBound(0, (int)(contrastFactor * (g - 128) + 128), 255);
b = qBound(0, (int)(contrastFactor * (b - 128) + 128), 255);
adjustedImage.setPixel(x, y, qRgba(r, g, b, qAlpha(pixel)));
}
}
currentImage = adjustedImage;
emit processingFinished();
emit imageProcessed(currentImage);
return adjustedImage;
}
// Gamma校正
QImage ImageProcessor::adjustGamma(double gamma)
{
if (currentImage.isNull() || gamma <= 0) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
// 创建Gamma查找表
uchar gammaTable[256];
for (int i = 0; i < 256; i++) {
gammaTable[i] = qRound(255.0 * pow(i / 255.0, 1.0 / gamma));
}
QImage gammaImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = gammaImage.width();
int height = gammaImage.height();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb pixel = gammaImage.pixel(x, y);
int r = gammaTable[qRed(pixel)];
int g = gammaTable[qGreen(pixel)];
int b = gammaTable[qBlue(pixel)];
gammaImage.setPixel(x, y, qRgba(r, g, b, qAlpha(pixel)));
}
}
currentImage = gammaImage;
emit processingFinished();
emit imageProcessed(currentImage);
return gammaImage;
}
// 直方图均衡化
QImage ImageProcessor::histogramEqualization()
{
if (currentImage.isNull()) {
return QImage();
}
emit processingStarted();
pushUndoStack();
clearRedoStack();
QImage equalizedImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = equalizedImage.width();
int height = equalizedImage.height();
// 计算灰度直方图
int histogram[256] = {0};
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb pixel = equalizedImage.pixel(x, y);
int gray = qGray(pixel);
histogram[gray]++;
}
}
// 计算累积分布函数
int cdf[256] = {0};
cdf[0] = histogram[0];
for (int i = 1; i < 256; i++) {
cdf[i] = cdf[i-1] + histogram[i];
}
// 创建映射表
int pixelCount = width * height;
uchar map[256];
for (int i = 0; i < 256; i++) {
map[i] = qRound((double)cdf[i] * 255 / pixelCount);
}
// 应用映射
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb pixel = equalizedImage.pixel(x, y);
int gray = qGray(pixel);
int newGray = map[gray];
equalizedImage.setPixel(x, y, qRgba(newGray, newGray, newGray, qAlpha(pixel)));
}
}
currentImage = equalizedImage;
emit processingFinished();
emit imageProcessed(currentImage);
return equalizedImage;
}
// 应用卷积核
QImage ImageProcessor::applyConvolutionKernel(const QVector<QVector<float>> &kernel)
{
if (currentImage.isNull() || kernel.isEmpty()) {
return QImage();
}
QImage convolvedImage = currentImage.convertToFormat(QImage::Format_ARGB32);
int width = convolvedImage.width();
int height = convolvedImage.height();
int kernelSize = kernel.size();
int halfKernel = kernelSize / 2;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float rSum = 0, gSum = 0, bSum = 0;
for (int ky = 0; ky < kernelSize; ky++) {
for (int kx = 0; kx < kernelSize; kx++) {
int px = qBound(0, x + kx - halfKernel, width - 1);
int py = qBound(0, y + ky - halfKernel, height - 1);
QRgb pixel = convolvedImage.pixel(px, py);
float weight = kernel[ky][kx];
rSum += qRed(pixel) * weight;
gSum += qGreen(pixel) * weight;
bSum += qBlue(pixel) * weight;
}
}
int r = qBound(0, qRound(rSum), 255);
int g = qBound(0, qRound(gSum), 255);
int b = qBound(0, qRound(bSum), 255);
convolvedImage.setPixel(x, y, qRgba(r, g, b, qAlpha(convolvedImage.pixel(x, y))));
}
}
return convolvedImage;
}
// 重置图像
void ImageProcessor::resetImage()
{
if (!originalImage.isNull()) {
pushUndoStack();
clearRedoStack();
currentImage = originalImage;
emit imageProcessed(currentImage);
}
}
// 撤销
void ImageProcessor::undo()
{
if (!undoStack.isEmpty()) {
redoStack.append(currentImage);
currentImage = undoStack.takeLast();
emit imageProcessed(currentImage);
}
}
// 重做
void ImageProcessor::redo()
{
if (!redoStack.isEmpty()) {
undoStack.append(currentImage);
currentImage = redoStack.takeLast();
emit imageProcessed(currentImage);
}
}
// 辅助函数:安全获取像素
QRgb ImageProcessor::getPixelSafe(const QImage &image, int x, int y)
{
if (x < 0) x = 0;
if (x >= image.width()) x = image.width() - 1;
if (y < 0) y = 0;
if (y >= image.height()) y = image.height() - 1;
return image.pixel(x, y);
}
// 双线性插值
QRgb ImageProcessor::bilinearInterpolation(const QImage &image, double x, double y)
{
int x1 = qFloor(x);
int y1 = qFloor(y);
int x2 = qCeil(x);
int y2 = qCeil(y);
if (x1 == x2 && y1 == y2) {
return getPixelSafe(image, x1, y1);
}
QRgb p11 = getPixelSafe(image, x1, y1);
QRgb p12 = getPixelSafe(image, x1, y2);
QRgb p21 = getPixelSafe(image, x2, y1);
QRgb p22 = getPixelSafe(image, x2, y2);
double dx = x - x1;
double dy = y - y1;
int r = (1-dx)*(1-dy)*qRed(p11) + dx*(1-dy)*qRed(p21) + (1-dx)*dy*qRed(p12) + dx*dy*qRed(p22);
int g = (1-dx)*(1-dy)*qGreen(p11) + dx*(1-dy)*qGreen(p21) + (1-dx)*dy*qGreen(p12) + dx*dy*qGreen(p22);
int b = (1-dx)*(1-dy)*qBlue(p11) + dx*(1-dy)*qBlue(p21) + (1-dx)*dy*qBlue(p12) + dx*dy*qBlue(p22);
return qRgba(qBound(0, qRound(r), 255),
qBound(0, qRound(g), 255),
qBound(0, qRound(b), 255), 255);
}
// 双三次插值
QRgb ImageProcessor::bicubicInterpolation(const QImage &image, double x, double y)
{
// 简化版双三次插值
int xi = qFloor(x);
int yi = qFloor(y);
// 获取周围16个像素
QRgb pixels[4][4];
for (int i = -1; i <= 2; i++) {
for (int j = -1; j <= 2; j++) {
pixels[i+1][j+1] = getPixelSafe(image, xi + i, yi + j);
}
}
// 双三次插值计算
double dx = x - xi;
double dy = y - yi;
// 三次插值权重函数
auto cubicWeight = double t -> double {
double a = -0.5;
if (t < 0) t = -t;
if (t < 1.0) return (a+2)*t*t*t - (a+3)*t*t + 1;
if (t < 2.0) return a*t*t*t - 5*a*t*t + 8*a*t - 4*a;
return 0.0;
};
// 计算插值
int r = 0, g = 0, b = 0;
for (int i = -1; i <= 2; i++) {
for (int j = -1; j <= 2; j++) {
double wx = cubicWeight(dx - i);
double wy = cubicWeight(dy - j);
QRgb pixel = pixels[i+1][j+1];
r += qRed(pixel) * wx * wy;
g += qGreen(pixel) * wx * wy;
b += qBlue(pixel) * wx * wy;
}
}
return qRgba(qBound(0, qRound(r), 255),
qBound(0, qRound(g), 255),
qBound(0, qRound(b), 255), 255);
}
// 推入撤销栈
void ImageProcessor::pushUndoStack()
{
undoStack.append(currentImage);
if (undoStack.size() > MAX_STACK_SIZE) {
undoStack.removeFirst();
}
}
// 清空重做栈
void ImageProcessor::clearRedoStack()
{
redoStack.clear();
}
2.3 主窗口界面 (MainWindow.h)
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QScrollArea>
#include <QPushButton>
#include <QComboBox>
#include <QSlider>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include <QFileDialog>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QMenuBar>
#include <QStatusBar>
#include <QToolBar>
#include <QAction>
#include <QMouseEvent>
#include <QWheelEvent>
#include "ImageProcessor.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
private slots:
// 文件操作
void openImage();
void saveImage();
void saveAsImage();
void batchProcess();
// 图像处理
void grayscale();
void sharpen();
void invertColors();
void rotate90CW();
void rotate180();
void rotate270CW();
void rotateCustom();
void scaleUp();
void scaleDown();
void scaleToFit();
void customScale();
// 其他滤镜
void blurImage();
void edgeDetect();
void embossEffect();
void oilPaintingEffect();
void histogramEqualize();
void adjustBrightnessContrast();
void adjustGamma();
// 编辑操作
void undo();
void redo();
void resetImage();
// 视图操作
void zoomIn();
void zoomOut();
void fitToWindow();
void actualSize();
// 帮助
void about();
void showImageInfo();
private:
void setupUI();
void setupMenus();
void setupToolBars();
void setupStatusBar();
void updateImageDisplay();
void updateZoomLabel();
void enableImageOperations(bool enable);
// UI组件
QScrollArea *scrollArea;
QLabel *imageLabel;
QStatusBar *statusBar;
// 图像处理
ImageProcessor *processor;
// 缩放相关
double zoomFactor;
QPoint lastMousePos;
// 控件
QComboBox *grayModeCombo;
QComboBox *sharpenModeCombo;
QComboBox *scaleModeCombo;
QSpinBox *sharpenStrengthSpin;
QSpinBox *blurRadiusSpin;
QSpinBox *oilRadiusSpin;
QSlider *brightnessSlider;
QSlider *contrastSlider;
QDoubleSpinBox *gammaSpin;
QDoubleSpinBox *rotateAngleSpin;
QDoubleSpinBox *scaleFactorSpin;
};
#endif // MAINWINDOW_H
2.4 主窗口实现 (MainWindow.cpp)
cpp
#include "MainWindow.h"
#include <QApplication>
#include <QFileDialog>
#include <QMessageBox>
#include <QImageWriter>
#include <QImageReader>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
#include <QSlider>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QAction>
#include <QIcon>
#include <QFileInfo>
#include <QDir>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
processor = new ImageProcessor(this);
setupUI();
setupMenus();
setupToolBars();
setupStatusBar();
enableImageOperations(false);
zoomFactor = 1.0;
}
MainWindow::~MainWindow()
{
}
void MainWindow::setupUI()
{
// 中央部件
scrollArea = new QScrollArea(this);
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setAlignment(Qt::AlignCenter);
scrollArea->setWidgetResizable(true);
imageLabel = new QLabel(scrollArea);
imageLabel->setAlignment(Qt::AlignCenter);
imageLabel->setScaledContents(true);
imageLabel->setMinimumSize(400, 300);
imageLabel->setText("请打开一张图片");
imageLabel->setStyleSheet("QLabel { background-color: darkgray; color: white; }");
scrollArea->setWidget(imageLabel);
setCentralWidget(scrollArea);
// 右侧控制面板
QWidget *controlPanel = new QWidget(this);
controlPanel->setFixedWidth(300);
controlPanel->setStyleSheet("QWidget { background-color: #f0f0f0; }");
QVBoxLayout *controlLayout = new QVBoxLayout(controlPanel);
// 灰度化组
QGroupBox *grayGroup = new QGroupBox("灰度化", controlPanel);
QVBoxLayout *grayLayout = new QGroupBox(grayGroup);
grayModeCombo = new QComboBox(grayGroup);
grayModeCombo->addItems({"加权平均法", "平均值法", "最大值法", "最小值法",
"红色通道", "绿色通道", "蓝色通道"});
QPushButton *grayBtn = new QPushButton("应用灰度化", grayGroup);
connect(grayBtn, &QPushButton::clicked, this, &MainWindow::grayscale);
grayLayout->addWidget(grayModeCombo);
grayLayout->addWidget(grayBtn);
grayGroup->setLayout(grayLayout);
// 锐化组
QGroupBox *sharpenGroup = new QGroupBox("锐化", controlPanel);
QVBoxLayout *sharpenLayout = new QVBoxLayout(sharpenGroup);
sharpenModeCombo = new QComboBox(sharpenGroup);
sharpenModeCombo->addItems({"拉普拉斯算子", "Sobel算子", "Prewitt算子", "自定义锐化"});
sharpenStrengthSpin = new QSpinBox(sharpenGroup);
sharpenStrengthSpin->setRange(1, 10);
sharpenStrengthSpin->setValue(1);
sharpenStrengthSpin->setSuffix(" 强度");
QPushButton *sharpenBtn = new QPushButton("应用锐化", sharpenGroup);
connect(sharpenBtn, &QPushButton::clicked, this, &MainWindow::sharpen);
sharpenLayout->addWidget(sharpenModeCombo);
sharpenLayout->addWidget(sharpenStrengthSpin);
sharpenLayout->addWidget(sharpenBtn);
sharpenGroup->setLayout(sharpenLayout);
// 缩放组
QGroupBox *scaleGroup = new QGroupBox("缩放", controlPanel);
QVBoxLayout *scaleLayout = new QVBoxLayout(scaleGroup);
scaleModeCombo = new QComboBox(scaleGroup);
scaleModeCombo->addItems({"最近邻插值", "双线性插值", "双三次插值", "Lanczos插值"});
scaleFactorSpin = new QDoubleSpinBox(scaleGroup);
scaleFactorSpin->setRange(0.1, 10.0);
scaleFactorSpin->setValue(1.0);
scaleFactorSpin->setSuffix(" 倍");
scaleFactorSpin->setSingleStep(0.1);
QPushButton *scaleBtn = new QPushButton("应用缩放", scaleGroup);
connect(scaleBtn, &QPushButton::clicked, this, &MainWindow::customScale);
scaleLayout->addWidget(scaleModeCombo);
scaleLayout->addWidget(scaleFactorSpin);
scaleLayout->addWidget(scaleBtn);
scaleGroup->setLayout(scaleLayout);
// 旋转组
QGroupBox *rotateGroup = new QGroupBox("旋转", controlPanel);
QVBoxLayout *rotateLayout = new QVBoxLayout(rotateGroup);
rotateAngleSpin = new QDoubleSpinBox(rotateGroup);
rotateAngleSpin->setRange(-360, 360);
rotateAngleSpin->setValue(90);
rotateAngleSpin->setSuffix(" °");
QPushButton *rotateBtn = new QPushButton("应用旋转", rotateGroup);
connect(rotateBtn, &QPushButton::clicked, this, &MainWindow::rotateCustom);
rotateLayout->addWidget(rotateAngleSpin);
rotateLayout->addWidget(rotateBtn);
rotateGroup->setLayout(rotateLayout);
// 调整组
QGroupBox *adjustGroup = new QGroupBox("调整", controlPanel);
QVBoxLayout *adjustLayout = new QVBoxLayout(adjustGroup);
QLabel *brightnessLabel = new QLabel("亮度:", adjustGroup);
brightnessSlider = new QSlider(Qt::Horizontal, adjustGroup);
brightnessSlider->setRange(-100, 100);
brightnessSlider->setValue(0);
QLabel *contrastLabel = new QLabel("对比度:", adjustGroup);
contrastSlider = new QSlider(Qt::Horizontal, adjustGroup);
contrastSlider->setRange(-100, 100);
contrastSlider->setValue(0);
QLabel *gammaLabel = new QLabel("Gamma:", adjustGroup);
gammaSpin = new QDoubleSpinBox(adjustGroup);
gammaSpin->setRange(0.1, 5.0);
gammaSpin->setValue(1.0);
gammaSpin->setSingleStep(0.1);
QPushButton *adjustBtn = new QPushButton("应用调整", adjustGroup);
connect(adjustBtn, &QPushButton::clicked, this, &MainWindow::adjustBrightnessContrast);
adjustLayout->addWidget(brightnessLabel);
adjustLayout->addWidget(brightnessSlider);
adjustLayout->addWidget(contrastLabel);
adjustLayout->addWidget(contrastSlider);
adjustLayout->addWidget(gammaLabel);
adjustLayout->addWidget(gammaSpin);
adjustLayout->addWidget(adjustBtn);
adjustGroup->setLayout(adjustLayout);
// 添加到控制面板
controlLayout->addWidget(grayGroup);
controlLayout->addWidget(sharpenGroup);
controlLayout->addWidget(scaleGroup);
controlLayout->addWidget(rotateGroup);
controlLayout->addWidget(adjustGroup);
controlLayout->addStretch();
// 添加到主窗口
QHBoxLayout *mainLayout = new QHBoxLayout();
mainLayout->addWidget(scrollArea, 1);
mainLayout->addWidget(controlPanel);
QWidget *centralWidget = new QWidget(this);
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
resize(1200, 800);
}
void MainWindow::setupMenus()
{
// 文件菜单
QMenu *fileMenu = menuBar()->addMenu("文件(&F)");
QAction *openAct = new QAction(QIcon::fromTheme("document-open"), "打开(&O)...", this);
openAct->setShortcut(QKeySequence::Open);
connect(openAct, &QAction::triggered, this, &MainWindow::openImage);
fileMenu->addAction(openAct);
fileMenu->addSeparator();
QAction *saveAct = new QAction(QIcon::fromTheme("document-save"), "保存(&S)", this);
saveAct->setShortcut(QKeySequence::Save);
connect(saveAct, &QAction::triggered, this, &MainWindow::saveImage);
fileMenu->addAction(saveAct);
QAction *saveAsAct = new QAction("另存为(&A)...", this);
saveAsAct->setShortcut(QKeySequence::SaveAs);
connect(saveAsAct, &QAction::triggered, this, &MainWindow::saveAsImage);
fileMenu->addAction(saveAsAct);
fileMenu->addSeparator();
QAction *batchAct = new QAction("批量处理(&B)...", this);
connect(batchAct, &QAction::triggered, this, &MainWindow::batchProcess);
fileMenu->addAction(batchAct);
fileMenu->addSeparator();
QAction *exitAct = new QAction("退出(&X)", this);
exitAct->setShortcut(QKeySequence::Quit);
connect(exitAct, &QAction::triggered, this, &QWidget::close);
fileMenu->addAction(exitAct);
// 编辑菜单
QMenu *editMenu = menuBar()->addMenu("编辑(&E)");
QAction *undoAct = new QAction(QIcon::fromTheme("edit-undo"), "撤销(&U)", this);
undoAct->setShortcut(QKeySequence::Undo);
connect(undoAct, &QAction::triggered, this, &MainWindow::undo);
editMenu->addAction(undoAct);
QAction *redoAct = new QAction(QIcon::fromTheme("edit-redo"), "重做(&R)", this);
redoAct->setShortcut(QKeySequence::Redo);
connect(redoAct, &QAction::triggered, this, &MainWindow::redo);
editMenu->addAction(redoAct);
editMenu->addSeparator();
QAction *resetAct = new QAction("重置(&R)", this);
connect(resetAct, &QAction::triggered, this, &MainWindow::resetImage);
editMenu->addAction(resetAct);
// 图像菜单
QMenu *imageMenu = menuBar()->addMenu("图像(&I)");
QMenu *grayMenu = imageMenu->addMenu("灰度化(&G)");
QAction *grayAvgAct = new QAction("平均值法", this);
connect(grayAvgAct, &QAction::triggered, {
grayModeCombo->setCurrentIndex(1);
grayscale();
});
grayMenu->addAction(grayAvgAct);
QAction *grayWeightedAct = new QAction("加权平均法", this);
connect(grayWeightedAct, &QAction::triggered, {
grayModeCombo->setCurrentIndex(0);
grayscale();
});
grayMenu->addAction(grayWeightedAct);
imageMenu->addSeparator();
QAction *invertAct = new QAction("反色(&I)", this);
connect(invertAct, &QAction::triggered, this, &MainWindow::invertColors);
imageMenu->addAction(invertAct);
QMenu *rotateMenu = imageMenu->addMenu("旋转(&R)");
QAction *rotate90Act = new QAction("顺时针90°", this);
connect(rotate90Act, &QAction::triggered, this, &MainWindow::rotate90CW);
rotateMenu->addAction(rotate90Act);
QAction *rotate180Act = new QAction("180°", this);
connect(rotate180Act, &QAction::triggered, this, &MainWindow::rotate180);
rotateMenu->addAction(rotate180Act);
QAction *rotate270Act = new QAction("顺时针270°", this);
connect(rotate270Act, &QAction::triggered, this, &MainWindow::rotate270CW);
rotateMenu->addAction(rotate270Act);
QMenu *scaleMenu = imageMenu->addMenu("缩放(&S)");
QAction *scaleUpAct = new QAction("放大", this);
connect(scaleUpAct, &QAction::triggered, this, &MainWindow::scaleUp);
scaleMenu->addAction(scaleUpAct);
QAction *scaleDownAct = new QAction("缩小", this);
connect(scaleDownAct, &QAction::triggered, this, &MainWindow::scaleDown);
scaleMenu->addAction(scaleDownAct);
// 滤镜菜单
QMenu *filterMenu = menuBar()->addMenu("滤镜(&F)");
QAction *blurAct = new QAction("模糊(&B)", this);
connect(blurAct, &QAction::triggered, this, &MainWindow::blurImage);
filterMenu->addAction(blurAct);
QAction *edgeAct = new QAction("边缘检测(&E)", this);
connect(edgeAct, &QAction::triggered, this, &MainWindow::edgeDetect);
filterMenu->addAction(edgeAct);
QAction *embossAct = new QAction("浮雕效果(&M)", this);
connect(embossAct, &QAction::triggered, this, &MainWindow::embossEffect);
filterMenu->addAction(embossAct);
QAction *oilAct = new QAction("油画效果(&O)", this);
connect(oilAct, &QAction::triggered, this, &MainWindow::oilPaintingEffect);
filterMenu->addAction(oilAct);
QAction *histogramAct = new QAction("直方图均衡化(&H)", this);
connect(histogramAct, &QAction::triggered, this, &MainWindow::histogramEqualize);
filterMenu->addAction(histogramAct);
// 视图菜单
QMenu *viewMenu = menuBar()->addMenu("视图(&V)");
QAction *zoomInAct = new QAction("放大(&I)", this);
zoomInAct->setShortcut(QKeySequence::ZoomIn);
connect(zoomInAct, &QAction::triggered, this, &MainWindow::zoomIn);
viewMenu->addAction(zoomInAct);
QAction *zoomOutAct = new QAction("缩小(&O)", this);
zoomOutAct->setShortcut(QKeySequence::ZoomOut);
connect(zoomOutAct, &QAction::triggered, this, &MainWindow::zoomOut);
viewMenu->addAction(zoomOutAct);
QAction *fitAct = new QAction("适应窗口(&F)", this);
connect(fitAct, &QAction::triggered, this, &MainWindow::fitToWindow);
viewMenu->addAction(fitAct);
QAction *actualAct = new QAction("实际大小(&A)", this);
connect(actualAct, &QAction::triggered, this, &MainWindow::actualSize);
viewMenu->addAction(actualAct);
// 帮助菜单
QMenu *helpMenu = menuBar()->addMenu("帮助(&H)");
QAction *infoAct = new QAction("图像信息(&I)", this);
connect(infoAct, &QAction::triggered, this, &MainWindow::showImageInfo);
helpMenu->addAction(infoAct);
helpMenu->addSeparator();
QAction *aboutAct = new QAction("关于(&A)", this);
connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
helpMenu->addAction(aboutAct);
}
void MainWindow::setupToolBars()
{
// 文件工具栏
QToolBar *fileToolBar = addToolBar("文件");
fileToolBar->addAction(QIcon::fromTheme("document-open"), "打开", this, &MainWindow::openImage);
fileToolBar->addAction(QIcon::fromTheme("document-save"), "保存", this, &MainWindow::saveImage);
// 编辑工具栏
QToolBar *editToolBar = addToolBar("编辑");
editToolBar->addAction(QIcon::fromTheme("edit-undo"), "撤销", this, &MainWindow::undo);
editToolBar->addAction(QIcon::fromTheme("edit-redo"), "重做", this, &MainWindow::redo);
// 视图工具栏
QToolBar *viewToolBar = addToolBar("视图");
viewToolBar->addAction(QIcon::fromTheme("zoom-in"), "放大", this, &MainWindow::zoomIn);
viewToolBar->addAction(QIcon::fromTheme("zoom-out"), "缩小", this, &MainWindow::zoomOut);
}
void MainWindow::setupStatusBar()
{
statusBar = new QStatusBar(this);
setStatusBar(statusBar);
statusBar->showMessage("就绪");
}
void MainWindow::openImage()
{
QString fileName = QFileDialog::getOpenFileName(this, "打开图像",
QDir::homePath(),
"图像文件 (*.png *.jpg *.jpeg *.bmp *.gif *.tiff);;所有文件 (*.*)");
if (!fileName.isEmpty()) {
if (processor->loadImage(fileName)) {
updateImageDisplay();
enableImageOperations(true);
statusBar->showMessage(QString("已加载: %1").arg(QFileInfo(fileName).fileName()));
} else {
QMessageBox::critical(this, "错误", "无法加载图像文件");
}
}
}
void MainWindow::saveImage()
{
if (processor->getCurrentImage().isNull()) {
QMessageBox::warning(this, "警告", "没有图像可保存");
return;
}
QString fileName = QFileDialog::getSaveFileName(this, "保存图像",
QDir::homePath() + "/untitled.png",
"PNG文件 (*.png);;JPEG文件 (*.jpg);;BMP文件 (*.bmp)");
if (!fileName.isEmpty()) {
QFileInfo fileInfo(fileName);
if (processor->saveImage(fileName, fileInfo.suffix().toUpper())) {
statusBar->showMessage(QString("已保存: %1").arg(fileInfo.fileName()));
} else {
QMessageBox::critical(this, "错误", "保存图像失败");
}
}
}
void MainWindow::saveAsImage()
{
saveImage();
}
void MainWindow::grayscale()
{
ImageProcessor::GrayMode mode = static_cast<ImageProcessor::GrayMode>(grayModeCombo->currentIndex());
processor->toGray(mode);
updateImageDisplay();
}
void MainWindow::sharpen()
{
ImageProcessor::SharpenMode mode = static_cast<ImageProcessor::SharpenMode>(sharpenModeCombo->currentIndex());
processor->sharpen(mode, sharpenStrengthSpin->value());
updateImageDisplay();
}
void MainWindow::invertColors()
{
processor->invertColors();
updateImageDisplay();
}
void MainWindow::rotate90CW()
{
processor->rotate(ImageProcessor::ROTATE_90_CW);
updateImageDisplay();
}
void MainWindow::rotate180()
{
processor->rotate(ImageProcessor::ROTATE_180);
updateImageDisplay();
}
void MainWindow::rotate270CW()
{
processor->rotate(ImageProcessor::ROTATE_270_CW);
updateImageDisplay();
}
void MainWindow::rotateCustom()
{
processor->rotateCustom(rotateAngleSpin->value());
updateImageDisplay();
}
void MainWindow::scaleUp()
{
double factor = scaleFactorSpin->value() + 0.1;
scaleFactorSpin->setValue(factor);
processor->scale(factor);
updateImageDisplay();
}
void MainWindow::scaleDown()
{
double factor = scaleFactorSpin->value() - 0.1;
scaleFactorSpin->setValue(factor);
processor->scale(factor);
updateImageDisplay();
}
void MainWindow::customScale()
{
double factor = scaleFactorSpin->value();
ImageProcessor::ScaleMode mode = static_cast<ImageProcessor::ScaleMode>(scaleModeCombo->currentIndex());
processor->scale(factor, mode);
updateImageDisplay();
}
void MainWindow::blurImage()
{
QDialog dialog(this);
dialog.setWindowTitle("模糊设置");
QVBoxLayout layout(&dialog);
QLabel label("模糊半径:", &dialog);
QSpinBox spinBox(&dialog);
spinBox.setRange(1, 20);
spinBox.setValue(3);
QHBoxLayout buttonLayout;
QPushButton okBtn("确定", &dialog);
QPushButton cancelBtn("取消", &dialog);
buttonLayout.addWidget(&okBtn);
buttonLayout.addWidget(&cancelBtn);
layout.addWidget(&label);
layout.addWidget(&spinBox);
layout.addLayout(&buttonLayout);
connect(&okBtn, &QPushButton::clicked, &dialog, &QDialog::accept);
connect(&cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject);
if (dialog.exec() == QDialog::Accepted) {
processor->blur(spinBox.value());
updateImageDisplay();
}
}
void MainWindow::edgeDetect()
{
processor->edgeDetection();
updateImageDisplay();
}
void MainWindow::embossEffect()
{
processor->emboss();
updateImageDisplay();
}
void MainWindow::oilPaintingEffect()
{
QDialog dialog(this);
dialog.setWindowTitle("油画效果设置");
QVBoxLayout layout(&dialog);
QLabel label("画笔半径:", &dialog);
QSpinBox spinBox(&dialog);
spinBox.setRange(1, 20);
spinBox.setValue(5);
QHBoxLayout buttonLayout;
QPushButton okBtn("确定", &dialog);
QPushButton cancelBtn("取消", &dialog);
buttonLayout.addWidget(&okBtn);
buttonLayout.addWidget(&cancelBtn);
layout.addWidget(&label);
layout.addWidget(&spinBox);
layout.addLayout(&buttonLayout);
connect(&okBtn, &QPushButton::clicked, &dialog, &QDialog::accept);
connect(&cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject);
if (dialog.exec() == QDialog::Accepted) {
processor->oilPainting(spinBox.value());
updateImageDisplay();
}
}
void MainWindow::histogramEqualize()
{
processor->histogramEqualization();
updateImageDisplay();
}
void MainWindow::adjustBrightnessContrast()
{
int brightness = brightnessSlider->value();
int contrast = contrastSlider->value();
double gamma = gammaSpin->value();
QImage adjusted = processor->adjustBrightnessContrast(brightness, contrast);
adjusted = processor->adjustGamma(gamma);
processor->resetImage(); // 临时重置以便应用调整
// 这里需要将调整后的图像设置为当前图像
updateImageDisplay();
}
void MainWindow::adjustGamma()
{
double gamma = gammaSpin->value();
processor->adjustGamma(gamma);
updateImageDisplay();
}
void MainWindow::undo()
{
processor->undo();
updateImageDisplay();
}
void MainWindow::redo()
{
processor->redo();
updateImageDisplay();
}
void MainWindow::resetImage()
{
processor->resetImage();
updateImageDisplay();
}
void MainWindow::zoomIn()
{
zoomFactor *= 1.25;
updateImageDisplay();
}
void MainWindow::zoomOut()
{
zoomFactor /= 1.25;
updateImageDisplay();
}
void MainWindow::fitToWindow()
{
if (processor->getCurrentImage().isNull()) return;
QSize imageSize = processor->getImageSize();
QSize viewportSize = scrollArea->viewport()->size();
double widthRatio = (double)viewportSize.width() / imageSize.width();
double heightRatio = (double)viewportSize.height() / imageSize.height();
zoomFactor = qMin(widthRatio, heightRatio) * 0.95; // 留一点边距
updateImageDisplay();
}
void MainWindow::actualSize()
{
zoomFactor = 1.0;
updateImageDisplay();
}
void MainWindow::about()
{
QMessageBox::about(this, "关于Qt图像处理系统",
"<h3>Qt图像处理系统</h3>"
"<p>一个基于Qt的图像处理应用程序,支持灰度化、锐化、反色、旋转、缩放等功能。</p>"
"<p>版本: 1.0</p>"
"<p>作者: Qt图像处理团队</p>");
}
void MainWindow::showImageInfo()
{
if (processor->getCurrentImage().isNull()) {
QMessageBox::information(this, "图像信息", "没有打开图像");
return;
}
QString info = processor->getImageInfo();
QMessageBox::information(this, "图像信息", info);
}
void MainWindow::updateImageDisplay()
{
QImage image = processor->getCurrentImage();
if (image.isNull()) {
imageLabel->setText("没有图像");
return;
}
// 应用缩放
QSize scaledSize = image.size() * zoomFactor;
QImage scaledImage = image.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
imageLabel->setPixmap(QPixmap::fromImage(scaledImage));
imageLabel->resize(scaledSize);
updateZoomLabel();
}
void MainWindow::updateZoomLabel()
{
statusBar->showMessage(QString("缩放: %1% | 图像: %2x%3")
.arg(zoomFactor * 100, 0, 'f', 1)
.arg(processor->getImageSize().width())
.arg(processor->getImageSize().height()));
}
void MainWindow::enableImageOperations(bool enable)
{
// 启用或禁用图像处理相关的菜单项和按钮
// 这里可以根据需要实现
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && !processor->getCurrentImage().isNull()) {
lastMousePos = event->pos();
}
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton && !processor->getCurrentImage().isNull()) {
QPoint delta = event->pos() - lastMousePos;
lastMousePos = event->pos();
// 移动滚动条
scrollArea->horizontalScrollBar()->setValue(scrollArea->horizontalScrollBar()->value() - delta.x());
scrollArea->verticalScrollBar()->setValue(scrollArea->verticalScrollBar()->value() - delta.y());
}
}
void MainWindow::wheelEvent(QWheelEvent *event)
{
if (processor->getCurrentImage().isNull()) return;
if (event->angleDelta().y() > 0) {
zoomIn();
} else {
zoomOut();
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setApplicationName("Qt图像处理系统");
app.setApplicationVersion("1.0");
app.setOrganizationName("Qt图像处理团队");
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}
三、项目配置
3.1 CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.16)
project(QtImageProcessor VERSION 1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# 查找Qt库
find_package(Qt6 COMPONENTS Core Widgets Gui REQUIRED)
# 源文件
set(SOURCES
main.cpp
MainWindow.cpp
ImageProcessor.cpp
)
# 头文件
set(HEADERS
MainWindow.h
ImageProcessor.h
)
# 创建可执行文件
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
# 链接库
target_link_libraries(${PROJECT_NAME}
Qt6::Core
Qt6::Widgets
Qt6::Gui
)
# 安装规则
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
)
3.2 qmake项目文件 (QtImageProcessor.pro)
pro
QT += core gui widgets
CONFIG += c++17
TARGET = QtImageProcessor
TEMPLATE = app
SOURCES += \
main.cpp \
MainWindow.cpp \
ImageProcessor.cpp
HEADERS += \
MainWindow.h \
ImageProcessor.h
RESOURCES += \
resources.qrc
# 安装
target.path = /usr/local/bin
INSTALLS += target
参考代码 基于Qt图像的灰度、锐化、反色、旋转、放缩等处理 www.youwenfan.com/contentcsv/72070.html
四、使用说明
4.1 编译运行
bash
# 使用CMake
mkdir build
cd build
cmake ..
make
./QtImageProcessor
# 使用qmake
qmake QtImageProcessor.pro
make
./QtImageProcessor
4.2 功能使用
- 打开图像:点击"文件"→"打开"或工具栏的打开按钮
- 灰度化:在右侧控制面板选择灰度化模式,点击"应用灰度化"
- 锐化:选择锐化模式和强度,点击"应用锐化"
- 反色:点击"图像"→"反色"或右侧控制面板的反色按钮
- 旋转:选择旋转角度,点击"应用旋转"
- 缩放:选择缩放模式和比例,点击"应用缩放"
- 其他滤镜:使用滤镜菜单应用各种效果
- 调整:使用滑块调整亮度、对比度和Gamma值
- 撤销/重做:使用编辑菜单或快捷键Ctrl+Z/Ctrl+Y
- 视图操作:使用鼠标滚轮缩放,拖动平移图像
4.3 键盘快捷键
Ctrl+O:打开图像Ctrl+S:保存图像Ctrl+Z:撤销Ctrl+Y:重做Ctrl++:放大Ctrl+-:缩小Ctrl+0:实际大小Ctrl+F:适应窗口