QT加载图像

 图像查看

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>
#include <QImage>
#include <QPixmap>
#include <QStatusBar>
#include <QTransform>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QEvent>
#include <QPoint>
#include <QPainter>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

protected:
    void keyPressEvent(QKeyEvent *event) override;
    void keyReleaseEvent(QKeyEvent *event) override;
    bool eventFilter(QObject *obj, QEvent *event) override;

private slots:
    void loadImage();
    void zoomIn();
    void zoomOut();
    void fitToWindow();
    void originalSize();
    void autoContrast();
    void showOriginal();

private:
    void setupUI();
    void updateImageDisplay();
    void updateStatusBar(int x, int y);
    void applyAutoContrast();
    int getPixelValue(const QImage &img, int x, int y);

    QWidget *centralWidget;
    QVBoxLayout *mainLayout;
    QHBoxLayout *buttonLayout;
    QGraphicsScene *scene;
    QGraphicsView *view;
    QGraphicsPixmapItem *pixmapItem;
    QPushButton *loadButton;
    QPushButton *zoomInButton;
    QPushButton *zoomOutButton;
    QPushButton *fitButton;
    QPushButton *originalButton;
    QPushButton *autoContrastButton;
    QPushButton *showOriginalButton;

    QImage originalImage;  // 存储当前显示图像(可能已应用对比度)
    QImage unmodifiedOriginalImage;  // 存储未变换的原始图像
    double scaleFactor;
    bool ctrlPressed;  // 跟踪Ctrl键状态

    // 拖拽平移(QGraphicsView 内置)
    bool leftMousePressed;
    QPoint lastDragPos;

    // 缩放限制(已取消)
    // static constexpr double MAX_SCALE_FACTOR = 10.0;  // 最大缩放限制
    // static constexpr double MIN_SCALE_FACTOR = 0.01;  // 最小缩放限制
};

#endif // MAINWINDOW_H
mainwindow.h
#include "mainwindow.h"
#include <QApplication>
#include <algorithm>  // for std::min, std::max
#include <QCursor>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), scaleFactor(1.0), ctrlPressed(false),
    leftMousePressed(false), lastDragPos(0, 0),
    scene(nullptr), view(nullptr), pixmapItem(nullptr)
{
    setupUI();
    // view->installEventFilter(this);  // 安装事件过滤器以捕获滚轮事件
    view->viewport()->installEventFilter(this);  // 安装到 viewport 而非 view
}

MainWindow::~MainWindow()
{
}

void MainWindow::setupUI()
{
    centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);

    mainLayout = new QVBoxLayout(centralWidget);
    buttonLayout = new QHBoxLayout();

    // 创建按钮
    loadButton = new QPushButton("加载图像", this);
    zoomInButton = new QPushButton("放大", this);
    zoomOutButton = new QPushButton("缩小", this);
    fitButton = new QPushButton("适应窗口", this);
    originalButton = new QPushButton("原始大小", this);
    autoContrastButton = new QPushButton("自适应对比度", this);
    showOriginalButton = new QPushButton("显示原图", this);

    // 连接信号槽
    connect(loadButton, &QPushButton::clicked, this, &MainWindow::loadImage);
    connect(zoomInButton, &QPushButton::clicked, this, &MainWindow::zoomIn);
    connect(zoomOutButton, &QPushButton::clicked, this, &MainWindow::zoomOut);
    connect(fitButton, &QPushButton::clicked, this, &MainWindow::fitToWindow);
    connect(originalButton, &QPushButton::clicked, this, &MainWindow::originalSize);
    connect(autoContrastButton, &QPushButton::clicked, this, &MainWindow::autoContrast);
    connect(showOriginalButton, &QPushButton::clicked, this, &MainWindow::showOriginal);

    // 创建场景和视图(替换 scrollArea 和 imageLabel)
    scene = new QGraphicsScene(this);
    view = new QGraphicsView(scene, this);
    view->setAlignment(Qt::AlignCenter);
    view->setBackgroundBrush(view->palette().brush(QPalette::Dark));
    view->setDragMode(QGraphicsView::ScrollHandDrag);  // 启用内置拖拽平移
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);  // 缩放锚点在鼠标下
    view->setResizeAnchor(QGraphicsView::AnchorViewCenter);  // 窗口调整大小时居中
    view->setRenderHint(QPainter::Antialiasing, false);  // 禁用抗锯齿以保持像素化
    view->setMouseTracking(true);  // 启用鼠标跟踪
    view->setVisible(false);

    // 布局设置
    buttonLayout->addWidget(loadButton);
    buttonLayout->addWidget(zoomInButton);
    buttonLayout->addWidget(zoomOutButton);
    buttonLayout->addWidget(fitButton);
    buttonLayout->addWidget(originalButton);
    buttonLayout->addWidget(autoContrastButton);
    buttonLayout->addWidget(showOriginalButton);
    buttonLayout->addStretch();

    mainLayout->addLayout(buttonLayout);
    mainLayout->addWidget(view);

    // 状态栏
    statusBar()->showMessage("准备就绪");

    setWindowTitle("图像查看器");
    resize(800, 600);
}

void MainWindow::loadImage()
{
    QString fileName = QFileDialog::getOpenFileName(
        this,
        "打开图像",
        "",
        "图像文件 (*.tif *.tiff *.png *.jpg *.jpeg *.bmp *.gif *.ico);;所有文件 (*.*)"
        );

    if (fileName.isEmpty()) {
        return;
    }

    originalImage = QImage(fileName);

    if (originalImage.isNull()) {
        QMessageBox::warning(this, "错误",
                             "无法加载图像!\n"
                             "请确保:\n"
                             "1. 文件格式正确\n"
                             "2. Qt已安装相应插件\n"
                             "3. 文件没有损坏");
        return;
    }

    // 支持8位和16位灰度图像,如果不是灰度则转换为16位灰度
    if (originalImage.format() != QImage::Format_Grayscale8 &&
        originalImage.format() != QImage::Format_Grayscale16) {
        originalImage = originalImage.convertToFormat(QImage::Format_Grayscale16);
    }

    // 存储未变换的原始图像
    unmodifiedOriginalImage = originalImage;

    scaleFactor = 1.0;

    // 清空场景并添加新项
    scene->clear();
    pixmapItem = new QGraphicsPixmapItem(QPixmap::fromImage(originalImage));
    pixmapItem->setTransformationMode(Qt::FastTransformation);  // 像素化缩放
    scene->addItem(pixmapItem);

    view->setVisible(true);
    updateImageDisplay();

    // 更新状态栏
    QFileInfo fileInfo(fileName);
    int bitDepth = (originalImage.format() == QImage::Format_Grayscale8 ? 8 : 16);
    statusBar()->showMessage(QString("已加载: %1 | 尺寸: %2x%3 | 格式: %4位灰度")
                                 .arg(fileInfo.fileName())
                                 .arg(originalImage.width())
                                 .arg(originalImage.height())
                                 .arg(bitDepth));

    setWindowTitle(QString("图像查看器 - %1").arg(fileInfo.fileName()));
}

void MainWindow::updateImageDisplay()
{
    if (originalImage.isNull() || !pixmapItem) return;

    // 更新 pixmapItem 的 pixmap(无缩放生成,使用原图像)
    pixmapItem->setPixmap(QPixmap::fromImage(originalImage));

    // 应用缩放变换(无内存增加)
    QTransform transform = QTransform::fromScale(scaleFactor, scaleFactor);
    view->setTransform(transform);

    // 调整场景边界
    scene->setSceneRect(pixmapItem->boundingRect());
}

void MainWindow::updateStatusBar(int x, int y)
{
    if (originalImage.isNull() || x < 0 || y < 0 || x >= originalImage.width() || y >= originalImage.height()) {
        statusBar()->showMessage("鼠标位置无效");
        return;
    }

    int grayValue = getPixelValue(originalImage, x, y);

    statusBar()->showMessage(QString("坐标: (%1, %2) | 灰度值: %3").arg(x).arg(y).arg(grayValue));
}

int MainWindow::getPixelValue(const QImage &img, int x, int y)
{
    if (img.format() == QImage::Format_Grayscale16) {
        // 对于16位灰度,手动读取像素值
        const quint16 *pixel = reinterpret_cast<const quint16*>(img.scanLine(y)) + x;
        return static_cast<int>(*pixel);
    } else {
        // 对于8位灰度
        return qGray(img.pixel(x, y));
    }
}

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    // 修改检查:
    if (obj == view->viewport()) {  // 而非 obj == view
        if (event->type() == QEvent::Wheel && ctrlPressed) {
            QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
            QPoint pos = wheelEvent->position().toPoint();  // Qt6;Qt5 用 wheelEvent->pos()
            double oldScale = scaleFactor;
            double zoomFactor = (wheelEvent->angleDelta().y() > 0) ? 1.25 : 0.8;
            double newScale = oldScale * zoomFactor;
            scaleFactor = newScale;

            // 缩放以鼠标为中心(由于 AnchorUnderMouse 已设置)
            view->scale(zoomFactor, zoomFactor);
            // 更新状态栏(使用缩放前场景点)
            QPointF scenePos = view->mapToScene(pos);
            updateStatusBar(static_cast<int>(scenePos.x()), static_cast<int>(scenePos.y()));
            return true;  // 消费事件
        } else if (event->type() == QEvent::MouseMove) {
            // 可选调试:qDebug() << "MouseMove captured at viewport";
            QMouseEvent *me = static_cast<QMouseEvent*>(event);
            QPointF scenePos = view->mapToScene(me->pos());
            // 场景坐标即原始图像坐标(考虑 scaleFactor,但 mapToScene 已处理)
            int imageX = static_cast<int>(scenePos.x());
            int imageY = static_cast<int>(scenePos.y());
            updateStatusBar(imageX, imageY);
            return false;  // 不消费,让 view 继续处理(如拖拽)
        }
    }
    return QMainWindow::eventFilter(obj, event);
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Control) {
        ctrlPressed = true;
    }
    QMainWindow::keyPressEvent(event);
}

void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Control) {
        ctrlPressed = false;
    }
    QMainWindow::keyReleaseEvent(event);
}

void MainWindow::zoomIn()
{
    double factor = 1.25;
    double newScale = scaleFactor * factor;
    // 取消最大缩放限制检查
    // 以视图中心缩放
    view->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    view->scale(factor, factor);
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    scaleFactor = newScale;
}

void MainWindow::zoomOut()
{
    double factor = 0.8;
    double newScale = scaleFactor * factor;
    // 取消最小缩放限制检查
    // 以视图中心缩放
    view->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    view->scale(factor, factor);
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    scaleFactor = newScale;
}

void MainWindow::fitToWindow()
{
    if (originalImage.isNull()) return;

    view->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio);
    scaleFactor = view->transform().m11();  // 更新 scaleFactor 为实际值
}

void MainWindow::originalSize()
{
    // 以视图中心重置
    view->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    view->setTransform(QTransform());
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    scaleFactor = 1.0;
}

void MainWindow::autoContrast()
{
    if (originalImage.isNull()) return;

    applyAutoContrast();
    updateImageDisplay();

    statusBar()->showMessage("已应用自适应对比度");
}

void MainWindow::showOriginal()
{
    if (unmodifiedOriginalImage.isNull()) return;

    originalImage = unmodifiedOriginalImage;
    updateImageDisplay();

    statusBar()->showMessage("已恢复原图");
}

void MainWindow::applyAutoContrast()
{
    // 简单的自适应对比度:计算直方图,找到min/max非零值,进行拉伸
    if (originalImage.format() != QImage::Format_Grayscale8 &&
        originalImage.format() != QImage::Format_Grayscale16) {
        return;
    }

    int width = originalImage.width();
    int height = originalImage.height();
    int maxPossible = (originalImage.format() == QImage::Format_Grayscale8 ? 255 : 65535);
    int minVal = maxPossible, maxVal = 0;

    // 找到实际min/max
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            int val = getPixelValue(originalImage, x, y);
            minVal = std::min(minVal, val);
            maxVal = std::max(maxVal, val);
        }
    }

    if (minVal == maxVal) return;  // 全黑或全白,无需调整

    QImage contrastedImage(originalImage.size(), originalImage.format());

    if (originalImage.format() == QImage::Format_Grayscale8) {
        for (int y = 0; y < height; ++y) {
            uchar *srcLine = originalImage.scanLine(y);
            uchar *dstLine = contrastedImage.scanLine(y);
            for (int x = 0; x < width; ++x) {
                int val = srcLine[x];
                long long stretched = static_cast<long long>(val - minVal) * 255LL / (maxVal - minVal);
                dstLine[x] = static_cast<uchar>(stretched);
            }
        }
    } else {  // 16位
        for (int y = 0; y < height; ++y) {
            const quint16 *srcLine = reinterpret_cast<const quint16*>(originalImage.scanLine(y));
            quint16 *dstLine = reinterpret_cast<quint16*>(contrastedImage.scanLine(y));
            for (int x = 0; x < width; ++x) {
                int val = srcLine[x];
                long long stretched = static_cast<long long>(val - minVal) * 65535LL / (maxVal - minVal);
                dstLine[x] = static_cast<quint16>(stretched);
            }
        }
    }

    originalImage = contrastedImage;  // 更新显示图像
}
mainwindow.cpp

1

 

图像播放

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QImage>
#include <QPixmap>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>
#include <QFileDialog>
#include <QMessageBox>
#include <QSpinBox>
#include <QLabel>
#include <QTimer>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QDir>
#include <QFileInfo>
#include <vector>
#include <algorithm> // for std::min, std::max

QT_BEGIN_NAMESPACE
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

protected:
    bool eventFilter(QObject *obj, QEvent *event) override;
    void keyPressEvent(QKeyEvent *event) override;
    void keyReleaseEvent(QKeyEvent *event) override;

private slots:
    void loadDirectory();
    void startImageLoop();
    void stopImageLoop();
    void refreshImages();
    void updateLoopInterval(int ms);
    void zoomIn();
    void zoomOut();
    void fitToWindow();
    void originalSize();
    void autoContrast();
    void showOriginal();
    void updateImageDisplay();
    void updateStatusBar(int x, int y);

private:
    // UI 组件
    QWidget *centralWidget;
    QVBoxLayout *mainLayout;
    QHBoxLayout *buttonLayout;
    QPushButton *loadButton;
    QPushButton *zoomInButton;
    QPushButton *zoomOutButton;
    QPushButton *fitButton;
    QPushButton *originalButton;
    QPushButton *autoContrastButton;
    QPushButton *showOriginalButton;
    QPushButton *startPlayButton;
    QPushButton *stopPlayButton;
    QSpinBox *intervalSpinBox;
    QLabel *intervalLabel; // 可选,如果需要

    // 图像和视图组件
    QGraphicsScene *scene;
    QGraphicsView *view;
    QGraphicsPixmapItem *pixmapItem;
    QImage originalImage;
    QImage unmodifiedOriginalImage;

    // 交互状态
    double scaleFactor;
    bool ctrlPressed;
    bool leftMousePressed;
    QPoint lastDragPos;

    // 循环播放相关
    QTimer *refreshTimer;
    bool useLoop;
    int currentImageIndex;
    int loopInterval;
    std::vector<QImage> loopedImages;
    std::vector<QString> loopedFileNames;

    // 私有辅助函数
    void setupUI();
    QImage applyContrastStretch(const QImage& src, int minV, int maxV);
    void applyAutoContrast();
    int getPixelValue(const QImage &img, int x, int y);
    void updateWindowTitle();

signals:
    // 如果需要自定义信号,可在此添加
};

#endif // MAINWINDOW_H
mainwindow.h
#include "mainwindow.h"
#include <QApplication>
#include <algorithm> // for std::min, std::max
#include <QCursor>
#include <QTimer> // 新增:用于定时器
#include <vector> // 新增:用于存储多张图像
#include <QFileDialog>
#include <QDir>
#include <QMessageBox>
#include <QLabel>
#include <QSpinBox>
#include <QFileInfo>
#include <QStatusBar>
#include <QScrollBar>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), scaleFactor(1.0), ctrlPressed(false),
    leftMousePressed(false), lastDragPos(0, 0),
    scene(nullptr), view(nullptr), pixmapItem(nullptr),
    refreshTimer(nullptr), useLoop(false), currentImageIndex(0), loopInterval(100)
{
    setupUI();
    // 移除自动启动图像循环
    // view->installEventFilter(this); // 安装事件过滤器以捕获滚轮事件
    view->viewport()->installEventFilter(this); // 安装到 viewport 而非 view
}
MainWindow::~MainWindow()
{
    if (refreshTimer) {
        refreshTimer->stop();
        delete refreshTimer;
    }
    loopedImages.clear(); // 释放循环图像内存
    loopedFileNames.clear();
}
void MainWindow::setupUI()
{
    centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);
    mainLayout = new QVBoxLayout(centralWidget);
    buttonLayout = new QHBoxLayout();
    // 创建按钮
    loadButton = new QPushButton("加载目录", this);
    zoomInButton = new QPushButton("放大", this);
    zoomOutButton = new QPushButton("缩小", this);
    fitButton = new QPushButton("适应窗口", this);
    originalButton = new QPushButton("原始大小", this);
    autoContrastButton = new QPushButton("自适应对比度", this);
    showOriginalButton = new QPushButton("显示原图", this);
    // 新增:开始播放和停止播放按钮
    startPlayButton = new QPushButton("开始播放", this);
    stopPlayButton = new QPushButton("停止播放", this);
    // 新增:间隔设置
    QLabel* intervalLabel = new QLabel("间隔:", this);
    intervalSpinBox = new QSpinBox(this);
    intervalSpinBox->setRange(1, 10000);
    intervalSpinBox->setValue(100);
    intervalSpinBox->setSuffix(" ms");
    // 连接信号槽
    connect(loadButton, &QPushButton::clicked, this, &MainWindow::loadDirectory);
    connect(startPlayButton, &QPushButton::clicked, this, &MainWindow::startImageLoop);
    connect(stopPlayButton, &QPushButton::clicked, this, &MainWindow::stopImageLoop);
    connect(zoomInButton, &QPushButton::clicked, this, &MainWindow::zoomIn);
    connect(zoomOutButton, &QPushButton::clicked, this, &MainWindow::zoomOut);
    connect(fitButton, &QPushButton::clicked, this, &MainWindow::fitToWindow);
    connect(originalButton, &QPushButton::clicked, this, &MainWindow::originalSize);
    connect(autoContrastButton, &QPushButton::clicked, this, &MainWindow::autoContrast);
    connect(showOriginalButton, &QPushButton::clicked, this, &MainWindow::showOriginal);
    connect(intervalSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::updateLoopInterval);
    // 创建场景和视图(替换 scrollArea 和 imageLabel)
    scene = new QGraphicsScene(this);
    view = new QGraphicsView(scene, this);
    view->setAlignment(Qt::AlignCenter);
    view->setBackgroundBrush(view->palette().brush(QPalette::Dark));
    view->setDragMode(QGraphicsView::ScrollHandDrag); // 启用内置拖拽平移
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); // 缩放锚点在鼠标下
    view->setResizeAnchor(QGraphicsView::AnchorViewCenter); // 窗口调整大小时居中
    view->setRenderHint(QPainter::Antialiasing, false); // 禁用抗锯齿以保持像素化
    view->setMouseTracking(true); // 启用鼠标跟踪
    view->setDragMode(QGraphicsView::NoDrag); // 改为 NoDrag 以禁用内置拖拽
    view->setCursor(Qt::ArrowCursor); // 默认指针形状
    view->setVisible(false);
    // 布局设置
    buttonLayout->addWidget(loadButton);
    buttonLayout->addWidget(startPlayButton);
    buttonLayout->addWidget(stopPlayButton);
    buttonLayout->addWidget(zoomInButton);
    buttonLayout->addWidget(zoomOutButton);
    buttonLayout->addWidget(fitButton);
    buttonLayout->addWidget(originalButton);
    buttonLayout->addWidget(autoContrastButton);
    buttonLayout->addWidget(showOriginalButton);
    buttonLayout->addWidget(intervalLabel);
    buttonLayout->addWidget(intervalSpinBox);
    buttonLayout->addStretch();
    mainLayout->addLayout(buttonLayout);
    mainLayout->addWidget(view);
    // 状态栏
    statusBar()->showMessage("准备就绪");
    setWindowTitle("图像查看器");
    resize(800, 600);
}
// 新增:应用固定范围的自适应对比度拉伸
QImage MainWindow::applyContrastStretch(const QImage& src, int minV, int maxV)
{
    if (src.isNull() || minV == maxV) return src;
    QImage dst(src.size(), src.format());
    int width = src.width();
    int height = src.height();
    // 假设为 Grayscale16 格式
    for (int y = 0; y < height; ++y) {
        const quint16 *srcLine = reinterpret_cast<const quint16*>(src.scanLine(y));
        quint16 *dstLine = reinterpret_cast<quint16*>(dst.scanLine(y));
        for (int x = 0; x < width; ++x) {
            int val = srcLine[x];
            long long stretched = static_cast<long long>(val - minV) * 65535LL / (maxV - minV);
            dstLine[x] = static_cast<quint16>(std::max(0LL, std::min(65535LL, stretched)));
        }
    }
    return dst;
}
// 新增:加载目录中的所有图像
void MainWindow::loadDirectory()
{
    if (useLoop) {
        stopImageLoop(); // 如果在循环中,加载新目录时停止循环
    }
    QString dirPath = QFileDialog::getExistingDirectory(
        this,
        "选择图像目录",
        "",
        QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
        );
    if (dirPath.isEmpty()) {
        return;
    }
    // 扫描目录
    QDir dir(dirPath);
    QStringList filters = {
        "*.tif", "*.tiff", "*.png", "*.jpg", "*.jpeg", "*.bmp", "*.gif", "*.ico"
    };
    QStringList fileNames = dir.entryList(filters, QDir::Files, QDir::Name); // 按名称排序
    if (fileNames.isEmpty()) {
        QMessageBox::warning(this, "错误", "目录中没有找到支持的图像文件!");
        return;
    }
    loopedImages.clear();
    loopedFileNames.clear();
    bool allLoaded = true;
    for (const QString& fileName : fileNames) {
        QString fullPath = dir.absoluteFilePath(fileName);
        QImage img(fullPath);
        if (img.isNull()) {
            allLoaded = false;
            continue;
        }
        // 支持8位和16位灰度图像,如果不是灰度则转换为16位灰度
        if (img.format() != QImage::Format_Grayscale8 &&
            img.format() != QImage::Format_Grayscale16) {
            img = img.convertToFormat(QImage::Format_Grayscale16);
        }
        loopedImages.push_back(img);
        loopedFileNames.push_back(fileName);
    }
    if (loopedImages.empty()) {
        QMessageBox::warning(this, "错误", "无法加载任何图像文件!请检查文件格式和完整性。");
        return;
    }
    if (!allLoaded) {
        QMessageBox::warning(this, "警告", QString("部分文件加载失败!成功加载 %1 张图像。").arg(loopedImages.size()));
    }
    // 计算第一张图像的 min/max 作为全局自适应系数
    const QImage& firstImage = loopedImages[0];
    int width = firstImage.width();
    int height = firstImage.height();
    int minVal = 65535, maxVal = 0;
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            int val = getPixelValue(firstImage, x, y);
            minVal = std::min(minVal, val);
            maxVal = std::max(maxVal, val);
        }
    }
    // 应用固定范围拉伸到所有图像
    if (minVal != maxVal) {
        for (auto& img : loopedImages) {
            img = applyContrastStretch(img, minVal, maxVal);
        }
    }
    // 初始化
    scaleFactor = 1.0;
    currentImageIndex = 0;
    originalImage = loopedImages[0];
    unmodifiedOriginalImage = loopedImages[0]; // 已应用对比度
    // 清空场景并添加新项
    scene->clear();
    pixmapItem = new QGraphicsPixmapItem(QPixmap::fromImage(originalImage));
    pixmapItem->setTransformationMode(Qt::FastTransformation); // 像素化缩放
    scene->addItem(pixmapItem);
    view->setVisible(true);
    updateImageDisplay();
    // 更新状态栏
    statusBar()->showMessage(QString("目录加载完成: %1 张图像 | 点击开始播放").arg(loopedImages.size()));
    setWindowTitle(QString("图像查看器 - 已加载目录 (%1 张图像)").arg(loopedImages.size()));
}
// 新增:更新循环间隔
void MainWindow::updateLoopInterval(int ms)
{
    loopInterval = ms;
    if (useLoop && refreshTimer) {
        refreshTimer->setInterval(ms);
    }
}
// 新增:启动图像循环功能(基于已加载的目录图像)
void MainWindow::startImageLoop()
{
    if (loopedImages.empty()) {
        QMessageBox::warning(this, "错误", "请先加载目录!");
        return;
    }
    if (useLoop) {
        return; // 已启动
    }
    // 初始化
    currentImageIndex = 0;
    originalImage = loopedImages[0];
    updateImageDisplay();
    // 创建并启动定时器
    if (!refreshTimer) {
        refreshTimer = new QTimer(this);
        connect(refreshTimer, &QTimer::timeout, this, &MainWindow::refreshImages);
    }
    refreshTimer->setInterval(loopInterval);
    refreshTimer->start();
    useLoop = true;
    // 更新状态栏
    statusBar()->showMessage(QString("已启动图像循环刷新 (%1 ms 间隔,已应用自适应对比度)").arg(loopInterval));
    updateWindowTitle(); // 更新标题显示当前图像
}
// 新增:停止图像循环
void MainWindow::stopImageLoop()
{
    if (!useLoop || !refreshTimer) return;
    refreshTimer->stop();
    useLoop = false;
    statusBar()->showMessage("已停止图像循环");
    setWindowTitle("图像查看器");
    // 保留最后一张图像显示
}
// 新增:刷新图像的槽函数
void MainWindow::refreshImages()
{
    if (!useLoop || loopedImages.empty()) return;
    currentImageIndex = (currentImageIndex + 1) % loopedImages.size(); // 循环索引
    originalImage = loopedImages[currentImageIndex];
    updateImageDisplay();
    updateWindowTitle(); // 更新标题
    // 更新状态栏显示当前图像(可选,减少频率以避免性能问题)
    // statusBar()->showMessage(QString("循环刷新: %1 | 尺寸: %2x%3")
    // .arg(QFileInfo(loopedFileNames[currentImageIndex]).fileName())
    // .arg(originalImage.width())
    // .arg(originalImage.height()));
}
// 新增:更新窗口标题
void MainWindow::updateWindowTitle()
{
    if (loopedFileNames.empty()) return;
    setWindowTitle(QString("图像查看器 - 循环模式 (%1 / %2): %3")
                       .arg(currentImageIndex + 1)
                       .arg(loopedImages.size())
                       .arg(QFileInfo(loopedFileNames[currentImageIndex]).fileName()));
}

void MainWindow::updateImageDisplay()
{
    if (originalImage.isNull() || !pixmapItem) return;
    // 更新 pixmapItem 的 pixmap(无缩放生成,使用原图像)
    pixmapItem->setPixmap(QPixmap::fromImage(originalImage));
    // 应用缩放变换(无内存增加)
    QTransform transform = QTransform::fromScale(scaleFactor, scaleFactor);
    view->setTransform(transform);
    // 调整场景边界
    scene->setSceneRect(pixmapItem->boundingRect());
    // 强制视图更新(确保刷新)
    view->viewport()->update();
}
void MainWindow::updateStatusBar(int x, int y)
{
    if (originalImage.isNull() || x < 0 || y < 0 || x >= originalImage.width() || y >= originalImage.height()) {
        statusBar()->showMessage("鼠标位置无效");
        return;
    }
    int grayValue = getPixelValue(originalImage, x, y);
    statusBar()->showMessage(QString("坐标: (%1, %2) | 灰度值: %3").arg(x).arg(y).arg(grayValue));
}
int MainWindow::getPixelValue(const QImage &img, int x, int y)
{
    if (img.format() == QImage::Format_Grayscale16) {
        // 对于16位灰度,手动读取像素值
        const quint16 *pixel = reinterpret_cast<const quint16*>(img.scanLine(y)) + x;
        return static_cast<int>(*pixel);
    } else {
        // 对于8位灰度
        return qGray(img.pixel(x, y));
    }
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == view->viewport()) {
        if (event->type() == QEvent::Wheel && ctrlPressed) {
            QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
            QPoint pos = wheelEvent->position().toPoint(); // Qt6;Qt5 用 wheelEvent->pos()
            double oldScale = scaleFactor;
            double zoomFactor = (wheelEvent->angleDelta().y() > 0) ? 1.25 : 0.8;
            double newScale = oldScale * zoomFactor;
            scaleFactor = newScale;
            // 缩放以鼠标为中心(由于 AnchorUnderMouse 已设置)
            view->scale(zoomFactor, zoomFactor);
            // 更新状态栏(使用缩放前场景点)
            QPointF scenePos = view->mapToScene(pos);
            updateStatusBar(static_cast<int>(scenePos.x()), static_cast<int>(scenePos.y()));
            return true; // 消费事件
        } else if (event->type() == QEvent::MouseButtonPress) {
            QMouseEvent *me = static_cast<QMouseEvent*>(event);
            if (me->button() == Qt::LeftButton && !ctrlPressed) { // 避免与缩放冲突
                leftMousePressed = true;
                lastDragPos = me->pos();
                view->viewport()->setCursor(Qt::ClosedHandCursor);
                return true; // 消费事件,开始拖拽
            }
        } else if (event->type() == QEvent::MouseButtonRelease) {
            QMouseEvent *me = static_cast<QMouseEvent*>(event);
            if (me->button() == Qt::LeftButton && leftMousePressed) {
                leftMousePressed = false;
                view->viewport()->setCursor(Qt::ArrowCursor);
                return false; // 释放事件,让视图处理
            }
        } else if (event->type() == QEvent::MouseMove) {
            QMouseEvent *me = static_cast<QMouseEvent*>(event);
            if (leftMousePressed) {
                // 拖拽逻辑
                QPoint delta = me->pos() - lastDragPos;
                lastDragPos = me->pos();
                // 通过滚动条实现平移
                QScrollBar *hBar = view->horizontalScrollBar();
                QScrollBar *vBar = view->verticalScrollBar();
                hBar->setValue(hBar->value() - delta.x());
                vBar->setValue(vBar->value() - delta.y());
                return true; // 消费事件
            } else {
                // 更新状态栏
                QPointF scenePos = view->mapToScene(me->pos());
                int imageX = static_cast<int>(scenePos.x());
                int imageY = static_cast<int>(scenePos.y());
                updateStatusBar(imageX, imageY);
                return false; // 不消费,让视图继续处理
            }
        }
    }
    return QMainWindow::eventFilter(obj, event);
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Control) {
        ctrlPressed = true;
    }
    QMainWindow::keyPressEvent(event);
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Control) {
        ctrlPressed = false;
    }
    QMainWindow::keyReleaseEvent(event);
}
void MainWindow::zoomIn()
{
    double factor = 1.25;
    double newScale = scaleFactor * factor;
    // 取消最大缩放限制检查
    // 以视图中心缩放
    view->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    view->scale(factor, factor);
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    scaleFactor = newScale;
}
void MainWindow::zoomOut()
{
    double factor = 0.8;
    double newScale = scaleFactor * factor;
    // 取消最小缩放限制检查
    // 以视图中心缩放
    view->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    view->scale(factor, factor);
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    scaleFactor = newScale;
}
void MainWindow::fitToWindow()
{
    if (originalImage.isNull()) return;
    view->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio);
    scaleFactor = view->transform().m11(); // 更新 scaleFactor 为实际值
}
void MainWindow::originalSize()
{
    // 以视图中心重置
    view->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    view->setTransform(QTransform());
    view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    scaleFactor = 1.0;
}
void MainWindow::autoContrast()
{
    if (originalImage.isNull() || useLoop) { // 在循环模式下禁用对比度(避免干扰循环)
        if (useLoop) {
            statusBar()->showMessage("循环模式下无法应用对比度(已预应用)");
        }
        return;
    }
    applyAutoContrast();
    updateImageDisplay();
    statusBar()->showMessage("已应用自适应对比度");
}
void MainWindow::showOriginal()
{
    if (unmodifiedOriginalImage.isNull() || useLoop) { // 在循环模式下禁用
        if (useLoop) {
            statusBar()->showMessage("循环模式下无法恢复原图(已预应用对比度)");
        }
        return;
    }
    originalImage = unmodifiedOriginalImage;
    updateImageDisplay();
    statusBar()->showMessage("已恢复原图");
}
void MainWindow::applyAutoContrast()
{
    // 简单的自适应对比度:计算直方图,找到min/max非零值,进行拉伸
    if (originalImage.format() != QImage::Format_Grayscale8 &&
        originalImage.format() != QImage::Format_Grayscale16) {
        return;
    }
    int width = originalImage.width();
    int height = originalImage.height();
    int maxPossible = (originalImage.format() == QImage::Format_Grayscale8 ? 255 : 65535);
    int minVal = maxPossible, maxVal = 0;
    // 找到实际min/max
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            int val = getPixelValue(originalImage, x, y);
            minVal = std::min(minVal, val);
            maxVal = std::max(maxVal, val);
        }
    }
    if (minVal == maxVal) return; // 全黑或全白,无需调整
    QImage contrastedImage(originalImage.size(), originalImage.format());
    if (originalImage.format() == QImage::Format_Grayscale8) {
        for (int y = 0; y < height; ++y) {
            uchar *srcLine = originalImage.scanLine(y);
            uchar *dstLine = contrastedImage.scanLine(y);
            for (int x = 0; x < width; ++x) {
                int val = srcLine[x];
                long long stretched = static_cast<long long>(val - minVal) * 255LL / (maxVal - minVal);
                dstLine[x] = static_cast<uchar>(stretched);
            }
        }
    } else { // 16位
        for (int y = 0; y < height; ++y) {
            const quint16 *srcLine = reinterpret_cast<const quint16*>(originalImage.scanLine(y));
            quint16 *dstLine = reinterpret_cast<quint16*>(contrastedImage.scanLine(y));
            for (int x = 0; x < width; ++x) {
                int val = srcLine[x];
                long long stretched = static_cast<long long>(val - minVal) * 65535LL / (maxVal - minVal);
                dstLine[x] = static_cast<quint16>(stretched);
            }
        }
    }
    originalImage = contrastedImage; // 更新显示图像
}
mainwindow.cpp

GIF 2025-11-10 20-41-27

 

posted @ 2025-10-30 16:34  阿坦  阅读(13)  评论(0)    收藏  举报