分别使用了HOperatorSet.GenImageInterleaved直接转换、C#获取图像各个通道内存首地址和HOperatorSet.GenImage3合成、OpenCV获取图像各通道内存首地址和HOperatorSet.GenImage3合成的三种方式。经测试发现还是使用HOperatorSet.GenImageInterleaved方法转换速度最快。
详细测试代码如下:
using HalconDotNet;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bitmaptohobject
{
class Program
{
static void Main(string[] args)
{
Bitmap bit = new Bitmap(@"D:/图片/ocr/MaxImage.bmp"); //100M的.bmp格式文件,
HObject ho;
HOperatorSet.GenEmptyObj(out ho);
Stopwatch st = new Stopwatch();
st.Start();
// Bitmap2HImageBpp24(bit,out ho);
Bitmap2CVHImageBpp24(bit, out ho);
//Bitmap2HObjectBpp24(bit, out ho);
st.Stop();
HOperatorSet.WriteImage(ho,"png",0,"1.png");
Console.WriteLine(st.ElapsedMilliseconds.ToString());
Console.ReadKey();
}
public static void Bitmap2HObjectBpp24(Bitmap bmp, out HObject image) //90ms
{
try
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData srcBmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
HOperatorSet.GenImageInterleaved(out image, srcBmpData.Scan0, "bgr", bmp.Width, bmp.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
bmp.UnlockBits(srcBmpData);
}
catch (Exception ex)
{
image = null;
}
}
public static void Bitmap2HImageBpp24(Bitmap bmp, out HObject image) //转换500ms
{
try
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmp_data = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte[] arrayR = new byte[bmp_data.Width * bmp_data.Height];//红色数组
byte[] arrayG = new byte[bmp_data.Width * bmp_data.Height];//绿色数组
byte[] arrayB = new byte[bmp_data.Width * bmp_data.Height];//蓝色数组
unsafe
{
byte* pBmp = (byte*)bmp_data.Scan0;//BitMap的头指针
//下面的循环分别提取出红绿蓝三色放入三个数组
for (int R = 0; R < bmp_data.Height; R++)
{
for (int C = 0; C < bmp_data.Width; C++)
{
//因为内存BitMap的储存方式,行宽用Stride算,C*3是因为这是三通道,另外BitMap是按BGR储存的
byte* pBase = pBmp + bmp_data.Stride * R + C * 3;
arrayR[R * bmp_data.Width + C] = *(pBase + 2);
arrayG[R * bmp_data.Width + C] = *(pBase + 1);
arrayB[R * bmp_data.Width + C] = *(pBase);
}
}
fixed (byte* pR = arrayR, pG = arrayG, pB = arrayB)
{
HOperatorSet.GenImage3(out image, "byte", bmp_data.Width, bmp_data.Height,
new IntPtr(pR), new IntPtr(pG), new IntPtr(pB));
//如果这里报错,仔细看看前面有没有写错
}
}
}
catch (Exception ex)
{
image = null;
}
}
public static void Bitmap2CVHImageBpp24(Bitmap bmp, out HObject image) //150ms
{
try
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmp_data = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
Mat M = new Mat(bmp.Height,bmp.Width,MatType.CV_8UC3,bmp_data.Scan0,0);
Mat[] MS = M.Split();
IntPtr red= MS[0].Ptr(); IntPtr g = MS[1].Ptr();
IntPtr b = MS[2].Ptr();
HOperatorSet.GenImage3(out image, "byte", bmp_data.Width, bmp_data.Height, red, g, b);
}
catch (Exception ex)
{
image = null;
}
}
public static void Bitmap2HObjectBpp8(Bitmap bmp, out HObject image)
{
try
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData srcBmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
HOperatorSet.GenImage1(out image, "byte", bmp.Width, bmp.Height, srcBmpData.Scan0);
bmp.UnlockBits(srcBmpData);
}
catch (Exception ex)
{
image = null;
}
}
}
}
附HObject转Bitmap代码:
//其中CopyMemory的API引用 需要引入命名空间 using System.Runtime.InteropServices; 和如下代码
[DllImport("kernel32.dll")]
public static extern void CopyMemory(int Destination, int add, int Length);
/// <summary>
/// HObject转8位Bitmap(单通道)
/// </summary>
/// <param name="image"></param>
/// <param name="res"></param>
private static void HObject2Bpp8(HObject image, out Bitmap res)
{
try
{
HTuple hpoint, type, width, height;
const int Alpha = 255;
int[] ptr = new int[2];
HOperatorSet.GetImagePointer1(image, out hpoint, out type, out width, out height);
res = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
ColorPalette pal = res.Palette;
for (int i = 0; i <= 255; i++)
{
pal.Entries[i] = Color.FromArgb(Alpha, i, i, i);
}
res.Palette = pal;
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bitmapData = res.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
int PixelSize = Bitmap.GetPixelFormatSize(bitmapData.PixelFormat) / 8;
ptr[0] = bitmapData.Scan0.ToInt32();
ptr[1] = hpoint.I;
if (width % 4 == 0)
CopyMemory(ptr[0], ptr[1], width * height * PixelSize);
else
{
for (int i = 0; i < height - 1; i++)
{
ptr[1] += width;
CopyMemory(ptr[0], ptr[1], width * PixelSize);
ptr[0] += bitmapData.Stride;
}
}
res.UnlockBits(bitmapData);
}
catch(Exception ex)
{
res = null;
throw ex;
}
}
/// <summary>
/// HObject转24位Bitmap
/// </summary>
/// <param name="image"></param>
/// <param name="res"></param>
private static void HObject2Bpp24(HObject image, out Bitmap res)
{
try
{
HTuple hred, hgreen, hblue, type, width, height;
HOperatorSet.GetImagePointer3(image, out hred, out hgreen, out hblue, out type, out width, out height);
res = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bitmapData = res.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
int imglength = width * height;
unsafe
{
byte* bptr = (byte*)bitmapData.Scan0;
byte* r = ((byte*)hred.I);
byte* g = ((byte*)hgreen.I);
byte* b = ((byte*)hblue.I);
for (int i = 0; i < imglength; i++)
{
bptr[i * 4] = (b)[i];
bptr[i * 4 + 1] = (g)[i];
bptr[i * 4 + 2] = (r)[i];
bptr[i * 4 + 3] = 255;
}
}
res.UnlockBits(bitmapData);
}
catch(Exception ex)
{
res = null;
throw ex;
}
}
20190902更新:
如评论一楼朋友weixin_43930542提出的使用 HObject转Bitmap代码 时遇到的问题:
我在使用Halcon转位图的时候在bptr[i * 4] = (b)[i]; bptr[i * 4 + 1] = (g)[i]; bptr[i * 4 + 2] = (r)[i]; bptr[i * 4 + 3] = 255; 的时候总是抛异常“尝试读取或写入受保护的内存。这通常指示其他内存已损坏” 请问博主有遇到过吗?怎么解决这个问题?
我同事使用的时候也出现类似的问题,我再次测试该段代码时也出现了类似问题(在公司电脑),然后在家里测试这段代码正常,也没找到具体原因,猜测可能是halcon环境问题。下面将我的测试demo连接发出来,大家可以测试一下。
注意:使用时,需要将Demo中的halcon引用删除,重新添加引用,halcon控件删除,重新添加halcon控件,然后再编译后调试。
20191015更新Demo:
Demo中halcon版本为Halcon18-Steady版本。
Demo下载连接:
链接:https://pan.baidu.com/s/1OgNo--w7K5QbxwRxtoajLg
提取码:rk7r
2019-12-23更新:
如本文评论中朋友weixin_42269849提出的在.net4.5及以上版本中会报错,并给出了解决方法,特此添加进文章,供大家学习。
//net4.5及以上环境的hobject转bitmap24方法
/// <summary>
/// HObject转24位Bitmap,net4.5及以上版本
/// </summary>
/// <param name="image"></param>
/// <param name="res"></param>
public static void HObject2Bpp24Net45(HObject image, out Bitmap res)
{
try
{
HTuple hred, hgreen, hblue, type, width, height;
HOperatorSet.GetImagePointer3(image, out hred, out hgreen, out hblue, out type, out width, out height);
res = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bitmapData = res.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
int imglength = width * height;
unsafe
{
byte* bptr = (byte*)bitmapData.Scan0;
byte* r = ((byte*)hred.L);
byte* g = ((byte*)hgreen.L);
byte* b = ((byte*)hblue.L);
for (int i = 0; i < imglength; i++)
{
bptr[i * 4] = (b)[i];
bptr[i * 4 + 1] = (g)[i];
bptr[i * 4 + 2] = (r)[i];
bptr[i * 4 + 3] = 255;
}
}
res.UnlockBits(bitmapData);
}
catch (Exception ex)
{
res = null;
throw ex;
}
}