基于Qt的图像处理系统

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 功能使用

  1. 打开图像:点击"文件"→"打开"或工具栏的打开按钮
  2. 灰度化:在右侧控制面板选择灰度化模式,点击"应用灰度化"
  3. 锐化:选择锐化模式和强度,点击"应用锐化"
  4. 反色:点击"图像"→"反色"或右侧控制面板的反色按钮
  5. 旋转:选择旋转角度,点击"应用旋转"
  6. 缩放:选择缩放模式和比例,点击"应用缩放"
  7. 其他滤镜:使用滤镜菜单应用各种效果
  8. 调整:使用滑块调整亮度、对比度和Gamma值
  9. 撤销/重做:使用编辑菜单或快捷键Ctrl+Z/Ctrl+Y
  10. 视图操作:使用鼠标滚轮缩放,拖动平移图像

4.3 键盘快捷键

  • Ctrl+O:打开图像
  • Ctrl+S:保存图像
  • Ctrl+Z:撤销
  • Ctrl+Y:重做
  • Ctrl++:放大
  • Ctrl+-:缩小
  • Ctrl+0:实际大小
  • Ctrl+F:适应窗口
相关推荐
码界筑梦坊2 小时前
150-基于Python的中国海洋水质数据可视化分析系统
开发语言·python·信息可视化·django·毕业设计
chushiyunen2 小时前
golang笔记、go
开发语言·笔记·golang
青枣八神2 小时前
Trae IDE 终端 JDK 版本与系统不一致的解决方案
java·开发语言·ide
Shadow(⊙o⊙)2 小时前
Linux内核级文件系统分析——文件系统入门内核级文章!
linux·运维·服务器·开发语言·c++
cjhbachelor2 小时前
C/C++内存管理
c语言·开发语言·c++
噜噜大王_2 小时前
C++ 类和对象(中):默认成员函数全解
开发语言·c++
草莓啵啵~3 小时前
pywinauto-打开程序+连接已打开的程序
开发语言·python
Ws_10 小时前
C#学习 Day2
开发语言·学习·c#
杰克尼10 小时前
天机学堂复习总结(day03-day04)
java·开发语言·redis·elasticsearch·spring cloud