【问题标题】:How to get RGB value of a pixel in WinRT如何在 WinRT 中获取像素的 RGB 值
【发布时间】:2012-12-19 05:13:00
【问题描述】:

我正在尝试获取 WinRT 应用程序中每个像素的 RGB 值。我可以访问包含PixelData 的字节数组,但我不知道如何使用它,那么如何从字节数组中提取 RGB 信息?

var bd = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
var pd = await bd.GetPixelDataAsync();
var tempBuffer = pd.DetachPixelData();
var PixelMatrix = new byte[bd.PixelWidth, bd.PixelHeight];
// how do I get the RGB value for PixelMatrix[0,0]?

【问题讨论】:

  • 您需要在PixelMatrix[0, 0] 存储三个值,因为您的图像具有三个波段:R、G 和 B。因此需要以不同的方式声明 PixelMatrix。或者您可以将三个字节打包成一个更大的单元,但不确定是否要处理这个复杂的问题。

标签: c# image image-processing windows-runtime


【解决方案1】:

由于您有 RGB 图像,tempBuffer[k + 0] 是红色通道,tempBuffer[k + 1] 是绿色通道,tempBuffer[k + 2] 是蓝色通道,即tempBuffer 是一维数组。如果您循环遍历所有像素,则伪代码将是:

for i = 0 to height - 1
    for j = 0 to width - 1
        k = (i * width + j) * 3
        r, g, b = tempBuffer[k + 0], tempBuffer[k + 1], tempBuffer[k + 2]

【讨论】:

  • 谢谢 这同样适用于其他编码方法吗?例如在 bgra8 上,第一个字节是否包含蓝色第二个绿色...
  • 在这种情况下,您需要乘以 4,因为您现在有 4 个通道。最好获得通道数,以便上述代码可以用于任意数量的通道。现在,这种情况下的第二个问题是 BGRA 与 RGBA 的排序明显不同,因此您对获得的通道的观察是正确的。如果我是你,我会做一个从 BGRA 到 RGBA 的映射,这样你的数组中的最终顺序总是相同的。
  • 必须为不同的像素格式更改代码。下面的方法性能更高,因为它在内部使用 WinRT 方法进行块复制。此外 - 如果像素由多字节组件组成,例如浮点数,则此方法将需要使用某种 BinaryReader。
  • @ananthonline:当通道顺序与 RGB 不同时,您的代码毫无意义。
  • 如果您没有注意到 - 我声明了两种像素格式。这个想法是声明为多种像素格式布局的结构并相应地使用它们。
【解决方案2】:

由于 Marshal 类在 WinRT 上不可用 - 最高效的方法是使用 SafeMemoryMappedViewHandle (SafeBuffer)。

此方法还可以处理具有多字节组件的像素格式,而无需使用 BinaryReader 并逐个组件读取它(RGBA16,每个组件 16 位)。使用解码器的BitmapPixelFormat 属性找出像素格式,并使用适当声明的结构。

    // declare more of these appropriately laid
    // out structures for different pixel formats
    struct RGBA16
    {
        public uint R;
        public uint G;
        public uint B;
        public uint A;
    }

    struct RGBA8
    {
        public byte R;
        public byte G;
        public byte B;
        public byte A;
    }

    struct BRGA8
    {
        public byte B;
        public byte G;
        public byte R;
        public byte A;
    }
    ...

    var handle = GCHandle.Alloc(tempBuffer /* the raw byte[] */, GCHandleType.Pinned);
    try
    {
        var ptr = handle.AddrOfPinnedObject();
        var safeBuffer = new SafeMemoryMappedViewHandle(true /* I believe DetachPixelData returns a copy? false otherwise  */)
        safeBuffer.SetHandle(ptr);

        #if STREAM_PROCESSING
            // pixel by pixel
            int offset = 0;
            for (int i = 0; i < width * height; i++)
            {
                var pixel = safeBuffer.Read<RGBA16>(offset);
                offset += RGB24bpp.Size;
            }
        #else
            // Read it all in at once - this makes a copy
            var pixels = new RGBA16[width * height];
            safeBuffer.ReadArray<RGBA16>(0, pixels, 0, width * height);
        #endif
    }
    finally
    {
        safeBuffer.Dispose();
        handle.Free;
    }

注意:此方法也可以替代任何需要 Marshal.PtrToStructure 或 WinRT 上类似等价物的任何操作。

【讨论】:

  • 这里留给那些可能需要在没有 Marshal 类的情况下使用 WinRT 有效读取基于多字节组件的像素格式的人。
猜你喜欢
  • 1970-01-01
  • 2021-08-10
  • 2010-09-13
  • 2010-11-08
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多