[.NET5] ピクセルデータを描画する処理時間の比較

公開:2020/11/28
更新:2020/11/28
3 min読了の目安(約3000字IDEAアイデア記事

0. やりたいこと

  1. ピクセルデータは外部から転送されてくることを想定(カメラ等)
  2. ワークメモリはアプリ起動中は解放されない

上記環境を想定して、フレームイベント毎にピクセルデータを画面に描画したい。

1. テストコード

#pragma once

#ifdef _DEBUG
#pragma comment(lib, "opencv_world450d.lib")
#else
#pragma comment(lib, "opencv_world450.lib")
#endif

#pragma comment(lib, "Gdi32.lib")

#include <windows.h>
#include <opencv2\opencv.hpp>

#using <System.dll>
#using <System.Drawing.dll>
//#using <System.Drawing.Common.dll>

using namespace System;
using namespace System::Drawing;
using namespace System::Drawing::Imaging;

namespace Library
{
  public ref class Class
  {
  private:
    static byte* _data;
    static cv::Mat* _mat;

  public:
    static void TestInit()
    {
      _data = new byte[640 * 480 * 3](); // zero clear
      _mat = new cv::Mat(480, 640, CV_8UC3, _data);

      //- 実環境では、カメラ等から取得したピクセルデータを想定
      auto temp = cv::imread("./image.bmp", cv::IMREAD_COLOR);
      memcpy(_mat->data, temp.data, 640 * 480 * 3);
      //------------
    }

    static void DrawWithBitmap(Bitmap^% bitmap)
    {
      bitmap = gcnew Bitmap(_mat->cols, _mat->rows, _mat->step, PixelFormat::Format24bppRgb, IntPtr(_mat->data));      
    }

    static void DrawWithMemcpy(Bitmap^% bitmap)
    {
      auto bmpData = bitmap->LockBits(System::Drawing::Rectangle(0, 0, _mat->cols, _mat->rows), ImageLockMode::ReadWrite, PixelFormat::Format24bppRgb);

      memcpy(bmpData->Scan0.ToPointer(), _mat->data, _mat->cols * _mat->rows * 3);

      bitmap->UnlockBits(bmpData);
    }

    static void DrawWithGraphics(Bitmap^% bitmap)
    {
      auto temp = gcnew Bitmap(_mat->cols, _mat->rows, _mat->step, PixelFormat::Format24bppRgb, IntPtr(_mat->data));

      auto g = Graphics::FromImage(bitmap);
      g->DrawImage(temp, 0, 0, _mat->cols, _mat->rows);
      
      delete g;
      delete temp;
    }

    static void DrawWithDC(Bitmap^% bitmap)
    {
      auto temp = gcnew Bitmap(_mat->cols, _mat->rows, _mat->step, PixelFormat::Format24bppRgb, IntPtr(_mat->data));

      auto g = Graphics::FromImage(bitmap);
      auto pDC = g->GetHdc();
      HDC hDC = static_cast<HDC>(pDC.ToPointer());

      HDC hMemDC = CreateCompatibleDC(hDC);
      HGDIOBJ hBitmap = static_cast<HGDIOBJ>(temp->GetHbitmap().ToPointer());
      HGDIOBJ oldObj = SelectObject(hMemDC, hBitmap);

      BitBlt(hDC, 0, 0, _mat->cols, _mat->rows, hMemDC, 0, 0, SRCCOPY);

      SelectObject(hMemDC, oldObj);
      DeleteObject(hBitmap);
      DeleteDC(hMemDC);
      g->ReleaseHdc(pDC);
      
      delete g;
      delete temp;
    }
  };
}

2. 結果

・環境

  1. .NET5
  2. C#/WinForms
  3. C++/CLI
  4. OpenCV4.5

・条件1
VGA: 640×480
試行回数: 10,000

Function Time [ms]
DrawWithBitmap 0.0098
DrawWithMemcpy 0.0261
DrawWithGraphics 0.7575
DrawWithDC 2.2837

・条件2
8K: 7,680×4,320
試行回数: 1,000

Function Time [ms]
DrawWithBitmap 0.0091
DrawWithMemcpy 7.0200
DrawWithGraphics 80.4080
DrawWithDC 27.0260

3. おわり

洗練されたコードではありませんが、明確な結果が出たと思います。