【问题标题】:c++ Get raw pixel data from hbitmapc++ 从 hbitmap 获取原始像素数据
【发布时间】:2014-03-29 19:31:54
【问题描述】:

我对使用 p/invoke 调用相当陌生,我想知道是否有人可以指导我如何从 hbitmap 中检索原始像素数据(无符号字符 *)。

这是我的场景:

我在 C# 端加载一个 .NET Bitmap 对象,并将它的 IntPtr 发送到我的非托管 c++ 方法。在 C++ 端收到 hbitmap ptr 后,我想访问位图的像素数据。我已经创建了一个接受 unsigned char* 的方法,它表示来自 c# 的原始像素数据,但是我发现从 c# 中提取 byte[] 相当慢。这就是为什么我想发送 Bitmap ptr 而不是将 Bitmap 转换为 byte[] 并将其发送到我的 C++ 方法。

获取Bitmap IntPtr的C#代码

Bitmap srcBitmap = new Bitmap(m_testImage);
IntPtr hbitmap = srcBitmap.GetHbitmap();

用于导入c++方法的C#代码

[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int ResizeImage(IntPtr srcImg);

将接收 Hbitmap 处理程序的 C++ 方法

int Resize::ResizeImage(unsigned char* srcImg){
    //access srcImgs raw pixel data (preferably in unsigned char* format)
   //do work with that 
   return status;
}

问题:

1) 由于我发送的是 IntPrt,我的 C++ 方法参数可以是 unsigned char* 吗?

2) 如果没有,我如何从 c++ 访问位图的原始数据?

【问题讨论】:

    标签: c# c++ bitmap pinvoke


    【解决方案1】:

    GetHbitmap 方法不检索像素数据。它产生一个 GDI 位图句柄,类型为 HBITMAP。您的非托管代码会将其作为HBITMAP 类型的参数接收。您可以使用 GDI 调用从中获取像素数据。但它本身并不是原始像素。

    事实上,我很确定你以错误的方式解决了这个问题。您可能正朝着这个方向前进,因为GetPixelSetPixel 很慢。这倒是真的。事实上,它们的 GDI 等价物也是如此。您需要做的是使用LockBits。这将允许您以有效的方式在 C# 中对整个像素数据进行操作。可以在此处找到对该主题的良好描述:https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx。请注意,为了提高效率,这是一种 C# 代码,其中不安全的代码和指针通常是最好的解决方案。

    如果出于某种原因,您仍然希望使用 C++ 代码对像素数据进行操作,那么您仍然可以使用 LockBits 作为获取指向像素数据的指针的最简单方法。它肯定比非托管 GDI 等价物容易得多。

    【讨论】:

    • 谢谢大卫,这正是我发现的!我发布了我的代码作为答案,希望能帮助其他寻找相同解决方案的人。
    • 你当然可以这样做。但是也应该可以生成高效的托管代码。使用不安全和指针。
    【解决方案2】:

    显然从 Scan0 发送指针与我正在搜索的内容相同。我可以通过发送从 bitmapData.Scan0 方法检索到的 IntPtr 来按预期操作数据。

    Bitmap srcBitmap = new Bitmap(m_testImage);
    Rectangle rect = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
    BitmapData bmpData = srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, srcBitmap.PixelFormat);
    //Get ptr to pixel data of image
    IntPtr ptr = bmpData.Scan0;
    //Call c++ method
    int status = myDll.ResizeImage(ptr);
    srcBitmap.UnlockBits(bmpData);
    

    为了进一步帮助澄清,我从最初的帖子中更改的唯一代码是第一个代码块。其余的都保持不变。 (C++ 方法仍然接受 unsigned char * 作为参数)

    【讨论】:

      【解决方案3】:

      首先,HBITMAP 不应该是 unsigned char*。如果您将HBITMAP 传递给C++,那么参数应该是HBITMAP

      int Resize::ResizeImage(HBITMAP hBmp)

      接下来从HBITMAP 转换为像素:

      std::vector<unsigned char> ToPixels(HBITMAP BitmapHandle, int &width, int &height)
      {        
          BITMAP Bmp = {0};
          BITMAPINFO Info = {0};
          std::vector<unsigned char> Pixels = std::vector<unsigned char>();
      
          HDC DC = CreateCompatibleDC(NULL);
          std::memset(&Info, 0, sizeof(BITMAPINFO)); //not necessary really..
          HBITMAP OldBitmap = (HBITMAP)SelectObject(DC, BitmapHandle);
          GetObject(BitmapHandle, sizeof(Bmp), &Bmp);
      
          Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
          Info.bmiHeader.biWidth = width = Bmp.bmWidth;
          Info.bmiHeader.biHeight = height = Bmp.bmHeight;
          Info.bmiHeader.biPlanes = 1;
          Info.bmiHeader.biBitCount = Bmp.bmBitsPixel;
          Info.bmiHeader.biCompression = BI_RGB;
          Info.bmiHeader.biSizeImage = ((width * Bmp.bmBitsPixel + 31) / 32) * 4 * height;
      
          Pixels.resize(Info.bmiHeader.biSizeImage);
          GetDIBits(DC, BitmapHandle, 0, height, &Pixels[0], &Info, DIB_RGB_COLORS);
          SelectObject(DC, OldBitmap);
      
          height = std::abs(height);
          DeleteDC(DC);
          return Pixels;
      }
      

      【讨论】:

      • 感谢您的回复,我会尝试比较使用这种方法和我发布的解决方案的性能差异。
      猜你喜欢
      • 2012-08-21
      • 1970-01-01
      • 2012-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-24
      • 2012-10-06
      • 2021-09-06
      相关资源
      最近更新 更多