Halcon对象HImage三通道图像转换为C#中的Bitmap的终极解决方案

时隔一年多,终于把这个问题解决掉了。现在回头看,这个问题其实并不复杂,当初处理不了,完全是因为缺乏经验,多看多写,可能就会在处理其他问题时看到解决困扰很久的问题的解决方法。
原问题参阅:C# Halcon联合编程问题(二)
原方案参考Halcon对象Hobject转换为.net对象Bitmap

于一天后(3.20)更新:
写完这篇第二天,突然想明白了之前没有搞明白的问题。
原来的方案有问题的地方终于知道在哪里了。
经测试发现,GetImagePointer3如果参数传递的是HTuple类型,可以看到返回的HTuple的type是Long,这种情况下,如果想要取HTuple指向的指针地址,显然是不应该用.I来取的,而应该用.L,这样一来,问题就解决了。包括如果用IntPtr,如果能接受unsafe的话,其实也不用把指向的地址中的值全都拷贝出来,直接用(byte*)IntPtr就是个地址,是和Scan0那个地方是一样的。当初别人用.I可能和平台有关系,别人可能是32位的,而我的是64位的。

本文提供两种类似的解决方案,思路其实和原方案都是一致的,两种方案的区别在于前者经测试同样的图片,耗时约是后者的20倍-30倍,后者要用到unsafe,我是不太喜欢在C#中用unsafe,这个单词给人的感觉就不太安全。
本文提供的解决方案与原来找到的解决方案最主要的不同,在于拿到图像灰度值的指针之后的操作,原方法转换为了byte*,而我的方法将指针所指向的数值复制到了byte数组中。
上代码:

// 读取图像
HImage image = new HImage(@"0.png");
// 获取存放r,g,b值的指针
image.GetImagePointer3(out IntPtr r, out IntPtr g, out IntPtr b, out string type, out int w, out int h);
byte[] red = new byte[w * h];
byte[] green = new byte[w * h];
byte[] blue = new byte[w * h];
// 将指针指向地址的值取出来放到byte数组中
Marshal.Copy(r, red, 0, w * h);
Marshal.Copy(g, green, 0, w * h);
Marshal.Copy(b, blue, 0, w * h);

以下是两种方案的区别
方案1:

Bitmap bitmap = new Bitmap(w, h, PixelFormat.Format32bppRgb);
Rectangle rect = new Rectangle(0, 0, w, h);
BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
IntPtr bptr = bitmapData.Scan0;
for (int i = 0; i < red.Length; i++)
{
    Marshal.Copy(blue, i, bptr + i * 4, 1);
    Marshal.Copy(green, i, bptr + i * 4 + 1, 1);
    Marshal.Copy(red, i, bptr + i * 4 + 2, 1);
    Marshal.Copy(new byte[] { 255 }, 0, bptr + i * 4 + 3, 1);
}
bitmap.UnlockBits(bitmapData);

方案2:

Bitmap bitmap2 = new Bitmap(w, h, PixelFormat.Format32bppRgb);
Rectangle rect2 = new Rectangle(0, 0, w, h);
BitmapData bitmapData2 = bitmap2.LockBits(rect2, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
unsafe
{
    byte* bptr2 = (byte*)bitmapData2.Scan0;
    for(int i = 0; i < w * h;i ++)
    {
        bptr2[i * 4] = blue[i];
        bptr2[i * 4 + 1] = green[i];
        bptr2[i * 4 + 2] = red[i];
        bptr2[i * 4 + 3] = 255;
    }
}
bitmap2.UnlockBits(bitmapData2);

两种方案都经过测试,可以获取到准确的图像数据。
测试使用的图像大小为3072*2048,使用方案1耗时约250ms,使用方案2耗时约10ms。
其实如果对图像格式没有特别的要求,Bitmap在new的时候完全可以使用Format24bppRgb而不是Format32bppRgb,这样遍历过程的耗时也会缩短很多,实测方案1可以缩短到不到200ms,方案2在10ms以内。
果然还是直接指针操作要快很多啊,但是如果可以的话,我是真不想用unsafe。
推测原来的方案是直接将HTuple.I转换为指针的时候出了毛病,但是因为对C/C++不太熟悉,暂时找不到不出错的写法。

原文地址:https://www.cnblogs.com/yutou2016/p/14558148.html