3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お前の前にいるのは、1000年以上前から非同期で計算を続けてきたエルフだ(嘘) —— cv::AsyncArrayで実現する魔族も驚く並行処理

3
Last updated at Posted at 2025-12-21

■ ベタ書き実装:何だ…? この行列の計算が終わるまであと5秒はかかるはずだ。なぜお前はもう動ける……?

サブスレッド:お前の前にいるのは、重たい計算をバックグラウンドで実行するようにメインスレッドから指示され、平行で非同期実行してきたサブスレッドだ。

「メインスレッド、cv::AsyncArray.get()しろ」

■はじめに:平行処理使ってますか?

画像処理、それも複雑な機械学習を使っていると、どうしても処理が非常に重くなっちゃうってことありますよね?
確かにOpenCVはマルチコア対応がかなり進んではいます。

だけど、重たい処理は分けたいなあ・・・ プラットフォームへの依存性は最小限にしつつ、それでも平行処理は夢みたいなあ、って気持ちありませんかね?

どーんと、お任せください。OpenCVには、こうした画像処理の並行処理も書きやすくなっています。

■cv::AyncPromiseから始める非同期処理入門

ではまず、サンプルコードから行きましょうかね?

// g++ main.cpp -o a.out -I/usr/local/include/opencv4 -lopencv_core -lopencv_imgcodecs -lpthread
#include <iostream>
#include <thread>
#include <chrono>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp> // 結果出力用
#include <opencv2/core/detail/async_promise.hpp> // 環境により必要

void heavyComputation(cv::AsyncPromise& promise) {
    const std::string tag = "[Worker] ";
    std::cout << tag << "計算開始(5秒かかります)..." << std::endl;

    std::this_thread::sleep_for(std::chrono::seconds(5));

    cv::Mat mat(1080, 1920, CV_8UC3, cv::Scalar(0, 0, 0));
    for(int y = 0; y < mat.rows; y++) {
        mat.row(y).setTo(cv::Scalar(y % 255, 128, 255 - (y % 255)));
    }

    promise.setValue(mat);
    std::cout << tag << "値をセットしました。" << std::endl;
}

int main() {
    const std::string tag = "[Main] ";
    cv::AsyncPromise promise;
    cv::AsyncArray future_mat = promise.getArrayResult();

    // promiseの参照を渡す!
    std::thread t(heavyComputation, std::ref(promise));

    std::cout << tag << "別の作業をしています..." << std::endl;
    for (int i = 0; i < 3; ++i) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout << tag << "経過... " << i + 1 << "s" << std::endl;
    }

    cv::Mat result;
    std::cout << tag << "結果を待機(get)します..." << std::endl;
    future_mat.get(result);

    if (!result.empty()) {
        cv::imwrite("output.png", result);
        std::cout << tag << "完了! output.png を確認してください。" << std::endl;
    }

    t.join(); // tのクロージング
    return 0;
}

▢ main側解説

    cv::AsyncPromise promise;
    cv::AsyncArray future_mat = promise.getArrayResult();

cv::AyncPromise のインスタンス promiseから、getArrayResult()を介してcv::AyncArrayのインスタンスを参照します。なお、このgetArrayResult()は一度しか読んじゃダメらしいので要注意!

    // promiseの参照を渡す!
    std::thread t(heavyComputation, std::ref(promise));

C++のstd::threadに、後述するheavyComputation関数とその引数としてstd::ref(promise)を渡し、スレッドtを生成します。なお、ここでstd::ref()で参照として渡す必要があります。

    cv::Mat result;
    std::cout << tag << "結果を待機(get)します..." << std::endl;
    future_mat.get(result);

future_mat.get()が呼ばれると、先ほどのheavyComputation関数側で、setValue()するまで待機します。完了すると、resultにデータが格納されている、はずです。

    t.join(); // tのクロージング

お作法として、クロージングしておきましょう。

▢ heavyComputeation()

void heavyComputation(cv::AsyncPromise& promise) {

promiseは参照型であることがポイントです!

    promise.setValue(mat);

計算結果のcv::Matを、promise.setValue()にセットしてあげましょう。これによってmain thread側からこの値を参照できるようになります!

ということで、OpenCVを使うのであれば、並列処理も簡単に実装できますね!

■まとめ

  • OpenCVには、平行処理のためのクラスもあります!
  • cv::AcyncPromiseの受け渡し方法は要注意。何なら参照使えばよいと覚えてもOK
  • メインスレッドAyncArray.get()すると、サブスレッドでsetValueするまで待つ!やったね!

以上です、ありがとうございました!

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?