0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AMD GPU OpenCV処理時間計測

0
Last updated at Posted at 2026-03-27

AMD GPU OpenCV処理時間計測

AMD Ryzen 7 255のGPU「Radeon 780M Graphics」でOpenCV処理がどの程度高速化出来るものなのかを計測しました。#自分の忘備録8

OpenCVはOpenCL(Open Computing Language)をサポートしていて、AMD GPUではこれを利用した処理が可能です。最近のOpenCVは自分でソースから構築しなくてもいろいろな機能がすぐに使えるので大変素晴らしいです。cv::Matをcv::UMat(Unified Matrix)に置き換えることで、OpenCVがOpenCLを呼び出してAMD GPUを使用してくれます。

2026/04/06(※1)追記
何となく振るわない処理時間の数値が何でだかわかりました。Windows版のcmakeのデフォルトはDebugビルドなのですね。cmakeをあまり使っていないのがバレバレですが:joy: WSL(Ubuntu)の同様の計測値がCPUなのに、あれ全然違う?ということで気が付きました。exeのディレクトリもDebugだったし計測数値もms単位ということで気が付ければ良かったですが、ロートルさで勘が鈍くなったというのが否めません:cold_sweat:
それで改めてDebugビルドとReleaseビルドで再計測(Debugビルドも再計測しましたが傾向は以前と同じ)。

OpenCV処理

以下のOpenCV処理の時間計測を行います。

  • 画像リサイズ(image_resize)
  • スケーリング/シフト(scaleabsolute)
  • ガンマ補正(gamma_correct)
  • ヒストグラム均一化(histgram_equalize)
  • 色空間変換(color_space)
  • ガウシアンぼかし(gaussian_blur)
  • シャープニング(filter_sharping)

OpenCV処理時間計測結果

OpenCV処理の時間計測にはsteady_clock(QueryPerformanceCounterをラップしている)を使用します。なので時間はかなり正確に計測出来ます。
プログラムはキャッシュやコンテキストスイッチの影響で1回目と連続した2回目以降の実行時間は結構異なるはずです。それを分けて計測します。
計測に使用した拙いC++ソースは終わりの test-opencv2.cpp です。USBカメラを接続して画像を取り込んで処理します(何となくそれらしい感じにしています)。
プログラムでは1回の処理毎の処理時間を計測して、コンテキストスイッチ後の1回目や連続した2回目以降の傾向を確かめながらExcelなどで平均化します。

下表の内容説明

  • funcはOpenCV処理(具体的なOpenCV関数はプログラム内の同じ名前の関数を参照)
  • 処理画像の大きさはUSBカメラの画素数(640×480)
  • 数値は1回の処理時間で、単位は全て[us]
  • cv::Matを使用したときはcpu、cv::UMatを使用したときはgpuと表現
  • (1)は1回目、ave(2~10)は2~10回目の平均(2回目以降もバラツキがあるものもありましたが、単純に平均しました)

Debugビルドの計測結果(※1)

func cpu(1) cpu ave(2~10) gpu(1) gpu ave(2~10)
image_resize 2794 1985 245 18
scaleabsolute 7147 6076 263 24
gamma_correct 862 428 717 391
histgram_equalize 913 796 652 205
color_space 2102 1510 283 17
gaussian_blur 5509 5251 1043 931
filter_sharping 12639 11692 408 101

Releaseビルドの計測結果(※1)

func cpu(1) cpu ave(2~10) gpu(1) gpu ave(2~10)
image_resize 358 74 310 5
scaleabsolute 242 110 301 6
gamma_correct 584 208 735 316
histgram_equalize 472 187 598 37
color_space 568 148 349 5
gaussian_blur 497 130 949 891
filter_sharping 1061 360 352 22
  • 処理にもよるがGPUはCPUよりかなり高速化される
  • GPUは連続処理でキャッシュに入るとさらなる高速化が期待できる、本当の威力を発揮する感じでしょうか
  • ReleaseビルドのCPUは安定して速くなっています。GPUの数値が大きいのは不得意なのがハッキリしているのか~OpenCLでは難しいのか??(※1)

計測プログラム構築例

OpenCVリリースサイトからWindows版の最新版OpenCV - 4.12.0(opencv-4.12.0-windows.exe)をダウンロードして使用します。
opencv-4.12.0-windows.exeを実行すると解凍が始まるので、適当なディレクトリ(今回はC:\)に解凍します。そうするとディレクトリ構成は以下のような感じ(必要なところだけ)。

C:\opencv\build\include
C:\opencv\build\x64\vc16\bin
C:\opencv\build\x64\vc16\lib

コンパイラはVisual Studio 2026を使用します。
C++ソースから構築するためにCMakeLists.txtファイルを作成するのですが、どうもOpenCV - 4.12.0はVisual Studio 2119(vc16)で構築されているようで、cmakeでOpenCVを見つけられるようなOpenCV_DIRを設定します(cmakeの詳細はわからないので適当に最低限)。

(CMakeLists.txt例)
# SPECIFY THE MINIMUM VERSION OF CMAKE REQUIRED
cmake_minimum_required(VERSION 3.31)

# SPECIFY YOUR PROJECT NAME
project(test-opencv2)

# C++20を設定
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# MAKE SURE OPENCV IS INSTALLED CORRECTLY
set(OpenCV_DIR "C:/opencv/build/x64/vc16/lib")
find_package(OpenCV REQUIRED)

# COMPILE CPP FILES USING THIS LINE
add_executable(test-opencv2 test-opencv2.cpp)

# TARGET INCLUDE AND LIBRARY
target_link_libraries(test-opencv2 PRIVATE ${OpenCV_LIBS})
(構築例)
cd test-opencv2        ← test-opencv2.cppとCMakeLists.txtを置いたディレクトリ
cmake -S . -B build
cmake --build build    ← これはDebugビルドと同じだった
 
cmake --build build --config Debug      ← Debugビルド(※1)
cmake --build build --config Release    ← Releaseビルド(※1)

(実行例)
path C:\opencv\build\x64\vc16\bin;%path%    ← OpenCVライブラリのパス追加
set OPENCV_LOG_LEVEL=ERROR                  ← OpenCVがいろいろログを出力するので、ERRORレベル以上を出力

build\Debug\test-opencv2.exe 0 0 0          ← 0=CamNo、0=image_resize、0=cv::Matで実行  Debugビルド(※1)
build\Release\test-opencv2.exe 0 0 0        ← 0=CamNo、0=image_resize、0=cv::Matで実行  Releaseビルド(※1) 

参考にしたサイト

使用したプログラムソース test-opencv2.cpp

#include <opencv2/opencv.hpp>
#include <conio.h>

using namespace std::chrono;  //steady_clock

#define countof(a) (sizeof(a)/sizeof(a[0]))


//image resize
//fx: 0.5
//fy: 0.5
int image_resize(cv::InputArray frame, cv::OutputArray frame2, double fx = 0.5, double fy = 0.5)
{
    auto tm1 = steady_clock::now();

    cv::resize(frame, frame2, cv::Size(), fx, fy);

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//scale absolute
//alpha: 1.5;  //scale
//beta: 50;    //offset
int scale_absolute(cv::InputArray frame, cv::OutputArray frame2, double alpha = 1.5, double beta = 50)
{
    auto tm1 = steady_clock::now();

    cv::convertScaleAbs(frame, frame2, alpha, beta);

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//gamma correct
//gamma: 2.0
int gamma_correct(cv::InputArray frame, cv::OutputArray frame2, double gamma = 2.0)
{
    auto tm1 = steady_clock::now();

    cv::Mat lookup_table(1, 256, CV_8U);
    for (int i = 0; i < 256; ++i) {
        lookup_table.at<uchar>(i) = cv::saturate_cast<uchar>(pow(i / 255.0, gamma) * 255.0);
    }
    if (frame.kind() == cv::_InputArray::MAT) {
        cv::LUT(frame, lookup_table, frame2);
    } else {
        cv::UMat utable;
        lookup_table.copyTo(utable);
        cv::LUT(frame, utable, frame2);
    }

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//gray scale
int gray_scale(cv::InputArray frame, cv::OutputArray frame2)
{
    auto tm1 = steady_clock::now();

    cv::cvtColor(frame, frame2, cv::COLOR_RGB2GRAY);

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//histgram equalize
int histgram_equalize(cv::InputArray frame, cv::OutputArray frame2)
{
    auto tm1 = steady_clock::now();

    cv::equalizeHist(frame, frame2);

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//color space conversion
int color_space(cv::InputArray frame, cv::OutputArray frame2)
{
    auto tm1 = steady_clock::now();

    cv::cvtColor(frame, frame2, cv::COLOR_BGR2HSV);

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//gaussian blur
//ksize: 7
int gaussian_blur(cv::InputArray frame, cv::OutputArray frame2, int ksize = 7)
{
    auto tm1 = steady_clock::now();

    cv::GaussianBlur(frame, frame2, cv::Size(ksize, ksize), 0);

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//filter sharping
int filter_sharping(cv::InputArray frame, cv::OutputArray frame2)
{
    auto tm1 = steady_clock::now();

    cv::Mat filter = (cv::Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
    cv::filter2D(frame, frame2, frame.depth(), filter);

    auto tm2 = steady_clock::now();
    int dtm = (int)duration_cast<microseconds>(tm2 - tm1).count();
    return dtm;
}


//function name
//                        0               1                2                3                    4              5                6
const char *funcstr[] = {"image_resize", "scaleabsolute", "gamma_correct", "histgram_equalize", "color_space", "gaussian_blur", "filter_sharping"};


//main
int main(int argc, char **argv)
{
    int camno = 0, func = 0, umat = 0, count = 0;
    if (argc > 1) camno = atoi(argv[1]);  //0~:camera no,-1:rtsp camera
    if (argc > 2) func = atoi(argv[2]);   //0~6:function no
    if (argc > 3) umat = atoi(argv[3]);   //0:mat,1:umat

    if (func >= countof(funcstr)) return 1;
    fprintf(stderr, "[funcstr=%s]\n", funcstr[func]);

    cv::VideoCapture cap;
    if (camno >= 0) {
        cap.open(camno, cv::CAP_DSHOW);
    } else {
        const std::string rtsp_url = "rtsp://192.168.11.9/live";
        cap.open(rtsp_url.c_str());
    }
    if (!cap.isOpened()) {
        std::cerr << "Camera open error !!" << std::endl;
        return 1;
    }
    cap.set(cv::CAP_PROP_BUFFERSIZE, 1);

    int camw = cap.get(cv::CAP_PROP_FRAME_WIDTH);
    int camh = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
    printf("#camno=%d w=%d h=%d func=%d umat=%d (funcstr=%s)\n", camno, camw, camh, func, umat, funcstr[func]);
    printf("cur,num,ave,max,min\n");


    try {
        int count = 10, memnum = 20 * count;
        int *mem = new int[memnum], *ptr = mem;

        int num = 0, sum = 0, max = 0, min = INT_MAX;

        cv::Mat frame, frame1, frame2;
        cv::UMat uframe, uframe1, uframe2;

        auto tmcap1 = steady_clock::now();

        while (true) {
            if (!cap.read(frame)) throw std::exception("#Capture error ??");
            if (cv::waitKey(1) >= 0 || (_kbhit() && _getch())) break;
            frame.copyTo(uframe);  // copy UMat

            auto tmcap2 = steady_clock::now();

            if (duration_cast<microseconds>(tmcap2 - tmcap1).count() >= 200000) {  //200ms毎に実行(コンテキストスイッチが入る)
                tmcap1 = tmcap2;

                //resize
                //double size = 0.5;
                //cv::resize(frame, frame, cv::Size(), size, size);

                int dtm;

                if (!umat) {

                    for (int i = 0; i < count; i++) {  //Mat使用、count回連続実行
                        switch (func) {
                        case 0:  //image resize
                            dtm = image_resize(frame, frame2);
                            break;

                        case 1:  //scaleabsolute
                            dtm = scale_absolute(frame, frame2);
                            break;

                        case 2:  //gamma correct
                            dtm = gamma_correct(frame, frame2);
                            break;

                        case 3:  //histgram equalize
                            gray_scale(frame, frame1);
                            dtm = histgram_equalize(frame1, frame2);
                            break;

                        case 4:  //color space conversion
                            dtm = color_space(frame, frame2);
                            break;

                        case 5:  //gaussian blur
                            dtm = gaussian_blur(frame, frame2);
                            break;

                        case 6:  //filter sharping
                            dtm = filter_sharping(frame, frame2);
                            break;
                        }

                        sum += dtm;
                        if (dtm > max) max = dtm;
                        if (dtm < min) min = dtm;
                        if (num++ < memnum) *ptr++ = dtm;
                    }

                } else {

                    for (int i = 0; i < count; i++) {  //UMat使用、count回連続実行
                        switch (func) {
                        case 0:  //image resize
                            dtm = image_resize(uframe, uframe2);
                            break;

                        case 1:  //scaleabsolute
                            dtm = scale_absolute(uframe, uframe2);
                            break;

                        case 2:  //gamma correct
                            dtm = gamma_correct(uframe, uframe2);
                            break;

                        case 3:  //histgram equalize
                            gray_scale(uframe, uframe1);
                            dtm = histgram_equalize(uframe1, uframe2);
                            break;

                        case 4:  //color space conversion
                            dtm = color_space(uframe, uframe2);
                            break;

                        case 5:  //gaussian blur
                            dtm = gaussian_blur(uframe, uframe2);
                            break;

                        case 6:  //filter sharping
                            dtm = filter_sharping(uframe, uframe2);
                            break;
                        }

                        sum += dtm;
                        if (dtm > max) max = dtm;
                        if (dtm < min) min = dtm;
                        if (num++ < memnum) *ptr++ = dtm;
                    }

                }

                if (!umat) {
                    cv::imshow("Cam", frame);
                    cv::imshow("Cam(2)", frame2);
                } else {
                    cv::imshow("Cam", uframe);
                    cv::imshow("Cam(2)", uframe2);
                }

                printf("%d,%d,%d,%d,%d\n", dtm, num, sum / num, max, min);

                if (num >= memnum) break;
            }
        }

        printf("#%d,%d,%d,%s,%s", camno, camw, camh, funcstr[func], umat ? "umat" : "mat");
        for (int i = 0; i < (num < memnum ? num : memnum); i++) {
            printf(i ? "," : ", ");
            printf("%d", mem[i]);
        }
        printf("\n");

        delete mem;

    } catch(const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    cv::destroyAllWindows();
    cap.release();

    return 0;
}
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?