前两天掉进一个大坑,记录一下。写接口的时候要求输入为Image或Bitmap的数据类型,所以在用Halcon处理之前要先把它们编程Hobject的数据类型,在网上搜了一下相关的转换方法,大多是先利用了BitmapData中的LockBits和UnLockBits,取出位图的存储首地址,然后再利用Halcon里的GenImage系列函数根据位图地址创建一个新的HObject。根据这种方法我对图像类型进行了转换,然后就发现原来的算法不起作用了,将转换后的图像用Halcon打开,发现变成了如下这样。
整个图就已经扭曲了。。
产生这种结果的代码如下:
public void Bitmap2HObjectBpp8(Bitmap SrcImage, out HObject image)
{
try
{
Point po = new Point(0, 0);
Size so = new Size(SrcImage.Width, SrcImage.Height);//template.Width, template.Height
Rectangle ro = new Rectangle(po, so);
Bitmap DstImg = new Bitmap(SrcImage.Width, SrcImage.Height, PixelFormat.Format8bppIndexed);
DstImg = SrcImage.Clone(ro, PixelFormat.Format8bppIndexed);
Rectangle rect = new Rectangle(0, 0, DstImg.Width, DstImg.Height);
BitmapData srcBmpData = DstImg.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
HOperatorSet.GenImage1(out image, "byte", DstImg.Width, DstImg.Height, srcBmpData.Scan0);
DstImg.UnlockBits(srcBmpData);
}
catch (Exception ex)
{
image = null;
}
}
最开始以为是图像的位数导致的,然后就把所有的图像转为Format8bppIndexed型,还是存在这个问题,就又仔细去看了GenImage1的文档,里面有一句话是Care must be taken not to truncate 64-bit pointers on 64-bit architectures,以为是指针有问题,弄了半天也搞懂为什么。然后偶然间发现这个转换方法对某些图是起作用的,然后看了图的尺寸为4032*3024(在此感谢上苍感谢我的手机。。),发现4032是64的倍数,此刻便开始怀疑这种方法的遍历是不是并非想象中的那样。
于是把目标转移到Scan0上,确实它是位图的首地址,而GenImage1里提到The pixels in PixelPointer are stored line-sequentially,在我们的结果中可以看到一条明显的分割线。
因此猜测是扫描的问题,去数了一下图中黑线在每一行的像素。。发现是delta=64*n-width,其中delta的取值范围是0~3,在此基本就真相大白了,去查了一下BitmapData类,发现其中的有一个属性是Stride,也就是扫描一行的字节数,它是4的倍数,所以说如果按照这个顺序来赋值内存的话,对于宽度不是64倍数的图片来说,不仅会丢失信息,还会造成图片的扭曲,并产生一个黑线。所以在使用GenImage系列的函数时,我们不能直接用Bitmap里面的Scan0指针。
对相关代码进行修改:
public static HObject BitmapToHImage(Bitmap SrcImage)
{
HObject Hobj;
HOperatorSet.GenEmptyObj(out Hobj);
Point po = new Point(0, 0);
Size so = new Size(SrcImage.Width, SrcImage.Height);//template.Width, template.Height
Rectangle ro = new Rectangle(po, so);
Bitmap DstImage = new Bitmap(SrcImage.Width, SrcImage.Height, PixelFormat.Format8bppIndexed);
DstImage = SrcImage.Clone(ro, PixelFormat.Format8bppIndexed);
int width = DstImage.Width;
int height = DstImage.Height;
Rectangle rect = new Rectangle(0, 0, width, height);
System.Drawing.Imaging.BitmapData dstBmpData =
DstImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);//pImage.PixelFormat
int PixelSize = Bitmap.GetPixelFormatSize(dstBmpData.PixelFormat) / 8;
int stride = dstBmpData.Stride;
//重点在此
unsafe
{
int count = height * width;
byte[] data = new byte[count];
byte* bptr = (byte*)dstBmpData.Scan0;
fixed (byte* pData = data)
{
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++ )
{
data[i * width + j ] = bptr[i * stride + j];
}
HOperatorSet.GenImage1(out Hobj, "byte", width, height, new IntPtr(pData));
}
}
DstImage.UnlockBits(dstBmpData);
return Hobj;
}
修改之后,就可以发现转换的结果没有问题啦!同样的方法也可以用在转换24位彩色图像上,只要记得这个Stride就行了。。