相机取图后转成视觉库的图像
2023 年 8 月 2 日 · 2 分钟读完
用相机厂家自己的 SDK 取图时(而不是用视觉库自带的采集接口),回调里拿到的只是一块像素数据,Halcon、VisionPro 这些视觉库不能直接用,需要转换成它们的图像类型。
不管哪家相机,2D 图像的关键信息都是这几个:
- 像素数据:
byte[]或IntPtr; - 宽、高;
- 像素格式:Mono8(8 位灰度)、BGR8、BayerRG8 等;
- 行填充:每行末尾有没有补齐的字节(有的 SDK 叫 PaddingX)。
大华相机
大华 SDK 回调里的图像数据:
public interface IGrabbedRawData
{
byte[] Image { get; } // 像素数据
IntPtr Raw { get; }
int ImageSize { get; }
int Width { get; } // 宽
int Height { get; } // 高
long BlockID { get; }
long TimeStamp { get; }
uint ImagePaddingX { get; } // 行填充
uint ImagePaddingY { get; }
GvspPixelFormatType PixelFmt { get; } // 像素格式
IChunkData ChunkData { get; }
IGrabbedRawData Clone();
void Show(IntPtr pWnd);
Bitmap ToBitmap(bool color);
}
我把需要的信息放进自己的类里,和具体相机解耦,然后再转 HImage:
public enum PixelFormatType { Mono8, BGR8 }
public class ImageData
{
public int Width { get; set; }
public int Height { get; set; }
public byte[] Raw { get; set; }
public PixelFormatType PixelFmt { get; set; }
public long TimeStamp { get; set; }
public unsafe HImage ToHImage()
{
var image = new HImage();
fixed (byte* p = Raw)
{
switch (PixelFmt)
{
case PixelFormatType.Mono8:
image.GenImage1("byte", Width, Height, new IntPtr(p));
break;
case PixelFormatType.BGR8:
image.GenImageInterleaved(new IntPtr(p), "bgr", Width, Height, 0, "byte",
Width, Height, 0, 0, -1, 0);
break;
default:
throw new NotSupportedException($"未支持的像素格式:{PixelFmt}");
}
}
return image;
}
}
原代码有个隐患:原文在 fixed 块里取到指针,出了 fixed 块之后才调用 GenImage1。fixed 只在块内固定数组,出了块之后 GC 随时可能把数组挪走,指针就指向了错误的内存。平时很难复现,偶尔会出现花图甚至崩溃。用指针的代码必须放在 fixed 块里面,像上面这样。
GenImage1 和 GenImageInterleaved 会把数据复制一份,所以返回后 Raw 被回收也没关系。
原文只写了大华 Mono8,彩色分支是补充的;
GenImageInterleaved各参数的含义建议对照 Halcon 帮助里的gen_image_interleaved再确认一遍。
不用复制的情况
如果 SDK 直接给了 IntPtr(非托管内存),就不需要 fixed:
image.GenImage1("byte", width, height, frame.Raw);
但要注意 SDK 的缓冲区什么时候被回收:很多 SDK 的回调返回后,这块内存就会被下一帧覆盖,所以必须在回调里完成转换(GenImage1 是复制,没问题),而不能把指针存起来以后再用。
延伸
- 行填充:有的相机为了对齐,每行末尾会补几个字节,这时每行实际字节数 ≠ 宽度。直接
GenImage1图像会变斜。要么在相机参数里关掉填充,要么先逐行拷成紧凑的数组,做法和 Bitmap 的 Stride 一样,见 Halcon 在 C# 里的数据类型转换。 - Bayer 格式:彩色相机输出 BayerRG8 之类的原始格式时,每个像素只有一种颜色,要先用
GenImage1生成单通道图,再用 Halcon 的cfa_to_rgb转成彩色。也可以在相机参数里直接设成 BGR8 输出,让相机去插值,代价是数据量变成 3 倍。 - 转 VisionPro:灰度图用
CogImage8Grey,可以从Bitmap构造(new CogImage8Grey(bitmap)),也可以用CogImage8Root的Initialize(width, height, pixelMemory, stride, disposable)包装已有的内存,免去一次复制。 - 海康相机:MVS SDK 回调里给的是
IntPtr pData和MV_FRAME_OUT_INFO_EX(里面有nWidth、nHeight、enPixelType),转换方式和上面一样。 - 取图线程:SDK 的回调在相机的线程里执行,回调里只做转换和入队,处理放到别的线程,否则处理慢了会丢帧。显示时更新界面要
Invoke。
原文首发于 blog.csdn.net