首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何批量识别出照片中的水印文字,并将文字作为照片名进行改名分类?基于QT和腾讯OCR的项目实战

如何批量识别出照片中的水印文字,并将文字作为照片名进行改名分类?基于QT和腾讯OCR的项目实战

原创
作者头像
不负众望
发布2025-03-26 17:00:51
发布2025-03-26 17:00:51
29600
代码可运行
举报
运行总次数:0
代码可运行

一、项目背景

在日常工作和生活中,我们常常需要处理大量的照片文件,这些照片中可能包含有用的文字信息。手动识别这些文字并对相应的照片进行重命名是一项繁琐且容易出错的工作。为了解决这一问题,本项目旨在开发一个基于QT和腾讯OCR(光学字符识别)技术的应用程序,实现批量识别照片中的文字并将识别出的文字作为照片的新文件名。

通过本项目,用户可以:

  • 批量选择包含文字的照片文件。
  • 利用腾讯OCR API自动识别每张照片中的文字。
  • 将识别出的文字作为照片的新文件名,实现自动化重命名。
  • 提高工作效率,减少手动操作的错误。

二、界面设计

QT提供了丰富的UI组件和灵活的布局方式,适合构建功能强大且用户友好的桌面应用。以下是该应用的主要界面设计元素:

1. 主窗口布局

  • 菜单栏
    • 文件:选择照片文件夹、退出应用
    • 帮助:关于、帮助文档
  • 工具栏
    • 选择文件夹按钮
    • 开始识别按钮
    • 暂停/继续按钮(可选)
  • 主内容区
    • 照片列表展示:显示已选择的照片缩略图,支持多选和删除。
    • OCR结果展示:显示每张照片的识别结果,支持手动编辑。
    • 进度条:显示当前操作进度。
    • 日志输出区:实时显示操作日志和错误信息。
  • 状态栏
    • 显示当前选中照片数量、已处理照片数量等信息。

2. 用户流程

  1. 选择照片文件夹:用户通过菜单或工具栏选择包含照片的文件夹。
  2. 加载照片:系统加载并展示所选文件夹中的所有照片缩略图。
  3. 开始识别:用户点击“开始识别”按钮,程序调用腾讯OCR API进行文字识别。
  4. 显示结果:识别结果实时显示在界面上,用户可以查看和编辑。
  5. 重命名照片:根据识别出的文字,自动将照片重命名为识别到的文字内容。
  6. 完成提示:系统完成所有操作后,显示处理结果和日志信息。

三、详细代码

1. 环境准备

  • QT版本:建议使用QT 5.12及以上版本。
  • 编译器:支持C++11及以上标准的编译器。
  • 腾讯OCR SDK:使用腾讯云提供的C++ SDK或通过HTTP接口调用RESTful API。
  • 第三方库:用于HTTP请求(如QNetworkAccessManager)、JSON解析(如nlohmann/json或QT自带的JSON解析器)、图像处理(如OpenCV)等。

2. 项目结构

代码语言:javascript
代码运行次数:0
运行
复制
PhotoOCRRenamer/
├── main.cpp
├── mainwindow.ui
├── mainwindow.cpp
├── mainwindow.h
├── ocrmanager.cpp
├── ocrmanager.h
├── photomanager.cpp
├── photomanager.h
├── utils.cpp
├── utils.h
├── resources.qrc
└── PhotoOCRRenamer.pro

3. 主要代码实现

3.1. main.cpp
代码语言:javascript
代码运行次数:0
运行
复制
cpp#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
3.2. mainwindow.h
代码语言:javascript
代码运行次数:0
运行
复制
cpp#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "photomanager.h"
#include "ocrmanager.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_selectFolderButton_clicked();
    void on_startButton_clicked();
    void updateProgress(int value);
    void logMessage(const QString &msg);
    void ocrFinished(const QString &photoPath, const QString &ocrText);

private:
    Ui::MainWindow *ui;
    PhotoManager *photoManager;
    OcrManager *ocrManager;
};

#endif // MAINWINDOW_H
3.3. mainwindow.cpp
代码语言:javascript
代码运行次数:0
运行
复制
cpp#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QDir>
#include <QFileInfo>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    photoManager = new PhotoManager(this);
    ocrManager = new OcrManager(this);

    // 连接信号与槽
    connect(ui->selectFolderButton, &QPushButton::clicked, this, &MainWindow::on_selectFolderButton_clicked);
    connect(ui->startButton, &QPushButton::clicked, this, &MainWindow::on_startButton_clicked);
    connect(ocrManager, &OcrManager::progressUpdated, this, &MainWindow::updateProgress);
    connect(ocrManager, &OcrManager::logMessage, this, &MainWindow::logMessage);
    connect(ocrManager, &OcrManager::ocrFinished, this, &MainWindow::ocrFinished);

    // 设置初始状态
    ui->startButton->setEnabled(false);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_selectFolderButton_clicked()
{
    QString folderPath = QFileDialog::getExistingDirectory(this, "选择照片文件夹");
    if (!folderPath.isEmpty()) {
        photoManager->loadPhotos(folderPath);
        ui->startButton->setEnabled(photoManager->photoCount() > 0);
        ui->statusBar->showMessage("已加载 " + QString::number(photoManager->photoCount()) + " 张照片");
    }
}

void MainWindow::on_startButton_clicked()
{
    if (photoManager->photoCount() == 0) {
        QMessageBox::warning(this, "警告", "请先选择照片文件夹!");
        return;
    }

    // 配置OCR参数(如API密钥等)
    ocrManager->setApiKey("YOUR_TENCENT_CLOUD_API_KEY");
    ocrManager->setSecretKey("YOUR_TENCENT_CLOUD_SECRET_KEY");

    // 开始OCR处理
    ocrManager->startProcessing(photoManager->photos());
}

void MainWindow::updateProgress(int value)
{
    ui->progressBar->setValue(value);
}

void MainWindow::logMessage(const QString &msg)
{
    ui->logTextEdit->append(msg);
}

void MainWindow::ocrFinished(const QString &photoPath, const QString &ocrText)
{
    // 重命名照片
    QFileInfo fileInfo(photoPath);
    QString newFileName = sanitizeFileName(ocrText) + QFileInfo(photoPath).suffix();
    QString newFilePath = fileInfo.dir().filePath(newFileName);

    int counter = 1;
    while (QFile::exists(newFilePath)) {
        newFileName = sanitizeFileName(ocrText) + "_" + QString::number(counter) + QFileInfo(photoPath).suffix();
        newFilePath = fileInfo.dir().filePath(newFileName);
        counter++;
    }

    bool success = QFile::rename(photoPath, newFilePath);
    if (success) {
        logMessage("重命名成功: " + photoPath + " -> " + newFilePath);
    } else {
        logMessage("重命名失败: " + photoPath);
    }

    // 更新进度
    int current = photoManager->processedCount() + 1;
    int total = photoManager->photoCount();
    updateProgress((current * 100) / total);

    // 检查是否所有照片都已处理
    if (current == total) {
        logMessage("所有照片处理完成!");
    }
}

QString MainWindow::sanitizeFileName(const QString &text)
{
    QString result = text;
    // 移除非法字符
    QRegularExpression regex("[<>:\"/\\\\|?*]");
    result.remove(regex);
    // 去除前后空格并替换内部空格为下划线
    result = result.trimmed().replace(" ", "_");
    return result;
}
3.4. photomanager.h
代码语言:javascript
代码运行次数:0
运行
复制
cpp#ifndef PHOTOMANAGER_H
#define PHOTOMANAGER_H

#include <QObject>
#include <QStringList>
#include <QFileInfo>

class PhotoManager : public QObject
{
    Q_OBJECT

public:
    explicit PhotoManager(QObject *parent = nullptr);
    void loadPhotos(const QString &folderPath);
    int photoCount() const;
    QList<QString> photos() const;
    int processedCount() const;

signals:
    void photoLoaded(const QString &photoPath);

public slots:
    void markProcessed(const QString &photoPath);

private:
    QList<QString> m_photos;
    QList<QString> m_processedPhotos;
};

#endif // PHOTOMANAGER_H
3.5. photomanager.cpp
代码语言:javascript
代码运行次数:0
运行
复制
cpp#include "photomanager.h"
#include <QDir>
#include <QFileInfo>

PhotoManager::PhotoManager(QObject *parent) : QObject(parent)
{
}

void PhotoManager::loadPhotos(const QString &folderPath)
{
    QDir dir(folderPath);
    QStringList filters;
    filters << "*.jpg" << "*.jpeg" << "*.png" << "*.bmp" << "*.tiff";
    dir.setNameFilters(filters);
    QFileInfoList list = dir.entryInfoList(QDir::Files);

    m_photos.clear();
    for(auto &info : list){
        m_photos.append(info.absoluteFilePath());
        emit photoLoaded(info.absoluteFilePath());
    }
}

int PhotoManager::photoCount() const
{
    return m_photos.size();
}

QList<QString> PhotoManager::photos() const
{
    return m_photos;
}

int PhotoManager::processedCount() const
{
    return m_processedPhotos.size();
}

void PhotoManager::markProcessed(const QString &photoPath)
{
    m_processedPhotos.append(photoPath);
}
3.6. ocrmanager.h
代码语言:javascript
代码运行次数:0
运行
复制
cpp#ifndef OCRMANAGER_H
#define OCRMANAGER_H

#include <QObject>
#include <QString>
#include <QList>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

class OcrManager : public QObject
{
    Q_OBJECT

public:
    explicit OcrManager(QObject *parent = nullptr);
    void setApiKey(const QString &apiKey);
    void setSecretKey(const QString &secretKey);
    void startProcessing(const QList<QString> &photoPaths);

signals:
    void progressUpdated(int percentage);
    void logMessage(const QString &msg);
    void ocrFinished(const QString &photoPath, const QString &ocrText);

private slots:
    void onReplyFinished(QNetworkReply *reply);

private:
    QString m_apiKey;
    QString m_secretKey;
    QNetworkAccessManager *m_networkManager;
    QList<QString> m_photoPaths;
    int m_processed;

    void performOcr(const QString &photoPath);
    QByteArray encodePhotoToBase64(const QString &filePath);
};

#endif // OCRMANAGER_H
3.7. ocrmanager.cpp
代码语言:javascript
代码运行次数:0
运行
复制
cpp#include "ocrmanager.h"
#include <QFile>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QJsonParseError>

OcrManager::OcrManager(QObject *parent) : QObject(parent),
    m_networkManager(new QNetworkAccessManager(this)),
    m_processed(0)
{
    // 腾讯云OCR的API端点(以通用文字识别为例)
    // 请根据实际API文档调整URL和请求参数
}

void OcrManager::setApiKey(const QString &apiKey)
{
    m_apiKey = apiKey;
}

void OcrManager::setSecretKey(const QString &secretKey)
{
    m_secretKey = secretKey;
}

void OcrManager::startProcessing(const QList<QString> &photoPaths)
{
    m_photoPaths = photoPaths;
    m_processed = 0;

    // 假设腾讯OCR需要逐个处理照片,这里采用串行处理
    // 可以根据需求改为并行处理,但需注意API调用频率限制
    QTimer::singleShot(0, this, [this]() {
        if (!m_photoPaths.isEmpty()) {
            performOcr(m_photoPaths.takeFirst());
        }
    });
}

void OcrManager::performOcr(const QString &photoPath)
{
    QFile file(photoPath);
    if (!file.open(QIODevice::ReadOnly)) {
        emit logMessage("无法打开文件: " + photoPath);
        markProcessed(photoPath);
        checkCompletion();
        return;
    }

    QByteArray imageData = file.readAll();
    file.close();

    // 腾讯OCR通用文字识别API需要将图片进行Base64编码
    QByteArray base64Data = imageData.toBase64();
    QString base64Str = QString::fromLatin1(base64Data);

    // 构建JSON请求体
    QJsonObject requestBody;
    requestBody["ImageBase64"] = base64Str;

    QJsonDocument jsonDoc(requestBody);
    QByteArray jsonData = jsonDoc.toJson();

    // 设置HTTP请求头
    QNetworkRequest request(QUrl("https://ocr.tencentcloudapi.com")); // 替换为实际的API端点
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    // 添加认证头,如API Key等,具体参考腾讯云OCR文档
    // request.setRawHeader("Authorization", "Your_Auth_Header");

    QNetworkReply *reply = m_networkManager->post(request, jsonData);
    connect(reply, &QNetworkReply::finished, this, &OcrManager::onReplyFinished);
}

void OcrManager::onReplyFinished(QNetworkReply *reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        emit logMessage("OCR请求失败: " + reply->errorString());
    } else {
        QByteArray responseData = reply->readAll();
        QJsonParseError parseError;
        QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &parseError);
        if (parseError.error != QJsonParseError::NoError) {
            emit logMessage("JSON解析失败: " + parseError.errorString());
            return;
        }

        if (!jsonDoc.isObject()) {
            emit logMessage("无效的JSON响应");
            return;
        }

        QJsonObject jsonObj = jsonDoc.object();
        // 根据腾讯OCR的响应结构解析识别结果
        // 假设响应中有"TextDetections"数组,每个元素包含"DetectedText"
        if (jsonObj.contains("TextDetections") && jsonObj["TextDetections"].isArray()) {
            QJsonArray detections = jsonObj["TextDetections"].toArray();
            QString ocrText;
            for(auto &det : detections) {
                if(det.isObject() && det.toObject().contains("DetectedText")) {
                    ocrText += det.toObject()["DetectedText"].toString() + "
";
                }
            }
            // 去除最后一个换行符
            if(!ocrText.isEmpty()) {
                ocrText.chop(1);
            }
            emit ocrFinished(currentPhotoPath(), ocrText.trimmed());
        } else {
            emit logMessage("未找到识别结果");
        }
    }

    reply->deleteLater();
    markProcessed(currentPhotoPath());
    checkCompletion();
}

void OcrManager::markProcessed(const QString &photoPath)
{
    m_processed++;
    // 可以在这里记录已处理的照片路径,防止重复处理
}

void OcrManager::checkCompletion()
{
    if(m_processed >= m_photoPaths.size()) { // 注意:这里假设串行处理,m_processed与m_photoPaths.size()对应
        emit logMessage("所有照片处理完成!");
        // 可以在这里发出完成信号
    } else {
        // 继续处理下一张照片
        QTimer::singleShot(0, this, [this]() {
            if(!m_photoPaths.isEmpty()) {
                performOcr(m_photoPaths.takeFirst());
            }
        });
    }
}

QString OcrManager::currentPhotoPath() const
{
    // 需要在performOcr中记录当前处理的照片路径
    // 这里简化处理,假设每次调用performOcr时m_photoPaths的第一个为当前
    if(!m_photoPaths.isEmpty()) {
        return m_photoPaths.first();
    }
    return QString();
}

QByteArray OcrManager::encodePhotoToBase64(const QString &filePath)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly)) {
        return QByteArray();
    }
    return file.readAll().toBase64();
}

注意

  • API端点和认证:上述代码中的API端点https://ocr.tencentcloudapi.com仅为示例,实际使用时需要替换为腾讯云OCR的具体API地址,并按照腾讯云的文档配置认证信息(如API Key、Secret Key等)。
  • 请求参数:腾讯OCR的不同服务(如通用文字识别、身份证识别等)可能需要不同的请求参数,请参考腾讯云OCR文档进行具体实现。
  • 错误处理:实际项目中需要更完善的错误处理机制,包括网络错误、API调用失败、响应解析错误等。
  • 并发处理:为提高效率,可以考虑并发处理多张照片,但需注意腾讯云API的调用频率限制,避免被封禁。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、界面设计
    • 2. 用户流程
  • 三、详细代码
    • 1. 环境准备
    • 2. 项目结构
    • 3. 主要代码实现
      • 3.1. main.cpp
      • 3.2. mainwindow.h
      • 3.3. mainwindow.cpp
      • 3.4. photomanager.h
      • 3.5. photomanager.cpp
      • 3.6. ocrmanager.h
      • 3.7. ocrmanager.cpp
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档