OpenCVSharpのMatオブジェクトとbyte配列で相互にピクセルデータをコピー

コンピュータ

Marshal.Copy()を使いMatオブジェクトとC#のbyte配列間でピクセルデータのコピーが出来るようなので試してみました。

Matオブジェクトからbyte配列へコピー

        // Matオブジェクト生成
        Mat mat = new Mat(200, 300, MatType.CV_8UC1, Scalar.Gray);
        // byte[]生成
        byte[] pixels = new byte[mat.Total() * mat.Channels()];
        // Matオブジェクトをbyte[]へコピー
        Marshal.Copy(mat.Data, pixels, 0, pixels.Length);

byte配列からへMatオブジェクトへコピー

        // byte[]をMatオブジェクトへコピー
        Marshal.Copy(pixels, 0, mat.Data, pixels.Length);

byte配列の書き換えサンプル(Winforms)

灰色の画像のMatオブジェクトからbyte配列を生成。
byte配列を黒から白へグラデーションになるように書換。
Winformsで表示します。

using System.Runtime.InteropServices;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace GrayMo01;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        // Matオブジェクト生成
        Mat mat = new Mat(200, 300, MatType.CV_8UC1, Scalar.Gray);
        // byte[]生成
        byte[] pixels = new byte[mat.Total() * mat.Channels()];
        // Matオブジェクトをbyte[]へコピー
        Marshal.Copy(mat.Data, pixels, 0, pixels.Length);

        // byte[]を書き換え
        for(int i = 0; i < pixels.Length; i++)
        {
            pixels[i] = (byte)(i / mat.Width);
        }
        // byte[]をMatオブジェクトへコピー
        Marshal.Copy(pixels, 0, mat.Data, pixels.Length);
        

        this.BackgroundImageLayout = ImageLayout.Center;
        this.BackgroundImage = mat.ToBitmap();
    }
}

(グラデーションの計算が雑。画像の高さが256以上だと破綻。)

カラーだと色々面倒そうなので8ビットのグレスケールでのサンプルになります。

System.Drawing.Bitmapとbyte配列をコピーする方法と似ていますが、こちらはBitmapのロックなどの機構がなく(あるかもしれませんが知らない)strideの計算が無い分シンプルな構成だと思います。

サンプルプログラムを作るにあたり、元の画像を灰色で塗りつぶされた画像を用意しましたが、初期化時の引数がwidthとheightではなくrowとcolumonですので縦横が反対になります。(何故?)
あとMatオブジェクトをビットマップオブジェクトに変更しフォームのバックグラウンドイメージにセットしていますが、Matおbジェクトからビットマップオブジェクトの変換がmat.ToBitmap()で行っています。BitmapConverter.ToBitmap()と同じものでしょう。(多分)

カラーの場合どのようになるのかは、必要に迫られたら調べてみたいと思います。

コメント