OpenCVの矩形描画は静的リンクと動的リンクでどれくらい速度が違うのか？

## 自作の物体検出アプリをもうすこし速くできないか？
Windows用の物体検出アプリをリリースしてしばらく経ちました。
モデルも当初の正方形から入力画像形状に近い長方形へ変更し、処理速度を大きく改善することができました。
また学習データも少しずつ増やして、検出精度自体も当初よりも向上していると考えています。

今後更に処理速度をあげるにはどのようなアプローチができるだろう？
物体検出をした後、OpenCVで検出個所を矩形描画を行っています。
OpenCVは動的リンクとなるworld.dllを利用しています。
これを静的リンクにしたらどの程度改善するのだろう？

どこかで、静的リンクと動的リンクで大きな差はない、とちらっと見ましたが、もしかすると意外と効果があるかもしれない！

ということで、やってみました。 
~~静的libがなかったのでOpenCVのビルドから~~

## 環境
OpenCV 4.11
Windows 11 Pro 24H2
VisualStudio 2022 17.14.3

CPU intel 11400F

## OpenCVの静的ライブラリを使いた時のCMakeでのビルド
わたしが良く分かっていなかったのですが、静的ライブラリを利用したい場合には、CMake Guiでは`BUILD_SHARED_LIBS`のチェックを外してGenerateする必要があります。
~~これが分かっておらず数回くらいビルドしました~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/7791fa59-ec36-497e-99bf-67246cfe90f6.png)

## テストコード
Windows Copilotに作ってもらいました。
矩形描画を100000回繰り返してもらい、かかった時間を計測してもらいます。
```cpp:test.cpp
#include <opencv2/opencv.hpp>
#include <chrono>
#include <iostream>
#include <cstdlib>

int main(int argc, char** argv)
{
    // デフォルトは 100000 回、コマンドライン引数で回数を指定可能（例：100000）
    int iterCount = 100000;
    if (argc > 1)
    {
        iterCount = std::atoi(argv[1]);
    }
    std::cout << "Iteration count: " << iterCount << std::endl;

    // 640x480 の黒画像を作成
    cv::Mat image(480, 640, CV_8UC3, cv::Scalar(0, 0, 0));

    // テスト対象：矩形描画のパラメータ
    cv::Point pt1(50, 50);
    cv::Point pt2(300, 300);
    cv::Scalar color(0, 255, 0); // 緑色
    int thickness = 2;

    // ウォームアップ（初回呼び出しによるオーバーヘッドの影響を抑える）
    cv::rectangle(image, pt1, pt2, color, thickness);

    // 時間計測開始
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterCount; ++i)
    {
        // 同じ画像に矩形を描画（オーバーライドされるため最終結果は同じですが、処理は実施される）
        cv::rectangle(image, pt1, pt2, color, thickness);
    }
    auto end = std::chrono::high_resolution_clock::now();

    // 計測結果（ミリ秒単位）
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "Total time for " << iterCount << " iterations: " << duration.count() << " ms" << std::endl;

    // 最適化によってループが削除されないよう、簡単なチェックを実施
    cv::Scalar sumVal = cv::sum(image);
    std::cout << "Checksum: "
        << sumVal[0] << ", "
        << sumVal[1] << ", "
        << sumVal[2] << std::endl;

    return 0;
}

```

- 静的リンクの時、プロジェクトのプロパティ ＞ リンカー ＞ 追加の依存ファイルに、次を設定しました
ippiw.lib;zlib.lib;ippicvmt.lib;opencv_core4110.lib;opencv_imgproc4110.lib
- 動的リンクの時、プロジェクトのプロパティ ＞ リンカー ＞ 追加の依存ファイルに、次を設定しています
opencv_world4110.lib;

## 実行結果
- 静的リンク
![スクリーンショット 2025-05-30 214139.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/e4a7f7a3-41ae-4b78-a55f-fa240a50bbc8.png) 
```
Iteration count: 100000
Total time for 100000 iterations: 905 ms
Checksum: 0, 763980, 0
```
何回か実行してみましたが、900～950くらいの間という感じでした。

- 動的リンク
![スクリーンショット 2025-05-30 220518.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679605/a88eae88-aeb6-49b3-a4a0-b2de3d5b9751.png)
```
Iteration count: 100000
Total time for 100000 iterations: 1052 ms
Checksum: 0, 763980, 0
```
何回か実行してみましたが、1000～1050くらいの間という感じでした。

## 考察と感想
静的リンクは手間がかかる割には、そこまで変わらない、という印象。
DLLで全然問題ない感じがします。
今回は評価できていませんが、静的リンクはビルドに時間がかかるらしいので、やっぱりDLLの方が簡単でいいかなと感じました。

本件は以前からなんとなく気になっていたことで、結果的には見聞きしていた通りでしたが、実際に自分で試してみてやっぱり似たような結果を得られた、というのは良かったです。
実際に自分でやってみて納得でき、良かったです。