C# Opencvsharp中使用TransposeND函数(二)

接上篇,上篇写了一个利用Opencv TransposeND方法导出DLL给Opencvsharp调用的方法,下面给出在C#中自己实现多维数据轴转置的方法,经测试,数据量小的情况下,二者效率差不多,数据量大的情况下,前者还是优于后者很多

代码如下:

public static class MatExtensions
{
    /// <summary>
    /// 多维数据轴转置(类似NumPy的transpose)
    /// </summary>
    /// <param name="src">输入矩阵(数据必须是连续的)</param>
    /// <param name="axes">目标轴顺序(例如[2,0,1])</param>
    /// <param name="shape">原始多维形状(例如[2,3,4])</param>
    /// <param name="newShape">输出转置后的新形状</param>
    /// <returns>转置后的矩阵(二维展平,需结合newShape解析)</returns>
    public static Mat TransposeND(this Mat src, int[] axes, int[] shape, out int[] newShape)
    {
        // 参数验证
        if (src == null)
            throw new ArgumentNullException(nameof(src));
        if (!src.IsContinuous())
            throw new ArgumentException("Input Mat must be continuous.");
        if (axes.Length != shape.Length)
            throw new ArgumentException("Axes length must match shape length.");
        foreach (int axis in axes)
        {
            if (axis < 0 || axis >= shape.Length)
                throw new ArgumentException($"Invalid axis {axis} in axes.");
        }

        // 计算新形状
        newShape = new int[axes.Length];
        for (int i = 0; i < axes.Length; i++)
        {
            newShape[i] = shape[axes[i]];
        }

        // 验证总元素数是否匹配
        int totalElements = 1;
        foreach (int dim in shape) totalElements *= dim;
        if (totalElements != src.Total())
            throw new ArgumentException("Shape does not match Mat's total elements.");

        // 获取原始数据
        byte[] srcData = new byte[src.Total() * src.ElemSize()];
        Marshal.Copy(src.Data, srcData, 0, srcData.Length);

        // 准备目标数据数组
        byte[] dstData = new byte[srcData.Length];
        int elemSize = src.ElemSize(); // 每个元素的字节数

        // 索引转换核心逻辑
        for (int linearIdx = 0; linearIdx < totalElements; linearIdx++)
        {
            // 计算原始多维索引
            int[] originalIndices = LinearToMultiIndex(linearIdx, shape);

            // 根据轴顺序调整索引
            int[] transposedIndices = new int[axes.Length];
            for (int i = 0; i < axes.Length; i++)
                transposedIndices[i] = originalIndices[axes[i]];

            // 计算目标线性索引
            int dstLinearIdx = MultiToLinearIndex(transposedIndices, newShape);

            // 复制数据
            Buffer.BlockCopy(
                src: srcData,
                srcOffset: linearIdx * elemSize,
                dst: dstData,
                dstOffset: dstLinearIdx * elemSize,
                count: elemSize
            );
        }

        // 展平为二维Mat
        int rows = CalculateFlattenedRows(newShape);
        int cols = newShape.Length > 0 ? newShape[newShape.Length-1] : 1;

        // 创建目标Mat
        return new Mat(rows, cols, src.Type(), dstData);
    }

    // 辅助方法:线性索引转多维索引
    private static int[] LinearToMultiIndex(int linearIndex, int[] shape)
    {
        int[] indices = new int[shape.Length];
        int temp = linearIndex;
        for (int i = shape.Length - 1; i >= 0; i--)
        {
            indices[i] = temp % shape[i];
            temp /= shape[i];
        }
        return indices;
    }

    // 辅助方法:多维索引转线性索引
    private static int MultiToLinearIndex(int[] indices, int[] shape)
    {
        int linearIndex = 0;
        int stride = 1;
        for (int i = shape.Length - 1; i >= 0; i--)
        {
            linearIndex += indices[i] * stride;
            stride *= shape[i];
        }
        return linearIndex;
    }

    // 计算二维展平后的行数
    private static int CalculateFlattenedRows(int[] shape)
    {
        if (shape.Length == 0) return 0;
        int rows = 1;
        for (int i = 0; i < shape.Length - 1; i++)
            rows *= shape[i];
        return rows;
    }
}

使用方法示例:

int[] sizes = { 22, 22, 85 };
Mat mat3D=new Mat(sizes,MatType.CV_32FC1,new Scalar(100));
Mat transposedMat = MatExtensions.TransposeND(mat3D, new int[] { 2, 1, 0 }, sizes,out var ns);

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值