接上篇,上篇写了一个利用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);

被折叠的 条评论
为什么被折叠?



