【问题标题】:Convert byte[,] to byte[]将 byte[,] 转换为 byte[]
【发布时间】:2011-06-08 03:30:17
【问题描述】:

有谁知道将 C# 中的 2d 数组(非锯齿状)展平为 1d 并再次返回的有效方法。我知道在后端 C# 必须将其作为一维数组保留,如果可能的话,我只想获得后端一维数组的句柄。

我想这样做的原因是因为我希望能够在托管代码中将其作为 2d,有时我想将其作为 1d 交给非托管 dll 导入代码(优化程序集在数字图像处理中就是一个很好的例子)。

【问题讨论】:

  • "我知道在后端 C# 必须将其作为一维数组保留" 是什么让你这么认为?根本不必如此。
  • @Jason:实际上,规范中的以下行似乎表明您可以假设底层表示是一个线性数组。从 §18.6 开始:“考虑到数组的存储方式,我们可以将任意维度的数组视为线性数组。”但是,请注意,这并不一定是这种情况,但它是真的很好。
  • @Jason:哈哈,你自己纠正了吗?
  • @Dan Tao:我们把它当作一个阐述。 :-)
  • @Jason:当然……或者是突然出现的分离性身份障碍;)

标签: .net image-processing


【解决方案1】:

从 2D 索引到 1D 索引:

int i = y * width + x;

然后返回:

int x = i % width;
int y = i / width;

那么最简单的就是遍历你的数组,一个一个地复制值。

【讨论】:

  • 这远不是最高效的数据复制方式。
  • @Jon Skeet:其实我还没有看到 Buffer.BlockCopy,所以我没有测量它的性能。我会相信你的话,它会更快。 :) 看看一些性能数据会很有趣,看看有什么区别。我会记住一个,因为这个问题经常出现(我很确定这个问题实际上是重复的,只是现在找不到另一个很好的匹配)。
  • 是的,这远非我所说的有效方式。
【解决方案2】:

好吧,对象本身并不是byte[],尽管它本身将数据存储在一个连续的内存块中。不要忘记数组也知道它的等级和长度等。

但是,您可以使用Buffer.BlockCopy 快速复制数组。例如:

byte[,] source = new byte[5,5];
byte[] dest = new byte[source.Length];
Buffer.BlockCopy(source, 0, dest, 0, source.Length);

请注意,最后一个参数是字节数,而不是数组元素的数量。

【讨论】:

  • 第 18.6 节规范中的相关行:“鉴于数组的存储方式,我们可以将任意维度的数组视为线性数组。”特别是,可以说byte[] destination = new byte[source.Length]; unsafe { fixed (byte *p = source) { for (int i = 0; i < source.Length; i++) { destination[i] = p[i]; } } },代码将按预期工作。
  • 另外,byte[] dest = new byte[source.Length];Buffer.BlockCopy(source, 0, dest, 0, source.Length * sizeof(byte)); 显然更好
  • @Jason: 是的,这是真的——虽然我不会打扰sizeof(byte)... 不过可能是为了不那么琐碎的事情:)
  • 我这样做只是为了提醒大家注意Buffer.BlockCopy的最后一个参数是要复制的字节的数量,而不是元素的数量.
  • @Jason:是的,这是有道理的。我将进行编辑以引起注意,但不在代码中。
【解决方案3】:

如果不将其复制出来,您将无法从 byte[,] 获取托管的 byte[] 数组(反正我不知道)。

如果您对unsafe 代码感到满意,您可以在阵列上修复byte*,我相信这应该可以:

fixed (byte* ptr = array)
{
    for (int i = 0; i < N; ++i)
    {
        byte b = ptr[i];
        // Do whatever you need.
    }
}

【讨论】:

  • @dko:确实有效。请参阅我对 Jon Skeet 回答的评论:stackoverflow.com/questions/4674394/convert-byte-to-byte/…
  • @Jason 你知道它是按行主要还是列主要顺序存储的? AKA byte[5,5] temp,索引 temp[0,3] 是第 3 个字节还是第 15 个字节?
  • @dko:这是行主要顺序。来自规范:“对于多维数组,数组元素的存储方式是最右边的维度的索引首先增加,然后是下一个左边的维度,依此类推到左边。”所以,temp[0, 3] 是第四个字节(你在询问它是否是第三个字节时差了一个)。
  • @dko:谷歌“ECMA 334”或查看c:\Program Files\Microsoft Visual Studio 10.0\VC#\Specifications\1033\CSharp Language Specification.doc 或类似目录,具体取决于您安装 Visual Studio 的位置和您拥有的版本。
  • @Jon:我认为byte[] OP 最初的意思是“线性排列的字节序列”,而不是“CLR byte[] 数组”;在我看来,他实际上并不需要复制字节。当然,我不知道他是如何将这个byte* 转到非托管 DLL 的。听起来他有他的方法。 (@dko:如果你是个女人,我很抱歉,我只是叫你“他”五次。)
【解决方案4】:
var myFlattenedByteArray = my2DByteArray.SelectMany(b=>b).ToArray();

这将以子数组元素的顺序返回二维字节数组的每个子数组的每个元素:myFlattenedByteArray[1] == my2DByteArray[0][1]。可能有更高效或更高效的解决方案(尤其是将 SelectMany 生成的 Enumerable 放入自己的数组中),但它是单行的。

【讨论】:

    猜你喜欢
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 2020-02-03
    • 2011-10-15
    • 2018-09-05
    • 1970-01-01
    • 2023-01-21
    • 2023-03-10
    相关资源
    最近更新 更多