Qtの応用 - ImageMagick

提供:MochiuWiki : SUSE, EC, PCB
2024年11月11日 (月) 18:37時点におけるWiki (トーク | 投稿記録)による版 (→‎概要)
ナビゲーションに移動 検索に移動

概要

ImageMagickライブラリは、画像処理を行う場合の強力なツールである。
画像の読み込み、編集、変換、保存等、包括的な画像処理機能を提供している。

ImageMagickライブラリは200以上の画像フォーマットをサポートしており、プロフェッショナルな画像処理が可能である。

画像の変換や編集において、ぼかし、シャープ化、回転、リサイズ等の基本的な操作から高度なフィルタ処理まで実行できる。
また、画像のメタデータの読み取りや編集も可能である。

ImageMagickライブラリのパフォーマンスについて、大量の画像処理を行う場合はメモリ使用量に注意が必要となる。
必要に応じて、画像のキャッシュサイズを調整することができる。

スレッド安全性のにおいて、ImageMagickライブラリは基本的にスレッドセーフであるが、初期化処理は必ずメインスレッドで行う必要がある。

ImageMagickライブラリは高度な画像処理機能を提供する強力なツールとして活用できる。
上記の例は基本的な使用方法の一部であり、さらに多くの機能や最適化オプションが用意されている。


画像の読み込み

 #include <Magick++.h>
 
 // ライブラリの初期化
 Magick::InitializeMagick(nullptr);
 
 // 画像の読み込み
 Magick::Image image;
 image.read("input.jpg");



画像のサイズ変更

 #include <Magick++.h>
 
 // ライブラリの初期化
 Magick::InitializeMagick(nullptr);
 
 // 画像のリサイズ
 image.resize(Magick::Geometry(800, 600));



画像の保存

 #include <Magick++.h>
 
 // ライブラリの初期化
 Magick::InitializeMagick(nullptr);
 
 // 画像の保存
 image.write("output.png");



画質調整

JPEG画質の設定やノイズ除去等、画質に関する詳細な制御が可能である。

 #include <Magick++.h>
 
 // ライブラリの初期化
 Magick::InitializeMagick(nullptr);
 
 Magick::Image image("input.jpg");
 
 // JPEG品質を85%に設定
 image.quality(85);
 
 image.write("output.jpg");



エラーハンドリング

ImageMagickライブラリは、専用の例外処理を使用してエラーを処理する。

 
 #include <Magick++.h>
 
 // ライブラリの初期化
 Magick::InitializeMagick(nullptr);
 
 try {
    Magick::Image image;
    image.read("nonexistent.jpg");
 }
 catch (Magick::Exception &error) {
    qDebug() << "エラー: " << QString::fromStdString(error.what());
 }



ImageMagickのインストール

パッケージ管理システムからインストール

# RHEL
sudo dnf install ImageMagick-devel libMagick++-devel

# SUSE
sudo zypper install ImageMagick-devel libMagick++-devel


ソースコードからインストール



基本的な使用例

以下の例では、ImageMagickライブラリを使用して、画像処理 (ノイズ除去、コントラスト調整、シャープネス強調、カラーバランス調整) を行っている。

  • 非同期処理
    QtConcurrentクラスを使用して画像処理を非同期で実行する。(メインスレッドをブロックせずに実行)
    QFutureWatcherクラスを使用して、処理の完了を監視する。
    シグナル / スロットによる非同期通信を行う。

  • ストリーミング処理
    画像処理をパイプライン形式で実装する。
    進捗状況をリアルタイムで通知する。
    段階的な画像処理を行う。(ノイズ除去、コントラスト調整等)

  • その他の機能
    スマートポインタを使用したメモリ管理


 # CMakeLists.txtファイル
 
 # Qt 5 または Qt 6コンポーネントを検索
 find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Concurrent)
 find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Concurrent)
 
 if(NOT Qt${QT_VERSION_MAJOR}_FOUND)
    find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Concurrent)
 endif()
 
 pkg_check_modules(QT_CORE Qt${QT_VERSION_MAJOR}Core REQUIRED IMPORTED_TARGET)
 pkg_check_modules(QT_CORE Qt${QT_VERSION_MAJOR}Concurrent REQUIRED IMPORTED_TARGET)
 
 # ImageMagick++を検索
 find_package(ImageMagick REQUIRED COMPONENTS Magick++)
 if(NOT ImageMagick_FOUND)
    message(FATAL_ERROR "ImageMagick++が見つかりません")
 endif()
 
 # インクルードディレクトリを設定
 target_include_directories(${PROJECT_NAME} PRIVATE
    # ...略
    ${ImageMagick_INCLUDE_DIRS}
 )
 
 # 各ライブラリをリンク
 target_link_libraries(${PROJECT_NAME} PRIVATE
    Qt${QT_VERSION_MAJOR}::Core
    Qt${QT_VERSION_MAJOR}::Concurrent
    ${ImageMagick_LIBRARIES}
 )


 // ImageProcessor.hファイル
 
 #include <QObject>
 #include <QFile>
 #include <QFuture>
 #include <QFutureWatcher>
 #include <QtConcurrent>
 #include <memory>
 #include <stdexcept>
 #include <Magick++.h>
 #include <QDebug>
 
 class ImageProcessor : public QObject
 {
    Q_OBJECT
 
 
 private:
    std::unique_ptr<QFutureWatcher<void>> m_futureWatcher;
 
    // 画像処理の実装
    void processImage(const QString& inputPath, const QString& outputPath)
    {
       try {
          // ImageMagickの初期化
          Magick::InitializeMagick(nullptr);
 
          // 画像の読み込み
          Magick::Image image;
          image.read(inputPath.toStdString());
 
          // ストリーミング処理の実行
          processImageStream(image);
 
          // 画像の保存
          image.write(outputPath.toStdString());
 
          emit progressUpdated(100);
       }
       catch (const Magick::Exception &e) {
          throw std::runtime_error(QString("ImageMagick処理エラー: %1").arg(e.what()).toStdString());
       }
       catch (const std::exception &e) {
          throw std::runtime_error(QString("一般エラー: %1").arg(e.what()).toStdString());
       }
    }
 
    // ストリーミング処理の実装
    void processImageStream(Magick::Image& image)
    {
       try {
          // 画像処理のパイプライン
          // 1. ノイズ除去
          image.reduceNoise();
          emit progressUpdated(25);
 
          // 2. コントラスト調整
          image.normalize();
          emit progressUpdated(50);
 
          // 3. シャープネス強調
          image.sharpen(0, 1.0);
          emit progressUpdated(75);
 
          // 4. カラーバランス調整
          image.modulate(100, 100, 100);
          emit progressUpdated(90);
       }
       catch (const Magick::Exception &e) {
          throw std::runtime_error(QString("ストリーム処理エラー: %1").arg(e.what()).toStdString());
       }
    }
 
 public:
    explicit ImageProcessor(QObject* parent = nullptr) : QObject(parent), m_futureWatcher(std::make_unique<QFutureWatcher<void>>())
    {
       // 非同期処理完了時のシグナルとスロットを接続
       connect(m_futureWatcher.get(), &QFutureWatcher<void>::finished, this, &ImageProcessor::handleProcessingFinished);
    }
 
    ~ImageProcessor() = default;
 
    // 画像処理を開始する
    void processImageAsync(const QString& inputPath, const QString& outputPath)
    {
       try {
          // 入力ファイルの存在確認
          if (!QFile::exists(inputPath)) {
             emit errorOccurred("入力ファイルが存在しない: " + inputPath);
             return;
          }
 
          // 非同期処理を開始
          QFuture<void> future = QtConcurrent::run([this, inputPath, outputPath]() {
             try {
                processImage(inputPath, outputPath);
             }
             catch (const std::exception& e) {
                // 非同期処理内でのエラーをメインスレッドに通知
                QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, Q_ARG(QString, QString("画像処理エラー: %1").arg(e.what())));
             }
          });
 
          m_futureWatcher->setFuture(future);
       }
       catch (const std::exception &e) {
          emit errorOccurred(QString("非同期処理の開始に失敗: %1").arg(e.what()));
       }
    }
 
 signals:
    void progressUpdated(int percentage);      // 進捗状況を通知するシグナル
    void processingCompleted();                // 処理完了を通知するシグナル
    void errorOccurred(const QString& error);  // エラーを通知するシグナル
 
 private slots:
    // 非同期処理完了時のスロット
    void handleProcessingFinished()
    {
       try {
          // 非同期処理の結果を確認
          m_futureWatcher->result();
          emit processingCompleted();
       }
       catch (const std::exception& e) {
          emit errorOccurred(QString("処理完了時のエラー: %1").arg(e.what()));
       }
    }
 };


 // main.cppファイル
 
 #include <QCoreApplication>
 #include "ImageProcessor.h"
 
 int main(int argc, char *argv[])
 {
    QCoreApplication app(argc, argv);
 
    ImageProcessor processor;
 
    // 進捗処理シグナルの接続
    QObject::connect(&processor, &ImageProcessor::progressUpdated, [](int percentage) {
       qDebug() << "進捗: " << percentage << "%";
    });
 
    // 処理完了シグナルの接続
    QObject::connect(&processor, &ImageProcessor::processingCompleted, []() {
       qDebug() << "処理が完了しました";
       QCoreApplication::quit();
    });
 
    // エラーシグナルの接続
    QObject::connect(&processor, &ImageProcessor::errorOccurred, [](const QString& error) {
       qDebug() << "エラー: " << error;
       QCoreApplication::quit();
    });
 
    // 画像処理の開始
    processor.processImageAsync("input.jpg", "output.jpg");
 
    return app.exec();
 }