前言
VisionMaster(后简称VM)作为一款功能强大的工业图形图像处理软件,可对工业中遇到的各种图像进行处理,同时还有功能强大的算子以及很全面的例程,作为图像处理的主程序是非常不错的。当前VM提供VM算法平台、VM SDK开发、算子SDK开发和定制化算法模块开发四种开发模式,覆盖各类开发族群。如何实现图像的输入输出,是开发者在调用视觉算法库时遇到的首要问题。当前行业主流的图像格式包括相机图像数据流、Bitmap、Mat和Halcon中的图像类型等,而VM不同开发模式的图像类型也有所不同,本文将介绍如何实现这些图像类型之间的互转。以下除定制化算法模块是C++语言,其它都是C#语言的示例代码。
注:本文章仅限VM4.2版本。
VM中对应的图像类型如下:
-
相机:图像数据流(此处图像数据流类型是MyCamera.MV_FRAME_OUT,是来自MvCameraControl.Net.dll,即海康机器人工业相机SDK,在MVS SDK和算子SDK中都有这个dll;算子SDK的MVDCamera.Net.dll也可以进行相机取流,它是对MvCameraControl.Net.dll的二次封装,用MVDCamera.Net.dll时,图像数据流类型是CMvdImage);
-
VM:脚本输入图像(图像类型是ImageData,C++不支持);
-
VM SDK:流程输入图像(ImageBaseData_V2,VM4.2 SDK新增)、Group输入图像(ImageBaseData_V2,VM4.2 SDK新增)、图像源SDK输入图像(ImageBaseData)、模块输入图像(InputImageData)、流程输出图像(区分VM4.0 SDK和VM4.2 SDK的获取输出图像的方式,VM4.2获取流程图像的类型是ImageBaseData_V2) ;
-
算子SDK:输入图像(CMvdImage);
-
算法模块:输入图像(HKA_IMAGE,C#不支持)。
需要注意的是, 三通道图像类型转换时,Bitmap、Mat为BGR,VM和二次开发为RGB。
下面针对常用图像转换场景,提供对应的图像转换示例供大家参考(每种图像转换用函数表达,函数输入某种图像类型,返回转换后的某种图像类型)。
图像转换
1.相机取流转VM对应类型图像格式
相机截取帧格式为MyCamera.MV_FRAME_OUT,以下为分别转换为流程输入图像、Group输入图像、图像源SDK输入图像、模块输入图像、算子输入图像的示例代码。
1.1 相机采图转流程输入和Group输入(ImageBaseData_V2)
//图像转换函数
public ImageBaseData_V2 CCDToImageBaseDataV2(MyCamera.MV_FRAME_OUT frameOut)
{
ImageBaseData_V2 imageBaseDataV2 = new ImageBaseData_V2();
if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
{
imageBaseDataV2 = new ImageBaseData_V2(frameOut.pBufAddr, frameOut.stFrameInfo.nFrameLen, frameOut.stFrameInfo.nWidth, frameOut.stFrameInfo.nHeight, VMPixelFormat.VM_PIXEL_MONO_08);
}
else if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
imageBaseDataV2 = new ImageBaseData_V2(frameOut.pBufAddr, frameOut.stFrameInfo.nFrameLen, frameOut.stFrameInfo.nWidth, frameOut.stFrameInfo.nHeight, VMPixelFormat.VM_PIXEL_RGB24_C3);
}
return imageBaseDataV2;
}
//设置示例代码
var image = CCDToImageBaseDataV2(stFrameOut);
//设置到流程
var procedure = VmSolution.Instance["流程1"] as VmProcedure;
procedure.ModuParams.SetInputImage_V2("ImageData", image);
//设置到Group
var group = VmSolution.Instance["流程1.组合模块1"] as IMVSGroupTool;
group.ModuParams.SetInputImage_V2("ImageData", image);
1.2 相机采图转图像源SDK输入(ImageBaseData)
//图像转换函数
public ImageBaseData CCDToImageBaseData(MyCamera.MV_FRAME_OUT frameOut)
{
ImageBaseData imageBaseData = new ImageBaseData();
imageBaseData.Width = frameOut.stFrameInfo.nWidth;
imageBaseData.Height = frameOut.stFrameInfo.nHeight;
imageBaseData.DataLen = frameOut.stFrameInfo.nFrameLen;
if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
{
imageBaseData.Pixelformat = (int)VMPixelFormat.VM_PIXEL_MONO_08;
}
else if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
imageBaseData.Pixelformat = (int)VMPixelFormat.VM_PIXEL_RGB24_C3;
}
imageBaseData.ImageData = new byte[frameOut.stFrameInfo.nFrameLen];
Marshal.Copy(frameOut.pBufAddr, imageBaseData.ImageData, 0, (int)frameOut.stFrameInfo.nFrameLen);
return imageBaseData;
}
//设置图像示例代码
var image = CCDToImageBaseData(stFrameOut);
//设置到图像源
ImageSourceModuleTool imageSourceModuleTool = (ImageSourceModuleTool)VmSolution.Instance["流程1.图像源1"];
imageSourceModuleTool.SetImageData(image);
1.3 相机采图转模块输入(InputImageData)
//图像转换函数
public InputImageData CCDToInputImageData(MyCamera.MV_FRAME_OUT frameOut)
{
InputImageData inputImageData = new InputImageData();
inputImageData.Names.DataName = "InImage";//只能使用默认名称InImage
inputImageData.Names.HeightName = "InImageHeight";//默认InImageHeight
inputImageData.Names.WidthName = "InImageWidth";//默认InImageWidth
inputImageData.Names.PixelFormatName = "InImagePixelFormat";//默认InImagePixelFormat
inputImageData.Width = frameOut.stFrameInfo.nWidth;
inputImageData.Height = frameOut.stFrameInfo.nHeight;
inputImageData.DataLen = frameOut.stFrameInfo.nFrameLen;
if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
{
inputImageData.Pixelformat = ImagePixelFormat.IMAGE_PIXEL_FORMAT_MONO8;
}
else if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
inputImageData.Pixelformat = ImagePixelFormat.IMAGE_PIXEL_FORMAT_RGB24;
}
//申请内存
inputImageData.Data = Marshal.AllocHGlobal((int)frameOut.stFrameInfo.nFrameLen);
byte[] imagedataBuffer = new byte[(int)frameOut.stFrameInfo.nFrameLen];
Marshal.Copy(frameOut.pBufAddr, imagedataBuffer, 0, (int)frameOut.stFrameInfo.nFrameLen);
Marshal.Copy(imagedataBuffer, 0, inputImageData.Data, (int)frameOut.stFrameInfo.nFrameLen);
return inputImageData;
}
//设置图像代码示例
var image= CCDToInputImageData(stFrameOut);
//设置到模块
var circlefindTool = VmSolution.Instance["流程1.圆查找1"] as IMVSImageEnhanceModuCs.IMVSImageEnhanceModuTool;
circlefindTool.ModuParams.SetInputImage(image);
1.4 相机采图转算子输入(CmvdImage)
//图像转换函数
public CMvdImage CCDToCMvdImage(MyCamera.MV_FRAME_OUT frameOut)
{
VisionDesigner.CMvdImage cMvdImage = new VisionDesigner.CMvdImage();
VisionDesigner.MVD_IMAGE_DATA_INFO stImageData = new VisionDesigner.MVD_IMAGE_DATA_INFO();
stImageData.stDataChannel[0].nLen = (uint)(frameOut.stFrameInfo.nFrameLen);
stImageData.stDataChannel[0].nSize = (uint)(frameOut.stFrameInfo.nFrameLen);
byte[] m_BufForDriver1 = new byte[frameOut.stFrameInfo.nFrameLen];
//数据Copy
Marshal.Copy(frameOut.pBufAddr, m_BufForDriver1, 0, (int)frameOut.stFrameInfo.nFrameLen);
stImageData.stDataChannel[0].arrDataBytes = m_BufForDriver1;
if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
{
stImageData.stDataChannel[0].nRowStep = (uint)frameOut.stFrameInfo.nWidth;
//初始化CMvdImage
cMvdImage.InitImage((uint)frameOut.stFrameInfo.nWidth, (uint)frameOut.stFrameInfo.nHeight, MVD_PIXEL_FORMAT.MVD_PIXEL_MONO_08, stImageData);
}
else if (frameOut.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
stImageData.stDataChannel[0].nRowStep = (uint)frameOut.stFrameInfo.nWidth * 3;
//初始化CMvdImage
cMvdImage.InitImage((uint)frameOut.stFrameInfo.nWidth, (uint)frameOut.stFrameInfo.nHeight, MVD_PIXEL_FORMAT.MVD_PIXEL_RGB_RGB24_C3, stImageData);
}
return cMvdImage;
}
2.Bitmap取图与VM对应图像格式互转
Bitmap与 流程输入图像、Group输入图像、图像源SDK输入图像、模块输入图像、算子输入图像,流程输出图像互相转换的示例代码如下所示。
2.1 Bitmap转流程输入和Group输入(ImageBaseData_V2)
public ImageBaseData_V2 BitmapToImageBaseData_V2(Bitmap bmpInputImg)
{
ImageBaseData_V2 imageBaseData_V2;
System.Drawing.Imaging.PixelFormat bitPixelFormat = bmpInputImg.PixelFormat;
BitmapData bmData = bmpInputImg.LockBits(new Rectangle(0, 0, bmpInputImg.Width, bmpInputImg.Height), ImageLockMode.ReadOnly, bitPixelFormat);//锁定
if (bitPixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
Int32 bitmapDataSize = bmData.Stride * bmData.Height;//bitmap图像缓存长度
int offset = bmData.Stride - bmData.Width;
Int32 ImageBaseDataSize = bmData.Width * bmData.Height;//imageBaseData_V2图像真正的缓存长度
byte[] _BitImageBufferBytes = new byte[bitmapDataSize];
byte[] _ImageBaseDataBufferBytes = new byte[ImageBaseDataSize];
Marshal.Copy(bmData.Scan0, _BitImageBufferBytes, 0, bitmapDataSize);
int bitmapIndex = 0;
int ImageBaseDataIndex = 0;
for (int i = 0; i < bmData.Height; i++)
{
for (int j = 0; j < bmData.Width; j++)
{
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex++];
}
bitmapIndex += offset;//删除冗余数据
}
IntPtr _ImageBaseDataIntptr = Marshal.AllocHGlobal(ImageBaseDataSize);
Marshal.Copy(_ImageBaseDataBufferBytes, 0, _ImageBaseDataIntptr, ImageBaseDataSize);
imageBaseData_V2 = new ImageBaseData_V2(_ImageBaseDataIntptr, (uint)ImageBaseDataSize, bmData.Width, bmData.Height, VMPixelFormat.VM_PIXEL_MONO_08);
Marshal.FreeHGlobal(_ImageBaseDataIntptr);
}
else if (bitPixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
Int32 bitmapDataSize = bmData.Stride * bmData.Height;//bitmap图像缓存长度
int offset = bmData.Stride - bmData.Width * 3;
Int32 ImageBaseDataSize = bmData.Width * bmData.Height * 3;//imageBaseData_V2图像真正的缓存长度
byte[] _BitImageBufferBytes = new byte[bitmapDataSize];
byte[] _ImageBaseDataBufferBytes = new byte[ImageBaseDataSize];
Marshal.Copy(bmData.Scan0, _BitImageBufferBytes, 0, bitmapDataSize);
int bitmapIndex = 0;
int ImageBaseDataIndex = 0;
for (int i = 0; i < bmData.Height; i++)
{
for (int j = 0; j < bmData.Width; j++)
{
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex + 2];//bitmap为BGR,imageBaseData_V2为RGB
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex + 1];
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex];
bitmapIndex += 3;
}
bitmapIndex += offset;
}
IntPtr _ImageBaseDataIntptr = Marshal.AllocHGlobal(ImageBaseDataSize);
Marshal.Copy(_ImageBaseDataBufferBytes, 0, _ImageBaseDataIntptr, ImageBaseDataSize);
imageBaseData_V2 = new ImageBaseData_V2(_ImageBaseDataIntptr, (uint)ImageBaseDataSize, bmData.Width, bmData.Height, VMPixelFormat.VM_PIXEL_RGB24_C3);
Marshal.FreeHGlobal(_ImageBaseDataIntptr);
}
bmpInputImg.UnlockBits(bmData); // 解除锁定
return imageBaseData_V2;
}
2.2 Bitmap转图像源SDK输入(ImageBaseData)
public ImageBaseData BitmapToImageBaseData(Bitmap bmpInputImg)
{
ImageBaseData imageBaseData = new ImageBaseData();
System.Drawing.Imaging.PixelFormat bitPixelFormat = bmpInputImg.PixelFormat;
BitmapData bmData = bmpInputImg.LockBits(new Rectangle(0, 0, bmpInputImg.Width, bmpInputImg.Height), ImageLockMode.ReadOnly, bitPixelFormat);//锁定
if (bitPixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
Int32 bitmapDataSize = bmData.Stride * bmData.Height;//bitmap图像缓存长度
int offset = bmData.Stride - bmData.Width;
Int32 ImageBaseDataSize = bmData.Width * bmData.Height;
byte[] _BitImageBufferBytes = new byte[bitmapDataSize];
byte[] _ImageBaseDataBufferBytes = new byte[ImageBaseDataSize];
Marshal.Copy(bmData.Scan0, _BitImageBufferBytes, 0, bitmapDataSize);
int bitmapIndex = 0;
int ImageBaseDataIndex = 0;
for (int i = 0; i < bmData.Height; i++)
{
for (int j = 0; j < bmData.Width; j++)
{
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex++];
}
bitmapIndex += offset;
}
imageBaseData = new ImageBaseData(_ImageBaseDataBufferBytes, (uint)ImageBaseDataSize, bmData.Width, bmData.Height, (int)VMPixelFormat.VM_PIXEL_MONO_08);
}
else if (bitPixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
Int32 bitmapDataSize = bmData.Stride * bmData.Height;//bitmap图像缓存长度
int offset = bmData.Stride - bmData.Width * 3;
Int32 ImageBaseDataSize = bmData.Width * bmData.Height * 3;
byte[] _BitImageBufferBytes = new byte[bitmapDataSize];
byte[] _ImageBaseDataBufferBytes = new byte[ImageBaseDataSize];
Marshal.Copy(bmData.Scan0, _BitImageBufferBytes, 0, bitmapDataSize);
int bitmapIndex = 0;
int ImageBaseDataIndex = 0;
for (int i = 0; i < bmData.Height; i++)
{
for (int j = 0; j < bmData.Width; j++)
{
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex + 2];
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex + 1];
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex];
bitmapIndex += 3;
}
bitmapIndex += offset;
}
imageBaseData = new ImageBaseData(_ImageBaseDataBufferBytes, (uint)ImageBaseDataSize, bmData.Width, bmData.Height, (int)VMPixelFormat.VM_PIXEL_RGB24_C3);
}
bmpInputImg.UnlockBits(bmData); // 解除锁定
return imageBaseData;
}
2.3 Bitmap转模块输入(InputImageData)
public InputImageData BitmapToInputImageData(Bitmap bmpInputImg){
InputImageData inputImageData = new InputImageData();
System.Drawing.Imaging.PixelFormat bitPixelFormat = bmpInputImg.PixelFormat;
BitmapData bmData = bmpInputImg.LockBits(new Rectangle(0, 0, bmpInputImg.Width, bmpInputImg.Height), ImageLockMode.ReadOnly, bitPixelFormat);//锁定
inputImageData.Names.DataName = "InImage";//只能使用默认名称InImage
inputImageData.Names.HeightName = "InImageHeight";//默认InImageHeight
inputImageData.Names.WidthName = "InImageWidth";//默认InImageWidth
inputImageData.Names.PixelFormatName = "InImagePixelFormat";//默认InImagePixelFormat
inputImageData.Width = bmData.Width;
inputImageData.Height = bmData.Height;
if (bitPixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
Int32 bitmapDataSize = bmData.Stride * bmData.Height;//bitmap图像缓存长度
int offset = bmData.Stride - bmData.Width;
Int32 ImageBaseDataSize = bmData.Width * bmData.Height;//imageBaseData_V2图像真正的缓存长度
byte[] _BitImageBufferBytes = new byte[bitmapDataSize];
byte[] _ImageBaseDataBufferBytes = new byte[ImageBaseDataSize];
Marshal.Copy(bmData.Scan0, _BitImageBufferBytes, 0, bitmapDataSize);
int bitmapIndex = 0; int ImageBaseDataIndex = 0;
for (int i = 0; i < bmData.Height; i++) {
for (int j = 0; j < bmData.Width; j++) {
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex++];
} bitmapIndex += offset; }
inputImageData.Pixelformat = ImagePixelFormat.IMAGE_PIXEL_FORMAT_MONO8;
inputImageData.DataLen = (uint)ImageBaseDataSize;
inputImageData.Data = Marshal.AllocHGlobal(ImageBaseDataSize);//inputImageData.Data需要申请内存;
Marshal.Copy(_ImageBaseDataBufferBytes, 0, inputImageData.Data, _ImageBaseDataBufferBytes.Length);
}
else if (bitPixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
Int32 bitmapDataSize = bmData.Stride * bmData.Height;//bitmap图像缓存长度
int offset = bmData.Stride - bmData.Width * 3;
Int32 ImageBaseDataSize = bmData.Width * bmData.Height * 3;//imageBaseData_V2图像真正的缓存长度
byte[] _BitImageBufferBytes = new byte[bitmapDataSize];
byte[] _ImageBaseDataBufferBytes = new byte[ImageBaseDataSize];
Marshal.Copy(bmData.Scan0, _BitImageBufferBytes, 0, bitmapDataSize);
int bitmapIndex = 0; int ImageBaseDataIndex = 0;
for (int i = 0; i < bmData.Height; i++) {
for (int j = 0; j < bmData.Width; j++) {
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex + 2];
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex + 1];
_ImageBaseDataBufferBytes[ImageBaseDataIndex++] = _BitImageBufferBytes[bitmapIndex];
bitmapIndex += 3; } bitmapIndex += offset;
}
inputImageData.Pixelformat = ImagePixelFormat.IMAGE_PIXEL_FORMAT_RGB24;
inputImageData.DataLen = (uint)ImageBaseDataSize;
inputImageData.Data = Marshal.AllocHGlobal(ImageBaseDataSize);//inputImageData.Data需要申请内存;
Marshal.Copy(_ImageBaseDataBufferBytes, 0, inputImageData.Data, _ImageBaseDataBufferBytes.Length);
} bmpInputImg.UnlockBits(bmData); // 解除锁定
return inputImageData;
}
2.4 Bitmap与算子(CmvdImage) 互转
public CMvdImage BitmapToCMvdImage(Bitmap bmpInputImg)
{
CMvdImage cMvdImage = new CMvdImage();
System.Drawing.Imaging.PixelFormat bitPixelFormat = bmpInputImg.PixelFormat;
BitmapData bmData = bmpInputImg.LockBits(new Rectangle(0, 0, bmpInputImg.Width, bmpInputImg.Height), ImageLockMode.ReadOnly, bitPixelFormat);//锁定
if (bitPixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
{
Int32 bitmapDataSize = bmData.Stride * bmData.Height;//bitmap图像缓存长度
int offset = bmData.Stride - bmData.Width;
Int32 ImageBaseDataSize = bmData.Width * bmData.Height;
byte[] _BitImageBufferBytes = new byte[bitmapDataSize];
byte[] _ImageBaseDataBufferBytes = new byte[ImageBaseDataSize];
Marshal.Copy(bmData.Scan0, _BitImageBufferBytes, 0, bitmapDataSize);
int bitmapIndex = 0