web-dev-qa-db-ja.com

EmguCVマット画像のピクセル値を取得および設定するにはどうすればよいですか?

OpenCV 3.0ライブラリのEmguCV 3.0.0ラッパーを使用しています。いくつかの場所でMatクラスを使用しています。以下は、doubleの値で構成された単一チャネルの8x8画像の例です。

Mat image = new Mat(8, 8, DepthType.Cv64F, 1);

Image<> クラスは ピクセル値を取得および設定するための合理的な手段 を提供し、メソッドは Matrix<> と同じですクラス、しかしそれは Mat クラスではそれほど明白ではないようです。個々のピクセルを設定する方法を理解した唯一の方法は、マスクを使用することです。

// set two pixel values, (0,0) to 9.0, (2, 3) to 42.0

Matrix<byte> mask = new Matrix<byte>(8,8);
mask.Data[0, 0] = 1;
image.SetTo(new MCvScalar(9.0), mask);

mask = new Matrix<byte>(8,8);
mask.Data[2, 3] = 1;
image.SetTo(new MCvScalar(42.0), mask);

これはfeelsで、6行ではなく2行になるはずなので、何かが足りないように感じます。 Matが複数のチャネルである場合、Matrix<>は2Dのみであるため、各チャネルのピクセルを設定するにはマスクを使用する必要があるため、状況はさらに複雑になります。

このようにピクセルを設定するための時間やメモリがありません。 1回のメソッド呼び出しでピクセルを設定するにはどうすればよいですか?

10
kdbanman

DataPointerを使用してアンマネージメモリブロックをコピーし、マネージ型をアンマネージ型に変換することで、Matから要素を取得できます。値を設定すると、反対方向にマーシャリングされます。

たとえば、このような拡張クラスを使用できます

public static class MatExtension
{
    public static dynamic GetValue(this Mat mat, int row, int col)
    {
        var value = CreateElement(mat.Depth);
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
        return value[0];
    }

    public static void SetValue(this Mat mat, int row, int col, dynamic value)
    {
        var target = CreateElement(mat.Depth, value);
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
    }
    private static dynamic CreateElement(DepthType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }

    private static dynamic CreateElement(DepthType depthType)
    {
        if (depthType == DepthType.Cv8S)
        {
            return new sbyte[1];
        }
        if (depthType == DepthType.Cv8U)
        {
            return new byte[1];
        }
        if (depthType == DepthType.Cv16S)
        {
            return new short[1];
        }
        if (depthType == DepthType.Cv16U)
        {
            return new ushort[1];
        }
        if (depthType == DepthType.Cv32S)
        {
            return new int[1];
        }
        if (depthType == DepthType.Cv32F)
        {
            return new float[1];
        }
        if (depthType == DepthType.Cv64F)
        {
            return new double[1];
        }
        return new float[1];
    }
}

その後、単一のメソッド呼び出しで値の取得と設定が可能です

var row = 2;
var col = 1;
var mat = new Mat(3, 3, DepthType.Cv64F, 3);
mat.SetValue(row, col, 3.14);
var value = mat.GetValue(row, col);

200000000オペレーションのテストでは、動的な型のバージョンは静的なものよりも最大2.5倍遅くなる可能性があることが示されています。

    public static double GetDoubleValue(this Mat mat, int row, int col)
    {
        var value = new double[1];
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
        return value[0];
    }

    public static void SetDoubleValue(this Mat mat, int row, int col, double value)
    {
        var target = new[] { value };
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
    }
14
Bartosz Rachwal

Bartosz Rachwalの素晴らしい答えに基づいて、OpenCvSharp用に記述しようとしました。

    public static dynamic GetValue(this Mat mat, int row, int col)
    {
        var value = CreateElement(mat.Type());
        Marshal.Copy(mat.Data + (row * mat.Cols + col) * mat.ElemSize(), value, 0, 1);
        return value[0];
    }
    public static void SetValue(this Mat mat, int row, int col, dynamic value)
    {
        var target = CreateElement(mat.Type(), value);
        Marshal.Copy(target, 0, mat.Data + (row * mat.Cols + col) * mat.ElemSize(), 1);
    }
    private static dynamic CreateElement(MatType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }
    private static dynamic CreateElement(MatType depthType)
    {
        switch (depthType)
        {
            case MatType.CV_8S:
                return new sbyte[1];
            case MatType.CV_8U:
                return new byte[1];
            case MatType.CV_16S:
                return new short[1];
            case MatType.CV_16U:
                return new ushort[1];
            case MatType.CV_32S:
                return new int[1];
            case MatType.CV_32F:
                return new float[1];
            case MatType.CV_64F:
                return new double[1];
            default:
                throw new NotImplementedException();
        }
    }
2
Koray