【数据转换】- Halcon<->Mat
背景介绍
最近在写C#联合Haclon调用C++的.dll文件进行联合编程。大致需求就是C#设计界面,然后调用Haclon的图像处理库,C++把目标检测的模型进行TensorRT部署生成动态链接库,之后界面操作加载模型、对图像进行检测等功能。
设计界面如下,画面简陋,勿喷!!!
其中有步操作,我需要将Halcon的HImage格式转换成OpenCV的Mat 。很耗时!
测试一下
将一张大小为2448*2048的图像转成Mat,我最开始的想法是逐像素遍历并手动调整通道顺序(RGB → BGR) ,非常耗时,大概24524ms. 具体实现代码如下:
// 获取Halcon图像的指针和数据
HTuple pointerRed, pointerGreen, pointerBlue;
HTuple type, width, height;
// 获取图像大小
HOperatorSet.GetImageSize(halconImage, out width, out height);
// 对于彩色图像,分别获取每个通道的数据
HOperatorSet.GetImagePointer3(halconImage, out pointerRed, out pointerGreen,
out pointerBlue, out type, out width, out height);
// 创建OpenCV的Mat对象 (3通道BGR格式)
Mat cvImage = new Mat(height, width, MatType.CV_8UC3);
// 将Halcon图像数据(RGB顺序)复制到OpenCV Mat中(BGR顺序)
unsafe
{
byte* cvData = (byte*)cvImage.Data;
byte* halconRed = (byte*)pointerRed.L;
byte* halconGreen = (byte*)pointerGreen.L;
byte* halconBlue = (byte*)pointerBlue.L;
创建计时器
//Stopwatch stopwatch = new Stopwatch();
开始计时
//stopwatch.Start();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int index = y * width * 3 + x * 3;
// OpenCV使用BGR顺序,Halcon是RGB
cvData[index + 0] = halconBlue[y * width + x]; // B
cvData[index + 1] = halconGreen[y * width + x]; // G
cvData[index + 2] = halconRed[y * width + x]; // R
}
}
耗时原因分析:
- 逐像素访问(最耗时)使用了 双重循环 for (y…) for (x…) 逐个像素处理,这种方式导致
缓存不友好:内存访问不是连续的,导致 CPU 缓存命中率低。
分支预测开销:循环内的条件判断(如 index 计算)会增加 CPU 流水线停顿。
测试数据:对于 1920x1080 的图像,循环次数 = 2,073,600 次,每次循环还要计算 index。 - 指针计算复杂
- 通道顺序调整的冗余操作
开始优化
跟部门主管说了这个疑问,主管建议我使用MemoryCopy.
于是乎,升级之后版本,时间 30ms
try
{
// 检查输入图像是否有效
if (himg == null || !himg.IsInitialized())
{
throw new Exception("Halcon 图像未初始化或为空");
}
// 获取通道数
HTuple htChannels = new HTuple();
HOperatorSet.CountChannels(himg, out htChannels);
if (htChannels.Length == 0)
{
return null;
}
int channels = htChannels[0].I;
HTuple width, height;
HOperatorSet.GetImageSize(himg, out width, out height);
// 检查图像尺寸是否有效
if (width <= 0 || height <= 0)
{
throw new Exception($"无效的图像尺寸: {width}x{height}");
}
Mat pImage;
if (channels == 1) // 单通道(灰度)
{
HTuple ptr, cType;
HOperatorSet.GetImagePointer1(himg, out ptr, out cType, out width, out height);
if (ptr.IP == IntPtr.Zero)
{
throw new Exception("Halcon 图像指针无效");
}
pImage = new Mat(height, width, MatType.CV_8UC1);
unsafe
{
Buffer.MemoryCopy((void*)ptr.IP, (void*)pImage.Data, width * height, width * height);
}
}
else if (channels == 3) // 三通道(RGB)
{
HTuple ptrRed, ptrGreen, ptrBlue, cType;
HOperatorSet.GetImagePointer3(himg, out ptrRed, out ptrGreen, out ptrBlue, out cType, out width, out height);
if (ptrRed.IP == IntPtr.Zero || ptrGreen.IP == IntPtr.Zero || ptrBlue.IP == IntPtr.Zero)
{
throw new Exception("Halcon RGB 通道指针无效");
}
// 创建单通道 Mat 存储 R/G/B
using (Mat pImageRed = new Mat(height, width, MatType.CV_8UC1))
using (Mat pImageGreen = new Mat(height, width, MatType.CV_8UC1))
using (Mat pImageBlue = new Mat(height, width, MatType.CV_8UC1))
{
unsafe
{
Buffer.MemoryCopy((void*)ptrRed.IP, (void*)pImageRed.Data, width * height, width * height);
Buffer.MemoryCopy((void*)ptrGreen.IP, (void*)pImageGreen.Data, width * height, width * height);
Buffer.MemoryCopy((void*)ptrBlue.IP, (void*)pImageBlue.Data, width * height, width * height);
}
// 合并为 3 通道 RGB
pImage = new Mat();
Cv2.Merge(new[] { pImageBlue, pImageGreen, pImageRed }, pImage); // Halcon 默认顺序是 RGB,OpenCV 是 BGR
}
}
else
{
throw new Exception($"不支持的通道数: {channels}(仅支持 1 或 3 通道)");
}
return pImage;
}
catch (Exception ex)
{
MessageBox.Show("转换失败", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}