繁体   English   中英

从位图的字节数组中获取像素数据

[英]Get Pixel Data out of Byte Array of Bitmap

我对以下问题problem之以鼻:

我在这里找到了一个类,可以使用SharpDX捕获屏幕。 它触发一个事件,并给我一个位图的字节数组。 以下类提供数组:

try
{
    SharpDX.DXGI.Resource screenResource;
    OutputDuplicateFrameInformation duplicateFrameInformation;

    // Try to get duplicated frame within given time is ms
    duplicatedOutput.AcquireNextFrame(5, out duplicateFrameInformation, out screenResource);

    // copy resource into memory that can be accessed by the CPU
    using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
        device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);

    // Get the desktop capture texture
    var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);

    // Create Drawing.Bitmap
    using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
    {
        var boundsRect = new Rectangle(0, 0, width, height);

        // Copy pixels from screen capture Texture to GDI bitmap
        var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
        var sourcePtr = mapSource.DataPointer;
        var destPtr = mapDest.Scan0;
        for (int y = 0; y < height; y++)
        {
            // Copy a single line 
            Utilities.CopyMemory(destPtr, sourcePtr, width * 4);

            // Advance pointers
            sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
            destPtr = IntPtr.Add(destPtr, mapDest.Stride);
        }

        // Release source and dest locks
        bitmap.UnlockBits(mapDest);
        device.ImmediateContext.UnmapSubresource(screenTexture, 0);

        //bitmap.Save("sample.bmp"); // Das geht!

        using (var ms = new MemoryStream())
        {
            bitmap.Save(ms, ImageFormat.Bmp);
            ScreenRefreshed?.Invoke(this, ms.ToArray());
            _init = true;

        }
        //_init = true; // Zsmhang: bitmap.Save
    }
    screenResource.Dispose();
    duplicatedOutput.ReleaseFrame();
}
catch (SharpDXException e)
{
    if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
    {
        logger.Error(e.Message);
        logger.Error(e.StackTrace);
    }
}

所以我得到了一个MemoryStream字节数组,我完全不确定如何从中获取像素数据。 我发现了许多类似的帖子,但似乎没人能正确地从线性数组中获取数据。 我尝试了多种变体,例如:

private void newFrameProcessing(object sender, byte[] data)
{
    logger.Debug("Frame Tick! ");

    Color clr = Color.Empty;

    // Get color components count (BGRA)
    int cCount = 4;

    int y = 0;

    // Get start index of the specified pixel
    for (int x = 0; x <= 2; x++)
    {
        int f = ((y * 1280) + x) * cCount;

        if (f > data.Length - cCount)
            throw new IndexOutOfRangeException();

        byte b = data[f];
        byte g = data[f + 1];
        byte r = data[f + 2];
        byte a = data[f + 3]; // a
        clr = Color.FromArgb(a, r, g, b);

        logger.Debug("GetPixel("+x+","+y+") : index-f = " + f + " RGB: " + clr.R + " " + clr.G + " " + clr.B);
    }
}

但这似乎是假设第一个像素在数组的开头。 我试图打印数组以查看其中的内容,而且数组中的第一个字节似乎是某种随机的东西(也许是位图标头之类的东西?),而不是正确的像素。 如果我将位图保存到.bmp文件中(事件触发前请参见注释行),我可以看到正确的屏幕截图,但是缺少文件夹名称之类的东西,这正常吗?

所以我的问题是:

  1. 我如何从线性阵列中获取正确的像素数据? 也许我需要大步走吗? 但是我不知道这在哪里有帮助
  2. 我发现的代码不是使用Marshal.Copy的,这似乎是获得像素数据的广泛接受的方法。 会更好吗?

提前致谢!

你可以做这样的事情

using (var bmp = new Bitmap(fromPath))
{   
  // lock the array for direct access int 32Bpp
  var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);

  // store the max length so we don't have to recalculate it
  var length = (int*)data.Scan0 + bmp.Height * bmp.Width;

  for (var p = (int*)data.Scan0; p < length; p++)           
  {          
     //*p is your pixel as an int;

     // change the color like this
     *p = Color.Red.ToArgb();
     Color = Color.FromArgb(*p);
  }

  // or if you want x/y
  var p = (int*)data.Scan0;

  for (int x = 0; x < bmp.Width; x++)
     for (int y = 0; y < bmp.Height; y++)
        *(p + x + y * bmp.Width) = Color.Red.ToArgb();

  // if you want to copy the whole array 
  int[] managedArray = new int[bmp.Width * bmp.Height];
  Marshal.Copy((int*)data.Scan0, managedArray, 0, bmp.Width * bmp.Height); 

  // unlock the bitmap
  bmp.UnlockBits(data);
}

注意 :完全未经测试

最后,当将位图存储在1d数组中时,将存储第一行像素,然后紧接着是下一行像素。

因此,如果您想知道单个索引中任何时间的位置,则只需2个for循环

for (int x = 0; x < bmp.Width; x++)
   for (int y = 0; y < bmp.Height; y++)
      // your pixel is at array[x + y * width] 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM